fix(desktop): keep agent hook debug logs off the PTY so they can't corrupt TUIs
Summary
- Agent lifecycle hooks (notify-hook, gemini-hook) were writing debug/diagnostic lines to stderr. Hooks run as children of TUI agents (OpenCode, Claude, Gemini) with stdio inherited from the PTY, so those lines printed at the cursor position and corrupted the TUI's diff-rendered screen — interleaved
[notify-hook]lines and orphaned text fragments overlaying the UI. - All diagnostics now go exclusively to the existing
debug_logfile channel (/tmp/superset-agent-hooks.log, overridable viaSUPERSET_HOOK_DEBUG_LOG). No hook template writes to stdout/stderr anymore (gemini-hook still prints its protocol-required{}JSON to stdout).
Why / Context
notify-hook.template.sh auto-enables debug output when SUPERSET_ENV=development, and its stderr echoes were exact duplicates of the debug_log file writes next to them. In dev builds, every agent lifecycle event (Start/Stop/PermissionRequest) sprayed log lines into the agent's TUI. Diff-rendering TUIs never recover from foreign writes: they repaint only cells they believe changed, so hook text persists as garbage and bottom-row writes desync their screen model by one line. gemini-hook.template.sh additionally had an ungated stderr echo on its v2→v1 fallback path that fired even in production.
How It Works
notify-hook.template.sh: dropped the three stderr echoes; the event-receiveddebug_logline now carries the full field set the echo had (addspaneId,workspaceId). The v1 dispatch was duplicated into debug/non-debug branches solely so debug mode could echo the status code — collapsed to a single branch that always captures the status anddebug_logs it.gemini-hook.template.sh: the fallback echo now appends to the same log file (unconditional, since it's a rare failure-path signal).- New regression test asserts no hook template contains
>&2.
Debugging workflow is unchanged in capability: tail -f /tmp/superset-agent-hooks.log instead of reading interleaved (and mangled) lines inside the TUI.
Manual QA
- Rendered the template (
{{MARKER}}/{{DEFAULT_PORT}}substituted) and executed it with a fake Stop payload:- with
SUPERSET_DEBUG_HOOKS=1: stdout+stderr empty, log file receives the event line (full field set) and dispatch status line - without debug: stdout+stderr empty
- with
bash -non both modified templates.- Rollout is automatic:
createNotifyScript()rewrites the installednotify.shon next app launch via content comparison (no marker bump needed — hook semantics unchanged, wire payloads identical).
Testing
bun test src/main/lib/agent-setup(73 pass, includes new no-stderr regression test)bun run lintbun run typecheck(desktop)
Known Limitations
- OpenCode's payload fields (
hookSessionId/resourceId) parse as empty in the notify hook — pre-existing and unrelated to the logging channel; the Stop event still dispatches. Worth a separate ticket.
Summary by cubic
Keep agent hook debug output off the PTY to stop corrupting diff-rendered TUIs. All diagnostics now go to the hook debug log file instead of stdout/stderr.
- Bug Fixes
- Removed all stderr echoes from
notify-hook.template.shandgemini-hook.template.sh; diagnostics append to${SUPERSET_HOOK_DEBUG_LOG:-/tmp/superset-agent-hooks.log}. Gemini still prints only the required JSON to stdout. - Simplified
notify-hookv1 dispatch into a single path and logged status with more context fields (paneId,workspaceId, etc.). - Added a regression test to ensure no hook template writes to stderr (
>&2) and updated expectations for the notify script content.
- Removed all stderr echoes from
Summary by CodeRabbit
- Bug Fixes
- Prevented agent hook diagnostics from appearing unexpectedly in terminal output or interfering with command-line protocols.
- Improved logging for host-service dispatch failures, including response status details and event context.
- Preserved fallback behavior when host-service delivery is unavailable, while recording failures in the debug log.