# daimonos default configuration # Copy to ~/.config/daimonos/config.toml or place next to the binary to override. [index] # eager = always warm in background; lazy = cold until first file search; # hybrid = lazy correctness with heuristic background warming (default) mode = "hybrid" # Maximum directory traversal depth max_depth = 20 # Hard cap on retained indexed paths. 0 uses an internal 50k safety cap. max_files = 50_000 # Hard cap on all filesystem entries visited by an index/preflight walk. # Must be at least max_files. max_walk_entries = 100_000 # Gate eager indexing on a signal instead of a path blocklist. A root larger # than the max_files preflight budget is auto-indexed only if it looks like a # real project (a project_markers entry is present at the root). This stops # daimonos from crawling an over-broad root (e.g. $HOME, a NAS mount) that an # editor inherited as cwd. In hybrid mode small/marked roots warm in the # background; all roots remain searchable through bounded cold population. guard_overbroad_roots = true # Filenames that mark a root as a real project for the gate above. project_markers = [ ".git", ".hg", ".svn", "Cargo.toml", "package.json", "pyproject.toml", "go.mod", "Gemfile", "pom.xml", "build.gradle", "CMakeLists.txt", "Makefile", ] # File extensions to skip during indexing (known binary formats) skip_extensions = [ "png", "jpg", "jpeg", "gif", "webp", "ico", "bmp", "svg", "mp3", "mp4", "avi", "mov", "mkv", "flac", "wav", "ogg", "webm", "zip", "tar", "gz", "bz2", "xz", "7z", "rar", "zst", "exe", "dll", "so", "dylib", "o", "a", "lib", "wasm", "pyc", "pyo", "class", "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "sqlite", "db", "mdb", "ttf", "otf", "woff", "woff2", "eot", ] [search] # Default max results for grep when not specified by caller default_grep_max = 100 # Default max results for find (trigram search) when not specified default_find_max = 20 [process] # Number of trailing output lines returned by poll poll_tail_lines = 20 # Max characters of exec stdout/stderr before truncation (keeps first+last lines) exec_output_max_chars = 100_000 # Bytes read per live foreground-exec progress update (ACP terminal streaming) exec_stream_chunk_bytes = 8_192 # Additional directories to prepend to PATH for exec/bg commands. # Common tool dirs (~/.cargo/bin, ~/.local/bin, etc.) are auto-detected; # use this for non-standard locations. # extra_path = ["/opt/custom/bin"] # Max entries in session-level caches (read_cache, exec_usage). # When exceeded, oldest/least-used entries are evicted. max_cache_entries = 1024 # Semantic output filters for exec commands. When enabled, recognized # commands (test runners, builds, installers, linters) get compressed # output — e.g. test runs show only failures, builds show only errors. # Set false to always return raw output. exec_output_filters = true # Redirect exec calls to native plugins when a match is found. # e.g. exec("cargo test --lib") routes through the cargo plugin # returning structured JSON instead of raw verbose output. # Set false to always use raw exec. exec_plugin_redirect = true # Max concurrent Starlark script threads. Each execute_script call runs on # a dedicated OS thread; pure-CPU runaway scripts cannot be cancelled # (starlark 0.13 does not expose a hook in its public API), so this caps # the number of leaked threads the daemon will tolerate before blocking # new execute_script calls. Raise if you legitimately run many concurrent # scripts; lower to fail fast under buggy script load. max_script_threads = 32 # In-script LLM sub-calls (ADR-008): let execute_script call the agent's own # model via `llm_query(prompt)` / `llm_query_batched(prompts)`. Off by default # — turn on only where a provider is attached (agent/chat/ACP mode). Sub-call # spend rolls into the turn's usage/cost and emits `script_subcall` generation # spans. script_llm_enabled = false # Max total sub-calls one execute_script run may issue (llm_query = 1, # llm_query_batched = its prompt count). Bounds cost/latency blast radius. max_script_subcalls = 32 # Max prompts a single llm_query_batched call may take. max_script_subcall_batch = 16 [tool_output] # Optional directory for complete outputs replaced by bounded previews. # Default: ~/.daimonos/tool-output # directory = "~/.daimonos/tool-output" # Maximum UTF-8 bytes and lines retained in each model-visible text result. max_bytes = 51_200 max_lines = 2_000 # Delete managed full-output files older than this many days during writes. retention_days = 7 # Within one agent turn, retain old successful results newest-first up to this # approximate token budget, while always preserving errors and recent results. intra_turn_result_budget_tokens = 40_000 intra_turn_keep_recent_results = 5 # Truncate large string arguments in old write_file/edit_file calls above this # character count. Paths and other metadata are never truncated. old_argument_max_chars = 2_000 [logging] # Persist structured JSON diagnostics independently of the ACP/MCP client. enabled = true # File log level: trace | debug | info | warn | error | off. level = "info" # Warnings/errors are also mirrored to stderr so Zed can surface them. # ACP/MCP protocol stdout is never used for logs. stderr_level = "warn" # Default: $XDG_STATE_HOME/daimonos/logs, then ~/.local/state/daimonos/logs. # directory = "~/.local/state/daimonos/logs" # Rotated filename prefix. file_prefix = "daimonos" # hourly | daily | never rotation = "daily" # Maximum number of rotated files retained. max_files = 14 # Periodic process telemetry (RSS, threads, fds, cumulative/delta CPU ticks). # Set 0 to disable. resource_interval_secs = 15 [observability] # Export agent traces over OTLP/HTTP. Disabled by default; local analytics and # secure process logs remain available independently. enabled = false # Langfuse self-hosted default. Langfuse Cloud users should set their regional # OTLP endpoint. Any compatible OTLP/HTTP endpoint may be used. endpoint = "http://localhost:3000/api/public/otel/v1/traces" # Set false for an unauthenticated local OTLP collector. basic_auth = true # Only environment-variable NAMES are stored here. Secret values are never # serialized into configuration or diagnostic logs. basic_auth_username_env = "LANGFUSE_PUBLIC_KEY" basic_auth_password_env = "LANGFUSE_SECRET_KEY" environment = "development" # release = "git-sha-or-release-name" # Parent-based sampling ratio, inclusive 0.0..=1.0. sample_ratio = 1.0 # Bounded batch exporter. Queue saturation drops telemetry instead of blocking # an agent turn. max_queue_size = 2048 max_batch_size = 512 batch_delay_ms = 5000 flush_timeout_ms = 3000 [analytics] # Track per-tool-call token usage, latency, and savings. # Data is stored in SQLite for cross-session history. enabled = true # Path to analytics database (default: ~/.daimonos/analytics.db) # db_path = "~/.daimonos/analytics.db" # Auto-delete records older than this many days retention_days = 90 [mcp] # Idle watchdog for `daimonos --mcp`. If no MCP request (list_tools or # call_tool) arrives in this many seconds, the process exits cleanly to # release resources (inotify watches, fds, memory). This protects against # orphaned subprocesses when a parent editor closes an agent panel without # sending a shutdown and without closing the stdin pipe (e.g. another # editor worker still holds the write-end). Set to 0 to disable. # Override at startup with the DAIMONOS_IDLE_TIMEOUT_SECS env var. idle_timeout_secs = 600 # When false (default), MCP mode avoids benign stderr lines during startup and # idle shutdown so Cursor does not surface them as `[error]` (Cursor treats all # MCP subprocess stderr as errors). Use `--verbose`, export DAIMONOS_LOG_STARTUP=1, # or set true here for diagnostics. startup_logs = false # When true, list_tools includes full JSON Schemas for Terse-tier plugin tools # (git, cargo, docker, …). Default false to save tokens. Glama introspection # expects full schemas — set true here or export DAIMONOS_MCP_FULL_SCHEMAS=1 # in the Glama Dockerfile build (recommended for release scoring only). full_tool_schemas = false [acp] # Maximum saved sessions returned by one session/list response. Clients receive # an opaque continuation cursor when more sessions remain. session_list_page_size = 50 [acp.mcp] # MCP-server bridge (ADR-003, #990). Zed forwards every configured context # server on session/new and session/load; when enabled, daimonos connects to # each as an MCP client, discovers its tools, and exposes them to the model as # `mcp__{server}__{tool}` alongside the native daimonos tools. enabled = true # Accept + advertise stdio-transport servers (command/args/env). allow_stdio = true # Accept + advertise HTTP-transport servers (url/headers). allow_http = true # Reuse identical initialized clients across ACP sessions in this process. # Set false for servers that intentionally require per-chat state isolation. shared_pool_enabled = true # Per-server budget (seconds) for the initialize + tools/list handshake. A # server that exceeds it is skipped (fail-open) — the session still gets the # remaining servers and all native tools. init_timeout_secs = 10 # Per remote tools/call budget (seconds). On timeout the model receives an # error tool result and the turn continues. call_timeout_secs = 60 # Maximum time to wait for an MCP runtime and its child process to shut down. # Expiry is logged and ACP teardown continues instead of orphaning daimonos. shutdown_timeout_secs = 5 # Upper bound on forwarded servers connected per session (bounds spawned # processes / connections). Extra servers are ignored. max_servers = 32 # Maximum initialize + tools/list handshakes in flight at once. Completed # servers are still registered in forwarded order so collision suffixes remain # deterministic. max_concurrent_connects = 8 # Upper bound on tools registered from any single server (bounds the exposed # tool set). Extra tools are ignored. max_tools_per_server = 128 # Fallback for unpatched Zed: when Zed forwards an EMPTY MCP server list at # session start (a cold-start race where its context-server store isn't # populated yet, and it never re-forwards to a live session), read Zed's own # `context_servers` settings directly and bridge those. Off by default because # it reads an external app's config and spawns that config's stdio servers, so # it must not fire for non-Zed ACP clients. Enable it only when running # daimonos as Zed's ACP agent on an unpatched Zed. Only triggers on an empty # forwarded list — servers Zed did forward are never overridden. zed_config_fallback = false # Path to Zed's settings.json for the fallback above. Empty/unset derives it # from $XDG_CONFIG_HOME/$HOME (~/.config/zed/settings.json). # zed_settings_path = "/home/you/.config/zed/settings.json" [discord] # Enable Discord integration tools. Defaults to false (off). enabled = false # Environment variable that holds the bot token. bot_token_env_var = "DISCORD_BOT_TOKEN" # Discord REST API base URL. api_base_url = "https://discord.com/api/v10" # Explicit allowlists. Empty means deny-by-default until configured. allow_guild_ids = [] allow_channel_ids = [] # Hard caps to keep responses token-bounded. max_messages_per_call = 100 max_message_chars = 4_000 max_response_chars = 32_000 # Keep write actions disabled unless explicitly enabled later. read_only_default = true # Retry behavior for Discord API 429 responses. rate_limit_max_retries = 2 rate_limit_max_sleep_ms = 10_000 [pipeline_cache] # The pipeline cache invalidates tool results when workspace files change. # It registers one inotify watch per source-tree directory (after filtering # out heavyweight dirs like .git/node_modules/target/dist/build/.venv via # .gitignore + a built-in skip list). # # Hard cap on directory watches the cache may register. On Linux this directly # bounds inotify usage so a single daimonos process can't exhaust # fs.inotify.max_user_watches. When the cap is reached the watcher stops # adding more dirs and logs a warning; later changes in unwatched dirs won't # invalidate the cache, but the process stays within its kernel-resource budget. max_watches = 8192 # Extra directory base names to skip on top of .gitignore + the built-in list. # Use this for project-specific build/cache dirs the watcher should ignore. # extra_ignore_dirs = ["bazel-out", "vendor"] extra_ignore_dirs = [] # Hard cap on the number of (tool_id, command) results held in memory. # When the cap is reached, the oldest entry is evicted to make room. Higher # values trade memory for cache hit rate; the default sits at the prior # hardcoded cap so existing operators see no behavioral change. max_entries = 1024 [kgl] # Tunables for the KGL (code+intent knowledge-graph) layer. KGL itself is gated # on the DAIMONOS_KGL_AUTOINDEX / DAIMONOS_KGL_OBSERVE environment variables; # these values govern its SQLite access and background file-watcher when on. # # SQLite busy-timeout (ms) applied to every KGL store connection. The autoindex # watcher writes on its own connection while kgl_query/kgl_assert use separate # ones; a non-zero timeout (with WAL journaling, enabled automatically) lets a # connection wait briefly for a lock instead of surfacing SQLITE_BUSY as a tool # error. busy_timeout_ms = 5000 # Hard cap on inotify watches the KGL file-watcher may register, so it never # exhausts fs.inotify.max_user_watches. max_watches = 4096 # Debounce window (seconds): coalesce change bursts into at most one rebuild. debounce_secs = 2 # Max task-matching defs `kgl_query orient` expands in one bundled call (each # adds its outgoing edges + dependents), bounding the orient response size. orient_max_matches = 12 # SQL LIMIT for kgl_query find results: caps how many nodes a broad LIKE query # can materialise from the graph in one call. find_max = 200 # Hard node cap for blast_radius BFS: stops dense call graphs from exhausting # CPU/memory during an unbounded transitive traversal. blast_radius_max = 500 # Directory base names never walked when detecting/indexing a substrate or # registering watches (build/vcs churn + our own store). skip_dirs = ["target", ".git", ".jj", "node_modules", ".kgl", "graphify-out"] [coordination] # Native agent-to-agent coordination ("agent mail", ADR-009). Lets multiple # daimonos processes on one workspace share identity, exchange directed # messages, and post advisory file reservations over a single dedicated SQLite # DB at ~/.daimonos/coordination/.db (WAL; opened directly by # each process; no broker, no HTTP). The DB lives OUTSIDE the target repo tree. # # Master switch. When false, coordination tools return a soft "disabled" error # and never touch disk (fail-open). enabled = true # SQLite busy-timeout (ms) for every coordination connection. WAL lets # concurrent processes proceed; the timeout absorbs brief write contention # instead of surfacing SQLITE_BUSY. busy_timeout_ms = 5000 # Optional override for the coordination base directory. Unset => the default # ~/.daimonos/coordination (the ~/.daimonos/ global-state convention). # db_dir = "~/.daimonos/coordination" # Default reservation TTL (seconds) when a caller omits one, and the hard # ceiling to which larger requests are clamped. default_reservation_ttl_secs = 3600 max_reservation_ttl_secs = 86400 # Default fetch_inbox page size when the caller omits `limit`. inbox_default_limit = 20 # Hard ceiling on fetch_inbox results: a caller-supplied `limit` is clamped to # this so a huge value cannot force an unbounded inbox read. inbox_max_limit = 500 # Hard cap on messages returned when reconstructing a thread — bounds the read # so a long/adversarial reply chain cannot blow up a response (no recursion). thread_max_messages = 500 [coordination.notifications] # Cooperative unread-mail delivery (#1063). Notices never interrupt an active # provider stream or tool call; model context is updated only at safe generation # boundaries. Message subjects/bodies are never injected automatically. enabled = true model_notice = true ui_notice = true # Idle ACP polling cadence for clearly labeled, visible Zed agent-message notifications. poll_interval_ms = 1500 [prompts] # Runtime overrides for the model-facing prompts (vikunja #974). Each key is a # path to a file whose contents REPLACE the built-in default embedded in the # binary. Leave a key unset (or empty) to use the embedded default. A path that # points at an unreadable file falls back to the embedded default and warns on # stderr. `~` expands to $HOME. The whole file is sent to the model verbatim, so # do NOT put comments inside a prompt file. See prompts/README.md for details. # # WARNING: these steer agent behavior, tool-use strategy, and output token cost. # Removing the execute_script guidance or the terse directive typically INCREASES # token usage. Override deliberately; prefer editing copies over the committed # defaults in prompts/ so you can compare against the baseline. # # agent_system = "~/.config/daimonos/prompts/agent_system.md" # mcp_instructions = "~/.config/daimonos/prompts/mcp_instructions.md" # kgl_hint = "~/.config/daimonos/prompts/kgl_hint.md" # summary = "~/.config/daimonos/prompts/summary.md" # tool_descriptions = "~/.config/daimonos/prompts/tool_descriptions.toml" # Note: `summary` is also overridable via the agent-env DAIMONOS_AGENT_SUMMARY_PROMPT, # which takes precedence over the path above. # `tool_descriptions` is a partial TOML overlay: omitted tools/variants keep # their embedded defaults; unknown tools and empty values are ignored. # # Agent/chat/ACP append optional user rules from # ~/.config/daimonos/agent-instructions.md (or $XDG_CONFIG_HOME/daimonos/...) # after the resolved agent_system prompt. Override that location per run with # `--agent-instructions /path/to/rules.md`. This does not apply to --mcp mode. # Registered tools for the tool runner. # Each tool gets a [tools.] section. # Tools with id "x07" use the built-in X07 plugin; all others use the generic CLI plugin. # [tools.x07] # bin = "/path/to/x07" # source_pattern = "**/*.x07.json" # manifest = "x07.json" # [tools.cargo] # bin = "cargo" # source_pattern = "**/*.rs" # manifest = "Cargo.toml" # --- agent (the `daimonos agent` CLI) --- # The agentic CLI's connection config is NOT in this TOML file. It is loaded # from a dotenv-style *agent env file* (vikunja #949), resolved as: # --agent-env > $DAIMONOS_AGENT_ENV > ~/.config/daimonos/agent.env # ALL values are required — `daimonos agent` errors out if the file or any of # them is missing (no built-in defaults). Example agent.env: # # DAIMONOS_AGENT_PROVIDER=openrouter # openrouter | anthropic # # model slug style differs by provider: # # openrouter -> "vendor/model": anthropic/claude-sonnet-4.6 | openai/gpt-4o-mini # # anthropic -> bare id: claude-opus-4-8 | claude-haiku-4-5 # DAIMONOS_AGENT_MODEL=anthropic/claude-sonnet-4.6 # DAIMONOS_AGENT_BASE_URL=https://openrouter.ai/api/v1 # DAIMONOS_AGENT_APPROVAL_MODE=interactive # auto | interactive | paranoid # DAIMONOS_AGENT_API_KEY=sk-... # DAIMONOS_AGENT_PROMPT_CACHE=off # optional; Anthropic only, default off # DAIMONOS_AGENT_ALLOWED_COMMANDS= # optional, comma-separated # DAIMONOS_AGENT_DENIED_COMMANDS=exec # optional, comma-separated