feat(tui-go): inline action rows in the OUTPUT transcript

Surface side-effecting ("external feedback") events in the conversation
flow, opencode-style, instead of only in the EVENTS side log.

- New RouterEntry role "action" (glyph + text) appended at arrival so it
  interleaves with chat turns by order. Rendered dim with an accent gutter
  glyph; the EVENTS panel still carries the full stream.
- Surfaced: tool start (⏵), tool done (✓ / ✎ wrote <path> (+a −b) for a
  diff), tool failed (✗), rejected (✕ blocked), approval resolved
  (⌘ approved / ✕ rejected, noting "· via grant" on a grant auto-approve),
  and grant create/revoke (⊞ / ⊟ · scope). ApprovalResolved names no tool
  on the wire, so the tool is recovered from the pending queue before the
  gate is dropped.
- Toggle: "inline actions" palette command flips actionsHidden (rows are
  always recorded, gated at render, so toggling reveals history); persisted
  in tui-prefs.json. prefs.go refactored to load/mutate/save so the new
  field and statusbarHidden no longer clobber each other.
- demo.go: "actions" preview kind.

go build / vet / test green; rendered the flow via cmd/preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 23:00:18 +00:00
parent 3f4c45a7b0
commit 6a06f2ead5
6 changed files with 191 additions and 27 deletions
+7 -1
View File
@@ -65,8 +65,9 @@ const (
// RouterEntry is one line in a session's conversation transcript.
type RouterEntry struct {
Role string // user | router | tool | narration | narration_llm
Role string // user | router | tool | narration | narration_llm | action
Content string
Icon string // action role only: the gutter glyph (⏵ ✓ ✎ ⌘ ✕ ⊞ ⊟)
Metrics *TurnMetrics
}
@@ -322,6 +323,10 @@ type Model struct {
sbHidden map[string]bool
sbIndex int
// actionsHidden mutes the inline action rows (tool calls / writes / approvals / grants) in
// the OUTPUT transcript; they always stay in the EVENTS panel. Persisted in tui-prefs.json.
actionsHidden bool
// standing grants (OverlayGrants) — active PROJECT/GLOBAL grants from the grant.list reply.
grants []protocol.GrantDto
grantsLoading bool
@@ -385,6 +390,7 @@ func NewModel(client *ws.Client) Model {
eventStripShown: true,
transcriptSel: -1,
sbHidden: loadStatusbarHidden(),
actionsHidden: loadPrefs().InlineActionsHidden,
}
}