diff --git a/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md b/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md index 379774d329..edc432c8df 100644 --- a/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md +++ b/packages/coding-agent/src/core/extensions/builtin/compaction/changes.md @@ -1,5 +1,16 @@ # Builtin compaction extension changes +## Summarization must not forward agent tools (2026-08-21) + +### What changed +- `getSummarizationTools()` now returns `[]` via `compactionSummarizationTools()` instead of copying `pi.getActiveTools()`. +- The summarizer remains a prose-only LLM call; `snapshot.tools` is empty so models cannot end the summary with a bare `toolUse`. + +### Why +- Forwarding the live tool list made tool-preferring models (observed: openai-codex/gpt-5.6-sol) finish summarization with `stopReason: toolUse` and no text. +- `getSummaryText()` only joins `content.type === "text"`, so that becomes `SummaryGenerationError("empty-summary")` and compaction is rejected while tokens stay above the threshold. +- Observed on session `01a01fae` at 257k tokens: auto, retry, and manual `/compact` all rejected with empty-summary `toolUse`. The 2026-07-21 note already documented this failure mode. + ## Skip Cursor compaction while the session is not idle (2026-08-19) Blocking and generated apply refuse `cursor` / `cursor-cli-oauth` when `!ctx.isIdle()`. Mid-run Cursor compact poisons `conversationId`. Idle `agent_end` / `pre_prompt` still compact. diff --git a/packages/coding-agent/src/core/extensions/builtin/compaction/index.ts b/packages/coding-agent/src/core/extensions/builtin/compaction/index.ts index 4746fee113..f5643e75b6 100644 --- a/packages/coding-agent/src/core/extensions/builtin/compaction/index.ts +++ b/packages/coding-agent/src/core/extensions/builtin/compaction/index.ts @@ -13,6 +13,7 @@ import type { } from "../../types.ts"; import * as checkpointState from "./checkpoint-state.ts"; import * as breaker from "./circuit-breaker.ts"; +import { compactionSummarizationTools } from "./summarization-tools.ts"; import { BUILTIN_CONTEXT_REDUCTION_OPTIONS, reduceContextMessages, @@ -211,16 +212,7 @@ export default function compactionExtension( const getLogger = (ctx: ExtensionContext): CompactionLogger => (logger ??= createCompactionLogger(ctx.agentDir)); function getSummarizationTools(): Tool[] { - if (typeof pi.getAllTools !== "function" || typeof pi.getActiveTools !== "function") return []; - try { - const definitionsByName = new Map(pi.getAllTools().map((tool) => [tool.name, tool])); - return pi.getActiveTools().flatMap((name) => { - const tool = definitionsByName.get(name); - return tool ? [{ name: tool.name, description: tool.description, parameters: tool.parameters }] : []; - }); - } catch { - return []; - } + return compactionSummarizationTools(); } let idleWarmupTimer: ReturnType | undefined; diff --git a/packages/coding-agent/src/core/extensions/builtin/compaction/summarization-tools.ts b/packages/coding-agent/src/core/extensions/builtin/compaction/summarization-tools.ts new file mode 100644 index 0000000000..f59c82e842 --- /dev/null +++ b/packages/coding-agent/src/core/extensions/builtin/compaction/summarization-tools.ts @@ -0,0 +1,11 @@ +import type { Tool } from "@earendil-works/pi-ai"; + +/** + * Summarization is a prose-only LLM call. Forwarding the live agent tool list + * lets tool-preferring models (observed: openai-codex/gpt-5.6-sol) end the + * summary with `stopReason: toolUse` and no text, which senpi rejects as + * `empty-summary` and leaves the session above the compaction threshold. + */ +export function compactionSummarizationTools(_liveTools?: Tool[]): Tool[] { + return []; +} diff --git a/packages/coding-agent/test/compaction/summarization-no-tools.test.ts b/packages/coding-agent/test/compaction/summarization-no-tools.test.ts new file mode 100644 index 0000000000..ffebe6a109 --- /dev/null +++ b/packages/coding-agent/test/compaction/summarization-no-tools.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import type { Tool } from "@earendil-works/pi-ai"; +import { compactionSummarizationTools } from "../../src/core/extensions/builtin/compaction/summarization-tools.ts"; + +describe("compactionSummarizationTools", () => { + it("returns no tools even when the live agent list is non-empty", () => { + const live = [ + { + name: "bash", + description: "run a command", + parameters: { type: "object", properties: {} }, + }, + ] as Tool[]; + expect(compactionSummarizationTools(live)).toEqual([]); + expect(compactionSummarizationTools()).toEqual([]); + }); +});