Context
pasteToSession in server/services/shell.js was extracted from sendBtwToAgent
(server/services/cosAgentLifecycle.js) and is now also used by the Shell photo
drop (server/services/shellImageDrop.js). It bracket-pastes text and submits it
with a single delayed \r (400ms) — the behavior sendBtwToAgent has always
had.
Two things are unfinished:
1. There are two submit strategies for the same resource. Every other place
that pastes into a shell-service session submits via scheduleSubmitEnters
(server/lib/tuiHandshake.js) — three Enters, 700ms apart:
server/services/agentTuiSpawning.js (initial prompt paste)
server/lib/tuiPromptRunner.js
That helper exists because a single \r can be swallowed while the TUI is still
reflowing a paste, stranding the prompt unsent. pasteToSession now carries the
weaker strategy under the most generic available name, so the next author who
reaches for the obvious primitive inherits it. Its docblock currently marks the
boundary in prose; the code should just be right.
2. One caller was left hand-rolled. The max-runtime wrap-up prod in
server/services/agentTuiSpawning.js (search buildWrapUpProdMessage) writes
\x1b[200~…\x1b[201~ to a shell-service session by sessionId with no
verification and no retry — structurally identical to pasteToSession, and it
should call it.
Correctly OUT of scope (verified, do not change):
server/lib/tuiPromptRunner.js and server/lib/tuiUsageScrape.js write to a
PTY they own directly that was never registered in shellSessions, so a
(sessionId, …)-shaped primitive has no handle to offer them.
agentTuiSpawning.js's attemptPaste is a paste-marker-verified retry loop;
its paste write is one step of a handshake, not the whole operation.
Task
- Have
pasteToSession submit via scheduleSubmitEnters(write, isFinalized)
instead of one setTimeout(…, 400), returning whatever handle is needed to
cancel pending Enters. tuiHandshake.js imports nothing from shell.js, so
there is no cycle.
- Replace the hand-rolled paste in
agentTuiSpawning.js's wrap-up prod with a
pasteToSession(...) call.
- Update
server/services/shell.test.js's pasteToSession suite (it currently
pins the single-Enter contract) and trim the now-stale "scope" paragraph in the
docblock.
Why this is safe
Extra Enters land on an empty prompt, which Claude Code ignores — that is why
scheduleSubmitEnters is already the strategy for the two larger paste sites.
The change affects sendBtwToAgent (BTW messages) and the Shell photo drop, both
of which paste short payloads today; the win is that they stop being a second
strategy the next caller can inherit by accident.
Provenance
Raised by an altitude review during the Shell photo-drop change; skipped there
because it alters sendBtwToAgent's production submit behavior and touches
agentTuiSpawning.js, both outside that diff.
Context
pasteToSessioninserver/services/shell.jswas extracted fromsendBtwToAgent(
server/services/cosAgentLifecycle.js) and is now also used by the Shell photodrop (
server/services/shellImageDrop.js). It bracket-pastes text and submits itwith a single delayed
\r(400ms) — the behaviorsendBtwToAgenthas alwayshad.
Two things are unfinished:
1. There are two submit strategies for the same resource. Every other place
that pastes into a shell-service session submits via
scheduleSubmitEnters(
server/lib/tuiHandshake.js) — three Enters, 700ms apart:server/services/agentTuiSpawning.js(initial prompt paste)server/lib/tuiPromptRunner.jsThat helper exists because a single
\rcan be swallowed while the TUI is stillreflowing a paste, stranding the prompt unsent.
pasteToSessionnow carries theweaker strategy under the most generic available name, so the next author who
reaches for the obvious primitive inherits it. Its docblock currently marks the
boundary in prose; the code should just be right.
2. One caller was left hand-rolled. The max-runtime wrap-up prod in
server/services/agentTuiSpawning.js(searchbuildWrapUpProdMessage) writes\x1b[200~…\x1b[201~to a shell-service session by sessionId with noverification and no retry — structurally identical to
pasteToSession, and itshould call it.
Correctly OUT of scope (verified, do not change):
server/lib/tuiPromptRunner.jsandserver/lib/tuiUsageScrape.jswrite to aPTY they own directly that was never registered in
shellSessions, so a(sessionId, …)-shaped primitive has no handle to offer them.agentTuiSpawning.js'sattemptPasteis a paste-marker-verified retry loop;its paste write is one step of a handshake, not the whole operation.
Task
pasteToSessionsubmit viascheduleSubmitEnters(write, isFinalized)instead of one
setTimeout(…, 400), returning whatever handle is needed tocancel pending Enters.
tuiHandshake.jsimports nothing fromshell.js, sothere is no cycle.
agentTuiSpawning.js's wrap-up prod with apasteToSession(...)call.server/services/shell.test.js'spasteToSessionsuite (it currentlypins the single-Enter contract) and trim the now-stale "scope" paragraph in the
docblock.
Why this is safe
Extra Enters land on an empty prompt, which Claude Code ignores — that is why
scheduleSubmitEntersis already the strategy for the two larger paste sites.The change affects
sendBtwToAgent(BTW messages) and the Shell photo drop, bothof which paste short payloads today; the win is that they stop being a second
strategy the next caller can inherit by accident.
Provenance
Raised by an altitude review during the Shell photo-drop change; skipped there
because it alters
sendBtwToAgent's production submit behavior and touchesagentTuiSpawning.js, both outside that diff.