From ad550c544e48d4e3dc3550efcc1ad7a8a30a1af6 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 6 Jul 2026 17:58:17 -0500 Subject: [PATCH] fix(core): preserve text attachments in context estimate --- packages/core/src/session/compaction.ts | 29 ++++++++----------- packages/core/test/session-compaction.test.ts | 14 ++------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts index da7b71ef40..f1103d0c3a 100644 --- a/packages/core/src/session/compaction.ts +++ b/packages/core/src/session/compaction.ts @@ -84,23 +84,18 @@ const estimate = (value: unknown) => Token.estimate(JSON.stringify(value)) const textContext = (request: LLMRequest) => ({ system: request.system, - // TODO: Replace blanket attachment exclusion with model-aware media and file token accounting. - messages: request.messages - // Text attachments are lowered into dedicated marked messages; the original prompt is emitted separately. - .filter((message) => message.metadata?.attachment === undefined) - .map((message) => ({ - id: message.id, - role: message.role, - content: message.content.flatMap((part) => { - if (part.type === "media") return [] - if (part.type !== "tool-result" || part.result.type !== "content") return [part] - return [ - { ...part, result: { ...part.result, value: part.result.value.filter((item) => item.type === "text") } }, - ] - }), - metadata: message.metadata, - native: message.native, - })), + // TODO: Replace media exclusion with model-aware attachment token accounting. + messages: request.messages.map((message) => ({ + id: message.id, + role: message.role, + content: message.content.flatMap((part) => { + if (part.type === "media") return [] + if (part.type !== "tool-result" || part.result.type !== "content") return [part] + return [{ ...part, result: { ...part.result, value: part.result.value.filter((item) => item.type === "text") } }] + }), + metadata: message.metadata, + native: message.native, + })), tools: request.tools, }) diff --git a/packages/core/test/session-compaction.test.ts b/packages/core/test/session-compaction.test.ts index cf24bcf87b..0f9f8e12ec 100644 --- a/packages/core/test/session-compaction.test.ts +++ b/packages/core/test/session-compaction.test.ts @@ -72,7 +72,7 @@ test("compaction describes tool media without embedding base64", () => { expect(serialized).not.toContain(base64) }) -it.effect("does not count file attachments as text context", () => +it.effect("does not count image attachments as text context", () => Effect.gen(function* () { requests = [] const compaction = yield* SessionCompaction.Service @@ -84,12 +84,6 @@ it.effect("does not count file attachments as text context", () => source: { type: "inline" }, name: "screenshot.png", }) - const document = FileAttachment.make({ - data: Base64.make(Buffer.alloc(64 * 1024, "a").toString("base64")), - mime: "text/plain", - source: { type: "inline" }, - name: "notes.txt", - }) const inputModel = Model.make({ id: "media-model", provider: "test", @@ -110,7 +104,7 @@ it.effect("does not count file attachments as text context", () => id: SessionMessage.ID.create(), type: "user", text: "Inspect this image", - files: [document, image], + files: [image], time: { created: DateTime.makeUnsafe(1) }, }), ] @@ -125,10 +119,6 @@ it.effect("does not count file attachments as text context", () => data, filename: "screenshot.png", }) - expect(request.messages[1]?.content[0]).toMatchObject({ - type: "text", - text: expect.stringContaining("Attached file: notes.txt"), - }) expect( yield* compaction.compactIfNeeded({