Skip to content

Commit e97c7bf

Browse files
author
SymbolStar
committed
fix(pruner): use dropAllThinkingBlocks for estimate when policy strips all thinking
Reviewer feedback on PR #81534: P2-1: pruner was conflating dropThinkingBlocks (keeps latest assistant thinking block) and dropAllThinkingBlocks (strips every assistant thinking block) into a single boolean on the context-pruning runtime. For Copilot Claude proxy paths transcriptPolicy.dropAllThinkingBlocks is true, but the pruner only called dropThinkingBlocks() when estimating context size, so big non-latest thinking signatures still counted toward the token estimate and the pruner could trim tool results that would not actually have been sent. Thread a separate dropAllThinkingBlocksForEstimate signal through ContextPruningRuntimeValue -> extension.ts -> pruneContextMessages so the estimate path matches what the wire format will look like. P2-2: attempt.spawn-workspace.test-support.ts mocked ../thinking.js with only dropReasoningFromHistory + dropThinkingBlocks. attempt.ts also imports dropAllThinkingBlocks now, so module evaluation would fail. Add the missing field to the mock. Tests added: - pruner.test.ts: dropAllThinkingBlocksForEstimate strips even the latest assistant thinking block from the estimate, allowing the oversized tool result through unchanged. - context-pruning.test.ts: updated setContextPruningRuntime callers for new mandatory dropAllThinkingBlocks field.
1 parent d354077 commit e97c7bf

7 files changed

Lines changed: 35 additions & 5 deletions

File tree

src/agents/pi-embedded-runner/extensions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ function buildContextPruningFactory(params: {
128128
settings,
129129
contextWindowTokens: resolveContextWindowTokens(params),
130130
isToolPrunable: makeToolPrunablePredicate(settings.tools),
131-
dropThinkingBlocks: transcriptPolicy.dropThinkingBlocks || transcriptPolicy.dropAllThinkingBlocks,
131+
dropThinkingBlocks: transcriptPolicy.dropThinkingBlocks === true,
132+
dropAllThinkingBlocks: transcriptPolicy.dropAllThinkingBlocks === true,
132133
lastCacheTouchAt: readLastCacheTtlTimestamp(params.sessionManager, {
133134
provider: params.provider,
134135
modelId: params.modelId,

src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ vi.mock("../sandbox-info.js", () => ({
752752
vi.mock("../thinking.js", () => ({
753753
dropReasoningFromHistory: <T>(messages: T) => messages,
754754
dropThinkingBlocks: <T>(messages: T) => messages,
755+
dropAllThinkingBlocks: <T>(messages: T) => messages,
755756
}));
756757

757758
vi.mock("../tool-name-allowlist.js", async (importOriginal) => {

src/agents/pi-hooks/context-pruning.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ describe("context-pruning", () => {
318318
contextWindowTokens: 1000,
319319
isToolPrunable: () => true,
320320
dropThinkingBlocks: false,
321+
dropAllThinkingBlocks: false,
321322
lastCacheTouchAt: Date.now() - DEFAULT_CONTEXT_PRUNING_SETTINGS.ttlMs - 1000,
322323
});
323324

@@ -341,6 +342,7 @@ describe("context-pruning", () => {
341342
contextWindowTokens: 1000,
342343
isToolPrunable: () => true,
343344
dropThinkingBlocks: false,
345+
dropAllThinkingBlocks: false,
344346
lastCacheTouchAt: lastTouch,
345347
});
346348

src/agents/pi-hooks/context-pruning/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default function contextPruningExtension(api: ExtensionAPI): void {
2727
isToolPrunable: runtime.isToolPrunable,
2828
contextWindowTokensOverride: runtime.contextWindowTokens ?? undefined,
2929
dropThinkingBlocksForEstimate: runtime.dropThinkingBlocks,
30+
dropAllThinkingBlocksForEstimate: runtime.dropAllThinkingBlocks,
3031
});
3132

3233
if (next === event.messages) {

src/agents/pi-hooks/context-pruning/pruner.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ function makeToolResult(
6464
function pruneWithOversizedAssistantThinking(params: {
6565
assistantBlock: AssistantContentBlock;
6666
dropThinkingBlocksForEstimate?: boolean;
67+
dropAllThinkingBlocksForEstimate?: boolean;
6768
}) {
6869
return pruneContextMessages({
6970
messages: [
@@ -77,6 +78,9 @@ function pruneWithOversizedAssistantThinking(params: {
7778
ctx: CONTEXT_WINDOW_5K,
7879
isToolPrunable: () => true,
7980
...(params.dropThinkingBlocksForEstimate ? { dropThinkingBlocksForEstimate: true } : {}),
81+
...(params.dropAllThinkingBlocksForEstimate
82+
? { dropAllThinkingBlocksForEstimate: true }
83+
: {}),
8084
});
8185
}
8286

@@ -356,6 +360,23 @@ describe("pruneContextMessages", () => {
356360
expect(result).toBe(messages);
357361
});
358362

363+
it("ignores latest-turn thinking signatures when dropAllThinkingBlocksForEstimate is true", () => {
364+
const result = pruneWithOversizedAssistantThinking({
365+
assistantBlock: {
366+
type: "thinking",
367+
thinking: "internal",
368+
thinkingSignature: "S".repeat(40_000),
369+
} as unknown as AssistantContentBlock,
370+
dropAllThinkingBlocksForEstimate: true,
371+
});
372+
const toolResult = result.find((m) => m.role === "toolResult") as Extract<
373+
AgentMessage,
374+
{ role: "toolResult" }
375+
>;
376+
const textBlock = toolResult.content[0] as { type: "text"; text: string };
377+
expect(textBlock.text).toBe("X".repeat(2_000));
378+
});
379+
359380
it("soft-trims image-containing tool results by replacing image blocks with placeholders", () => {
360381
const messages: AgentMessage[] = [
361382
makeUser("summarize this"),

src/agents/pi-hooks/context-pruning/pruner.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { AgentMessage } from "@earendil-works/pi-agent-core";
22
import type { ImageContent, TextContent, ToolResultMessage } from "@earendil-works/pi-ai";
33
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
44
import { CHARS_PER_TOKEN_ESTIMATE, estimateStringChars } from "../../../utils/cjk-chars.js";
5-
import { dropThinkingBlocks } from "../../pi-embedded-runner/thinking.js";
5+
import { dropAllThinkingBlocks, dropThinkingBlocks } from "../../pi-embedded-runner/thinking.js";
66
import type { EffectiveContextPruningSettings } from "./settings.js";
77
import { makeToolPrunablePredicate } from "./tools.js";
88

@@ -291,6 +291,7 @@ export function pruneContextMessages(params: {
291291
isToolPrunable?: (toolName: string) => boolean;
292292
contextWindowTokensOverride?: number;
293293
dropThinkingBlocksForEstimate?: boolean;
294+
dropAllThinkingBlocksForEstimate?: boolean;
294295
}): AgentMessage[] {
295296
const { messages, settings, ctx } = params;
296297
const contextWindowTokens =
@@ -320,9 +321,11 @@ export function pruneContextMessages(params: {
320321
const pruneStartIndex = firstUserIndex === null ? messages.length : firstUserIndex;
321322

322323
const isToolPrunable = params.isToolPrunable ?? makeToolPrunablePredicate(settings.tools);
323-
const estimatedMessages = params.dropThinkingBlocksForEstimate
324-
? dropThinkingBlocks(messages)
325-
: messages;
324+
const estimatedMessages = params.dropAllThinkingBlocksForEstimate
325+
? dropAllThinkingBlocks(messages)
326+
: params.dropThinkingBlocksForEstimate
327+
? dropThinkingBlocks(messages)
328+
: messages;
326329

327330
const totalCharsBefore = estimateContextChars(estimatedMessages);
328331
let totalChars = totalCharsBefore;

src/agents/pi-hooks/context-pruning/runtime.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type ContextPruningRuntimeValue = {
66
contextWindowTokens?: number | null;
77
isToolPrunable: (toolName: string) => boolean;
88
dropThinkingBlocks: boolean;
9+
dropAllThinkingBlocks: boolean;
910
lastCacheTouchAt?: number | null;
1011
};
1112

0 commit comments

Comments
 (0)