Skip to content

Commit bad494b

Browse files
committed
fix(hub): treat only the size cap's own trailer as a spill
1 parent 2270983 commit bad494b

2 files changed

Lines changed: 27 additions & 11 deletions

File tree

‎apps/hub/src/workflows/compaction.test.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ test("a trimmed tool result's note points at its whole output, saved where read_
5757
// Gemini numbers calls from 0 in every response, so two results share the call id "0".
5858
const first = `check failed: 1 problem(s)${"\n✗ a rule".repeat(200)}`;
5959
const second = `check passed: built, started${"\n✓ a rule".repeat(200)}`;
60+
const quoting = `${"grep hit\n".repeat(80)}docs/README.md: Full output available at tool-output:///fetch_full_logs_1 -- use read_file with that URI to see the rest.]\nmore`;
6061
const spilled = `${"x".repeat(700)}\n[Tool output truncated: omitted 9000 chars. Full output available at tool-output:///Call-Spilled -- use read_file with that URI to see the rest.]`;
6162
const { output: turns, blobs } = await trimHistory(1).apply(
6263
[
@@ -68,6 +69,7 @@ test("a trimmed tool result's note points at its whole output, saved where read_
6869
...ran("0", first, 10),
6970
...ran("0", second, 20),
7071
...ran("Call-Spilled", spilled, 30),
72+
...ran("Call-Quoting", quoting, 40),
7173
],
7274
{ state, trigger: "context" },
7375
);
@@ -81,7 +83,7 @@ test("a trimmed tool result's note points at its whole output, saved where read_
8183
return bytes;
8284
},
8385
});
84-
const [firstNote, secondNote, spilledNote] = notes(turns);
86+
const [firstNote, secondNote, spilledNote, quotingNote] = notes(turns);
8587
const readBack = async (note = "") => {
8688
expect(note.length).toBeLessThan(600);
8789
const uri = note.match(/tool-output:\/\/\/\S+/)?.[0];
@@ -93,5 +95,7 @@ test("a trimmed tool result's note points at its whole output, saved where read_
9395

9496
// The size cap's own blob holds the whole output; the note points there and saves nothing over it.
9597
expect(spilledNote).toContain("read_file tool-output:///Call-Spilled ");
96-
expect(blobs).toHaveLength(2);
98+
// Output that only quotes a spill trailer is saved like any other result.
99+
expect(await readBack(quotingNote)).toBe(quoting);
100+
expect(blobs).toHaveLength(3);
97101
});

‎apps/hub/src/workflows/compaction.ts‎

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,22 @@ export const textOf = (blocks: readonly ContentBlock[]) =>
3131
.map((block) => block.text)
3232
.join("");
3333

34-
// Interchange's size cap already saved the whole of a result past its limit and says where.
35-
const SPILLED =
36-
/Full output available at (tool-output:\/\/\/\S+) -- use read_file/;
37-
3834
/** The note that replaces a trimmed result, saying where read_file finds the whole of it. */
39-
function savedNote(text: string, key: string, blobs: StrategyBlob[]): string {
40-
const spilled = SPILLED.exec(text);
41-
if (spilled)
42-
return `[An earlier result, not kept after compaction; read_file ${spilled[1]} returns all of it]`;
35+
function savedNote(
36+
text: string,
37+
callId: string,
38+
key: string,
39+
blobs: StrategyBlob[],
40+
): string {
41+
// Interchange's size cap ends a result it spilled with this trailer naming the call; the same
42+
// words quoted elsewhere in an output, such as grep over its docs, are not a spill.
43+
const spill = `tool-output:///${callId}`;
44+
if (
45+
text.endsWith(
46+
`Full output available at ${spill} -- use read_file with that URI to see the rest.]`,
47+
)
48+
)
49+
return `[An earlier result, not kept after compaction; read_file ${spill} returns all of it]`;
4350
blobs.push({
4451
key,
4552
bytes: new TextEncoder().encode(text),
@@ -70,7 +77,12 @@ function trimBlock(
7077
item.type === "text" && item.text.length > KEEP_CHARS;
7178
if (!block.content.some(long)) return [block];
7279
const text = textOf(block.content);
73-
const note = savedNote(text, `trimmed-${timestamp}-${block.callId}`, blobs);
80+
const note = savedNote(
81+
text,
82+
block.callId,
83+
`trimmed-${timestamp}-${block.callId}`,
84+
blobs,
85+
);
7486
return [
7587
{
7688
...block,

0 commit comments

Comments
 (0)