Background
While code-tracing the #136 / #138 macOS setRawMode errno 5 chain (the claude-code-cli runtime fix shipped in v0.10.0 via launchAgent's await-child Promise), I noticed an independent latent issue in the claude-agent-sdk runtime path that no user has triggered yet but could surface the same kind of TTY problem in interactive scenarios.
Filed today per a commitment to 通信龙 (commhub message 5f9f9a29, post-v0.9.2 ship): "today/明天 open separate track".
Observation
The claude-agent-sdk runtime path doesn't invoke claude-code-cli directly. agent-node uses query() from @anthropic-ai/claude-agent-sdk, which spawns the bundled claude binary as a child process:
agent-node/src/cli.ts:679 pathToClaudeCodeExecutable: claudePath // passed to query()
agent-node/src/cli.ts:705 for await (const message of query({ prompt, options }))
The SDK's Options type documents an escape hatch for the spawn:
node_modules/@anthropic-ai/claude-agent-sdk/sdk.d.ts:1791
spawnClaudeCodeProcess?: (options: SpawnOptions) => SpawnedProcess
agent-node does not currently set spawnClaudeCodeProcess, so the SDK falls back to its internal default spawn. Per the SDK type docs ("When provided, this function is called instead of the default local spawn") and the way query() consumes the agent stream over stdout, the default spawn very likely uses stdio: ['ignore' | 'pipe', 'pipe', 'pipe'] — i.e. stdin is not inherited from the parent terminal to the spawned claude binary.
Why nothing's on fire today
The claude-agent-sdk runtime path is primarily exercised in fan-out / batch scenarios where no user TTY interaction is expected. claude-side calls like setRawMode are skipped when no controlling TTY is present (or fall through silently). So the gap exists but doesn't bite.
If/when an interactive use case for the claude-agent-sdk runtime emerges (think: anet node start <alias> against a --runtime claude-agent-sdk node, where the user types into the spawned claude binary), the same family of setRawMode errno 5 issues that the claude-code-cli runtime had with #136 / #138 could re-surface in this codepath.
Suggested investigation (P2, no urgency)
- Confirm the SDK default spawn shape by checking the minified
sdk.mjs (or running a quick tee/strace probe on what fd 0 looks like inside the spawned child). My current claim is inferred from the type docs + the fact that the SDK consumes the agent stream via stdout pipe — not directly proven.
- If confirmed: agent-node should opt-in to
spawnClaudeCodeProcess and pass stdio: ['inherit', 'pipe', 'inherit'] (or equivalent) so stdin is a real PTY when the user is interactive, while keeping stdout pipe for the SDK's stream consumption.
- Add an interactive scenario to the
release-gate-playbook.md (#85) so future regressions in this lane are caught the way pexpect + chain-test caught the v0.9.2 chain.
Cross-references
Acceptance criteria
Author-Agent: 通信工程马
Priority: P2 — no user-facing impact today, latent risk if claude-agent-sdk interactive flow ever lands
Estimated effort: ~1.5h investigation + ~30min ship (if fix needed) + playbook addendum
Background
While code-tracing the #136 / #138 macOS
setRawMode errno 5chain (theclaude-code-cliruntime fix shipped in v0.10.0 vialaunchAgent's await-child Promise), I noticed an independent latent issue in theclaude-agent-sdkruntime path that no user has triggered yet but could surface the same kind of TTY problem in interactive scenarios.Filed today per a commitment to 通信龙 (commhub message
5f9f9a29, post-v0.9.2 ship): "today/明天 open separate track".Observation
The
claude-agent-sdkruntime path doesn't invokeclaude-code-clidirectly. agent-node usesquery()from@anthropic-ai/claude-agent-sdk, which spawns the bundledclaudebinary as a child process:The SDK's
Optionstype documents an escape hatch for the spawn:agent-node does not currently set
spawnClaudeCodeProcess, so the SDK falls back to its internal default spawn. Per the SDK type docs ("When provided, this function is called instead of the default local spawn") and the wayquery()consumes the agent stream over stdout, the default spawn very likely usesstdio: ['ignore' | 'pipe', 'pipe', 'pipe']— i.e. stdin is not inherited from the parent terminal to the spawnedclaudebinary.Why nothing's on fire today
The
claude-agent-sdkruntime path is primarily exercised in fan-out / batch scenarios where no user TTY interaction is expected.claude-side calls likesetRawModeare skipped when no controlling TTY is present (or fall through silently). So the gap exists but doesn't bite.If/when an interactive use case for the
claude-agent-sdkruntime emerges (think:anet node start <alias>against a--runtime claude-agent-sdknode, where the user types into the spawned claude binary), the same family ofsetRawMode errno 5issues that theclaude-code-cliruntime had with #136 / #138 could re-surface in this codepath.Suggested investigation (P2, no urgency)
sdk.mjs(or running a quicktee/straceprobe on what fd 0 looks like inside the spawned child). My current claim is inferred from the type docs + the fact that the SDK consumes the agent stream via stdout pipe — not directly proven.spawnClaudeCodeProcessand passstdio: ['inherit', 'pipe', 'inherit'](or equivalent) so stdin is a real PTY when the user is interactive, while keeping stdout pipe for the SDK's stream consumption.release-gate-playbook.md(#85) so future regressions in this lane are caught the waypexpect+chain-testcaught the v0.9.2 chain.Cross-references
docs/research/intern-tool-calling-code-trace.md(Layer 3 section, generated for [bug][P0] intern-s2-preview 模型不会调用 MCP 工具 — multi-agent 协作链断 (MiniMax baseline 通) #130)launchAgentawait-child fix--tmuxopt-inAcceptance criteria
spawnClaudeCodeProcessopt-in in agent-node (Method B 2-phase, perfeedback_npm_publish_two_phase)claude-agent-sdkinteractive smoke case to release-gate playbookAuthor-Agent: 通信工程马
Priority: P2 — no user-facing impact today, latent risk if
claude-agent-sdkinteractive flow ever landsEstimated effort: ~1.5h investigation + ~30min ship (if fix needed) + playbook addendum