Documentation
¶
Overview ¶
Package config loads and merges odek configuration from multiple sources.
Priority (lowest to highest):
- ~/.odek/config.json — global defaults (shared across projects)
- ./odek.json — project-specific overrides
- ODEK_* env vars — runtime/environment overrides
- CLI flags — explicit invocation overrides (highest)
Both config files are optional. Missing files are silently ignored. String values in config files support ${VAR} environment variable substitution (e.g. "api_key": "${MY_API_KEY}"). Use $$ for a literal dollar sign.
Index ¶
- Constants
- func GlobalConfigPath() string
- func ProjectConfigPath() string
- type CLIFlags
- type FileConfig
- type MaintenanceConfig
- type ProjectSandboxOverride
- type ResolvedConfig
- type ScheduleConfig
- type SchedulesConfig
- type SessionsConfig
- type SkillsConfig
- type ToolConfig
- type ToolsConfig
- type TranscriptionConfig
- type VisionConfig
- type WebSearchConfig
Constants ¶
const (
DefaultSandboxNetwork = "none"
)
Variables ¶
This section is empty.
Functions ¶
func GlobalConfigPath ¶
func GlobalConfigPath() string
GlobalConfigPath returns the path to the global config file. Uses $HOME/.odek/config.json.
func ProjectConfigPath ¶
func ProjectConfigPath() string
ProjectConfigPath returns the path to the project-level config file. Uses ./odek.json relative to the current working directory.
Types ¶
type CLIFlags ¶
type CLIFlags struct {
Model string
BaseURL string
System string
Thinking string
MaxIter int // 0 = not set
Sandbox *bool // nil = not set
NoColor *bool // nil = not set
NoAgents *bool // nil = not set
Learn *bool // nil = not set
Task string
// ToolsEnabled and ToolsDisabled control which tools are exposed to the LLM.
// These override file/env config.
ToolsEnabled []string
ToolsDisabled []string
// PromptCaching enables prompt caching markers for supported providers.
// Config: prompt_caching, ODEK_PROMPT_CACHING, --prompt-caching.
PromptCaching *bool // nil = not set
// Compaction enables LLM-based rolling compaction of trimmed context
// (default: on). Config: compaction, ODEK_COMPACTION,
// --compaction / --no-compaction.
Compaction *bool // nil = not set
// Sandbox-specific
SandboxImage string
SandboxNetwork string
SandboxMemory string
SandboxCPUs string
SandboxUser string
SandboxReadonly *bool // nil = not set
// InteractionMode controls how tool-call progress is surfaced.
// "engaging" (default) = emoji-rich narration, progress message edited.
// "enhance" = per-tool narrated messages appended, progress header kept.
// "verbose" = raw tool names, args, and results.
// "off" = no intermediate progress output, clean answer only.
InteractionMode string
// Extended memory subsystem CLI overrides.
MemoryExtendedEnabled *bool // nil = not set
MemoryExtendedMaxSizeMB int // 0 = not set
MemoryExtendedAtomMaxChars int // 0 = not set
MemoryExtendedMemoryBudgetChars int // 0 = not set
MemoryExtendedUserStateTurnInterval int // 0 = not set
MemoryExtendedUserStateMaxPending int // 0 = not set
MemoryExtendedAssociationsEnabled *bool // nil = not set
MemoryExtendedAssociationSemanticTopK int // 0 = not set
MemoryExtendedProactiveReturnAfterBreak *bool // nil = not set
MemoryExtendedStyleMirroringEnabled *bool // nil = not set
MemoryExtendedAnaphoraResolutionEnabled *bool // nil = not set
MemoryExtendedFollowUpAnticipationEnabled *bool // nil = not set
// Guard subsystem CLI overrides.
GuardProvider string // "" = not set
GuardURL string // "" = not set
GuardBatchURL string // "" = not set
GuardLongURL string // "" = not set
GuardSocketPath string // "" = not set
GuardThreshold float64 // 0 = not set
GuardTimeoutSeconds int // 0 = not set
GuardFallbackToLocal *bool // nil = not set
GuardScanMemory *bool // nil = not set
GuardScanSystemPrompt *bool // nil = not set
GuardScanMCP *bool // nil = not set
GuardScanSkills *bool // nil = not set
GuardScanToolOutputs *bool // nil = not set
GuardScanTelegram *bool // nil = not set
// TrustedProxies is a list of IP addresses or CIDR ranges of reverse proxies
// whose X-Forwarded-For / X-Real-Ip headers are trusted. Empty means headers
// are ignored even from loopback. Only used by `odek serve`.
TrustedProxies []string
// Execution-budget CLI overrides (odek-extension/v1). CLI flags are
// operator intent: they set limits explicitly, 0 = flag not passed.
MaxRuntimeSeconds int64
MaxToolCalls int64
MaxInputTokens int64
MaxOutputTokens int64
MaxCostUSD float64
}
CLIFlags holds values parsed from the CLI. Zero/nil values mean the flag was not explicitly passed — the config loader will look at lower priority layers for these fields.
CLIFlags holds CLI-only configuration. These fields participate in the same merge chain: global file → project file → ODEK_* env → CLI. Fields typed as *bool distinguish "explicitly set to false" from "not set", which matters when the config file says "sandbox_readonly: false" (user explicitly wants writable) vs the field being absent (inherit from lower layer or default).
type FileConfig ¶
type FileConfig struct {
Model string `json:"model,omitempty"`
BaseURL string `json:"base_url,omitempty"`
APIKey string `json:"api_key,omitempty"`
Thinking string `json:"thinking,omitempty"`
MaxIter int `json:"max_iterations,omitempty"`
Sandbox *bool `json:"sandbox,omitempty"`
NoColor *bool `json:"no_color,omitempty"`
NoAgents *bool `json:"no_agents,omitempty"`
// PromptCaching enables prompt caching markers for supported providers.
PromptCaching *bool `json:"prompt_caching,omitempty"`
// Compaction enables LLM-based rolling compaction of trimmed context
// (default: on; set false to explicitly disable).
Compaction *bool `json:"compaction,omitempty"`
System string `json:"system,omitempty"`
// Sandbox-specific fields.
SandboxImage string `json:"sandbox_image,omitempty"`
SandboxNetwork string `json:"sandbox_network,omitempty"`
SandboxReadonly *bool `json:"sandbox_readonly,omitempty"`
SandboxMemory string `json:"sandbox_memory,omitempty"`
SandboxCPUs string `json:"sandbox_cpus,omitempty"`
SandboxUser string `json:"sandbox_user,omitempty"`
SandboxEnv map[string]string `json:"sandbox_env,omitempty"`
SandboxVolumes []string `json:"sandbox_volumes,omitempty"`
// Dangerous operation approval settings.
Dangerous *danger.DangerousConfig `json:"dangerous,omitempty"`
// Skills section (see internal/skills package).
Skills *SkillsConfig `json:"skills,omitempty"`
// Memory section controls the persistent memory system.
Memory *memory.MemoryConfig `json:"memory,omitempty"`
// Guard configures the prompt-injection guard subsystem.
// Operator-controlled: rejected from project-level ./odek.json.
Guard *guard.Config `json:"guard,omitempty"`
// Embedding is the shared default embedding backend for semantic retrieval.
// Every subsystem (memory, sessions, skills) uses it unless that subsystem
// sets its own override (memory.embedding / sessions.embedding /
// skills.embedding). See internal/embedding.Config.
Embedding *embedding.Config `json:"embedding,omitempty"`
// Sessions configures the session subsystem. Currently only an optional
// embedding override for semantic session_search.
Sessions *SessionsConfig `json:"sessions,omitempty"`
// MCPServers maps server names to MCP server configurations.
// Each server is an external MCP server (e.g., Playwright, database,
// web scraping) whose tools are exposed to the agent.
// Format matches Claude Code's mcpServers config:
//
// "mcp_servers": {
// "playwright": {
// "command": "npx",
// "args": ["@playwright/mcp"]
//\t }
//\t}
MCPServers map[string]mcpclient.ServerConfig `json:"mcp_servers,omitempty"`
// MaxConcurrency limits how many sub-agent tasks run in parallel.
// Config: max_concurrency, ODEK_MAX_CONCURRENCY.
// Default: 3.
MaxConcurrency int `json:"max_concurrency,omitempty"`
// MaxToolParallel limits how many tool calls run concurrently per
// agent iteration. Config: max_tool_parallel.
// Default: 0 (loop uses default of 4).
MaxToolParallel int `json:"max_tool_parallel,omitempty"`
// TrustedProxies lists IP addresses or CIDR ranges of reverse proxies whose
// X-Forwarded-For / X-Real-Ip headers are trusted. Empty means headers are
// ignored even from loopback. Config: trusted_proxies, ODEK_TRUSTED_PROXIES.
// Only used by `odek serve`.
TrustedProxies []string `json:"trusted_proxies,omitempty"`
// Telegram configures the Telegram bot integration.
Telegram *telegram.TelegramConfig `json:"telegram,omitempty"`
// Transcription configures local audio transcription (whisper.cpp).
Transcription *TranscriptionConfig `json:"transcription,omitempty"`
// Vision configures local image/video understanding (MiniCPM-V 4.6 via llama-mtmd-cli).
Vision *VisionConfig `json:"vision,omitempty"`
// WebSearch configures the web_search tool (self-hosted SearXNG backend).
WebSearch *WebSearchConfig `json:"web_search,omitempty"`
// Schedules configures the native in-process task scheduler.
Schedules *SchedulesConfig `json:"schedules,omitempty"`
// Maintenance configures the storage-maintenance janitor (retention and
// deletion of sessions, audit records, plans, logs, skip-list entries).
// Operator-controlled: rejected from project-level ./odek.json.
Maintenance *MaintenanceConfig `json:"maintenance,omitempty"`
// Tools controls which tools are exposed to the LLM.
// Project-level ./odek.json may only disable tools, not enable them.
Tools *ToolsConfig `json:"tools,omitempty"`
// InteractionMode controls how the agent communicates tool/progress updates.
// "engaging" (default) = emoji-rich narration, progress message edited.
// "enhance" = per-tool narrated messages, progress header kept.
// "verbose" = raw tool names, args, and results.
// "off" = no progress output, clean answer only.
InteractionMode string `json:"interaction_mode,omitempty"`
// ToolProgress controls per-tool progress messages for the Telegram bot.
// "all" (default) — show every tool call
// "new" — only when the tool name changes (dedup consecutive same-tool)
// "verbose" — full tool arguments in progress messages
// "off" — no per-tool progress messages (just thinking + final answer)
ToolProgress string `json:"tool_progress,omitempty"`
// ToolProgressCleanup controls whether progress messages are deleted after
// the final answer. Default: true (delete progress messages).
ToolProgressCleanup *bool `json:"tool_progress_cleanup,omitempty"`
// Limits is the "limits" section: hard execution budgets
// (odek-extension/v1). The global config may set any limit; the project
// config may only LOWER an existing one (see clampProjectLimits).
Limits *budget.Limits `json:"limits,omitempty"`
}
FileConfig is the JSON schema used by ~/.odek/config.json and ./odek.json. Pointer booleans distinguish "explicitly set to false" from "not set".
type MaintenanceConfig ¶ added in v1.15.0
type MaintenanceConfig struct {
Enabled *bool `json:"enabled,omitempty"`
IntervalMinutes *int `json:"interval_minutes,omitempty"`
SessionsMaxAgeDays *int `json:"sessions_max_age_days,omitempty"`
AuditMaxAgeDays *int `json:"audit_max_age_days,omitempty"`
LogMaxMB *int64 `json:"log_max_mb,omitempty"`
PlansMaxAgeDays *int `json:"plans_max_age_days,omitempty"`
SkillsSkipMaxAgeDays *int `json:"skills_skip_max_age_days,omitempty"`
}
MaintenanceConfig is the file-level "maintenance" section. Pointer fields distinguish "not set" (inherit the default) from an explicit 0, which is meaningful for the retention knobs (0 = keep forever / disable). Operator-controlled: rejected from project-level ./odek.json because it governs DELETION of user data.
type ProjectSandboxOverride ¶ added in v1.14.0
type ProjectSandboxOverride struct {
HasEnv bool
EnvKeys []string
EnvHasInterpolation bool
HasImage bool
Image string
HasNetwork bool
Network string
HasVolumes bool
Volumes []string
}
ProjectSandboxOverride records which sandbox knobs were supplied by the project-level ./odek.json config. These require explicit operator approval before they are applied, because a malicious repo could otherwise exfiltrate host secrets (via ${VAR} interpolation in sandbox_env), pull an attacker-controlled image, or widen the container's network access.
type ResolvedConfig ¶
type ResolvedConfig struct {
Model string
BaseURL string
APIKey string
Thinking string
MaxIter int
Sandbox bool
NoColor bool
NoAgents bool
PromptCaching bool
Compaction bool
System string
// SandboxImage is the Docker image for the sandbox container.
// Default: "alpine:latest" (applied at call site, not here —
// set to "alpine:latest" only if Dockerfile.odek doesn't exist).
// Config: sandbox_image, ODEK_SANDBOX_IMAGE, --sandbox-image.
SandboxImage string
// SandboxNetwork is the Docker network mode.
// Default: "bridge" (internet access by default).
// Config: sandbox_network, ODEK_SANDBOX_NETWORK, --sandbox-network.
SandboxNetwork string
// SandboxReadonly, when true, mounts the working directory read-only
// in the container. The agent can read /workspace but cannot write to it.
// Config: sandbox_readonly, ODEK_SANDBOX_READONLY, --sandbox-readonly.
SandboxReadonly bool
// SandboxMemory is the container memory limit (e.g. "512m", "2g").
// Empty string means no limit.
// Config: sandbox_memory, ODEK_SANDBOX_MEMORY, --sandbox-memory.
SandboxMemory string
// SandboxCPUs is the container CPU limit (e.g. "0.5", "2", "4").
// Empty string means no limit.
// Config: sandbox_cpus, ODEK_SANDBOX_CPUS, --sandbox-cpus.
SandboxCPUs string
// SandboxUser sets the container user (e.g. "1000:1000" or "node").
// Empty string means root (default Docker behavior).
// Config: sandbox_user, ODEK_SANDBOX_USER, --sandbox-user.
SandboxUser string
// SandboxEnv holds extra environment variables injected into the
// container. File-only — no env var or CLI mapping.
// Config: sandbox_env.
SandboxEnv map[string]string
// SandboxVolumes holds extra volume mounts in "host:container" format.
// File-only — no env var or CLI mapping.
// Config: sandbox_volumes.
SandboxVolumes []string
// Dangerous is the resolved dangerous operations config.
// Uses danger.DangerousConfig defaults for any unset fields.
Dangerous danger.DangerousConfig
// Skills is the resolved skills config with default values.
Skills skills.SkillsConfig
// Memory is the resolved memory config with default values.
Memory memory.MemoryConfig
// Guard is the resolved injection-guard config with default values.
Guard guard.Config
// Embedding is the resolved shared embedding backend — the default every
// subsystem inherits unless it overrides. nil = default RandomProjections.
Embedding *embedding.Config
// SessionEmbedding is the embedding backend sessions use for semantic
// session_search: sessions.embedding when set, else the shared Embedding.
SessionEmbedding *embedding.Config
// MCPServers maps server names to external MCP server configurations.
// Populated from the mcp_servers section of odek.json.
MCPServers map[string]mcpclient.ServerConfig
// ProjectMCPServerNames lists the MCP server names that were introduced by
// the project-level ./odek.json config. These require explicit user approval
// before their subprocesses are spawned, because a malicious repo could
// otherwise execute arbitrary code via the mcp_servers section.
ProjectMCPServerNames []string
// ProjectSandboxOverride records sandbox knobs supplied by the project-level
// ./odek.json config. These require explicit operator approval before they
// are applied, because a malicious repo could otherwise exfiltrate host
// secrets or pull an attacker-controlled sandbox image.
ProjectSandboxOverride ProjectSandboxOverride
// MaxConcurrency limits how many sub-agent tasks run in parallel.
// Config: max_concurrency, ODEK_MAX_CONCURRENCY.
// Default: 3.
MaxConcurrency int
// MaxToolParallel limits how many tool calls run concurrently per
// agent iteration. Config: max_tool_parallel.
// Default: 0 (loop uses default of 4).
MaxToolParallel int
// Telegram is the resolved Telegram bot configuration.
Telegram telegram.TelegramConfig
// Transcription is the resolved transcription config.
// Default: auto_transcribe=true, model="tiny", language="", no binary_path.
Transcription TranscriptionConfig
// Vision is the resolved vision config.
// Default: VideoFrames=8, ModelsDir="" (auto-detect), BinaryPath="" (PATH lookup).
Vision VisionConfig
// WebSearch is the resolved web_search config.
// Default: MaxResults=10, Timeout=15, BaseURL="" (tool disabled until set).
WebSearch WebSearchConfig
// Schedules is the resolved scheduler config.
// Default: enabled=true, max_concurrent=2, timezone="UTC", catchup=false.
Schedules ScheduleConfig
// Maintenance is the resolved storage-maintenance config.
// Default: maintenance.DefaultConfig() (enabled, 60min tick, sessions 30d,
// audit 14d, logs 50MB, plans 30d, skip-list 90d).
Maintenance maintenance.Config
// Tools is the resolved tool-list configuration.
// Empty Enabled/Disabled means "no restriction" for that direction.
Tools ToolConfig
// InteractionMode is the resolved interaction style.
// Values: "engaging" (default), "enhance", "verbose", or "off".
// "engaging" (default), "enhance", or "verbose".
InteractionMode string
// ToolProgress is the resolved tool progress mode for Telegram.
// Default: "all".
ToolProgress string
// ToolProgressCleanup controls whether progress messages are deleted
// after the final answer. Default: true.
ToolProgressCleanup bool
// TrustedProxies lists IP addresses or CIDR ranges of reverse proxies whose
// X-Forwarded-For / X-Real-Ip headers are trusted by `odek serve`.
TrustedProxies []string
// Limits is the resolved execution-budget configuration
// (odek-extension/v1). Zero fields mean "no limit". Merge rule: the global
// config may set any limit; the untrusted project ./odek.json may only
// LOWER an existing limit (never raise, never disable); CLI flags are
// operator intent and set limits explicitly.
Limits budget.Limits
}
ResolvedConfig is the fully merged result. Every field has a concrete value — callers can read directly without checking for "not set".
func LoadConfig ¶
func LoadConfig(cli CLIFlags) ResolvedConfig
LoadConfig merges configuration from all four layers and returns the fully resolved result.
Priority (lowest → highest):
global file → project file → ODEK_* env → CLI flags
For each field, the highest-priority layer that provides a value wins. API key has an additional fallback: if none of the four layers provides one, it falls back to DEEPSEEK_API_KEY → OPENAI_API_KEY (legacy env vars).
type ScheduleConfig ¶ added in v1.2.0
type ScheduleConfig struct {
Enabled bool
MaxConcurrent int
Timezone string
Catchup bool
AllowTelegramManagement bool
TelegramAdminChats []int64
TelegramAdminUsers []int64
// Dangerous is the schedule-specific dangerous-operations policy. See
// SchedulesConfig.Dangerous for semantics.
Dangerous danger.DangerousConfig
}
ScheduleConfig is the resolved scheduler config (all fields concrete).
type SchedulesConfig ¶ added in v1.2.0
type SchedulesConfig struct {
Enabled *bool `json:"enabled,omitempty"` // run the embedded scheduler inside `odek telegram` (default true)
MaxConcurrent int `json:"max_concurrent,omitempty"` // max jobs running at once (default 2)
Timezone string `json:"timezone,omitempty"` // default timezone for jobs with none (default UTC)
Catchup *bool `json:"catchup,omitempty"` // global default: run a missed fire once on startup (default false)
// AllowTelegramManagement gates the in-chat `/schedule` management commands.
// When false, the Telegram bot still lists/previews jobs but refuses to
// add/remove/enable/disable/run them — manage from the host CLI instead.
AllowTelegramManagement *bool `json:"allow_telegram_management,omitempty"` // default true
// TelegramAdminChats restricts mutating `/schedule` commands to the listed
// chat IDs. When empty, management falls back to telegram.default_chat_id
// (if set). Read-only commands are not affected.
TelegramAdminChats []int64 `json:"telegram_admin_chats,omitempty"`
// TelegramAdminUsers restricts mutating `/schedule` commands to the listed
// user IDs. Read-only commands are not affected.
TelegramAdminUsers []int64 `json:"telegram_admin_users,omitempty"`
// Dangerous overrides the global dangerous-operations policy for scheduled
// (unattended) runs only. It is applied on top of the global dangerous
// config, then a non-overrideable safety floor is applied by the scheduler
// itself: destructive and blocked classes are always denied, and
// non_interactive is always deny because no human is present to approve.
// This lets operators allow network_egress/system_write/etc. for cron jobs
// without widening the policy for interactive CLI/REPL/WebUI use.
Dangerous *danger.DangerousConfig `json:"dangerous,omitempty"`
}
SchedulesConfig is the file-level scheduler configuration. Tri-state fields use pointers so "unset" is distinguishable from an explicit false.
type SessionsConfig ¶ added in v1.6.0
SessionsConfig is the "sessions" section of odek.json. It currently only carries an optional embedding override for semantic session_search; when unset, sessions use the shared top-level embedding default.
type SkillsConfig ¶
type SkillsConfig struct {
MaxAutoLoad *int `json:"max_auto_load,omitempty"`
MaxLazySlots *int `json:"max_lazy_slots,omitempty"`
Learn *bool `json:"learn,omitempty"`
Dirs []string `json:"dirs,omitempty"`
Import *skills.ImportConfig `json:"import,omitempty"`
Curation *skills.CurationConfig `json:"curation,omitempty"`
AutoSave *skills.AutoSaveConfig `json:"auto_save,omitempty"`
LLMLearn *bool `json:"llm_learn,omitempty"`
LLMCurate *bool `json:"llm_curate,omitempty"`
Verbose *bool `json:"verbose,omitempty"`
Embedding *embedding.Config `json:"embedding,omitempty"`
}
SkillsConfig holds the skills configuration section from JSON files.
type ToolConfig ¶ added in v1.11.0
type ToolConfig struct {
Enabled []string `json:"enabled,omitempty"`
Disabled []string `json:"disabled,omitempty"`
}
ToolConfig controls which tools are exposed to the LLM. Config: tools.enabled, tools.disabled; ODEK_TOOLS_ENABLED, ODEK_TOOLS_DISABLED; --tool, --no-tool.
type ToolsConfig ¶ added in v1.11.0
type ToolsConfig = ToolConfig
ToolsConfig is the "tools" section of odek.json. It is intentionally a pointer in FileConfig so "not set" can be distinguished from an explicit empty list.
type TranscriptionConfig ¶
type TranscriptionConfig struct {
Model string `json:"model,omitempty"`
Language string `json:"language,omitempty"`
AutoTranscribe bool `json:"auto_transcribe,omitempty"`
ModelsDir string `json:"models_dir,omitempty"`
BinaryPath string `json:"binary_path,omitempty"`
}
TranscriptionConfig controls the transcribe tool (local whisper.cpp). Populated from the "transcription" section of odek.json.
type VisionConfig ¶ added in v1.3.0
type VisionConfig struct {
// ModelsDir is the directory containing model.gguf and mmproj.gguf.
// Default: /usr/local/share/minicpm-v/models (Docker image path), with
// fallback to ~/.odek/minicpm-v/models for out-of-container installs.
ModelsDir string `json:"models_dir,omitempty"`
// BinaryPath overrides PATH lookup for the llama-mtmd-cli binary.
BinaryPath string `json:"binary_path,omitempty"`
// VideoFrames is the number of frames to sample evenly from a video file.
// Default: 8.
VideoFrames int `json:"video_frames,omitempty"`
// AutoDescribe controls whether photos received over Telegram are
// automatically run through the vision model to extract a description
// before the agent answers (mirrors transcription.auto_transcribe).
// Default: true.
AutoDescribe bool `json:"auto_describe,omitempty"`
}
VisionConfig controls the vision tool (MiniCPM-V 4.6 via llama-mtmd-cli). Populated from the "vision" section of odek.json or ~/.odek/config.json.
type WebSearchConfig ¶ added in v1.4.0
type WebSearchConfig struct {
// BaseURL is the SearXNG instance the tool queries, e.g.
// "http://searxng:8080" (Docker compose) or "http://127.0.0.1:8888"
// (host). Empty disables the tool.
BaseURL string `json:"base_url,omitempty"`
// Categories optionally restricts the SearXNG categories queried
// (comma-separated, e.g. "general" or "general,news"). Empty = SearXNG default.
Categories string `json:"categories,omitempty"`
// Language optionally sets the SearXNG language code (e.g. "en"). Empty = SearXNG default.
Language string `json:"language,omitempty"`
// MaxResults caps how many results are returned to the agent. Default: 10.
MaxResults int `json:"max_results,omitempty"`
// Timeout is the per-request timeout in seconds. Default: 15.
Timeout int `json:"timeout_seconds,omitempty"`
}
WebSearchConfig controls the web_search tool (self-hosted SearXNG backend). Populated from the "web_search" section of odek.json or ~/.odek/config.json. The tool is registered only when BaseURL is non-empty — without a reachable SearXNG instance there is no backend, so the tool stays hidden by default (a plain `go install` has no sidecar; the Docker compose setup sets BaseURL).