@archships/dim-plugin-auto-compact
Advanced tools
+21
-4
@@ -120,2 +120,3 @@ //#region src/index.ts | ||
| compactedMessages: request.plan.compactedMessages, | ||
| summaryInputMode: request.summaryInputMode, | ||
| pruneToolOutputs: input.options.compaction.prune, | ||
@@ -189,3 +190,3 @@ sessionId: input.context.sessionId, | ||
| async function generateSummary(input) { | ||
| const renderedInput = buildSummaryInput(input.existingSystemSegments, input.compactedMessages, input.pruneToolOutputs, input.contextStatus); | ||
| const renderedInput = buildSummaryInput(input.existingSystemSegments, input.compactedMessages, input.summaryInputMode, input.pruneToolOutputs, input.contextStatus); | ||
| const summaryInputTokens = estimateSectionsTokens([...renderedInput.prefixSections, ...renderedInput.messageSections]); | ||
@@ -226,4 +227,20 @@ const summaryInputBudget = Math.max(256, input.summaryInputMaxTokens - DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN); | ||
| } | ||
| function buildSummaryInput(existingSystemSegments, compactedMessages, pruneToolOutputs, status) { | ||
| const preservedToolOutputIds = buildPreservedToolOutputIds(compactedMessages, pruneToolOutputs); | ||
| function buildSummaryInput(existingSystemSegments, compactedMessages, summaryInputMode, pruneToolOutputs, status) { | ||
| const summaryMessages = summaryInputMode === "exclude_tool_messages" ? compactedMessages.filter((message) => message.role !== "tool") : compactedMessages; | ||
| if (summaryMessages.length === 0) return { | ||
| prefixSections: [ | ||
| createRenderedSummarySection("status", ["Current canonical session status:", status ? [ | ||
| `- sessionId: ${status.sessionId}`, | ||
| `- model: ${status.model.provider}/${status.model.modelId}`, | ||
| `- messageCount: ${status.messageCount}`, | ||
| `- compactionCursor: ${status.compaction.cursor}` | ||
| ].join("\n") : "- unavailable"].join("\n")), | ||
| createRenderedSummarySection("existing-summary", ["Existing compaction summary:", existingSystemSegments.length > 0 ? existingSystemSegments.join("\n\n") : "(none)"].join("\n")), | ||
| createRenderedSummarySection("messages-heading", "Messages to compact:\n(no eligible messages after summary-input filtering)") | ||
| ], | ||
| messageSections: [], | ||
| prunedToolOutputCount: 0, | ||
| preservedToolOutputCount: 0 | ||
| }; | ||
| const preservedToolOutputIds = buildPreservedToolOutputIds(summaryMessages, pruneToolOutputs); | ||
| let prunedToolOutputCount = 0; | ||
@@ -242,3 +259,3 @@ let preservedToolOutputCount = 0; | ||
| ], | ||
| messageSections: compactedMessages.map((message, index) => { | ||
| messageSections: summaryMessages.map((message, index) => { | ||
| const renderedMessage = renderMessage(message, pruneToolOutputs, preservedToolOutputIds); | ||
@@ -245,0 +262,0 @@ if (renderedMessage.prunedToolOutput) prunedToolOutputCount += 1; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\n CompactionExecutionRequest,\n CompactionExecutionResult,\n Message,\n ModelRef,\n ModelRequest,\n ModelRequestUsageScope,\n ModelStreamEvent,\n SessionCompactorController,\n SessionStatus,\n UsageRequestKind,\n} from '@archships/dim-agent-sdk'\nimport type {\n DimPlugin,\n JsonObject,\n LoggerGateway,\n PluginSessionControllerContext,\n PluginSessionStateEntry,\n StructuredData,\n} from '@archships/dim-plugin-api'\n\nexport interface AutoCompactPluginOptions {\n summaryModel?: ModelRef\n maxSummaryTokens?: number\n summaryInputMaxTokens?: number\n summaryPrompt?: string\n retainMessages?: number\n compaction?: {\n auto?: boolean\n prune?: boolean\n reserved?: number\n }\n}\n\ninterface ResolvedOptions {\n summaryModel?: ModelRef\n maxSummaryTokens: number\n summaryInputMaxTokens: number\n summaryPrompt: string\n retainMessages: number\n compaction: {\n auto: boolean\n prune: boolean\n reserved: number\n }\n}\n\ninterface AutoCompactPluginStateData {\n strategy: 'controller-v2'\n summaryModel?: ModelRef\n lastResult?: AutoCompactPersistedResult\n}\n\ninterface AutoCompactPersistedResult {\n trigger: 'manual' | 'threshold'\n status: 'compacted' | 'skipped' | 'failed'\n reasonCode?: string\n reasonMessage?: string\n compactedMessageCount: number\n retainedMessageCount: number\n summaryNormalization?: SummaryNormalizationMode\n summaryInputTokens?: number\n summaryOutputTokensEstimate?: number\n summaryChars?: number\n summaryCallCount?: number\n summaryChunkCount?: number\n prunedToolOutputCount?: number\n preservedToolOutputCount?: number\n updatedAt: number\n}\n\ninterface RenderedSummarySection {\n id: string\n text: string\n tokens: number\n}\n\ninterface RenderedSummaryInput {\n prefixSections: RenderedSummarySection[]\n messageSections: RenderedSummarySection[]\n prunedToolOutputCount: number\n preservedToolOutputCount: number\n}\n\ninterface NormalizedSummaryGenerationResult {\n summary: string\n summaryNormalization: SummaryNormalizationMode\n summaryInputTokens: number\n summaryOutputTokensEstimate: number\n summaryChars: number\n summaryCallCount: number\n summaryChunkCount: number\n prunedToolOutputCount: number\n preservedToolOutputCount: number\n}\n\ntype SummaryGenerationResult =\n | ({ status: 'normalized' } & NormalizedSummaryGenerationResult)\n | { status: 'empty_body_preserve_previous' }\n | { status: 'invalid_summary_contract' }\n\ntype SummaryNormalizationMode = 'plain_text' | 'wrapped_block' | 'extracted_block'\n\ntype CanonicalContinuationSummary =\n | {\n status: 'normalized'\n summary: string\n normalization: SummaryNormalizationMode\n }\n | {\n status: 'empty_body'\n }\n\ntype SuccessfulContinuationSummary = Extract<\n CanonicalContinuationSummary,\n { status: 'normalized' }\n>\n\ninterface RenderedMessage {\n text: string\n prunedToolOutput: boolean\n preservedToolOutput: boolean\n}\n\ninterface AutoCompactPlanningController extends SessionCompactorController {\n readPlanningOptions(): {\n auto: boolean\n retainMessages: number\n reservedTokens: number\n summaryTokenReserve: number\n }\n}\n\nconst DEFAULT_MAX_SUMMARY_TOKENS = 1024\nconst DEFAULT_SUMMARY_INPUT_MAX_TOKENS = 16000\nconst DEFAULT_RETAIN_MESSAGES = 4\nconst DEFAULT_THRESHOLD_HEADROOM = 1024\nconst DEFAULT_RECENT_TOOL_OUTPUTS_TO_PRESERVE = 2\nconst DEFAULT_TOOL_OUTPUT_CHAR_LIMIT = 1200\nconst DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN = 1024\nconst MAX_SUMMARY_RECURSION_PASSES = 2\nconst CONTINUATION_SUMMARY_OPEN_TAG = '<continuation_summary>'\nconst CONTINUATION_SUMMARY_CLOSE_TAG = '</continuation_summary>'\nconst CONTINUATION_SUMMARY_BLOCK_PATTERN =\n /<continuation_summary>([\\s\\S]*?)<\\/continuation_summary>/g\nconst CONTINUATION_SUMMARY_TAG_FRAGMENT_PATTERN = /<\\/?continuation_summary>?/g\n\nconst DEFAULT_SUMMARY_PROMPT = `You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include:\n\nTask Overview\nThe user's core request and success criteria\nAny clarifications or constraints they specified\n\nCurrent State\nWhat has been completed so far\nFiles created, modified, or analyzed (with paths if relevant)\nKey outputs or artifacts produced\n\nImportant Discoveries\nTechnical constraints or requirements uncovered\nDecisions made and their rationale\nErrors encountered and how they were resolved\nWhat approaches were tried that didn't work (and why)\n\nNext Steps\nSpecific actions needed to complete the task\nAny blockers or open questions to resolve\nPriority order if multiple steps remain\n\nContext to Preserve\nUser preferences or style requirements\nDomain-specific details that aren't obvious\nAny promises made to the user\n\nBe concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes.\nWrite in a way that enables immediate resumption of the task.\nReturn only the continuation summary body as plain text. Do not add XML tags, wrappers, or surrounding commentary.`\n\nexport function createAutoCompactPlugin(): DimPlugin<'auto-compact', SessionCompactorController>\nexport function createAutoCompactPlugin(\n options: AutoCompactPluginOptions,\n): DimPlugin<'auto-compact', SessionCompactorController>\nexport function createAutoCompactPlugin<TId extends string>(\n options: AutoCompactPluginOptions & { id: TId },\n): DimPlugin<TId, SessionCompactorController>\nexport function createAutoCompactPlugin<TId extends string>(\n options: AutoCompactPluginOptions & { id?: TId } = {},\n): DimPlugin<TId, SessionCompactorController> {\n const resolved = resolveOptions(options)\n\n return {\n manifest: {\n id: (options.id ?? 'auto-compact') as TId,\n version: '0.2.0',\n apiVersion: 1,\n permissions: { model: true },\n capabilities: ['auto-compact'],\n },\n setup(context) {\n return {\n inspector: {\n getConfig: async () =>\n ({\n summaryModel: resolved.summaryModel\n ? {\n provider: resolved.summaryModel.provider,\n modelId: resolved.summaryModel.modelId,\n }\n : null,\n maxSummaryTokens: resolved.maxSummaryTokens,\n summaryInputMaxTokens: resolved.summaryInputMaxTokens,\n summaryPrompt: resolved.summaryPrompt,\n retainMessages: resolved.retainMessages,\n compaction: {\n auto: resolved.compaction.auto,\n prune: resolved.compaction.prune,\n reserved: resolved.compaction.reserved,\n },\n }) as unknown as StructuredData,\n getSessionState: async (sessionId) =>\n structuredClone(\n readPluginState(await context.services.pluginState.get(sessionId)),\n ) as unknown as StructuredData,\n },\n createSessionController: (controllerContext) =>\n createAutoCompactController({\n options: resolved,\n logger: context.services.logger,\n model: context.services.model,\n context: controllerContext,\n }),\n }\n },\n }\n}\n\nfunction createAutoCompactController(input: {\n options: ResolvedOptions\n logger: LoggerGateway\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n context: PluginSessionControllerContext\n}): AutoCompactPlanningController {\n return {\n readPlanningOptions() {\n return {\n auto: input.options.compaction.auto,\n retainMessages: input.options.retainMessages,\n reservedTokens: input.options.compaction.reserved,\n summaryTokenReserve: input.options.maxSummaryTokens,\n }\n },\n async compact(request: CompactionExecutionRequest): Promise<CompactionExecutionResult> {\n const sessionStatus = input.context.getStatus()\n const summaryModel = input.options.summaryModel ?? sessionStatus.model\n\n if (!summaryModel) {\n const result: CompactionExecutionResult = {\n status: 'skipped',\n reasonCode: 'missing_summary_model',\n reasonMessage: 'Auto compact summary model is unavailable',\n diagnostics: {},\n }\n await persistPluginState(input.context, {\n strategy: 'controller-v2',\n ...(input.options.summaryModel ? { summaryModel: input.options.summaryModel } : {}),\n lastResult: createPersistedResult(request, result),\n })\n return result\n }\n\n try {\n const summary = await generateSummary({\n contextStatus: sessionStatus,\n summaryModel,\n maxSummaryTokens: input.options.maxSummaryTokens,\n summaryInputMaxTokens: input.options.summaryInputMaxTokens,\n summaryPrompt: input.options.summaryPrompt,\n existingSystemSegments: request.plan.existingSystemSegments,\n compactedMessages: request.plan.compactedMessages,\n pruneToolOutputs: input.options.compaction.prune,\n sessionId: input.context.sessionId,\n usageKind:\n request.plan.trigger === 'manual' ? 'manual_compaction' : 'auto_compaction',\n model: input.model,\n logger: input.logger,\n })\n\n const result =\n summary.status === 'normalized'\n ? {\n status: 'compacted' as const,\n summary: summary.summary,\n diagnostics: {\n summaryNormalization: summary.summaryNormalization,\n summaryInputTokens: summary.summaryInputTokens,\n summaryOutputTokensEstimate: summary.summaryOutputTokensEstimate,\n summaryChars: summary.summaryChars,\n summaryCallCount: summary.summaryCallCount,\n summaryChunkCount: summary.summaryChunkCount,\n prunedToolOutputCount: summary.prunedToolOutputCount,\n preservedToolOutputCount: summary.preservedToolOutputCount,\n },\n }\n : summary.status === 'empty_body_preserve_previous'\n ? {\n status: 'skipped' as const,\n reasonCode: 'empty_summary_preserved_previous',\n reasonMessage:\n 'Summary generation produced an empty body and preserved the existing continuation summary',\n diagnostics: {},\n }\n : {\n status: 'failed' as const,\n reasonCode: 'invalid_summary_contract',\n reasonMessage: 'Auto compact summary body cannot be empty',\n diagnostics: {},\n }\n\n await persistPluginState(input.context, {\n strategy: 'controller-v2',\n summaryModel,\n lastResult: createPersistedResult(request, result),\n })\n return result\n } catch (error) {\n const result: CompactionExecutionResult = {\n status: 'failed',\n reasonCode: readErrorCode(error) ?? 'summary_generation_failed',\n reasonMessage: readErrorMessage(error),\n diagnostics: {},\n }\n await persistPluginState(input.context, {\n strategy: 'controller-v2',\n summaryModel,\n lastResult: createPersistedResult(request, result),\n })\n return result\n }\n },\n }\n}\n\nfunction resolveOptions(options: AutoCompactPluginOptions): ResolvedOptions {\n return {\n summaryModel: options.summaryModel,\n maxSummaryTokens: positiveIntegerOr(\n options.maxSummaryTokens,\n DEFAULT_MAX_SUMMARY_TOKENS,\n ),\n summaryInputMaxTokens: Math.max(\n DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN + 256,\n positiveIntegerOr(options.summaryInputMaxTokens, DEFAULT_SUMMARY_INPUT_MAX_TOKENS),\n ),\n summaryPrompt: options.summaryPrompt?.trim() || DEFAULT_SUMMARY_PROMPT,\n retainMessages: Math.max(1, positiveIntegerOr(options.retainMessages, DEFAULT_RETAIN_MESSAGES)),\n compaction: {\n auto: options.compaction?.auto ?? true,\n prune: options.compaction?.prune ?? true,\n reserved: Math.max(0, Math.floor(options.compaction?.reserved ?? DEFAULT_THRESHOLD_HEADROOM)),\n },\n }\n}\n\nasync function generateSummary(input: {\n contextStatus?: SessionStatus\n summaryModel: ModelRef\n maxSummaryTokens: number\n summaryInputMaxTokens: number\n summaryPrompt: string\n existingSystemSegments: string[]\n compactedMessages: Message[]\n pruneToolOutputs: boolean\n sessionId: string\n usageKind: UsageRequestKind\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n logger: LoggerGateway\n}): Promise<SummaryGenerationResult> {\n const renderedInput = buildSummaryInput(\n input.existingSystemSegments,\n input.compactedMessages,\n input.pruneToolOutputs,\n input.contextStatus,\n )\n const summaryInputTokens = estimateSectionsTokens([\n ...renderedInput.prefixSections,\n ...renderedInput.messageSections,\n ])\n const summaryInputBudget = Math.max(\n 256,\n input.summaryInputMaxTokens - DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN,\n )\n let summarized: Awaited<ReturnType<typeof summarizeRenderedSections>>\n\n try {\n summarized = await summarizeRenderedSections({\n stage: 'source',\n remainingLevels: MAX_SUMMARY_RECURSION_PASSES,\n prefixSections: renderedInput.prefixSections,\n messageSections: renderedInput.messageSections,\n summaryInputBudget,\n sessionId: input.sessionId,\n summaryModel: input.summaryModel,\n maxSummaryTokens: input.maxSummaryTokens,\n summaryPrompt: input.summaryPrompt,\n usageKind: input.usageKind,\n model: input.model,\n logger: input.logger,\n })\n } catch (error) {\n if (readErrorCode(error) === 'empty_summary_body') {\n return input.existingSystemSegments.join('\\n\\n').trim()\n ? { status: 'empty_body_preserve_previous' }\n : { status: 'invalid_summary_contract' }\n }\n throw error\n }\n\n return {\n status: 'normalized',\n summary: summarized.summary,\n summaryNormalization: summarized.summaryNormalization,\n summaryInputTokens,\n summaryOutputTokensEstimate: estimateTextTokens(summarized.summary),\n summaryChars: summarized.summary.length,\n summaryCallCount: summarized.summaryCallCount,\n summaryChunkCount: summarized.summaryChunkCount,\n prunedToolOutputCount: renderedInput.prunedToolOutputCount,\n preservedToolOutputCount: renderedInput.preservedToolOutputCount,\n }\n}\n\nfunction buildSummaryInput(\n existingSystemSegments: string[],\n compactedMessages: Message[],\n pruneToolOutputs: boolean,\n status?: SessionStatus,\n): RenderedSummaryInput {\n const preservedToolOutputIds = buildPreservedToolOutputIds(compactedMessages, pruneToolOutputs)\n let prunedToolOutputCount = 0\n let preservedToolOutputCount = 0\n\n const prefixSections = [\n createRenderedSummarySection(\n 'status',\n [\n 'Current canonical session status:',\n status\n ? [\n `- sessionId: ${status.sessionId}`,\n `- model: ${status.model.provider}/${status.model.modelId}`,\n `- messageCount: ${status.messageCount}`,\n `- compactionCursor: ${status.compaction.cursor}`,\n ].join('\\n')\n : '- unavailable',\n ].join('\\n'),\n ),\n createRenderedSummarySection(\n 'existing-summary',\n [\n 'Existing compaction summary:',\n existingSystemSegments.length > 0 ? existingSystemSegments.join('\\n\\n') : '(none)',\n ].join('\\n'),\n ),\n createRenderedSummarySection('messages-heading', 'Messages to compact:'),\n ]\n\n const messageSections = compactedMessages.map((message, index) => {\n const renderedMessage = renderMessage(message, pruneToolOutputs, preservedToolOutputIds)\n if (renderedMessage.prunedToolOutput) prunedToolOutputCount += 1\n if (renderedMessage.preservedToolOutput) preservedToolOutputCount += 1\n\n return createRenderedSummarySection(\n message.id,\n [`Message ${index + 1}`, renderedMessage.text].join('\\n'),\n )\n })\n\n return {\n prefixSections,\n messageSections,\n prunedToolOutputCount,\n preservedToolOutputCount,\n }\n}\n\nasync function summarizeRenderedSections(input: {\n stage: 'source' | 'merge'\n remainingLevels: number\n prefixSections: RenderedSummarySection[]\n messageSections: RenderedSummarySection[]\n summaryInputBudget: number\n sessionId: string\n summaryModel: ModelRef\n maxSummaryTokens: number\n summaryPrompt: string\n usageKind: UsageRequestKind\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n logger: LoggerGateway\n}): Promise<{\n summary: string\n summaryNormalization: SummaryNormalizationMode\n summaryCallCount: number\n summaryChunkCount: number\n}> {\n const combinedText = joinSummarySections(input.prefixSections, input.messageSections)\n if (estimateTextTokens(combinedText) <= input.summaryInputBudget) {\n const canonical = await streamSummaryText({\n sessionId: input.sessionId,\n summaryModel: input.summaryModel,\n maxSummaryTokens: input.maxSummaryTokens,\n summaryPrompt: input.summaryPrompt,\n summaryInputText: combinedText,\n summaryKind: input.stage === 'source' ? 'summary' : 'summary-merge',\n usageKind: input.usageKind,\n model: input.model,\n logger: input.logger,\n })\n return {\n summary: canonical.summary,\n summaryNormalization: canonical.normalization,\n summaryCallCount: 1,\n summaryChunkCount: 0,\n }\n }\n\n if (input.remainingLevels <= 1) {\n throw createCodedError(\n input.stage === 'merge' ? 'summary_merge_over_budget' : 'summary_source_too_large',\n input.stage === 'merge'\n ? 'Chunk summaries still exceed the summary model input budget'\n : 'Source history exceeds the summary model input budget',\n )\n }\n\n const chunks = createSummaryChunks(\n input.prefixSections,\n input.messageSections,\n input.summaryInputBudget,\n )\n if (chunks.length <= 1) {\n throw createCodedError(\n input.stage === 'merge' ? 'summary_merge_over_budget' : 'summary_source_too_large',\n input.stage === 'merge'\n ? 'Unable to merge chunk summaries within the summary model input budget'\n : 'Unable to fit source history into chunked summary requests',\n )\n }\n\n let summaryCallCount = 0\n let summaryNormalization: SummaryNormalizationMode = 'plain_text'\n const chunkSummarySections: RenderedSummarySection[] = []\n\n for (const [index, chunk] of chunks.entries()) {\n const canonical = await streamSummaryText({\n sessionId: input.sessionId,\n summaryModel: input.summaryModel,\n maxSummaryTokens: input.maxSummaryTokens,\n summaryPrompt: input.summaryPrompt,\n summaryInputText: joinSummarySections(input.prefixSections, chunk),\n summaryKind: 'summary-chunk',\n usageKind: input.usageKind,\n model: input.model,\n chunkIndex: index,\n chunkCount: chunks.length,\n logger: input.logger,\n })\n summaryCallCount += 1\n summaryNormalization = mergeSummaryNormalizationModes(\n summaryNormalization,\n canonical.normalization,\n )\n\n chunkSummarySections.push(\n createRenderedSummarySection(\n `chunk-summary-${index + 1}`,\n `Chunk summary ${index + 1} of ${chunks.length}\\n${canonical.summary}`,\n ),\n )\n }\n\n const merged = await summarizeRenderedSections({\n ...input,\n stage: 'merge',\n remainingLevels: input.remainingLevels - 1,\n messageSections: chunkSummarySections,\n })\n\n return {\n summary: merged.summary,\n summaryNormalization: mergeSummaryNormalizationModes(\n summaryNormalization,\n merged.summaryNormalization,\n ),\n summaryCallCount: summaryCallCount + merged.summaryCallCount,\n summaryChunkCount: chunks.length + merged.summaryChunkCount,\n }\n}\n\nasync function streamSummaryText(input: {\n sessionId: string\n summaryModel: ModelRef\n maxSummaryTokens: number\n summaryPrompt: string\n summaryInputText: string\n summaryKind: string\n usageKind: UsageRequestKind\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n logger: LoggerGateway\n chunkIndex?: number\n chunkCount?: number\n}): Promise<SuccessfulContinuationSummary> {\n const request: ModelRequest = {\n model: input.summaryModel,\n maxOutputTokens: input.maxSummaryTokens,\n metadata: {\n dimAutoCompact: true,\n dimAutoCompactKind: input.summaryKind,\n sessionId: input.sessionId,\n ...(typeof input.chunkIndex === 'number' ? { chunkIndex: input.chunkIndex } : {}),\n ...(typeof input.chunkCount === 'number' ? { chunkCount: input.chunkCount } : {}),\n },\n messages: [\n createSyntheticMessage('system', input.summaryPrompt),\n createSyntheticMessage('user', input.summaryInputText),\n ],\n }\n\n let text = ''\n for await (const event of input.model.stream(request, {\n usage: {\n sessionId: input.sessionId,\n kind: input.usageKind,\n },\n })) {\n if (event.type === 'text_delta') text += event.delta\n if (event.type === 'error')\n throw createCodedError(\n event.error.code ?? 'summary_generation_failed',\n event.error.message,\n )\n }\n\n if (!text.trim())\n throw createCodedError('empty_summary_body', 'Auto compact summary body cannot be empty')\n\n const canonical = canonicalizeContinuationSummary(text)\n if (canonical.status === 'empty_body') {\n throw createCodedError('empty_summary_body', 'Auto compact summary body cannot be empty')\n }\n if (canonical.normalization !== 'plain_text') {\n input.logger.emit({\n level: 'warn',\n source: 'auto-compact',\n message:\n 'Auto compact summary output deviated from the preferred plain-text contract; normalized automatically',\n metadata: {\n sessionId: input.sessionId,\n summaryKind: input.summaryKind,\n summaryNormalization: canonical.normalization,\n ...(typeof input.chunkIndex === 'number' ? { chunkIndex: input.chunkIndex } : {}),\n ...(typeof input.chunkCount === 'number' ? { chunkCount: input.chunkCount } : {}),\n },\n })\n }\n\n return canonical\n}\n\nfunction renderMessage(\n message: Message,\n pruneToolOutputs: boolean,\n preservedToolOutputIds: Set<string>,\n): RenderedMessage {\n const text = renderMessageText(message).trim() || '(empty)'\n\n if (message.role !== 'tool') {\n return {\n text: `[${message.role}] ${text}`,\n prunedToolOutput: false,\n preservedToolOutput: false,\n }\n }\n\n const header = `[tool:${message.toolName} call=${message.toolCallId} status=${\n message.isError ? 'error' : 'ok'\n }]`\n if (!pruneToolOutputs || preservedToolOutputIds.has(message.id)) {\n const truncated = truncateText(text, DEFAULT_TOOL_OUTPUT_CHAR_LIMIT)\n return {\n text: [header, truncated].join('\\n'),\n prunedToolOutput: false,\n preservedToolOutput: pruneToolOutputs,\n }\n }\n\n return {\n text: `${header}\\noutput omitted for compaction summary (originalChars=${text.length})`,\n prunedToolOutput: true,\n preservedToolOutput: false,\n }\n}\n\nfunction renderMessageText(message: Message): string {\n const blocks = message.content.map((block) => {\n if (block.type === 'text') return block.text\n return `[file:${block.mediaType}]`\n })\n const text = blocks.join('\\n').trim()\n\n if (message.role === 'tool' && text.length === 0 && message.structuredContent)\n return JSON.stringify(message.structuredContent)\n\n return text\n}\n\nfunction buildPreservedToolOutputIds(\n messages: Message[],\n pruneToolOutputs: boolean,\n): Set<string> {\n if (!pruneToolOutputs)\n return new Set(\n messages\n .filter((message) => message.role === 'tool')\n .map((message) => message.id),\n )\n\n const preserved = new Set<string>()\n const toolMessages = messages.filter(\n (message): message is Extract<Message, { role: 'tool' }> => message.role === 'tool',\n )\n\n for (const message of toolMessages.slice(-DEFAULT_RECENT_TOOL_OUTPUTS_TO_PRESERVE))\n preserved.add(message.id)\n for (const message of toolMessages) {\n if (message.isError) preserved.add(message.id)\n }\n\n return preserved\n}\n\nfunction createRenderedSummarySection(id: string, text: string): RenderedSummarySection {\n return {\n id,\n text,\n tokens: estimateTextTokens(text),\n }\n}\n\nfunction createSummaryChunks(\n prefixSections: RenderedSummarySection[],\n messageSections: RenderedSummarySection[],\n summaryInputBudget: number,\n): RenderedSummarySection[][] {\n if (messageSections.length === 0) return []\n\n const prefixTokens = estimateSectionsTokens(prefixSections)\n const messageBudget = summaryInputBudget - prefixTokens\n if (messageBudget <= 64) return []\n\n const chunks: RenderedSummarySection[][] = []\n let currentChunk: RenderedSummarySection[] = []\n let currentChunkTokens = 0\n\n for (const section of messageSections) {\n let nextSection = section\n if (nextSection.tokens > messageBudget) {\n nextSection = truncateSectionToBudget(nextSection, messageBudget)\n if (nextSection.tokens > messageBudget) return []\n }\n\n if (currentChunk.length > 0 && currentChunkTokens + nextSection.tokens > messageBudget) {\n chunks.push(currentChunk)\n currentChunk = []\n currentChunkTokens = 0\n }\n\n currentChunk.push(nextSection)\n currentChunkTokens += nextSection.tokens\n }\n\n if (currentChunk.length > 0) chunks.push(currentChunk)\n return chunks\n}\n\nfunction truncateSectionToBudget(\n section: RenderedSummarySection,\n budgetTokens: number,\n): RenderedSummarySection {\n const budgetChars = Math.max(128, budgetTokens * 4 - 64)\n if (section.text.length <= budgetChars) return section\n\n return createRenderedSummarySection(section.id, truncateText(section.text, budgetChars))\n}\n\nfunction truncateText(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text\n\n const suffix = `\\n[truncated from ${text.length} chars]`\n const preservedChars = Math.max(0, maxChars - suffix.length)\n return `${text.slice(0, preservedChars).trimEnd()}${suffix}`\n}\n\nfunction joinSummarySections(\n prefixSections: RenderedSummarySection[],\n messageSections: RenderedSummarySection[],\n): string {\n return [...prefixSections, ...messageSections].map((section) => section.text).join('\\n\\n')\n}\n\nfunction estimateSectionsTokens(sections: RenderedSummarySection[]): number {\n return sections.reduce((total, section) => total + section.tokens, 0)\n}\n\nfunction estimateTextTokens(text: string): number {\n return Math.max(1, Math.ceil(text.length / 4))\n}\n\nfunction createSyntheticMessage(role: 'system' | 'user', text: string): Message {\n return {\n id: `auto_compact_${role}_${Date.now()}`,\n role,\n content: [{ type: 'text', text }],\n createdAt: Date.now(),\n }\n}\n\nfunction canonicalizeContinuationSummary(summary: string): CanonicalContinuationSummary {\n const trimmed = summary.trim()\n if (!trimmed) return { status: 'empty_body' }\n\n const blockMatches = [...trimmed.matchAll(CONTINUATION_SUMMARY_BLOCK_PATTERN)]\n const blockBodies = blockMatches\n .map((match) => match?.[1]?.trim() ?? '')\n .filter((body) => body.length > 0)\n\n if (blockBodies.length > 0) {\n const [match] = blockMatches\n return {\n status: 'normalized',\n summary: renderCanonicalContinuationSummary(blockBodies.join('\\n\\n')),\n normalization:\n blockMatches.length === 1 && match?.index === 0 && match[0].length === trimmed.length\n ? 'wrapped_block'\n : 'extracted_block',\n }\n }\n\n const stripped = stripContinuationSummaryTagFragments(trimmed).trim()\n if (!stripped) return { status: 'empty_body' }\n\n return {\n status: 'normalized',\n summary: renderCanonicalContinuationSummary(stripped),\n normalization: stripped === trimmed ? 'plain_text' : 'extracted_block',\n }\n}\n\nfunction renderCanonicalContinuationSummary(body: string): string {\n return `${CONTINUATION_SUMMARY_OPEN_TAG}\\n${body}\\n${CONTINUATION_SUMMARY_CLOSE_TAG}`\n}\n\nfunction stripContinuationSummaryTagFragments(text: string): string {\n return text.replace(CONTINUATION_SUMMARY_TAG_FRAGMENT_PATTERN, '\\n')\n}\n\nfunction mergeSummaryNormalizationModes(\n left: SummaryNormalizationMode,\n right: SummaryNormalizationMode,\n): SummaryNormalizationMode {\n const rank: Record<SummaryNormalizationMode, number> = {\n plain_text: 0,\n wrapped_block: 1,\n extracted_block: 2,\n }\n return rank[left] >= rank[right] ? left : right\n}\n\nasync function persistPluginState(\n context: PluginSessionControllerContext,\n state: AutoCompactPluginStateData,\n): Promise<void> {\n await context.pluginState.replace(context.sessionId, {\n version: 2,\n data: serializePluginState(state),\n updatedAt: Date.now(),\n })\n}\n\nfunction readPluginState(entry: PluginSessionStateEntry | null): AutoCompactPluginStateData {\n if (!entry || entry.version !== 2) return { strategy: 'controller-v2' }\n if (!isAutoCompactPluginStateData(entry.data)) return { strategy: 'controller-v2' }\n return structuredClone(entry.data)\n}\n\nfunction serializePluginState(state: AutoCompactPluginStateData): JsonObject {\n return {\n strategy: state.strategy,\n ...(state.summaryModel\n ? {\n summaryModel: {\n provider: state.summaryModel.provider,\n modelId: state.summaryModel.modelId,\n },\n }\n : {}),\n ...(state.lastResult\n ? {\n lastResult: {\n trigger: state.lastResult.trigger,\n status: state.lastResult.status,\n compactedMessageCount: state.lastResult.compactedMessageCount,\n retainedMessageCount: state.lastResult.retainedMessageCount,\n updatedAt: state.lastResult.updatedAt,\n ...(state.lastResult.reasonCode\n ? { reasonCode: state.lastResult.reasonCode }\n : {}),\n ...(state.lastResult.reasonMessage\n ? { reasonMessage: state.lastResult.reasonMessage }\n : {}),\n ...(state.lastResult.summaryNormalization\n ? { summaryNormalization: state.lastResult.summaryNormalization }\n : {}),\n ...(typeof state.lastResult.summaryInputTokens === 'number'\n ? { summaryInputTokens: state.lastResult.summaryInputTokens }\n : {}),\n ...(typeof state.lastResult.summaryOutputTokensEstimate === 'number'\n ? {\n summaryOutputTokensEstimate: state.lastResult.summaryOutputTokensEstimate,\n }\n : {}),\n ...(typeof state.lastResult.summaryChars === 'number'\n ? { summaryChars: state.lastResult.summaryChars }\n : {}),\n ...(typeof state.lastResult.summaryCallCount === 'number'\n ? { summaryCallCount: state.lastResult.summaryCallCount }\n : {}),\n ...(typeof state.lastResult.summaryChunkCount === 'number'\n ? { summaryChunkCount: state.lastResult.summaryChunkCount }\n : {}),\n ...(typeof state.lastResult.prunedToolOutputCount === 'number'\n ? { prunedToolOutputCount: state.lastResult.prunedToolOutputCount }\n : {}),\n ...(typeof state.lastResult.preservedToolOutputCount === 'number'\n ? { preservedToolOutputCount: state.lastResult.preservedToolOutputCount }\n : {}),\n },\n }\n : {}),\n }\n}\n\nfunction createPersistedResult(\n request: CompactionExecutionRequest,\n result: CompactionExecutionResult,\n): AutoCompactPersistedResult {\n return {\n trigger: request.plan.trigger,\n status: result.status,\n compactedMessageCount: request.plan.compactedMessages.length,\n retainedMessageCount: request.plan.retainedMessages.length,\n updatedAt: Date.now(),\n ...(result.reasonCode ? { reasonCode: result.reasonCode } : {}),\n ...(result.reasonMessage ? { reasonMessage: result.reasonMessage } : {}),\n ...(result.diagnostics.summaryNormalization\n ? { summaryNormalization: result.diagnostics.summaryNormalization }\n : {}),\n ...(typeof result.diagnostics.summaryInputTokens === 'number'\n ? { summaryInputTokens: result.diagnostics.summaryInputTokens }\n : {}),\n ...(typeof result.diagnostics.summaryOutputTokensEstimate === 'number'\n ? {\n summaryOutputTokensEstimate: result.diagnostics.summaryOutputTokensEstimate,\n }\n : {}),\n ...(typeof result.diagnostics.summaryChars === 'number'\n ? { summaryChars: result.diagnostics.summaryChars }\n : {}),\n ...(typeof result.diagnostics.summaryCallCount === 'number'\n ? { summaryCallCount: result.diagnostics.summaryCallCount }\n : {}),\n ...(typeof result.diagnostics.summaryChunkCount === 'number'\n ? { summaryChunkCount: result.diagnostics.summaryChunkCount }\n : {}),\n ...(typeof result.diagnostics.prunedToolOutputCount === 'number'\n ? { prunedToolOutputCount: result.diagnostics.prunedToolOutputCount }\n : {}),\n ...(typeof result.diagnostics.preservedToolOutputCount === 'number'\n ? { preservedToolOutputCount: result.diagnostics.preservedToolOutputCount }\n : {}),\n }\n}\n\nfunction positiveIntegerOr(value: number | undefined, fallback: number): number {\n if (!Number.isFinite(value) || value === undefined || value <= 0) return fallback\n return Math.floor(value)\n}\n\nfunction isAutoCompactPluginStateData(value: unknown): value is AutoCompactPluginStateData {\n if (!isRecord(value)) return false\n if (value.strategy !== 'controller-v2') return false\n if (value.summaryModel !== undefined && !isModelRef(value.summaryModel)) return false\n if (value.lastResult !== undefined && !isAutoCompactPersistedResult(value.lastResult)) return false\n return true\n}\n\nfunction isAutoCompactPersistedResult(value: unknown): value is AutoCompactPersistedResult {\n if (!isRecord(value)) return false\n if (value.trigger !== 'manual' && value.trigger !== 'threshold') return false\n if (value.status !== 'compacted' && value.status !== 'skipped' && value.status !== 'failed')\n return false\n if (typeof value.compactedMessageCount !== 'number') return false\n if (typeof value.retainedMessageCount !== 'number') return false\n if (typeof value.updatedAt !== 'number') return false\n if (value.reasonCode !== undefined && typeof value.reasonCode !== 'string') return false\n if (value.reasonMessage !== undefined && typeof value.reasonMessage !== 'string') return false\n if (\n value.summaryNormalization !== undefined &&\n value.summaryNormalization !== 'plain_text' &&\n value.summaryNormalization !== 'wrapped_block' &&\n value.summaryNormalization !== 'extracted_block'\n )\n return false\n if (value.summaryInputTokens !== undefined && typeof value.summaryInputTokens !== 'number')\n return false\n if (\n value.summaryOutputTokensEstimate !== undefined &&\n typeof value.summaryOutputTokensEstimate !== 'number'\n )\n return false\n if (value.summaryChars !== undefined && typeof value.summaryChars !== 'number') return false\n if (value.summaryCallCount !== undefined && typeof value.summaryCallCount !== 'number')\n return false\n if (value.summaryChunkCount !== undefined && typeof value.summaryChunkCount !== 'number')\n return false\n if (\n value.prunedToolOutputCount !== undefined &&\n typeof value.prunedToolOutputCount !== 'number'\n )\n return false\n if (\n value.preservedToolOutputCount !== undefined &&\n typeof value.preservedToolOutputCount !== 'number'\n )\n return false\n return true\n}\n\nfunction isModelRef(value: unknown): value is ModelRef {\n return isRecord(value) && typeof value.provider === 'string' && typeof value.modelId === 'string'\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction createCodedError(code: string, message: string): Error & { code: string } {\n const error = new Error(message) as Error & { code: string }\n error.code = code\n return error\n}\n\nfunction readErrorCode(error: unknown): string | undefined {\n if (!isRecord(error)) return undefined\n return typeof error.code === 'string' ? error.code : undefined\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\n CompactionExecutionRequest,\n CompactionExecutionResult,\n CompactionSummaryInputMode,\n Message,\n ModelRef,\n ModelRequest,\n ModelRequestUsageScope,\n ModelStreamEvent,\n SessionCompactorController,\n SessionStatus,\n UsageRequestKind,\n} from '@archships/dim-agent-sdk'\nimport type {\n DimPlugin,\n JsonObject,\n LoggerGateway,\n PluginSessionControllerContext,\n PluginSessionStateEntry,\n StructuredData,\n} from '@archships/dim-plugin-api'\n\nexport interface AutoCompactPluginOptions {\n summaryModel?: ModelRef\n maxSummaryTokens?: number\n summaryInputMaxTokens?: number\n summaryPrompt?: string\n retainMessages?: number\n compaction?: {\n auto?: boolean\n prune?: boolean\n reserved?: number\n }\n}\n\ninterface ResolvedOptions {\n summaryModel?: ModelRef\n maxSummaryTokens: number\n summaryInputMaxTokens: number\n summaryPrompt: string\n retainMessages: number\n compaction: {\n auto: boolean\n prune: boolean\n reserved: number\n }\n}\n\ninterface AutoCompactPluginStateData {\n strategy: 'controller-v2'\n summaryModel?: ModelRef\n lastResult?: AutoCompactPersistedResult\n}\n\ninterface AutoCompactPersistedResult {\n trigger: 'manual' | 'threshold'\n status: 'compacted' | 'skipped' | 'failed'\n reasonCode?: string\n reasonMessage?: string\n compactedMessageCount: number\n retainedMessageCount: number\n summaryNormalization?: SummaryNormalizationMode\n summaryInputTokens?: number\n summaryOutputTokensEstimate?: number\n summaryChars?: number\n summaryCallCount?: number\n summaryChunkCount?: number\n prunedToolOutputCount?: number\n preservedToolOutputCount?: number\n updatedAt: number\n}\n\ninterface RenderedSummarySection {\n id: string\n text: string\n tokens: number\n}\n\ninterface RenderedSummaryInput {\n prefixSections: RenderedSummarySection[]\n messageSections: RenderedSummarySection[]\n prunedToolOutputCount: number\n preservedToolOutputCount: number\n}\n\ninterface NormalizedSummaryGenerationResult {\n summary: string\n summaryNormalization: SummaryNormalizationMode\n summaryInputTokens: number\n summaryOutputTokensEstimate: number\n summaryChars: number\n summaryCallCount: number\n summaryChunkCount: number\n prunedToolOutputCount: number\n preservedToolOutputCount: number\n}\n\ntype SummaryGenerationResult =\n | ({ status: 'normalized' } & NormalizedSummaryGenerationResult)\n | { status: 'empty_body_preserve_previous' }\n | { status: 'invalid_summary_contract' }\n\ntype SummaryNormalizationMode = 'plain_text' | 'wrapped_block' | 'extracted_block'\n\ntype CanonicalContinuationSummary =\n | {\n status: 'normalized'\n summary: string\n normalization: SummaryNormalizationMode\n }\n | {\n status: 'empty_body'\n }\n\ntype SuccessfulContinuationSummary = Extract<\n CanonicalContinuationSummary,\n { status: 'normalized' }\n>\n\ninterface RenderedMessage {\n text: string\n prunedToolOutput: boolean\n preservedToolOutput: boolean\n}\n\ninterface AutoCompactPlanningController extends SessionCompactorController {\n readPlanningOptions(): {\n auto: boolean\n retainMessages: number\n reservedTokens: number\n summaryTokenReserve: number\n }\n}\n\nconst DEFAULT_MAX_SUMMARY_TOKENS = 1024\nconst DEFAULT_SUMMARY_INPUT_MAX_TOKENS = 16000\nconst DEFAULT_RETAIN_MESSAGES = 4\nconst DEFAULT_THRESHOLD_HEADROOM = 1024\nconst DEFAULT_RECENT_TOOL_OUTPUTS_TO_PRESERVE = 2\nconst DEFAULT_TOOL_OUTPUT_CHAR_LIMIT = 1200\nconst DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN = 1024\nconst MAX_SUMMARY_RECURSION_PASSES = 2\nconst CONTINUATION_SUMMARY_OPEN_TAG = '<continuation_summary>'\nconst CONTINUATION_SUMMARY_CLOSE_TAG = '</continuation_summary>'\nconst CONTINUATION_SUMMARY_BLOCK_PATTERN =\n /<continuation_summary>([\\s\\S]*?)<\\/continuation_summary>/g\nconst CONTINUATION_SUMMARY_TAG_FRAGMENT_PATTERN = /<\\/?continuation_summary>?/g\n\nconst DEFAULT_SUMMARY_PROMPT = `You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include:\n\nTask Overview\nThe user's core request and success criteria\nAny clarifications or constraints they specified\n\nCurrent State\nWhat has been completed so far\nFiles created, modified, or analyzed (with paths if relevant)\nKey outputs or artifacts produced\n\nImportant Discoveries\nTechnical constraints or requirements uncovered\nDecisions made and their rationale\nErrors encountered and how they were resolved\nWhat approaches were tried that didn't work (and why)\n\nNext Steps\nSpecific actions needed to complete the task\nAny blockers or open questions to resolve\nPriority order if multiple steps remain\n\nContext to Preserve\nUser preferences or style requirements\nDomain-specific details that aren't obvious\nAny promises made to the user\n\nBe concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes.\nWrite in a way that enables immediate resumption of the task.\nReturn only the continuation summary body as plain text. Do not add XML tags, wrappers, or surrounding commentary.`\n\nexport function createAutoCompactPlugin(): DimPlugin<'auto-compact', SessionCompactorController>\nexport function createAutoCompactPlugin(\n options: AutoCompactPluginOptions,\n): DimPlugin<'auto-compact', SessionCompactorController>\nexport function createAutoCompactPlugin<TId extends string>(\n options: AutoCompactPluginOptions & { id: TId },\n): DimPlugin<TId, SessionCompactorController>\nexport function createAutoCompactPlugin<TId extends string>(\n options: AutoCompactPluginOptions & { id?: TId } = {},\n): DimPlugin<TId, SessionCompactorController> {\n const resolved = resolveOptions(options)\n\n return {\n manifest: {\n id: (options.id ?? 'auto-compact') as TId,\n version: '0.2.0',\n apiVersion: 1,\n permissions: { model: true },\n capabilities: ['auto-compact'],\n },\n setup(context) {\n return {\n inspector: {\n getConfig: async () =>\n ({\n summaryModel: resolved.summaryModel\n ? {\n provider: resolved.summaryModel.provider,\n modelId: resolved.summaryModel.modelId,\n }\n : null,\n maxSummaryTokens: resolved.maxSummaryTokens,\n summaryInputMaxTokens: resolved.summaryInputMaxTokens,\n summaryPrompt: resolved.summaryPrompt,\n retainMessages: resolved.retainMessages,\n compaction: {\n auto: resolved.compaction.auto,\n prune: resolved.compaction.prune,\n reserved: resolved.compaction.reserved,\n },\n }) as unknown as StructuredData,\n getSessionState: async (sessionId) =>\n structuredClone(\n readPluginState(await context.services.pluginState.get(sessionId)),\n ) as unknown as StructuredData,\n },\n createSessionController: (controllerContext) =>\n createAutoCompactController({\n options: resolved,\n logger: context.services.logger,\n model: context.services.model,\n context: controllerContext,\n }),\n }\n },\n }\n}\n\nfunction createAutoCompactController(input: {\n options: ResolvedOptions\n logger: LoggerGateway\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n context: PluginSessionControllerContext\n}): AutoCompactPlanningController {\n return {\n readPlanningOptions() {\n return {\n auto: input.options.compaction.auto,\n retainMessages: input.options.retainMessages,\n reservedTokens: input.options.compaction.reserved,\n summaryTokenReserve: input.options.maxSummaryTokens,\n }\n },\n async compact(request: CompactionExecutionRequest): Promise<CompactionExecutionResult> {\n const sessionStatus = input.context.getStatus()\n const summaryModel = input.options.summaryModel ?? sessionStatus.model\n\n if (!summaryModel) {\n const result: CompactionExecutionResult = {\n status: 'skipped',\n reasonCode: 'missing_summary_model',\n reasonMessage: 'Auto compact summary model is unavailable',\n diagnostics: {},\n }\n await persistPluginState(input.context, {\n strategy: 'controller-v2',\n ...(input.options.summaryModel ? { summaryModel: input.options.summaryModel } : {}),\n lastResult: createPersistedResult(request, result),\n })\n return result\n }\n\n try {\n const summary = await generateSummary({\n contextStatus: sessionStatus,\n summaryModel,\n maxSummaryTokens: input.options.maxSummaryTokens,\n summaryInputMaxTokens: input.options.summaryInputMaxTokens,\n summaryPrompt: input.options.summaryPrompt,\n existingSystemSegments: request.plan.existingSystemSegments,\n compactedMessages: request.plan.compactedMessages,\n summaryInputMode: request.summaryInputMode,\n pruneToolOutputs: input.options.compaction.prune,\n sessionId: input.context.sessionId,\n usageKind:\n request.plan.trigger === 'manual' ? 'manual_compaction' : 'auto_compaction',\n model: input.model,\n logger: input.logger,\n })\n\n const result =\n summary.status === 'normalized'\n ? {\n status: 'compacted' as const,\n summary: summary.summary,\n diagnostics: {\n summaryNormalization: summary.summaryNormalization,\n summaryInputTokens: summary.summaryInputTokens,\n summaryOutputTokensEstimate: summary.summaryOutputTokensEstimate,\n summaryChars: summary.summaryChars,\n summaryCallCount: summary.summaryCallCount,\n summaryChunkCount: summary.summaryChunkCount,\n prunedToolOutputCount: summary.prunedToolOutputCount,\n preservedToolOutputCount: summary.preservedToolOutputCount,\n },\n }\n : summary.status === 'empty_body_preserve_previous'\n ? {\n status: 'skipped' as const,\n reasonCode: 'empty_summary_preserved_previous',\n reasonMessage:\n 'Summary generation produced an empty body and preserved the existing continuation summary',\n diagnostics: {},\n }\n : {\n status: 'failed' as const,\n reasonCode: 'invalid_summary_contract',\n reasonMessage: 'Auto compact summary body cannot be empty',\n diagnostics: {},\n }\n\n await persistPluginState(input.context, {\n strategy: 'controller-v2',\n summaryModel,\n lastResult: createPersistedResult(request, result),\n })\n return result\n } catch (error) {\n const result: CompactionExecutionResult = {\n status: 'failed',\n reasonCode: readErrorCode(error) ?? 'summary_generation_failed',\n reasonMessage: readErrorMessage(error),\n diagnostics: {},\n }\n await persistPluginState(input.context, {\n strategy: 'controller-v2',\n summaryModel,\n lastResult: createPersistedResult(request, result),\n })\n return result\n }\n },\n }\n}\n\nfunction resolveOptions(options: AutoCompactPluginOptions): ResolvedOptions {\n return {\n summaryModel: options.summaryModel,\n maxSummaryTokens: positiveIntegerOr(\n options.maxSummaryTokens,\n DEFAULT_MAX_SUMMARY_TOKENS,\n ),\n summaryInputMaxTokens: Math.max(\n DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN + 256,\n positiveIntegerOr(options.summaryInputMaxTokens, DEFAULT_SUMMARY_INPUT_MAX_TOKENS),\n ),\n summaryPrompt: options.summaryPrompt?.trim() || DEFAULT_SUMMARY_PROMPT,\n retainMessages: Math.max(1, positiveIntegerOr(options.retainMessages, DEFAULT_RETAIN_MESSAGES)),\n compaction: {\n auto: options.compaction?.auto ?? true,\n prune: options.compaction?.prune ?? true,\n reserved: Math.max(0, Math.floor(options.compaction?.reserved ?? DEFAULT_THRESHOLD_HEADROOM)),\n },\n }\n}\n\nasync function generateSummary(input: {\n contextStatus?: SessionStatus\n summaryModel: ModelRef\n maxSummaryTokens: number\n summaryInputMaxTokens: number\n summaryPrompt: string\n existingSystemSegments: string[]\n compactedMessages: Message[]\n summaryInputMode: CompactionSummaryInputMode\n pruneToolOutputs: boolean\n sessionId: string\n usageKind: UsageRequestKind\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n logger: LoggerGateway\n}): Promise<SummaryGenerationResult> {\n const renderedInput = buildSummaryInput(\n input.existingSystemSegments,\n input.compactedMessages,\n input.summaryInputMode,\n input.pruneToolOutputs,\n input.contextStatus,\n )\n const summaryInputTokens = estimateSectionsTokens([\n ...renderedInput.prefixSections,\n ...renderedInput.messageSections,\n ])\n const summaryInputBudget = Math.max(\n 256,\n input.summaryInputMaxTokens - DEFAULT_SUMMARY_INPUT_SAFETY_MARGIN,\n )\n let summarized: Awaited<ReturnType<typeof summarizeRenderedSections>>\n\n try {\n summarized = await summarizeRenderedSections({\n stage: 'source',\n remainingLevels: MAX_SUMMARY_RECURSION_PASSES,\n prefixSections: renderedInput.prefixSections,\n messageSections: renderedInput.messageSections,\n summaryInputBudget,\n sessionId: input.sessionId,\n summaryModel: input.summaryModel,\n maxSummaryTokens: input.maxSummaryTokens,\n summaryPrompt: input.summaryPrompt,\n usageKind: input.usageKind,\n model: input.model,\n logger: input.logger,\n })\n } catch (error) {\n if (readErrorCode(error) === 'empty_summary_body') {\n return input.existingSystemSegments.join('\\n\\n').trim()\n ? { status: 'empty_body_preserve_previous' }\n : { status: 'invalid_summary_contract' }\n }\n throw error\n }\n\n return {\n status: 'normalized',\n summary: summarized.summary,\n summaryNormalization: summarized.summaryNormalization,\n summaryInputTokens,\n summaryOutputTokensEstimate: estimateTextTokens(summarized.summary),\n summaryChars: summarized.summary.length,\n summaryCallCount: summarized.summaryCallCount,\n summaryChunkCount: summarized.summaryChunkCount,\n prunedToolOutputCount: renderedInput.prunedToolOutputCount,\n preservedToolOutputCount: renderedInput.preservedToolOutputCount,\n }\n}\n\nfunction buildSummaryInput(\n existingSystemSegments: string[],\n compactedMessages: Message[],\n summaryInputMode: CompactionSummaryInputMode,\n pruneToolOutputs: boolean,\n status?: SessionStatus,\n): RenderedSummaryInput {\n const summaryMessages =\n summaryInputMode === 'exclude_tool_messages'\n ? compactedMessages.filter((message) => message.role !== 'tool')\n : compactedMessages\n if (summaryMessages.length === 0) {\n return {\n prefixSections: [\n createRenderedSummarySection(\n 'status',\n [\n 'Current canonical session status:',\n status\n ? [\n `- sessionId: ${status.sessionId}`,\n `- model: ${status.model.provider}/${status.model.modelId}`,\n `- messageCount: ${status.messageCount}`,\n `- compactionCursor: ${status.compaction.cursor}`,\n ].join('\\n')\n : '- unavailable',\n ].join('\\n'),\n ),\n createRenderedSummarySection(\n 'existing-summary',\n [\n 'Existing compaction summary:',\n existingSystemSegments.length > 0 ? existingSystemSegments.join('\\n\\n') : '(none)',\n ].join('\\n'),\n ),\n createRenderedSummarySection(\n 'messages-heading',\n 'Messages to compact:\\n(no eligible messages after summary-input filtering)',\n ),\n ],\n messageSections: [],\n prunedToolOutputCount: 0,\n preservedToolOutputCount: 0,\n }\n }\n\n const preservedToolOutputIds = buildPreservedToolOutputIds(summaryMessages, pruneToolOutputs)\n let prunedToolOutputCount = 0\n let preservedToolOutputCount = 0\n\n const prefixSections = [\n createRenderedSummarySection(\n 'status',\n [\n 'Current canonical session status:',\n status\n ? [\n `- sessionId: ${status.sessionId}`,\n `- model: ${status.model.provider}/${status.model.modelId}`,\n `- messageCount: ${status.messageCount}`,\n `- compactionCursor: ${status.compaction.cursor}`,\n ].join('\\n')\n : '- unavailable',\n ].join('\\n'),\n ),\n createRenderedSummarySection(\n 'existing-summary',\n [\n 'Existing compaction summary:',\n existingSystemSegments.length > 0 ? existingSystemSegments.join('\\n\\n') : '(none)',\n ].join('\\n'),\n ),\n createRenderedSummarySection('messages-heading', 'Messages to compact:'),\n ]\n\n const messageSections = summaryMessages.map((message, index) => {\n const renderedMessage = renderMessage(message, pruneToolOutputs, preservedToolOutputIds)\n if (renderedMessage.prunedToolOutput) prunedToolOutputCount += 1\n if (renderedMessage.preservedToolOutput) preservedToolOutputCount += 1\n\n return createRenderedSummarySection(\n message.id,\n [`Message ${index + 1}`, renderedMessage.text].join('\\n'),\n )\n })\n\n return {\n prefixSections,\n messageSections,\n prunedToolOutputCount,\n preservedToolOutputCount,\n }\n}\n\nasync function summarizeRenderedSections(input: {\n stage: 'source' | 'merge'\n remainingLevels: number\n prefixSections: RenderedSummarySection[]\n messageSections: RenderedSummarySection[]\n summaryInputBudget: number\n sessionId: string\n summaryModel: ModelRef\n maxSummaryTokens: number\n summaryPrompt: string\n usageKind: UsageRequestKind\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n logger: LoggerGateway\n}): Promise<{\n summary: string\n summaryNormalization: SummaryNormalizationMode\n summaryCallCount: number\n summaryChunkCount: number\n}> {\n const combinedText = joinSummarySections(input.prefixSections, input.messageSections)\n if (estimateTextTokens(combinedText) <= input.summaryInputBudget) {\n const canonical = await streamSummaryText({\n sessionId: input.sessionId,\n summaryModel: input.summaryModel,\n maxSummaryTokens: input.maxSummaryTokens,\n summaryPrompt: input.summaryPrompt,\n summaryInputText: combinedText,\n summaryKind: input.stage === 'source' ? 'summary' : 'summary-merge',\n usageKind: input.usageKind,\n model: input.model,\n logger: input.logger,\n })\n return {\n summary: canonical.summary,\n summaryNormalization: canonical.normalization,\n summaryCallCount: 1,\n summaryChunkCount: 0,\n }\n }\n\n if (input.remainingLevels <= 1) {\n throw createCodedError(\n input.stage === 'merge' ? 'summary_merge_over_budget' : 'summary_source_too_large',\n input.stage === 'merge'\n ? 'Chunk summaries still exceed the summary model input budget'\n : 'Source history exceeds the summary model input budget',\n )\n }\n\n const chunks = createSummaryChunks(\n input.prefixSections,\n input.messageSections,\n input.summaryInputBudget,\n )\n if (chunks.length <= 1) {\n throw createCodedError(\n input.stage === 'merge' ? 'summary_merge_over_budget' : 'summary_source_too_large',\n input.stage === 'merge'\n ? 'Unable to merge chunk summaries within the summary model input budget'\n : 'Unable to fit source history into chunked summary requests',\n )\n }\n\n let summaryCallCount = 0\n let summaryNormalization: SummaryNormalizationMode = 'plain_text'\n const chunkSummarySections: RenderedSummarySection[] = []\n\n for (const [index, chunk] of chunks.entries()) {\n const canonical = await streamSummaryText({\n sessionId: input.sessionId,\n summaryModel: input.summaryModel,\n maxSummaryTokens: input.maxSummaryTokens,\n summaryPrompt: input.summaryPrompt,\n summaryInputText: joinSummarySections(input.prefixSections, chunk),\n summaryKind: 'summary-chunk',\n usageKind: input.usageKind,\n model: input.model,\n chunkIndex: index,\n chunkCount: chunks.length,\n logger: input.logger,\n })\n summaryCallCount += 1\n summaryNormalization = mergeSummaryNormalizationModes(\n summaryNormalization,\n canonical.normalization,\n )\n\n chunkSummarySections.push(\n createRenderedSummarySection(\n `chunk-summary-${index + 1}`,\n `Chunk summary ${index + 1} of ${chunks.length}\\n${canonical.summary}`,\n ),\n )\n }\n\n const merged = await summarizeRenderedSections({\n ...input,\n stage: 'merge',\n remainingLevels: input.remainingLevels - 1,\n messageSections: chunkSummarySections,\n })\n\n return {\n summary: merged.summary,\n summaryNormalization: mergeSummaryNormalizationModes(\n summaryNormalization,\n merged.summaryNormalization,\n ),\n summaryCallCount: summaryCallCount + merged.summaryCallCount,\n summaryChunkCount: chunks.length + merged.summaryChunkCount,\n }\n}\n\nasync function streamSummaryText(input: {\n sessionId: string\n summaryModel: ModelRef\n maxSummaryTokens: number\n summaryPrompt: string\n summaryInputText: string\n summaryKind: string\n usageKind: UsageRequestKind\n model: {\n stream(\n request: ModelRequest,\n options?: { usage?: ModelRequestUsageScope },\n ): AsyncIterable<ModelStreamEvent>\n }\n logger: LoggerGateway\n chunkIndex?: number\n chunkCount?: number\n}): Promise<SuccessfulContinuationSummary> {\n const request: ModelRequest = {\n model: input.summaryModel,\n maxOutputTokens: input.maxSummaryTokens,\n metadata: {\n dimAutoCompact: true,\n dimAutoCompactKind: input.summaryKind,\n sessionId: input.sessionId,\n ...(typeof input.chunkIndex === 'number' ? { chunkIndex: input.chunkIndex } : {}),\n ...(typeof input.chunkCount === 'number' ? { chunkCount: input.chunkCount } : {}),\n },\n messages: [\n createSyntheticMessage('system', input.summaryPrompt),\n createSyntheticMessage('user', input.summaryInputText),\n ],\n }\n\n let text = ''\n for await (const event of input.model.stream(request, {\n usage: {\n sessionId: input.sessionId,\n kind: input.usageKind,\n },\n })) {\n if (event.type === 'text_delta') text += event.delta\n if (event.type === 'error')\n throw createCodedError(\n event.error.code ?? 'summary_generation_failed',\n event.error.message,\n )\n }\n\n if (!text.trim())\n throw createCodedError('empty_summary_body', 'Auto compact summary body cannot be empty')\n\n const canonical = canonicalizeContinuationSummary(text)\n if (canonical.status === 'empty_body') {\n throw createCodedError('empty_summary_body', 'Auto compact summary body cannot be empty')\n }\n if (canonical.normalization !== 'plain_text') {\n input.logger.emit({\n level: 'warn',\n source: 'auto-compact',\n message:\n 'Auto compact summary output deviated from the preferred plain-text contract; normalized automatically',\n metadata: {\n sessionId: input.sessionId,\n summaryKind: input.summaryKind,\n summaryNormalization: canonical.normalization,\n ...(typeof input.chunkIndex === 'number' ? { chunkIndex: input.chunkIndex } : {}),\n ...(typeof input.chunkCount === 'number' ? { chunkCount: input.chunkCount } : {}),\n },\n })\n }\n\n return canonical\n}\n\nfunction renderMessage(\n message: Message,\n pruneToolOutputs: boolean,\n preservedToolOutputIds: Set<string>,\n): RenderedMessage {\n const text = renderMessageText(message).trim() || '(empty)'\n\n if (message.role !== 'tool') {\n return {\n text: `[${message.role}] ${text}`,\n prunedToolOutput: false,\n preservedToolOutput: false,\n }\n }\n\n const header = `[tool:${message.toolName} call=${message.toolCallId} status=${\n message.isError ? 'error' : 'ok'\n }]`\n if (!pruneToolOutputs || preservedToolOutputIds.has(message.id)) {\n const truncated = truncateText(text, DEFAULT_TOOL_OUTPUT_CHAR_LIMIT)\n return {\n text: [header, truncated].join('\\n'),\n prunedToolOutput: false,\n preservedToolOutput: pruneToolOutputs,\n }\n }\n\n return {\n text: `${header}\\noutput omitted for compaction summary (originalChars=${text.length})`,\n prunedToolOutput: true,\n preservedToolOutput: false,\n }\n}\n\nfunction renderMessageText(message: Message): string {\n const blocks = message.content.map((block) => {\n if (block.type === 'text') return block.text\n return `[file:${block.mediaType}]`\n })\n const text = blocks.join('\\n').trim()\n\n if (message.role === 'tool' && text.length === 0 && message.structuredContent)\n return JSON.stringify(message.structuredContent)\n\n return text\n}\n\nfunction buildPreservedToolOutputIds(\n messages: Message[],\n pruneToolOutputs: boolean,\n): Set<string> {\n if (!pruneToolOutputs)\n return new Set(\n messages\n .filter((message) => message.role === 'tool')\n .map((message) => message.id),\n )\n\n const preserved = new Set<string>()\n const toolMessages = messages.filter(\n (message): message is Extract<Message, { role: 'tool' }> => message.role === 'tool',\n )\n\n for (const message of toolMessages.slice(-DEFAULT_RECENT_TOOL_OUTPUTS_TO_PRESERVE))\n preserved.add(message.id)\n for (const message of toolMessages) {\n if (message.isError) preserved.add(message.id)\n }\n\n return preserved\n}\n\nfunction createRenderedSummarySection(id: string, text: string): RenderedSummarySection {\n return {\n id,\n text,\n tokens: estimateTextTokens(text),\n }\n}\n\nfunction createSummaryChunks(\n prefixSections: RenderedSummarySection[],\n messageSections: RenderedSummarySection[],\n summaryInputBudget: number,\n): RenderedSummarySection[][] {\n if (messageSections.length === 0) return []\n\n const prefixTokens = estimateSectionsTokens(prefixSections)\n const messageBudget = summaryInputBudget - prefixTokens\n if (messageBudget <= 64) return []\n\n const chunks: RenderedSummarySection[][] = []\n let currentChunk: RenderedSummarySection[] = []\n let currentChunkTokens = 0\n\n for (const section of messageSections) {\n let nextSection = section\n if (nextSection.tokens > messageBudget) {\n nextSection = truncateSectionToBudget(nextSection, messageBudget)\n if (nextSection.tokens > messageBudget) return []\n }\n\n if (currentChunk.length > 0 && currentChunkTokens + nextSection.tokens > messageBudget) {\n chunks.push(currentChunk)\n currentChunk = []\n currentChunkTokens = 0\n }\n\n currentChunk.push(nextSection)\n currentChunkTokens += nextSection.tokens\n }\n\n if (currentChunk.length > 0) chunks.push(currentChunk)\n return chunks\n}\n\nfunction truncateSectionToBudget(\n section: RenderedSummarySection,\n budgetTokens: number,\n): RenderedSummarySection {\n const budgetChars = Math.max(128, budgetTokens * 4 - 64)\n if (section.text.length <= budgetChars) return section\n\n return createRenderedSummarySection(section.id, truncateText(section.text, budgetChars))\n}\n\nfunction truncateText(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text\n\n const suffix = `\\n[truncated from ${text.length} chars]`\n const preservedChars = Math.max(0, maxChars - suffix.length)\n return `${text.slice(0, preservedChars).trimEnd()}${suffix}`\n}\n\nfunction joinSummarySections(\n prefixSections: RenderedSummarySection[],\n messageSections: RenderedSummarySection[],\n): string {\n return [...prefixSections, ...messageSections].map((section) => section.text).join('\\n\\n')\n}\n\nfunction estimateSectionsTokens(sections: RenderedSummarySection[]): number {\n return sections.reduce((total, section) => total + section.tokens, 0)\n}\n\nfunction estimateTextTokens(text: string): number {\n return Math.max(1, Math.ceil(text.length / 4))\n}\n\nfunction createSyntheticMessage(role: 'system' | 'user', text: string): Message {\n return {\n id: `auto_compact_${role}_${Date.now()}`,\n role,\n content: [{ type: 'text', text }],\n createdAt: Date.now(),\n }\n}\n\nfunction canonicalizeContinuationSummary(summary: string): CanonicalContinuationSummary {\n const trimmed = summary.trim()\n if (!trimmed) return { status: 'empty_body' }\n\n const blockMatches = [...trimmed.matchAll(CONTINUATION_SUMMARY_BLOCK_PATTERN)]\n const blockBodies = blockMatches\n .map((match) => match?.[1]?.trim() ?? '')\n .filter((body) => body.length > 0)\n\n if (blockBodies.length > 0) {\n const [match] = blockMatches\n return {\n status: 'normalized',\n summary: renderCanonicalContinuationSummary(blockBodies.join('\\n\\n')),\n normalization:\n blockMatches.length === 1 && match?.index === 0 && match[0].length === trimmed.length\n ? 'wrapped_block'\n : 'extracted_block',\n }\n }\n\n const stripped = stripContinuationSummaryTagFragments(trimmed).trim()\n if (!stripped) return { status: 'empty_body' }\n\n return {\n status: 'normalized',\n summary: renderCanonicalContinuationSummary(stripped),\n normalization: stripped === trimmed ? 'plain_text' : 'extracted_block',\n }\n}\n\nfunction renderCanonicalContinuationSummary(body: string): string {\n return `${CONTINUATION_SUMMARY_OPEN_TAG}\\n${body}\\n${CONTINUATION_SUMMARY_CLOSE_TAG}`\n}\n\nfunction stripContinuationSummaryTagFragments(text: string): string {\n return text.replace(CONTINUATION_SUMMARY_TAG_FRAGMENT_PATTERN, '\\n')\n}\n\nfunction mergeSummaryNormalizationModes(\n left: SummaryNormalizationMode,\n right: SummaryNormalizationMode,\n): SummaryNormalizationMode {\n const rank: Record<SummaryNormalizationMode, number> = {\n plain_text: 0,\n wrapped_block: 1,\n extracted_block: 2,\n }\n return rank[left] >= rank[right] ? left : right\n}\n\nasync function persistPluginState(\n context: PluginSessionControllerContext,\n state: AutoCompactPluginStateData,\n): Promise<void> {\n await context.pluginState.replace(context.sessionId, {\n version: 2,\n data: serializePluginState(state),\n updatedAt: Date.now(),\n })\n}\n\nfunction readPluginState(entry: PluginSessionStateEntry | null): AutoCompactPluginStateData {\n if (!entry || entry.version !== 2) return { strategy: 'controller-v2' }\n if (!isAutoCompactPluginStateData(entry.data)) return { strategy: 'controller-v2' }\n return structuredClone(entry.data)\n}\n\nfunction serializePluginState(state: AutoCompactPluginStateData): JsonObject {\n return {\n strategy: state.strategy,\n ...(state.summaryModel\n ? {\n summaryModel: {\n provider: state.summaryModel.provider,\n modelId: state.summaryModel.modelId,\n },\n }\n : {}),\n ...(state.lastResult\n ? {\n lastResult: {\n trigger: state.lastResult.trigger,\n status: state.lastResult.status,\n compactedMessageCount: state.lastResult.compactedMessageCount,\n retainedMessageCount: state.lastResult.retainedMessageCount,\n updatedAt: state.lastResult.updatedAt,\n ...(state.lastResult.reasonCode\n ? { reasonCode: state.lastResult.reasonCode }\n : {}),\n ...(state.lastResult.reasonMessage\n ? { reasonMessage: state.lastResult.reasonMessage }\n : {}),\n ...(state.lastResult.summaryNormalization\n ? { summaryNormalization: state.lastResult.summaryNormalization }\n : {}),\n ...(typeof state.lastResult.summaryInputTokens === 'number'\n ? { summaryInputTokens: state.lastResult.summaryInputTokens }\n : {}),\n ...(typeof state.lastResult.summaryOutputTokensEstimate === 'number'\n ? {\n summaryOutputTokensEstimate: state.lastResult.summaryOutputTokensEstimate,\n }\n : {}),\n ...(typeof state.lastResult.summaryChars === 'number'\n ? { summaryChars: state.lastResult.summaryChars }\n : {}),\n ...(typeof state.lastResult.summaryCallCount === 'number'\n ? { summaryCallCount: state.lastResult.summaryCallCount }\n : {}),\n ...(typeof state.lastResult.summaryChunkCount === 'number'\n ? { summaryChunkCount: state.lastResult.summaryChunkCount }\n : {}),\n ...(typeof state.lastResult.prunedToolOutputCount === 'number'\n ? { prunedToolOutputCount: state.lastResult.prunedToolOutputCount }\n : {}),\n ...(typeof state.lastResult.preservedToolOutputCount === 'number'\n ? { preservedToolOutputCount: state.lastResult.preservedToolOutputCount }\n : {}),\n },\n }\n : {}),\n }\n}\n\nfunction createPersistedResult(\n request: CompactionExecutionRequest,\n result: CompactionExecutionResult,\n): AutoCompactPersistedResult {\n return {\n trigger: request.plan.trigger,\n status: result.status,\n compactedMessageCount: request.plan.compactedMessages.length,\n retainedMessageCount: request.plan.retainedMessages.length,\n updatedAt: Date.now(),\n ...(result.reasonCode ? { reasonCode: result.reasonCode } : {}),\n ...(result.reasonMessage ? { reasonMessage: result.reasonMessage } : {}),\n ...(result.diagnostics.summaryNormalization\n ? { summaryNormalization: result.diagnostics.summaryNormalization }\n : {}),\n ...(typeof result.diagnostics.summaryInputTokens === 'number'\n ? { summaryInputTokens: result.diagnostics.summaryInputTokens }\n : {}),\n ...(typeof result.diagnostics.summaryOutputTokensEstimate === 'number'\n ? {\n summaryOutputTokensEstimate: result.diagnostics.summaryOutputTokensEstimate,\n }\n : {}),\n ...(typeof result.diagnostics.summaryChars === 'number'\n ? { summaryChars: result.diagnostics.summaryChars }\n : {}),\n ...(typeof result.diagnostics.summaryCallCount === 'number'\n ? { summaryCallCount: result.diagnostics.summaryCallCount }\n : {}),\n ...(typeof result.diagnostics.summaryChunkCount === 'number'\n ? { summaryChunkCount: result.diagnostics.summaryChunkCount }\n : {}),\n ...(typeof result.diagnostics.prunedToolOutputCount === 'number'\n ? { prunedToolOutputCount: result.diagnostics.prunedToolOutputCount }\n : {}),\n ...(typeof result.diagnostics.preservedToolOutputCount === 'number'\n ? { preservedToolOutputCount: result.diagnostics.preservedToolOutputCount }\n : {}),\n }\n}\n\nfunction positiveIntegerOr(value: number | undefined, fallback: number): number {\n if (!Number.isFinite(value) || value === undefined || value <= 0) return fallback\n return Math.floor(value)\n}\n\nfunction isAutoCompactPluginStateData(value: unknown): value is AutoCompactPluginStateData {\n if (!isRecord(value)) return false\n if (value.strategy !== 'controller-v2') return false\n if (value.summaryModel !== undefined && !isModelRef(value.summaryModel)) return false\n if (value.lastResult !== undefined && !isAutoCompactPersistedResult(value.lastResult)) return false\n return true\n}\n\nfunction isAutoCompactPersistedResult(value: unknown): value is AutoCompactPersistedResult {\n if (!isRecord(value)) return false\n if (value.trigger !== 'manual' && value.trigger !== 'threshold') return false\n if (value.status !== 'compacted' && value.status !== 'skipped' && value.status !== 'failed')\n return false\n if (typeof value.compactedMessageCount !== 'number') return false\n if (typeof value.retainedMessageCount !== 'number') return false\n if (typeof value.updatedAt !== 'number') return false\n if (value.reasonCode !== undefined && typeof value.reasonCode !== 'string') return false\n if (value.reasonMessage !== undefined && typeof value.reasonMessage !== 'string') return false\n if (\n value.summaryNormalization !== undefined &&\n value.summaryNormalization !== 'plain_text' &&\n value.summaryNormalization !== 'wrapped_block' &&\n value.summaryNormalization !== 'extracted_block'\n )\n return false\n if (value.summaryInputTokens !== undefined && typeof value.summaryInputTokens !== 'number')\n return false\n if (\n value.summaryOutputTokensEstimate !== undefined &&\n typeof value.summaryOutputTokensEstimate !== 'number'\n )\n return false\n if (value.summaryChars !== undefined && typeof value.summaryChars !== 'number') return false\n if (value.summaryCallCount !== undefined && typeof value.summaryCallCount !== 'number')\n return false\n if (value.summaryChunkCount !== undefined && typeof value.summaryChunkCount !== 'number')\n return false\n if (\n value.prunedToolOutputCount !== undefined &&\n typeof value.prunedToolOutputCount !== 'number'\n )\n return false\n if (\n value.preservedToolOutputCount !== undefined &&\n typeof value.preservedToolOutputCount !== 'number'\n )\n return false\n return true\n}\n\nfunction isModelRef(value: unknown): value is ModelRef {\n return isRecord(value) && typeof value.provider === 'string' && typeof value.modelId === 'string'\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction createCodedError(code: string, message: string): Error & { code: string } {\n const error = new Error(message) as Error & { code: string }\n error.code = code\n return error\n}\n\nfunction readErrorCode(error: unknown): string | undefined {\n if (!isRecord(error)) return undefined\n return typeof error.code === 'string' ? error.code : undefined\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"],"mappings":"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"} |
+3
-3
| { | ||
| "name": "@archships/dim-plugin-auto-compact", | ||
| "version": "0.0.15", | ||
| "version": "0.0.16", | ||
| "description": "Official auto compaction plugin for dim-agent-sdk.", | ||
@@ -27,4 +27,4 @@ "homepage": "https://dimcode.dev/", | ||
| "devDependencies": { | ||
| "@archships/dim-agent-sdk": "0.0.55", | ||
| "@archships/dim-plugin-api": "0.0.21" | ||
| "@archships/dim-agent-sdk": "0.0.56", | ||
| "@archships/dim-plugin-api": "0.0.22" | ||
| }, | ||
@@ -31,0 +31,0 @@ "scripts": { |
+11
-8
@@ -7,9 +7,10 @@ # @archships/dim-plugin-auto-compact | ||
| - listens to `context.compact.before` | ||
| - exposes `SessionCompactorController.compact(...)` | ||
| - summarizes older history into one canonical compaction segment | ||
| - optionally uses a dedicated `summaryModel` instead of the session model | ||
| - aligns compaction cut points to real user messages so projected history never starts with `assistant` / `tool` | ||
| - injects the retained summary as a synthetic `user` message, and injects one lightweight auto-compact context `user` message ahead of the retained anchor query | ||
| - accepts an SDK-owned `CompactionExecutionRequest`, including `summaryInputMode: 'default' | 'exclude_tool_messages'` | ||
| - normalizes summary output back into one canonical `<continuation_summary>...</continuation_summary>` block | ||
| - prunes older tool outputs inside the plugin summary input when configured | ||
| - keeps full `session.messages` intact for UI and restore | ||
| - stores provenance in `pluginState['auto-compact']`, including rolling checkpoint entries (`checkpointId`, `createdAt`) plus semantic `lastAttempt` diagnostics | ||
| - stores plugin-owned provenance in `pluginState['auto-compact']`, including semantic `lastResult` diagnostics for the controller call only | ||
@@ -53,12 +54,14 @@ ## Requirements | ||
| - `maxSummaryTokens` is optional; the plugin defaults summary requests to `1024` | ||
| - automatic compaction starts from threshold compaction pressure; the plugin reacts in `context.compact.before` after SDK core budget detection fires | ||
| - summary failures are fail-open and fall back to `context_compaction_required` | ||
| - SDK core owns planning, cursor movement, checkpoints, typed notifications, and threshold fallback orchestration | ||
| - threshold fallback order is fixed in SDK core: default summary, one `exclude_tool_messages` retry, then SDK-owned last-message fallback | ||
| - the plugin only returns typed controller results; SDK-owned last-message fallback does not write into plugin state | ||
| - summary model calls now opt into the SDK usage ledger with semantic request kinds: threshold-triggered passes record `auto_compaction`, and `session.compact()` summary calls record `manual_compaction` | ||
| - the default summary prompt asks the model for plain-text summary body only; runtime normalizes accepted output back into one canonical `<continuation_summary>...</continuation_summary>` block before replay, including wrapped blocks, merged multi-block outputs, and malformed-tag fragments that still yield a non-empty semantic body | ||
| - when normalization yields an empty body, runtime preserves the previous saved continuation summary when one already exists; only empty output without a previous summary fails with `invalid_summary_contract` | ||
| - `lastAttempt` is updated on every threshold-triggered compaction pass with `planned` / `skipped` / `compacted` / `failed`, budget metadata, estimator-based `estimatedInputTokensAfter` / `estimatedSavedTokens`, reason codes such as `missing_summary_model`, `nothing_to_compact`, `empty_summary_preserved_previous`, `summary_generation_failed`, or `compaction_owner_required`, and optional `summaryNormalization` values (`plain_text`, `wrapped_block`, `extracted_block`) when the summary output was inspected | ||
| - `lastResult` is updated on every controller call with `compacted` / `skipped` / `failed`, plus summary normalization and summary-call diagnostics when available | ||
| - runtime notifications expose typed fallback metadata: `context.compacted.metadata.resolution` can be `summary`, `summary_without_tools`, or `last_message_fallback`; `context.compaction.failed.metadata.attemptStage` distinguishes `default_summary` from `summary_without_tools` | ||
| - persisted plugin state keeps only semantic provenance fields; summary-call counters and per-message id arrays stay out of `pluginState` | ||
| - `context.compacted` notifications now include compacted / retained message counts plus estimator-based before / after / saved token fields when budgeting is enabled | ||
| - `retainMessages` remains a preferred target, but the planner can keep compacting down to the newest real user boundary when needed | ||
| - the runtime now keeps leading system messages first, appends runtime prompt/context next, then uses synthetic `user` messages for the auto-compact environment note and the compaction summary around the retained anchor query | ||
| - the runtime keeps leading system messages first, appends runtime prompt/context next, then uses synthetic `user` messages for the auto-compact environment note and the compaction summary around the retained anchor query | ||
| - run `pnpm run demo:auto-compact` in the repo for the scripted walkthrough |
Explicitly Unlicensed Item
LicenseSomething was found which is explicitly marked as unlicensed.
Found 1 instance in 1 package
Explicitly Unlicensed Item
LicenseSomething was found which is explicitly marked as unlicensed.
Found 1 instance in 1 package
87036
3.81%618
2.83%66
4.76%