# OpenSquilla Configuration # Copy to opensquilla.toml and edit as needed: # cp opensquilla.toml.example opensquilla.toml # # Precedence: env vars > opensquilla.toml > defaults # Also searched: ~/.opensquilla/config.toml (global user config) # # Applying changes: edits made through the RPC/Web UI (config.set/patch/apply) # hot-apply immediately. Hand-edits to this file are only read at boot — run # `opensquilla gateway reload` (or restart) to pick them up. Channel, memory # embedding/retrieval-mode, and sandbox/permissions changes always need a full # `opensquilla gateway restart`; auth, host/port, file logging, and the search # provider are also read only at boot (reload updates the stored values but # the running components keep the boot-time ones). # Workspace/state defaults # Defaults: # workspace_dir = "~/.opensquilla/workspace" # state_dir = "~/.opensquilla/state" # memory.source = "workspace" # workspace_dir = "/path/to/workspace" # state_dir = "/path/to/state" # workspace_strict = true # true restricts read-side file tools to workspace # Realtime feedback timing. Heartbeats are non-persistent UI/CLI liveness # events while a run is active; stream idle timeout is the real upstream stall # detector. Keep browser grace above stream idle so server terminal errors win. # agent_stream_heartbeat_interval_seconds = 15.0 # agent_stream_idle_timeout_seconds = 180.0 # webui_stream_idle_grace_seconds = 210.0 # WebSocket per-connection outbound writer queue. When enabled (default), # each WS connection gets a bounded asyncio queue + dedicated writer task. # Producers enqueue and return immediately; slow clients trigger a fast # 1011 close instead of back-pressuring the gateway. Disable to fall back # to the legacy direct-`ws.send_text` path under a per-connection lock. # Kill switch affects new connections only; existing connections retain # their startup-time behavior. # Env override: OPENSQUILLA_WS_WRITER_QUEUE_ENABLED=true|false # ws_writer_queue_enabled = true # Env override: OPENSQUILLA_WS_WRITER_QUEUE_MAXSIZE=512 # ws_writer_queue_maxsize = 512 # Gateway debug file logging. Raw prompt/tool call capture is separate from # debug.log and standard diagnostics; it is opt-in through # OPENSQUILLA_TURN_CALL_LOG=1 or `opensquilla diagnostics on --raw`. # log_file_enabled = true # log_level = "DEBUG" # CRITICAL/FATAL/ERROR/WARNING/WARN/INFO/DEBUG/TRACE # log_file_max_bytes = 5000000 # log_file_backup_count = 3 # Developer diagnostics. debug is security-sensitive; keep it false in shared # deployments. diagnostics_enabled enables standard diagnostics at startup. # Raw turn-call capture remains a separate explicit runtime/env switch. # debug = false # diagnostics_enabled = false [control_ui] # Vue is the product UI. Use "legacy" only as a maintainer rollback fallback # for the frozen vanilla-JS frontend; changing this requires a gateway restart. # Env override: OPENSQUILLA_CONTROL_UI_FRONTEND=vue|legacy frontend = "vue" # Web search settings # Use DuckDuckGo for the no-key path, or configure Bocha, Brave, IQS, Tavily, or Exa. # search_provider = "duckduckgo" # "duckduckgo", "bocha", "brave", "iqs", "tavily", or "exa" # search_api_key = "" # one-time pasted key; prefer search_api_key_env # search_api_key_env = "" # BOCHA_SEARCH_API_KEY, BRAVE_SEARCH_API_KEY, IQS_SEARCH_API_KEY, TAVILY_API_KEY, or EXA_API_KEY # search_max_results = 5 # search_proxy = "" # e.g. "http://127.0.0.1:7890" # search_use_env_proxy = false # true = allow HTTP_PROXY/HTTPS_PROXY if search_proxy is empty # search_fallback_policy = "off" # "off" or "network" retry via DuckDuckGo # search_diagnostics = false # true = include provider-attempt diagnostics # Tool safety settings. # Some local proxy/fake-IP DNS setups resolve public domains through RFC 2544 # test-network addresses (198.18.0.0/15). Leave this empty unless you trust # that fake-IP resolver; other private/internal ranges remain hard-blocked. # [tools] # trusted_fake_ip_cidrs = ["198.18.0.0/15"] # Attachment ingestion. Any file type is accepted by default: rendered # families (images, PDF, text, Office, email) are extracted or inlined for # the model; everything else stages as an opaque agent-workspace file whose # bytes are never parsed or inlined into a prompt. # Env prefix: OPENSQUILLA_ATTACHMENTS_ # [attachments] # accept_opaque = true # false = legacy rendered-types-only admission # opaque_max_bytes = 31457280 # 30 MiB per-file ceiling for opaque types # upload_store_max_total_bytes = 314572800 # 300 MiB staged-upload RAM ceiling # # (raise-only; <=0 = default) # workspace_attachment_disk_budget_bytes = 1073741824 # 1 GiB workspace copies # # (<=0 = unbounded) # persist_transcripts = true # transcript_disk_budget_bytes = 2147483648 # artifact_max_bytes = 31457280 # artifact_disk_budget_bytes = 536870912 [llm] provider = "tokenrhythm" model = "deepseek-v4-pro" # api_key = "" # TokenRhythm API key; env TOKENRHYTHM_API_KEY also works base_url = "https://tokenrhythm.studio/v1" # proxy = "" # e.g. http://127.0.0.1:7890 # Provider quick reference. Support levels marked compat_mock_verified are # verified by local mocked-contract tests, not by live vendor calls. # # provider env var default base_url # tokenrhythm TOKENRHYTHM_API_KEY https://tokenrhythm.studio/v1 # openrouter OPENROUTER_API_KEY https://openrouter.ai/api/v1 # openai OPENAI_API_KEY https://api.openai.com/v1 # openai_responses OPENAI_API_KEY https://api.openai.com/v1 (native Responses-API shape; chat+responses) # anthropic ANTHROPIC_API_KEY https://api.anthropic.com # ollama none http://localhost:11434 # deepseek DEEPSEEK_API_KEY https://api.deepseek.com # gemini GEMINI_API_KEY https://generativelanguage.googleapis.com/v1beta/openai # dashscope DASHSCOPE_API_KEY https://dashscope.aliyuncs.com/compatible-mode/v1 # moonshot MOONSHOT_API_KEY https://api.moonshot.ai/v1 # mistral MISTRAL_API_KEY https://api.mistral.ai/v1 # groq GROQ_API_KEY https://api.groq.com/openai/v1 # zhipu ZAI_API_KEY https://open.bigmodel.cn/api/paas/v4 # siliconflow SILICONFLOW_API_KEY https://api.siliconflow.cn/v1 # volcengine VOLCENGINE_API_KEY https://ark.cn-beijing.volces.com/api/v3 # volcengine_coding_plan VOLCENGINE_API_KEY https://ark.cn-beijing.volces.com/api/coding/v3 # byteplus BYTEPLUS_API_KEY https://ark.ap-southeast.bytepluses.com/api/v3 # vllm none explicit base_url required # custom CUSTOM_LLM_API_KEY (optional) explicit base_url required # lm_studio none http://localhost:1234/v1 # ovms none http://localhost:8000/v3 # qianfan QIANFAN_API_KEY https://qianfan.baidubce.com/v2 # aihubmix AIHUBMIX_API_KEY https://aihubmix.com/v1 # minimax MINIMAX_API_KEY https://api.minimaxi.com/anthropic (Anthropic-shape backend) # minimax_openai MINIMAX_API_KEY https://api.minimax.io/v1 (OpenAI-compatible variant) # azure provider-specific unsupported_for_A # # Example direct DeepSeek config: # provider = "deepseek" # model = "deepseek-v4-flash" # api_key = "" # env DEEPSEEK_API_KEY also works # base_url = "https://api.deepseek.com" # # Example direct TokenRhythm config (aggregator: DeepSeek/GLM/MiniMax/Kimi/ # MiMo/Qwen families on one key). Every served model streams # reasoning_content, and reasoning tokens count against max_tokens — keep # max_tokens = 0 (auto) rather than setting small caps: # provider = "tokenrhythm" # model = "deepseek-v4-pro" # api_key = "" # env TOKENRHYTHM_API_KEY also works # base_url = "https://tokenrhythm.studio/v1" # # Example self-hosted / custom OpenAI-compatible endpoint (vLLM, SGLang, TGI, # llama.cpp server, or any proxy speaking the Chat Completions API): # provider = "custom" # base_url = "http://127.0.0.1:8000/v1" # required — no default endpoint # model = "qwen3-32b-awq" # # api_key is optional: set env CUSTOM_LLM_API_KEY (or api_key here) only if # # your endpoint enforces one. Declare the endpoint's real context window via # # [models.custom."qwen3-32b-awq"] — see the per-model overrides section below. # Pin each model to its native upstream provider on OpenRouter. # Sends provider.only=[slug] + allow_fallbacks=true per request. [llm.provider_routing] "anthropic/claude-opus-4.8" = "anthropic" "anthropic/claude-sonnet-4.6" = "anthropic" "deepseek/deepseek-v4-flash" = "deepseek" "google/gemini-3.5-flash" = "google" "moonshotai/kimi-k2.6" = "moonshotai" "openai/gpt-5.4-mini" = "openai" "openai/gpt-5.5" = "openai" "qwen/qwen3-coder-plus" = "qwen" "x-ai/grok-4.3" = "x-ai" "z-ai/glm-4.6" = "z-ai" "z-ai/glm-5.1" = "z-ai" "z-ai/glm-5.2" = "z-ai" # LLM ensemble routing — the DEFAULT routing surface. # A fresh install ships enabled = true with selection_mode = # "static_openrouter_b5": each turn fans out to a fixed five-member # OpenRouter proposer set and fuses their candidates into one answer. # # Upgrade note (0.5.0rc1 → current): # * static_openrouter_b5 is the default for FRESH installs only. Configs # saved by earlier versions keep their stored `enabled` value. # * Enabling the ensemble now selects "static_openrouter_b5", where # 0.5.0rc1's enable toggle gave "router_dynamic". Set # selection_mode = "router_dynamic" to restore the rc1 behavior. # # Credential requirement: static_openrouter_b5 runs its members on # OpenRouter, so it needs a resolvable OpenRouter credential — the [llm] # api_key when the active provider is "openrouter", or OPENROUTER_API_KEY # in the environment otherwise. Without one the ensemble is inactive and # every turn falls back to the single configured provider — # `opensquilla doctor` reports this as the llm_ensemble finding # "LLM ensemble is enabled but cannot run". # "static_tokenrhythm_b5" is the TokenRhythm mirror of the same lineup # (deepseek-v4-pro, glm-5.2, kimi-k2.7-code, qwen3.7-max + glm-5.2 # aggregator) and needs a TokenRhythm credential the same way (the [llm] # api_key when the active provider is "tokenrhythm", or # TOKENRHYTHM_API_KEY in the environment). # # The keys below are also writable at runtime via the Web UI / the # `onboarding.ensemble.configure` RPC; omitted keys keep their current # values. Changes take effect on the next turn without a gateway restart. [llm_ensemble] enabled = true # "static_openrouter_b5": fixed five-member OpenRouter proposer set (default). # "static_tokenrhythm_b5": the same lineup served through TokenRhythm. # "custom_b5": an explicit user-authored lineup from [[llm_ensemble.candidates]] # — 2-6 enabled proposer rows plus at most one role = "aggregator" row (the # member that fuses drafts into the final answer; omitted = the current chat # model fuses). Proposer roles ("primary", "contrast", "fast_check", # "critic") are advisory labels shown in the UI and decision trace. # "router_dynamic": legacy automatic selection; kept readable for existing # configs but no longer offered in the Web UI. selection_mode = "static_openrouter_b5" # Explicit lineup for custom_b5 (ignored by the static profiles): # [[llm_ensemble.candidates]] # provider = "volcengine" # model = "doubao-2.0-pro" # role = "primary" # [[llm_ensemble.candidates]] # provider = "volcengine" # model = "deepseek-v4-pro" # role = "aggregator" # # Candidate pool for the legacy router_dynamic mode (ignored otherwise). model_options = [ "deepseek/deepseek-v4-pro", "z-ai/glm-5.2", "qwen/qwen3.7-plus", "deepseek/deepseek-v4-flash", "qwen/qwen3.7-max", "moonshotai/kimi-k2.6", "moonshotai/kimi-k2.7-code", "minimax/minimax-m3", ] # Minimum proposers that must succeed before aggregation (>= 1). The fixed # lineups (static profiles and custom_b5) replace the untouched default of 1 # with their own quorum at runtime: 3-of-4 for the static profiles, N-1 for a # custom lineup of N proposers. min_successful_proposers = 1 # When aggregation cannot proceed: "fallback_single" (single-provider turn) or "error". all_failed_policy = "fallback_single" # Advanced knobs — TOML-only (not on the configure RPC surface). Defaults: # mode = "b5_fusion" # only supported ensemble mode # proposer_tools = false # allow tool calls inside proposer turns # candidate_max_chars = 24000 # per-candidate transcript budget (0 = unlimited) # proposer_timeout_seconds = 3600.0 # aggregator_timeout_seconds = 3600.0 # shuffle_candidates = true # shuffle candidate order before aggregation # record_candidates = false # persist per-proposer candidates for replay/debug # Model metadata catalog (added in 0.5.x; schema only — wiring lands in a # follow-up release). Offline-first: with refresh = "off" (the default) the # gateway never fetches model metadata (context windows, output caps, pricing, # capability flags) from the network. Today's OpenRouter live model-list fetch # is a separate, existing mechanism that this flag does NOT govern yet. # Downgrade note: model_catalog and the models tables below are top-level # sections written to disk only when a config persist re-materializes the full # file. 0.5.0rc1 and older reject unknown top-level keys, so delete both # sections before downgrading (same class as config_version; see the 0.5.x # release notes). # [model_catalog] # refresh = "off" # "off" | "startup" (fetch once at gateway boot) # pin_path = "" # local JSON/TOML catalog override for air-gapped deploys # stale_after_days = 45 # advisory doctor threshold for metadata age (days) # Per-model metadata overrides (added in 0.5.x), keyed # [models..""]. Quote model ids that contain dots or # slashes. Exact ids only — globs are not supported in user config. Every # field is optional; anything unset keeps resolving from catalog/registry # metadata as before. # # Self-hosted example: a vLLM endpoint declaring its real context window so # context budgeting and compaction stop assuming a conservative default: # [models.vllm."qwen3-32b-awq"] # context_window = 131072 # max_output_tokens = 8192 # supports_tools = true # # The generic `custom` provider (any self-hosted OpenAI-compatible endpoint; # see the [llm] example above) uses the same override table, keyed by its # served model id: # [models.custom."qwen3-32b-awq"] # context_window = 131072 # max_output_tokens = 8192 # supports_tools = true # # [models.openrouter."z-ai/glm-5.2"] # # reasoning_format: openrouter | openai | deepseek | gemini | zai | # # dashscope | moonshot | volcengine | none # reasoning_format = "openrouter" # supports_reasoning = true # input_cost_per_mtok = 0.5 # USD per million input tokens (example value) # output_cost_per_mtok = 2.0 # USD per million output tokens (example value) # cache_read_cost_per_mtok = 0.05 # USD per million cached-prompt-read tokens (example value) # cache_write_cost_per_mtok = 0.6 # USD per million cached-prompt-write tokens (example value) # thinking_level_map = { high = "high", medium = "medium" } [memory] # "workspace" stores MEMORY.md and memory/*.md under workspace_dir. # SQLite indexes still live under state_dir. source = "workspace" # Long-term memory vector indexing defaults to provider="auto": it tries the # bundled local BGE-small ONNX model first, then a memory-specific remote key # if one is configured, then FTS-only. Chat LLM/OpenRouter credentials are not # used for memory embeddings unless explicitly configured below. # retrieval_mode = "hybrid" # "hybrid" | "fts_only" # # [memory.embedding] # provider = "auto" # "auto" | "none" | "local" | "openai" | "openai-compatible" | "ollama" # # [memory.embedding.local] # onnx_dir = "" # optional; empty uses bundled BGE; supports absolute, ~, or process-relative paths # # [memory.embedding.remote] # api_key = "" # base_url = "https://api.openai.com/v1" # model = "text-embedding-3-small" # headers = {} # # [memory.embedding.ollama] # base_url = "http://localhost:11434" # model = "nomic-embed-text" # Turn capture writes memory/archive/** audit transcripts by default. # These archives are not searchable memory unless explicitly opted in. # capture_user = true # capture_assistant = false # index_captured_turns = false # capture_excluded_run_kinds = ["recall", "session_recall"] # capture_excluded_provenance_kinds = ["recall", "tool_result", "memory_injected"] [memory.dream] # Dream consolidation is safety-first by default. Background scheduling and # curated MEMORY.md writes require explicit opt-in. enabled = false preview_mode = true auto_schedule = false # interval_h = 24 # cron = "0 3 * * *" [skills] # Experimental skill relevance filtering. Default is off; deterministic # skill gating still runs for visibility, platform, and tool availability. filter_enabled = false filter_top_k = 5 max_skills_prompt_chars = 8000 # injection_mode = "system" # "system", "user_context", or "user_message" # The default lexical strategy is dependency-free. "semantic" and "hybrid" # are legacy experimental modes that need the bundled BGE ONNX backend # (onnxruntime + transformers tokenizer + the int8 ONNX export shipped under # squilla_router/models/v4.2_phase3_inference/bge_onnx/). Install via # `uv sync --extra recommended`. If unavailable, they degrade to lexical-only. # filter_strategy = "lexical" # "lexical" | "semantic" | "hybrid" # filter_lexical_top_n = 20 # filter_semantic_top_n = 20 # filter_rrf_k = 60 # filter_embedding_model = "BAAI/bge-small-zh-v1.5" [task_runtime] # Server-side agent turn queue. Same-session tasks are serialized; # different sessions can run concurrently up to this limit. max_concurrency = 4 # Waiting tasks per session before new follow-up work is rejected. max_pending_per_session = 64 [squilla_router] enabled = true # V4 model router auto_thinking = true rollout_phase = "full" strategy = "v4_phase3" # Optional provider tier profile. Leave unset to preserve the built-in # OpenRouter defaults below. If set, it must match [llm].provider; the router # does not switch providers at runtime. # tier_profile = "dashscope" # openrouter | dashscope | deepseek | gemini | volcengine | openai | zhipu | moonshot # Bundled router prerequisites: # 1. install with `uv sync --extra model-router` or `uv sync --extra recommended` # 2. hydrate model assets via: # git lfs pull --include="src/opensquilla/squilla_router/models/**" default_tier = "c1" confidence_threshold = 0.5 v4_use_aux_head = true kv_cache_anti_downgrade_enabled = true kv_cache_anti_downgrade_window_seconds = 600 complaint_upgrade_enabled = true complaint_upgrade_steps = 1 complaint_upgrade_max_chars = 160 require_router_runtime = true estimated_output_savings_pct = 0.03 upgrade_to_c3_compaction_enabled = true # What routing does when a tier names a provider other than [llm].provider # while cross_provider_tiers is off. "route" (default) preserves the historical # behavior: the mismatch is logged but the tier's model runs on the active # provider's credentials. "veto" instead rebinds the turn to the nearest tier # that executes on the active provider (or the default tier). # tier_provider_mismatch = "route" # route | veto # TokenRhythm serves every model in reasoning-streaming mode and rejects # thinking-toggle request fields, so these tiers set no thinking_level. [squilla_router.tiers.c0] provider = "tokenrhythm" model = "deepseek-v4-flash" description = "Fast DeepSeek V4 Flash route for trivial chat, short rewrites, extraction, and low-risk simple Q&A" supports_image = false [squilla_router.tiers.c1] provider = "tokenrhythm" model = "deepseek-v4-pro" description = "Default balanced text model for normal agent work, coding assistance, debugging, and moderate analysis" supports_image = false [squilla_router.tiers.c2] provider = "tokenrhythm" model = "kimi-k2.7-code" description = "Stronger Kimi K2.7 Code route for multi-step coding, structured reasoning, larger context synthesis, and harder analysis" supports_image = false [squilla_router.tiers.c3] provider = "tokenrhythm" model = "glm-5.2" description = "Highest-tier GLM 5.2 route for difficult planning, deep review, complex debugging, and high-stakes synthesis" supports_image = false [squilla_router.tiers.image_model] provider = "tokenrhythm" model = "kimi-k2.6" description = "Image model: vision-capable route for user-supplied image attachments, screenshots, diagrams, and visual question answering" supports_image = true image_only = true # Example: DashScope profile requires: # [llm] # provider = "dashscope" # model = "qwen3.6-plus" # api_key = "${DASHSCOPE_API_KEY}" # base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1" # # [squilla_router] # tier_profile = "dashscope" # ───────────────────────────────────────────────────────────────────────────── # Tool policy # ───────────────────────────────────────────────────────────────────────────── # # Optional coding/repo-coding narrowed tool surface. Keep commented unless you # are intentionally narrowing the tool surface for a scripted run. # # [tools] # profile = "coding" # also_allow = ["retrieve_tool_result"] # deny = ["execute_code", "background_process", "process"] # file_edit_requires_fresh_read = true # file_edit_flexible_recovery = true # default; records used/rejected recovery events # workspace_write_deny_globs = [] [agent_token_saving] # Project fresh tool results with the built-in tokenjuice reducer before they # are fed back into the model. Raw tool responses remain available through the # tool-result store when configured by the gateway runtime. tool_result_projection_max_inline_chars = 60000 tool_result_store_max_bytes = 8388608 tool_result_store_disk_budget_bytes = 268435456 tool_result_store_retention_seconds = 604800 [compaction] enabled = true # model = "" # None = use session model # timeout_seconds = 30.0 [sandbox] # Per-command ephemeral sandbox + security grading. # # sandbox on -> processes run under namespace/profile isolation # off -> host execution is allowed (logs a WARNING per run) # security_grading on -> action_kind drives the selected SecurityLevel # off -> a fixed STANDARD policy is used, no approval flow # # Both default to false for the out-of-box bypass posture. Turning sandbox off # while grading stays on is silently coerced to grading=false with a warning. sandbox = false security_grading = false # default_level = "STANDARD" # DISABLED | STANDARD | STRICT | LOCKED # backend = "auto" # auto | bubblewrap | seatbelt | windows_default | noop # allow_legacy_mode = false # required for default_level = DISABLED # network_default = "proxy_allowlist" # none | proxy_allowlist # denial_threshold = 3 # pause autonomous runs after N denials # extra_ro_mounts = [] # extra_rw_mounts = [] # cpu_seconds = 30 # memory_mb = 1024 # wall_seconds = 60 [permissions] # Owner/operator default permission mode. The shipped default is "bypass", which # runs local/operator tool execution on the host while still blocking sensitive # paths. Use `opensquilla sandbox on|bypass|full|reset` to update this together # with the sandbox section. default_mode = "bypass" # off | on | bypass | full # [auth] # mode = "none" # none | token | password # token = "" # [cors] # Cross-origin browser access to the gateway HTTP API. Off by default: the # Web UI is served same-origin from the gateway itself and non-browser # clients (CLI, desktop app, curl) never need CORS. Only list origins here # if you host a separate frontend on another origin; avoid "*", especially # together with allow_credentials. # allowed_origins = [] # allow_credentials = true # [channels] # [[channels.channels]] # name = "my-slack" # type = "slack" # token = "xoxb-..." # connection_mode = "socket" # socket = no public URL; webhook = Events API # app_token = "xapp-..." # required for Slack Socket Mode # signing_secret = "" # required for Slack webhook mode # slack_channel_id = "C12345" # reply_in_thread = false # ───────────────────────────────────────────────────────────────────────────── # Meta-Skill subsystem # ───────────────────────────────────────────────────────────────────────────── [meta_skill] # Master gate. When false the meta-skill subsystem is fully off: meta-skills are # not injected into prompts, meta_invoke is not surfaced, and the /meta command # refuses (both list and run). Default: true. # enabled = true # Automatic activation. When false (the default), meta-skills are MANUAL-ONLY: # no system-prompt guidance, no keyword/semantic auto-trigger, meta_invoke is not # offered for automatic invocation, and meta-skills are hidden from automatic # skill listings. # They run only via the explicit `/meta ` command. Set true to restore the # previous automatic behavior. Default: false. # auto_trigger = false [meta_skill.persistence] # Whether to write meta-skill execution traces to SQLite (G4 traceable audit). # Disabling drops the writer to no-op; the CLI `skills meta runs ...` will # report no rows. Default: true. # enabled = true # How long (seconds) a 'running' row may live before boot cleanup marks it # 'failed' as an orphan from a crashed prior process. Only applies to rows # whose owner_pid differs from the current process pid. Default: 3600 (1h). # orphan_cleanup_age_seconds = 3600 # ───────────────────────────────────────────────────────────────────────────── # Meta-Skill auto-propose: unattended synthesis from co-occurrence patterns # # Two independent triggers feed the same library function # (skills.creator.auto_propose): # * `enabled` — schedule a recurring cron job (Path 1) # * `on_dream_complete` — piggyback on memory-consolidation dreams (Path 2) # Both default OFF. Operators turn them on after reviewing how # meta-skill-creator's gated output looks once. # ───────────────────────────────────────────────────────────────────────────── [meta_skill.auto_propose] # enabled = false # Path 1 cron toggle # cron = "0 5 * * *" # 5-field local-time cron; daily 05:00 # window_days = 30 # log-history window for co-occurrence # min_freq = 3 # drop chains observed fewer than N times # top_k = 5 # consider at most N patterns per fire # on_dream_complete = false # Path 2 dream-hook toggle (independent) # agent_ids = ["main"] # empty/omitted = all configured agents