Skip to content

web: surface core's turn modeling in the transcript, header, and inspect panel - #18

Merged
PerishCode merged 4 commits into
mainfrom
feat/turn-closure
Jun 12, 2026
Merged

web: surface core's turn modeling in the transcript, header, and inspect panel#18
PerishCode merged 4 commits into
mainfrom
feat/turn-closure

Conversation

@PerishCode

Copy link
Copy Markdown
Owner

Summary

"Turn closure": surface core's already-modeled agent runtime in the UI instead of flattening it into a message list. Zero core/contracts changes — the whole PR is mqueue → hooks → web.

  • Turn-grouped transcript (d146b20): mqueue now listens to turn_started (core emitted it; the browser dropped it), keeps turnsBySessionId, and builds TurnGroup[] with attribution rules (tool calls via turn_id, streaming transients via stream_<turn_id>, user messages via trigger_ref, the rest via the input_through_session_seq watermark; unattributable items fall into envelope-less groups so old data always renders). Turn failures render in place in the transcript with their error_text; the composer notice suppresses errors a failed turn already carries. The projection write surface moved to projection.ts.
  • Streaming visibility (adfa850): pending messages show generating… instead of a timestamp; the header busy chip names the running turn's actual phase (running tool / generating / thinking).
  • Agent inspect panel (c6dffe6): the runtime snapshot was fetched and stored but never read. useSessionRuntime() + an Inspect toggle in the header showing session memory (with seq watermark), compaction summaries, and effect statuses.
  • Rail previews + deferred ledger (ad89db2): untitled sessions show their first user message instead of sess_<uuid>; README records core capabilities deliberately not yet surfaced, each with its unlock condition.

Test plan

  • Live-verified via playwright: history rebuild, streaming send, SSE turn_failed in-place rendering, HTTP-rebuilt failed turns, recovery after provider restore, inspect panel toggle, rail preview switch on first send
  • pnpm -r typecheck / build clean; pre-commit (fmt, flavor, sidecar) green per commit

🤖 Generated with Claude Code

Surface core's Turn state machine in the browser projection instead of
flattening it away:

- mqueue: listen to turn_started SSE (core already emitted it; the
  browser dropped it), keep turnsBySessionId, mark turns failed on
  turn_failed, and merge turns from runtime snapshots and send
  responses.
- mqueue: build TurnGroup[] from the flat timeline. Attribution:
  tool calls via turn_id, streaming transients via the stream_<turn_id>
  synthetic id, user messages via trigger_ref, everything else via the
  input_through_session_seq watermark. Unattributable items fall into
  envelope-less groups so old data always renders.
- mqueue: extract the projection write surface into projection.ts; it
  owns every mutation that keeps messages, the flat timeline, and the
  turn-grouped timeline consistent.
- hooks: replace useSessionTimeline with useSessionTurnTimeline.
- web: Transcript renders turn groups; a failed turn shows TURN FAILED
  with its error_text in place in the transcript, and the composer
  notice suppresses errors a failed turn already carries.

Verified live: history rebuild, streaming send, SSE turn_failed,
HTTP-rebuilt failed turns, and recovery after provider restore.
- A pending (streaming) message shows 'generating…' in place of its
  timestamp instead of rendering identically to a fixed message.
- The header busy chip derives its label from the running turn's actual
  phase — 'running tool' while a tool call awaits its result,
  'generating' while a pending message streams, 'thinking' otherwise,
  with 'sending' as the no-turn fallback. The chip system stays
  quiet-by-default.

Verified live: 'generating' chip during a streaming reply, normal
completion afterwards.
The runtime snapshot (selectAndGet already fetches it) was stored in the
projection but nothing read it. Sign for it:

- mqueue/hooks: re-export the runtime domain types (SoulSession,
  Compact, SessionEffect, SessionRuntimeSnapshot) and add
  useSessionRuntime() plus a refreshRuntime action.
- web: an Inspect toggle in the chat header swaps the transcript for a
  read-only panel showing the soul session's memory (with its
  last-seen seq watermark), compaction summaries with the seq ranges
  they replace, and hook effect statuses. Switching sessions returns
  to the transcript; opening the panel refreshes the snapshot.

First visible landing of the 'transparent, inspectable local agent'
direction. Verified live: panel renders memory/compacts/effects empty
states with real seq watermark, toggle restores the transcript.
…d ledger

- hooks: useSessionPreviews() exposes the first user-authored line of
  each loaded session; the rail falls back title -> preview -> raw id,
  so untitled sessions stop reading as sess_<uuid>.
- README: record core capabilities deliberately not yet surfaced in the
  web UI (forking, image parts, message edit/delete, soul-view timeline,
  effects detail), each with its unlock condition, so deferral stays
  distinguishable from neglect.

Verified live: a fresh untitled session shows its raw id until the
first send, then the rail row switches to the message preview.
@PerishCode
PerishCode merged commit 034054d into main Jun 12, 2026
4 checks passed
@PerishCode
PerishCode deleted the feat/turn-closure branch June 12, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant