You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every turn on a raw engine starts a fresh conversation — continuity is one preset string, and no preset has it
Filed from the #249 assessment, recommendation item 3 — "Continuity as configuration… Zero code.
Memo item 2, still open."#249 cannot close leaving the proposed ADR as its only record.
The mechanism
Claude is the only persistent engine: the runner keeps one process alive and speaks structured stream-json to it across turns. Every other engine is one-shot
(packages/browser-runner/src/coding/headless.ts:378-380, :463-482):
with the contract stated deliberately at :466-468:
"the preset command is a prefix and the turn text is appended as the final argument"
So the Pilot's step 7 instruction is codex exec --sandbox danger-full-access "<step 7>" — a brand
new process that has never seen steps 1–6. The working tree carries state; the conversation does
not. A step that says "now do the same for the other two files" has no antecedent.
docs/coding-engines.md:7-27 documents the prefix contract precisely and correctly, and says
nothing about what it costs across turns.
None of the shipped presets carries a resume flag
workers/api/src/lib/coding-engines.ts:253-267 — the defaults an instance that has saved nothing gets:
id
command
persistent?
claude
claude --dangerously-skip-permissions
yes — stream-json, --resume handled by buildClaudeArgs
codex
codex exec --sandbox danger-full-access
no
gemini
gemini --approval-mode yolo --skip-trust --prompt
no
grok
grok --permission-mode bypassPermissions -p
no
local
ollama run llama3
no
Claude's continuity is not a preset — it is the runner re-spawning with --resume <session id>
against ~/.claude (headless.ts:394-397, and buildClaudeArgs strips a user-supplied --resume
because it carries the wire protocol). Nothing equivalent exists for a raw engine, and nothing needs
to: a CLI that offers a resume subcommand gains multi-turn memory by editing one text field.
What this ticket is, and is not
It is not a request to change the runner. The mechanism above is deliberate and docs/adr's
proposed ADR 0002 states it as a consequence rather than a defect: "Multi-turn memory for raw
engines is a preset string, not a backend property." The gap is that the platform never says so, so
the trade is invisible at exactly the moment a user picks an engine.
Stated honestly: I have not run a multi-turn codex/gemini/grok session. The claim above is
reconstructed from the spawn path and the shipped preset strings, both of which are unambiguous. What
I cannot tell you from the code is how badly it degrades a real 10-step objective — that needs a run,
and a run spends the owner's tokens.
What to do, cheapest first
Say it where the choice is made. The ⚙ CLI engines panel (EnginesModal.tsx) is where a
preset is edited; one line per engine — "one-shot: each turn is a fresh process, with no memory
of the previous one" vs "keeps its session between turns" — costs nothing and is the whole
user-facing gap. deriveClientType(command) already decides which of the two a command is, so
the label is derivable rather than hardcoded.
Document it in docs/coding-engines.md, next to the prefix contract it is a consequence of.
The doc currently explains that everything after the binary passes through verbatim, which is
exactly the sentence a reader needs in order to act on this — it just does not connect the two.
Ship resume-capable presets where the vendor has one. This is the item that needs a
decision and a check per CLI, because a wrong flag is worse than none: a preset that dies on an
unknown flag takes the whole session with it, which is precisely what happened to grok --prompt (coding-engines.ts:262-263: "grok has --prompt-file and --prompt-json but no --prompt, so the old preset died on an unknown/ambiguous flag before reaching the model").
Verify against the installed binary, not against a memory of its docs.
Consider a second preset rather than a changed one — e.g. codex and codex (continues) —
so the existing default is untouched for anyone relying on a clean-slate turn. Cheap, reversible,
and it makes the trade a visible choice instead of a silent default change.
Have the Pilot re-send the whole transcript as the prompt each turn. Rejected: it pays the
entire history in tokens on every turn against a CLI that is itself paying for its own context, and
the Pilot's decision loop is deliberately snapshot→decide→act rather than transcript-carrying. If
context is genuinely needed the engine's own resume is the mechanism the vendor built for it.
Detect resume support automatically (probe --help). Rejected: parsing a vendor's help text is
a new drift surface per CLI per release, for a value that belongs in a text field the user already
owns.
Acceptance criteria
The engines panel states, per preset, whether turns share a session — derived from deriveClientType, not restated per engine.
docs/coding-engines.md says the one-shot spawn means no cross-turn memory, and that a
vendor resume subcommand is the fix, in the same section as the prefix contract.
If resume presets ship: a two-turn session on that engine where turn 2 references turn 1 gets a
correct answer, and the run id is recorded on this ticket.
Regression risk
A resume flag changes what the engine costs and what it remembers. A session that resumes into
the wrong prior conversation is a worse failure than starting clean — verify the vendor's resume
selects the session the runner intends, not "the last one on this machine", before shipping it as a
default.
ENGINE_WRITE_FLAGS (coding-engines.ts:274-285) asserts no shipped default forgets its
write flag. A new preset must appear there too, or an engine ships that cannot edit files.
Presets are per-instance and user-editable; changing DEFAULT_ENGINES only affects instances that
have saved nothing (readEngines falls back only when the stored list is empty/invalid). Do not
assume a default change reaches existing users — it does not.
Files: packages/browser-runner/src/coding/headless.ts:378-397,463-500, workers/api/src/lib/coding-engines.ts:253-285, docs/coding-engines.md:1-40, agents/coder/web/src/EnginesModal.tsx.
Related: #249 (the assessment that asks for this, item 3, and its proposed ADR 0002), #391 (process exit as the turn boundary for one-shot engines), #248 (the console does not
report what actually resolved), #433 (the docs that should describe the split).
Every turn on a raw engine starts a fresh conversation — continuity is one preset string, and no preset has it
Filed from the #249 assessment, recommendation item 3 — "Continuity as configuration… Zero code.
Memo item 2, still open." #249 cannot close leaving the proposed ADR as its only record.
The mechanism
Claude is the only persistent engine: the runner keeps one process alive and speaks structured
stream-jsonto it across turns. Every other engine is one-shot(
packages/browser-runner/src/coding/headless.ts:378-380,:463-482):with the contract stated deliberately at
:466-468:So the Pilot's step 7 instruction is
codex exec --sandbox danger-full-access "<step 7>"— a brandnew process that has never seen steps 1–6. The working tree carries state; the conversation does
not. A step that says "now do the same for the other two files" has no antecedent.
docs/coding-engines.md:7-27documents the prefix contract precisely and correctly, and saysnothing about what it costs across turns.
None of the shipped presets carries a resume flag
workers/api/src/lib/coding-engines.ts:253-267— the defaults an instance that has saved nothing gets:claudeclaude --dangerously-skip-permissions--resumehandled bybuildClaudeArgscodexcodex exec --sandbox danger-full-accessgeminigemini --approval-mode yolo --skip-trust --promptgrokgrok --permission-mode bypassPermissions -plocalollama run llama3Claude's continuity is not a preset — it is the runner re-spawning with
--resume <session id>against
~/.claude(headless.ts:394-397, andbuildClaudeArgsstrips a user-supplied--resumebecause it carries the wire protocol). Nothing equivalent exists for a raw engine, and nothing needs
to: a CLI that offers a resume subcommand gains multi-turn memory by editing one text field.
What this ticket is, and is not
It is not a request to change the runner. The mechanism above is deliberate and
docs/adr'sproposed ADR 0002 states it as a consequence rather than a defect: "Multi-turn memory for raw
engines is a preset string, not a backend property." The gap is that the platform never says so, so
the trade is invisible at exactly the moment a user picks an engine.
Stated honestly: I have not run a multi-turn codex/gemini/grok session. The claim above is
reconstructed from the spawn path and the shipped preset strings, both of which are unambiguous. What
I cannot tell you from the code is how badly it degrades a real 10-step objective — that needs a run,
and a run spends the owner's tokens.
What to do, cheapest first
EnginesModal.tsx) is where apreset is edited; one line per engine — "one-shot: each turn is a fresh process, with no memory
of the previous one" vs "keeps its session between turns" — costs nothing and is the whole
user-facing gap.
deriveClientType(command)already decides which of the two a command is, sothe label is derivable rather than hardcoded.
docs/coding-engines.md, next to the prefix contract it is a consequence of.The doc currently explains that everything after the binary passes through verbatim, which is
exactly the sentence a reader needs in order to act on this — it just does not connect the two.
decision and a check per CLI, because a wrong flag is worse than none: a preset that dies on an
unknown flag takes the whole session with it, which is precisely what happened to
grok --prompt(coding-engines.ts:262-263: "grok has--prompt-fileand--prompt-jsonbut no--prompt, so the old preset died on an unknown/ambiguous flag before reaching the model").Verify against the installed binary, not against a memory of its docs.
codexandcodex (continues)—so the existing default is untouched for anyone relying on a clean-slate turn. Cheap, reversible,
and it makes the trade a visible choice instead of a silent default change.
Alternatives considered and rejected
start()used tospawn one and it produced an instant "exited with code 1" (
headless.ts:387-392) — anon-interactive binary has nothing to keep alive. It is also what [bug] A one-shot raw engine is judged idle by a 1.5s quiet timer and killed mid-turn — process exit is already the exact signal #391 fixed at the other end, by
making process exit the authoritative turn boundary for one-shot engines.
entire history in tokens on every turn against a CLI that is itself paying for its own context, and
the Pilot's decision loop is deliberately snapshot→decide→act rather than transcript-carrying. If
context is genuinely needed the engine's own resume is the mechanism the vendor built for it.
metering (Coding Engine spend is invisible in the usage ledger — the largest real cost isn't recorded #267), the acts audit (A delegated run records its outcome but not its consequential acts — merging to main is invisible #294) and merge authority (migration 0091), and a screen buffer is
not a conversation anyway.
--help). Rejected: parsing a vendor's help text isa new drift surface per CLI per release, for a value that belongs in a text field the user already
owns.
Acceptance criteria
deriveClientType, not restated per engine.docs/coding-engines.mdsays the one-shot spawn means no cross-turn memory, and that avendor resume subcommand is the fix, in the same section as the prefix contract.
correct answer, and the run id is recorded on this ticket.
Regression risk
the wrong prior conversation is a worse failure than starting clean — verify the vendor's resume
selects the session the runner intends, not "the last one on this machine", before shipping it as a
default.
ENGINE_WRITE_FLAGS(coding-engines.ts:274-285) asserts no shipped default forgets itswrite flag. A new preset must appear there too, or an engine ships that cannot edit files.
DEFAULT_ENGINESonly affects instances thathave saved nothing (
readEnginesfalls back only when the stored list is empty/invalid). Do notassume a default change reaches existing users — it does not.
Files:
packages/browser-runner/src/coding/headless.ts:378-397,463-500,workers/api/src/lib/coding-engines.ts:253-285,docs/coding-engines.md:1-40,agents/coder/web/src/EnginesModal.tsx.Related: #249 (the assessment that asks for this, item 3, and its proposed ADR 0002),
#391 (process exit as the turn boundary for one-shot engines), #248 (the console does not
report what actually resolved), #433 (the docs that should describe the split).