feat(agents): synthesize shell_output_delta for claude-code via container log stream #994
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!994
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "code-lead/957"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Synthesises
shell_output_deltaevents for the claude-code SDK so the dashboard's live shell pane streams stdout/stderr fromBashtool calls in real time — parity with the cursor adapter's SDK-native delta channel. Closes #957.Approach — Option A (bash wrapper via PreToolUse hook)
Considered three options from the issue:
teeso the model'stool_resultis unchanged. Picked.Pipeline
/usr/local/bin/shell-stream-hook+cl-shell-stream-cap.renderForInstance()injects shell-stream-hook intosettings.json'sPreToolUseBash matcher, ordered after rtk so rtk's compression rewrite runs first; the wrapper rewrites the rtk-rewritten command.cl-shell-stream-capappends<call_id>\t<stream>\t<base64>\nframes inside the container, splitting at 2 KiB so each append stays atomic under PIPE_BUF.ShellStreamTailerrunsdocker exec … tail -n0 -F …, decodes frames, and emitsShellOutputDeltaEvents through the existingonTaskEventpipe.Wire format
Reuses the existing
ShellOutputDeltaEventshape fromclaude-port.ts. UI doesn't know which provider produced the event.Edge cases (all in tests)
callId. UI re-stitches.tailer.stop()runs in the runner'sfinallyblock (and onAbortSignal) so the docker-exec subprocess is killed cleanly;start()'s promise is awaited so we never leak a zombie tailer.CLAUDE_HOOKS_SHELL_STREAM=offdisables the host tailer without touching the in-container hook (hot, no rebuild).Test plan
just qaclean (typecheck + lint + format + tests + sql/paraglide checks)parseShellLogLine: valid / malformed / EOF / utf-8 / CRLF.LineBuffer: chunked stream, partial trailing line, split UTF-8.ShellStreamTailer:callId.callId.renderForInstance: shell-stream hook is injected idempotently and ordered after rtk.bun test(~30 s); confirm dashboard pane streams output as it lands rather than waiting for completion.Closes #957
qa+qa-1red on run #1742 (sha6b46ef2). Failures are snapshot mismatches inapps/web/src/components/agent/tool-card.test.tsx(duration value3svs5sin snapshot), unrelated to this PR's server-only changes. All new server tests pass locally (16 tailer + 29 render-for-instance). Rebase on main to pick up any snapshot updates from concurrent PRs; if the failure persists on main independently, update the snapshots there first.