From 5ca0224fed88ae0314662aef80b0b1ff2c776366 Mon Sep 17 00:00:00 2001 From: Jeremy Corbello Date: Thu, 29 Jan 2026 07:41:04 -0600 Subject: [PATCH] fix(compaction): inject workspace context into summarization The compaction system was creating context-blind summaries because the summarization LLM never saw the workspace context files (AGENTS.md, TOOLS.md, MEMORY.md, etc.). While contextFiles were correctly resolved during session creation, they were not passed through to the summarization call chain. This fix: - Extends CompactionSafeguardRuntimeValue type to include workspaceContext - Serializes contextFiles in compact.ts and stores them in the runtime - Retrieves and injects workspace context into summarization instructions in compaction-safeguard.ts The summarizing LLM now receives the actual workspace file contents, allowing it to generate summaries that preserve: - User identity and preferences - Agent identity and capabilities - Tool configurations and infrastructure - Ongoing work items and project context This is a proper architectural fix replacing the previous heuristic approach that only added generic hints to the instructions. Fixes context loss after compaction in workspace-aware sessions. --- src/agents/pi-embedded-runner/compact.ts | 11 +++++++ .../compaction-safeguard-runtime.ts | 1 + .../pi-extensions/compaction-safeguard.ts | 31 +++++++++++++++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index dc68561c2..341e4bec5 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -69,6 +69,7 @@ import type { EmbeddedPiCompactResult } from "./types.js"; import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js"; import { describeUnknownError, mapThinkingLevel, resolveExecToolDefaults } from "./utils.js"; import { buildTtsSystemPromptHint } from "../../tts/tts.js"; +import { setCompactionSafeguardRuntime } from "../pi-extensions/compaction-safeguard-runtime.js"; export type CompactEmbeddedPiSessionParams = { sessionId: string; @@ -210,6 +211,12 @@ export async function compactEmbeddedPiSessionDirect( sessionId: params.sessionId, warn: makeBootstrapWarn({ sessionLabel, warn: (message) => log.warn(message) }), }); + + const workspaceContextForCompaction = contextFiles + .filter((f) => f.content?.trim()) + .map((f) => `## ${f.path}\n${f.content}`) + .join("\n\n---\n\n"); + const runAbortController = new AbortController(); const toolsRaw = createMoltbotCodingTools({ exec: { @@ -365,6 +372,10 @@ export async function compactEmbeddedPiSessionDirect( allowSyntheticToolResults: transcriptPolicy.allowSyntheticToolResults, }); trackSessionManagerAccess(params.sessionFile); + + setCompactionSafeguardRuntime(sessionManager, { + workspaceContext: workspaceContextForCompaction || undefined, + }); const settingsManager = SettingsManager.create(effectiveWorkspace, agentDir); ensurePiCompactionReserveTokens({ settingsManager, diff --git a/src/agents/pi-extensions/compaction-safeguard-runtime.ts b/src/agents/pi-extensions/compaction-safeguard-runtime.ts index f42cf7abe..6e6b7f366 100644 --- a/src/agents/pi-extensions/compaction-safeguard-runtime.ts +++ b/src/agents/pi-extensions/compaction-safeguard-runtime.ts @@ -1,5 +1,6 @@ export type CompactionSafeguardRuntimeValue = { maxHistoryShare?: number; + workspaceContext?: string; }; // Session-scoped runtime registry keyed by object identity. diff --git a/src/agents/pi-extensions/compaction-safeguard.ts b/src/agents/pi-extensions/compaction-safeguard.ts index b2fe39884..aba3114f3 100644 --- a/src/agents/pi-extensions/compaction-safeguard.ts +++ b/src/agents/pi-extensions/compaction-safeguard.ts @@ -177,6 +177,31 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { const runtime = getCompactionSafeguardRuntime(ctx.sessionManager); const maxHistoryShare = runtime?.maxHistoryShare ?? 0.5; + const workspaceContext = runtime?.workspaceContext; + + const buildEnhancedInstructions = (baseInstructions?: string): string | undefined => { + if (!workspaceContext) { + return baseInstructions; + } + const contextSection = ` +## Workspace Context Files +The following files define this workspace's identity, user, and configuration: + +${workspaceContext} + +IMPORTANT: Your summary MUST preserve: +- References to projects, tasks, and goals mentioned in these context files +- User identity and preferences from USER.md +- Agent identity and capabilities from SOUL.md or AGENTS.md +- Tool configurations and infrastructure from TOOLS.md +- Any ongoing work items referenced in MEMORY.md + +The summary will be used to continue this work - ensure the next agent session understands WHO the user is, WHAT they're working on, and WHY.`; + + return baseInstructions ? `${baseInstructions}\n\n${contextSection}` : contextSection; + }; + + const enhancedCustomInstructions = buildEnhancedInstructions(customInstructions); const tokensBefore = typeof preparation.tokensBefore === "number" && Number.isFinite(preparation.tokensBefore) @@ -228,7 +253,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { reserveTokens: Math.max(1, Math.floor(preparation.settings.reserveTokens)), maxChunkTokens: droppedMaxChunkTokens, contextWindow: contextWindowTokens, - customInstructions, + customInstructions: enhancedCustomInstructions, previousSummary: preparation.previousSummary, }); } catch (droppedError) { @@ -261,7 +286,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { reserveTokens, maxChunkTokens, contextWindow: contextWindowTokens, - customInstructions, + customInstructions: enhancedCustomInstructions, previousSummary: effectivePreviousSummary, }); @@ -275,7 +300,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { reserveTokens, maxChunkTokens, contextWindow: contextWindowTokens, - customInstructions: TURN_PREFIX_INSTRUCTIONS, + customInstructions: buildEnhancedInstructions(TURN_PREFIX_INSTRUCTIONS), previousSummary: undefined, }); summary = `${historySummary}\n\n---\n\n**Turn Context (split turn):**\n\n${prefixSummary}`;