feat(session): experimental length-nudge for truncated turns - #45256
feat(session): experimental length-nudge for truncated turns#45256wojciak wants to merge 2 commits into
Conversation
When a turn ends with finish reason 'length' and produced no visible text (e.g. the entire output budget was consumed by reasoning tokens), the loop used to exit immediately, leaving the task incomplete. This adds an opt-in experimental flag that injects a synthetic 'continue' user nudge so the model can resume from where it stopped. Env vars (all under OPENCODE_EXPERIMENTAL_): - OPENCODE_EXPERIMENTAL_LENGTH_NUDGE=true enable the feature - OPENCODE_EXPERIMENTAL_LENGTH_NUDGE_MAX=N max nudges per prompt (default 3) - OPENCODE_EXPERIMENTAL_LENGTH_NUDGE_PROMPT custom nudge text The nudge is capped to avoid burning tokens in a reasoning loop that repeatedly hits the cap. Default is OFF; enable via env or OPENCODE_EXPERIMENTAL=true umbrella flag.
|
The following comment was made by an LLM, it may be inaccurate: Based on the search results, I found two related PRs that may be addressing similar issues: Potential Related PRs:
These PRs appear to be exploring related solutions to the truncated output problem. Your PR's approach with an opt-in experimental length-nudge mechanism seems like an evolution or alternative approach to these earlier fixes. You may want to review how #40142 and #39397 handled this to ensure compatibility or understand if your approach supersedes them. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Issue for this PR
Related: #39397, #40142, #40146, #17471 (not closing any of these — they touch the same problem area from different angles).
When a turn hits the output token cap and produces nothing visible — the whole budget goes into the reasoning channel, so the message is just
step-start+reasoning+step-finish, no text, no tool call — the loop breaks and the session reports as done with an empty answer. The task is lost and there's no error telling the user why. This bites hardest on reasoning models running under a constrainedOPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX.Type of change
What does this PR do?
Adds an opt-in experimental flag that, when a turn ends
lengthwith no visible text, injects a synthetic "continue from where you stopped" user message and lets the loop run again, so the model can finish the work instead of the session silently ending.Why it works, and how it differs from the related PRs:
lengthfinish, but unboundedly and with no extra instruction to the model. Ours is bounded (default 3, configurable) and sends an explicit "resume, don't redo" nudge, so a model that keeps filling its budget with reasoning can't loop forever burning tokens.OutputLengthErrorand stops. Ours instead tries to recover the work. Both are reasonable; I'm proposing the recover-first path as an opt-in so users who hit this get a chance rather than a dead end.synthetic: trueso it shows up in the transcript but is clearly not user input.Config (all off by default, no behaviour change unless opted in):
OPENCODE_EXPERIMENTAL_LENGTH_NUDGE— enable (also on via the umbrellaOPENCODE_EXPERIMENTAL=true)OPENCODE_EXPERIMENTAL_LENGTH_NUDGE_MAX— max nudges per prompt (default 3)OPENCODE_EXPERIMENTAL_LENGTH_NUDGE_PROMPT— override the nudge textFiles:
runtime-flags.ts(3 new flags),session/prompt.ts(detection + synthetic message inrunLoop),test/effect/runtime-flags.test.ts(flag tests).How did you verify your code works?
bun turbo typecheck— clean across packagesbun test test/effect/runtime-flags.test.ts— 47 pass, 0 fail (9 new cases: default-off, explicit on, umbrella inheritance, max parsing, prompt override)finish: "length", the nudge fired (confirmed as asyntheticuser part in the session DB), and the following turn produced visible output.Screenshots / recordings
Not a UI change.
Checklist