Summary
runInteractiveSingle() passes the entire system prompt — which since #54 includes
the project's AGENTS.md, allowed up to MAX_BYTES = 65536 — as an argv string
inside the command handed to tmux new-session. tmux refuses any command string
longer than ~16 KB (its imsg limit), so the session is never created and the user
sees only:
failed to start tmux session for interactive engine
The real error, command too long, is lost because tmux is spawned with
stdio: 'ignore' (claude-interactive.js:379) and only tmuxHasSession() is checked.
Repro
Project with a 24 807-byte AGENTS.md and no CLAUDE.md (real case: a monorepo
whose conventions file is ~25 KB), opened in the studio with the interactive engine.
const { agentsMdPreamble } = require('/app/agents-md.js');
const p = agentsMdPreamble('/proj'); // preamble bytes: 24972
const cmd = `env -u CLAUDECODE claude --session-id x --model sonnet \
--dangerously-skip-permissions --append-system-prompt ${shq(p)}`;
// innerCmd bytes: 25125
$ tmux new-session -d -s diag "$cmd"
command too long
$ echo $?
1
Measured threshold on the same tmux build: 16 000 B → ok, 16 333 B → ok,
16 334 B → command too long. So the ceiling is the tmux command, not argv
(ARG_MAX ~2 MB on Linux), which is what MAX_BYTES = 65536 in agents-md.js
is sized against.
Anything else appended to the prompt (studio system prompt, mode preamble)
counts toward the same 16 KB, so a smaller AGENTS.md can hit this too.
Expected
The interactive engine starts regardless of instruction-file size, or fails with
the actual tmux error.
Suggested fix
Spill the prompt to a file and let the shell inside tmux read it back — same
pattern as the existing mcpConfigPath() (claude-interactive.js:239-251):
// claude-interactive.js
function systemPromptPath(sp) {
const hash = crypto.createHash('sha256').update(sp).digest('hex').slice(0, 16);
const p = path.join(os.tmpdir(), `ccs-sp-${hash}.txt`);
if (!fs.existsSync(p)) fs.writeFileSync(p, sp, { mode: 0o600 });
return p;
}
// line 374
if (sp) innerCmd += ` --append-system-prompt "$(cat ${shq(systemPromptPath(sp))})"`;
Verified locally: innerCmd drops from 25 125 to 68 bytes, the session starts,
and the prompt reaches the child byte-identical (only the trailing newline is
stripped by $( )).
Two smaller items worth fixing alongside:
- Surface the tmux error. Spawning with
stdio: 'pipe' and including stderr in
the wsSend({ type: 'error', ... }) payload turns this class of failure from
a 2-hour bisect into one line on screen.
MAX_BYTES = 65536 in agents-md.js documents itself as safe against argv
ceilings; with the prompt travelling through a tmux command the effective
ceiling is ~16 KB, so the comment is misleading even after the fix above
removes the limit.
Summary
runInteractiveSingle()passes the entire system prompt — which since #54 includesthe project's
AGENTS.md, allowed up toMAX_BYTES = 65536— as an argv stringinside the command handed to
tmux new-session. tmux refuses any command stringlonger than ~16 KB (its imsg limit), so the session is never created and the user
sees only:
The real error,
command too long, is lost because tmux is spawned withstdio: 'ignore'(claude-interactive.js:379) and onlytmuxHasSession()is checked.Repro
Project with a 24 807-byte
AGENTS.mdand noCLAUDE.md(real case: a monorepowhose conventions file is ~25 KB), opened in the studio with the interactive engine.
Measured threshold on the same tmux build: 16 000 B → ok, 16 333 B → ok,
16 334 B →
command too long. So the ceiling is the tmux command, not argv(
ARG_MAX~2 MB on Linux), which is whatMAX_BYTES = 65536inagents-md.jsis sized against.
Anything else appended to the prompt (studio system prompt, mode preamble)
counts toward the same 16 KB, so a smaller
AGENTS.mdcan hit this too.Expected
The interactive engine starts regardless of instruction-file size, or fails with
the actual tmux error.
Suggested fix
Spill the prompt to a file and let the shell inside tmux read it back — same
pattern as the existing
mcpConfigPath()(claude-interactive.js:239-251):Verified locally:
innerCmddrops from 25 125 to 68 bytes, the session starts,and the prompt reaches the child byte-identical (only the trailing newline is
stripped by
$( )).Two smaller items worth fixing alongside:
stdio: 'pipe'and including stderr inthe
wsSend({ type: 'error', ... })payload turns this class of failure froma 2-hour bisect into one line on screen.
MAX_BYTES = 65536inagents-md.jsdocuments itself as safe against argvceilings; with the prompt travelling through a tmux command the effective
ceiling is ~16 KB, so the comment is misleading even after the fix above
removes the limit.