From 9cc82b1f030ea3a4d392e3cc3963c4ade3cbd91f Mon Sep 17 00:00:00 2001 From: Alexander Kirilin Date: Mon, 20 Jul 2026 13:37:36 -0400 Subject: [PATCH] fix(devin): retain flushed history in overflow sizing --- packages/ai/src/providers/devin.ts | 18 +++++++++----- .../test/devin-large-request-recovery.test.ts | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/ai/src/providers/devin.ts b/packages/ai/src/providers/devin.ts index 6aebec776..8b1b8aaa5 100644 --- a/packages/ai/src/providers/devin.ts +++ b/packages/ai/src/providers/devin.ts @@ -245,12 +245,18 @@ export const streamDevin: StreamFunction<"devin-agent"> = ( // schemas, which history maintenance cannot shrink. Re-encode // only the repeated history field before choosing recovery. let activeTailCount = 0; - for (let i = context.messages.length - 1; i >= 0; i--) { - const role = context.messages[i].role; - if (role === "user" || role === "developer") { - activeTailCount++; - } else { - break; + const lastRole = context.messages.at(-1)?.role; + if (lastRole === "user" || lastRole === "developer") { + activeTailCount = 1; + // A trailing developer message can accompany the current user + // prompt. Earlier user-role records may instead be flushed + // execution history and must remain eligible for compaction. + if (lastRole === "developer") { + for (let i = context.messages.length - 2; i >= 0; i--) { + const role = context.messages[i].role; + if (role !== "user" && role !== "developer") break; + activeTailCount++; + } } } const shrinkablePrompts = diff --git a/packages/ai/test/devin-large-request-recovery.test.ts b/packages/ai/test/devin-large-request-recovery.test.ts index 61c8cfa6c..da869df8c 100644 --- a/packages/ai/test/devin-large-request-recovery.test.ts +++ b/packages/ai/test/devin-large-request-recovery.test.ts @@ -237,6 +237,30 @@ describe("streamDevin large request recovery", () => { expect(result.errorMessage).toContain("trace ID: tool-execution-overflow"); expect(AIError.is(result.errorId, AIError.Flag.ContextOverflow)).toBe(true); }); + it("keeps prior user-role execution history eligible before the active prompt", async () => { + const result = await runTrailerError( + { + messages: [ + { + role: "user" as const, + content: "execution output: ".concat("x".repeat(520 * 1024)), + timestamp: 1, + }, + { + role: "user" as const, + content: "small active prompt", + timestamp: 2, + }, + ], + }, + "invalid_argument", + "an internal error occurred (trace ID: user-role-history)", + ); + + expect(result.stopReason).toBe("error"); + expect(AIError.is(result.errorId, AIError.Flag.ContextOverflow)).toBe(true); + }); + it("keeps the trailer transient for a large current prompt split across multiple trailing user/developer messages", async () => { const result = await runTrailerError( {