@trenchwork/coder 1.4.0 → 1.5.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/dist/contracts/v1/agent.d.ts +2 -0
- package/dist/contracts/v1/agent.d.ts.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +77 -2
- package/dist/core/agent.js.map +1 -1
- package/dist/core/contextManager.d.ts +10 -137
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js +74 -540
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/errorClassification.d.ts.map +1 -1
- package/dist/core/errorClassification.js +8 -0
- package/dist/core/errorClassification.js.map +1 -1
- package/dist/core/hooks.d.ts.map +1 -1
- package/dist/core/hooks.js +42 -19
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/keyResolution.d.ts +30 -0
- package/dist/core/keyResolution.d.ts.map +1 -0
- package/dist/core/keyResolution.js +38 -0
- package/dist/core/keyResolution.js.map +1 -0
- package/dist/core/permissionMode.d.ts +17 -2
- package/dist/core/permissionMode.d.ts.map +1 -1
- package/dist/core/permissionMode.js +20 -7
- package/dist/core/permissionMode.js.map +1 -1
- package/dist/core/reasoningFallback.d.ts +22 -0
- package/dist/core/reasoningFallback.d.ts.map +1 -0
- package/dist/core/reasoningFallback.js +22 -0
- package/dist/core/reasoningFallback.js.map +1 -0
- package/dist/core/sessionStore.js +34 -8
- package/dist/core/sessionStore.js.map +1 -1
- package/dist/core/slashCommands.d.ts.map +1 -1
- package/dist/core/slashCommands.js +0 -3
- package/dist/core/slashCommands.js.map +1 -1
- package/dist/core/taskCompletionDetector.d.ts.map +1 -1
- package/dist/core/taskCompletionDetector.js +10 -3
- package/dist/core/taskCompletionDetector.js.map +1 -1
- package/dist/core/toolRuntime.d.ts +1 -0
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +21 -2
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/turnTokenMeter.d.ts +19 -0
- package/dist/core/turnTokenMeter.d.ts.map +1 -0
- package/dist/core/turnTokenMeter.js +36 -0
- package/dist/core/turnTokenMeter.js.map +1 -0
- package/dist/headless/interactiveShell.d.ts +3 -3
- package/dist/headless/interactiveShell.d.ts.map +1 -1
- package/dist/headless/interactiveShell.js +68 -115
- package/dist/headless/interactiveShell.js.map +1 -1
- package/dist/plugins/providers/deepseek/index.js +4 -6
- package/dist/plugins/providers/deepseek/index.js.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.js +33 -26
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +16 -7
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/runtime/agentSession.d.ts.map +1 -1
- package/dist/runtime/agentSession.js +10 -3
- package/dist/runtime/agentSession.js.map +1 -1
- package/dist/tools/bashTools.d.ts +63 -0
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +186 -77
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/grepTools.d.ts.map +1 -1
- package/dist/tools/grepTools.js +41 -23
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/searchTools.d.ts.map +1 -1
- package/dist/tools/searchTools.js +18 -8
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/webTools.d.ts.map +1 -1
- package/dist/tools/webTools.js +10 -2
- package/dist/tools/webTools.js.map +1 -1
- package/dist/ui/ink/App.d.ts +6 -1
- package/dist/ui/ink/App.d.ts.map +1 -1
- package/dist/ui/ink/App.js +20 -2
- package/dist/ui/ink/App.js.map +1 -1
- package/dist/ui/ink/ChatStatic.d.ts.map +1 -1
- package/dist/ui/ink/ChatStatic.js +9 -2
- package/dist/ui/ink/ChatStatic.js.map +1 -1
- package/dist/ui/ink/InkPromptController.d.ts +8 -8
- package/dist/ui/ink/InkPromptController.d.ts.map +1 -1
- package/dist/ui/ink/InkPromptController.js +19 -11
- package/dist/ui/ink/InkPromptController.js.map +1 -1
- package/dist/ui/ink/StatusLine.d.ts +6 -7
- package/dist/ui/ink/StatusLine.d.ts.map +1 -1
- package/dist/ui/ink/StatusLine.js +9 -9
- package/dist/ui/ink/StatusLine.js.map +1 -1
- package/dist/ui/ink/markdownRender.d.ts +2 -0
- package/dist/ui/ink/markdownRender.d.ts.map +1 -0
- package/dist/ui/ink/markdownRender.js +76 -0
- package/dist/ui/ink/markdownRender.js.map +1 -0
- package/package.json +2 -1
- package/dist/core/hostedAuth.d.ts +0 -88
- package/dist/core/hostedAuth.d.ts.map +0 -1
- package/dist/core/hostedAuth.js +0 -219
- package/dist/core/hostedAuth.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextManager.d.ts","sourceRoot":"","sources":["../../src/core/contextManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGtD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,QAAQ,EAAE,mBAAmB,EAAE,KAC5B,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"contextManager.d.ts","sourceRoot":"","sources":["../../src/core/contextManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,59 +29,6 @@ Format:
|
|
|
29
29
|
|
|
30
30
|
Conversation:
|
|
31
31
|
{conversation}`;
|
|
32
|
-
/**
|
|
33
|
-
* Pre-defined AI Flow Patterns for intelligent context management
|
|
34
|
-
*/
|
|
35
|
-
export const DEFAULT_AI_FLOW_PATTERNS = [
|
|
36
|
-
{
|
|
37
|
-
patternId: 'read_edit_workflow',
|
|
38
|
-
description: 'Standard file modification workflow',
|
|
39
|
-
toolSequence: ['read', 'edit'],
|
|
40
|
-
contextImpact: 1500,
|
|
41
|
-
compactionOpportunity: true,
|
|
42
|
-
preservationPriority: 8,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
patternId: 'analysis_phase',
|
|
46
|
-
description: 'Code analysis and exploration phase',
|
|
47
|
-
toolSequence: ['read', 'grep', 'glob', 'search'],
|
|
48
|
-
contextImpact: 3000,
|
|
49
|
-
compactionOpportunity: true,
|
|
50
|
-
preservationPriority: 6,
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
patternId: 'implementation_phase',
|
|
54
|
-
description: 'Active code implementation phase',
|
|
55
|
-
toolSequence: ['edit', 'write'],
|
|
56
|
-
contextImpact: 2000,
|
|
57
|
-
compactionOpportunity: false, // Preserve implementation context
|
|
58
|
-
preservationPriority: 9,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
patternId: 'validation_phase',
|
|
62
|
-
description: 'Code validation and testing phase',
|
|
63
|
-
toolSequence: ['run_tests', 'run_build', 'run_repo_checks'],
|
|
64
|
-
contextImpact: 1000,
|
|
65
|
-
compactionOpportunity: true,
|
|
66
|
-
preservationPriority: 5,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
patternId: 'parallel_execution',
|
|
70
|
-
description: 'Efficient parallel tool usage',
|
|
71
|
-
toolSequence: ['read', 'read', 'read'], // Multiple parallel reads
|
|
72
|
-
contextImpact: 2500,
|
|
73
|
-
compactionOpportunity: true,
|
|
74
|
-
preservationPriority: 7,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
patternId: 'git_workflow',
|
|
78
|
-
description: 'Git operations workflow',
|
|
79
|
-
toolSequence: ['git_smart_commit', 'git_sync', 'git_create_pr'],
|
|
80
|
-
contextImpact: 1200,
|
|
81
|
-
compactionOpportunity: true,
|
|
82
|
-
preservationPriority: 6,
|
|
83
|
-
},
|
|
84
|
-
];
|
|
85
32
|
export class ContextManager {
|
|
86
33
|
config;
|
|
87
34
|
sessionStartTime = Date.now();
|
|
@@ -215,28 +162,54 @@ export class ContextManager {
|
|
|
215
162
|
}
|
|
216
163
|
return Math.ceil(charCount / this.config.estimatedCharsPerToken);
|
|
217
164
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Detect context overflow risk from recent tool usage patterns
|
|
220
|
-
*/
|
|
221
|
-
detectContextOverflowRisk(toolCalls) {
|
|
222
|
-
const recentTools = toolCalls.slice(-10); // Last 10 tools
|
|
223
|
-
// Check for broad search patterns without limits
|
|
224
|
-
const broadSearches = recentTools.filter(tool => tool.includes('Glob') && !tool.includes('head_limit'));
|
|
225
|
-
// Check for multiple large file reads
|
|
226
|
-
const fileReads = recentTools.filter(tool => tool.includes('Read') || tool.includes('read_file'));
|
|
227
|
-
// Check for redundant context_snapshot calls
|
|
228
|
-
const contextSnapshots = recentTools.filter(tool => tool.includes('context_snapshot'));
|
|
229
|
-
// Risk threshold: 2+ broad searches OR 5+ file reads OR 1+ context_snapshot
|
|
230
|
-
return broadSearches.length >= 2 ||
|
|
231
|
-
fileReads.length >= 5 ||
|
|
232
|
-
contextSnapshots.length >= 1;
|
|
233
|
-
}
|
|
234
165
|
/**
|
|
235
166
|
* Estimate total tokens in conversation
|
|
236
167
|
*/
|
|
237
168
|
estimateTotalTokens(messages) {
|
|
238
169
|
return messages.reduce((sum, msg) => sum + this.estimateTokens(msg), 0);
|
|
239
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Intra-turn reduction for the tool-heavy single-turn case. Normal pruning
|
|
173
|
+
* keeps whole turns and counts only USER turns, so one request with many tool
|
|
174
|
+
* rounds (one user message, dozens of assistant+tool messages) prunes nothing
|
|
175
|
+
* and can overflow. This shrinks the OLDEST verbose tool-result bodies to a
|
|
176
|
+
* placeholder — never dropping a message (tool_call/result pairing stays
|
|
177
|
+
* intact), never touching the user task, assistant reasoning, or the most
|
|
178
|
+
* recent few messages. Returns the new list and how many bodies were truncated.
|
|
179
|
+
*/
|
|
180
|
+
reduceOversizedHistory(messages) {
|
|
181
|
+
const KEEP_INTACT = 6; // leave the most recent messages untouched
|
|
182
|
+
const MIN_TRUNCATABLE = 400; // only shrink substantial bodies (chars)
|
|
183
|
+
const PLACEHOLDER = '[older tool output truncated to fit the context budget]';
|
|
184
|
+
const out = messages.map((m) => ({ ...m }));
|
|
185
|
+
let total = this.estimateTotalTokens(out);
|
|
186
|
+
let truncated = 0;
|
|
187
|
+
// Collapse an oversized tool RESULT at index i (they hold the bulk — file
|
|
188
|
+
// reads, command output — and are safe to drop; user/assistant text stays).
|
|
189
|
+
const tryTruncate = (i) => {
|
|
190
|
+
const m = out[i];
|
|
191
|
+
if (!m || m.role !== 'tool' || !m.content)
|
|
192
|
+
return;
|
|
193
|
+
if (m.content.length < MIN_TRUNCATABLE || m.content === PLACEHOLDER)
|
|
194
|
+
return;
|
|
195
|
+
const before = this.estimateTokens(m);
|
|
196
|
+
m.content = PLACEHOLDER;
|
|
197
|
+
total -= before - this.estimateTokens(m);
|
|
198
|
+
truncated++;
|
|
199
|
+
};
|
|
200
|
+
// Pass 1: oldest-first, leaving the most recent KEEP_INTACT messages intact.
|
|
201
|
+
const editableUpTo = Math.max(0, out.length - KEEP_INTACT);
|
|
202
|
+
for (let i = 0; i < editableUpTo && total >= this.config.targetTokens; i++) {
|
|
203
|
+
tryTruncate(i);
|
|
204
|
+
}
|
|
205
|
+
// Pass 2 (last resort): still over budget because a giant tool output sits
|
|
206
|
+
// inside the protected tail (e.g. a single `[user, assistant, tool(HUGE)]`
|
|
207
|
+
// turn). Collapse those too, oldest-first — but never the very last message.
|
|
208
|
+
for (let i = editableUpTo; i < out.length - 1 && total >= this.config.targetTokens; i++) {
|
|
209
|
+
tryTruncate(i);
|
|
210
|
+
}
|
|
211
|
+
return { messages: out, truncated };
|
|
212
|
+
}
|
|
240
213
|
/**
|
|
241
214
|
* Prune old messages when approaching limit
|
|
242
215
|
*
|
|
@@ -274,6 +247,14 @@ export class ContextManager {
|
|
|
274
247
|
// Tool results belong with the current assistant turn
|
|
275
248
|
currentTurn.push(msg);
|
|
276
249
|
}
|
|
250
|
+
else if (msg.role === 'system') {
|
|
251
|
+
// Mid-conversation system messages — a PRIOR compaction summary, a
|
|
252
|
+
// recovery note — must not be silently dropped (the grouping used to
|
|
253
|
+
// have no `system` branch). Attach to the current turn so they travel
|
|
254
|
+
// with it: kept if recent, folded into the next summary if old. The
|
|
255
|
+
// leading base system prompt is handled separately above.
|
|
256
|
+
currentTurn.push(msg);
|
|
257
|
+
}
|
|
277
258
|
}
|
|
278
259
|
if (currentTurn.length > 0) {
|
|
279
260
|
turns.push(currentTurn);
|
|
@@ -344,6 +325,15 @@ export class ContextManager {
|
|
|
344
325
|
});
|
|
345
326
|
}
|
|
346
327
|
pruned.push(...recentMessages);
|
|
328
|
+
// Tool-heavy single-turn fallback: if turn-level pruning removed nothing
|
|
329
|
+
// (only one user turn, so the whole conversation is "recent") but we're
|
|
330
|
+
// still over budget, shrink the oldest verbose tool outputs in place.
|
|
331
|
+
if (removedCount === 0 && this.estimateTotalTokens(pruned) >= this.config.targetTokens) {
|
|
332
|
+
const reduced = this.reduceOversizedHistory(pruned);
|
|
333
|
+
if (reduced.truncated > 0) {
|
|
334
|
+
return { pruned: reduced.messages, removed: reduced.truncated };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
347
337
|
return {
|
|
348
338
|
pruned,
|
|
349
339
|
removed: removedCount,
|
|
@@ -389,6 +379,12 @@ export class ContextManager {
|
|
|
389
379
|
else if (msg.role === 'tool') {
|
|
390
380
|
currentTurn.push(msg);
|
|
391
381
|
}
|
|
382
|
+
else if (msg.role === 'system') {
|
|
383
|
+
// Carry mid-conversation system messages (a prior compaction summary)
|
|
384
|
+
// into a turn so they're folded into the next summary instead of being
|
|
385
|
+
// silently dropped by the grouping.
|
|
386
|
+
currentTurn.push(msg);
|
|
387
|
+
}
|
|
392
388
|
}
|
|
393
389
|
if (currentTurn.length > 0) {
|
|
394
390
|
turns.push(currentTurn);
|
|
@@ -449,6 +445,15 @@ export class ContextManager {
|
|
|
449
445
|
const toSummarize = summarizeTurns.flat();
|
|
450
446
|
// If nothing to summarize, return as-is
|
|
451
447
|
if (toSummarize.length === 0) {
|
|
448
|
+
// Nothing spans multiple turns to summarize (e.g. a single tool-heavy
|
|
449
|
+
// turn). If still over budget, shrink the oldest verbose tool outputs in
|
|
450
|
+
// place rather than no-op into an overflow.
|
|
451
|
+
if (this.estimateTotalTokens(messages) >= this.config.targetTokens) {
|
|
452
|
+
const reduced = this.reduceOversizedHistory(messages);
|
|
453
|
+
if (reduced.truncated > 0) {
|
|
454
|
+
return { pruned: reduced.messages, removed: reduced.truncated, summarized: false };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
452
457
|
return { pruned: messages, removed: 0, summarized: false };
|
|
453
458
|
}
|
|
454
459
|
try {
|
|
@@ -540,472 +545,6 @@ export class ContextManager {
|
|
|
540
545
|
updateConfig(config) {
|
|
541
546
|
this.config = { ...this.config, ...config };
|
|
542
547
|
}
|
|
543
|
-
// ============================================================================
|
|
544
|
-
// INTELLIGENT COMPACTION SYSTEM
|
|
545
|
-
// Automatically detects optimal points for conversation compaction
|
|
546
|
-
// ============================================================================
|
|
547
|
-
/**
|
|
548
|
-
* Default patterns that indicate task boundaries
|
|
549
|
-
*/
|
|
550
|
-
static DEFAULT_TASK_BOUNDARY_PATTERNS = [
|
|
551
|
-
// Completion indicators
|
|
552
|
-
/\b(done|completed|finished|fixed|resolved|implemented|added|created|updated)\b/i,
|
|
553
|
-
/\b(all\s+(?:tests?\s+)?pass(?:ing|ed)?)\b/i,
|
|
554
|
-
/\b(successfully|works?\s+(?:now|correctly))\b/i,
|
|
555
|
-
// Transition indicators
|
|
556
|
-
/\b(next|now\s+(?:let's|we\s+can)|moving\s+on)\b/i,
|
|
557
|
-
/\b(that's\s+(?:it|all|done))\b/i,
|
|
558
|
-
// Acknowledgment patterns
|
|
559
|
-
/^(?:great|perfect|thanks|thank\s+you|got\s+it|understood)\b/i,
|
|
560
|
-
];
|
|
561
|
-
/**
|
|
562
|
-
* Patterns indicating topic/task shifts
|
|
563
|
-
*/
|
|
564
|
-
static TOPIC_SHIFT_PATTERNS = [
|
|
565
|
-
/\b(different|another|new|separate|unrelated)\s+(?:task|thing|topic|issue|question)\b/i,
|
|
566
|
-
/\b(can\s+you|could\s+you|please|now|let's)\s+(?:also|help|do|make|create|fix|add)\b/i,
|
|
567
|
-
/\b(switching|changing|moving)\s+to\b/i,
|
|
568
|
-
/\b(forget|ignore|never\s*mind)\s+(?:that|the|about)\b/i,
|
|
569
|
-
/^(?:ok|okay|alright|anyway|so)\s*[,.]?\s*(?:can|could|now|let|please)/i,
|
|
570
|
-
];
|
|
571
|
-
/**
|
|
572
|
-
* Patterns indicating user pivots (abandoning current direction)
|
|
573
|
-
*/
|
|
574
|
-
static USER_PIVOT_PATTERNS = [
|
|
575
|
-
/\b(actually|wait|hold\s+on|stop|cancel|scratch\s+that)\b/i,
|
|
576
|
-
/\b(let's\s+(?:try|do)\s+(?:something|it)\s+(?:else|differently))\b/i,
|
|
577
|
-
/\b(go\s+back|revert|undo|start\s+over)\b/i,
|
|
578
|
-
/\b(wrong|not\s+(?:what|right)|that's\s+not)\b/i,
|
|
579
|
-
];
|
|
580
|
-
/**
|
|
581
|
-
* Analyze the conversation to detect intelligent compaction points
|
|
582
|
-
*/
|
|
583
|
-
analyzeCompactionPoints(messages) {
|
|
584
|
-
const signals = [];
|
|
585
|
-
const totalTokens = this.estimateTotalTokens(messages);
|
|
586
|
-
const tokenPercentage = totalTokens / this.config.maxTokens;
|
|
587
|
-
const compactionThreshold = this.config.compactionThreshold ?? 0.5;
|
|
588
|
-
const minConfidence = this.config.minSignalConfidence ?? 0.6;
|
|
589
|
-
// Don't analyze if below threshold
|
|
590
|
-
if (tokenPercentage < compactionThreshold) {
|
|
591
|
-
return {
|
|
592
|
-
shouldCompact: false,
|
|
593
|
-
signals: [],
|
|
594
|
-
recommendedCompactionPoint: null,
|
|
595
|
-
urgency: 'none',
|
|
596
|
-
preserveFromIndex: 0,
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
// Analyze each message for compaction signals
|
|
600
|
-
for (let i = 0; i < messages.length; i++) {
|
|
601
|
-
const msg = messages[i];
|
|
602
|
-
if (!msg)
|
|
603
|
-
continue;
|
|
604
|
-
// Detect task boundaries
|
|
605
|
-
const taskBoundary = this.detectTaskBoundary(msg, i, messages);
|
|
606
|
-
if (taskBoundary && taskBoundary.confidence >= minConfidence) {
|
|
607
|
-
signals.push(taskBoundary);
|
|
608
|
-
}
|
|
609
|
-
// Detect topic shifts
|
|
610
|
-
const topicShift = this.detectTopicShift(msg, i, messages);
|
|
611
|
-
if (topicShift && topicShift.confidence >= minConfidence) {
|
|
612
|
-
signals.push(topicShift);
|
|
613
|
-
}
|
|
614
|
-
// Detect user pivots
|
|
615
|
-
const userPivot = this.detectUserPivot(msg, i);
|
|
616
|
-
if (userPivot && userPivot.confidence >= minConfidence) {
|
|
617
|
-
signals.push(userPivot);
|
|
618
|
-
}
|
|
619
|
-
// Detect context saturation (tool output heavy regions)
|
|
620
|
-
const saturation = this.detectContextSaturation(msg, i, messages);
|
|
621
|
-
if (saturation && saturation.confidence >= minConfidence) {
|
|
622
|
-
signals.push(saturation);
|
|
623
|
-
}
|
|
624
|
-
// Detect milestones
|
|
625
|
-
const milestone = this.detectMilestone(msg, i, messages);
|
|
626
|
-
if (milestone && milestone.confidence >= minConfidence) {
|
|
627
|
-
signals.push(milestone);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
// Determine urgency based on token percentage
|
|
631
|
-
const urgency = this.calculateUrgency(tokenPercentage);
|
|
632
|
-
// Find the best compaction point
|
|
633
|
-
const recommendedPoint = this.findBestCompactionPoint(signals, messages, urgency);
|
|
634
|
-
// Calculate preserve index (everything after this should be kept)
|
|
635
|
-
const preserveFromIndex = recommendedPoint !== null
|
|
636
|
-
? this.findSafePreservePoint(recommendedPoint, messages)
|
|
637
|
-
: messages.length;
|
|
638
|
-
return {
|
|
639
|
-
shouldCompact: signals.length > 0 && urgency !== 'none',
|
|
640
|
-
signals,
|
|
641
|
-
recommendedCompactionPoint: recommendedPoint,
|
|
642
|
-
urgency,
|
|
643
|
-
preserveFromIndex,
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Detect task boundary signals
|
|
648
|
-
*/
|
|
649
|
-
detectTaskBoundary(msg, index, messages) {
|
|
650
|
-
if (msg.role !== 'user' && msg.role !== 'assistant')
|
|
651
|
-
return null;
|
|
652
|
-
const content = msg.content || '';
|
|
653
|
-
const patterns = this.config.taskBoundaryPatterns
|
|
654
|
-
? this.config.taskBoundaryPatterns.map(p => new RegExp(p, 'i'))
|
|
655
|
-
: ContextManager.DEFAULT_TASK_BOUNDARY_PATTERNS;
|
|
656
|
-
let matchCount = 0;
|
|
657
|
-
const reasons = [];
|
|
658
|
-
for (const pattern of patterns) {
|
|
659
|
-
if (pattern.test(content)) {
|
|
660
|
-
matchCount++;
|
|
661
|
-
reasons.push(pattern.source.slice(0, 30));
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
if (matchCount === 0)
|
|
665
|
-
return null;
|
|
666
|
-
// Higher confidence if followed by a new user message with different intent
|
|
667
|
-
let confidence = Math.min(0.4 + matchCount * 0.2, 0.9);
|
|
668
|
-
// Boost confidence if this looks like a conclusion
|
|
669
|
-
if (msg.role === 'assistant' && this.looksLikeConclusion(content)) {
|
|
670
|
-
confidence = Math.min(confidence + 0.2, 0.95);
|
|
671
|
-
}
|
|
672
|
-
// Boost if next user message starts a new topic
|
|
673
|
-
const nextUserMsg = messages.slice(index + 1).find(m => m.role === 'user');
|
|
674
|
-
if (nextUserMsg && this.isNewTopic(content, nextUserMsg.content || '')) {
|
|
675
|
-
confidence = Math.min(confidence + 0.15, 0.95);
|
|
676
|
-
}
|
|
677
|
-
return {
|
|
678
|
-
type: 'task_boundary',
|
|
679
|
-
confidence,
|
|
680
|
-
messageIndex: index,
|
|
681
|
-
reason: `Task completion detected: ${reasons.slice(0, 2).join(', ')}`,
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* Detect topic shift signals
|
|
686
|
-
*/
|
|
687
|
-
detectTopicShift(msg, index, messages) {
|
|
688
|
-
if (msg.role !== 'user')
|
|
689
|
-
return null;
|
|
690
|
-
const content = msg.content || '';
|
|
691
|
-
const sensitivity = this.config.topicShiftSensitivity ?? 0.7;
|
|
692
|
-
// Check explicit shift patterns
|
|
693
|
-
for (const pattern of ContextManager.TOPIC_SHIFT_PATTERNS) {
|
|
694
|
-
if (pattern.test(content)) {
|
|
695
|
-
return {
|
|
696
|
-
type: 'topic_shift',
|
|
697
|
-
confidence: 0.7 + sensitivity * 0.2,
|
|
698
|
-
messageIndex: index,
|
|
699
|
-
reason: 'Explicit topic shift language detected',
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
// Check semantic shift from previous context
|
|
704
|
-
const prevMessages = messages.slice(Math.max(0, index - 5), index);
|
|
705
|
-
const prevContent = prevMessages
|
|
706
|
-
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
707
|
-
.map(m => m.content || '')
|
|
708
|
-
.join(' ');
|
|
709
|
-
if (prevContent && this.isNewTopic(prevContent, content)) {
|
|
710
|
-
return {
|
|
711
|
-
type: 'topic_shift',
|
|
712
|
-
confidence: 0.6 + sensitivity * 0.2,
|
|
713
|
-
messageIndex: index,
|
|
714
|
-
reason: 'Semantic topic shift detected',
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
return null;
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* Detect user pivot signals (abandoning current direction)
|
|
721
|
-
*/
|
|
722
|
-
detectUserPivot(msg, index) {
|
|
723
|
-
if (msg.role !== 'user')
|
|
724
|
-
return null;
|
|
725
|
-
const content = msg.content || '';
|
|
726
|
-
for (const pattern of ContextManager.USER_PIVOT_PATTERNS) {
|
|
727
|
-
if (pattern.test(content)) {
|
|
728
|
-
return {
|
|
729
|
-
type: 'user_pivot',
|
|
730
|
-
confidence: 0.85,
|
|
731
|
-
messageIndex: index,
|
|
732
|
-
reason: 'User pivot/direction change detected',
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
return null;
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
|
-
* Detect context saturation (heavy tool output regions)
|
|
740
|
-
*/
|
|
741
|
-
detectContextSaturation(msg, index, messages) {
|
|
742
|
-
if (msg.role !== 'tool')
|
|
743
|
-
return null;
|
|
744
|
-
// Look at the surrounding region
|
|
745
|
-
const windowStart = Math.max(0, index - 10);
|
|
746
|
-
const windowEnd = Math.min(messages.length, index + 5);
|
|
747
|
-
const window = messages.slice(windowStart, windowEnd);
|
|
748
|
-
// Count tool messages and their sizes
|
|
749
|
-
let toolCount = 0;
|
|
750
|
-
let totalToolSize = 0;
|
|
751
|
-
for (const m of window) {
|
|
752
|
-
if (m.role === 'tool') {
|
|
753
|
-
toolCount++;
|
|
754
|
-
totalToolSize += (m.content || '').length;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
// High saturation if many tool outputs with large content
|
|
758
|
-
if (toolCount >= 5 && totalToolSize > 20000) {
|
|
759
|
-
// Find the last tool message in this cluster as compaction point
|
|
760
|
-
let lastToolIndex = index;
|
|
761
|
-
for (let i = index + 1; i < windowEnd; i++) {
|
|
762
|
-
if (messages[i]?.role === 'tool') {
|
|
763
|
-
lastToolIndex = i;
|
|
764
|
-
}
|
|
765
|
-
else if (messages[i]?.role === 'user') {
|
|
766
|
-
break; // Stop at next user message
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return {
|
|
770
|
-
type: 'context_saturation',
|
|
771
|
-
confidence: Math.min(0.5 + toolCount * 0.05, 0.85),
|
|
772
|
-
messageIndex: lastToolIndex,
|
|
773
|
-
reason: `Heavy tool output region (${toolCount} tools, ${Math.round(totalToolSize / 1000)}k chars)`,
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
return null;
|
|
777
|
-
}
|
|
778
|
-
/**
|
|
779
|
-
* Detect milestone signals (significant accomplishments)
|
|
780
|
-
*/
|
|
781
|
-
detectMilestone(msg, index, _messages) {
|
|
782
|
-
if (msg.role !== 'assistant')
|
|
783
|
-
return null;
|
|
784
|
-
const content = msg.content || '';
|
|
785
|
-
// Look for milestone indicators
|
|
786
|
-
const milestonePatterns = [
|
|
787
|
-
/\b(commit(?:ted)?|pushed|deployed|merged|released)\b/i,
|
|
788
|
-
/\b(all\s+tests?\s+pass(?:ing|ed)?)\b/i,
|
|
789
|
-
/\b(build\s+(?:succeed|success|pass))\b/i,
|
|
790
|
-
/\b(feature\s+(?:complete|done|ready))\b/i,
|
|
791
|
-
/\b(pr\s+(?:created|opened|merged))\b/i,
|
|
792
|
-
];
|
|
793
|
-
for (const pattern of milestonePatterns) {
|
|
794
|
-
if (pattern.test(content)) {
|
|
795
|
-
return {
|
|
796
|
-
type: 'milestone',
|
|
797
|
-
confidence: 0.9,
|
|
798
|
-
messageIndex: index,
|
|
799
|
-
reason: 'Significant milestone achieved',
|
|
800
|
-
};
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
return null;
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* Check if content looks like a task conclusion
|
|
807
|
-
*/
|
|
808
|
-
looksLikeConclusion(content) {
|
|
809
|
-
const conclusionPatterns = [
|
|
810
|
-
/\b(let\s+me\s+know|feel\s+free|if\s+you\s+(?:need|have|want))\b/i,
|
|
811
|
-
/\b(anything\s+else|other\s+questions?)\b/i,
|
|
812
|
-
/\b(should\s+be\s+(?:good|working|ready|done))\b/i,
|
|
813
|
-
/\b(that\s+should|this\s+(?:should|will))\s+(?:fix|solve|work)/i,
|
|
814
|
-
];
|
|
815
|
-
return conclusionPatterns.some(p => p.test(content));
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Check if two contents represent different topics (simple heuristic)
|
|
819
|
-
*/
|
|
820
|
-
isNewTopic(prevContent, newContent) {
|
|
821
|
-
// Extract key terms (simple tokenization)
|
|
822
|
-
const extractTerms = (text) => {
|
|
823
|
-
const words = text.toLowerCase()
|
|
824
|
-
.replace(/[^a-z0-9\s]/g, ' ')
|
|
825
|
-
.split(/\s+/)
|
|
826
|
-
.filter(w => w.length > 3);
|
|
827
|
-
return new Set(words);
|
|
828
|
-
};
|
|
829
|
-
const prevTerms = extractTerms(prevContent);
|
|
830
|
-
const newTerms = extractTerms(newContent);
|
|
831
|
-
if (prevTerms.size === 0 || newTerms.size === 0)
|
|
832
|
-
return false;
|
|
833
|
-
// Calculate overlap
|
|
834
|
-
let overlap = 0;
|
|
835
|
-
for (const term of newTerms) {
|
|
836
|
-
if (prevTerms.has(term))
|
|
837
|
-
overlap++;
|
|
838
|
-
}
|
|
839
|
-
const overlapRatio = overlap / Math.min(prevTerms.size, newTerms.size);
|
|
840
|
-
// Low overlap suggests new topic
|
|
841
|
-
return overlapRatio < 0.2;
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Calculate urgency level based on token percentage
|
|
845
|
-
*/
|
|
846
|
-
calculateUrgency(tokenPercentage) {
|
|
847
|
-
if (tokenPercentage >= 0.9)
|
|
848
|
-
return 'critical';
|
|
849
|
-
if (tokenPercentage >= 0.75)
|
|
850
|
-
return 'high';
|
|
851
|
-
if (tokenPercentage >= 0.6)
|
|
852
|
-
return 'medium';
|
|
853
|
-
if (tokenPercentage >= 0.5)
|
|
854
|
-
return 'low';
|
|
855
|
-
return 'none';
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Find the best compaction point from signals
|
|
859
|
-
*/
|
|
860
|
-
findBestCompactionPoint(signals, messages, urgency) {
|
|
861
|
-
if (signals.length === 0)
|
|
862
|
-
return null;
|
|
863
|
-
// Score each signal based on type priority and confidence
|
|
864
|
-
const typePriority = {
|
|
865
|
-
milestone: 1.0,
|
|
866
|
-
task_boundary: 0.9,
|
|
867
|
-
user_pivot: 0.85,
|
|
868
|
-
ai_flow_pattern: 0.82, // AI flow patterns like thinking/tool use cycles
|
|
869
|
-
topic_shift: 0.8,
|
|
870
|
-
context_saturation: 0.7,
|
|
871
|
-
};
|
|
872
|
-
// Urgency affects how far back we're willing to compact
|
|
873
|
-
const urgencyDepth = {
|
|
874
|
-
none: 0,
|
|
875
|
-
low: 0.3, // Compact only recent 30%
|
|
876
|
-
medium: 0.5,
|
|
877
|
-
high: 0.7,
|
|
878
|
-
critical: 0.9,
|
|
879
|
-
};
|
|
880
|
-
const maxDepth = urgencyDepth[urgency] ?? 0.5;
|
|
881
|
-
const minIndex = Math.floor(messages.length * (1 - maxDepth));
|
|
882
|
-
// Find highest scoring signal within allowed depth
|
|
883
|
-
let bestSignal = null;
|
|
884
|
-
let bestScore = 0;
|
|
885
|
-
for (const signal of signals) {
|
|
886
|
-
if (signal.messageIndex < minIndex)
|
|
887
|
-
continue;
|
|
888
|
-
const score = signal.confidence * typePriority[signal.type];
|
|
889
|
-
if (score > bestScore) {
|
|
890
|
-
bestScore = score;
|
|
891
|
-
bestSignal = signal;
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
return bestSignal?.messageIndex ?? null;
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* Find a safe preservation point that doesn't break tool call chains
|
|
898
|
-
*/
|
|
899
|
-
findSafePreservePoint(compactionPoint, messages) {
|
|
900
|
-
// Start from compaction point and move forward to find a safe break
|
|
901
|
-
for (let i = compactionPoint + 1; i < messages.length; i++) {
|
|
902
|
-
const msg = messages[i];
|
|
903
|
-
if (!msg)
|
|
904
|
-
continue;
|
|
905
|
-
// Safe if it's a user message
|
|
906
|
-
if (msg.role === 'user') {
|
|
907
|
-
return i;
|
|
908
|
-
}
|
|
909
|
-
// Safe if it's an assistant without pending tool calls
|
|
910
|
-
if (msg.role === 'assistant' && !msg.toolCalls?.length) {
|
|
911
|
-
return i;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
// If no safe point found, keep more messages
|
|
915
|
-
return Math.min(compactionPoint + 1, messages.length);
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Perform intelligent compaction based on analysis
|
|
919
|
-
* This method analyzes the conversation and compacts at the optimal point
|
|
920
|
-
*/
|
|
921
|
-
async intelligentCompact(messages) {
|
|
922
|
-
// Analyze for compaction points
|
|
923
|
-
const analysis = this.analyzeCompactionPoints(messages);
|
|
924
|
-
// If no compaction needed or no good point found
|
|
925
|
-
if (!analysis.shouldCompact || analysis.recommendedCompactionPoint === null) {
|
|
926
|
-
return {
|
|
927
|
-
compacted: messages,
|
|
928
|
-
analysis,
|
|
929
|
-
summarized: false,
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
// Separate messages to summarize and preserve
|
|
933
|
-
const firstMessage = messages[0];
|
|
934
|
-
const systemMessage = firstMessage?.role === 'system' ? firstMessage : null;
|
|
935
|
-
const startIndex = systemMessage ? 1 : 0;
|
|
936
|
-
const toSummarize = messages.slice(startIndex, analysis.preserveFromIndex);
|
|
937
|
-
const toPreserve = messages.slice(analysis.preserveFromIndex);
|
|
938
|
-
// If nothing to summarize, return as-is
|
|
939
|
-
if (toSummarize.length === 0) {
|
|
940
|
-
return {
|
|
941
|
-
compacted: messages,
|
|
942
|
-
analysis,
|
|
943
|
-
summarized: false,
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
// Build result
|
|
947
|
-
const compacted = [];
|
|
948
|
-
if (systemMessage) {
|
|
949
|
-
compacted.push(systemMessage);
|
|
950
|
-
}
|
|
951
|
-
// Try LLM summarization if available
|
|
952
|
-
if (this.config.summarizationCallback && this.config.useLLMSummarization !== false) {
|
|
953
|
-
try {
|
|
954
|
-
const summary = await this.config.summarizationCallback(toSummarize);
|
|
955
|
-
compacted.push({
|
|
956
|
-
role: 'system',
|
|
957
|
-
content: [
|
|
958
|
-
'=== Intelligent Context Summary ===',
|
|
959
|
-
`Compaction triggered: ${analysis.signals[0]?.reason || 'Context optimization'}`,
|
|
960
|
-
'',
|
|
961
|
-
summary.trim(),
|
|
962
|
-
'',
|
|
963
|
-
`[Summarized ${toSummarize.length} messages. ${toPreserve.length} recent messages preserved.]`,
|
|
964
|
-
].join('\n'),
|
|
965
|
-
});
|
|
966
|
-
compacted.push(...toPreserve);
|
|
967
|
-
return {
|
|
968
|
-
compacted,
|
|
969
|
-
analysis,
|
|
970
|
-
summarized: true,
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
catch {
|
|
974
|
-
// Fall through to simple compaction
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
// Simple compaction without LLM
|
|
978
|
-
compacted.push({
|
|
979
|
-
role: 'system',
|
|
980
|
-
content: `[Context Manager: Intelligently compacted ${toSummarize.length} messages at "${analysis.signals[0]?.reason || 'optimal point'}". ${toPreserve.length} recent messages preserved.]`,
|
|
981
|
-
});
|
|
982
|
-
compacted.push(...toPreserve);
|
|
983
|
-
return {
|
|
984
|
-
compacted,
|
|
985
|
-
analysis,
|
|
986
|
-
summarized: false,
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
/**
|
|
990
|
-
* Check if intelligent compaction should be triggered
|
|
991
|
-
* Call this before generation to proactively manage context
|
|
992
|
-
*/
|
|
993
|
-
shouldTriggerCompaction(messages) {
|
|
994
|
-
if (this.config.enableIntelligentCompaction === false) {
|
|
995
|
-
return { shouldCompact: false, urgency: 'none', reason: null };
|
|
996
|
-
}
|
|
997
|
-
const analysis = this.analyzeCompactionPoints(messages);
|
|
998
|
-
if (!analysis.shouldCompact) {
|
|
999
|
-
return { shouldCompact: false, urgency: analysis.urgency, reason: null };
|
|
1000
|
-
}
|
|
1001
|
-
const topSignal = analysis.signals
|
|
1002
|
-
.sort((a, b) => b.confidence - a.confidence)[0];
|
|
1003
|
-
return {
|
|
1004
|
-
shouldCompact: true,
|
|
1005
|
-
urgency: analysis.urgency,
|
|
1006
|
-
reason: topSignal?.reason || 'Context optimization recommended',
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
548
|
}
|
|
1010
549
|
/**
|
|
1011
550
|
* Create a default context manager instance with model-aware limits
|
|
@@ -1022,11 +561,6 @@ export function createDefaultContextManager(overrides, model) {
|
|
|
1022
561
|
preserveRecentMessages: 5, // Keep last 5 exchanges
|
|
1023
562
|
estimatedCharsPerToken: 3.5, // More aggressive estimate (accounts for special tokens, JSON overhead)
|
|
1024
563
|
useLLMSummarization: true, // Enable LLM summarization by default
|
|
1025
|
-
// Intelligent compaction defaults
|
|
1026
|
-
enableIntelligentCompaction: true,
|
|
1027
|
-
compactionThreshold: 0.5, // Start analyzing at 50% context usage
|
|
1028
|
-
minSignalConfidence: 0.6, // Require 60% confidence for compaction signals
|
|
1029
|
-
topicShiftSensitivity: 0.7, // Moderately sensitive to topic changes
|
|
1030
564
|
model,
|
|
1031
565
|
...overrides,
|
|
1032
566
|
});
|