Agent Runtime Status Authority
Status: Partially implemented. The evidence-arbitration authority is live enough to ship the first operator-visible status pass: PTY output and keystrokes update recency only (the byte-driven flap engine is deleted; sessions start Unknown); the daemon tick is the sole state path — assemble EvidenceSnapshot → arbitrate → apply_watchdog (Linux-gated) → debounce → publish; the semantic reporter channel (report-event CLI → control socket → gating → AuthorityEvidence), /proc physics, OSC evidence (title / OSC 9;4 progress / OSC 133), and the screen rule-pack engine all feed arbitration. The rule engine carries line_regex/forbids_regex, the prompt-caret region family, the recursive nested all/any/not gate grammar, a RULE_ENGINE_VERSION floor, and semver-bounded validated_versions. Seen/ack, flap/stuck telemetry, status explain / status capture, per-launch reporter installation (merge-safe; Amp installs none), and derived-image asset copying have landed. The remaining work is the live-evidence pass: record whole-tab sessions, turn real frames into fixtures, continue pack rewrites from observed agent behavior, and decide production pack-distribution policy.
Shipped facts
crates/jackin-agent-status/src/lib.rs (and its arbitrate/policy/gating/process/rules children) is the source of truth for pure detection and arbitration. Capsule's crates/jackin-capsule/src/agent_status.rs re-exports that crate and keeps the container reporter installers under hook_installer. This page carries only the stable status contract other roadmap items depend on, the per-runtime detection contract, and the remaining refinements. The prior-art review and the original forward-looking design proposal that shaped the implementation now live in Agent runtime status: prior art and original design proposal — read that page for why the design took this shape, and for the specific list of proposed details that did not survive contact with the real implementation (notably the session.report_agent_state control-message family, and a "hook/API report wins" arbitration precedence — the shipped protocol instead uses a single ReportRuntimeEvent message plus an events-not-states gating layer, and Claude/Codex hooks are identity-only rather than state-authoring, per Decision 0a below).
The built-in runtimes are claude, codex, amp, kimi, and opencode, defined in crates/jackin-core/src/agent.rs; runtime setup centralizes agent-home seeding in crates/jackin-capsule/src/runtime_setup.rs; launch flags live in docker/runtime/entrypoint.sh and are tracked separately by Agent launch flags API. Capsule owns the PTY reader, jackin-term screen state, session lifecycle, the 1Hz ticker, the control socket at /jackin/run/jackin.sock, and in-container setup hooks; host-side code consumes structured state, it does not scrape terminal output through docker exec/docker attach.
Status vocabulary (stable contract)
One consistent status vocabulary across jackin console, jackin hardline, the daemon, Desktop Agent Hub, and scripts. Downstream items (agent attention prompts, autonomous task queue, Desktop Agent Hub, console agent session control, terminal observation automation) consume this surface.
| Public status | Meaning |
|---|---|
working | Actively generating, running tools, processing — not ready for operator input. |
blocked | Waiting on an operator decision: approval prompt, question, auth prompt, interactive input. |
done | Returned to idle after work; the operator has not yet reviewed that pane. |
idle | Idle and already seen, or a shell pane with no active agent. |
unknown | No reliable evidence. Unknown is safer than pretending. |
stuck | Diagnostic overlay: progress was expected, no reliable transition arrived in time. Never replaces blocked. Shipped today only as daemon clog! telemetry (status.stuck: …); there is no wire-level Stuck state, console glyph, or rate-limited operator affordance yet — that surfacing work belongs to the cross-process event stream tracked by jackin❯ Capsule and Agent attention prompts, not to this item. |
Raw detector states are unknown | working | blocked | idle plus per-pane seen; done is derived as raw idle + !seen and is never reported by any detector or reporter. The wire carries the public AgentState (including Unknown); the capsule derives done from seen. Roll-up is blocked > done > working > idle > unknown.
Non-goals (unchanged): probabilistic state estimation, Windows ConPTY, LLM-provider traffic interception, autonomous recovery after stuck (this authority reports and routes attention only), per-tool/per-command granularity, and built-in support for agents beyond the five runtimes (custom roles use the reporter contract and the unknown fallback).
Per-runtime detection (stable contract)
Decision 0a — Claude and Codex hooks are identity-only, not state authorities. Their hooks report session identity (which agent owns the pane, the resumable session id) and refresh freshness/liveness; they do not author
working/blocked/idle. State for both comes from the always-on rule pack + OSC evidence + physics watchdog. Completion-class hook events for these two CLIs are unreliable in order and timing (aSubagentStop/recap/away-summary can fire after the turn'sStopand revive an idle pane); because jackin❯ pins each CLI version in the role image, the co-versioned screen pack is a dependable state owner, so identity-only eliminates that failure class. If a future Claude/Codex release ships a documented, ordered, permission-result-complete event surface, revisit and promote to graded state authority.
| Runtime | State owner | Notes |
|---|---|---|
| Claude Code | Rule pack (primary) + OSC 9-family / OSC 9;4 edges + physics watchdog. Hook = identity/freshness only (Decision 0a). | No Stop→idle / SubagentStop→idle, so the post-Stop revive hazard can't occur. Reporter is a dumb pipe; installer merges additively. |
| Codex | Rule pack (primary) + title-spinner busy/idle + OSC 9 approval edge + physics watchdog. Hook = identity/freshness only. | The title-spinner and approval OSC plus the pack own state, removing dependence on Codex's permission-result ordering. |
| OpenCode | Container-local plugin (session.status/session.idle/permission.*/tool.execute.*/session.error). Grade: Complete. | Cross-checked by rule pack + watchdog. Status can stick after async prompts; idle handlers are fire-and-forget. |
| Amp | Screen rule pack + /proc physics + watchdog. No reporter installed. | Amp has no plugins.json node-plugin mechanism (writing one crashes it); a real reporter needs Amp's MCP/toolbox surface — see remaining work. |
| Kimi | Rule pack (primary) + process evidence + watchdog. | The worked example of vendor-independence: the co-versioned screen pack carries Kimi at full quality with no upstream hook. |
| Custom role agents | Role-authored reporter over the capsule socket (events-not-states contract). | unknown is the default; a future role-manifest contract may declare named visible states + ship rule packs. |
Runtime setup hard rules (unchanged): assets under /jackin/runtime/agent-status/, state under /jackin/state/agent-status/, socket under /jackin/run/; hook/plugin config merged only into container-local agent homes; nothing written to the host or bind-mounted project trees; drift repaired every launch.
Remaining / deferred work
- Whole-session evidence collection and replay — current live pack coverage was expanded from operator-provided captures, but the workflow is still too manual. A follow-up should add an opt-in tab/session recording path so contributors can replay exact capsule frames, inspect tab glyph/state transitions, and extract detector fixtures without relying on screenshots.
- Capture-driven pack completion — continue collecting real blocked/working/idle captures for supported agents and update packs from evidence. The current PR lands basic coverage for Claude, Codex, Amp, Kimi, OpenCode, and Grok, but follow-up PRs should keep treating new live frame shapes as fixtures rather than relying on guessed strings.
- Runtime pack-vs-CLI co-versioning enforcement — proven-blocked, not deferred-by-effort. Agents are bind-mounted (not baked), so the daemon has no runtime source for the running agent's CLI version.
validated_versionsstays as bounded pack provenance metadata andmin_engine_versionis the wired floor on the engine axis, but enforcing the pack-vs-CLI window needs a launch-time<agent> --versionprobe (which this design discourages). Revisit if a non-invasive version source appears. - Dirty-session sub-1 Hz faster-recheck cadence — the 700 ms
IDLE_HOLD_CAP(the latency contract) is landed and governs at the 1 Hz floor; the sub-1 Hz accelerator needs a one-shot timer armed on PTY damage in the biased daemon select loop and risks render thrash, so it stays deferred with that architectural reason. - OSC-9 notify-edge capture and BEL — a plain OSC 9 is currently host-forwarded only, not captured as an
OscEvidencefield, and BEL needs a jackin-term Bell passthrough variant. The notification attention-edge consumer lives in the unbuilt agent attention prompts item; capturing them now would be written-but-never-read fields. They land with that consumer. - A real Amp reporter — Amp falls back to screen + physics today. A graded Amp authority needs its MCP/toolbox surface rather than the (unsupported) OpenCode-style plugin shape.
stuckoperator surfacing — currently daemon-internal telemetry only (see the vocabulary table above). Promoting it to a console-visible affordance needs the cross-process event stream, which is jackin❯ Capsule Phase 4 / jackin❯ daemon work, not a change to this authority itself.- Recursive-gate compile cache — the nested-gate regex leaves compile once at load via
CompiledGate; a shared compiled cache keyed across packs is a noted micro-optimization, not a correctness gap.
Related Files
crates/jackin-agent-status/src/lib.rs— pure module root;arbitrate.rsis the sole state path,policy.rsthe debounce/watchdog,gating.rsthe event table,process.rsthe/procsampler, andrules.rsthe screen rule-pack engine.crates/jackin-capsule/src/agent_status/hook_installer.rs— container-local reporter installers.crates/jackin-capsule/src/session.rs— PTY/process source;advance_statusis the only state-authoring call.crates/jackin-capsule/src/daemon.rs— the tick that assembles evidence, arbitrates, and publishes.crates/jackin-protocol/src/control.rsandcrates/jackin-protocol/src/agent_status.rs— the status/report protocol surface.crates/jackin-core/src/agent.rs— built-in runtime enum and slugs.crates/jackin-agent-status/packs/claude.toml— the bundled rule packs live alongside this one (claude/codex/amp/kimi/opencode) and are copied into the derived image at/jackin/runtime/agent-status/packs/.
Related work
- Research: Agent runtime status: prior art and original design proposal — Herdr/multicode/CCManager/etc. prior-art review, the original data model and control-protocol proposal, and the honest list of what shipped differently.
- jackin❯ Capsule control plane — owns the per-container control-channel event-stream work this authority's consumers need.
- Agent attention prompts — consumes
blocked/donetransitions for operator notifications; ownsstuckoperator surfacing. - Agent launch flags API — owns the per-runtime launch-flag surface referenced above.