fix: send max-steps instruction as user message, not assistant - #36970
fix: send max-steps instruction as user message, not assistant#36970rguliyev wants to merge 1 commit into
Conversation
When an agent hits its configured steps cap mid-turn, the loop appended the "wrap up now" instruction as a role: assistant message, leaving the outgoing request ending on an assistant turn. Providers that reject assistant-message prefill bounce this immediately with "This model does not support assistant message prefill." The instruction is directed at the model, so it belongs on a user turn, not tacked onto the assistant role. Adds a regression test asserting the max-steps message is sent as role: user.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
The following comment was made by an LLM, it may be inaccurate: Potential duplicate found:
This appears to be addressing the exact same issue — converting the max-steps instruction from |
|
Closing in favor of #34276, which is more complete — it also fixes the Leaving our independent confirmation over there as supporting evidence |
Add regression tests locking in the fix from the previous commit: - packages/opencode/test/session/prompt.test.ts: assert the V1 loop sends the MAXIMUM STEPS REACHED notice with role "user". - packages/core/test/session-runner.test.ts: update the existing V2 assertion (which locked in the buggy "assistant" role) to expect "user". Verified: - bun test test/session/prompt.test.ts (57 pass) - bun test test/session-runner.test.ts (80 pass) - bun turbo typecheck (29/29) V1 test borrowed from anomalyco#36970. Co-authored-by: rguliyev <rguliyev@users.noreply.github.com>
Add regression tests locking in the fix from the previous commit: - packages/opencode/test/session/prompt.test.ts: assert the V1 loop sends the MAXIMUM STEPS REACHED notice with role "user". - packages/core/test/session-runner.test.ts: update the existing V2 assertion (which locked in the buggy "assistant" role) to expect "user". Verified: - bun test test/session/prompt.test.ts (57 pass) - bun test test/session-runner.test.ts (80 pass) - bun turbo typecheck (29/29) V1 test borrowed from anomalyco#36970. Co-authored-by: rguliyev <rguliyev@users.noreply.github.com>
Fixes #32548
Problem
When an agent hits its configured
stepscap mid-turn, the loop forcesa text-only response by appending the "wrap up now" instruction
(
MAX_STEPS_PROMPT) to the outgoing message array as arole: "assistant"message:
This leaves the request ending on an assistant turn. Anthropic (and other
providers) treat a trailing assistant message as a response prefill, and
Claude models with thinking enabled reject it outright:
Independently confirmed in production logs across every agent/model
combination in proportion to usage, all with zero cost/output tokens on
the failing turn, all traced back to this exact
isLastStepbranch —matches #32548's 100%-reproducible repro steps exactly.
Fix
The max-steps instruction is directed at the model, not written by it —
it belongs on a
userturn:Tool results already present in
modelMsgsare untouched; only the roleof the appended instruction changes.
Testing
prompt.test.tsthat configuresagent: { build: { steps: 1 } }, drives the loop to the cap, andasserts the appended max-steps message has
role: "user"(fails ifreverted to
role: "assistant").bun run lintclean on both changed files (no new warnings).bun turbo typecheckpasses (ran via the pre-push hook, 30/30 packages).bun test test/session/prompt.test.ts— new test passes; the file has2 pre-existing flaky/timeout failures unrelated to this change,
confirmed to occur identically on a clean checkout before this patch.