#41
feat(ops): live ops dashboard, cost tracking, config wizard + supervisor; reasoning/OOM fixes
Operator control plane around the dispatcher plus three spend/stability fixes, from running Cairn on live engagements. No secrets or runtime data — all config variants and CTF artifacts are gitignored.
Note: this branch builds on the network-topology tab (#40), so those commits are included here too.
Observability — "Navigation" dashboard tab
- New
/dashboardand/telemetryrouters: engine vitals, host resources, per-worker state, task outcomes, timeout counts (parsed from the dispatcher log), and a live timeline — built from the DB + a telemetry snapshot the dispatcher pushes each tick. No schema coupling. - UI polls every 3s; live speed/limit sliders and engine pause/resume via
/settings.
Cost — model-agnostic spend tracking
- New
dispatcher/cost.py: per-1M-token price table with glob model matching; precedence worker-env → central table → unpriced (tokens are still counted when a model is unpriced — never a silent $0). Thread-safe aggregation per model and per currency;CAIRN_PRICING_JSONoverride. - Workers report usage via a new
WorkerDriver.extract_usage()(the pi adapter sumsturn_endevents); recorded after every worker call. Surfaced in a dashboard cost card.
Config wizard + supervisor
- New
cairn supervise: an always-on anchor that owns the dispatcher as a managed child, restarts it on request, and parks in awaitingstate on a missing/invalid config instead of crash-looping — so it comes up cleanly on first boot with a default no-model config. - New
/wizardand/supervisorendpoints and a 3-step dashboard wizard to build, validate (through the realDispatchConfig.load), save and applydispatch.yaml.*configs. API keys are always masked in responses and never echoed back; edit mode keeps the existing key when the field is left blank. install.sh(fresh-machine setup) and a path-portablestart-cairn.shlauncher.
Spend / stability fixes
- Reasoning burn: with some OpenAI-compatible reasoning models, default thinking consumed the whole token budget and returned empty output on large explore prompts. The pi adapter now forwards a configurable
PI_THINKING(defaultreasoning_effort low); the anthropic/responses paths are untouched, andPI_THINKING=nonerestores the previous behavior. - OOM: cap unbounded worker stdout buffering (
CAIRN_WORKER_STDOUT_CAP_MB). - Prompt rules: callbacks must use the tunnel interface routable from the target; agents must not block waiting on inbound callbacks (plant and conclude).
Security / hygiene
.gitignore hardened: dispatch.yaml.*, dispatch.*backup*.yaml, and local run/log artifacts. The diff contains no API keys, tokens, passwords, or target data (only env-var names and masking logic).
🤖 Generated with Claude Code