fix(opencode): carry the in-flight request through auto-compaction - #46
PierrotAWB wants to merge 1 commit into
Conversation
Scheduled compaction can start the retained tail inside the current turn, so the user's own instruction is summarized into a one-line Objective and the model resumes from a paraphrase plus a nudge that permits stopping. In production it replied "Here's where things stand..." and asked what the request meant. The continue prompt now quotes the request verbatim and tells the model to resume without restating the summary. USE-2680. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
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. |
|
The following comment was made by an LLM, it may be inaccurate: |
There was a problem hiding this comment.
2 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/opencode/test/session/compaction.test.ts">
<violation number="1" location="packages/opencode/test/session/compaction.test.ts:943">
P3: The new test "quotes the original request, not a prior continue prompt, on a second compaction" does not actually verify its stated intent. In the buggy scenario it guards against (currentRequest picking up the prior synthetic nudge as the request), the second nudge would wrap the first nudge's text, which itself already contains the `<user_request>\nhello\n</user_request>` block. So `toContain("<user_request>\nhello\n</user_request>")` passes both with and without the `!part.synthetic` filter, and the test never fails on the regression it is named for. Assert the directive appears exactly once (or that the quoted request block contains only "hello"), e.g. expect the text to be the exact expected string, or check `text.split("Continue the work from Next Move")` has length 2 rather than a single `toContain`.</violation>
</file>
<file name="packages/opencode/src/session/compaction.ts">
<violation number="1" location="packages/opencode/src/session/compaction.ts:59">
P2: When a later user message contains only whitespace, `currentRequest` treats it as the in-flight request and omits the actual request from the continuation prompt. Require a non-whitespace text part in the `findLast` predicate so the helper matches the stated substantive-request behavior.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| // The verbatim request the turn is carrying out; the summary only paraphrases it. | ||
| function currentRequest(messages: SessionV1.WithParts[]) { | ||
| const request = messages.findLast((m) => m.info.role === "user" && m.parts.some(isRequestText)) |
There was a problem hiding this comment.
P2: When a later user message contains only whitespace, currentRequest treats it as the in-flight request and omits the actual request from the continuation prompt. Require a non-whitespace text part in the findLast predicate so the helper matches the stated substantive-request behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/session/compaction.ts, line 59:
<comment>When a later user message contains only whitespace, `currentRequest` treats it as the in-flight request and omits the actual request from the continuation prompt. Require a non-whitespace text part in the `findLast` predicate so the helper matches the stated substantive-request behavior.</comment>
<file context>
@@ -49,6 +49,22 @@ type CompletedCompaction = {
+
+// The verbatim request the turn is carrying out; the summary only paraphrases it.
+function currentRequest(messages: SessionV1.WithParts[]) {
+ const request = messages.findLast((m) => m.info.role === "user" && m.parts.some(isRequestText))
+ const text =
+ request?.parts
</file context>
| const request = messages.findLast((m) => m.info.role === "user" && m.parts.some(isRequestText)) | |
| const request = messages.findLast((m) => m.info.role === "user" && m.parts.some((part) => isRequestText(part) && part.text.trim())) |
| yield* SessionCompaction.use.process({ parentID: msg.id, messages: second, sessionID: session.id, auto: true }) | ||
|
|
||
| const last = (yield* ssn.messages({ sessionID: session.id })).at(-1) | ||
| expect(last?.parts[0]?.type === "text" && last.parts[0].text).toContain("<user_request>\nhello\n</user_request>") |
There was a problem hiding this comment.
P3: The new test "quotes the original request, not a prior continue prompt, on a second compaction" does not actually verify its stated intent. In the buggy scenario it guards against (currentRequest picking up the prior synthetic nudge as the request), the second nudge would wrap the first nudge's text, which itself already contains the <user_request>\nhello\n</user_request> block. So toContain("<user_request>\nhello\n</user_request>") passes both with and without the !part.synthetic filter, and the test never fails on the regression it is named for. Assert the directive appears exactly once (or that the quoted request block contains only "hello"), e.g. expect the text to be the exact expected string, or check text.split("Continue the work from Next Move") has length 2 rather than a single toContain.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/test/session/compaction.test.ts, line 943:
<comment>The new test "quotes the original request, not a prior continue prompt, on a second compaction" does not actually verify its stated intent. In the buggy scenario it guards against (currentRequest picking up the prior synthetic nudge as the request), the second nudge would wrap the first nudge's text, which itself already contains the `<user_request>\nhello\n</user_request>` block. So `toContain("<user_request>\nhello\n</user_request>")` passes both with and without the `!part.synthetic` filter, and the test never fails on the regression it is named for. Assert the directive appears exactly once (or that the quoted request block contains only "hello"), e.g. expect the text to be the exact expected string, or check `text.split("Continue the work from Next Move")` has length 2 rather than a single `toContain`.</comment>
<file context>
@@ -921,11 +921,29 @@ describe("session.compaction.process", () => {
+ yield* SessionCompaction.use.process({ parentID: msg.id, messages: second, sessionID: session.id, auto: true })
+
+ const last = (yield* ssn.messages({ sessionID: session.id })).at(-1)
+ expect(last?.parts[0]?.type === "text" && last.parts[0].text).toContain("<user_request>\nhello\n</user_request>")
+ }),
+ )
</file context>
| expect(last?.parts[0]?.type === "text" && last.parts[0].text).toContain("<user_request>\nhello\n</user_request>") | |
| const text = last?.parts[0]?.type === "text" ? last.parts[0].text : "" | |
| expect(text).toContain("<user_request>\nhello\n</user_request>") | |
| // The directive must appear exactly once: a nested prior nudge would quote it again. | |
| expect(text.split("Continue the work from Next Move")).toHaveLength(2) |
Description
Fixes the remaining half of USE-2680. andytown anomalyco#27141 hid the compaction summary from the chat, and the synthetic continue prompt was already hidden. What users still see is the model's own behavior after compaction: it stops, narrates the state, and asks a question instead of resuming.
Two things in the fork combine to cause that:
splitTurnmay start it at an assistant message inside the current turn. The user's actual instruction is then summarized into a one-line## Objective. In the Swanson session (bbe6fd08…),tail_start_idlanded on the 17:52:37 assistant step, so the 17:52:20 "rebrand this for my site and branding…" request left the model's context. In Noah's session the tail was empty.This PR changes the continue prompt only. It quotes the in-flight request verbatim (the last user message with a non-synthetic, non-ignored text part, capped at 8,000 chars) and replaces the nudge with: continue from Next Move, say so briefly if nothing remains, do not restate the summary, and only stop to ask if you cannot proceed without the user's input. The summary template, tail selection, and overflow replay are untouched.
Not shipped to the fleet until a release tag and an
OPENCODE_VERSIONbump in andytown.Testing
bun test test/session/compaction.test.ts: 54 pass. The existing auto-continue test now asserts the quoted request and the new directive. A new test runs two compactions on one turn and asserts the second nudge quotes the original request rather than the prior synthetic nudge.bun run typecheckinpackages/opencode: clean.🤖 Generated with Claude Code
Summary by cubic
Fixes USE-2680 by carrying the user's in-flight request through auto-compaction: the continue prompt now quotes the request verbatim and directs the model to resume, so it stops narrating its state and asking what the request meant.
Bug Fixes
OPENCODE_VERSIONbump.Written for commit 44cd78a. Summary will update on new commits.