server: host: 127.0.0.1 port: 9000 # ── Global timezone ───────────────────────────────────────────────────────────── # IANA timezone name applied globally to: # - cron expression evaluation (next_run_at computation) # - the date/time string injected into the LLM context each turn # When omitted, the server's local system timezone is used. # Examples: Europe/Rome, Europe/London, America/New_York, Asia/Tokyo # # timezone: Europe/Rome # ──────────────────────────────────────────────────────────────────────────────── web: static_dir: ./web # ── Connector marketplace ────────────────────────────────────────────────────── # The feed of vetted connectors the admin browses under Connectors → Marketplace. # It is *consultative*: the feed proposes, the admin installs into the local # catalog, and only then can a connector be enabled globally or activated by a # user. The trust anchor stays on this box. # # Point it at a self-hosted mirror or a local copy to run fully offline — the feed # is plain static files (`connectors.json` + `/connector.json`). marketplace: url: https://connectors.skaldagent.net # The database lives at ./database/system.db — fixed, not configurable. # ── LLM clients ──────────────────────────────────────────────────────────────── # LLM clients (providers, models, API keys, strength) are configured # via the web app and stored in the database — not in this file. # ─────────────────────────────────────────────────────────────────────────────── llm: # ── History window (DISABLED by default) ──────────────────────────────────── # Hard cap on the number of history messages sent to the LLM, applied as a # sliding tail window: past the cap, the oldest messages are dropped. # # Off by default, for two reasons: # 1. Cache. Once history exceeds the cap, every turn shifts the window's # start, so the prompt prefix changes on every single request and the # provider's prompt cache (Anthropic breakpoints, OpenAI automatic prefix # caching) misses every time. Append-only history keeps the prefix stable # for the whole conversation. # 2. Memory. The window drops messages with no summary standing in for them, # so the assistant silently forgets. `/compact` replaces them with a # summary instead. # # With this off and automatic compaction off (the shipped default), the context # grows until the model's own limit — use `/compact` to summarise it. # Ignored when automatic compaction is enabled (see `compaction` below). # # max_history_messages: 30 # ─────────────────────────────────────────────────────────────────────────── max_tool_rounds: 100 # Max synchronous sub-agents dispatched concurrently when the LLM emits a # homogeneous batch (≥2) of sub-agent calls (execute_task mode=sync / # execute_subtask) in a single response. Bounds fan-out to avoid provider # rate-limit storms. Omit for the default (4); set to 1 to force sequential. max_parallel_subagents: 4 # Injects "Current date and time: Sunday 2026-08-02 17:00 +02:00 (Europe/Rome)" # as the last system message of each request. The time is always truncated to # the hour and the block tells the model so, pointing it at `date` when it # needs the exact minute — there is no rounding setting. datetime: enabled: true # ── Tool result size limit ────────────────────────────────────────────────── # When set, tool results from *previous* turns that exceed this character # count are replaced (at context-build time) with a short placeholder: # "[Tool response hidden: N chars. Call the tool again if you need it.]" # The original result is always preserved in the database and shown in the # frontend; only what the LLM receives in subsequent turns is affected. # The current turn always sees full results regardless of this limit. # Omit or comment out to disable (no limit). max_tool_result_chars: 10000 # ─────────────────────────────────────────────────────────────────────────── # ── Context compaction (AUTOMATIC pass disabled by default) ───────────────── # Compaction summarises old history into a single block, persisted to the DB # and injected at the start of subsequent turns in place of the messages it # covers, keeping the last `keep_recent` raw messages for immediate context. # # The `/compact` command works ALWAYS and needs nothing here — this whole # section is optional and only tunes it. # # `threshold_tokens` is what arms the AUTOMATIC pass: set it, and history is # compacted on its own once the previous turn exceeded that many input tokens. # It is COMMENTED OUT by default: every compaction rewrites the prompt prefix # and so costs a prompt-cache miss, and doing it unprompted trades away context # the user may still need. Compact manually with `/compact` for now. # (Future: an automatic pass triggered by the model's own context window rather # than by a hand-tuned token count.) # # `strength` controls which LLM is picked for summary generation via the AUTO # selector (same strength levels used for agent assignment). Compaction is a # simple writing task — `low` or `average` is usually sufficient. # Omit `strength` to use whatever AUTO picks. # NOTE: the Settings page has an instance-wide "Compaction model" picker # (registry config key `compaction_model`) — when set, it wins over this # `strength` fallback and needs no restart. # # When the LLM provider does not report token usage (e.g. some LM Studio # setups), a rough estimate (total chars / 4) is used as a fallback. # # compaction: # threshold_tokens: 30000 # arms the automatic pass, above this many input tokens # keep_recent: 6 # raw messages kept outside the summary # strength: low # LLM strength for summary generation # ─────────────────────────────────────────────────────────────────────────── # ── Date/time injection ───────────────────────────────────────────────────── # Configured above as `datetime`. The only setting is `enabled`. # The injected time is always truncated to the hour: not for the prompt cache # (the block is the LAST system message, so the cached prefix never changes # whatever the timestamp says) but because a second-precision stamp reads as # exact to the model long after it stopped being true. The block states the # granularity and tells the agent to run `date` when it needs the minute. # enabled: true # set to false to disable injection entirely # ─────────────────────────────────────────────────────────────────────────── # ── LLM request/response log ──────────────────────────────────────────────── # Logs every chat_with_tools call to the `llm_requests` table. # Captures the full HTTP request body, response body, headers (api-key redacted), # token counts, and round-trip duration. # # WARNING: disabling `enabled` also disables the home-page LLM statistics. # # What to save (all default to true): # request_payload_save — full request JSON (can be hundreds of KB per call) # response_payload_save — full response JSON # request_header_save — HTTP request headers (api-key always redacted) # response_header_save — HTTP response headers # # Cleanup policy (all optional — omit to keep data forever): # cleanup_request_payload_after — set request_json = '' after N days # cleanup_response_payload_after — set response_json = NULL after N days # cleanup_headers_after — null out both header columns after N days # cleanup_rows_after — physically delete rows after N days # # The cleaner runs 1 minute after startup, then every 12 hours. # requests_log: enabled: true request_payload_save: false response_payload_save: true request_header_save: true response_header_save: true cleanup_request_payload_after: 7 cleanup_response_payload_after: 14 cleanup_headers_after: 30 cleanup_rows_after: 90 # ─────────────────────────────────────────────────────────────────────────── # ── Event triage (background event processor) ────────────────────────────────── # Runs periodically to process pending MCP events (email, calendar, WhatsApp) and # decide whether to surface a notification to the user. # # NOTE: top-level, NOT under `llm:` — nesting it there parses fine and is then # silently ignored. # # interval_secs — how often it runs (default: 900 = 15 minutes) # batch_size — max events processed per pass (default: 50) # # event_triage: # interval_secs: 900 # batch_size: 50 # ───────────────────────────────────────────────────────────────────────────────