fix: preserve compaction files and classify Z.AI overflow (#35747)

Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: 冯基魁 <56265583+fengjikui@users.noreply.github.com>
This commit is contained in:
Aiden Cline 2026-07-07 09:54:31 -05:00 committed by GitHub
commit f488089179
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 34 additions and 7 deletions

View file

@ -25,20 +25,27 @@ const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <te
- [constraints/preferences, decisions and why, important facts/assumptions, exact context needed to continue, or "(none)"] - [constraints/preferences, decisions and why, important facts/assumptions, exact context needed to continue, or "(none)"]
## Work State ## Work State
- Completed: [finished work, verified facts, or changes made; otherwise "(none)"] ### Completed
- Active: [current work, partial changes, or investigation state; otherwise "(none)"] - [finished work, verified facts, or changes made; otherwise "(none)"]
- Blocked: [blockers, failing commands, or unknowns; otherwise "(none)"]
### Active
- [current work, partial changes, or investigation state; otherwise "(none)"]
### Blocked
- [blockers, failing commands, or unknowns; otherwise "(none)"]
## Next Move ## Next Move
1. [immediate concrete action, or "(none)"] 1. [immediate concrete action, or "(none)"]
2. [next action if known, or "(none)"] 2. [next action if known, or "(none)"]
## Relevant Files
- [file or directory path: why it matters, or "(none)"]
</template> </template>
Rules: Rules:
- Keep every section, even when empty. - Keep every section, even when empty.
- Use terse bullets, not prose paragraphs. - Use terse bullets, not prose paragraphs.
- Preserve exact file paths, symbols, commands, error strings, URLs, and identifiers when known. - Preserve exact file paths, symbols, commands, error strings, URLs, and identifiers when known.
- Put relevant files and symbols inside the section where they matter; do not add extra sections.
- Do not mention the summary process or that context was compacted.` - Do not mention the summary process or that context was compacted.`
type Settings = { type Settings = {

View file

@ -52,6 +52,15 @@ const it = testEffect(
), ),
) )
test("compaction prompt preserves detailed work state and relevant files", () => {
const prompt = SessionCompaction.buildPrompt({ context: ["conversation history"] })
expect(prompt).toContain("## Work State\n### Completed")
expect(prompt).toContain("### Active")
expect(prompt).toContain("### Blocked")
expect(prompt).toContain("## Relevant Files")
})
test("compaction describes tool media without embedding base64", () => { test("compaction describes tool media without embedding base64", () => {
const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB" const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
const serialized = SessionCompaction.serializeToolContent([ const serialized = SessionCompaction.serializeToolContent([
@ -74,13 +83,14 @@ test("compaction prompt requires the checkpoint headings in order", () => {
"## Objective", "## Objective",
"## Important Details", "## Important Details",
"## Work State", "## Work State",
"### Completed",
"### Active",
"### Blocked",
"## Next Move", "## Next Move",
"## Relevant Files",
]) ])
expect(prompt).toContain("one or two brief sentences") expect(prompt).toContain("one or two brief sentences")
expect(prompt).toContain("constraints/preferences, decisions and why") expect(prompt).toContain("constraints/preferences, decisions and why")
expect(prompt).toContain("Completed:")
expect(prompt).toContain("Active:")
expect(prompt).toContain("Blocked:")
expect(prompt).toContain("immediate concrete action") expect(prompt).toContain("immediate concrete action")
expect(prompt).toContain("next action if known") expect(prompt).toContain("next action if known")
expect(prompt).toContain("Keep every section, even when empty.") expect(prompt).toContain("Keep every section, even when empty.")

View file

@ -6,6 +6,7 @@ const patterns = [
/input is too long for requested model/i, /input is too long for requested model/i,
/exceeds the context window/i, /exceeds the context window/i,
/input token count.*exceeds the maximum/i, /input token count.*exceeds the maximum/i,
/tokens in request more than max tokens allowed/i,
/maximum prompt length is \d+/i, /maximum prompt length is \d+/i,
/reduce the length of the messages/i, /reduce the length of the messages/i,
/maximum context length is \d+ tokens/i, /maximum context length is \d+ tokens/i,

View file

@ -0,0 +1,8 @@
import { describe, expect, test } from "bun:test"
import { isContextOverflow } from "../src"
describe("provider error classification", () => {
test("classifies Z.AI GLM token limit messages as context overflow", () => {
expect(isContextOverflow("tokens in request more than max tokens allowed")).toBe(true)
})
})

View file

@ -1448,6 +1448,7 @@ describe("session.message-v2.fromError", () => {
"prompt is too long: 213462 tokens > 200000 maximum", "prompt is too long: 213462 tokens > 200000 maximum",
"Your input exceeds the context window of this model", "Your input exceeds the context window of this model",
"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)", "The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)",
"tokens in request more than max tokens allowed",
"Please reduce the length of the messages or completion", "Please reduce the length of the messages or completion",
"400 status code (no body)", "400 status code (no body)",
"413 status code (no body)", "413 status code (no body)",