Skip to content

feat(session): experimental length-nudge for truncated turns - #45256

Open
wojciak wants to merge 2 commits into
anomalyco:devfrom
wojciak:experimental-length-nudge
Open

feat(session): experimental length-nudge for truncated turns#45256
wojciak wants to merge 2 commits into
anomalyco:devfrom
wojciak:experimental-length-nudge

Conversation

@wojciak

@wojciak wojciak commented Aug 26, 2026

Copy link
Copy Markdown

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 constrained OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds an opt-in experimental flag that, when a turn ends length with 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:

  • fix: continue session loop when response is truncated by length (problem with local, smaller LLMs) #39397 also keeps the loop going on a length finish, 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.
  • fix(opencode): surface truncated turns instead of ending the loop #40142 takes the opposite call for this exact reasoning-only case: it surfaces OutputLengthError and 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.
  • Detection only fires when there is zero visible text. If the model already emitted text or a tool call, the turn is considered useful and no nudge is injected, so partial answers are not double-answered.
  • The nudge message is marked synthetic: true so 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 umbrella OPENCODE_EXPERIMENTAL=true)
  • OPENCODE_EXPERIMENTAL_LENGTH_NUDGE_MAX — max nudges per prompt (default 3)
  • OPENCODE_EXPERIMENTAL_LENGTH_NUDGE_PROMPT — override the nudge text

Files: runtime-flags.ts (3 new flags), session/prompt.ts (detection + synthetic message in runLoop), test/effect/runtime-flags.test.ts (flag tests).

How did you verify your code works?

  • bun turbo typecheck — clean across packages
  • bun test test/effect/runtime-flags.test.ts — 47 pass, 0 fail (9 new cases: default-off, explicit on, umbrella inheritance, max parsing, prompt override)
  • Ran the built binary against a local endpoint with a very low output cap and a prompt that forces reasoning: first turn returned reasoning-only with finish: "length", the nudge fired (confirmed as a synthetic user part in the session DB), and the following turn produced visible output.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

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.
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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:

  1. fix(opencode): surface truncated turns instead of ending the loop #40142 - fix(opencode): surface truncated turns instead of ending the loop

    • Directly addresses handling truncated turns in the session loop, which is the core problem this PR solves
  2. fix: continue session loop when response is truncated by length (problem with local, smaller LLMs) #39397 - fix: continue session loop when response is truncated by length (problem with local, smaller LLMs)

    • Similar use case: continuing the loop when responses are truncated by length constraints, especially for smaller/local models
  3. fix(opencode): drop truncated reasoning from replayed history #40148 - fix(opencode): drop truncated reasoning from replayed history

    • Related to handling truncated reasoning/tokens in session history
  4. fix(session): retry empty stream truncations and discard partial parts #26167 - fix(session): retry empty stream truncations and discard partial parts

    • Addresses retrying when the stream is empty/truncated

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.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant