Skip to content

Commit 3f7bd3b

Browse files
committed
fix: split before unfinished compaction tool turns
1 parent 3017a71 commit 3f7bd3b

2 files changed

Lines changed: 41 additions & 4 deletions

File tree

src/agents/compaction.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,20 @@ describe("splitMessagesByTokenShare", () => {
183183
expect(parts.length).toBe(2);
184184
expect(parts.flat().length).toBe(messages.length);
185185
});
186+
187+
it("splits before unfinished tool-call turns that never get a result", () => {
188+
const messages: AgentMessage[] = [
189+
makeMessage(1, 4000),
190+
makeAssistantToolCall(2, "call_missing"),
191+
makeMessage(3, 4000),
192+
];
193+
194+
const parts = splitMessagesByTokenShare(messages, 2);
195+
196+
expect(parts.length).toBe(2);
197+
expect(parts[0]?.map((m) => m.timestamp)).toEqual([1]);
198+
expect(parts[1]?.map((m) => m.timestamp)).toEqual([2, 3]);
199+
});
186200
});
187201

188202
describe("pruneHistoryForContextShare", () => {

src/agents/compaction.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,22 @@ export function splitMessagesByTokenShare(
133133
let currentTokens = 0;
134134

135135
let pendingToolCallIds = new Set<string>();
136+
let pendingChunkStartIndex: number | null = null;
137+
138+
const splitCurrentAtPendingBoundary = (): boolean => {
139+
if (
140+
pendingChunkStartIndex === null ||
141+
pendingChunkStartIndex <= 0 ||
142+
chunks.length >= normalizedParts - 1
143+
) {
144+
return false;
145+
}
146+
chunks.push(current.slice(0, pendingChunkStartIndex));
147+
current = current.slice(pendingChunkStartIndex);
148+
currentTokens = current.reduce((sum, msg) => sum + estimateCompactionMessageTokens(msg), 0);
149+
pendingChunkStartIndex = 0;
150+
return true;
151+
};
136152

137153
for (const message of messages) {
138154
const messageTokens = estimateCompactionMessageTokens(message);
@@ -146,6 +162,7 @@ export function splitMessagesByTokenShare(
146162
chunks.push(current);
147163
current = [];
148164
currentTokens = 0;
165+
pendingChunkStartIndex = null;
149166
}
150167

151168
current.push(message);
@@ -154,14 +171,15 @@ export function splitMessagesByTokenShare(
154171
if (message.role === "assistant") {
155172
const toolCalls = extractToolCallsFromAssistant(message);
156173
const stopReason = (message as { stopReason?: unknown }).stopReason;
157-
pendingToolCallIds =
158-
stopReason === "aborted" || stopReason === "error"
159-
? new Set()
160-
: new Set(toolCalls.map((t) => t.id));
174+
const keepsPending =
175+
stopReason !== "aborted" && stopReason !== "error" && toolCalls.length > 0;
176+
pendingToolCallIds = keepsPending ? new Set(toolCalls.map((t) => t.id)) : new Set();
177+
pendingChunkStartIndex = keepsPending ? current.length - 1 : null;
161178
} else if (message.role === "toolResult" && pendingToolCallIds.size > 0) {
162179
const resultId = extractToolResultId(message);
163180
if (!resultId) {
164181
pendingToolCallIds = new Set();
182+
pendingChunkStartIndex = null;
165183
} else {
166184
pendingToolCallIds.delete(resultId);
167185
}
@@ -173,10 +191,15 @@ export function splitMessagesByTokenShare(
173191
chunks.push(current);
174192
current = [];
175193
currentTokens = 0;
194+
pendingChunkStartIndex = null;
176195
}
177196
}
178197
}
179198

199+
if (pendingToolCallIds.size > 0 && currentTokens > targetTokens) {
200+
splitCurrentAtPendingBoundary();
201+
}
202+
180203
if (current.length > 0) {
181204
chunks.push(current);
182205
}

0 commit comments

Comments
 (0)