#41

feat(ops): live ops dashboard, cost tracking, config wizard + supervisor; reasoning/OOM fixes

Openbronezppg-archwants to merge intofrom

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 /dashboard and /telemetry routers: 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_JSON override.
  • Workers report usage via a new WorkerDriver.extract_usage() (the pi adapter sums turn_end events); 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 a waiting state on a missing/invalid config instead of crash-looping — so it comes up cleanly on first boot with a default no-model config.
  • New /wizard and /supervisor endpoints and a 3-step dashboard wizard to build, validate (through the real DispatchConfig.load), save and apply dispatch.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-portable start-cairn.sh launcher.

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 (default reasoning_effort low); the anthropic/responses paths are untouched, and PI_THINKING=none restores 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