@animus-labs/cortex 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/budget-guard.d.ts +75 -0
- package/dist/budget-guard.d.ts.map +1 -0
- package/dist/budget-guard.js +142 -0
- package/dist/budget-guard.js.map +1 -0
- package/dist/compaction/compaction.d.ts +99 -0
- package/dist/compaction/compaction.d.ts.map +1 -0
- package/dist/compaction/compaction.js +302 -0
- package/dist/compaction/compaction.js.map +1 -0
- package/dist/compaction/failsafe.d.ts +57 -0
- package/dist/compaction/failsafe.d.ts.map +1 -0
- package/dist/compaction/failsafe.js +135 -0
- package/dist/compaction/failsafe.js.map +1 -0
- package/dist/compaction/index.d.ts +381 -0
- package/dist/compaction/index.d.ts.map +1 -0
- package/dist/compaction/index.js +979 -0
- package/dist/compaction/index.js.map +1 -0
- package/dist/compaction/microcompaction.d.ts +219 -0
- package/dist/compaction/microcompaction.d.ts.map +1 -0
- package/dist/compaction/microcompaction.js +536 -0
- package/dist/compaction/microcompaction.js.map +1 -0
- package/dist/compaction/observational/buffering.d.ts +225 -0
- package/dist/compaction/observational/buffering.d.ts.map +1 -0
- package/dist/compaction/observational/buffering.js +354 -0
- package/dist/compaction/observational/buffering.js.map +1 -0
- package/dist/compaction/observational/constants.d.ts +70 -0
- package/dist/compaction/observational/constants.d.ts.map +1 -0
- package/dist/compaction/observational/constants.js +507 -0
- package/dist/compaction/observational/constants.js.map +1 -0
- package/dist/compaction/observational/index.d.ts +219 -0
- package/dist/compaction/observational/index.d.ts.map +1 -0
- package/dist/compaction/observational/index.js +641 -0
- package/dist/compaction/observational/index.js.map +1 -0
- package/dist/compaction/observational/observer.d.ts +97 -0
- package/dist/compaction/observational/observer.d.ts.map +1 -0
- package/dist/compaction/observational/observer.js +424 -0
- package/dist/compaction/observational/observer.js.map +1 -0
- package/dist/compaction/observational/recall-tool.d.ts +27 -0
- package/dist/compaction/observational/recall-tool.d.ts.map +1 -0
- package/dist/compaction/observational/recall-tool.js +93 -0
- package/dist/compaction/observational/recall-tool.js.map +1 -0
- package/dist/compaction/observational/reflector.d.ts +94 -0
- package/dist/compaction/observational/reflector.d.ts.map +1 -0
- package/dist/compaction/observational/reflector.js +167 -0
- package/dist/compaction/observational/reflector.js.map +1 -0
- package/dist/compaction/observational/types.d.ts +271 -0
- package/dist/compaction/observational/types.d.ts.map +1 -0
- package/dist/compaction/observational/types.js +15 -0
- package/dist/compaction/observational/types.js.map +1 -0
- package/dist/context-manager.d.ts +134 -0
- package/dist/context-manager.d.ts.map +1 -0
- package/dist/context-manager.js +170 -0
- package/dist/context-manager.js.map +1 -0
- package/dist/cortex-agent.d.ts +1020 -0
- package/dist/cortex-agent.d.ts.map +1 -0
- package/dist/cortex-agent.js +3589 -0
- package/dist/cortex-agent.js.map +1 -0
- package/dist/error-classifier.d.ts +48 -0
- package/dist/error-classifier.d.ts.map +1 -0
- package/dist/error-classifier.js +152 -0
- package/dist/error-classifier.js.map +1 -0
- package/dist/event-bridge.d.ts +166 -0
- package/dist/event-bridge.d.ts.map +1 -0
- package/dist/event-bridge.js +381 -0
- package/dist/event-bridge.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-client.d.ts +119 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +474 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/model-wrapper.d.ts +58 -0
- package/dist/model-wrapper.d.ts.map +1 -0
- package/dist/model-wrapper.js +86 -0
- package/dist/model-wrapper.js.map +1 -0
- package/dist/noop-logger.d.ts +4 -0
- package/dist/noop-logger.d.ts.map +1 -0
- package/dist/noop-logger.js +8 -0
- package/dist/noop-logger.js.map +1 -0
- package/dist/prompt-diagnostics.d.ts +47 -0
- package/dist/prompt-diagnostics.d.ts.map +1 -0
- package/dist/prompt-diagnostics.js +230 -0
- package/dist/prompt-diagnostics.js.map +1 -0
- package/dist/provider-manager.d.ts +224 -0
- package/dist/provider-manager.d.ts.map +1 -0
- package/dist/provider-manager.js +563 -0
- package/dist/provider-manager.js.map +1 -0
- package/dist/provider-registry.d.ts +115 -0
- package/dist/provider-registry.d.ts.map +1 -0
- package/dist/provider-registry.js +305 -0
- package/dist/provider-registry.js.map +1 -0
- package/dist/schema-converter.d.ts +20 -0
- package/dist/schema-converter.d.ts.map +1 -0
- package/dist/schema-converter.js +48 -0
- package/dist/schema-converter.js.map +1 -0
- package/dist/skill-preprocessor.d.ts +46 -0
- package/dist/skill-preprocessor.d.ts.map +1 -0
- package/dist/skill-preprocessor.js +237 -0
- package/dist/skill-preprocessor.js.map +1 -0
- package/dist/skill-registry.d.ts +107 -0
- package/dist/skill-registry.d.ts.map +1 -0
- package/dist/skill-registry.js +330 -0
- package/dist/skill-registry.js.map +1 -0
- package/dist/skill-tool.d.ts +54 -0
- package/dist/skill-tool.d.ts.map +1 -0
- package/dist/skill-tool.js +88 -0
- package/dist/skill-tool.js.map +1 -0
- package/dist/sub-agent-manager.d.ts +90 -0
- package/dist/sub-agent-manager.d.ts.map +1 -0
- package/dist/sub-agent-manager.js +192 -0
- package/dist/sub-agent-manager.js.map +1 -0
- package/dist/token-estimator.d.ts +23 -0
- package/dist/token-estimator.d.ts.map +1 -0
- package/dist/token-estimator.js +27 -0
- package/dist/token-estimator.js.map +1 -0
- package/dist/tool-contract.d.ts +68 -0
- package/dist/tool-contract.d.ts.map +1 -0
- package/dist/tool-contract.js +35 -0
- package/dist/tool-contract.js.map +1 -0
- package/dist/tool-result-persistence.d.ts +89 -0
- package/dist/tool-result-persistence.d.ts.map +1 -0
- package/dist/tool-result-persistence.js +152 -0
- package/dist/tool-result-persistence.js.map +1 -0
- package/dist/tools/bash/index.d.ts +71 -0
- package/dist/tools/bash/index.d.ts.map +1 -0
- package/dist/tools/bash/index.js +485 -0
- package/dist/tools/bash/index.js.map +1 -0
- package/dist/tools/bash/interactive.d.ts +47 -0
- package/dist/tools/bash/interactive.d.ts.map +1 -0
- package/dist/tools/bash/interactive.js +262 -0
- package/dist/tools/bash/interactive.js.map +1 -0
- package/dist/tools/bash/safety.d.ts +149 -0
- package/dist/tools/bash/safety.d.ts.map +1 -0
- package/dist/tools/bash/safety.js +1116 -0
- package/dist/tools/bash/safety.js.map +1 -0
- package/dist/tools/edit.d.ts +57 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +310 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +34 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +268 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +53 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +673 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +62 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +43 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +459 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/runtime.d.ts +62 -0
- package/dist/tools/runtime.d.ts.map +1 -0
- package/dist/tools/runtime.js +116 -0
- package/dist/tools/runtime.js.map +1 -0
- package/dist/tools/shared/cwd-tracker.d.ts +32 -0
- package/dist/tools/shared/cwd-tracker.d.ts.map +1 -0
- package/dist/tools/shared/cwd-tracker.js +44 -0
- package/dist/tools/shared/cwd-tracker.js.map +1 -0
- package/dist/tools/shared/edit-history.d.ts +55 -0
- package/dist/tools/shared/edit-history.d.ts.map +1 -0
- package/dist/tools/shared/edit-history.js +72 -0
- package/dist/tools/shared/edit-history.js.map +1 -0
- package/dist/tools/shared/edit-matcher.d.ts +83 -0
- package/dist/tools/shared/edit-matcher.d.ts.map +1 -0
- package/dist/tools/shared/edit-matcher.js +359 -0
- package/dist/tools/shared/edit-matcher.js.map +1 -0
- package/dist/tools/shared/file-mutation-lock.d.ts +22 -0
- package/dist/tools/shared/file-mutation-lock.d.ts.map +1 -0
- package/dist/tools/shared/file-mutation-lock.js +35 -0
- package/dist/tools/shared/file-mutation-lock.js.map +1 -0
- package/dist/tools/shared/gitignore.d.ts +17 -0
- package/dist/tools/shared/gitignore.d.ts.map +1 -0
- package/dist/tools/shared/gitignore.js +59 -0
- package/dist/tools/shared/gitignore.js.map +1 -0
- package/dist/tools/shared/pdf-extractor.d.ts +96 -0
- package/dist/tools/shared/pdf-extractor.d.ts.map +1 -0
- package/dist/tools/shared/pdf-extractor.js +196 -0
- package/dist/tools/shared/pdf-extractor.js.map +1 -0
- package/dist/tools/shared/read-registry.d.ts +66 -0
- package/dist/tools/shared/read-registry.d.ts.map +1 -0
- package/dist/tools/shared/read-registry.js +65 -0
- package/dist/tools/shared/read-registry.js.map +1 -0
- package/dist/tools/shared/safe-env.d.ts +18 -0
- package/dist/tools/shared/safe-env.d.ts.map +1 -0
- package/dist/tools/shared/safe-env.js +70 -0
- package/dist/tools/shared/safe-env.js.map +1 -0
- package/dist/tools/sub-agent.d.ts +91 -0
- package/dist/tools/sub-agent.d.ts.map +1 -0
- package/dist/tools/sub-agent.js +89 -0
- package/dist/tools/sub-agent.js.map +1 -0
- package/dist/tools/task-output.d.ts +38 -0
- package/dist/tools/task-output.d.ts.map +1 -0
- package/dist/tools/task-output.js +186 -0
- package/dist/tools/task-output.js.map +1 -0
- package/dist/tools/tool-search/index.d.ts +40 -0
- package/dist/tools/tool-search/index.d.ts.map +1 -0
- package/dist/tools/tool-search/index.js +110 -0
- package/dist/tools/tool-search/index.js.map +1 -0
- package/dist/tools/tool-search/registry.d.ts +82 -0
- package/dist/tools/tool-search/registry.d.ts.map +1 -0
- package/dist/tools/tool-search/registry.js +238 -0
- package/dist/tools/tool-search/registry.js.map +1 -0
- package/dist/tools/undo-edit.d.ts +51 -0
- package/dist/tools/undo-edit.d.ts.map +1 -0
- package/dist/tools/undo-edit.js +231 -0
- package/dist/tools/undo-edit.js.map +1 -0
- package/dist/tools/web-fetch/cache.d.ts +49 -0
- package/dist/tools/web-fetch/cache.d.ts.map +1 -0
- package/dist/tools/web-fetch/cache.js +89 -0
- package/dist/tools/web-fetch/cache.js.map +1 -0
- package/dist/tools/web-fetch/index.d.ts +53 -0
- package/dist/tools/web-fetch/index.d.ts.map +1 -0
- package/dist/tools/web-fetch/index.js +513 -0
- package/dist/tools/web-fetch/index.js.map +1 -0
- package/dist/tools/write.d.ts +59 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +316 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/types.d.ts +881 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/working-tags.d.ts +44 -0
- package/dist/working-tags.d.ts.map +1 -0
- package/dist/working-tags.js +103 -0
- package/dist/working-tags.js.map +1 -0
- package/package.json +87 -0
- package/src/budget-guard.ts +170 -0
- package/src/compaction/compaction.ts +386 -0
- package/src/compaction/failsafe.ts +185 -0
- package/src/compaction/index.ts +1199 -0
- package/src/compaction/microcompaction.ts +709 -0
- package/src/compaction/observational/buffering.ts +430 -0
- package/src/compaction/observational/constants.ts +532 -0
- package/src/compaction/observational/index.ts +837 -0
- package/src/compaction/observational/observer.ts +510 -0
- package/src/compaction/observational/recall-tool.ts +130 -0
- package/src/compaction/observational/reflector.ts +221 -0
- package/src/compaction/observational/types.ts +343 -0
- package/src/context-manager.ts +237 -0
- package/src/cortex-agent.ts +4297 -0
- package/src/error-classifier.ts +199 -0
- package/src/event-bridge.ts +508 -0
- package/src/index.ts +292 -0
- package/src/mcp-client.ts +582 -0
- package/src/model-wrapper.ts +128 -0
- package/src/noop-logger.ts +9 -0
- package/src/prompt-diagnostics.ts +296 -0
- package/src/provider-manager.ts +823 -0
- package/src/provider-registry.ts +386 -0
- package/src/schema-converter.ts +51 -0
- package/src/skill-preprocessor.ts +314 -0
- package/src/skill-registry.ts +378 -0
- package/src/skill-tool.ts +130 -0
- package/src/sub-agent-manager.ts +236 -0
- package/src/token-estimator.ts +26 -0
- package/src/tool-contract.ts +113 -0
- package/src/tool-result-persistence.ts +197 -0
- package/src/tools/bash/index.ts +633 -0
- package/src/tools/bash/interactive.ts +302 -0
- package/src/tools/bash/safety.ts +1297 -0
- package/src/tools/edit.ts +422 -0
- package/src/tools/glob.ts +330 -0
- package/src/tools/grep.ts +819 -0
- package/src/tools/index.ts +110 -0
- package/src/tools/read.ts +580 -0
- package/src/tools/runtime.ts +173 -0
- package/src/tools/shared/cwd-tracker.ts +50 -0
- package/src/tools/shared/edit-history.ts +96 -0
- package/src/tools/shared/edit-matcher.ts +457 -0
- package/src/tools/shared/file-mutation-lock.ts +40 -0
- package/src/tools/shared/gitignore.ts +61 -0
- package/src/tools/shared/pdf-extractor.ts +290 -0
- package/src/tools/shared/read-registry.ts +93 -0
- package/src/tools/shared/safe-env.ts +82 -0
- package/src/tools/sub-agent.ts +171 -0
- package/src/tools/task-output.ts +236 -0
- package/src/tools/tool-search/index.ts +167 -0
- package/src/tools/tool-search/registry.ts +278 -0
- package/src/tools/undo-edit.ts +314 -0
- package/src/tools/web-fetch/cache.ts +112 -0
- package/src/tools/web-fetch/index.ts +604 -0
- package/src/tools/write.ts +385 -0
- package/src/types.ts +1057 -0
- package/src/working-tags.ts +118 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 2: Conversation Summarization.
|
|
3
|
+
*
|
|
4
|
+
* Replaces older conversation history with an LLM-generated summary
|
|
5
|
+
* while preserving a tail of recent turns. Uses the primary model
|
|
6
|
+
* for summarization quality (the conversation history is structurally
|
|
7
|
+
* complex with interleaved tool calls and multi-turn reasoning).
|
|
8
|
+
*
|
|
9
|
+
* Fires at 70% of context window (configurable). Emits lifecycle
|
|
10
|
+
* events (onBeforeCompaction, onPostCompaction) for consumer
|
|
11
|
+
* coordination (e.g., observational memory flush).
|
|
12
|
+
*
|
|
13
|
+
* References:
|
|
14
|
+
* - compaction-strategy.md (Layer 2: Conversation Summarization)
|
|
15
|
+
* - phase-5-compaction.md (5.3)
|
|
16
|
+
*/
|
|
17
|
+
import { estimateTokens } from '../token-estimator.js';
|
|
18
|
+
import { extractTextContent, isToolUseMessage, isToolResultMessage } from './microcompaction.js';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Defaults
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
export const COMPACTION_DEFAULTS = {
|
|
23
|
+
threshold: 0.70,
|
|
24
|
+
preserveRecentTurns: 6,
|
|
25
|
+
};
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Summarization prompt
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
const DEFAULT_SUMMARIZATION_PROMPT = `Your task is to create a detailed summary of the conversation so far. This summary will replace the conversation history, so it must capture everything needed to continue work without losing context. A small tail of the most recent turns is preserved separately and does not need to be repeated.
|
|
30
|
+
|
|
31
|
+
Before writing your summary, analyze the conversation inside <analysis> tags. Walk through the conversation chronologically and note:
|
|
32
|
+
- Each user request and how it was addressed
|
|
33
|
+
- Key decisions and their rationale
|
|
34
|
+
- Tool calls made, what they returned, and any errors
|
|
35
|
+
- User feedback or corrections (especially when you were told to do something differently)
|
|
36
|
+
- What was being worked on most recently
|
|
37
|
+
|
|
38
|
+
The <analysis> block is a private scratchpad. Keep it concise (a line or two per point). Save all detail for the <summary> block.
|
|
39
|
+
|
|
40
|
+
Then write your summary inside <summary> tags with the following sections:
|
|
41
|
+
|
|
42
|
+
1. Primary Request and Intent
|
|
43
|
+
Capture all user requests and intents in detail. Preserve the user's exact words for directives, preferences, and constraints.
|
|
44
|
+
|
|
45
|
+
2. Key Technical Concepts
|
|
46
|
+
List all important technical concepts, technologies, and frameworks discussed.
|
|
47
|
+
|
|
48
|
+
3. Files and Code Sections
|
|
49
|
+
Enumerate specific files and code sections examined, modified, or created. Include file paths and relevant code snippets. For each file, summarize why it was read or edited and what changed.
|
|
50
|
+
|
|
51
|
+
4. Tool Call Outcomes
|
|
52
|
+
What tools were called, what they found, and what failed. Include specific file paths, function names, URLs, error messages, and return values. Pay special attention to tool results that informed later decisions.
|
|
53
|
+
|
|
54
|
+
5. Errors and Fixes
|
|
55
|
+
List all errors encountered and how they were resolved. Include specific user feedback received, especially corrections or redirections.
|
|
56
|
+
|
|
57
|
+
6. All User Messages
|
|
58
|
+
List ALL user messages that are not tool results. These are critical for understanding the user's feedback and changing intent. Preserve the user's exact words.
|
|
59
|
+
|
|
60
|
+
7. Problem Solving
|
|
61
|
+
Document problems solved and any ongoing troubleshooting efforts.
|
|
62
|
+
|
|
63
|
+
8. Pending Tasks
|
|
64
|
+
Outline any pending tasks that have been explicitly requested but not yet completed.
|
|
65
|
+
|
|
66
|
+
9. Current Work
|
|
67
|
+
Describe precisely what was being worked on immediately before this summary. Include file names, code snippets, and the specific state of the work. This section is the most important for seamless continuation.
|
|
68
|
+
|
|
69
|
+
10. Key Decisions (Cumulative)
|
|
70
|
+
If a previous compaction summary exists in the conversation, carry forward its Key Decisions section and append any new decisions from this cycle. This section grows across compactions to prevent progressive loss of important decisions.
|
|
71
|
+
|
|
72
|
+
11. Optional Next Step
|
|
73
|
+
List the next step related to the most recent work, but ONLY if it is directly in line with the user's most recent explicit request. If the last task was concluded, do not suggest tangential work. Include direct quotes from the conversation showing exactly what task was in progress.
|
|
74
|
+
|
|
75
|
+
When preserving details, extract and retain exact values rather than paraphrasing:
|
|
76
|
+
- File paths, directory names, and line numbers
|
|
77
|
+
- URLs, API endpoints, and query parameters
|
|
78
|
+
- Function names, class names, variable names
|
|
79
|
+
- IDs, hashes, version numbers, and configuration values
|
|
80
|
+
- Error messages and status codes
|
|
81
|
+
- Specific quantities, dates, and thresholds
|
|
82
|
+
|
|
83
|
+
Be thorough. Err on the side of including information that would prevent duplicate work or repeated mistakes.`;
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Summary extraction
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Extract the <summary> content from the LLM's compaction output.
|
|
89
|
+
* The prompt asks for <analysis> (scratchpad) then <summary> (the actual summary).
|
|
90
|
+
* We strip the analysis and keep only the summary content.
|
|
91
|
+
* If no <summary> tags are found, return the full output (the model may
|
|
92
|
+
* have skipped the tags but still produced useful content).
|
|
93
|
+
*/
|
|
94
|
+
export function extractSummaryContent(raw) {
|
|
95
|
+
const match = raw.match(/<summary>([\s\S]*?)<\/summary>/);
|
|
96
|
+
if (match?.[1]) {
|
|
97
|
+
return match[1].trim();
|
|
98
|
+
}
|
|
99
|
+
// Fallback: strip <analysis> block if present, return the rest
|
|
100
|
+
const stripped = raw.replace(/<analysis>[\s\S]*?<\/analysis>/g, '').trim();
|
|
101
|
+
return stripped || raw.trim();
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Summarization
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
/**
|
|
107
|
+
* Partition conversation history into compaction target and preserved tail.
|
|
108
|
+
*
|
|
109
|
+
* @param history - The full conversation history (post-slot region)
|
|
110
|
+
* @param preserveRecentTurns - Number of recent turns to preserve
|
|
111
|
+
* @returns [target, preserved] where target is summarized and preserved is kept verbatim
|
|
112
|
+
*/
|
|
113
|
+
export function partitionHistory(history, preserveRecentTurns) {
|
|
114
|
+
if (history.length <= preserveRecentTurns) {
|
|
115
|
+
return [[], history];
|
|
116
|
+
}
|
|
117
|
+
let splitPoint = history.length - preserveRecentTurns;
|
|
118
|
+
// Never split between a tool_use (assistant) and its tool_result (user).
|
|
119
|
+
// If the split lands on a tool_result whose preceding message is a tool_use,
|
|
120
|
+
// move the split back one so the entire pair goes into the preserved tail.
|
|
121
|
+
if (splitPoint > 0 &&
|
|
122
|
+
splitPoint < history.length &&
|
|
123
|
+
isToolResultMessage(history[splitPoint]) &&
|
|
124
|
+
isToolUseMessage(history[splitPoint - 1])) {
|
|
125
|
+
splitPoint -= 1;
|
|
126
|
+
}
|
|
127
|
+
// Guard: don't create an empty target from the adjustment
|
|
128
|
+
if (splitPoint <= 0) {
|
|
129
|
+
return [[], history];
|
|
130
|
+
}
|
|
131
|
+
return [history.slice(0, splitPoint), history.slice(splitPoint)];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build the compaction summary message wrapping it in XML tags.
|
|
135
|
+
*
|
|
136
|
+
* @param summary - The LLM-generated summary text
|
|
137
|
+
* @param turnsCompacted - Number of turns that were summarized
|
|
138
|
+
* @returns A user-role message containing the tagged summary
|
|
139
|
+
*/
|
|
140
|
+
export function buildSummaryMessage(summary, turnsCompacted) {
|
|
141
|
+
const timestamp = new Date().toISOString();
|
|
142
|
+
const content = `<compaction-summary generated="${timestamp}" turns-summarized="${turnsCompacted}">\n${summary}\n</compaction-summary>`;
|
|
143
|
+
return { role: 'user', content, timestamp: Date.now() };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Format conversation turns for the summarization prompt.
|
|
147
|
+
* Extracts text content and labels each turn with role.
|
|
148
|
+
*/
|
|
149
|
+
export function formatTurnsForSummarization(turns) {
|
|
150
|
+
// No per-turn truncation. The compaction target is already bounded by
|
|
151
|
+
// partitionHistory (everything minus the preserved tail), and the
|
|
152
|
+
// summarizer needs access to full turn content for high-quality
|
|
153
|
+
// compression. See compaction-strategy.md Layer 2.
|
|
154
|
+
return turns
|
|
155
|
+
.map((msg, i) => {
|
|
156
|
+
const text = extractTextContent(msg);
|
|
157
|
+
return `[Turn ${i + 1}] ${msg.role}:\n${text}`;
|
|
158
|
+
})
|
|
159
|
+
.join('\n\n---\n\n');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Run Layer 2 conversation summarization.
|
|
163
|
+
*
|
|
164
|
+
* Steps:
|
|
165
|
+
* 1. Partition history into target and preserved tail
|
|
166
|
+
* 2. Emit onBeforeCompaction (awaited)
|
|
167
|
+
* 3. Generate summary via LLM
|
|
168
|
+
* 4. Build new history: [summary message] + [preserved tail]
|
|
169
|
+
* 5. Emit onPostCompaction
|
|
170
|
+
*
|
|
171
|
+
* @param history - Current conversation history (post-slot region)
|
|
172
|
+
* @param config - Compaction configuration
|
|
173
|
+
* @param complete - LLM completion function
|
|
174
|
+
* @param handlers - Consumer lifecycle handlers
|
|
175
|
+
* @returns The new conversation history and compaction result
|
|
176
|
+
*/
|
|
177
|
+
export async function runCompaction(history, config, complete, handlers = {},
|
|
178
|
+
/** Actual full-context token count (includes system prompt, slots, tools). When provided, used as tokensBefore instead of text-only heuristic. */
|
|
179
|
+
actualContextTokens) {
|
|
180
|
+
const [target, preserved] = partitionHistory(history, config.preserveRecentTurns);
|
|
181
|
+
if (target.length === 0) {
|
|
182
|
+
// Nothing to compact; not enough history
|
|
183
|
+
throw new Error('Not enough conversation history to compact');
|
|
184
|
+
}
|
|
185
|
+
// Compute text-only heuristic for history content.
|
|
186
|
+
const historyTextTokens = estimateTokens(history.map(m => extractTextContent(m)).join('\n'));
|
|
187
|
+
// Use actual full-context token count when provided (includes system prompt,
|
|
188
|
+
// slots, tool definitions); fall back to text-only heuristic for backward compat.
|
|
189
|
+
const tokensBefore = actualContextTokens ?? historyTextTokens;
|
|
190
|
+
// Overhead = system prompt + slots + tool definitions (everything except history text).
|
|
191
|
+
// Used to compute tokensAfter on the same basis as tokensBefore.
|
|
192
|
+
const overhead = actualContextTokens ? Math.max(0, actualContextTokens - historyTextTokens) : 0;
|
|
193
|
+
// Build compaction target info for the event
|
|
194
|
+
const targetInfo = {
|
|
195
|
+
turnsToCompact: target.length,
|
|
196
|
+
estimatedTokens: estimateTokens(target.map(m => extractTextContent(m)).join('\n')),
|
|
197
|
+
};
|
|
198
|
+
// Emit onBeforeCompaction (awaited)
|
|
199
|
+
if (handlers.onBeforeCompaction) {
|
|
200
|
+
for (const handler of handlers.onBeforeCompaction) {
|
|
201
|
+
await handler(targetInfo);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Generate summary via LLM
|
|
205
|
+
const prompt = config.customPrompt ?? DEFAULT_SUMMARIZATION_PROMPT;
|
|
206
|
+
const turnsText = formatTurnsForSummarization(target);
|
|
207
|
+
let summary;
|
|
208
|
+
try {
|
|
209
|
+
summary = await complete({
|
|
210
|
+
systemPrompt: prompt,
|
|
211
|
+
messages: [
|
|
212
|
+
{
|
|
213
|
+
role: 'user',
|
|
214
|
+
content: `Here are the conversation turns to summarize:\n\n${turnsText}`,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
221
|
+
// Emit compaction error
|
|
222
|
+
if (handlers.onCompactionError) {
|
|
223
|
+
for (const handler of handlers.onCompactionError) {
|
|
224
|
+
try {
|
|
225
|
+
handler(error);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Swallow handler errors
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
// Extract summary content from <summary> tags, stripping <analysis>
|
|
235
|
+
const parsedSummary = extractSummaryContent(summary);
|
|
236
|
+
// Build new history
|
|
237
|
+
const summaryMessage = buildSummaryMessage(parsedSummary, target.length);
|
|
238
|
+
const newHistory = [summaryMessage, ...preserved];
|
|
239
|
+
// Calculate result metrics. Include the same overhead (system prompt, slots,
|
|
240
|
+
// tool definitions) so tokensBefore and tokensAfter are on the same basis.
|
|
241
|
+
const newHistoryTextTokens = estimateTokens(newHistory.map(m => extractTextContent(m)).join('\n'));
|
|
242
|
+
const tokensAfter = overhead + newHistoryTextTokens;
|
|
243
|
+
const summaryTokens = estimateTokens(parsedSummary);
|
|
244
|
+
// The oldest preserved turn's index in the original history.
|
|
245
|
+
// target.length is the split point: all turns before it were compacted.
|
|
246
|
+
const oldestPreservedIndex = target.length;
|
|
247
|
+
// Attempt to find a timestamp in the preserved messages; null if not found.
|
|
248
|
+
const oldestPreservedTimestamp = findOldestTimestamp(preserved);
|
|
249
|
+
const result = {
|
|
250
|
+
tokensBefore,
|
|
251
|
+
tokensAfter,
|
|
252
|
+
turnsCompacted: target.length,
|
|
253
|
+
turnsPreserved: preserved.length,
|
|
254
|
+
summaryTokens,
|
|
255
|
+
oldestPreservedTimestamp,
|
|
256
|
+
oldestPreservedIndex,
|
|
257
|
+
summary: parsedSummary,
|
|
258
|
+
};
|
|
259
|
+
// Emit onPostCompaction
|
|
260
|
+
if (handlers.onPostCompaction) {
|
|
261
|
+
for (const handler of handlers.onPostCompaction) {
|
|
262
|
+
try {
|
|
263
|
+
handler(result);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// Swallow handler errors
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { newHistory, result };
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Attempt to find the oldest timestamp in a set of messages.
|
|
274
|
+
*
|
|
275
|
+
* Scans message content for ISO date patterns. Returns the first match
|
|
276
|
+
* or null if none found. This is a best-effort heuristic; the consumer
|
|
277
|
+
* should prefer `oldestPreservedIndex` from CompactionResult for
|
|
278
|
+
* reliable timestamp resolution via their own database.
|
|
279
|
+
*/
|
|
280
|
+
function findOldestTimestamp(messages) {
|
|
281
|
+
const isoPattern = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
282
|
+
for (const msg of messages) {
|
|
283
|
+
const text = extractTextContent(msg);
|
|
284
|
+
const match = isoPattern.exec(text);
|
|
285
|
+
if (match) {
|
|
286
|
+
return match[0];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// No ISO timestamp found in preserved messages. Return null rather
|
|
290
|
+
// than Date.now() so the consumer knows no timestamp was found and
|
|
291
|
+
// can fall back to oldestPreservedIndex for database-level resolution.
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Check if compaction should trigger based on token count and threshold.
|
|
296
|
+
*/
|
|
297
|
+
export function shouldCompact(currentTokens, contextWindow, threshold) {
|
|
298
|
+
if (contextWindow <= 0)
|
|
299
|
+
return false;
|
|
300
|
+
return (currentTokens / contextWindow) >= threshold;
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=compaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/compaction/compaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 3: Emergency Truncation (Failsafe).
|
|
3
|
+
*
|
|
4
|
+
* Last-resort truncation when Layer 2 fails or context is still too large.
|
|
5
|
+
* Drops the oldest conversation turns purely mechanically (no LLM call).
|
|
6
|
+
* Preserves structural integrity: tool_use/tool_result pairs are dropped together.
|
|
7
|
+
*
|
|
8
|
+
* Triggers at 90% of context window (configurable), or reactively when
|
|
9
|
+
* the API returns a context overflow error.
|
|
10
|
+
*
|
|
11
|
+
* This layer also serves as a mid-loop safety valve: it fires inside
|
|
12
|
+
* transformContext during the agentic loop when estimated token count
|
|
13
|
+
* exceeds 90%. Mid-loop truncation does NOT emit onBeforeCompaction
|
|
14
|
+
* (no observational memory processing mid-loop).
|
|
15
|
+
*
|
|
16
|
+
* References:
|
|
17
|
+
* - compaction-strategy.md (Layer 3: Emergency Truncation)
|
|
18
|
+
* - phase-5-compaction.md (5.4)
|
|
19
|
+
*/
|
|
20
|
+
import type { AgentMessage } from '../context-manager.js';
|
|
21
|
+
import type { FailsafeConfig } from '../types.js';
|
|
22
|
+
export declare const FAILSAFE_DEFAULTS: FailsafeConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Result of an emergency truncation operation.
|
|
25
|
+
*/
|
|
26
|
+
export interface FailsafeTruncationResult {
|
|
27
|
+
/** The truncated conversation history. */
|
|
28
|
+
newHistory: AgentMessage[];
|
|
29
|
+
/** Number of turns removed. */
|
|
30
|
+
turnsRemoved: number;
|
|
31
|
+
/** Estimated tokens after truncation. */
|
|
32
|
+
tokensAfter: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Perform emergency truncation on conversation history.
|
|
36
|
+
*
|
|
37
|
+
* Drops the oldest turns (preserving structural pairs) until the
|
|
38
|
+
* estimated token count drops below the threshold, or until only
|
|
39
|
+
* the preserved tail remains.
|
|
40
|
+
*
|
|
41
|
+
* @param history - Conversation history (post-slot region)
|
|
42
|
+
* @param contextWindow - Total context window size in tokens
|
|
43
|
+
* @param slotTokens - Estimated tokens used by slots
|
|
44
|
+
* @param threshold - Usage ratio threshold (default 0.90)
|
|
45
|
+
* @returns Truncation result with new history and metrics
|
|
46
|
+
*/
|
|
47
|
+
export declare function emergencyTruncate(history: AgentMessage[], contextWindow: number, slotTokens: number, threshold?: number): FailsafeTruncationResult;
|
|
48
|
+
/**
|
|
49
|
+
* Check if emergency truncation should fire based on token count.
|
|
50
|
+
*/
|
|
51
|
+
export declare function shouldTruncate(currentTokens: number, contextWindow: number, threshold?: number): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Check if an error represents a context overflow.
|
|
54
|
+
* Matches common API error patterns from various providers.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isContextOverflow(error: Error): boolean;
|
|
57
|
+
//# sourceMappingURL=failsafe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failsafe.d.ts","sourceRoot":"","sources":["../../src/compaction/failsafe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQlD,eAAO,MAAM,iBAAiB,EAAE,cAE/B,CAAC;AAYF;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,0CAA0C;IAC1C,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;CACrB;AA8BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EAAE,EACvB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAAoC,GAC9C,wBAAwB,CAqD1B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,MAAoC,GAC9C,OAAO,CAGT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAYvD"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 3: Emergency Truncation (Failsafe).
|
|
3
|
+
*
|
|
4
|
+
* Last-resort truncation when Layer 2 fails or context is still too large.
|
|
5
|
+
* Drops the oldest conversation turns purely mechanically (no LLM call).
|
|
6
|
+
* Preserves structural integrity: tool_use/tool_result pairs are dropped together.
|
|
7
|
+
*
|
|
8
|
+
* Triggers at 90% of context window (configurable), or reactively when
|
|
9
|
+
* the API returns a context overflow error.
|
|
10
|
+
*
|
|
11
|
+
* This layer also serves as a mid-loop safety valve: it fires inside
|
|
12
|
+
* transformContext during the agentic loop when estimated token count
|
|
13
|
+
* exceeds 90%. Mid-loop truncation does NOT emit onBeforeCompaction
|
|
14
|
+
* (no observational memory processing mid-loop).
|
|
15
|
+
*
|
|
16
|
+
* References:
|
|
17
|
+
* - compaction-strategy.md (Layer 3: Emergency Truncation)
|
|
18
|
+
* - phase-5-compaction.md (5.4)
|
|
19
|
+
*/
|
|
20
|
+
import { estimateTokens } from '../token-estimator.js';
|
|
21
|
+
import { isToolResultMessage, isToolUseMessage, extractTextContent } from './microcompaction.js';
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Defaults
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
export const FAILSAFE_DEFAULTS = {
|
|
26
|
+
threshold: 0.90,
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Minimum number of recent turns to preserve during emergency truncation.
|
|
30
|
+
* Fewer turns preserved than Layer 2 since this is a last resort.
|
|
31
|
+
*/
|
|
32
|
+
const FAILSAFE_PRESERVE_TURNS = 3;
|
|
33
|
+
/**
|
|
34
|
+
* Find structural pairs in conversation history.
|
|
35
|
+
* A tool_use message and its corresponding tool_result form a pair.
|
|
36
|
+
* When dropping one, we must drop both.
|
|
37
|
+
*
|
|
38
|
+
* Returns indices that should be dropped together for each index.
|
|
39
|
+
* If a message at index i is part of a pair, pairMap[i] contains
|
|
40
|
+
* all indices in that pair.
|
|
41
|
+
*/
|
|
42
|
+
function findStructuralPairs(history) {
|
|
43
|
+
const pairMap = new Map();
|
|
44
|
+
for (let i = 0; i < history.length; i++) {
|
|
45
|
+
const msg = history[i];
|
|
46
|
+
if (isToolUseMessage(msg)) {
|
|
47
|
+
// Look for the corresponding tool_result in the next message
|
|
48
|
+
if (i + 1 < history.length && isToolResultMessage(history[i + 1])) {
|
|
49
|
+
const pair = [i, i + 1];
|
|
50
|
+
pairMap.set(i, pair);
|
|
51
|
+
pairMap.set(i + 1, pair);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return pairMap;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Perform emergency truncation on conversation history.
|
|
59
|
+
*
|
|
60
|
+
* Drops the oldest turns (preserving structural pairs) until the
|
|
61
|
+
* estimated token count drops below the threshold, or until only
|
|
62
|
+
* the preserved tail remains.
|
|
63
|
+
*
|
|
64
|
+
* @param history - Conversation history (post-slot region)
|
|
65
|
+
* @param contextWindow - Total context window size in tokens
|
|
66
|
+
* @param slotTokens - Estimated tokens used by slots
|
|
67
|
+
* @param threshold - Usage ratio threshold (default 0.90)
|
|
68
|
+
* @returns Truncation result with new history and metrics
|
|
69
|
+
*/
|
|
70
|
+
export function emergencyTruncate(history, contextWindow, slotTokens, threshold = FAILSAFE_DEFAULTS.threshold) {
|
|
71
|
+
if (history.length === 0) {
|
|
72
|
+
return { newHistory: [], turnsRemoved: 0, tokensAfter: slotTokens };
|
|
73
|
+
}
|
|
74
|
+
const targetTokens = contextWindow * threshold;
|
|
75
|
+
const pairMap = findStructuralPairs(history);
|
|
76
|
+
const dropped = new Set();
|
|
77
|
+
// Calculate initial token estimate
|
|
78
|
+
let currentTokens = slotTokens + estimateTokens(history.map(m => extractTextContent(m)).join('\n'));
|
|
79
|
+
// Drop from the front, but respect the preserved tail
|
|
80
|
+
const preserveFrom = Math.max(0, history.length - FAILSAFE_PRESERVE_TURNS);
|
|
81
|
+
let i = 0;
|
|
82
|
+
while (currentTokens > targetTokens && i < preserveFrom) {
|
|
83
|
+
if (dropped.has(i)) {
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
// Get all indices that must be dropped together
|
|
88
|
+
const pair = pairMap.get(i);
|
|
89
|
+
const indicesToDrop = pair ?? [i];
|
|
90
|
+
// Check that none of the pair indices are in the preserved tail
|
|
91
|
+
const canDrop = indicesToDrop.every(idx => idx < preserveFrom);
|
|
92
|
+
if (!canDrop) {
|
|
93
|
+
i++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// Drop the turn(s)
|
|
97
|
+
for (const idx of indicesToDrop) {
|
|
98
|
+
const msgTokens = estimateTokens(extractTextContent(history[idx]));
|
|
99
|
+
currentTokens -= msgTokens;
|
|
100
|
+
dropped.add(idx);
|
|
101
|
+
}
|
|
102
|
+
i++;
|
|
103
|
+
}
|
|
104
|
+
// Build new history excluding dropped messages
|
|
105
|
+
const newHistory = history.filter((_, idx) => !dropped.has(idx));
|
|
106
|
+
return {
|
|
107
|
+
newHistory,
|
|
108
|
+
turnsRemoved: dropped.size,
|
|
109
|
+
tokensAfter: currentTokens,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if emergency truncation should fire based on token count.
|
|
114
|
+
*/
|
|
115
|
+
export function shouldTruncate(currentTokens, contextWindow, threshold = FAILSAFE_DEFAULTS.threshold) {
|
|
116
|
+
if (contextWindow <= 0)
|
|
117
|
+
return false;
|
|
118
|
+
return (currentTokens / contextWindow) >= threshold;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if an error represents a context overflow.
|
|
122
|
+
* Matches common API error patterns from various providers.
|
|
123
|
+
*/
|
|
124
|
+
export function isContextOverflow(error) {
|
|
125
|
+
const msg = error.message.toLowerCase();
|
|
126
|
+
return (msg.includes('context_length_exceeded') ||
|
|
127
|
+
msg.includes('context window') ||
|
|
128
|
+
msg.includes('maximum context length') ||
|
|
129
|
+
msg.includes('token limit') ||
|
|
130
|
+
msg.includes('too many tokens') ||
|
|
131
|
+
msg.includes('request too large') ||
|
|
132
|
+
msg.includes('prompt is too long') ||
|
|
133
|
+
msg.includes('input too long'));
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=failsafe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failsafe.js","sourceRoot":"","sources":["../../src/compaction/failsafe.ts"],"names":[],"mappings":"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"}
|