Mobile half of the agent sub-status indicator.
The phase is derived and emitted by the streamer — see threadbase-streamer#538.
Blocked on that shipping, since there is no field to render until it does.
Verified state
Checked 2026-08-12 against origin/main @ 86eeb2a1.
The earlier client-side attempt, PR #647, was closed unmerged, so none of its code is on main — verified individually: lib/agentSubStatus.ts absent, getTailLines zero hits in services/virtual-terminal.ts, "working" zero hits across all four locales.
There is nothing to remove.
What this actually costs
A review pass found the obvious estimate — add a field to a type and render it — to be wrong on three counts.
SessionPresentationInput (lib/sessionPresentation.ts:58-72) is standalone, enumerating eleven fields rather than deriving from Session. Adding subStatus to types/api.ts alone leaves deriveSessionPresentation unable to see it.
- Gate on
presentation.live, not raw status. deriveSessionPresentation has branches that never consult status — external_live keys off ownership + processLiveness, stale off processLiveness === 'gone' — and status is a loose string by design. A raw-status gate could render a phase indicator beside a badge reading "Idle" or "External". SessionStatusBadge.tsx:47 already uses presentation.live for the LiveDot the indicator sits beside, and SessionCard.tsx:79-82 states the rule: one answer for liveness, not a third derivation.
- No client-side time decay. The app has no precedent for it, and
sessionPresentation.ts is a pure function — nothing re-invokes it when a clock crosses a threshold, so a freshness gate would clear only on an unrelated re-render. The established idiom is processLiveness === 'gone': the server decides liveness and says so. The streamer clears the phase explicitly at turn end.
Theming is bimodal. colorForToken is module-private to components/sessions/SessionStatusBadge.tsx:18 and bridges two closed five-value unions. Reusing an existing token is free; a phase-specific colour means extending the union and the Theme interface and adding the colour to 17 theme objects. Never a hardcoded hex — #647 used #3fb950, invisible on the four light themes.
Question-card suppression should copy components/conversation/ThinkingBubble.tsx:107-134, which correctly covers both the structured question and the PTY-scraped block.
Tree mode (components/sessions/tree/TreeRow.tsx:63) is deliberately out of scope.
Plan
threadbase-streamer#537 -> docs/plans/2026-08-12-agent-sub-status-server-side.md, Part B.
Mobile half of the agent sub-status indicator.
The phase is derived and emitted by the streamer — see threadbase-streamer#538.
Blocked on that shipping, since there is no field to render until it does.
Verified state
Checked 2026-08-12 against
origin/main@86eeb2a1.The earlier client-side attempt, PR #647, was closed unmerged, so none of its code is on
main— verified individually:lib/agentSubStatus.tsabsent,getTailLineszero hits inservices/virtual-terminal.ts,"working"zero hits across all four locales.There is nothing to remove.
What this actually costs
A review pass found the obvious estimate — add a field to a type and render it — to be wrong on three counts.
SessionPresentationInput(lib/sessionPresentation.ts:58-72) is standalone, enumerating eleven fields rather than deriving fromSession. AddingsubStatustotypes/api.tsalone leavesderiveSessionPresentationunable to see it.presentation.live, not rawstatus.deriveSessionPresentationhas branches that never consultstatus—external_livekeys offownership+processLiveness,staleoffprocessLiveness === 'gone'— andstatusis a loosestringby design. A raw-statusgate could render a phase indicator beside a badge reading "Idle" or "External".SessionStatusBadge.tsx:47already usespresentation.livefor theLiveDotthe indicator sits beside, andSessionCard.tsx:79-82states the rule: one answer for liveness, not a third derivation.sessionPresentation.tsis a pure function — nothing re-invokes it when a clock crosses a threshold, so a freshness gate would clear only on an unrelated re-render. The established idiom isprocessLiveness === 'gone': the server decides liveness and says so. The streamer clears the phase explicitly at turn end.Theming is bimodal.
colorForTokenis module-private tocomponents/sessions/SessionStatusBadge.tsx:18and bridges two closed five-value unions. Reusing an existing token is free; a phase-specific colour means extending the union and theThemeinterface and adding the colour to 17 theme objects. Never a hardcoded hex — #647 used#3fb950, invisible on the four light themes.Question-card suppression should copy
components/conversation/ThinkingBubble.tsx:107-134, which correctly covers both the structured question and the PTY-scraped block.Tree mode (
components/sessions/tree/TreeRow.tsx:63) is deliberately out of scope.Plan
threadbase-streamer#537 ->
docs/plans/2026-08-12-agent-sub-status-server-side.md, Part B.