fix(devin): retain flushed history in overflow sizing

This commit is contained in:
Alexander Kirilin
2026-07-20 13:37:36 -04:00
parent dcb04238d8
commit 9cc82b1f03
2 changed files with 36 additions and 6 deletions
+12 -6
View File
@@ -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 =
@@ -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(
{