Skip to content

fix(opencode): carry the in-flight request through auto-compaction - #46

Open
PierrotAWB wants to merge 1 commit into
devfrom
andrew/use-2680-compaction-keeps-request
Open

PierrotAWB wants to merge 1 commit into
devfrom
andrew/use-2680-compaction-keeps-request

Conversation

@PierrotAWB

@PierrotAWB PierrotAWB commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

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:

  1. The retained tail is a contiguous slice chosen by token budget, and splitTurn may 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_id landed 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.
  2. The continue prompt said "Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed." A model holding only a paraphrase is always a little unsure. GPT-5.6 replied "Here's where things stand: …" and asked "When you said 'rebrand this for my site and branding' … what did you mean?"

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_VERSION bump 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 typecheck in packages/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

  • The continue prompt includes the last non-synthetic user text, capped at 8,000 chars, and tells the model not to restate the summary.
  • Adds a test covering a second compaction, which quotes the original request rather than the prior continue prompt.
  • Not shipped to the fleet until a release tag and an OPENCODE_VERSION bump.

Written for commit 44cd78a. Summary will update on new commits.

Review in cubic

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>
@github-actions

Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown

The following comment was made by an LLM, it may be inaccurate:

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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>")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
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)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant