fix: recover from compaction overflow
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
- Models/Providers: treat credential validation failures as auth errors to trigger fallback; normalize `${ENV_VAR}` apiKey values and auto-fill missing provider keys; preserve explicit GitHub Copilot provider config + agent-dir auth profiles.
|
||||
- Auth: drop invalid auth profiles from ordering so environment keys can still be used for providers like MiniMax.
|
||||
- Gemini: normalize Gemini 3 ids to preview variants; strip Gemini CLI tool call/response ids; downgrade missing `thought_signature`; strip Claude `msg_*` thought_signature fields to avoid base64 decode errors.
|
||||
- Agents: auto-recover from compaction context overflow by resetting the session and retrying; propagate overflow details from embedded runs so callers can recover.
|
||||
- MiniMax: strip malformed tool invocation XML; include `MiniMax-VL-01` in implicit provider for image pairing.
|
||||
- Onboarding/Auth: honor `CLAWDBOT_AGENT_DIR` / `PI_CODING_AGENT_DIR` when writing auth profiles (MiniMax). (#829) — thanks @roshanasingh4.
|
||||
- Anthropic: merge consecutive user turns (preserve newest metadata) before validation to avoid incorrect role errors.
|
||||
|
||||
@@ -370,8 +370,8 @@ export function formatAssistantErrorText(
|
||||
// Check for context overflow (413) errors
|
||||
if (isContextOverflowError(raw)) {
|
||||
return (
|
||||
"Context overflow: the conversation history is too large. " +
|
||||
"Use /new or /reset to start a fresh session."
|
||||
"Context overflow: prompt too large for the model. " +
|
||||
"Try again with less input or a larger-context model."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -362,6 +362,10 @@ export type EmbeddedPiRunMeta = {
|
||||
durationMs: number;
|
||||
agentMeta?: EmbeddedPiAgentMeta;
|
||||
aborted?: boolean;
|
||||
error?: {
|
||||
kind: "context_overflow" | "compaction_failure";
|
||||
message: string;
|
||||
};
|
||||
};
|
||||
|
||||
function buildModelAliasLines(cfg?: ClawdbotConfig) {
|
||||
@@ -1976,12 +1980,15 @@ export async function runEmbeddedPiAgent(params: {
|
||||
if (promptError && !aborted) {
|
||||
const errorText = describeUnknownError(promptError);
|
||||
if (isContextOverflowError(errorText)) {
|
||||
const kind = isCompactionFailureError(errorText)
|
||||
? "compaction_failure"
|
||||
: "context_overflow";
|
||||
return {
|
||||
payloads: [
|
||||
{
|
||||
text:
|
||||
"Context overflow: the conversation history is too large for the model. " +
|
||||
"Use /new or /reset to start a fresh session, or try a model with a larger context window.",
|
||||
"Context overflow: prompt too large for the model. " +
|
||||
"Try again with less input or a larger-context model.",
|
||||
isError: true,
|
||||
},
|
||||
],
|
||||
@@ -1992,6 +1999,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
provider,
|
||||
model: model.id,
|
||||
},
|
||||
error: { kind, message: errorText },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -525,6 +525,65 @@ describe("runReplyAgent typing (heartbeat)", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("retries after context overflow payload by resetting the session", async () => {
|
||||
const prevStateDir = process.env.CLAWDBOT_STATE_DIR;
|
||||
const stateDir = await fs.mkdtemp(
|
||||
path.join(tmpdir(), "clawdbot-session-overflow-reset-"),
|
||||
);
|
||||
process.env.CLAWDBOT_STATE_DIR = stateDir;
|
||||
try {
|
||||
const sessionId = "session";
|
||||
const storePath = path.join(stateDir, "sessions", "sessions.json");
|
||||
const sessionEntry = { sessionId, updatedAt: Date.now() };
|
||||
const sessionStore = { main: sessionEntry };
|
||||
|
||||
await fs.mkdir(path.dirname(storePath), { recursive: true });
|
||||
await fs.writeFile(storePath, JSON.stringify(sessionStore), "utf-8");
|
||||
|
||||
runEmbeddedPiAgentMock
|
||||
.mockImplementationOnce(async () => ({
|
||||
payloads: [
|
||||
{ text: "Context overflow: prompt too large", isError: true },
|
||||
],
|
||||
meta: {
|
||||
durationMs: 1,
|
||||
error: {
|
||||
kind: "context_overflow",
|
||||
message:
|
||||
'Context overflow: Summarization failed: 400 {"message":"prompt is too long"}',
|
||||
},
|
||||
},
|
||||
}))
|
||||
.mockImplementationOnce(async () => ({
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: { durationMs: 1 },
|
||||
}));
|
||||
|
||||
const callsBefore = runEmbeddedPiAgentMock.mock.calls.length;
|
||||
const { run } = createMinimalRun({
|
||||
sessionEntry,
|
||||
sessionStore,
|
||||
sessionKey: "main",
|
||||
storePath,
|
||||
});
|
||||
const res = await run();
|
||||
|
||||
expect(runEmbeddedPiAgentMock.mock.calls.length - callsBefore).toBe(2);
|
||||
const payload = Array.isArray(res) ? res[0] : res;
|
||||
expect(payload).toMatchObject({ text: "ok" });
|
||||
expect(sessionStore.main.sessionId).not.toBe(sessionId);
|
||||
|
||||
const persisted = JSON.parse(await fs.readFile(storePath, "utf-8"));
|
||||
expect(persisted.main.sessionId).toBe(sessionStore.main.sessionId);
|
||||
} finally {
|
||||
if (prevStateDir) {
|
||||
process.env.CLAWDBOT_STATE_DIR = prevStateDir;
|
||||
} else {
|
||||
delete process.env.CLAWDBOT_STATE_DIR;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("still replies even if session reset fails to persist", async () => {
|
||||
const prevStateDir = process.env.CLAWDBOT_STATE_DIR;
|
||||
const stateDir = await fs.mkdtemp(
|
||||
|
||||
@@ -834,6 +834,20 @@ export async function runReplyAgent(params: {
|
||||
runResult = fallbackResult.result;
|
||||
fallbackProvider = fallbackResult.provider;
|
||||
fallbackModel = fallbackResult.model;
|
||||
|
||||
// Some embedded runs surface context overflow as an error payload instead of throwing.
|
||||
// Treat those as a session-level failure and auto-recover by starting a fresh session.
|
||||
const embeddedError = runResult.meta?.error;
|
||||
if (
|
||||
embeddedError &&
|
||||
isContextOverflowError(embeddedError.message) &&
|
||||
!didResetAfterCompactionFailure &&
|
||||
(await resetSessionAfterCompactionFailure(embeddedError.message))
|
||||
) {
|
||||
didResetAfterCompactionFailure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
@@ -894,7 +908,7 @@ export async function runReplyAgent(params: {
|
||||
defaultRuntime.error(`Embedded agent failed before reply: ${message}`);
|
||||
return finalizeWithFollowup({
|
||||
text: isContextOverflow
|
||||
? "⚠️ Context overflow - conversation too long. Starting fresh might help!"
|
||||
? "⚠️ Context overflow — prompt too large for this model. Try a shorter message or a larger-context model."
|
||||
: `⚠️ Agent failed before reply: ${message}. Check gateway logs for details.`,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user