@codebolt/agent 5.0.9 → 6.0.1

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.
Files changed (47) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +41 -0
  3. package/dist/processor-pieces/messageModifiers/chatHistoryMessageModifier.js +2 -4
  4. package/dist/processor-pieces/messageModifiers/contextAssemblyModifier.d.ts +81 -0
  5. package/dist/processor-pieces/messageModifiers/contextAssemblyModifier.js +316 -0
  6. package/dist/processor-pieces/messageModifiers/coreSystemPromptModifier.js +2 -19
  7. package/dist/processor-pieces/messageModifiers/directoryContextModifier.js +2 -6
  8. package/dist/processor-pieces/messageModifiers/environmentContextModifier.js +2 -7
  9. package/dist/processor-pieces/messageModifiers/ideContextModifier.js +2 -21
  10. package/dist/processor-pieces/messageModifiers/index.d.ts +3 -0
  11. package/dist/processor-pieces/messageModifiers/index.js +7 -1
  12. package/dist/processor-pieces/messageModifiers/toolInjectionModifier.js +2 -1
  13. package/dist/processor-pieces/postToolCallProcessors/conversationCompactorModifier.d.ts +34 -5
  14. package/dist/processor-pieces/postToolCallProcessors/conversationCompactorModifier.js +374 -89
  15. package/dist/processor-pieces/preInferenceProcessors/chatCompressionModifier.d.ts +3 -0
  16. package/dist/processor-pieces/preInferenceProcessors/chatCompressionModifier.js +50 -27
  17. package/dist/unified/agent/agent.d.ts +10 -0
  18. package/dist/unified/agent/agent.js +198 -10
  19. package/dist/unified/agent/codeboltAgent.d.ts +19 -100
  20. package/dist/unified/agent/codeboltAgent.js +209 -109
  21. package/dist/unified/base/agentStep.js +17 -17
  22. package/dist/unified/base/initialPromptGenerator.js +13 -31
  23. package/dist/unified/base/promptContext.d.ts +13 -0
  24. package/dist/unified/base/promptContext.js +213 -0
  25. package/dist/unified/base/responseExecutor.d.ts +7 -19
  26. package/dist/unified/base/responseExecutor.js +351 -258
  27. package/dist/unified/index.d.ts +9 -0
  28. package/dist/unified/index.js +20 -13
  29. package/dist/unified/services/CompressionCoordinator.d.ts +67 -0
  30. package/dist/unified/services/CompressionCoordinator.js +214 -0
  31. package/dist/unified/services/compaction/autoCompact.d.ts +52 -0
  32. package/dist/unified/services/compaction/autoCompact.js +294 -0
  33. package/dist/unified/services/compaction/compactionOrchestrator.d.ts +78 -0
  34. package/dist/unified/services/compaction/compactionOrchestrator.js +230 -0
  35. package/dist/unified/services/compaction/contextCollapse.d.ts +63 -0
  36. package/dist/unified/services/compaction/contextCollapse.js +291 -0
  37. package/dist/unified/services/compaction/microCompact.d.ts +34 -0
  38. package/dist/unified/services/compaction/microCompact.js +195 -0
  39. package/dist/unified/services/compaction/postCompactCleanup.d.ts +15 -0
  40. package/dist/unified/services/compaction/postCompactCleanup.js +37 -0
  41. package/dist/unified/services/compaction/reactiveCompact.d.ts +65 -0
  42. package/dist/unified/services/compaction/reactiveCompact.js +301 -0
  43. package/dist/unified/services/compaction/snipCompact.d.ts +31 -0
  44. package/dist/unified/services/compaction/snipCompact.js +124 -0
  45. package/dist/unified/services/compaction/types.d.ts +66 -0
  46. package/dist/unified/services/compaction/types.js +39 -0
  47. package/package.json +22 -30
@@ -0,0 +1,301 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 5: Reactive Compact
4
+ *
5
+ * Emergency compaction triggered by API errors (413 prompt-too-long,
6
+ * media-size errors). This is the last line of defense when all proactive
7
+ * layers fail to keep the context under the limit.
8
+ *
9
+ * Key properties:
10
+ * - Only fires on real API errors after proactive measures fail
11
+ * - Single-shot per turn (prevents spiral: error -> compact -> error -> compact)
12
+ * - Tries: strip images -> strip older messages -> full compact
13
+ * - Withholds error from consumers until recovery succeeds or exhausts
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ReactiveCompact = void 0;
17
+ const types_1 = require("./types");
18
+ /** Error patterns that indicate context overflow */
19
+ const CONTEXT_ERROR_PATTERNS = [
20
+ /prompt too long/i,
21
+ /context length/i,
22
+ /maximum context/i,
23
+ /context window/i,
24
+ /too many tokens/i,
25
+ /token limit/i,
26
+ /request too large/i,
27
+ /input is too long/i,
28
+ /reduced_image_max_pixels/i,
29
+ /image.*too large/i,
30
+ ];
31
+ /** Messages to always preserve during reactive compact */
32
+ const MIN_PRESERVED_MESSAGES = 6;
33
+ class ReactiveCompact {
34
+ constructor(options) {
35
+ var _a, _b, _c;
36
+ this.name = 'reactive';
37
+ this.hasAttemptedThisTurn = false;
38
+ this.retryCount = 0;
39
+ this.options = {
40
+ retryLimit: (_a = options === null || options === void 0 ? void 0 : options.retryLimit) !== null && _a !== void 0 ? _a : 1,
41
+ modelTokenLimit: (_b = options === null || options === void 0 ? void 0 : options.modelTokenLimit) !== null && _b !== void 0 ? _b : 128000,
42
+ enableLogging: (_c = options === null || options === void 0 ? void 0 : options.enableLogging) !== null && _c !== void 0 ? _c : false,
43
+ };
44
+ }
45
+ shouldApply(_ctx) {
46
+ // Reactive only fires when explicitly triggered by an error
47
+ // via tryRecoverFromError(), not proactively
48
+ return false;
49
+ }
50
+ async apply(_ctx) {
51
+ // Reactive compact is not applied proactively
52
+ return _ctx;
53
+ }
54
+ reset() {
55
+ this.hasAttemptedThisTurn = false;
56
+ this.retryCount = 0;
57
+ }
58
+ /**
59
+ * Reset per-turn guard (call at the start of each turn).
60
+ */
61
+ resetForTurn() {
62
+ this.hasAttemptedThisTurn = false;
63
+ }
64
+ /**
65
+ * Check if an error message indicates a recoverable context error.
66
+ */
67
+ isRecoverableError(errorMessage) {
68
+ return CONTEXT_ERROR_PATTERNS.some(pattern => pattern.test(errorMessage));
69
+ }
70
+ /**
71
+ * Check if an error is a media/size error (images, PDFs, etc.)
72
+ */
73
+ isMediaSizeError(error) {
74
+ if (!error)
75
+ return false;
76
+ const msg = error instanceof Error
77
+ ? error.message
78
+ : typeof error === 'string'
79
+ ? error
80
+ : String(error);
81
+ return (/image.*too large/i.test(msg) ||
82
+ /reduced_image_max_pixels/i.test(msg) ||
83
+ /media.*size/i.test(msg) ||
84
+ /too many images/i.test(msg));
85
+ }
86
+ /**
87
+ * Attempt to recover from a context overflow error.
88
+ * Single-shot per turn to prevent death spirals.
89
+ *
90
+ * Recovery strategy:
91
+ * 1. Strip images and retry (for media errors)
92
+ * 2. Strip oldest messages (keep recent context)
93
+ * 3. Force full compact if available
94
+ */
95
+ async tryRecoverFromError(ctx, error) {
96
+ const estimator = new types_1.TokenEstimator();
97
+ const messages = [...ctx.messages];
98
+ const tokensBefore = estimator.estimateForMessages(messages);
99
+ // Single-shot guard
100
+ if (this.hasAttemptedThisTurn) {
101
+ return {
102
+ recovered: false,
103
+ messages,
104
+ reason: 'Already attempted reactive compact this turn',
105
+ tokensBefore,
106
+ tokensAfter: tokensBefore,
107
+ };
108
+ }
109
+ // Check retry limit
110
+ if (this.retryCount >= this.options.retryLimit) {
111
+ return {
112
+ recovered: false,
113
+ messages,
114
+ reason: `Reactive compact retry limit reached (${this.options.retryLimit})`,
115
+ tokensBefore,
116
+ tokensAfter: tokensBefore,
117
+ };
118
+ }
119
+ this.hasAttemptedThisTurn = true;
120
+ this.retryCount++;
121
+ // Strategy 1: Strip images (for media-size errors)
122
+ if (this.isMediaSizeError(error)) {
123
+ const stripped = this.stripImages(messages);
124
+ const tokensAfter = estimator.estimateForMessages(stripped);
125
+ if (tokensAfter < tokensBefore) {
126
+ if (this.options.enableLogging) {
127
+ console.log(`[ReactiveCompact] Stripped images: ${tokensBefore} -> ${tokensAfter} tokens`);
128
+ }
129
+ return {
130
+ recovered: true,
131
+ messages: stripped,
132
+ reason: 'Stripped oversized images',
133
+ tokensBefore,
134
+ tokensAfter,
135
+ };
136
+ }
137
+ }
138
+ // Strategy 2: Strip oldest messages (aggressive)
139
+ const aggressiveResult = this.stripOldestMessages(messages);
140
+ if (aggressiveResult) {
141
+ const tokensAfter = estimator.estimateForMessages(aggressiveResult);
142
+ const threshold = this.options.modelTokenLimit * 0.7;
143
+ if (tokensAfter < threshold) {
144
+ if (this.options.enableLogging) {
145
+ console.log(`[ReactiveCompact] Stripped old messages: ${tokensBefore} -> ${tokensAfter} tokens`);
146
+ }
147
+ return {
148
+ recovered: true,
149
+ messages: aggressiveResult,
150
+ reason: 'Stripped oldest messages',
151
+ tokensBefore,
152
+ tokensAfter,
153
+ };
154
+ }
155
+ }
156
+ // Strategy 3: Force compact - summarize everything aggressively
157
+ const forceResult = await this.forceCompact(messages);
158
+ if (forceResult) {
159
+ const tokensAfter = estimator.estimateForMessages(forceResult);
160
+ if (tokensAfter < tokensBefore * 0.8) {
161
+ if (this.options.enableLogging) {
162
+ console.log(`[ReactiveCompact] Force compact: ${tokensBefore} -> ${tokensAfter} tokens`);
163
+ }
164
+ return {
165
+ recovered: true,
166
+ messages: forceResult,
167
+ reason: 'Force compacted conversation',
168
+ tokensBefore,
169
+ tokensAfter,
170
+ };
171
+ }
172
+ }
173
+ return {
174
+ recovered: false,
175
+ messages,
176
+ reason: 'All reactive recovery strategies exhausted',
177
+ tokensBefore,
178
+ tokensAfter: tokensBefore,
179
+ };
180
+ }
181
+ // ─── Private Helpers ──────────────────────────────────────────────
182
+ stripImages(messages) {
183
+ return messages.map(msg => {
184
+ if (!msg || typeof msg.content !== 'object' || !Array.isArray(msg.content)) {
185
+ return msg;
186
+ }
187
+ const filtered = msg.content.filter((block) => {
188
+ if (!block)
189
+ return true;
190
+ if (block.type === 'image_url' ||
191
+ block.type === 'image' ||
192
+ (block.type === 'text' &&
193
+ typeof block.text === 'string' &&
194
+ block.text.startsWith('data:image'))) {
195
+ return false;
196
+ }
197
+ return true;
198
+ });
199
+ if (filtered.length === msg.content.length) {
200
+ return msg;
201
+ }
202
+ if (filtered.length < msg.content.length) {
203
+ filtered.push({
204
+ type: 'text',
205
+ text: `[${msg.content.length - filtered.length} image(s) removed due to size limits]`,
206
+ });
207
+ }
208
+ return { ...msg, content: filtered };
209
+ });
210
+ }
211
+ stripOldestMessages(messages) {
212
+ const systemMessages = messages.filter(m => (m === null || m === void 0 ? void 0 : m.role) === 'system');
213
+ const nonSystemMessages = messages.filter(m => (m === null || m === void 0 ? void 0 : m.role) !== 'system');
214
+ if (nonSystemMessages.length <= MIN_PRESERVED_MESSAGES) {
215
+ return null;
216
+ }
217
+ const recentMessages = nonSystemMessages.slice(-MIN_PRESERVED_MESSAGES);
218
+ const strippedCount = nonSystemMessages.length - MIN_PRESERVED_MESSAGES;
219
+ return [
220
+ ...systemMessages,
221
+ {
222
+ role: 'user',
223
+ content: `[Emergency context reduction: ${strippedCount} older messages removed due to context overflow. The current task context is preserved below.]`,
224
+ },
225
+ {
226
+ role: 'assistant',
227
+ content: 'Understood. I will work with the available context.',
228
+ },
229
+ ...recentMessages,
230
+ ];
231
+ }
232
+ async forceCompact(messages) {
233
+ var _a, _b, _c, _d, _e;
234
+ try {
235
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
236
+ const codebolt = require('@codebolt/codeboltjs');
237
+ const systemMessages = messages.filter(m => (m === null || m === void 0 ? void 0 : m.role) === 'system');
238
+ const nonSystemMessages = messages.filter(m => (m === null || m === void 0 ? void 0 : m.role) !== 'system');
239
+ if (nonSystemMessages.length <= MIN_PRESERVED_MESSAGES) {
240
+ return null;
241
+ }
242
+ const toSummarize = nonSystemMessages.slice(0, -MIN_PRESERVED_MESSAGES);
243
+ const toKeep = nonSystemMessages.slice(-MIN_PRESERVED_MESSAGES);
244
+ const historyText = toSummarize
245
+ .map((msg, i) => {
246
+ var _a;
247
+ const content = typeof msg.content === 'string'
248
+ ? msg.content
249
+ : JSON.stringify((_a = msg.content) !== null && _a !== void 0 ? _a : '');
250
+ const truncated = content.length > 500
251
+ ? content.slice(0, 250) + '...[truncated]...' + content.slice(-250)
252
+ : content;
253
+ return `[${i}] ${msg.role}: ${truncated}`;
254
+ })
255
+ .join('\n');
256
+ const prompt = `Emergency context compression. Create a very concise summary preserving only:
257
+ 1. The user's original task/request
258
+ 2. Key files being worked on (paths)
259
+ 3. Current state and next step
260
+ 4. Any critical errors/blockers
261
+
262
+ Conversation:
263
+ ${historyText}`;
264
+ const response = await codebolt.llm.inference({
265
+ messages: [
266
+ {
267
+ role: 'system',
268
+ content: 'Create an extremely concise summary. Focus on actionable facts only.',
269
+ },
270
+ { role: 'user', content: prompt },
271
+ ],
272
+ });
273
+ let summary;
274
+ if (typeof ((_a = response === null || response === void 0 ? void 0 : response.completion) === null || _a === void 0 ? void 0 : _a.content) === 'string') {
275
+ summary = response.completion.content;
276
+ }
277
+ else if ((_e = (_d = (_c = (_b = response === null || response === void 0 ? void 0 : response.completion) === null || _b === void 0 ? void 0 : _b.choices) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message) === null || _e === void 0 ? void 0 : _e.content) {
278
+ summary = response.completion.choices[0].message.content;
279
+ }
280
+ if (!summary || summary.trim().length === 0) {
281
+ return null;
282
+ }
283
+ return [
284
+ ...systemMessages,
285
+ {
286
+ role: 'user',
287
+ content: `[Emergency context recovery. Previous conversation summary:\n\n${summary}]\n\nRecent context continues below.`,
288
+ },
289
+ {
290
+ role: 'assistant',
291
+ content: 'Understood. I have the compressed context and will continue.',
292
+ },
293
+ ...toKeep,
294
+ ];
295
+ }
296
+ catch {
297
+ return null;
298
+ }
299
+ }
300
+ }
301
+ exports.ReactiveCompact = ReactiveCompact;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Layer 1: Snip Compact
3
+ *
4
+ * Proactively removes older messages from the model-facing view each turn.
5
+ * Keeps them in the conversation for scrollback/UI purposes, but removes
6
+ * them from what gets sent to the LLM.
7
+ *
8
+ * Key properties:
9
+ * - Runs every turn, proactively
10
+ * - tokensFreed is plumbed to autocompact so its threshold check is accurate
11
+ * - Cheapest compaction layer (no LLM call, just array slicing)
12
+ */
13
+ import { type CompactionContext, type CompactionLayer, type CompactionLayerKind } from './types';
14
+ export interface SnipCompactOptions {
15
+ /** Number of recent turns to protect (default: 4) */
16
+ protectedTurns?: number;
17
+ /** Max fraction of messages to remove (default: 0.4) */
18
+ maxSnipFraction?: number;
19
+ /** Enable logging (default: false) */
20
+ enableLogging?: boolean;
21
+ }
22
+ export declare class SnipCompact implements CompactionLayer {
23
+ readonly name: CompactionLayerKind;
24
+ private readonly options;
25
+ constructor(options?: SnipCompactOptions);
26
+ shouldApply(ctx: CompactionContext): boolean;
27
+ apply(ctx: CompactionContext): Promise<CompactionContext>;
28
+ reset(): void;
29
+ private findTurnStarts;
30
+ private isToolResponse;
31
+ }
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 1: Snip Compact
4
+ *
5
+ * Proactively removes older messages from the model-facing view each turn.
6
+ * Keeps them in the conversation for scrollback/UI purposes, but removes
7
+ * them from what gets sent to the LLM.
8
+ *
9
+ * Key properties:
10
+ * - Runs every turn, proactively
11
+ * - tokensFreed is plumbed to autocompact so its threshold check is accurate
12
+ * - Cheapest compaction layer (no LLM call, just array slicing)
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SnipCompact = void 0;
16
+ const types_1 = require("./types");
17
+ /** Number of recent turns (user messages) to always protect from snipping */
18
+ const DEFAULT_PROTECTED_TURNS = 4;
19
+ /** Maximum fraction of messages that snip is allowed to remove in one pass */
20
+ const DEFAULT_MAX_SNIP_FRACTION = 0.4;
21
+ class SnipCompact {
22
+ constructor(options) {
23
+ var _a, _b, _c;
24
+ this.name = 'snip';
25
+ this.options = {
26
+ protectedTurns: (_a = options === null || options === void 0 ? void 0 : options.protectedTurns) !== null && _a !== void 0 ? _a : DEFAULT_PROTECTED_TURNS,
27
+ maxSnipFraction: (_b = options === null || options === void 0 ? void 0 : options.maxSnipFraction) !== null && _b !== void 0 ? _b : DEFAULT_MAX_SNIP_FRACTION,
28
+ enableLogging: (_c = options === null || options === void 0 ? void 0 : options.enableLogging) !== null && _c !== void 0 ? _c : false,
29
+ };
30
+ }
31
+ shouldApply(ctx) {
32
+ // Snip runs proactively every turn, as long as there are enough messages.
33
+ // Skip if there's been a recent compaction boundary we must respect.
34
+ const messages = ctx.messages;
35
+ if (!messages || messages.length <= this.options.protectedTurns * 3) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ async apply(ctx) {
41
+ var _a;
42
+ const messages = ctx.messages;
43
+ const estimator = new types_1.TokenEstimator();
44
+ // Find turn boundaries (each turn starts with a user message that is not a tool response)
45
+ const turnStarts = this.findTurnStarts(messages);
46
+ if (turnStarts.length <= this.options.protectedTurns) {
47
+ return ctx;
48
+ }
49
+ // Determine how many turns to snip
50
+ const maxSnippable = turnStarts.length - this.options.protectedTurns;
51
+ const snipCount = Math.min(maxSnippable, Math.ceil(turnStarts.length * this.options.maxSnipFraction));
52
+ if (snipCount <= 0) {
53
+ return ctx;
54
+ }
55
+ // The first protected turn index
56
+ const firstProtectedTurn = (_a = turnStarts[snipCount]) !== null && _a !== void 0 ? _a : messages.length;
57
+ // Count tokens in the snipped range
58
+ const snippedMessages = messages.slice(0, firstProtectedTurn);
59
+ const tokensFreed = estimator.estimateForMessages(snippedMessages);
60
+ // Preserve system messages from the snipped range
61
+ const systemMessages = snippedMessages.filter(m => m.role === 'system');
62
+ // Keep: system messages + protected range
63
+ const protectedMessages = messages.slice(firstProtectedTurn);
64
+ const resultMessages = [
65
+ ...systemMessages,
66
+ // Add a lightweight placeholder noting what was snipped
67
+ {
68
+ role: 'user',
69
+ content: `[Context: ${snipCount} earlier conversation turn(s) trimmed to manage context length. The original task and all recent work are preserved below.]`,
70
+ },
71
+ {
72
+ role: 'assistant',
73
+ content: 'Understood. I have the trimmed context and will continue from the current state.',
74
+ },
75
+ ...protectedMessages,
76
+ ];
77
+ const boundary = {
78
+ layer: 'snip',
79
+ tokensFreed,
80
+ messagesRemoved: snippedMessages.length - systemMessages.length,
81
+ timestamp: new Date().toISOString(),
82
+ };
83
+ if (this.options.enableLogging) {
84
+ console.log(`[SnipCompact] Snipped ${snipCount} turns (${snippedMessages.length} messages, ~${tokensFreed} tokens freed)`);
85
+ }
86
+ return {
87
+ ...ctx,
88
+ messages: resultMessages,
89
+ snipTokensFreed: tokensFreed,
90
+ compactionHistory: [...(ctx.compactionHistory || []), boundary],
91
+ };
92
+ }
93
+ reset() {
94
+ // SnipCompact is stateless across turns
95
+ }
96
+ findTurnStarts(messages) {
97
+ const turns = [];
98
+ for (let i = 0; i < messages.length; i++) {
99
+ const msg = messages[i];
100
+ if (!msg)
101
+ continue;
102
+ // A turn starts at a user message that isn't a tool response
103
+ if (msg.role === 'user' &&
104
+ !this.isToolResponse(msg)) {
105
+ turns.push(i);
106
+ }
107
+ }
108
+ return turns;
109
+ }
110
+ isToolResponse(msg) {
111
+ if (msg.role === 'tool')
112
+ return true;
113
+ if (msg.tool_call_id)
114
+ return true;
115
+ if (typeof msg.content === 'object' && msg.content !== null) {
116
+ if (Array.isArray(msg.content)) {
117
+ return msg.content.some((block) => block &&
118
+ (block.type === 'tool_result' || block.type === 'function_response'));
119
+ }
120
+ }
121
+ return false;
122
+ }
123
+ }
124
+ exports.SnipCompact = SnipCompact;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Multi-Layer Compaction Types
3
+ *
4
+ * Shared types for the 5-layer defense-in-depth compaction system.
5
+ */
6
+ import type { MessageObject } from '@codebolt/types/sdk';
7
+ export declare class TokenEstimator {
8
+ private charsPerToken;
9
+ estimate(text: string): number;
10
+ estimateForMessages(messages: MessageObject[]): number;
11
+ calibrate(estimatedTokens: number, actualTokens: number): void;
12
+ }
13
+ export type CompactionLayerKind = 'snip' | 'micro' | 'collapse' | 'auto' | 'reactive';
14
+ export interface CompactionBoundary {
15
+ /** Which layer produced this boundary */
16
+ layer: CompactionLayerKind;
17
+ /** Approximate tokens freed by this compaction */
18
+ tokensFreed: number;
19
+ /** Number of messages removed */
20
+ messagesRemoved: number;
21
+ /** ISO timestamp */
22
+ timestamp: string;
23
+ /** For auto-compact: whether a summary was included */
24
+ summaryIncluded?: boolean;
25
+ /** For collapse: number of granular summaries committed */
26
+ committed?: number;
27
+ }
28
+ export interface CompactionContext {
29
+ /** Current messages to evaluate/compress */
30
+ messages: MessageObject[];
31
+ /** Tokens freed by snip compact (layer 1) */
32
+ snipTokensFreed?: number;
33
+ /** Whether context collapse is enabled (suppresses auto-compact when true) */
34
+ contextCollapseEnabled?: boolean;
35
+ /** Compaction history (boundaries applied so far) */
36
+ compactionHistory?: CompactionBoundary[];
37
+ /** Auto-compact tracking state */
38
+ autoCompactTracking?: {
39
+ compacted: boolean;
40
+ turnId: string;
41
+ turnCounter: number;
42
+ consecutiveFailures: number;
43
+ };
44
+ }
45
+ export interface CompactionOrchestratorOptions {
46
+ modelTokenLimit?: number;
47
+ autoCompactEnabled?: boolean;
48
+ autoCompactBufferTokens?: number;
49
+ maxConsecutiveFailures?: number;
50
+ snipEnabled?: boolean;
51
+ microCompactEnabled?: boolean;
52
+ contextCollapseEnabled?: boolean;
53
+ microCompactGapMinutes?: number;
54
+ microCompactKeepRecent?: number;
55
+ collapseCommitThreshold?: number;
56
+ collapseBlockingThreshold?: number;
57
+ reactiveRetryLimit?: number;
58
+ llmRole?: string;
59
+ enableLogging?: boolean;
60
+ }
61
+ export interface CompactionLayer {
62
+ readonly name: CompactionLayerKind;
63
+ shouldApply(ctx: CompactionContext): boolean;
64
+ apply(ctx: CompactionContext): Promise<CompactionContext>;
65
+ reset(): void;
66
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Multi-Layer Compaction Types
4
+ *
5
+ * Shared types for the 5-layer defense-in-depth compaction system.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.TokenEstimator = void 0;
9
+ // ─── Token Estimation ────────────────────────────────────────────────
10
+ class TokenEstimator {
11
+ constructor() {
12
+ this.charsPerToken = 4;
13
+ }
14
+ estimate(text) {
15
+ if (!text)
16
+ return 0;
17
+ return Math.ceil(text.length / this.charsPerToken);
18
+ }
19
+ estimateForMessages(messages) {
20
+ if (!messages || !Array.isArray(messages))
21
+ return 0;
22
+ return messages.reduce((total, msg) => {
23
+ var _a;
24
+ if (!msg)
25
+ return total;
26
+ const content = typeof msg.content === 'string'
27
+ ? msg.content
28
+ : JSON.stringify((_a = msg.content) !== null && _a !== void 0 ? _a : '');
29
+ return total + this.estimate(content) + 4;
30
+ }, 0);
31
+ }
32
+ calibrate(estimatedTokens, actualTokens) {
33
+ if (actualTokens <= 0 || estimatedTokens <= 0)
34
+ return;
35
+ const ratio = estimatedTokens / actualTokens;
36
+ this.charsPerToken = Math.max(2, Math.min(6, this.charsPerToken * ratio));
37
+ }
38
+ }
39
+ exports.TokenEstimator = TokenEstimator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebolt/agent",
3
- "version": "5.0.9",
3
+ "version": "6.0.1",
4
4
  "description": "CodeBolt Agent utilities for building and managing AI agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,6 +9,16 @@
9
9
  "README.md",
10
10
  "LICENSE"
11
11
  ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "clean": "rm -rf dist",
15
+ "dev": "tsc --watch",
16
+ "test": "echo \"No tests yet, but build passes\" && exit 0",
17
+ "lint:test": "eslint src/**/*.ts && tsc --noEmit",
18
+ "docs": "node script/gen-docusaurus-agent-types.js",
19
+ "docs:clean": "node script/gen-docusaurus-agent-types.js --clean",
20
+ "docs:watch": "node script/gen-docusaurus-agent-types.js --watch"
21
+ },
12
22
  "keywords": [
13
23
  "ai",
14
24
  "agent",
@@ -24,40 +34,32 @@
24
34
  },
25
35
  "repository": {
26
36
  "type": "git",
27
- "url": "git+https://github.com/codeboltai/codeboltjs.git",
37
+ "url": "git+https://github.com/codeboltai/codebolt.git",
28
38
  "directory": "packages/agent"
29
39
  },
30
40
  "dependencies": {
41
+ "@codebolt/types": "*",
31
42
  "js-yaml": "^4.1.0",
32
- "zod": "^3.22.4",
33
- "@codebolt/types": "5.0.9"
43
+ "zod": "^3.22.4"
34
44
  },
35
45
  "peerDependencies": {
36
- "@codebolt/codeboltjs": "5.0.9"
46
+ "@codebolt/codeboltjs": "*"
37
47
  },
38
48
  "devDependencies": {
49
+ "@codebolt/codeboltjs": "*",
50
+ "@codebolt/types": "*",
39
51
  "@types/js-yaml": "^4.0.9",
40
52
  "@types/node": "^20.14.2",
41
53
  "@types/uri-templates": "^0.1.34",
42
54
  "@types/ws": "^8.5.10",
43
55
  "typedoc": "0.28.16",
44
56
  "typedoc-plugin-markdown": "4.9.0",
45
- "typescript": "^5.4.5",
46
- "@codebolt/codeboltjs": "5.0.9",
47
- "@codebolt/types": "5.0.9"
57
+ "typescript": "^5.4.5"
48
58
  },
49
59
  "exports": {
50
- "./builder": {
51
- "types": "./dist/builderpattern/index.d.ts",
52
- "default": "./dist/builderpattern/index.js"
53
- },
54
- "./composable": {
55
- "types": "./dist/composablepattern/index.d.ts",
56
- "default": "./dist/composablepattern/index.js"
57
- },
58
- "./processor": {
59
- "types": "./dist/processor/index.d.ts",
60
- "default": "./dist/processor/index.js"
60
+ ".": {
61
+ "types": "./dist/index.d.ts",
62
+ "default": "./dist/index.js"
61
63
  },
62
64
  "./processor-pieces": {
63
65
  "types": "./dist/processor-pieces/index.d.ts",
@@ -67,15 +69,5 @@
67
69
  "types": "./dist/unified/index.d.ts",
68
70
  "default": "./dist/unified/index.js"
69
71
  }
70
- },
71
- "scripts": {
72
- "build": "tsc",
73
- "clean": "rm -rf dist",
74
- "dev": "tsc --watch",
75
- "test": "echo \"No tests yet, but build passes\" && exit 0",
76
- "lint:test": "eslint src/**/*.ts && tsc --noEmit",
77
- "docs": "node script/gen-docusaurus-agent-types.js",
78
- "docs:clean": "node script/gen-docusaurus-agent-types.js --clean",
79
- "docs:watch": "node script/gen-docusaurus-agent-types.js --watch"
80
72
  }
81
- }
73
+ }