Expand description
HTTP handlers backing the muxspect CLI — Phase 1 of
docs/specs/SPEC_MUXSPECT_LIVE_INTROSPECTION_TOOL_2026_08_01.md, plus
the dock/dock clear extension from
docs/specs/SPEC_MUXSPECT_DOCK_DIAGNOSIS_AND_REMEDIATION_2026_08_06.md.
Diagnostic-only surface: a thin read composition over ProcessBroker
(Phase A of the process-tracking consolidation,
crates/srv/src/broker/process.rs) and its sibling registries — never
a new independent snapshot of process/turn state (spec §5.1/§3 point 8).
Reached the same way agentmux-mcp already reaches every other
/api/v1/* route: plain HTTP, X-AuthKey header, $AGENTMUX_LOCAL_URL/
$AGENTMUX_AUTH_KEY inherited from the caller’s own environment (spec
§5.2) — no new IPC mechanism, no new auth scheme. dock clear is this
module’s first MUTATING route — see the 2026-08-06 spec §2 for why that
scope call is narrow and deliberate, not a reversal of the read-only
design generally.
Structs§
- Background
Task View - Dock
Node View - Last
Error Frame - The most recent persisted
error_during_executionframe at the tail of a block’s ownoutputfile — populated ONLY when the very last non-blank line IS one (a block that errored once and then kept producing normal output afterward must NOT be flagged; only “the last thing that happened to this block was an unrecovered error” counts). - Muxspect
Background Tasks Query - Muxspect
Describe Query - Muxspect
Dock Clear Request - Muxspect
Dock Query - Muxspect
Find Query - Muxspect
Layout Query - Muxspect
Verify Sender Query - Sender
Candidate 🔒 - One already-fetched candidate, tagged with the tier it was found on.
Keeps
classify_senderpure and independent ofAgentRegistration/AgentEntry/LanInstance’s actual (differently-shaped) types — the handler below does the field mapping once, at the IO boundary, same discipline asdock_node_views/last_error_frameabove. - Verify
Sender Verdict - Not a security/trust primitive. This reports registry LIVENESS only
(does an agent named X currently exist in the discovery data at all) —
it performs NO cryptographic check and must not be confused with the
actual JEKT sender-authentication mechanism, which is per-message,
automatic, and already computes a real
TRUST=/SIG=value on every delivered jekt via HMAC-SHA256 (host tier) / Ed25519 (LAN tier) / a pinned Ed25519 key (reagent WAN) — see this repo’s rootCLAUDE.md, “Is a jekt’s sender identity actually verified? — the real answer”. Earlier drafts of this route reused that exact vocabulary (host-verified/network-claimed) for a mere presence check, which collided with — and could be mistaken for — that real, stronger guarantee (reagentx-workflow review on PR #2702, P1). Deliberately notrust/verifiedfield here at all:status+tierfully describe what was actually checked (presence, not authenticity), with nothing that could be misread as more than that.
Constants§
- CROSS_
CHANNEL_ 🔒PREVIEW_ TIMEOUT_ MS - Short per-forward timeout for cross-channel preview fetches in
handle_muxspect_conversations— a single dead/unresponsive channel must not stall the whole listing; best-effort, matches this file’s diagnostic (never authoritative) posture elsewhere. - DOCK_
STUCK_ 🔒THRESHOLD_ MS - A
ToolNode’s status stays"running"this long (matches the frontend’s ownTOOL_PROMOTION_MS,frontend/app/view/agent/activity/ tool-adapter.ts) before it’s eligible to be flaggedstuckhere — a tool call that’s merely taking a while must not be flagged the instant it starts. No shared constant exists between the two languages; keep this value in sync with the frontend’s if either changes. - LAST_
ERROR_ 🔒TAIL_ BYTES - Bounded tail window read from a block’s own persisted
outputfile when looking for a trailingerror_during_executionframe — seelast_error_frame. Large enough to comfortably hold the frame itself (a single JSON line, typically well under 1KB) plus whatever partial line/chunking noise might precede it; small enough to stay O(1) and not turnmuxspectinto a log-walker (that’smuxlog’s job, not this tool’s — see the tool’s own design spec §1). - PREVIEW_
MAX_ 🔒CHARS - Truncation length for a
last_message_preview— keepsconversations’ response small even when the tail line is a long tool-call/result frame; this is a liveness-glance preview, not a transcript reader (that’sGetAgentTranscript/muxspect conversation <agent>’s job). Applied to BOTH the host-tier read (last_line_preview_and_activity) and the cross-channel forwarded read — codex P2 on PR #2715 caught the cross-channel path skipping this entirely, which let one huge remote tool-call/result line inflate an otherwise-bounded response. - PREVIEW_
TAIL_ 🔒BYTES - Bounded tail window read directly off the live FileStore file in
last_line_preview_and_activity’s fast path — deliberately small relative to a whole session (which can be many MB): only needs to comfortably contain the single last non-blank line, not search for a specific frame shape the wayLAST_ERROR_TAIL_BYTES’s window does.
Functions§
- classify_
last_ 🔒error_ source - Best-effort classification of which pre-spawn failure path wrote a
persisted
error_during_executionframe, inferred from the message text itself (each construction site’s message is distinct and stable — seeidentity/resolver/errors.rs’sSpawnGateError::Display,subprocess/container_spawn.rs,subprocess/host_spawn.rs, andagent_handlers/input.rs’s containerensure_runningpath, the only four call sites that build this frame today).messageshould have the[AgentMux]prefix already stripped. - classify_
sender 🔒 - Pure verdict computation over already-fetched discovery data — see
handle_muxspect_verify_senderfor the IO/fetch boundary this composes with.candidatesmust already be in tier-priority order (host, then cross-channel, then lan, then wan — most-trusted first). - describe_
lifecycle_ 🔒is_ unknown GET /api/v1/muxspect/find?block_id=Xor?agent=X— Ext 4 ofdocs/reports/REPORT_MUXSPECT_MUXLOG_CROSS_CHANNEL_INSPECTION_2026_08_22.md: “which running instance(s), if any, have a controller or subagent dispatch matching block_id/agent X” — the query that debugging session needed and had no tool for, ending in manual filesystem/env-var archaeology across every channel by hand.- dock_
node_ 🔒views - Pure computation behind
handle_muxspect_dock— separated out so the stuck-flagging logic (the actual diagnostic value of this endpoint) is unit-testable without constructing a fullAppState/axum request, same discipline aslast_error_frame/classify_last_error_sourceabove. - flatten_
layout_ 🔒nodes - Flatten one layout tree into a compact, renderable node list.
- handle_
muxspect_ background_ tasks GET /api/v1/muxspect/background-tasks[?block_id=X]— the durable declared-background task registry (db_background_tasks), the source of truthhandle_muxspect_dock’s ephemeral, 1-hour-evictedDockSnapshotCachedeliberately isn’t (see docs/status/STATUS_ATTACHED_TASK_AXIS_AND_DEV_LOOP_2026_08_15.md). A task still shows up here after the dock cache has evicted it, and survives a pane reload — it’s backed by SQLite, not an in-memory cache.- handle_
muxspect_ conversations GET /api/v1/muxspect/conversations— a single-call, all-tier glance at every agent’s most recent activity, so an agent (or a human viamuxspect conversations) doesn’t have to composeDiscoverAgents+ N×GetAgentTranscriptcalls by hand. Seedocs/specs/SPEC_MUXSPECT_CROSS_TIER_CONVERSATION_VISIBILITY_2026_08_21.mdPhase A.- handle_
muxspect_ describe GET /api/v1/muxspect/describe?block_id=X— composesProcessBrokerstatus, the coarseBlockControllerRuntimeStatus, and the OS-process tree for one block into a single response. This is the “describe everything about block X” queryREPORT_PROCESS_ARCHITECTURE_STATE_AND_RETHINK_2026_07_22.md§5.4 named as missing — getting this picture today takes 2-3 separate, uncomposed RPC round-trips.- handle_
muxspect_ dock GET /api/v1/muxspect/dock?block_id=X— the cachedToolNodestatus snapshot for one block, cross-referenced againstProcessBrokerto flag entries that look stuck. Seedocs/specs/SPEC_MUXSPECT_DOCK_DIAGNOSIS_AND_REMEDIATION_2026_08_06.md§3.1.- handle_
muxspect_ dock_ clear POST /api/v1/muxspect/dock/clear— validatesnode_idis a currently tracked node forblock_id(per the cache — seedocs/specs/SPEC_MUXSPECT_DOCK_DIAGNOSIS_AND_REMEDIATION_2026_08_06.md§3.2), then publishesEVENT_DOCK_CLEARscoped toblock:<id>. Only a renderer currently displaying that block and still holding that node ever receives or acts on it (real server-side scope routing, not client-side self-filtering — same mechanismEVENT_SHELL_NODE_CREATEalready uses). Returns 404 if the node isn’t in the cache — either it was never pushed, or it already resolved/was already cleared; either way there’s nothing to do.- handle_
muxspect_ find - handle_
muxspect_ layout GET /api/v1/muxspect/layout[?tab_id=X]— the persisted layout tree for every tab (or one), plus the layout doctor’s verdict on each.- handle_
muxspect_ list GET /api/v1/muxspect/list— every controller-backed block’s currentProcessStatus, full detail (unlikeagent.tracked-blocks, which intentionally returns onlyblock_idsfor its Swarm-pane contract). Each row also carriesis_agent—ProcessStatus::is_agent()’s complete classification rule (subprocess/persistent/acp are ALWAYS agents regardless ofis_agent_pane, which only applies to shell/cmd) — so consumers don’t reimplement that rule themselves (codex P2 on PR #2380: the CLI’s own naiveis_agent_pane-only rendering mislabeled exactly those three controller types).- handle_
muxspect_ migrations GET /api/v1/muxspect/migrations— the migration doctor for THIS instance’s data dir: every registry migration with its applied/pending state and, for applied ones, the same post-condition verdictagentmux-srv migrate --verifyprints (SPEC_MIGRATION_SYSTEM_HARDENING_2026_08_03.mdPhase 1c). Built by the one producer both share (migrations::doctor_report_for_instance), so an operator inside a running instance gets exactly the answer the CLI would give against a stopped one — without hunting for the srv binary or the data dir. The incident the hardening spec was written about (§1.6: a migration “recorded as applied that wrote nothing”) is exactly a “why did this agent fail to start” question, which ismuxspect’s job.- handle_
muxspect_ verify_ sender GET /api/v1/muxspect/verify-sender?name=X— answers “is an agent named X currently registered, and via what tier” in one call, so an agent that just received a[JEKT:FROM=X ...]marker doesn’t have to fall back to manual filesystem/process archaeology to sanity-check the claimed sender exists at all. SeeVerifySenderVerdict’s doc comment for the important caveat this is NOT the JEKT protocol’s own cryptographic sender verification. Seedocs/retro/RETRO_JEKT_CROSS_ CHANNEL_TRUST_SELF_DECLARED_2026_08_21.mdfor the incident this closes the liveness-lookup gap on, anddocs/specs/SPEC_MUXSPECT_VERIFY_ SENDER_2026_08_21.mdfor the full design.- last_
error_ 🔒frame - Read the bounded tail of
block_id’soutputfile and return the parsedLastErrorFrameif its last non-blank line is one.Nonecovers every other case (no such block, empty file, last line is normal output, last line doesn’t parse as this exact frame shape) — deliberately not distinguished further, since all of them mean the same thing to a caller: nothing actionable to surface here. - last_
line_ 🔒preview_ and_ activity - Last non-blank line of a block’s own transcript, truncated for preview
display, plus a real “when was this last written” timestamp —
Nonefor either if the block has no session output yet (e.g. an agent registered but no turn has produced output) — missing is not an error, same “encode absence in the body, not the status” posture as the rest of this file. - merge_
background_ 🔒tasks - Append a synthetic
DockNodeViewfor everyRunningdb_background_tasksrow not already represented innodes(by id —db_background_tasks.idmirrors the dock’s ownnode_id, seebackground_tasks.rs’s module doc comment). Pure, unit-testable likedock_node_viewsabove. A task still present innodesis left as-is — the live cache’s own status is more specific (carriestool_name, realstuckcomputation) than anything this synthesizes from the registry alone. - truncate_
preview 🔒 - Truncate a preview line to
PREVIEW_MAX_CHARS, appending an ellipsis when it was actually cut. Shared by the host-tier and cross-channel preview paths so they can’t drift out of sync again (codex P2 on PR #2715 — the cross-channel path originally didn’t call this at all).