@codebolt/agent 6.0.0 → 6.1.2
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/README.md +118 -153
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -0
- package/dist/processor-pieces/messageModifiers/chatHistoryMessageModifier.js +2 -4
- package/dist/processor-pieces/messageModifiers/contextAssemblyModifier.d.ts +81 -0
- package/dist/processor-pieces/messageModifiers/contextAssemblyModifier.js +316 -0
- package/dist/processor-pieces/messageModifiers/coreSystemPromptModifier.js +2 -19
- package/dist/processor-pieces/messageModifiers/directoryContextModifier.js +2 -6
- package/dist/processor-pieces/messageModifiers/environmentContextModifier.js +2 -7
- package/dist/processor-pieces/messageModifiers/ideContextModifier.js +2 -21
- package/dist/processor-pieces/messageModifiers/index.d.ts +3 -0
- package/dist/processor-pieces/messageModifiers/index.js +7 -1
- package/dist/processor-pieces/messageModifiers/toolInjectionModifier.js +2 -1
- package/dist/processor-pieces/postToolCallProcessors/conversationCompactorModifier.d.ts +34 -5
- package/dist/processor-pieces/postToolCallProcessors/conversationCompactorModifier.js +374 -89
- package/dist/processor-pieces/preInferenceProcessors/chatCompressionModifier.d.ts +3 -0
- package/dist/processor-pieces/preInferenceProcessors/chatCompressionModifier.js +50 -27
- package/dist/unified/agent/agent.d.ts +10 -0
- package/dist/unified/agent/agent.js +198 -10
- package/dist/unified/agent/codeboltAgent.d.ts +19 -100
- package/dist/unified/agent/codeboltAgent.js +209 -109
- package/dist/unified/base/agentStep.js +17 -17
- package/dist/unified/base/initialPromptGenerator.js +13 -31
- package/dist/unified/base/promptContext.d.ts +13 -0
- package/dist/unified/base/promptContext.js +213 -0
- package/dist/unified/base/responseExecutor.d.ts +7 -19
- package/dist/unified/base/responseExecutor.js +280 -259
- package/dist/unified/index.d.ts +9 -0
- package/dist/unified/index.js +20 -13
- package/dist/unified/services/CompressionCoordinator.d.ts +67 -0
- package/dist/unified/services/CompressionCoordinator.js +214 -0
- package/dist/unified/services/compaction/autoCompact.d.ts +52 -0
- package/dist/unified/services/compaction/autoCompact.js +294 -0
- package/dist/unified/services/compaction/compactionOrchestrator.d.ts +78 -0
- package/dist/unified/services/compaction/compactionOrchestrator.js +230 -0
- package/dist/unified/services/compaction/contextCollapse.d.ts +63 -0
- package/dist/unified/services/compaction/contextCollapse.js +291 -0
- package/dist/unified/services/compaction/microCompact.d.ts +34 -0
- package/dist/unified/services/compaction/microCompact.js +195 -0
- package/dist/unified/services/compaction/postCompactCleanup.d.ts +15 -0
- package/dist/unified/services/compaction/postCompactCleanup.js +37 -0
- package/dist/unified/services/compaction/reactiveCompact.d.ts +65 -0
- package/dist/unified/services/compaction/reactiveCompact.js +301 -0
- package/dist/unified/services/compaction/snipCompact.d.ts +31 -0
- package/dist/unified/services/compaction/snipCompact.js +124 -0
- package/dist/unified/services/compaction/types.d.ts +66 -0
- package/dist/unified/services/compaction/types.js +39 -0
- package/package.json +25 -29
|
@@ -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": "6.
|
|
3
|
+
"version": "6.1.2",
|
|
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,17 @@
|
|
|
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": "eslint src/**/*.ts && tsc --noEmit",
|
|
18
|
+
"lint:test": "npm run lint",
|
|
19
|
+
"docs": "node script/gen-docusaurus-agent-types.js",
|
|
20
|
+
"docs:clean": "node script/gen-docusaurus-agent-types.js --clean",
|
|
21
|
+
"docs:watch": "node script/gen-docusaurus-agent-types.js --watch"
|
|
22
|
+
},
|
|
12
23
|
"keywords": [
|
|
13
24
|
"ai",
|
|
14
25
|
"agent",
|
|
@@ -24,40 +35,35 @@
|
|
|
24
35
|
},
|
|
25
36
|
"repository": {
|
|
26
37
|
"type": "git",
|
|
27
|
-
"url": "git+https://github.com/codeboltai/
|
|
38
|
+
"url": "git+https://github.com/codeboltai/codebolt.git",
|
|
28
39
|
"directory": "packages/agent"
|
|
29
40
|
},
|
|
30
41
|
"dependencies": {
|
|
42
|
+
"@codebolt/types": "*",
|
|
31
43
|
"js-yaml": "^4.1.0",
|
|
32
|
-
"zod": "^3.22.4"
|
|
33
|
-
"@codebolt/types": "6.0.0"
|
|
44
|
+
"zod": "^3.22.4"
|
|
34
45
|
},
|
|
35
46
|
"peerDependencies": {
|
|
36
|
-
"@codebolt/codeboltjs": "
|
|
47
|
+
"@codebolt/codeboltjs": "*"
|
|
37
48
|
},
|
|
38
49
|
"devDependencies": {
|
|
50
|
+
"@codebolt/codeboltjs": "*",
|
|
51
|
+
"@codebolt/types": "*",
|
|
52
|
+
"@eslint/js": "^8.57.1",
|
|
39
53
|
"@types/js-yaml": "^4.0.9",
|
|
40
54
|
"@types/node": "^20.14.2",
|
|
41
55
|
"@types/uri-templates": "^0.1.34",
|
|
42
56
|
"@types/ws": "^8.5.10",
|
|
57
|
+
"eslint": "^8.57.1",
|
|
43
58
|
"typedoc": "0.28.16",
|
|
44
59
|
"typedoc-plugin-markdown": "4.9.0",
|
|
45
60
|
"typescript": "^5.4.5",
|
|
46
|
-
"
|
|
47
|
-
"@codebolt/types": "6.0.0"
|
|
61
|
+
"typescript-eslint": "^7.18.0"
|
|
48
62
|
},
|
|
49
63
|
"exports": {
|
|
50
|
-
"
|
|
51
|
-
"types": "./dist/
|
|
52
|
-
"default": "./dist/
|
|
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"
|
|
64
|
+
".": {
|
|
65
|
+
"types": "./dist/index.d.ts",
|
|
66
|
+
"default": "./dist/index.js"
|
|
61
67
|
},
|
|
62
68
|
"./processor-pieces": {
|
|
63
69
|
"types": "./dist/processor-pieces/index.d.ts",
|
|
@@ -67,15 +73,5 @@
|
|
|
67
73
|
"types": "./dist/unified/index.d.ts",
|
|
68
74
|
"default": "./dist/unified/index.js"
|
|
69
75
|
}
|
|
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
76
|
}
|
|
81
|
-
}
|
|
77
|
+
}
|