Type Alias: AgentPaneCommand
agentmux / agent-pane-state-store / AgentPaneCommand
AgentPaneCommand = {
type:"InitStart"; } | {at:number;type:"InitReady"; } | {at:number;reason:string;type:"InitFailed"; } | {at:number;type:"StreamSubscribe"; } | {at:number;type:"StreamUnsubscribe"; } | {addedCount:number;at:number;turnRelevant?:boolean;type:"StreamFlushObserved"; } | {nowMs:number;type:"StreamWatchdogTick"; } | {active:boolean;at:number;type:"ReconcileTurnActive"; } | {at?:number|null;model?:string|null;reportedWindows?:ReportedContextWindows|null;tokens?:number|null;type:"ReconcileContextFromHistory"; } | {type:"ContextWindowsReported";windows:ReportedContextWindows; } | {reason:"fresh_session"|"transcript_replaced";type:"ContextInvalidated"; } | {model:string;type:"ContextModelSwitched"; } | {at:number;content?:string;hidden?:boolean;type:"TurnStart"; } | {stats:SessionStats|null;type:"TurnEnd"; } | {type:"TurnReset"; } | {ledger:TurnLedger;type:"TurnObserved"; } | {type:"TurnStartFailed"; } | {arg?:string;id?:string;name:string;params?:Record<string,unknown>;parentId?:string;type:"ToolStart"; } | {id:string;text:string;type:"ToolProgress"; } | {at:number;id:string;type:"ToolOutput"; } | {text:string;type:"ThinkingHeadline"; } | {id?:string;type:"ToolEnd"; } | {cacheCreation?:number;cacheRead?:number;freshInput?:number;input:number;model?:string;type:"TokensIn"; } | {output:number;type:"TokensOut"; } | {type:"StreamSessionStarted"; } | {chars:number;kind?:"text"|"thinking"|"tool_input";type:"OutputStreamed"; } | {type:"RequestStarted"; } | {type:"ModelEndedTurn"; } | {type:"ModelMessageStarted"; } | {at:number;type:"RequestStop"; } | {type:"StopFailed"; } | {at:number;type:"InterruptTimeoutElapsed"; } | {at:number;type:"SubmitTimeoutElapsed"; } | {at:number;attachments?:object[];enqueuedWhileBusy:boolean;id:string;text:string;type:"PendingMessageQueued"; } | {id:string;type:"PendingMessageFlushStarted"; } | {id:string;type:"PendingMessageAccepted"; } | {id:string;type:"PendingMessageRejected"; } | {id:string;type:"PendingMessageExpired"; } | {at:number;reason:"rate_limited";retryAfterMs:number|null;type:"ProviderWaiting"; } | {at:number;failure:AgentFailure;turnAttempted?:boolean;type:"FailureObserved"; } | {type:"FailureCleared"; } | {type:"DetailsToggle"; } | {type:"DetailsExpand"; } | {type:"DetailsCollapse"; } | {type:"StashToggle"; } | {type:"StashExpand"; } | {type:"StashCollapse"; } | {at:number;trigger:"manual"|"auto";type:"CompactionStarted"; } | {at:number;boundaryUuid?:string|null;durationMs:number;frameTimestamp?:string|null;postTokens:number;preTokens:number;trigger:"manual"|"auto";type:"CompactionBoundary"; } | {at:number;type:"AttachedTaskObserved"; } | {type:"AttachedTaskCleared"; } | {at:number;type:"ResumeRetryStarted"; } | {type:"ResumeRetryResolved"; } | {at:number;type:"RegistryAttachedTaskObserved"; } | {type:"RegistryAttachedTaskCleared"; }
Defined in: agent-pane-state/types.ts:636
Union Members
Section titled “Union Members”Type Literal
Section titled “Type Literal”{ type: "InitStart"; }
Caller signal: history fetch began. Idempotent — if the pane is
already in any terminal state (InitReady / InitFailed),
InitStart is a no-op (we don’t reset back to pending).
Type Literal
Section titled “Type Literal”{ at: number; type: "InitReady"; }
Caller signal: history fetch resolved successfully.
Type Literal
Section titled “Type Literal”{ at: number; reason: string; type: "InitFailed"; }
Caller signal: history fetch failed; reason surfaced for diagnostics.
Type Literal
Section titled “Type Literal”{ at: number; type: "StreamSubscribe"; }
Hook signal: subscription is up.
Type Literal
Section titled “Type Literal”{ at: number; type: "StreamUnsubscribe"; }
Hook signal: subscription torn down.
Type Literal
Section titled “Type Literal”{ addedCount: number; at: number; turnRelevant?: boolean; type: "StreamFlushObserved"; }
RAF flush observed by useAgentStream — bumps bufferSize + lastEventTime. No state change beyond those counters.
addedCount
Section titled “addedCount”addedCount:
number
at:
number
turnRelevant?
Section titled “turnRelevant?”
optionalturnRelevant?:boolean
Did this flush carry anything that is evidence the TURN is alive — document nodes, node updates, tool chunks, or a new shell node?
false means the flush was shell OUTPUT only. That is a live
BACKGROUND process writing to its dock row; it says nothing about
whether the model is doing anything, so it must not refresh the
turn idle clock or resurrect a finished turn. Omitted is treated as
true — only the flush queue knows the difference, and only it
sets this.
See docs/reports/REPORT_AGENT_PANE_PROGRESS_INDICATORS_CONSOLIDATION_2026_09_09.md.
type:
"StreamFlushObserved"
Type Literal
Section titled “Type Literal”{ nowMs: number; type: "StreamWatchdogTick"; }
Periodic tick from useAgentStream’s watchdog interval. Emits a
stream-stuck diagnostic past STUCK_THRESHOLD_MS, and — past the
longer LIVENESS_RECOVERY_MS — force-recovers a hung Streaming turn
(no tool active, not rate-limited) to Idle, clearing a stuck
“Working” that never received its terminal session_end. See
SPEC_WORKING_STATE_LIVENESS_MODEL_2026_06_29.md.
Type Literal
Section titled “Type Literal”{ active: boolean; at: number; type: "ReconcileTurnActive"; }
Reconciliation from BlockControllerRuntimeStatus.turn_active
(backend-verified, from the health monitor wired to the NDJSON
stream — see crates/srv/src/backend/blockcontroller/health.rs),
fetched via GetControllerStatus at mount AND dispatched on every
live controllerstatus MPS event thereafter (useControllerStatusEvents).
Bidirectional:
active: true— promote the mount-defaultIdletoStreamingwhen the backend reports a turn already in flight. Corrects ONLY anIdlephase; never overrides a phase a real stream/user event already produced (TurnStart, StreamFlushObserved, etc. win if they got there first). #2005 Finding 1.active: false— demote a stuckStreamingphase toIdle. The backend flipsturn_activefalse only on the CLI’sresultevent (genuine turn-end), so this is authoritative. Normally the frontend reachesDoneon its own viasession_end; this covers the case where it MISSED that event (pane backgrounded/unmounted across the transition, or a dropped MPS event) and would otherwise stayStreamingforever — the Agent1 stuck-”Working” / Agent2 stuck-”Queued” class. OnlyStreamingis demoted:Submittingis covered bySUBMIT_TIMEOUT(and is where the send-race lives — a just-dispatched localTurnStartracing a backend that hasn’t yet marked the turn active),InterruptingbyINTERRUPT_TIMEOUT,Done/Idle/Disconnectedare already correct. See docs/retro/retro-agent2-stuck-queued-message-2026-07-16.md.
Type Literal
Section titled “Type Literal”{ at?: number | null; model?: string | null; reportedWindows?: ReportedContextWindows | null; tokens?: number | null; type: "ReconcileContextFromHistory"; }
Mount-time reconciliation: history replay found the main agent’s last
API call from before this pane went live (HistoryParser.lastContext
— that call’s whole prompt and model, never a result’s turn total).
Seeds context so the meter shows a real number immediately instead of
sitting blank until the first live TokensIn. Seeds only while
contextSeedable (no live reading, invalidation, compaction or reset
yet) — a historical snapshot must never clobber real-time data. An
implausible seed is refused (context-reading-rejected). tokens is
absent when the restored window holds no main-agent call.
reportedWindows (the history’s own result.modelUsage windows) merge
UNDER any reported live, either way.
See docs/plans/PLAN_PANE_REOPEN_SESSION_RESUME_AND_STATS_BAR_2026_07_10.md
and docs/reports/REPORT_AGENT_PANE_CONTEXT_METER_2026_10_05.md.
optionalat?:number|null
When that call was received (unix ms), if the line was stamped.
model?
Section titled “model?”
optionalmodel?:string|null
reportedWindows?
Section titled “reportedWindows?”
optionalreportedWindows?:ReportedContextWindows|null
tokens?
Section titled “tokens?”
optionaltokens?:number|null
type:
"ReconcileContextFromHistory"
Type Literal
Section titled “Type Literal”{ type: "ContextWindowsReported"; windows: ReportedContextWindows; }
A result frame reported context windows (modelUsage[*].contextWindow).
Recorded per model; the current reading’s window is re-resolved.
Type Literal
Section titled “Type Literal”{ reason: "fresh_session" | "transcript_replaced"; type: "ContextInvalidated"; }
The conversation the reading measured is gone — a fresh session
replaced it, or the transcript was archived or restored. Clears
context (and ends contextSeedable), so the next call’s reading isn’t
read as a compaction by the TokensIn heuristic and a late history
seed can’t bring the old conversation back.
Type Literal
Section titled “Type Literal”{ model: string; type: "ContextModelSwitched"; }
The pane’s model setting changed (/model, the model menu). The
conversation, and so the reading’s tokens, carry over; the window is
the new model’s, known only when its first reply reports it, so the
reading’s window becomes unknown until then. model is the setting’s
value (often a family alias, e.g. sonnet, which doesn’t say its
window).
Type Literal
Section titled “Type Literal”{ at: number; content?: string; hidden?: boolean; type: "TurnStart"; }
User pressed send — turn becomes active. Also clears stale sessionStats from the previous turn.
content — the literal text just submitted, threaded through into
TurnPhase.Submitting.pendingContent (optional so the ~90 existing
reducer/hook test call sites that construct this action without it
keep compiling — the reducer defaults to "" when absent). Used by
useAgentActivitySummary.ts to derive the session-title Haiku call
without a separate FileStore read. See
docs/specs/SPEC_AMBIENT_PANE_TITLE_OVERALL_GOAL_TRACKING_2026_08_17.md.
at:
number
content?
Section titled “content?”
optionalcontent?:string
hidden?
Section titled “hidden?”
optionalhidden?:boolean
True for a hidden memory-reinjection turn
(memory-reinjection-controller.ts) — never set by any
ordinary send. Propagated onto TurnPhase.Submitting.hidden
so every ambient side-effect hook keyed off turnPhase
(useAgentActivitySummary.ts, useNextPromptSuggestion.ts,
and any future one) can skip its own LLM call for this turn
without needing separate wiring — turnPhase already reaches
them as their effect dependency. Found necessary the hard way:
useAgentActivitySummary.ts forwarded pendingContent
(unconditionally, regardless of source) to an ambient
summarization call whose result became the human-visible pane
title — reagentx P0 on PR #3502. Defense-in-depth, not the
only fix: content for a hidden turn is ALSO never the real
message (see memory-reinjection-controller.ts) — a consumer
that forgets to check hidden still never sees real content,
only a safe placeholder.
type:
"TurnStart"
Type Literal
Section titled “Type Literal”{ stats: SessionStats | null; type: "TurnEnd"; }
Stream produced session_end (or fallback timer fired). Final stats merged with current turn-tokens, which are still there when the backend’s turn-ended push already moved the phase to Idle. Clears currentTool and turnTokens, and transitions the phase to Done (interrupting → Done.stopped, otherwise Done.completed).
Type Literal
Section titled “Type Literal”{ type: "TurnReset"; }
Truncate path: the doc was reset (or whatever caused the local stream-restart). Clears all per-turn state but does NOT touch pending or streaming.
Type Literal
Section titled “Type Literal”{ ledger: TurnLedger; type: "TurnObserved"; }
srv published the block’s turn ledger (agentturn). An older turn’s,
or an older state of this one, landing late is ignored.
Type Literal
Section titled “Type Literal”{ type: "TurnStartFailed"; }
Revert an OPTIMISTIC TurnStart when the turn never actually began —
the initiating send’s own RPC call failed synchronously (no
controller registered, the identity spawn gate blocked it, a plain
network rejection), or the input was a slash/bang command handled
locally. Phase-only: unlike TurnReset, this must NOT
touch sessionStats/sessionTotals/context — those
accumulate across a pane’s whole lifetime, and a transient send
failure on an agent with prior completed turns must not wipe that
history (reagent/codex P2 on PR #2318 — TurnReset was reused here
first and incorrectly cleared them). See
useAgentCommands.ts’s deliverToBackend.
Type Literal
Section titled “Type Literal”{ arg?: string; id?: string; name: string; params?: Record<string, unknown>; parentId?: string; type: "ToolStart"; }
id: the tool_use id, so its end removes exactly it; params: its
input, for the live status’s words (tool-labels.ts).
Type Literal
Section titled “Type Literal”{ id: string; text: string; type: "ToolProgress"; }
A running call’s test progress, from its live output (status/test-progress.ts).
Type Literal
Section titled “Type Literal”{ at: number; id: string; type: "ToolOutput"; }
A running call wrote output (at most every few seconds per call).
Type Literal
Section titled “Type Literal”{ text: string; type: "ThinkingHeadline"; }
The model’s thinking block’s gist (status/thinking-headline.ts).
Type Literal
Section titled “Type Literal”{ id?: string; type: "ToolEnd"; }
Type Literal
Section titled “Type Literal”{ cacheCreation?: number; cacheRead?: number; freshInput?: number; input: number; model?: string; type: "TokensIn"; }
Type Literal
Section titled “Type Literal”{ output: number; type: "TokensOut"; }
Type Literal
Section titled “Type Literal”{ type: "StreamSessionStarted"; }
The stream shows a new CLI session starting (system/init, not the one
a compaction writes mid-turn; view/agent/session-start.ts): any turn
still holding tokens ended without a result (the process died), so
they belong to no turn that is coming.
Type Literal
Section titled “Type Literal”{ chars: number; kind?: "text" | "thinking" | "tool_input"; type: "OutputStreamed"; }
Characters the main agent’s latest call streamed (text, thinking, tool
input) since the last one: its output estimate until TokensOut.
Type Literal
Section titled “Type Literal”{ type: "RequestStarted"; }
The main agent’s tool results went back: a request is in flight (↑)
until the next call’s TokensIn.
Type Literal
Section titled “Type Literal”{ type: "ModelEndedTurn"; }
The main agent’s model sent stop_reason: end_turn; see modelEndedTurn on the Streaming phase.
Type Literal
Section titled “Type Literal”{ type: "ModelMessageStarted"; }
The main agent’s model began a new message; it is working again.
Type Literal
Section titled “Type Literal”{ at: number; type: "RequestStop"; }
User pressed Esc / clicked Stop.
Type Literal
Section titled “Type Literal”{ type: "StopFailed"; }
Stop RPC failed — bail on the stopping state.
Type Literal
Section titled “Type Literal”{ at: number; type: "InterruptTimeoutElapsed"; }
Interrupt timeout watchdog fired (caller-scheduled setTimeout — see
schedule-interrupt-timeout event). Bounded Interrupting → Done
force-transition: if the agent never acks the SIGINT (backend died,
stream dropped, etc.) the pane was previously stuck in Interrupting
forever and the working animation would never settle. This command
is a no-op if the phase has already moved off Interrupting
(e.g. agent acked first, user already resumed). Spec §8.
Type Literal
Section titled “Type Literal”{ at: number; type: "SubmitTimeoutElapsed"; }
Submit timeout watchdog fired (caller-scheduled setTimeout — see
schedule-submit-timeout event). PR D — bounded
Submitting → Done.errored force-transition: if the backend never
acks a TurnStart with a chunk / token / tool event within
SUBMIT_TIMEOUT_MS, the pane was previously stuck in Submitting
forever (no isWorking → false signal) and the working animation
would never settle. No-op if the phase has already moved off
Submitting (e.g. promotion to Streaming via flush / bumpEvent,
user already stopped, stream dropped). Spec §8 / issue #728 gap 2.
Type Literal
Section titled “Type Literal”{ at: number; attachments?: object[]; enqueuedWhileBusy: boolean; id: string; text: string; type: "PendingMessageQueued"; }
at:
number
attachments?
Section titled “attachments?”
optionalattachments?:object[]
Images sent with the message (ids + names, tray order).
enqueuedWhileBusy
Section titled “enqueuedWhileBusy”enqueuedWhileBusy:
boolean
True when the user sent this message while a turn was already in-flight (isWorking was true before TurnStart). False for idle sends. Stored on the PendingMessage entry and used by PendingMessagesPanel to gate visibility — idle-send messages must never flash in the amber queued zone. See ANALYSIS_IDLE_SEND_RACE_2026_06_11 (never committed to this repo).
id:
string
text:
string
type:
"PendingMessageQueued"
Type Literal
Section titled “Type Literal”{ id: string; type: "PendingMessageFlushStarted"; }
flushHeldMessages (useAgentCommands.ts) is about to actually deliver
this held message — dispatched right before the deliverToBackend
call, i.e. the moment sending genuinely begins, not merely “the turn
it was queued behind has ended” (those can be far apart when a
controller refresh is deferred — see the PendingMessageFlushStarted
reducer arm’s own doc comment). Marks the entry flushing so
PendingMessagesPanel can stop claiming “sends at the agent’s next
step” once that’s no longer true. See
docs/specs/SPEC_AGENT_WORKING_STATE_UNIFICATION_2026_09_04.md Phase 1.
Type Literal
Section titled “Type Literal”{ id: string; type: "PendingMessageAccepted"; }
Backend acknowledged the message — remove from pending.
Type Literal
Section titled “Type Literal”{ id: string; type: "PendingMessageRejected"; }
RPC failed — remove the entry so user doesn’t see a ghost row.
Type Literal
Section titled “Type Literal”{ id: string; type: "PendingMessageExpired"; }
Pending entry ageing watchdog fired (caller-scheduled setTimeout). Removes the entry by id; idempotent no-op if already accepted/rejected. Issue #728 gap 2 — prevents zombie pending rows when the backend never acknowledges a queued message (e.g. RPC hang post-send).
Type Literal
Section titled “Type Literal”{ at: number; reason: "rate_limited"; retryAfterMs: number | null; type: "ProviderWaiting"; }
Provider is rate-limited (429) and waiting to retry. Updates
lastEventMs (keeps the stuck-stream watchdog quiet) and sets
waitingReason on Streaming so the working row can show
“Rate limited — retrying…” instead of the thinking phrase.
Cleared automatically on the next real activity (bumpEvent).
Type Literal
Section titled “Type Literal”{ at: number; failure: AgentFailure; turnAttempted?: boolean; type: "FailureObserved"; }
Backend classified a failure for this pane (the agentfailure wave
event). Unconditionally force-ends a working turn (Submitting /
Streaming / Interrupting → Done.errored) — this is what closes the
“stuck Waiting after a rate-limit interruption” bug: an authoritative
failure classification no longer depends on the CLI process actually
exiting (persistent-mode agents never do between turns) to clear the
turn phase. See SPEC_AGENT_PANE_UNIFIED_FAILURE_REDUCER_2026_07_06.md.
at:
number
failure
Section titled “failure”failure:
AgentFailure
turnAttempted?
Section titled “turnAttempted?”
optionalturnAttempted?:boolean
See PaneFailure.turnAttempted. Omitted by the backend-event
path (a classified failure always FOLLOWS a turn that ran), set to
false only by the pane’s own synthetic pre-launch auth failure.
type:
"FailureObserved"
Type Literal
Section titled “Type Literal”{ type: "FailureCleared"; }
User dismissed the failure row (Dismiss / ×). Episode over.
Type Literal
Section titled “Type Literal”{ type: "DetailsToggle"; }
Toggle the log panel open/closed.
Type Literal
Section titled “Type Literal”{ type: "DetailsExpand"; }
Explicitly open the log panel. Idempotent if already open.
Type Literal
Section titled “Type Literal”{ type: "DetailsCollapse"; }
Explicitly close the log panel. Idempotent if already closed.
Type Literal
Section titled “Type Literal”{ type: "StashToggle"; }
Toggle the Stash drawer open/closed. Drives the header icon’s own toggle.
Type Literal
Section titled “Type Literal”{ type: "StashExpand"; }
Explicitly open the Stash drawer. Idempotent if already open.
Type Literal
Section titled “Type Literal”{ type: "StashCollapse"; }
Explicitly close the Stash drawer. Idempotent if already closed.
Type Literal
Section titled “Type Literal”{ at: number; trigger: "manual" | "auto"; type: "CompactionStarted"; }
The PreCompact hook fired — compaction has begun. Sourced from
the compaction_started MPS event (agentmux-bashwrap precompact
→ useCompactionStream.ts). Sets compacting so the view can
show a live “Compacting…” status chip + elapsed counter (Tier
1/2). Also bumps lastEventMs like ProviderWaiting does, so
the stuck-stream watchdog doesn’t misfire during a long
compaction that produces no other stream activity.
Type Literal
Section titled “Type Literal”{ at: number; boundaryUuid?: string | null; durationMs: number; frameTimestamp?: string | null; postTokens: number; preTokens: number; trigger: "manual" | "auto"; type: "CompactionBoundary"; }
The real compact_boundary frame arrived — compaction finished.
Sourced from the backend’s AgentEvent::CompactionBoundary
(translated 1:1 from Claude Code’s stream-json system frame —
exact data, not inferred). Clears compacting, records
lastCompactionBoundaryAt (dedup guard for the TokensIn
heuristic), and reconciles context to postTokens so
the context-fill bar reflects the real post-compaction size
immediately rather than waiting for the next TokensIn.
at:
number
boundaryUuid?
Section titled “boundaryUuid?”
optionalboundaryUuid?:string|null
The frame’s own uuid: keys the emitted context-compacted
node’s id, so it matches parseHistoryLines.ts’s id for the
same line. Optional for callers without a frame.
durationMs
Section titled “durationMs”durationMs:
number
frameTimestamp?
Section titled “frameTimestamp?”
optionalframeTimestamp?:string|null
The compact_boundary frame’s own timestamp field, raw
(not parsed/reformatted) — absent/null if the frame didn’t
have one (or the caller doesn’t have a raw frame at all, e.g.
a test constructing this command directly). A time value
only: the boundary’s completion time for the stale-boundary
checks and the node’s time, falling back to at / receipt
time when absent (the CLI’s stdout frame has none). Never an
id — see boundaryUuid.
postTokens
Section titled “postTokens”postTokens:
number
preTokens
Section titled “preTokens”preTokens:
number
trigger
Section titled “trigger”trigger:
"manual"|"auto"
type:
"CompactionBoundary"
Type Literal
Section titled “Type Literal”{ at: number; type: "AttachedTaskObserved"; }
Fires on the 0→1 transition of “≥1 PinnedActivity is running for
this block.” No-op (idempotent) if an episode is already in
progress — must NOT reset since when a second task starts running
alongside an already-running one.
Type Literal
Section titled “Type Literal”{ type: "AttachedTaskCleared"; }
Fires on the 1→0 transition — the last currently-running
PinnedActivity for this block just ended. No-op if already null.
Type Literal
Section titled “Type Literal”{ at: number; type: "ResumeRetryStarted"; }
mps.EVENT_AGENT_RESUME_RETRY’s {status:"retrying"} landed — a
stale --resume was just detected and a retry/recovery attempt is
about to fire. No-op (idempotent) if already reconnecting, mirroring
AttachedTaskObserved — a cascaded second retry must not reset
startedAt back to a later time.
Type Literal
Section titled “Type Literal”{ type: "ResumeRetryResolved"; }
{status:"resolved"} landed — the retry’s outcome (Fresh or Resumed)
is now known. No-op if already null.
Type Literal
Section titled “Type Literal”{ at: number; type: "RegistryAttachedTaskObserved"; }
Sets registryAttachedTaskSince verbatim (idempotent set, not an
edge-triggered toggle like AttachedTaskObserved — the registry’s own
earliest-running-task computation already handles the “already
observed” case, so the reducer doesn’t need its own no-op guard).
Type Literal
Section titled “Type Literal”{ type: "RegistryAttachedTaskCleared"; }
Clears registryAttachedTaskSince back to null.