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

# The database lives at ./database/system.db — fixed, not configurable.


# ── LLM clients ────────────────────────────────────────────────────────────────
# LLM clients (providers, models, API keys, strength, scope) are configured
# via the web app and stored in the database — not in this file.
# ───────────────────────────────────────────────────────────────────────────────
llm:
  # Maximum number of messages kept in the LLM context window.
  # NOTE: this setting is ignored when `compaction` is enabled — in that case
  # the compactor manages the token budget and truncating by count would silently
  # discard history that should be summarised instead.  With compaction active
  # this field has no effect; without it, this is the only context-size guard.
  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
  datetime:
    enabled: true
    round_minutes: 60  # Help with KV cache (instead of 10:54, it will pass 10:50 to the LLM)

  # ── 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 ──────────────────────────────────────────────────────
  # When enabled, the conversation history is automatically summarised when the
  # previous turn consumed more than `threshold_tokens` input tokens.
  # The summary is persisted to the DB and injected at the start of subsequent
  # turns, replacing the old messages while preserving the last `keep_recent`
  # raw messages for immediate context.
  #
  # `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.
  #
  # 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  # trigger above this many input tokens
  #   keep_recent: 6            # raw messages kept outside the summary
  #   strength: low             # LLM strength for summary generation
  # ───────────────────────────────────────────────────────────────────────────

  # ── TIC background event processor ─────────────────────────────────────────
  # TIC runs periodically to process pending MCP events (email, calendar, WhatsApp)
  # and decide whether to surface a notification to the user.
  #
  # interval_secs  — how often TIC runs (default: 900 = 15 minutes)
  # batch_size     — max events processed per tick (default: 50)
  #
  # tic:
  #   interval_secs: 900
  #   batch_size: 50
  # ───────────────────────────────────────────────────────────────────────────

  # ── Date/time injection ─────────────────────────────────────────────────────
  # Controls how the current date/time is injected into each LLM request.
  # By default the exact timestamp is used, which changes every second and
  # prevents the dynamic tail from being KV-cached across requests.
  #
  # datetime:
  #   enabled: true          # set to false to disable injection entirely
  #   round_minutes: 10      # round down to nearest N minutes (e.g. 10:56 → 10:50)
  #                          # keeps the string stable for up to N minutes
  # ───────────────────────────────────────────────────────────────────────────

  # ── 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
  # ───────────────────────────────────────────────────────────────────────────

