mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-19 02:02:00 +00:00
494 lines
30 KiB
Python
494 lines
30 KiB
Python
"""Ouroboros — the settings vocabulary.
|
|
|
|
What settings exist, the values a fresh install ships, which keys a release has
|
|
retired, and which keys never travel between disk and the environment. Data and
|
|
derivations only: nothing here reads or writes settings.json.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from ouroboros.update_channels import UPDATE_SETTINGS_DEFAULTS
|
|
|
|
FINALIZATION_GRACE_DEFAULT_SEC = 120
|
|
# Owner finalization outer cap starts at the stop request; grace starts at control delivery
|
|
# (the loop's mailbox drain). No summary by this cap -> honest custody cancel.
|
|
OWNER_STOP_OUTER_CAP_SEC = 600
|
|
# Cadence for intrinsic self-pacing checkpoints when a task has NO deadline_at (headless benchmark runs). Advisory only — surfaces elapsed/rounds/cost for self-pacing; 0 disables.
|
|
PACING_INTERVAL_DEFAULT_SEC = 600
|
|
# Supervisor-loop liveness deadline (WS3, v6.34.0): a watchdog thread flags the main supervisor loop STALLED if it has not ticked within this many seconds (healthy tick ~0.5s, real wedges only). 0 disables.
|
|
SUPERVISOR_LIVENESS_DEADLINE_DEFAULT_SEC = 90
|
|
|
|
# Caller wait windows, not process lifetime or permission to terminate a startup.
|
|
CLAUDEXOR_STARTUP_WAIT_SEC = 20.0
|
|
CLAUDEXOR_STARTUP_POLL_SEC = 0.25
|
|
CLAUDEXOR_ADMISSION_WAIT_SEC = 5.0
|
|
CLAUDEXOR_ADMISSION_POLL_SEC = 0.15
|
|
|
|
|
|
# Shipped router profile. Keeping the root-loop role policy beside the direct
|
|
# provider profiles gives onboarding, runtime defaults, and tests one vocabulary
|
|
# instead of repeating model ids across those surfaces.
|
|
OPENROUTER_DEFAULTS = {
|
|
"main": "google/gemini-3.8-flash",
|
|
"heavy": "",
|
|
"light": "openai/gpt-5.6-luna",
|
|
"vision": "",
|
|
"consciousness": "",
|
|
"fallback": "openai/gpt-5.6-luna",
|
|
# Plain Sol, not the `-pro` routing slug: a pro-mode call bills the prompt
|
|
# three to four times over (parallel test-time compute), so the density
|
|
# witness it records can never admit a repository-sized pack, and every
|
|
# packed review costs that multiple. Same id the direct-OpenAI slot ships.
|
|
"deep_self_review": "openai/gpt-5.6-sol",
|
|
}
|
|
|
|
OPENROUTER_REVIEW_DEFAULTS = {
|
|
"triad": (
|
|
"google/gemini-3.8-flash",
|
|
"openai/gpt-5.6-terra",
|
|
"anthropic/claude-opus-5",
|
|
),
|
|
"scope": ("openai/gpt-5.6-terra",),
|
|
# Routed catalog id (the retired Claude-SDK spelling migrated same-model);
|
|
# without provider credentials the advisory gate records an audited bypass.
|
|
"advisory": "anthropic/claude-sonnet-5",
|
|
}
|
|
|
|
|
|
# Settings defaults
|
|
SETTINGS_DEFAULTS = {**UPDATE_SETTINGS_DEFAULTS,
|
|
"OPENROUTER_API_KEY": "",
|
|
"OPENAI_API_KEY": "",
|
|
"OPENAI_BASE_URL": "",
|
|
"OPENAI_COMPATIBLE_API_KEY": "",
|
|
"OPENAI_COMPATIBLE_BASE_URL": "",
|
|
"CLOUDRU_FOUNDATION_MODELS_API_KEY": "",
|
|
"CLOUDRU_FOUNDATION_MODELS_BASE_URL": "https://foundation-models.api.cloud.ru/v1",
|
|
"GIGACHAT_CREDENTIALS": "",
|
|
"GIGACHAT_USER": "",
|
|
"GIGACHAT_PASSWORD": "",
|
|
"GIGACHAT_SCOPE": "GIGACHAT_API_PERS",
|
|
"GIGACHAT_BASE_URL": "https://api.giga.chat/v1",
|
|
"GIGACHAT_VERIFY_SSL_CERTS": "true",
|
|
"GIGACHAT_PROFANITY_CHECK": "",
|
|
"ANTHROPIC_API_KEY": "",
|
|
"MINIMAX_API_KEY": "",
|
|
"MINIMAX_REGION": "",
|
|
"DEEPSEEK_API_KEY": "",
|
|
"OUROBOROS_NETWORK_PASSWORD": "",
|
|
"OUROBOROS_SERVER_HOST": "127.0.0.1",
|
|
"OUROBOROS_HOST_SERVICE_PORT": 8767,
|
|
"OUROBOROS_MODEL": OPENROUTER_DEFAULTS["main"],
|
|
# Role-owned choices; empty account and zero window mean Auto, not healthy/known.
|
|
"OUROBOROS_MODEL_ACCOUNTS": "{}",
|
|
"OUROBOROS_MODEL_CONTEXT_WINDOWS": "{}",
|
|
"OUROBOROS_PROCESSING_PREFERENCE": "",
|
|
"OUROBOROS_MODEL_PROCESSING_PREFERENCES": "{}",
|
|
# Worker lanes; empty means "use OUROBOROS_MODEL" (one model by default, per-lane
|
|
# override optional). HEAVY = mutative first-level subagents; LIGHT = auto/deep bulk.
|
|
"OUROBOROS_MODEL_HEAVY": OPENROUTER_DEFAULTS["heavy"],
|
|
"OUROBOROS_MODEL_LIGHT": OPENROUTER_DEFAULTS["light"],
|
|
"OUROBOROS_MODEL_VISION": OPENROUTER_DEFAULTS["vision"],
|
|
"OUROBOROS_IMAGE_INPUT_MODE": "auto",
|
|
# Background consciousness is a high-horizon loop, not a cheap helper lane.
|
|
"OUROBOROS_MODEL_CONSCIOUSNESS": OPENROUTER_DEFAULTS["consciousness"],
|
|
# Cross-model resilience CHAIN (comma-separated, ordered). A single model is a
|
|
# 1-element chain; empty disables cross-model fallback. Resilience slot — keeps a
|
|
# real default, unlike the worker lanes. (Renamed from the singular MODEL_FALLBACK.)
|
|
"OUROBOROS_MODEL_FALLBACKS": OPENROUTER_DEFAULTS["fallback"],
|
|
"OUROBOROS_MODEL_DEEP_SELF_REVIEW": OPENROUTER_DEFAULTS["deep_self_review"],
|
|
"OUROBOROS_MAX_WORKERS": 10, "OUROBOROS_PRESENCE_MAX_ACTIVE": 2,
|
|
"OUROBOROS_MAX_ACTIVE_SUBAGENTS_PER_ROOT": 6,
|
|
"OUROBOROS_MAX_SUBAGENT_DEPTH": 3,
|
|
# Mutative ("acting") subagents master toggle. Empty = follow runtime mode
|
|
# (ON in advanced/pro, OFF in light); explicit true/false overrides. Owner-
|
|
# controlled; light-mode self-repo writes stay blocked by the sandbox.
|
|
"OUROBOROS_ALLOW_MUTATIVE_SUBAGENTS": "",
|
|
# Acting self_worktree base location + durable genesis projects root (both
|
|
# outside repo/ and data/). genesis projects are durable and never GC'd.
|
|
"OUROBOROS_SUBAGENT_WORKTREE_ROOT": "",
|
|
"OUROBOROS_SUBAGENT_PROJECTS_ROOT": "",
|
|
"OUROBOROS_DELIVERABLES_ROOT": "",
|
|
# Unified age-based GC retention (days) for ALL disposable runtime artifacts:
|
|
# subagent worktrees, headless/direct task drives, and leftover service logs.
|
|
# Single owner-facing knob (math SSOT in ouroboros/retention.py); deprecated
|
|
# per-subsystem keys are migrated to this on settings load.
|
|
"OUROBOROS_GC_RETENTION_DAYS": 7,
|
|
"TOTAL_BUDGET": 200.0,
|
|
"OUROBOROS_PER_TASK_COST_USD": 50.0,
|
|
# cloud.ru catalog prices are RUB per 1M while the budget is USD. No implicit
|
|
# exchange rate: the owner must explicitly configure the divisor.
|
|
"OUROBOROS_RUB_USD_RATE": "",
|
|
# Live-pricing (OpenRouter + cloud.ru catalog) refetch interval; prices/FX drift.
|
|
"OUROBOROS_PRICING_TTL_SEC": 21600,
|
|
# Main-loop round ceiling (was an inline literal in loop.py — hot-reloadable now).
|
|
"OUROBOROS_MAX_ROUNDS": 200,
|
|
# Same-model attempt budget for TRANSIENT provider failure classes
|
|
# (finish_reason=null, 429/5xx/overloaded); floored at the caller's base
|
|
# retry budget. Permanent classes fail fast regardless.
|
|
"OUROBOROS_TRANSIENT_RETRY_MAX": 6,
|
|
# #4 self-DoS guard: max concurrent provider calls per (model, use_local) route; excess
|
|
# worker threads wait (deadline-bounded) instead of storming one model's rate limit. <=0
|
|
# disables. Default-on, fail-soft (see ouroboros/model_concurrency.py).
|
|
"OUROBOROS_MODEL_MAX_CONCURRENCY": 3,
|
|
# Hard ceiling (seconds) a provider call waits for a concurrency slot when the task has
|
|
# NO deadline; past it the call proceeds WITHOUT a slot (never blocks forever). SSOT here.
|
|
"OUROBOROS_MODEL_SLOT_MAX_WAIT_SEC": 180,
|
|
# LIGHT one-shot ceilings (naming, its gateway wait, the update letter): SSOT here, never consumer magic numbers.
|
|
"OUROBOROS_UPDATE_LETTER_TIMEOUT_SEC": 120,
|
|
"OUROBOROS_PROJECT_NAMING_TIMEOUT_SEC": 60,
|
|
"OUROBOROS_PROJECT_NAMING_ASYNC_TIMEOUT_SEC": 8,
|
|
# Skill lifecycle lane deadline (wedged-job loud-failure bound).
|
|
"OUROBOROS_SKILL_LIFECYCLE_TIMEOUT_SEC": 1800,
|
|
"OUROBOROS_CLAUDEXOR_HARNESS_INSTALL_TIMEOUT_SEC": 300,
|
|
"OUROBOROS_CLAUDEXOR_QUOTA_REFRESH_TIMEOUT_SEC": 90,
|
|
# Onboarding completion: the ONE blocking Claudexor snapshot read is bounded so a
|
|
# wedged owned-daemon initialization surfaces as a typed preset failure, not a
|
|
# wizard stuck on "Saving..." (issue #464).
|
|
"OUROBOROS_ONBOARDING_SNAPSHOT_TIMEOUT_SEC": 45,
|
|
# In-process settings-document lock: a writer wedged inside its hot-reload
|
|
# side effects must surface as a typed "settings busy" answer to the next
|
|
# writer (the onboarding save among them), never as an endless wait.
|
|
"OUROBOROS_SETTINGS_DOCUMENT_LOCK_TIMEOUT_SEC": 30,
|
|
# Custody's bounded wait for an in-process direct-chat turn to reach its
|
|
# next round boundary after the owner's stop control was written; the
|
|
# same custody pass runs on the supervisor sweep, so this stays short.
|
|
"OUROBOROS_DIRECT_TURN_STOP_WAIT_SEC": 2,
|
|
# Activity-based liveness (the flat wall-clock pair it replaced is RETIRED below):
|
|
# idle window = no real progress AND no progressing subtree; abs ceiling = the
|
|
# unconditional per-task backstop (budget/cost stays a separate hard axis).
|
|
"OUROBOROS_TASK_IDLE_TIMEOUT_SEC": 900,
|
|
"OUROBOROS_TASK_ABS_CEILING_SEC": 21600,
|
|
"OUROBOROS_PER_CALL_TIMEOUT_CEILING_SEC": 1800,
|
|
"OUROBOROS_FINALIZATION_GRACE_SEC": FINALIZATION_GRACE_DEFAULT_SEC,
|
|
"OUROBOROS_SUPERVISOR_LIVENESS_DEADLINE_SEC": SUPERVISOR_LIVENESS_DEADLINE_DEFAULT_SEC,
|
|
"OUROBOROS_PACING_INTERVAL_SEC": PACING_INTERVAL_DEFAULT_SEC,
|
|
"OUROBOROS_TOOL_TIMEOUT_SEC": 600,
|
|
"OUROBOROS_VISION_CAPTION_TIMEOUT_SEC": 90,
|
|
"OUROBOROS_BG_MAX_ROUNDS": 10,
|
|
"OUROBOROS_BG_WAKEUP_MIN": 30,
|
|
"OUROBOROS_BG_WAKEUP_MAX": 7200,
|
|
# Post-task self-evolution envelope (V4). Owner-enabled capability whose
|
|
# CONTENT stays LLM-first; default OFF. When enabled, after a qualifying task
|
|
# the worker may promote one high-value code-class backlog item into the
|
|
# existing (gated) evolution campaign. Cadence: off | llm | every_n:<k>.
|
|
"OUROBOROS_POST_TASK_EVOLUTION": "false",
|
|
"OUROBOROS_POST_TASK_EVOLUTION_CADENCE": "llm",
|
|
"OUROBOROS_POST_TASK_EVOLUTION_BUDGET_USD": 0.0,
|
|
# Optional owner steer appended to each evolution cycle's objective (never
|
|
# overrides the LLM-first promotion). Empty = pure LLM choice.
|
|
"OUROBOROS_EVOLUTION_PERSISTENT_OBJECTIVE": "",
|
|
"OUROBOROS_WEBSEARCH_MODEL": "gpt-5.2",
|
|
# web_search backend pin: auto (default OpenAI-first cascade) | ddgs (pure
|
|
# retrieval, no second LLM — for fixed-model runs) | openai | openrouter | anthropic.
|
|
"OUROBOROS_WEBSEARCH_BACKEND": "auto",
|
|
# Main-loop OpenRouter server web-search tool. Off by default: provider-
|
|
# specific capability, not a core provider-independence requirement.
|
|
"OUROBOROS_MAIN_WEB_SEARCH": "off",
|
|
"OUROBOROS_MAIN_WEB_SEARCH_ENGINE": "auto",
|
|
"OUROBOROS_MAIN_WEB_SEARCH_MAX_TOTAL_RESULTS": 10,
|
|
# OpenRouter provider routing: "" (off) | resilience (same-model failover, cache-warm)
|
|
# | repro (pin, no failover — fixed-model runs) | a raw JSON `provider` object.
|
|
"OUROBOROS_OR_PROVIDER": "",
|
|
# search_code total wall-clock budget (seconds) bounding the rg walk + the fallback walk.
|
|
"OUROBOROS_SEARCH_CODE_WALL_SEC": "45",
|
|
# NOTE: OUROBOROS_OBSERVABILITY_KEEP_RAW (writes UNREDACTED secret-bearing payloads to
|
|
# disk) is intentionally NOT a settings/UI carrier — it is an env-only operator debug
|
|
# override so a self-change or non-owner save can never enable secret logging.
|
|
# Generative context-window probe machinery: when enabled AND a caller passes
|
|
# allow_generative=True, confirms a route's >=1M window from a FREE over-window
|
|
# reject; *_CHARS sizes the padding. Since the settings-time Max gate retirement
|
|
# no production surface passes allow_generative=True (dormant; kept for tests
|
|
# and future explicit owner probes).
|
|
"OUROBOROS_GENERATIVE_PROBE": "1",
|
|
"OUROBOROS_GENERATIVE_PROBE_CHARS": "5000000",
|
|
# ABI 7.0 (ABI-10, owner 5.4=A): the structured slot value is the ONE
|
|
# reviewer configuration surface; "" = the shipped default panel. The
|
|
# legacy comma-list settings keys are RETIRED (see RETIRED_SETTING_KEYS);
|
|
# their env spellings survive only as the derived runtime projection.
|
|
"OUROBOROS_REVIEWER_SLOTS": "",
|
|
"OUROBOROS_SUBAGENTS": "", # configured task-actor SSOT; "" = bounded legacy/undecided read
|
|
# INSTALL-TIME facts: the agent-preset generation this install received, and WHEN onboarding last completed
|
|
# (recorded on EVERY completion). Endpoint-authored and disk-only — see ENDPOINT_AUTHORED_SETTINGS.
|
|
"OUROBOROS_SUBSCRIPTION_PRESET_VERSION": "",
|
|
"OUROBOROS_SUBAGENT_PRESET_RECEIPT": "",
|
|
"OUROBOROS_ONBOARDING_COMPLETED_AT": "",
|
|
# Pre-commit review enforcement: advisory | blocking
|
|
"OUROBOROS_REVIEW_ENFORCEMENT": "advisory",
|
|
# Native tool-round reviewer episode transcript CEILING (chars); the effective
|
|
# bound is derived from the reviewer window (review_native_episode.py); a
|
|
# surface's declared mandatory reading may lift it up to the window. No round cap.
|
|
"OUROBOROS_REVIEW_NATIVE_MAX_TRANSCRIPT_CHARS": "900000",
|
|
# Auto-grant reviewed-skill requests by default; grants stay bound to the
|
|
# reviewed content hash and editing a skill still invalidates them.
|
|
"OUROBOROS_AUTO_GRANT_REVIEWED_SKILLS": "true",
|
|
# Launcher-seeded native skills carry a hash-pinned native-trust review
|
|
# verdict (the payload bytes shipped through the repo commit gate); the
|
|
# zero-grant ones also auto-enable. Editing the payload still goes stale.
|
|
# Owner opt-out: set to false to keep manual review for native seeds.
|
|
"OUROBOROS_TRUST_NATIVE_SEEDED_SKILLS": "true",
|
|
# Agent-requested restarts drain running tasks first: while any RUNNING
|
|
# task still heartbeats, the restart waits up to this many seconds before
|
|
# proceeding fail-closed (0 = no drain, restart immediately).
|
|
"OUROBOROS_RESTART_DRAIN_MAX_SEC": 120,
|
|
# Runtime mode: light | advanced | pro; pro still requires review gates.
|
|
"OUROBOROS_RUNTIME_MODE": "advanced",
|
|
# Context mode: nano | low | max. Owner-only working-context size profile. max = full always-on docs +
|
|
# current memory granularity; low = ARCHITECTURE as a navigation map + deeper memory consolidation,
|
|
# sized for ~200k / local models. Cognitive-horizon knob (BIBLE P1): the agent cannot lower it
|
|
# (owner-only), and it never changes model / reasoning-effort / output-token budgets.
|
|
"OUROBOROS_CONTEXT_MODE": "max",
|
|
# One-window compatibility tombstone for the retired persistent auto-Low mechanism.
|
|
# It never sizes or routes context and no runtime writer may set it true. An explicit
|
|
# false still distinguishes owner-authored Low from a bare forwarded env Low for P3.
|
|
"OUROBOROS_CONTEXT_MODE_AUTO_LOW": "false",
|
|
# Optional extra user-managed skills checkout; Ouroboros never clones/pulls it.
|
|
"OUROBOROS_SKILLS_REPO_PATH": "",
|
|
"OUROBOROS_CLAWHUB_REGISTRY_URL": "https://clawhub.ai/api/v1",
|
|
"OUROBOROS_HUB_CATALOG_URL": "https://raw.githubusercontent.com/razzant/OuroborosHub/main/catalog.json",
|
|
"MCP_ENABLED": False,
|
|
"MCP_SERVERS": [],
|
|
"MCP_TOOL_TIMEOUT_SEC": 60,
|
|
"OUROBOROS_TASK_REVIEW_MODE": "auto",
|
|
# LLM safety-supervisor coverage (owner-only, like runtime/context mode):
|
|
# full (shipped default; fail-closed fallbacks land here; a FRESH wizard authors
|
|
# "light") — LLM check on POLICY_CHECK + conditional shell.
|
|
# light — LLM check ONLY on POLICY_CHECK integration tools; POLICY_CHECK_CONDITIONAL
|
|
# shell/verify fall to the deterministic whitelist + registry guards (no LLM).
|
|
# off — no LLM safety calls at all; the deterministic registry sandbox, protected-path
|
|
# policy and light-mode guards STAY ON. Every non-full mode audits durably.
|
|
"OUROBOROS_SAFETY_MODE": "full",
|
|
# Safety-supervisor LLM call shaping (v6.54.3 parse-bug fix): a tight output
|
|
# budget + no reasoning keeps the light model from spending its whole budget on
|
|
# hidden reasoning and returning a 1-token/empty body that fails JSON parse and
|
|
# then fail-closed blocks a benign command. Registered numeric SSOT (no inline literals).
|
|
"OUROBOROS_SAFETY_MAX_TOKENS": 2000,
|
|
"OUROBOROS_SAFETY_CALL_TIMEOUT_SEC": 60,
|
|
# v6.54.3 transport-timeout SSOT (deadline package D). web_search: 480 is one
|
|
# provider-attempt bound; the ToolEntry envelope derives the configured paid
|
|
# cascade. LLM no_proxy: 2700 leaves room for long silent reasoning without
|
|
# pinning a worker on a dead socket.
|
|
"OUROBOROS_WEBSEARCH_TIMEOUT_SEC": 480,
|
|
"OUROBOROS_LLM_TRANSPORT_READ_TIMEOUT_SEC": 2700,
|
|
# v6.54.3 (1.5): plan_task deadline scaling. With a task deadline the planning swarm's
|
|
# wait ceiling is min(configured ceiling, remaining/4); below this floor plan_task SKIPS
|
|
# with a typed reason + telemetry rather than eat the tail of the budget.
|
|
"OUROBOROS_PLAN_TASK_DEADLINE_MIN_SEC": 300,
|
|
# Acceptance-review budget layer (task_pacing SSOT): the configurable
|
|
# acceptance admission floor, clamped to >=200 s by task_pacing.
|
|
"OUROBOROS_ACCEPTANCE_REVIEW_EST_SEC": 200,
|
|
# Shared paid-review-cycle cap (SSOT + per-gate meaning: ouroboros/review_cycles.py):
|
|
# STRING "N"|"unlimited": plan review, acceptance (passes = cycles - 1), commit gate and skill review (paid cycles per root task / manual snapshot); identical material is never re-reviewed for pay on any gate.
|
|
"OUROBOROS_REVIEW_MAX_CYCLES": "2",
|
|
"OUROBOROS_ACCEPTANCE_RESERVE_PCT": 5,
|
|
# Prompt-cache TTL, one honest GLOBAL override (owner decision 2026-08-08, batch #2 Q2=A): applied to
|
|
# EVERY cache_control breakpoint on the Anthropic-normalizing family — main loop, review lanes, safety
|
|
# supervisor alike — at the ONE send-time finalizer (llm._normalize_payload_cache_ttl). 'default' = bare
|
|
# markers (provider default 5m tier); '5m'/'1h' = the explicit Anthropic ephemeral tiers ('1h' bills cache
|
|
# writes at the documented 2x-vs-1.25x ratio). Non-Anthropic wire formats are a NO-OP by construction
|
|
# (Gemini documents no ttl field — the v5.30.0 outage class).
|
|
"OUROBOROS_PROMPT_CACHE_TTL": "1h",
|
|
# Reasoning effort per task type: any EFFORT_SCALE tier (the ordered SSOT in settings_scales)
|
|
"OUROBOROS_EFFORT_TASK": "medium",
|
|
"OUROBOROS_EFFORT_EVOLUTION": "high",
|
|
"OUROBOROS_EFFORT_REVIEW": "high",
|
|
"OUROBOROS_EFFORT_SCOPE_REVIEW": "high",
|
|
"OUROBOROS_EFFORT_DEEP_SELF_REVIEW": "high",
|
|
"OUROBOROS_EFFORT_CONSCIOUSNESS": "high",
|
|
"OUROBOROS_RETURN_REASONING": True,
|
|
"OUROBOROS_REASONING_SUMMARY": "auto",
|
|
"GITHUB_TOKEN": "",
|
|
"GITHUB_REPO": "",
|
|
# Local model (llama-cpp-python server)
|
|
"LOCAL_MODEL_SOURCE": "",
|
|
"LOCAL_MODEL_FILENAME": "",
|
|
"LOCAL_MODEL_PORT": 8766,
|
|
"LOCAL_MODEL_N_GPU_LAYERS": 0,
|
|
"LOCAL_MODEL_CONTEXT_LENGTH": 16384,
|
|
"LOCAL_MODEL_CHAT_FORMAT": "",
|
|
"USE_LOCAL_MAIN": False,
|
|
"USE_LOCAL_HEAVY": False,
|
|
"USE_LOCAL_LIGHT": False,
|
|
"USE_LOCAL_CONSCIOUSNESS": False,
|
|
"USE_LOCAL_FALLBACK": False,
|
|
"OUROBOROS_FILE_BROWSER_DEFAULT": "",
|
|
# 429-aware cross-model fallback: process-local cooldown for transiently failing
|
|
# models (429/5xx/overloaded), passive heal-back. Owner-tunable; default-on, fail-soft.
|
|
"OUROBOROS_FALLBACK_COOLDOWN_ENABLED": True,
|
|
"OUROBOROS_FALLBACK_COOLDOWN_SEC": 120,
|
|
"OUROBOROS_FALLBACK_ATTEMPTS_PER_MODEL": 1,
|
|
# Delegated subagents. NARROW key, read ONLY by the subagent scheduler; deliberately absent from
|
|
# provider_models.MODEL_SETTING_KEYS (see ARCHITECTURE "Delegated subagents"). Empty = delegation off AND
|
|
# undecided (Settings' Subagents section offers the connected-subscription default); the literal `off` =
|
|
# delegation off because the owner said so. Wait keys bound the nanny's QUIET wait only.
|
|
"OUROBOROS_SUBAGENT_HARNESS": "",
|
|
# Optional Delegation account pin (D-U5): a credential-profile id sent as `credentialProfileId`; empty = engine
|
|
# rotation pool (D28; presets never author it). Read ONLY by get_subagent_harness -> DelegationRoute.profile_id.
|
|
"OUROBOROS_SUBAGENT_PROFILE": "",
|
|
"OUROBOROS_DELEGATE_WAIT_SEC": 120,
|
|
"OUROBOROS_DELEGATE_WAIT_MAX_SEC": 1800,
|
|
}
|
|
|
|
|
|
# Setting keys a release DELETED. `load_settings` keeps unrecognized keys so a rename never destroys
|
|
# an owner customization — which would otherwise leave a removed key living in data/settings.json
|
|
# forever, still served by GET /api/settings. Retiring a key is a decision; its ghost is not.
|
|
RETIRED_SETTING_KEYS: tuple[str, ...] = (
|
|
# v6.87.7: the depth cap conflated how DEEP delegation nests with how STRONG a descendant is.
|
|
"OUROBOROS_SUBAGENT_CAPABILITY_DEPTH_LIMIT",
|
|
# knobs are retired (the review-cycle cap OUROBOROS_REVIEW_MAX_CYCLES bounds plan review).
|
|
"OUROBOROS_ACCEPTANCE_MAX_IMPROVEMENT_PASSES",
|
|
"OUROBOROS_PLAN_TASK_SWARM_TIMEOUT_SEC",
|
|
"OUROBOROS_PLAN_TASK_SWARM_MAX_WAIT_SEC",
|
|
"OUROBOROS_PLAN_TASK_SWARM_HEARTBEAT_STALE_SEC",
|
|
# ABI 7.0 (owner Q10=A): the P3 scope-review floor key is REMOVED, not merely
|
|
# enforcement-inert. Deprecated + inert since v6.80.0 (applicability follows the
|
|
# owner-only OUROBOROS_CONTEXT_MODE); the whole surface — key, owner endpoint,
|
|
# guards, SAFETY clause — is retired in the 7.0 ABI window.
|
|
"OUROBOROS_SCOPE_REVIEW_FLOOR",
|
|
# ABI 7.0 (ABI-10, owner 5.4=A): the reviewer comma-lists and the phase-5
|
|
# per-row/advisory route envs are retired as SETTINGS keys — the structured
|
|
# OUROBOROS_REVIEWER_SLOTS is the one configuration surface. An install
|
|
# that carried only comma keys gets the shipped default panel (the RC
|
|
# auditor names this migration explicitly). The ENV spellings of the two
|
|
# comma model lists live on as the derived runtime projection for the
|
|
# API-pinned surfaces, never as a settings surface.
|
|
"OUROBOROS_REVIEW_MODELS",
|
|
"OUROBOROS_SCOPE_REVIEW_MODELS",
|
|
"OUROBOROS_SCOPE_REVIEW_MODEL",
|
|
"OUROBOROS_REVIEW_ROUTES",
|
|
"OUROBOROS_SCOPE_REVIEW_ROUTES",
|
|
"OUROBOROS_ADVISORY_REVIEW_ROUTE",
|
|
# CPL4-C22 (owner 7A): the observability retention knob was parsed,
|
|
# clamped and reported while deleting nothing — a documented no-op is a
|
|
# misleading operator surface. Manifests/blobs stay preserved
|
|
# indefinitely BY CONTRACT; the knob is gone, not honored.
|
|
"OUROBOROS_OBSERVABILITY_RETENTION_DAYS",
|
|
# ABI 7.0 (D04, owner 1B): the flat wall-clock pair. Their kill was replaced by
|
|
# the activity model (OUROBOROS_TASK_IDLE_TIMEOUT_SEC + OUROBOROS_TASK_ABS_CEILING_SEC)
|
|
# long ago, after which they were kept as typed no-ops that a settings save reported
|
|
# as "retired" and a non-default value merely logged. A knob the UI accepts, the
|
|
# status line renders and nothing obeys is the misleading-surface class; the 7.0 ABI
|
|
# window deletes it rather than carrying the ghost another minor.
|
|
"OUROBOROS_SOFT_TIMEOUT_SEC",
|
|
"OUROBOROS_HARD_TIMEOUT_SEC",
|
|
"OUROBOROS_REVIEW_NATIVE_MAX_ROUNDS", # a ceiling on rounds; bounds are transcript/deadline/ledger
|
|
)
|
|
|
|
|
|
# ABI 7.0 (ABI-10) classification INSIDE RETIRED_SETTING_KEYS: the reviewer
|
|
# comma-list / phase-5 route keys whose migration is "move the config to the
|
|
# structured OUROBOROS_REVIEWER_SLOTS BEFORE the upgrade" (an install carrying
|
|
# only comma keys gets the shipped default panel). The RC auditor
|
|
# (scripts/rc_audit.py) snaps this list at execution time instead of hardcoding
|
|
# it; membership in RETIRED_SETTING_KEYS is pinned by the auditor at runtime
|
|
# (fail-closed) and by tests/test_rc_audit_fixture_suite.py.
|
|
RETIRED_COMMA_LIST_SETTING_KEYS: tuple[str, ...] = (
|
|
"OUROBOROS_REVIEW_MODELS",
|
|
"OUROBOROS_SCOPE_REVIEW_MODELS",
|
|
"OUROBOROS_SCOPE_REVIEW_MODEL",
|
|
"OUROBOROS_REVIEW_ROUTES",
|
|
"OUROBOROS_SCOPE_REVIEW_ROUTES",
|
|
"OUROBOROS_ADVISORY_REVIEW_ROUTE",
|
|
)
|
|
|
|
|
|
# The second classification INSIDE RETIRED_SETTING_KEYS: retired keys whose
|
|
# SUCCESSOR SETTING this retirement table states, so the first-boot notice can
|
|
# name it instead of telling the owner there is none. Membership is a decision
|
|
# recorded HERE, next to the retirement it explains — a retired key is absent
|
|
# from this map when the table names no successor for it (the knob's effect
|
|
# became fixed behavior, or the replacement is a surface rather than a setting),
|
|
# and the notice then stays neutral instead of claiming either. The pair below
|
|
# is stated twice over: by the comment above the keys in the tuple, and by the
|
|
# ABI-5/D04 rows in docs/ARCHITECTURE.md.
|
|
#
|
|
# A key whose value the read seam MIGRATES does not belong here even though its
|
|
# successor is named: `OUROBOROS_ACCEPTANCE_MAX_IMPROVEMENT_PASSES` is consumed
|
|
# into `OUROBOROS_REVIEW_MAX_CYCLES` before the purge computes the dropped set,
|
|
# so it never reaches the notice — there is no loss to report, and an entry for
|
|
# it would promise a line nothing emits.
|
|
RETIRED_SETTING_SUCCESSORS: dict[str, tuple[str, ...]] = {
|
|
# The flat wall-clock pair was superseded by the activity model.
|
|
"OUROBOROS_SOFT_TIMEOUT_SEC": (
|
|
"OUROBOROS_TASK_IDLE_TIMEOUT_SEC", "OUROBOROS_TASK_ABS_CEILING_SEC"),
|
|
"OUROBOROS_HARD_TIMEOUT_SEC": (
|
|
"OUROBOROS_TASK_IDLE_TIMEOUT_SEC", "OUROBOROS_TASK_ABS_CEILING_SEC"),
|
|
}
|
|
|
|
|
|
def retired_setting_keys_notice(dropped: tuple[str, ...], *, reviewer_slots: tuple[str, str]) -> str:
|
|
"""THE sentence every surface says about retired keys found in the owner's document.
|
|
|
|
Read by the settings read seam (``config.normalize_settings_raw``, the once-per-process
|
|
log line) and by the boot-time owner chat notice (``server_maintenance``), so the log
|
|
and the chat never describe the same loss differently. It names every dropped key,
|
|
says they are NOT honored, and states what replaced them from the two tables above;
|
|
for the reviewer comma-lists it names what runs NOW from ``reviewer_slots`` — the
|
|
``(state, parse_error)`` pair ``reviewer_slot_config.authored_reviewer_slots_state``
|
|
derives from the document's ``OUROBOROS_REVIEWER_SLOTS``: the panel authored there,
|
|
the shipped default panel while it is absent, or — malformed — NO panel, because the
|
|
loader rejects it and every review surface refuses with that parse error until the
|
|
owner repairs the setting. "Which reviewers run" is the fact the owner has to see, and
|
|
a default panel that is not serving must never be announced.
|
|
"""
|
|
comma = [k for k in dropped if k in RETIRED_COMMA_LIST_SETTING_KEYS]
|
|
clauses = []
|
|
if comma:
|
|
state, parse_error = reviewer_slots
|
|
if state == "authored":
|
|
panel = "the reviewer panel authored in that setting"
|
|
elif state == "invalid":
|
|
panel = (
|
|
"NO reviewer panel: that setting is malformed, so reviews are refused "
|
|
"(commit review blocks; under Advisory enforcement it warns and commits "
|
|
"unreviewed) until it is repaired on the Settings page — %s" % parse_error)
|
|
else:
|
|
panel = "the SHIPPED default reviewer panel until that setting is authored (Settings page)"
|
|
clauses.append(
|
|
"the reviewer comma-lists (%s) are replaced by the structured "
|
|
"OUROBOROS_REVIEWER_SLOTS, so this install now runs %s" % (", ".join(comma), panel))
|
|
if named := [k for k in dropped if k in RETIRED_SETTING_SUCCESSORS]:
|
|
clauses.append("the retirement table names a successor setting: %s" % "; ".join(
|
|
"%s -> %s" % (k, ", ".join(RETIRED_SETTING_SUCCESSORS[k])) for k in named))
|
|
if rest := [k for k in dropped if k not in comma and k not in named]:
|
|
clauses.append(
|
|
"the retirement table names no successor setting for %s: they are "
|
|
"removed, not honored — see the release notes for what, if anything, "
|
|
"replaced them" % ", ".join(rest))
|
|
return "retired key(s) %s are present in the settings document and are NOT honored; %s" % (
|
|
", ".join(dropped), "; ".join(clauses))
|
|
|
|
|
|
# The same keys from the other side: load_settings overlays env onto disk-ABSENT keys, so without this an
|
|
# ordinary load->save round-trip in a process whose env says low/off would launder that value onto disk
|
|
# unauthorised — or, once the guard reads disk, raise a PermissionError nobody authored. Owner endpoints
|
|
# write BOTH disk and env, so the owner path is unaffected.
|
|
_DISK_AUTHORED_SETTINGS = ("OUROBOROS_CONTEXT_MODE", "OUROBOROS_CONTEXT_MODE_AUTO_LOW", "OUROBOROS_SAFETY_MODE")
|
|
|
|
# ENDPOINT-AUTHORED, DISK-ONLY: install-time facts POST /api/onboarding/complete alone writes. The ratchets above are
|
|
# disk-authored yet DO project once the file carries them; these never leave disk in EITHER direction — an env timestamp alone closed the onboarding window on a fresh install, and an env marker was then persisted by a save.
|
|
ENDPOINT_AUTHORED_SETTINGS = frozenset({"OUROBOROS_SUBSCRIPTION_PRESET_VERSION", "OUROBOROS_SUBAGENT_PRESET_RECEIPT", "OUROBOROS_ONBOARDING_COMPLETED_AT"})
|
|
|
|
|
|
# Settings keys deliberately NOT projected into the environment. Everything else in SETTINGS_DEFAULTS IS
|
|
# exported, by derivation rather than a parallel hand-kept list: such a list drifts silently and the failure
|
|
# is invisible — settings accept the key, the UI shows it saved, and the consumer goes on reading os.environ
|
|
# and falling back to its hardcoded constant (OUROBOROS_SKILL_LIFECYCLE_TIMEOUT_SEC sat like that behind a
|
|
# hardcoded 1800). Deriving makes export the DEFAULT for a new key and an exclusion a decision written here.
|
|
SETTINGS_KEYS_NOT_EXPORTED_TO_ENV = frozenset({
|
|
# Structured list value: `str(value)` is a Python repr no reader parses back, and every consumer already reads
|
|
# it from the settings dict (mcp_client.parse_servers, gateway.mcp), never from the environment.
|
|
"MCP_SERVERS",
|
|
# ENV IS THE AUTHORITY for the bind host, not settings. `ouroboros server --host 0.0.0.0` puts the choice in
|
|
# the environment, and both consumers (server.main, server_control.restart_current_process) deliberately read
|
|
# env BEFORE settings. Exporting this key stamped the settings value — usually the shipped 127.0.0.1 default,
|
|
# which no owner authored — back over that environment, so the operator's LAN-reachable server silently became
|
|
# loopback at the first self-restart. A default standing in for an absent key is not a decision.
|
|
"OUROBOROS_SERVER_HOST",
|
|
}) | ENDPOINT_AUTHORED_SETTINGS # disk-only in BOTH directions (never read from env, never exported to it)
|
|
|
|
|
|
def settings_env_keys() -> list:
|
|
"""Settings keys projected into os.environ, derived from SETTINGS_DEFAULTS."""
|
|
return [k for k in SETTINGS_DEFAULTS if k not in SETTINGS_KEYS_NOT_EXPORTED_TO_ENV]
|