Skip to content

Latest commit

 

History

History
412 lines (329 loc) · 15.7 KB

File metadata and controls

412 lines (329 loc) · 15.7 KB

Configuration

`odek uses a layered configuration system with convention over configuration — opt-in files and environment variables, no mandatory setup.

Priority chain

Each layer overrides the one below it. Unset fields inherit from the layer below:

0.  ~/.odek/secrets.env     ← Auto-loaded into process environment on startup
1.  ~/.odek/config.json     ← Global defaults (shared across projects)
2.  ./odek.json             ← Project-specific overrides
3.  ODEK_* env vars         ← Runtime/environment overrides
4.  CLI flags               ← Explicit invocation (highest priority)

Layer 0 is unique: it does not hold config fields directly. Instead it injects KEY=VALUE pairs into the process environment so they're available for:

  • Layer 1–2 ${VAR} substitution in config files
  • Layer 3 ODEK_* env var lookups (e.g. ODEK_API_KEY)
  • Legacy fallbacks like DEEPSEEK_API_KEY / OPENAI_API_KEY

Config files

Global defaults (~/.odek/config.json)

Shared across all projects:

{
  "model": "deepseek-v4-flash",
  "base_url": "https://api.deepseek.com/v1",
  "api_key": "${ODEK_API_KEY}",
  "thinking": "",
  "max_iterations": 90,
  "sandbox": false,
  "interaction_mode": "engaging",
  "no_color": false,
  "no_agents": false,
  "max_tool_parallel": 4,
  "tool_progress": "all",
  "tool_progress_cleanup": true,
  "system": ""
}

Project overrides (./odek.json)

Same schema as global. Only set the fields you want to override:

{
  "model": "gpt-4o",
  "base_url": "https://api.openai.com/v1",
  "max_iterations": 30
}

Both files are optional. Missing files are silently ignored. String values support ${VAR} environment variable substitution — useful for API keys without plaintext storage.

Secrets file (~/.odek/secrets.env)

Auto-loaded on every odek invocation before any config file or env var is read. Each KEY=VALUE line is injected into the process environment via os.Setenv.

ODEK_API_KEY=sk-...
GITHUB_TOKEN=ghp_...

Rules:

  • File format: KEY=VALUE — one per line, no export keyword needed
  • Blank lines and # comments are skipped
  • Existing env vars are NOT overwritten — if ODEK_API_KEY is already in the environment, the file is ignored for that key
  • Missing/unreadable file is silently ignored (not an error)
  • Permissions: keep 0600 (chmod 600 ~/.odek/secrets.env)

This lets you keep secrets out of config files entirely:

// ~/.odek/config.json — no plaintext secrets
{
  "model": "deepseek-v4-flash",
  "api_key": "${ODEK_API_KEY}"      // ← resolved from secrets.env at runtime
}

Environment variables

Every config knob has a ODEK_* counterpart:

Variable Maps to Type
ODEK_MODEL --model string
ODEK_BASE_URL --base-url string
ODEK_API_KEY config files only string
ODEK_THINKING --thinking string
ODEK_MAX_ITER --max-iter int
ODEK_SANDBOX --sandbox bool
ODEK_INTERACTION_MODE --interaction-mode string
ODEK_NO_COLOR --no-color bool
ODEK_NO_AGENTS --no-agents bool
ODEK_SYSTEM --system string
ODEK_SKILLS_LEARN skills.learn bool
ODEK_PROMPT_CACHING prompt_caching bool
ODEK_TOOL_PROGRESS tool_progress string (all|new|verbose|off)
ODEK_SANDBOX_IMAGE --sandbox-image string
ODEK_SANDBOX_NETWORK --sandbox-network string
ODEK_SANDBOX_READONLY --sandbox-readonly bool
ODEK_SANDBOX_MEMORY --sandbox-memory string
ODEK_SANDBOX_CPUS --sandbox-cpus string
ODEK_SANDBOX_USER --sandbox-user string
ODEK_MAX_TOOL_PARALLEL max_tool_parallel int

API key fallback order

ODEK_API_KEYDEEPSEEK_API_KEYOPENAI_API_KEY

Parallel tool execution

When a model emits multiple tool calls in one response (tool_calls array with N entries), odek executes them concurrently in goroutines bounded by a semaphore.

Field Default Env var Description
max_tool_parallel 4 ODEK_MAX_TOOL_PARALLEL Max concurrent tool calls per iteration. 0 = default 4. Set to 1 for sequential execution.

I/O-bound tools (read_file, search_files, shell) benefit most — latency drops from sum(latencies) to max(latency).

Approval gate: When an approver is configured and the LLM returns multiple tool calls, a single batch approval prompt is shown before any tool executes. If approved, all tools run in parallel. If denied, no tools run.

Skills configuration

The skills section controls the skill system:

{
  "skills": {
    "max_auto_load": 3,
    "max_lazy_slots": 5,
    "learn": true,
    "llm_learn": true,
    "llm_curate": true,
    "import": {
      "max_size_bytes": 1048576,
      "timeout_seconds": 5,
      "require_https": false
    },
    "curation": {
      "staleness_days": 90,
      "auto_prune": false,
      "auto_curate": true,
      "skip_threshold": 1,
      "skip_reset_days": 30
    },
    "auto_save": {
      "enabled": true,
      "require_llm": true,
      "max_per_run": 3
    }
  }
}
Field Env var Default Description
max_auto_load 3 Max skills injected into system prompt on start
max_lazy_slots 5 Max skills loaded per user input via trigger matching
learn ODEK_SKILLS_LEARN true Enable skill learning mode (detects patterns, suggests skills). On by default
llm_learn true Use LLM to enrich detected patterns. Template-only — set via odek init, not parsed from JSON at runtime
llm_curate true Use LLM for curation quality assessment. Template-only — set via odek init, not parsed from JSON at runtime
dirs [] Extra skill directories beyond ~/.odek/skills and ./.odek/skills
import.max_size_bytes 1048576 (1MB) Max size for fetched skill content
import.timeout_seconds 5 HTTP timeout for skill URI fetch
import.require_https false Reject http:// URIs when true
curation.staleness_days 90 Days without use before flagging as stale
curation.auto_prune false Auto-delete stale skills on curate (no prompt)
curation.auto_curate true Run auto-curation after sessions (merge, dedup, prune)
curation.skip_threshold 1 Times a skill must be skipped before permanent suppression
curation.skip_reset_days 30 Days after which a skip expires (re-allows suggestion)
auto_save.enabled true Auto-save quality skill suggestions without prompting
auto_save.require_llm true Only auto-save if LLM enhancement was applied
auto_save.max_per_run 3 Max skills to auto-save per session

Memory configuration

The memory section controls the persistent memory system (see docs/MEMORY.md):

{
  "memory": {
    "enabled": true,
    "facts_limit_user": 1500,
    "facts_limit_env": 2500,
    "buffer_lines": 20,
    "buffer_enabled": true,
    "merge_on_write": true,
    "extract_on_end": true,
    "llm_search": true,
    "llm_extract": true,
    "llm_consolidate": true,
    "merge_threshold": 0.7,
    "add_threshold": 0.3
  }
}
Field Default Description
enabled true Enable memory system entirely
facts_limit_user 1500 Max chars for user.md fact file
facts_limit_env 2500 Max chars for env.md fact file
buffer_lines 20 Max turn summaries in session buffer
buffer_enabled true Enable the turn-level buffer
merge_on_write true Use go-vector RP similarity to auto-merge related entries
extract_on_end true Extract durable facts via LLM at session end (≥3 turns)
llm_search true Use LLM to rank episode search results by relevance
llm_extract true Use LLM for end-of-session fact extraction
llm_consolidate true Use LLM to merge related fact entries
merge_threshold 0.7 go-vector cosine threshold for auto-merge (0.0–1.0)
add_threshold 0.3 go-vector cosine threshold for auto-add (0.0–1.0)

Sub-agent configuration

The subagent section controls task decomposition and parallel sub-agent execution (see docs/SUBAGENTS.md):

{
  "subagent": {
    "max_concurrency": 3,
    "timeout_seconds": 120,
    "max_iterations": 15
  }
}
Field Default Description
max_concurrency 3 Max sub-agents running in parallel (max 8)
timeout_seconds 120 Default timeout per sub-agent (overridden by --timeout)
max_iterations 15 Default max think→act cycles per sub-agent (overridden by --max-iter)

This section is optional. Omitted fields inherit sensible defaults.

Note: The subagent section is currently read only from odek.json by the odek subagent command in test code. Runtime values (max_concurrency, timeout_seconds) are hardcoded in production odek run/odek serve. This may be wired up fully in a future release.

MCP server configuration

Connect to external MCP servers and expose their tools to the agent. Any MCP server that works with Claude Code works with odek — same config format.

{
  "mcp_servers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp"]
    },
    "fetch": {
      "command": "uvx",
      "args": ["mcp-server-fetch"]
    }
  }
}
Field Description
command The executable to run
args Optional command-line arguments
env Optional environment variable overrides (empty string removes from env)

Tools are registered as <server_name>__<tool_name> (e.g., playwright__navigate) and are available in odek run, odek repl, odek continue, and odek serve.

See docs/MCP.md for detailed instructions.

Telegram

The telegram section configures the Telegram bot integration and the --deliver flag.

{
  "telegram": {
    "bot_token": "8610437446:AAElHFJ...",
    "allowed_users": [8592463065],
    "allowed_chats": [],
    "poll_interval": 1,
    "poll_timeout": 30,
    "max_msg_length": 4096,
    "session_ttl_hours": 24,
    "log_level": "info",
    "log_file": "",
    "default_chat_id": 8592463065
  }
}
Field Env var Default Description
bot_token ODEK_TELEGRAM_BOT_TOKEN — (required) Telegram bot API token from @BotFather
allowed_users all Restrict bot to specific user IDs
allowed_chats all Restrict bot to specific chat IDs
poll_interval 1 Seconds between poll cycles
poll_timeout 30 Long-poll timeout (1-60 seconds)
max_msg_length 4096 Max characters per message
session_ttl_hours 24 Hours before inactive session expires
log_level info Log level: debug, info, warn, error
log_file stderr Log file path (empty = stderr)
default_chat_id 0 Required for --deliver — numeric chat ID where odek run --deliver sends results. Get this from your bot's update or use a tool like @userinfobot.

--deliver flag

The --deliver flag on odek run sends the agent's final response to the configured default_chat_id as a plain text message. This enables cron-based scheduled agent workflows — no daemon needed.

# Run an agent task and deliver the result to Telegram
odek run --deliver "Check the CI pipeline status"

# Works with task text first too
odek run "Daily summary" --deliver

See docs/TELEGRAM.md for full cron setup instructions.

Tool Progress

Controls how per-tool progress messages appear inside the Telegram bot during agent runs. Independent from interaction_mode — you can have engaging terminal output with minimal Telegram progress, or verbose terminal with rich progress bubbles.

{
  "tool_progress": "all",
  "tool_progress_cleanup": true
}

tool_progress

Value Behavior Use case
"all" (default) Single editable progress bubble with smart previews — e.g. 📝 read_file: "main.go". Includes edit throttling (1.5s), tool dedup (×N counter for repeated same-tool), and automatic flood-control fallback General use — shows what the agent is doing without spamming the chat
"new" Same as "all" but only updates when the tool name changes. Consecutive read_file calls produce one line; a shell call starts a new line Long-running agents with repetitive tool chains (e.g. reading 50 files in batch)
"verbose" Raw tool arguments as separate messages. Each tool call sends a new message with full JSON args; on completion the result is sent as a new message ✅ (size) Debugging — see exactly what the agent passes to each tool
"off" No per-tool progress messages at all. Only the initial "🤔 Looking into that..." and final answer are shown Privacy-sensitive contexts or users who prefer zero noise

tool_progress_cleanup

Default: true. Controls whether the progress message bubble is deleted after the agent's final answer arrives:

  • true — delete the progress bubble (clean chat, no stale tool traces)
  • false — keep the progress bubble as a breadcrumb of what the agent did

How it works

The progress system is an evolving single message that gets edited in-place (similar to an animated status). Each tool call adds a line like:

📝 read_file: "main.go"
💻 shell: "npm test"
📝 read_file: "utils.go" (×3)

Key behaviors:

  • Smart previews — instead of showing raw JSON args, the system extracts meaningful context: filename for file tools, the command text for shell, URL for browser, query text for memory/search tools, audio filename for transcribe
  • Edit throttling — edits are rate-limited to one every 1.5 seconds to avoid hitting Telegram's flood control limits. Rapid tool chains don't produce 429 errors
  • Tool dedup — when the same tool runs consecutively (common with parallel batch tools like batch_read), identical lines are collapsed into a (×N) counter instead of repeating N times
  • Flood control fallback — if an edit message fails with "flood" or "retry after", the system automatically switches to sending new messages instead of editing. This prevents the bot from becoming unresponsive under heavy load
  • Content reset — when the agent calls send_message mid-run to send an interim message, the progress bubble resets below that content, keeping the chat timeline in correct order

odek init

Create a config file template:

# Local project config (./odek.json)
odek init

# Global config (~/.odek/config.json)
odek init --global

# Overwrite existing file
odek init --force

Quick examples

# Set API key via secrets.env (recommended — keeps secrets out of config files)
echo 'ODEK_API_KEY="sk-..."' >> ~/.odek/secrets.env
chmod 600 ~/.odek/secrets.env

# Global config (model and other settings only, no secrets)
echo '{"model": "deepseek-v4-flash"}' > ~/.odek/config.json
odek run "list files"

# Per-project override
echo '{"max_iterations": 30}' > ./odek.json
odek run "quick status"

# Env var override for one-off
ODEK_SANDBOX=true odek run "run untrusted script"

# Enable skill learning via env var
ODEK_SKILLS_LEARN=true odek run "set up CI"

# Sub-agent config (project-level)
echo '{"subagent": {"max_concurrency": 5, "timeout_seconds": 300}}' > ./odek.json

# CLI flag always wins
odek run --model gpt-4o --base-url https://api.openai.com/v1 "task"