@agentionai/agents 0.8.1 → 0.10.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/README.md +44 -0
- package/dist/agents/AgentConfig.d.ts +6 -0
- package/dist/agents/BaseAgent.d.ts +3 -3
- package/dist/agents/BaseAgent.js +8 -4
- package/dist/agents/anthropic/ClaudeAgent.d.ts +2 -2
- package/dist/agents/anthropic/ClaudeAgent.js +31 -4
- package/dist/agents/google/GeminiAgent.d.ts +2 -2
- package/dist/agents/google/GeminiAgent.js +10 -4
- package/dist/agents/mistral/MistralAgent.d.ts +2 -2
- package/dist/agents/mistral/MistralAgent.js +10 -4
- package/dist/agents/openai/OpenAiAgent.d.ts +2 -2
- package/dist/agents/openai/OpenAiAgent.js +10 -4
- package/dist/history/History.d.ts +144 -9
- package/dist/history/History.js +242 -5
- package/dist/history/index.d.ts +5 -0
- package/dist/history/index.js +17 -0
- package/dist/history/plugins/compressionPlugin.d.ts +54 -0
- package/dist/history/plugins/compressionPlugin.js +143 -0
- package/dist/history/plugins/index.d.ts +3 -0
- package/dist/history/plugins/index.js +8 -0
- package/dist/history/plugins/toolResultMaskingPlugin.d.ts +66 -0
- package/dist/history/plugins/toolResultMaskingPlugin.js +141 -0
- package/dist/history/transformers.js +101 -14
- package/dist/history/types.d.ts +51 -1
- package/dist/history/types.js +26 -0
- package/package.json +9 -1
package/dist/history/History.js
CHANGED
|
@@ -1,19 +1,84 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.History = exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.textMessage = exports.toolResult = exports.toolUse = exports.text = void 0;
|
|
39
|
+
exports.History = exports.isImageContent = exports.isImageBase64Content = exports.isImageUrlContent = exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.imageBase64 = exports.imageUrl = exports.textMessage = exports.toolResult = exports.toolUse = exports.text = void 0;
|
|
40
|
+
exports.resetTokenxCache = resetTokenxCache;
|
|
7
41
|
const events_1 = __importDefault(require("events"));
|
|
8
42
|
const types_1 = require("./types");
|
|
43
|
+
// Cached tokenx estimator — starts as a character-based fallback and is
|
|
44
|
+
// replaced with the real tokenx implementation once the module loads.
|
|
45
|
+
let _estimateTokenCount = (t) => Math.ceil(t.length / 4);
|
|
46
|
+
void Promise.resolve().then(() => __importStar(require("tokenx"))).then((mod) => {
|
|
47
|
+
_estimateTokenCount = mod.estimateTokenCount;
|
|
48
|
+
})
|
|
49
|
+
.catch(() => {
|
|
50
|
+
/* keep fallback */
|
|
51
|
+
});
|
|
52
|
+
/** @internal — exposed for test teardown only */
|
|
53
|
+
function resetTokenxCache() {
|
|
54
|
+
_estimateTokenCount = (t) => Math.ceil(t.length / 4);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Estimate token count for a content block array.
|
|
58
|
+
* Image blocks use a flat 1000-token estimate (resolution-independent conservative value).
|
|
59
|
+
* Text and tool blocks fall through to the tokenx estimator.
|
|
60
|
+
*/
|
|
61
|
+
function estimateContentTokens(content) {
|
|
62
|
+
return content.reduce((sum, block) => {
|
|
63
|
+
if ((0, types_1.isImageContent)(block)) {
|
|
64
|
+
return sum + 1000;
|
|
65
|
+
}
|
|
66
|
+
return sum + _estimateTokenCount(JSON.stringify(block));
|
|
67
|
+
}, 0);
|
|
68
|
+
}
|
|
9
69
|
var types_2 = require("./types");
|
|
10
70
|
Object.defineProperty(exports, "text", { enumerable: true, get: function () { return types_2.text; } });
|
|
11
71
|
Object.defineProperty(exports, "toolUse", { enumerable: true, get: function () { return types_2.toolUse; } });
|
|
12
72
|
Object.defineProperty(exports, "toolResult", { enumerable: true, get: function () { return types_2.toolResult; } });
|
|
13
73
|
Object.defineProperty(exports, "textMessage", { enumerable: true, get: function () { return types_2.textMessage; } });
|
|
74
|
+
Object.defineProperty(exports, "imageUrl", { enumerable: true, get: function () { return types_2.imageUrl; } });
|
|
75
|
+
Object.defineProperty(exports, "imageBase64", { enumerable: true, get: function () { return types_2.imageBase64; } });
|
|
14
76
|
Object.defineProperty(exports, "isTextContent", { enumerable: true, get: function () { return types_2.isTextContent; } });
|
|
15
77
|
Object.defineProperty(exports, "isToolUseContent", { enumerable: true, get: function () { return types_2.isToolUseContent; } });
|
|
16
78
|
Object.defineProperty(exports, "isToolResultContent", { enumerable: true, get: function () { return types_2.isToolResultContent; } });
|
|
79
|
+
Object.defineProperty(exports, "isImageUrlContent", { enumerable: true, get: function () { return types_2.isImageUrlContent; } });
|
|
80
|
+
Object.defineProperty(exports, "isImageBase64Content", { enumerable: true, get: function () { return types_2.isImageBase64Content; } });
|
|
81
|
+
Object.defineProperty(exports, "isImageContent", { enumerable: true, get: function () { return types_2.isImageContent; } });
|
|
17
82
|
/**
|
|
18
83
|
* Manages conversation history in a provider-agnostic format.
|
|
19
84
|
*
|
|
@@ -23,6 +88,10 @@ Object.defineProperty(exports, "isToolResultContent", { enumerable: true, get: f
|
|
|
23
88
|
* History can be shared between agents of different providers, enabling
|
|
24
89
|
* cross-provider conversations and handoffs.
|
|
25
90
|
*
|
|
91
|
+
* Plugins can be registered with `history.use(plugin)` to add read-time
|
|
92
|
+
* transforms (e.g., tool result masking) or async reduce strategies
|
|
93
|
+
* (e.g., rolling LLM summarization).
|
|
94
|
+
*
|
|
26
95
|
* @example Basic usage
|
|
27
96
|
* ```typescript
|
|
28
97
|
* const history = new History();
|
|
@@ -30,6 +99,19 @@ Object.defineProperty(exports, "isToolResultContent", { enumerable: true, get: f
|
|
|
30
99
|
* history.addText("assistant", "Hi there!");
|
|
31
100
|
* ```
|
|
32
101
|
*
|
|
102
|
+
* @example With tool result masking plugin
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { toolResultMaskingPlugin } from "agention-lib/history/plugins";
|
|
105
|
+
*
|
|
106
|
+
* const maskingPlugin = toolResultMaskingPlugin({ keepRecentResults: 2 });
|
|
107
|
+
* const history = new History().use(maskingPlugin);
|
|
108
|
+
*
|
|
109
|
+
* const agent = new ClaudeAgent(
|
|
110
|
+
* { tools: [maskingPlugin.retrieveTool, ...otherTools] },
|
|
111
|
+
* history
|
|
112
|
+
* );
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
33
115
|
* @example Sharing between agents
|
|
34
116
|
* ```typescript
|
|
35
117
|
* const history = new History();
|
|
@@ -43,6 +125,8 @@ class History extends events_1.default {
|
|
|
43
125
|
super();
|
|
44
126
|
this._entries = [];
|
|
45
127
|
this.transient = false;
|
|
128
|
+
this._plugins = [];
|
|
129
|
+
this._reducing = false;
|
|
46
130
|
this.options = options;
|
|
47
131
|
this.transient = Boolean(options?.transient);
|
|
48
132
|
// Convert initial entries to internal format with metadata
|
|
@@ -50,13 +134,39 @@ class History extends events_1.default {
|
|
|
50
134
|
this.addEntry(entry);
|
|
51
135
|
}
|
|
52
136
|
}
|
|
137
|
+
// ===========================================================================
|
|
138
|
+
// Plugin registration
|
|
139
|
+
// ===========================================================================
|
|
140
|
+
/**
|
|
141
|
+
* Register a plugin with this history instance.
|
|
142
|
+
* Calls plugin.onRegistered(this) immediately after registration.
|
|
143
|
+
* Returns `this` for chaining.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* history
|
|
148
|
+
* .use(compressionPlugin(summaryAgent))
|
|
149
|
+
* .use(toolResultMaskingPlugin({ keepRecentResults: 2 }));
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
use(plugin) {
|
|
153
|
+
this._plugins.push(plugin);
|
|
154
|
+
plugin.onRegistered?.(this);
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
// ===========================================================================
|
|
158
|
+
// Core write operations
|
|
159
|
+
// ===========================================================================
|
|
53
160
|
/**
|
|
54
161
|
* Add a complete history entry
|
|
55
162
|
*/
|
|
56
163
|
addEntry(entry) {
|
|
164
|
+
const serialized = JSON.stringify(entry.content);
|
|
165
|
+
const contentLength = serialized.length;
|
|
57
166
|
const __metadata = {
|
|
58
167
|
date: new Date().toISOString(),
|
|
59
|
-
contentLength
|
|
168
|
+
contentLength,
|
|
169
|
+
estimatedTokens: estimateContentTokens(entry.content),
|
|
60
170
|
};
|
|
61
171
|
this._entries.push({
|
|
62
172
|
...entry,
|
|
@@ -65,7 +175,27 @@ class History extends events_1.default {
|
|
|
65
175
|
if (this.options.maxLength && this._entries.length > this.options.maxLength) {
|
|
66
176
|
this._entries = this._entries.slice(this._entries.length - this.options.maxLength);
|
|
67
177
|
}
|
|
178
|
+
if (this.options.maxTokens) {
|
|
179
|
+
this.trimToTokenBudget();
|
|
180
|
+
}
|
|
68
181
|
this.emit("entry", entry);
|
|
182
|
+
// Fire plugin afterAdd hooks. Skipped during reduce() to avoid recursion
|
|
183
|
+
// when compression plugins add summary entries to the history.
|
|
184
|
+
if (!this._reducing) {
|
|
185
|
+
for (const plugin of this._plugins) {
|
|
186
|
+
if (!plugin.afterAdd)
|
|
187
|
+
continue;
|
|
188
|
+
void Promise.resolve(plugin.afterAdd(this)).catch((err) => {
|
|
189
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
190
|
+
if (this.options.onPluginError) {
|
|
191
|
+
this.options.onPluginError(error, plugin, "afterAdd");
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
this.emit("pluginError", error, plugin, "afterAdd");
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
69
199
|
}
|
|
70
200
|
/**
|
|
71
201
|
* Add a simple text message
|
|
@@ -88,8 +218,28 @@ class History extends events_1.default {
|
|
|
88
218
|
addSystem(content) {
|
|
89
219
|
this.addText("system", content);
|
|
90
220
|
}
|
|
221
|
+
// ===========================================================================
|
|
222
|
+
// Read operations
|
|
223
|
+
// ===========================================================================
|
|
224
|
+
/**
|
|
225
|
+
* Get entries as agents should see them — with all registered transform
|
|
226
|
+
* plugins applied in registration order.
|
|
227
|
+
*
|
|
228
|
+
* Use this when building API requests. The raw `entries` getter is
|
|
229
|
+
* reserved for serialization, cloning, and other internal purposes.
|
|
230
|
+
*/
|
|
231
|
+
getEntries() {
|
|
232
|
+
let entries = this._entries;
|
|
233
|
+
for (const plugin of this._plugins) {
|
|
234
|
+
if (plugin.transform) {
|
|
235
|
+
entries = plugin.transform(entries);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return entries.map(({ __metadata, ...rest }) => rest);
|
|
239
|
+
}
|
|
91
240
|
/**
|
|
92
|
-
* Get all entries
|
|
241
|
+
* Get all entries without transform plugins applied (raw storage).
|
|
242
|
+
* Use for serialization, cloning, and debugging.
|
|
93
243
|
*/
|
|
94
244
|
get entries() {
|
|
95
245
|
return this._entries.map((entry) => {
|
|
@@ -97,6 +247,24 @@ class History extends events_1.default {
|
|
|
97
247
|
return rest;
|
|
98
248
|
});
|
|
99
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Get the full content of a tool result by its tool_use_id.
|
|
252
|
+
* Always reads from raw stored entries — never affected by transform plugins.
|
|
253
|
+
*
|
|
254
|
+
* For RedisHistory: call await load() before using this method.
|
|
255
|
+
*
|
|
256
|
+
* @returns The full result string, or undefined if not found.
|
|
257
|
+
*/
|
|
258
|
+
getToolResult(tool_use_id) {
|
|
259
|
+
for (const entry of this._entries) {
|
|
260
|
+
for (const block of entry.content) {
|
|
261
|
+
if ((0, types_1.isToolResultContent)(block) && block.tool_use_id === tool_use_id) {
|
|
262
|
+
return block.content;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
100
268
|
/**
|
|
101
269
|
* Get the number of entries
|
|
102
270
|
*/
|
|
@@ -111,6 +279,15 @@ class History extends events_1.default {
|
|
|
111
279
|
return total + __metadata.contentLength;
|
|
112
280
|
}, 0);
|
|
113
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Get total estimated token count across all entries.
|
|
284
|
+
* Uses a rough approximation of 1 token ≈ 4 characters.
|
|
285
|
+
*/
|
|
286
|
+
get totalEstimatedTokens() {
|
|
287
|
+
return this._entries.reduce((total, { __metadata }) => {
|
|
288
|
+
return total + __metadata.estimatedTokens;
|
|
289
|
+
}, 0);
|
|
290
|
+
}
|
|
114
291
|
/**
|
|
115
292
|
* Get the last entry
|
|
116
293
|
*/
|
|
@@ -133,11 +310,52 @@ class History extends events_1.default {
|
|
|
133
310
|
.join("\n");
|
|
134
311
|
}
|
|
135
312
|
/**
|
|
136
|
-
* Get entries without system messages
|
|
313
|
+
* Get entries without system messages, with transform plugins applied.
|
|
137
314
|
*/
|
|
138
315
|
getMessagesWithoutSystem() {
|
|
139
|
-
return this.
|
|
316
|
+
return this.getEntries().filter((e) => e.role !== "system");
|
|
317
|
+
}
|
|
318
|
+
// ===========================================================================
|
|
319
|
+
// Async reduction
|
|
320
|
+
// ===========================================================================
|
|
321
|
+
/**
|
|
322
|
+
* Asynchronously compact history using registered reduce plugins.
|
|
323
|
+
*
|
|
324
|
+
* Plugins are called in registration order, each receiving and returning
|
|
325
|
+
* the full entry array. If no plugin has a `reduce` hook, this is a no-op —
|
|
326
|
+
* the addEntry() safety net (FIFO drop via maxTokens) runs independently.
|
|
327
|
+
*
|
|
328
|
+
* Re-entrant calls during an in-progress reduce() return immediately.
|
|
329
|
+
*
|
|
330
|
+
* @example Rolling summarization
|
|
331
|
+
* ```typescript
|
|
332
|
+
* history.use(compressionPlugin(summaryAgent));
|
|
333
|
+
* await history.reduce({ maxTokens: 4000 });
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
async reduce(options = {}) {
|
|
337
|
+
if (this._reducing)
|
|
338
|
+
return;
|
|
339
|
+
const hasReducePlugin = this._plugins.some((p) => Boolean(p.reduce));
|
|
340
|
+
if (!hasReducePlugin)
|
|
341
|
+
return;
|
|
342
|
+
this._reducing = true;
|
|
343
|
+
try {
|
|
344
|
+
let entries = [...this._entries];
|
|
345
|
+
for (const plugin of this._plugins) {
|
|
346
|
+
if (plugin.reduce) {
|
|
347
|
+
entries = await plugin.reduce(entries, options);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
this._entries = entries;
|
|
351
|
+
}
|
|
352
|
+
finally {
|
|
353
|
+
this._reducing = false;
|
|
354
|
+
}
|
|
140
355
|
}
|
|
356
|
+
// ===========================================================================
|
|
357
|
+
// Utility
|
|
358
|
+
// ===========================================================================
|
|
141
359
|
/**
|
|
142
360
|
* Clear all history entries
|
|
143
361
|
*/
|
|
@@ -164,6 +382,25 @@ class History extends events_1.default {
|
|
|
164
382
|
clone(options) {
|
|
165
383
|
return new History(this.entries, options ?? this.options);
|
|
166
384
|
}
|
|
385
|
+
// ===========================================================================
|
|
386
|
+
// Private helpers
|
|
387
|
+
// ===========================================================================
|
|
388
|
+
/**
|
|
389
|
+
* Drop oldest non-system entries until totalEstimatedTokens fits within budget.
|
|
390
|
+
* Called synchronously from addEntry() as a safety net.
|
|
391
|
+
* The system message is always preserved.
|
|
392
|
+
*/
|
|
393
|
+
trimToTokenBudget(maxTokens) {
|
|
394
|
+
const budget = maxTokens ?? this.options.maxTokens;
|
|
395
|
+
if (!budget)
|
|
396
|
+
return;
|
|
397
|
+
while (this.totalEstimatedTokens > budget && this._entries.length > 1) {
|
|
398
|
+
const firstNonSystem = this._entries.findIndex((e) => e.role !== "system");
|
|
399
|
+
if (firstNonSystem === -1)
|
|
400
|
+
break;
|
|
401
|
+
this._entries.splice(firstNonSystem, 1);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
167
404
|
}
|
|
168
405
|
exports.History = History;
|
|
169
406
|
//# sourceMappingURL=History.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { History, resetTokenxCache, type EntryMetadata, type ReducibleEntry, type HistoryPlugin, } from "./History";
|
|
2
|
+
export { RedisHistory } from "./RedisHistory";
|
|
3
|
+
export type { HistoryEntry, MessageRole, MessageContent, TextContent, ToolUseContent, ToolResultContent, ProviderMeta, ReduceOptions, } from "./types";
|
|
4
|
+
export { text, toolUse, toolResult, textMessage, isTextContent, isToolUseContent, isToolResultContent, } from "./types";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.textMessage = exports.toolResult = exports.toolUse = exports.text = exports.RedisHistory = exports.resetTokenxCache = exports.History = void 0;
|
|
4
|
+
var History_1 = require("./History");
|
|
5
|
+
Object.defineProperty(exports, "History", { enumerable: true, get: function () { return History_1.History; } });
|
|
6
|
+
Object.defineProperty(exports, "resetTokenxCache", { enumerable: true, get: function () { return History_1.resetTokenxCache; } });
|
|
7
|
+
var RedisHistory_1 = require("./RedisHistory");
|
|
8
|
+
Object.defineProperty(exports, "RedisHistory", { enumerable: true, get: function () { return RedisHistory_1.RedisHistory; } });
|
|
9
|
+
var types_1 = require("./types");
|
|
10
|
+
Object.defineProperty(exports, "text", { enumerable: true, get: function () { return types_1.text; } });
|
|
11
|
+
Object.defineProperty(exports, "toolUse", { enumerable: true, get: function () { return types_1.toolUse; } });
|
|
12
|
+
Object.defineProperty(exports, "toolResult", { enumerable: true, get: function () { return types_1.toolResult; } });
|
|
13
|
+
Object.defineProperty(exports, "textMessage", { enumerable: true, get: function () { return types_1.textMessage; } });
|
|
14
|
+
Object.defineProperty(exports, "isTextContent", { enumerable: true, get: function () { return types_1.isTextContent; } });
|
|
15
|
+
Object.defineProperty(exports, "isToolUseContent", { enumerable: true, get: function () { return types_1.isToolUseContent; } });
|
|
16
|
+
Object.defineProperty(exports, "isToolResultContent", { enumerable: true, get: function () { return types_1.isToolResultContent; } });
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ReduceOptions } from "../types";
|
|
2
|
+
import type { HistoryPlugin } from "../History";
|
|
3
|
+
import type { BaseAgent } from "../../agents/BaseAgent";
|
|
4
|
+
/** Options for {@link compressionPlugin}. */
|
|
5
|
+
export type CompressionPluginOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* When set, the plugin automatically calls `history.reduce()` from its
|
|
8
|
+
* `afterAdd` hook whenever the history exceeds the given threshold.
|
|
9
|
+
* The same `ReduceOptions` object is forwarded to `reduce()`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* history.use(compressionPlugin(summaryAgent, { autoReduceWhen: { maxTokens: 6000 } }));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
autoReduceWhen?: ReduceOptions;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Creates a rolling-summary compression plugin.
|
|
20
|
+
*
|
|
21
|
+
* When `history.reduce(options)` is called, this plugin compresses old entries
|
|
22
|
+
* into a single summary entry using the provided agent. On subsequent reduces,
|
|
23
|
+
* the existing summary is included as prior context so the agent can extend it
|
|
24
|
+
* (rolling strategy) — at most one summary entry exists at any time.
|
|
25
|
+
*
|
|
26
|
+
* The summary entry uses `role: "user"` because no LLM provider has a
|
|
27
|
+
* dedicated summary role. Content is always wrapped as
|
|
28
|
+
* `[Earlier conversation summary: ...]` so it can be detected by pattern
|
|
29
|
+
* as well as the `isSummary` metadata flag.
|
|
30
|
+
*
|
|
31
|
+
* Pass `autoReduceWhen` to trigger compression automatically after every
|
|
32
|
+
* `addEntry()` call when the given threshold is exceeded — no manual
|
|
33
|
+
* `history.reduce()` call required.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const summaryAgent = new ClaudeAgent({
|
|
38
|
+
* id: "summarizer",
|
|
39
|
+
* name: "Summarizer",
|
|
40
|
+
* description: "Summarizes conversation history",
|
|
41
|
+
* apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
42
|
+
* model: "claude-haiku-4-5-20251001",
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* // Manual trigger
|
|
46
|
+
* history.use(compressionPlugin(summaryAgent));
|
|
47
|
+
* await history.reduce({ maxTokens: 4000 });
|
|
48
|
+
*
|
|
49
|
+
* // Automatic trigger
|
|
50
|
+
* history.use(compressionPlugin(summaryAgent, { autoReduceWhen: { maxTokens: 6000 } }));
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function compressionPlugin(agent: BaseAgent, options?: CompressionPluginOptions): HistoryPlugin;
|
|
54
|
+
//# sourceMappingURL=compressionPlugin.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compressionPlugin = compressionPlugin;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
/**
|
|
6
|
+
* Creates a rolling-summary compression plugin.
|
|
7
|
+
*
|
|
8
|
+
* When `history.reduce(options)` is called, this plugin compresses old entries
|
|
9
|
+
* into a single summary entry using the provided agent. On subsequent reduces,
|
|
10
|
+
* the existing summary is included as prior context so the agent can extend it
|
|
11
|
+
* (rolling strategy) — at most one summary entry exists at any time.
|
|
12
|
+
*
|
|
13
|
+
* The summary entry uses `role: "user"` because no LLM provider has a
|
|
14
|
+
* dedicated summary role. Content is always wrapped as
|
|
15
|
+
* `[Earlier conversation summary: ...]` so it can be detected by pattern
|
|
16
|
+
* as well as the `isSummary` metadata flag.
|
|
17
|
+
*
|
|
18
|
+
* Pass `autoReduceWhen` to trigger compression automatically after every
|
|
19
|
+
* `addEntry()` call when the given threshold is exceeded — no manual
|
|
20
|
+
* `history.reduce()` call required.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const summaryAgent = new ClaudeAgent({
|
|
25
|
+
* id: "summarizer",
|
|
26
|
+
* name: "Summarizer",
|
|
27
|
+
* description: "Summarizes conversation history",
|
|
28
|
+
* apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
29
|
+
* model: "claude-haiku-4-5-20251001",
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Manual trigger
|
|
33
|
+
* history.use(compressionPlugin(summaryAgent));
|
|
34
|
+
* await history.reduce({ maxTokens: 4000 });
|
|
35
|
+
*
|
|
36
|
+
* // Automatic trigger
|
|
37
|
+
* history.use(compressionPlugin(summaryAgent, { autoReduceWhen: { maxTokens: 6000 } }));
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function compressionPlugin(agent, options = {}) {
|
|
41
|
+
const { autoReduceWhen } = options;
|
|
42
|
+
function shouldAutoReduce(history) {
|
|
43
|
+
if (!autoReduceWhen)
|
|
44
|
+
return false;
|
|
45
|
+
if (autoReduceWhen.maxTokens !== undefined) {
|
|
46
|
+
return history.totalEstimatedTokens > autoReduceWhen.maxTokens;
|
|
47
|
+
}
|
|
48
|
+
if (autoReduceWhen.maxEntries !== undefined) {
|
|
49
|
+
return history.length > autoReduceWhen.maxEntries;
|
|
50
|
+
}
|
|
51
|
+
// olderThan: always trigger so reduce() can evaluate
|
|
52
|
+
if (autoReduceWhen.olderThan !== undefined)
|
|
53
|
+
return true;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
afterAdd(history) {
|
|
58
|
+
if (!autoReduceWhen)
|
|
59
|
+
return;
|
|
60
|
+
if (!shouldAutoReduce(history))
|
|
61
|
+
return;
|
|
62
|
+
// Fire-and-forget — History._reducing guard prevents re-entrancy
|
|
63
|
+
void history.reduce(autoReduceWhen);
|
|
64
|
+
},
|
|
65
|
+
async reduce(entries, options) {
|
|
66
|
+
const { maxTokens, maxEntries, olderThan } = options;
|
|
67
|
+
// Separate system entries — always preserved verbatim
|
|
68
|
+
const systemEntries = entries.filter((e) => e.role === "system");
|
|
69
|
+
const nonSystemEntries = entries.filter((e) => e.role !== "system");
|
|
70
|
+
if (nonSystemEntries.length === 0)
|
|
71
|
+
return entries;
|
|
72
|
+
// Determine which non-system entries to compress
|
|
73
|
+
let toCompress = [];
|
|
74
|
+
if (olderThan) {
|
|
75
|
+
const cutoff = olderThan.toISOString();
|
|
76
|
+
toCompress = nonSystemEntries.filter((e) => e.__metadata.date < cutoff);
|
|
77
|
+
}
|
|
78
|
+
else if (maxEntries !== undefined) {
|
|
79
|
+
const totalNonSystem = nonSystemEntries.length;
|
|
80
|
+
if (totalNonSystem <= maxEntries)
|
|
81
|
+
return entries;
|
|
82
|
+
toCompress = nonSystemEntries.slice(0, totalNonSystem - maxEntries);
|
|
83
|
+
}
|
|
84
|
+
else if (maxTokens !== undefined) {
|
|
85
|
+
// Accumulate from oldest until we'd fit within budget
|
|
86
|
+
let totalTokens = entries.reduce((sum, e) => sum + e.__metadata.estimatedTokens, 0);
|
|
87
|
+
let i = 0;
|
|
88
|
+
while (totalTokens > maxTokens && i < nonSystemEntries.length) {
|
|
89
|
+
toCompress.push(nonSystemEntries[i]);
|
|
90
|
+
totalTokens -= nonSystemEntries[i].__metadata.estimatedTokens;
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (toCompress.length === 0)
|
|
95
|
+
return entries;
|
|
96
|
+
// Build prompt — include existing rolling summary as prior context
|
|
97
|
+
const existingSummary = toCompress.find((e) => e.__metadata.isSummary);
|
|
98
|
+
const rawToCompress = toCompress.filter((e) => !e.__metadata.isSummary);
|
|
99
|
+
let prompt = "Produce a concise summary of the following conversation. " +
|
|
100
|
+
"Preserve key facts, decisions, and outcomes. " +
|
|
101
|
+
"Omit filler and repetition.\n\n";
|
|
102
|
+
if (existingSummary) {
|
|
103
|
+
const summaryText = existingSummary.content
|
|
104
|
+
.filter(types_1.isTextContent)
|
|
105
|
+
.map((c) => c.text)
|
|
106
|
+
.join("\n");
|
|
107
|
+
prompt += `Prior context:\n${summaryText}\n\nAdditional turns to incorporate:\n`;
|
|
108
|
+
}
|
|
109
|
+
for (const entry of rawToCompress) {
|
|
110
|
+
const lines = entry.content
|
|
111
|
+
.filter(types_1.isTextContent)
|
|
112
|
+
.map((c) => c.text)
|
|
113
|
+
.join(" ");
|
|
114
|
+
prompt += `[${entry.role}]: ${lines}\n`;
|
|
115
|
+
}
|
|
116
|
+
const summaryText = await agent.execute(prompt);
|
|
117
|
+
// Determine date range covered by this summary
|
|
118
|
+
const allCompressed = existingSummary
|
|
119
|
+
? [existingSummary, ...rawToCompress]
|
|
120
|
+
: rawToCompress;
|
|
121
|
+
const earliestDate = existingSummary?.__metadata.coversRange?.from ??
|
|
122
|
+
allCompressed[0].__metadata.date;
|
|
123
|
+
const latestDate = allCompressed[allCompressed.length - 1].__metadata.date;
|
|
124
|
+
const content = `[Earlier conversation summary: ${summaryText}]`;
|
|
125
|
+
const __metadata = {
|
|
126
|
+
date: new Date().toISOString(),
|
|
127
|
+
contentLength: content.length,
|
|
128
|
+
estimatedTokens: Math.ceil(content.length / 4),
|
|
129
|
+
isSummary: true,
|
|
130
|
+
coversRange: { from: earliestDate, to: latestDate },
|
|
131
|
+
};
|
|
132
|
+
const summaryEntry = {
|
|
133
|
+
role: "user",
|
|
134
|
+
content: [(0, types_1.text)(content)],
|
|
135
|
+
__metadata,
|
|
136
|
+
};
|
|
137
|
+
const toCompressSet = new Set(toCompress);
|
|
138
|
+
const recent = nonSystemEntries.filter((e) => !toCompressSet.has(e));
|
|
139
|
+
return [...systemEntries, summaryEntry, ...recent];
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=compressionPlugin.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toolResultMaskingPlugin = exports.compressionPlugin = void 0;
|
|
4
|
+
var compressionPlugin_1 = require("./compressionPlugin");
|
|
5
|
+
Object.defineProperty(exports, "compressionPlugin", { enumerable: true, get: function () { return compressionPlugin_1.compressionPlugin; } });
|
|
6
|
+
var toolResultMaskingPlugin_1 = require("./toolResultMaskingPlugin");
|
|
7
|
+
Object.defineProperty(exports, "toolResultMaskingPlugin", { enumerable: true, get: function () { return toolResultMaskingPlugin_1.toolResultMaskingPlugin; } });
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Tool } from "../../tools/Tool";
|
|
2
|
+
import type { HistoryPlugin } from "../History";
|
|
3
|
+
export type ToolResultMaskingOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Number of most-recent tool results to keep verbatim.
|
|
6
|
+
* Older results are replaced with a reference marker.
|
|
7
|
+
* @default 2
|
|
8
|
+
*/
|
|
9
|
+
keepRecentResults?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Tool names that are never masked, regardless of age or size.
|
|
12
|
+
* Mutually exclusive with `include`.
|
|
13
|
+
*/
|
|
14
|
+
exclude?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* When set, only results from these tools are masked.
|
|
17
|
+
* Mutually exclusive with `exclude`.
|
|
18
|
+
*/
|
|
19
|
+
include?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Skip masking for results whose estimated token count is below this
|
|
22
|
+
* threshold. Small results don't consume a `keepRecentResults` slot.
|
|
23
|
+
*/
|
|
24
|
+
minTokensToMask?: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A HistoryPlugin with an attached `retrieveTool`.
|
|
28
|
+
* Register the plugin with `history.use(maskingPlugin)`, then add
|
|
29
|
+
* `maskingPlugin.retrieveTool` to the agent's tool list so the model can
|
|
30
|
+
* fetch masked results on demand.
|
|
31
|
+
*/
|
|
32
|
+
export type ToolResultMaskingPlugin = HistoryPlugin & {
|
|
33
|
+
/** Tool the agent can call to retrieve a masked result by its tool_use_id. */
|
|
34
|
+
readonly retrieveTool: Tool<string>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Creates a read-time tool result masking plugin.
|
|
38
|
+
*
|
|
39
|
+
* Old tool results are replaced with a reference marker `[MASKED - ref: <id>]`
|
|
40
|
+
* at read time via `history.getEntries()`. Stored entries are never mutated —
|
|
41
|
+
* the full content is always available via `history.getToolResult(id)` or the
|
|
42
|
+
* attached `retrieveTool`.
|
|
43
|
+
*
|
|
44
|
+
* Only results that pass the include/exclude/minTokensToMask filters are
|
|
45
|
+
* candidates for masking. Filtered-out results stay verbatim and do not
|
|
46
|
+
* consume a `keepRecentResults` slot.
|
|
47
|
+
*
|
|
48
|
+
* @throws If both `exclude` and `include` are provided.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const maskingPlugin = toolResultMaskingPlugin({
|
|
53
|
+
* keepRecentResults: 2,
|
|
54
|
+
* exclude: ["calculator", "get_date"],
|
|
55
|
+
* minTokensToMask: 50,
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* history.use(maskingPlugin);
|
|
59
|
+
*
|
|
60
|
+
* const agent = new ClaudeAgent({
|
|
61
|
+
* tools: [maskingPlugin.retrieveTool, ...otherTools],
|
|
62
|
+
* }, history);
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function toolResultMaskingPlugin(options?: ToolResultMaskingOptions): ToolResultMaskingPlugin;
|
|
66
|
+
//# sourceMappingURL=toolResultMaskingPlugin.d.ts.map
|