@compilr-dev/agents 0.3.11 → 0.3.12
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/agent.d.ts +23 -0
- package/dist/agent.js +41 -7
- package/dist/anchors/manager.js +3 -2
- package/dist/context/delegated-result-store.d.ts +67 -0
- package/dist/context/delegated-result-store.js +99 -0
- package/dist/context/delegation-types.d.ts +82 -0
- package/dist/context/delegation-types.js +18 -0
- package/dist/context/index.d.ts +6 -0
- package/dist/context/index.js +4 -0
- package/dist/context/manager.js +12 -32
- package/dist/context/tool-result-delegator.d.ts +63 -0
- package/dist/context/tool-result-delegator.js +305 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +9 -3
- package/dist/memory/loader.js +2 -1
- package/dist/memory/types.d.ts +1 -1
- package/dist/providers/claude.d.ts +1 -5
- package/dist/providers/claude.js +3 -28
- package/dist/providers/gemini-native.d.ts +1 -1
- package/dist/providers/gemini-native.js +3 -24
- package/dist/providers/mock.d.ts +1 -1
- package/dist/providers/mock.js +3 -24
- package/dist/providers/openai-compatible.d.ts +1 -5
- package/dist/providers/openai-compatible.js +3 -28
- package/dist/rate-limit/provider-wrapper.d.ts +1 -1
- package/dist/rate-limit/provider-wrapper.js +3 -27
- package/dist/tools/builtin/index.d.ts +2 -0
- package/dist/tools/builtin/index.js +2 -0
- package/dist/tools/builtin/recall-result.d.ts +29 -0
- package/dist/tools/builtin/recall-result.js +48 -0
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/tokenizer.d.ts +18 -0
- package/dist/utils/tokenizer.js +56 -0
- package/package.json +3 -2
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Result Delegator
|
|
3
|
+
*
|
|
4
|
+
* Intercepts large tool results via the AfterToolHook mechanism,
|
|
5
|
+
* stores the full result for optional recall, and replaces it
|
|
6
|
+
* with a compact summary to conserve context tokens.
|
|
7
|
+
*/
|
|
8
|
+
import { DEFAULT_DELEGATION_CONFIG } from './delegation-types.js';
|
|
9
|
+
import { DelegatedResultStore } from './delegated-result-store.js';
|
|
10
|
+
import { countTokens } from '../utils/tokenizer.js';
|
|
11
|
+
/**
|
|
12
|
+
* System prompt for LLM-based summarization.
|
|
13
|
+
*/
|
|
14
|
+
const SUMMARIZATION_SYSTEM_PROMPT = 'You are a tool result summarizer. Compress this tool output preserving:\n' +
|
|
15
|
+
'- ALL errors/warnings verbatim\n' +
|
|
16
|
+
'- File paths, function names, line numbers, counts\n' +
|
|
17
|
+
'- Overall structure (sections, item counts)\n' +
|
|
18
|
+
'- First few items of lists verbatim\n' +
|
|
19
|
+
'Drop: verbose repetition, successful routine output, function bodies.\n' +
|
|
20
|
+
'Never add information not in the original.\n' +
|
|
21
|
+
'Respond with ONLY the summary, no preamble.';
|
|
22
|
+
/**
|
|
23
|
+
* Core class that creates an AfterToolHook for auto-delegating large tool results.
|
|
24
|
+
*/
|
|
25
|
+
export class ToolResultDelegator {
|
|
26
|
+
store;
|
|
27
|
+
config;
|
|
28
|
+
provider;
|
|
29
|
+
onEvent;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.provider = options.provider;
|
|
32
|
+
this.config = { ...DEFAULT_DELEGATION_CONFIG, ...options.config };
|
|
33
|
+
this.onEvent = options.onEvent;
|
|
34
|
+
this.store = new DelegatedResultStore({
|
|
35
|
+
maxSize: this.config.maxStoredResults,
|
|
36
|
+
defaultTTL: this.config.resultTTL,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns an AfterToolHook to register with HooksManager.
|
|
41
|
+
*/
|
|
42
|
+
createHook() {
|
|
43
|
+
const getToolConfig = this.getToolConfig.bind(this);
|
|
44
|
+
const delegateResult = this.delegateResult.bind(this);
|
|
45
|
+
return (context) => {
|
|
46
|
+
// Skip failed results — errors should always pass through
|
|
47
|
+
if (!context.result.success)
|
|
48
|
+
return undefined;
|
|
49
|
+
// Never re-delegate recall_full_result — that would create a loop
|
|
50
|
+
if (context.toolName === 'recall_full_result')
|
|
51
|
+
return undefined;
|
|
52
|
+
// Check if delegation is enabled for this tool
|
|
53
|
+
const toolConfig = getToolConfig(context.toolName);
|
|
54
|
+
if (!toolConfig.enabled)
|
|
55
|
+
return undefined;
|
|
56
|
+
// Serialize the result content
|
|
57
|
+
const content = typeof context.result.result === 'string'
|
|
58
|
+
? context.result.result
|
|
59
|
+
: JSON.stringify(context.result.result);
|
|
60
|
+
// Count tokens
|
|
61
|
+
const tokens = countTokens(content);
|
|
62
|
+
// Below threshold — pass through unchanged
|
|
63
|
+
if (tokens <= toolConfig.threshold)
|
|
64
|
+
return undefined;
|
|
65
|
+
// Delegate asynchronously
|
|
66
|
+
return delegateResult(context, content, tokens, toolConfig);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Perform the actual delegation (async path).
|
|
71
|
+
*/
|
|
72
|
+
async delegateResult(context, content, tokens, toolConfig) {
|
|
73
|
+
// Generate delegation ID
|
|
74
|
+
const id = this.store.generateId();
|
|
75
|
+
this.onEvent?.({
|
|
76
|
+
type: 'delegation:started',
|
|
77
|
+
toolName: context.toolName,
|
|
78
|
+
originalTokens: tokens,
|
|
79
|
+
delegationId: id,
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
// Determine strategy
|
|
83
|
+
const strategy = toolConfig.strategy === 'auto' ? 'extractive' : toolConfig.strategy;
|
|
84
|
+
let summary;
|
|
85
|
+
let usedStrategy = strategy;
|
|
86
|
+
if (strategy === 'llm') {
|
|
87
|
+
const llmSummary = await this.summarizeLLM(content, context.toolName);
|
|
88
|
+
if (llmSummary !== null) {
|
|
89
|
+
summary = llmSummary;
|
|
90
|
+
usedStrategy = 'llm';
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// LLM failed, fall back to extractive
|
|
94
|
+
summary = this.summarizeExtractive(content);
|
|
95
|
+
usedStrategy = 'extractive';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (toolConfig.strategy === 'auto') {
|
|
99
|
+
// Auto: try LLM first, fall back to extractive
|
|
100
|
+
const llmSummary = await this.summarizeLLM(content, context.toolName);
|
|
101
|
+
if (llmSummary !== null) {
|
|
102
|
+
summary = llmSummary;
|
|
103
|
+
usedStrategy = 'llm';
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
summary = this.summarizeExtractive(content);
|
|
107
|
+
usedStrategy = 'extractive';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
summary = this.summarizeExtractive(content);
|
|
112
|
+
usedStrategy = 'extractive';
|
|
113
|
+
}
|
|
114
|
+
const summaryTokens = countTokens(summary);
|
|
115
|
+
// Store the full result
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
const stored = {
|
|
118
|
+
id,
|
|
119
|
+
toolName: context.toolName,
|
|
120
|
+
toolInput: context.input,
|
|
121
|
+
fullContent: content,
|
|
122
|
+
fullTokens: tokens,
|
|
123
|
+
summary,
|
|
124
|
+
summaryTokens,
|
|
125
|
+
storedAt: now,
|
|
126
|
+
expiresAt: now + this.config.resultTTL,
|
|
127
|
+
};
|
|
128
|
+
this.store.add(stored);
|
|
129
|
+
this.onEvent?.({
|
|
130
|
+
type: 'delegation:completed',
|
|
131
|
+
toolName: context.toolName,
|
|
132
|
+
originalTokens: tokens,
|
|
133
|
+
summaryTokens,
|
|
134
|
+
delegationId: id,
|
|
135
|
+
strategy: usedStrategy,
|
|
136
|
+
});
|
|
137
|
+
// Return modified result with summary
|
|
138
|
+
// The format must be unmistakable so the LLM never confuses
|
|
139
|
+
// a delegated summary with the actual tool output.
|
|
140
|
+
return {
|
|
141
|
+
result: {
|
|
142
|
+
success: true,
|
|
143
|
+
result: {
|
|
144
|
+
_delegated: true,
|
|
145
|
+
_delegationId: id,
|
|
146
|
+
_originalTokens: tokens,
|
|
147
|
+
_summaryTokens: summaryTokens,
|
|
148
|
+
_warning: 'THIS IS A SUMMARY, NOT THE FULL RESULT. You did not see the full output.',
|
|
149
|
+
summary,
|
|
150
|
+
recall: `To get the full output, call recall_full_result with id="${id}"`,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
this.onEvent?.({
|
|
157
|
+
type: 'delegation:failed',
|
|
158
|
+
toolName: context.toolName,
|
|
159
|
+
error: error instanceof Error ? error.message : String(error),
|
|
160
|
+
});
|
|
161
|
+
// On failure, return original result unchanged
|
|
162
|
+
return { result: context.result };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Access the store (needed by recall_full_result tool).
|
|
167
|
+
*/
|
|
168
|
+
getStore() {
|
|
169
|
+
return this.store;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the resolved config for a specific tool.
|
|
173
|
+
*/
|
|
174
|
+
getToolConfig(toolName) {
|
|
175
|
+
const override = this.config.toolOverrides?.[toolName];
|
|
176
|
+
return {
|
|
177
|
+
enabled: override?.enabled ?? this.config.enabled,
|
|
178
|
+
threshold: override?.threshold ?? this.config.delegationThreshold,
|
|
179
|
+
strategy: override?.strategy ?? this.config.strategy,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Extractive summarization: first/last lines + prioritized structural markers.
|
|
184
|
+
* No LLM cost, fast, preserves actual code signatures.
|
|
185
|
+
*/
|
|
186
|
+
summarizeExtractive(content) {
|
|
187
|
+
const lines = content.split('\n');
|
|
188
|
+
const totalLines = lines.length;
|
|
189
|
+
if (totalLines <= 40) {
|
|
190
|
+
// Small enough to include mostly as-is
|
|
191
|
+
return content;
|
|
192
|
+
}
|
|
193
|
+
const parts = [];
|
|
194
|
+
// Header: makes it impossible to confuse with full content
|
|
195
|
+
parts.push(`[SUMMARIZED — showing first 20 + last 10 of ${String(totalLines)} lines, plus key signatures. Use recall_full_result for complete output.]`);
|
|
196
|
+
parts.push('');
|
|
197
|
+
// First 20 lines
|
|
198
|
+
parts.push(...lines.slice(0, 20));
|
|
199
|
+
// Extract structural markers from the middle, prioritized by importance
|
|
200
|
+
const middleLines = lines.slice(20, -10);
|
|
201
|
+
// High priority: exports, class/interface/function declarations with signatures
|
|
202
|
+
const highPriority = /^\s*export\s+(default\s+)?(async\s+)?(function|class|interface|type|const|enum)\s/;
|
|
203
|
+
// Medium priority: non-exported declarations, method signatures
|
|
204
|
+
const medPriority = /^\s*(async\s+)?(function|class|interface|type|const|enum)\s|^\s+(async\s+)?(\w+)\s*\([^)]*\)\s*[:{]/;
|
|
205
|
+
// Low priority: errors, headings, section markers
|
|
206
|
+
const lowPriority = /^\s*(ERROR|WARN|FAIL|error|warning|#{1,6}\s|---{3,}|\*{3,}|={3,}|\/\/\s*={3,})/;
|
|
207
|
+
const high = [];
|
|
208
|
+
const med = [];
|
|
209
|
+
const low = [];
|
|
210
|
+
for (const line of middleLines) {
|
|
211
|
+
if (highPriority.test(line)) {
|
|
212
|
+
high.push(line);
|
|
213
|
+
}
|
|
214
|
+
else if (medPriority.test(line)) {
|
|
215
|
+
med.push(line);
|
|
216
|
+
}
|
|
217
|
+
else if (lowPriority.test(line)) {
|
|
218
|
+
low.push(line);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Budget: scale markers with file size, cap at 40
|
|
222
|
+
const totalMarkers = high.length + med.length + low.length;
|
|
223
|
+
const budget = Math.min(40, Math.max(15, Math.floor(totalLines / 80)));
|
|
224
|
+
// Fill budget: all high, then medium, then low
|
|
225
|
+
const selected = [];
|
|
226
|
+
for (const line of high) {
|
|
227
|
+
if (selected.length >= budget)
|
|
228
|
+
break;
|
|
229
|
+
selected.push(line);
|
|
230
|
+
}
|
|
231
|
+
for (const line of med) {
|
|
232
|
+
if (selected.length >= budget)
|
|
233
|
+
break;
|
|
234
|
+
selected.push(line);
|
|
235
|
+
}
|
|
236
|
+
for (const line of low) {
|
|
237
|
+
if (selected.length >= budget)
|
|
238
|
+
break;
|
|
239
|
+
selected.push(line);
|
|
240
|
+
}
|
|
241
|
+
if (selected.length > 0) {
|
|
242
|
+
parts.push('');
|
|
243
|
+
parts.push(`... (${String(middleLines.length)} lines omitted, ${String(totalMarkers)} structural markers found, showing ${String(selected.length)} key signatures) ...`);
|
|
244
|
+
parts.push(...selected);
|
|
245
|
+
if (totalMarkers > selected.length) {
|
|
246
|
+
parts.push(`... (${String(totalMarkers - selected.length)} more markers omitted) ...`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
parts.push('');
|
|
251
|
+
parts.push(`... (${String(middleLines.length)} lines omitted) ...`);
|
|
252
|
+
}
|
|
253
|
+
// Last 10 lines
|
|
254
|
+
parts.push('');
|
|
255
|
+
parts.push(...lines.slice(-10));
|
|
256
|
+
parts.push('');
|
|
257
|
+
parts.push(`[Total: ${String(totalLines)} lines]`);
|
|
258
|
+
return parts.join('\n');
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* LLM-based summarization using the provider.
|
|
262
|
+
* Returns null if the LLM call fails.
|
|
263
|
+
*/
|
|
264
|
+
async summarizeLLM(content, toolName) {
|
|
265
|
+
try {
|
|
266
|
+
const messages = [
|
|
267
|
+
{
|
|
268
|
+
role: 'system',
|
|
269
|
+
content: SUMMARIZATION_SYSTEM_PROMPT,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
role: 'user',
|
|
273
|
+
content: `Summarize this ${toolName} tool result (keep under ${String(this.config.summaryMaxTokens)} tokens):\n\n${content}`,
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
let text = '';
|
|
277
|
+
for await (const chunk of this.provider.chat(messages, {
|
|
278
|
+
maxTokens: this.config.summaryMaxTokens,
|
|
279
|
+
temperature: 0,
|
|
280
|
+
model: undefined, // Use the provider's default model
|
|
281
|
+
})) {
|
|
282
|
+
if (chunk.type === 'text' && chunk.text) {
|
|
283
|
+
text += chunk.text;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return text.trim() || null;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* System prompt addition when delegation is enabled.
|
|
295
|
+
* Append to the agent's system prompt.
|
|
296
|
+
*/
|
|
297
|
+
export const DELEGATION_SYSTEM_PROMPT = '\n\n## Tool Result Delegation\n' +
|
|
298
|
+
'Some tool results are automatically summarized to conserve context. When a result has `_delegated: true`, ' +
|
|
299
|
+
'you received a SUMMARY, not the full output. You MUST acknowledge this — never claim you read the full content.\n' +
|
|
300
|
+
'Delegated results include:\n' +
|
|
301
|
+
'- `_warning`: confirms this is a summary\n' +
|
|
302
|
+
'- `summary`: extracted key information\n' +
|
|
303
|
+
'- `recall`: how to get the full output\n' +
|
|
304
|
+
'Use `recall_full_result` with the delegation ID to get the full output when the summary is insufficient.\n' +
|
|
305
|
+
'Only recall when you need specific code, exact values, or the summary indicates omitted content.\n';
|
package/dist/index.d.ts
CHANGED
|
@@ -31,16 +31,16 @@ export { PerplexityProvider, createPerplexityProvider } from './providers/index.
|
|
|
31
31
|
export type { PerplexityProviderConfig } from './providers/index.js';
|
|
32
32
|
export { OpenRouterProvider, createOpenRouterProvider } from './providers/index.js';
|
|
33
33
|
export type { OpenRouterProviderConfig } from './providers/index.js';
|
|
34
|
-
export type { Tool, ToolHandler, ToolRegistry, ToolInputSchema, ToolExecutionResult, ToolRegistryOptions, ToolFallbackHandler, DefineToolOptions, ReadFileInput, WriteFileInput, BashInput, BashResult, FifoDetectionResult, GrepInput, GlobInput, EditInput, TodoWriteInput, TodoReadInput, TodoItem, TodoStatus, TodoContextCleanupOptions, TaskInput, TaskResult, AgentTypeConfig, TaskToolOptions, ContextMode, ThoroughnessLevel, SubAgentEventInfo, SuggestInput, SuggestToolOptions, } from './tools/index.js';
|
|
34
|
+
export type { Tool, ToolHandler, ToolRegistry, ToolInputSchema, ToolExecutionResult, ToolRegistryOptions, ToolFallbackHandler, DefineToolOptions, ReadFileInput, WriteFileInput, BashInput, BashResult, FifoDetectionResult, GrepInput, GlobInput, EditInput, TodoWriteInput, TodoReadInput, TodoItem, TodoStatus, TodoContextCleanupOptions, TaskInput, TaskResult, AgentTypeConfig, TaskToolOptions, ContextMode, ThoroughnessLevel, SubAgentEventInfo, SuggestInput, SuggestToolOptions, RecallResultInput, RecallResultToolOptions, } from './tools/index.js';
|
|
35
35
|
export { defineTool, createSuccessResult, createErrorResult, wrapToolExecute, DefaultToolRegistry, createToolRegistry, } from './tools/index.js';
|
|
36
|
-
export { readFileTool, createReadFileTool, writeFileTool, createWriteFileTool, bashTool, createBashTool, execStream, detectFifoUsage, bashOutputTool, createBashOutputTool, killShellTool, createKillShellTool, ShellManager, getDefaultShellManager, setDefaultShellManager, grepTool, createGrepTool, globTool, createGlobTool, editTool, createEditTool, todoWriteTool, todoReadTool, createTodoTools, TodoStore, resetDefaultTodoStore, getDefaultTodoStore, createIsolatedTodoStore, cleanupTodoContextMessages, getTodoContextStats, webFetchTool, createWebFetchTool, createTaskTool, defaultAgentTypes, suggestTool, createSuggestTool, builtinTools, allBuiltinTools, TOOL_NAMES, TOOL_SETS, } from './tools/index.js';
|
|
36
|
+
export { readFileTool, createReadFileTool, writeFileTool, createWriteFileTool, bashTool, createBashTool, execStream, detectFifoUsage, bashOutputTool, createBashOutputTool, killShellTool, createKillShellTool, ShellManager, getDefaultShellManager, setDefaultShellManager, grepTool, createGrepTool, globTool, createGlobTool, editTool, createEditTool, todoWriteTool, todoReadTool, createTodoTools, TodoStore, resetDefaultTodoStore, getDefaultTodoStore, createIsolatedTodoStore, cleanupTodoContextMessages, getTodoContextStats, webFetchTool, createWebFetchTool, createTaskTool, defaultAgentTypes, suggestTool, createSuggestTool, createRecallResultTool, builtinTools, allBuiltinTools, TOOL_NAMES, TOOL_SETS, } from './tools/index.js';
|
|
37
37
|
export { userMessage, assistantMessage, systemMessage, textBlock, toolUseBlock, toolResultBlock, getTextContent, getToolUses, getToolResults, hasToolUses, validateToolUseResultPairing, repairToolPairing, ensureMessageContent, normalizeMessages, } from './messages/index.js';
|
|
38
38
|
export type { ToolPairingValidation } from './messages/index.js';
|
|
39
|
-
export { generateId, sleep, retry, truncate, withRetryGenerator, calculateBackoffDelay, DEFAULT_RETRY_CONFIG, } from './utils/index.js';
|
|
39
|
+
export { generateId, sleep, retry, truncate, withRetryGenerator, calculateBackoffDelay, DEFAULT_RETRY_CONFIG, countTokens, countMessageTokens, } from './utils/index.js';
|
|
40
40
|
export type { RetryConfig as LLMRetryConfig, WithRetryOptions } from './utils/index.js';
|
|
41
41
|
export { AgentError, ProviderError, ToolError, ToolTimeoutError, ToolLoopError, ValidationError, MaxIterationsError, AbortError, ContextOverflowError, isAgentError, isProviderError, isToolError, isToolTimeoutError, isToolLoopError, isContextOverflowError, wrapError, } from './errors.js';
|
|
42
|
-
export { ContextManager, DEFAULT_CONTEXT_CONFIG, FileAccessTracker, createFileTrackingHook, TRACKED_TOOLS, } from './context/index.js';
|
|
43
|
-
export type { ContextManagerOptions, ContextCategory, BudgetAllocation, CategoryBudgetInfo, PreflightResult, VerbosityLevel, VerbosityConfig, ContextConfig, FilteringConfig, CompactionConfig, SummarizationConfig, CompactionResult, SummarizationResult, FilteringResult, ContextEvent, ContextEventHandler, ContextStats, FileAccessType, FileAccess, FileAccessTrackerOptions, FormatHintsOptions, FileAccessStats, } from './context/index.js';
|
|
42
|
+
export { ContextManager, DEFAULT_CONTEXT_CONFIG, FileAccessTracker, createFileTrackingHook, TRACKED_TOOLS, DelegatedResultStore, ToolResultDelegator, DELEGATION_SYSTEM_PROMPT, DEFAULT_DELEGATION_CONFIG, } from './context/index.js';
|
|
43
|
+
export type { ContextManagerOptions, ContextCategory, BudgetAllocation, CategoryBudgetInfo, PreflightResult, VerbosityLevel, VerbosityConfig, ContextConfig, FilteringConfig, CompactionConfig, SummarizationConfig, CompactionResult, SummarizationResult, FilteringResult, ContextEvent, ContextEventHandler, ContextStats, FileAccessType, FileAccess, FileAccessTrackerOptions, FormatHintsOptions, FileAccessStats, DelegatedResultStoreStats, ToolResultDelegatorOptions, DelegationConfig, StoredResult, DelegationEvent, } from './context/index.js';
|
|
44
44
|
export { SkillRegistry, defineSkill, createSkillRegistry, builtinSkills, getDefaultSkillRegistry, resetDefaultSkillRegistry, } from './skills/index.js';
|
|
45
45
|
export type { Skill, SkillInvocationResult, SkillInvokeOptions } from './skills/index.js';
|
|
46
46
|
export { JsonSerializer, CompactJsonSerializer, defaultSerializer, MemoryCheckpointer, FileCheckpointer, StateError, StateErrorCode, CURRENT_STATE_VERSION, } from './state/index.js';
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,9 @@ export { readFileTool, createReadFileTool, writeFileTool, createWriteFileTool, b
|
|
|
29
29
|
// Task tool (sub-agent spawning)
|
|
30
30
|
createTaskTool, defaultAgentTypes,
|
|
31
31
|
// Suggest tool (next action suggestions)
|
|
32
|
-
suggestTool, createSuggestTool,
|
|
32
|
+
suggestTool, createSuggestTool,
|
|
33
|
+
// Recall result tool (delegation)
|
|
34
|
+
createRecallResultTool, builtinTools, allBuiltinTools,
|
|
33
35
|
// Tool names - single source of truth
|
|
34
36
|
TOOL_NAMES, TOOL_SETS, } from './tools/index.js';
|
|
35
37
|
// Message utilities
|
|
@@ -37,11 +39,15 @@ export { userMessage, assistantMessage, systemMessage, textBlock, toolUseBlock,
|
|
|
37
39
|
// Utilities
|
|
38
40
|
export { generateId, sleep,
|
|
39
41
|
// eslint-disable-next-line @typescript-eslint/no-deprecated -- Kept for backward compatibility
|
|
40
|
-
retry, truncate, withRetryGenerator, calculateBackoffDelay, DEFAULT_RETRY_CONFIG,
|
|
42
|
+
retry, truncate, withRetryGenerator, calculateBackoffDelay, DEFAULT_RETRY_CONFIG,
|
|
43
|
+
// Token counting (tiktoken-based)
|
|
44
|
+
countTokens, countMessageTokens, } from './utils/index.js';
|
|
41
45
|
// Errors
|
|
42
46
|
export { AgentError, ProviderError, ToolError, ToolTimeoutError, ToolLoopError, ValidationError, MaxIterationsError, AbortError, ContextOverflowError, isAgentError, isProviderError, isToolError, isToolTimeoutError, isToolLoopError, isContextOverflowError, wrapError, } from './errors.js';
|
|
43
47
|
// Context management
|
|
44
|
-
export { ContextManager, DEFAULT_CONTEXT_CONFIG, FileAccessTracker, createFileTrackingHook, TRACKED_TOOLS,
|
|
48
|
+
export { ContextManager, DEFAULT_CONTEXT_CONFIG, FileAccessTracker, createFileTrackingHook, TRACKED_TOOLS,
|
|
49
|
+
// Tool result delegation
|
|
50
|
+
DelegatedResultStore, ToolResultDelegator, DELEGATION_SYSTEM_PROMPT, DEFAULT_DELEGATION_CONFIG, } from './context/index.js';
|
|
45
51
|
// Skills system
|
|
46
52
|
export { SkillRegistry, defineSkill, createSkillRegistry, builtinSkills, getDefaultSkillRegistry, resetDefaultSkillRegistry, } from './skills/index.js';
|
|
47
53
|
// State management
|
package/dist/memory/loader.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import * as fs from 'fs/promises';
|
|
27
27
|
import * as path from 'path';
|
|
28
|
+
import { countTokens } from '../utils/tokenizer.js';
|
|
28
29
|
/**
|
|
29
30
|
* Built-in patterns for various LLM providers
|
|
30
31
|
*/
|
|
@@ -268,7 +269,7 @@ export class ProjectMemoryLoader {
|
|
|
268
269
|
files,
|
|
269
270
|
content,
|
|
270
271
|
rootDir: absoluteRoot,
|
|
271
|
-
estimatedTokens:
|
|
272
|
+
estimatedTokens: countTokens(content),
|
|
272
273
|
};
|
|
273
274
|
this.emit({ type: 'memory:search_complete', memory });
|
|
274
275
|
return memory;
|
package/dist/memory/types.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface ProjectMemory {
|
|
|
41
41
|
content: string;
|
|
42
42
|
/** The root directory where search started */
|
|
43
43
|
rootDir: string;
|
|
44
|
-
/** Total token estimate (
|
|
44
|
+
/** Total token estimate (tiktoken cl100k_base) */
|
|
45
45
|
estimatedTokens: number;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
@@ -61,11 +61,7 @@ export declare class ClaudeProvider implements LLMProvider {
|
|
|
61
61
|
*/
|
|
62
62
|
chat(messages: Message[], options?: ChatOptions): AsyncIterable<StreamChunk>;
|
|
63
63
|
/**
|
|
64
|
-
* Count tokens in messages
|
|
65
|
-
*
|
|
66
|
-
* Note: Token counting API is available via Anthropic's beta endpoints
|
|
67
|
-
* but not yet exposed in the stable SDK. For now, this provides an
|
|
68
|
-
* approximation based on character count.
|
|
64
|
+
* Count tokens in messages using tiktoken (cl100k_base encoding)
|
|
69
65
|
*/
|
|
70
66
|
countTokens(messages: Message[]): Promise<number>;
|
|
71
67
|
/**
|
package/dist/providers/claude.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
13
|
import Anthropic from '@anthropic-ai/sdk';
|
|
14
|
+
import { countMessageTokens } from '../utils/tokenizer.js';
|
|
14
15
|
import { ProviderError } from '../errors.js';
|
|
15
16
|
/**
|
|
16
17
|
* Default model for Claude API
|
|
@@ -122,36 +123,10 @@ export class ClaudeProvider {
|
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
/**
|
|
125
|
-
* Count tokens in messages
|
|
126
|
-
*
|
|
127
|
-
* Note: Token counting API is available via Anthropic's beta endpoints
|
|
128
|
-
* but not yet exposed in the stable SDK. For now, this provides an
|
|
129
|
-
* approximation based on character count.
|
|
126
|
+
* Count tokens in messages using tiktoken (cl100k_base encoding)
|
|
130
127
|
*/
|
|
131
128
|
countTokens(messages) {
|
|
132
|
-
|
|
133
|
-
let charCount = 0;
|
|
134
|
-
for (const msg of messages) {
|
|
135
|
-
if (typeof msg.content === 'string') {
|
|
136
|
-
charCount += msg.content.length;
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
for (const block of msg.content) {
|
|
140
|
-
switch (block.type) {
|
|
141
|
-
case 'text':
|
|
142
|
-
charCount += block.text.length;
|
|
143
|
-
break;
|
|
144
|
-
case 'tool_use':
|
|
145
|
-
charCount += JSON.stringify(block.input).length;
|
|
146
|
-
break;
|
|
147
|
-
case 'tool_result':
|
|
148
|
-
charCount += block.content.length;
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return Promise.resolve(Math.ceil(charCount / 4));
|
|
129
|
+
return Promise.resolve(countMessageTokens(messages));
|
|
155
130
|
}
|
|
156
131
|
/**
|
|
157
132
|
* Convert our Message format to Anthropic's format
|
|
@@ -52,7 +52,7 @@ export declare class GeminiNativeProvider implements LLMProvider {
|
|
|
52
52
|
*/
|
|
53
53
|
chat(messages: Message[], options?: ChatOptions): AsyncIterable<StreamChunk>;
|
|
54
54
|
/**
|
|
55
|
-
* Count tokens in messages (
|
|
55
|
+
* Count tokens in messages using tiktoken (cl100k_base encoding)
|
|
56
56
|
*/
|
|
57
57
|
countTokens(messages: Message[]): Promise<number>;
|
|
58
58
|
/**
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { GoogleGenAI } from '@google/genai';
|
|
17
17
|
import { ProviderError } from '../errors.js';
|
|
18
|
+
import { countMessageTokens } from '../utils/tokenizer.js';
|
|
18
19
|
/**
|
|
19
20
|
* Default model for Gemini API
|
|
20
21
|
*/
|
|
@@ -137,32 +138,10 @@ export class GeminiNativeProvider {
|
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
/**
|
|
140
|
-
* Count tokens in messages (
|
|
141
|
+
* Count tokens in messages using tiktoken (cl100k_base encoding)
|
|
141
142
|
*/
|
|
142
143
|
countTokens(messages) {
|
|
143
|
-
|
|
144
|
-
let charCount = 0;
|
|
145
|
-
for (const msg of messages) {
|
|
146
|
-
if (typeof msg.content === 'string') {
|
|
147
|
-
charCount += msg.content.length;
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
for (const block of msg.content) {
|
|
151
|
-
switch (block.type) {
|
|
152
|
-
case 'text':
|
|
153
|
-
charCount += block.text.length;
|
|
154
|
-
break;
|
|
155
|
-
case 'tool_use':
|
|
156
|
-
charCount += JSON.stringify(block.input).length;
|
|
157
|
-
break;
|
|
158
|
-
case 'tool_result':
|
|
159
|
-
charCount += block.content.length;
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return Promise.resolve(Math.ceil(charCount / 4));
|
|
144
|
+
return Promise.resolve(countMessageTokens(messages));
|
|
166
145
|
}
|
|
167
146
|
/**
|
|
168
147
|
* Convert our Message format to Google's format
|
package/dist/providers/mock.d.ts
CHANGED
|
@@ -122,7 +122,7 @@ export declare class MockProvider implements LLMProvider {
|
|
|
122
122
|
*/
|
|
123
123
|
chat(messages: Message[], options?: ChatOptions): AsyncIterable<StreamChunk>;
|
|
124
124
|
/**
|
|
125
|
-
* Count tokens (
|
|
125
|
+
* Count tokens using tiktoken (cl100k_base encoding)
|
|
126
126
|
*/
|
|
127
127
|
countTokens(messages: Message[]): Promise<number>;
|
|
128
128
|
private sleep;
|
package/dist/providers/mock.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
27
|
import { ProviderError } from '../errors.js';
|
|
28
|
+
import { countMessageTokens } from '../utils/tokenizer.js';
|
|
28
29
|
/**
|
|
29
30
|
* MockProvider for testing agents without API calls.
|
|
30
31
|
*
|
|
@@ -171,32 +172,10 @@ export class MockProvider {
|
|
|
171
172
|
yield { type: 'done' };
|
|
172
173
|
}
|
|
173
174
|
/**
|
|
174
|
-
* Count tokens (
|
|
175
|
+
* Count tokens using tiktoken (cl100k_base encoding)
|
|
175
176
|
*/
|
|
176
177
|
countTokens(messages) {
|
|
177
|
-
|
|
178
|
-
let charCount = 0;
|
|
179
|
-
for (const msg of messages) {
|
|
180
|
-
if (typeof msg.content === 'string') {
|
|
181
|
-
charCount += msg.content.length;
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
for (const block of msg.content) {
|
|
185
|
-
if (block.type === 'text') {
|
|
186
|
-
charCount += block.text.length;
|
|
187
|
-
}
|
|
188
|
-
else if (block.type === 'tool_result') {
|
|
189
|
-
// Count tool result content (always a string per our type definition)
|
|
190
|
-
charCount += block.content.length;
|
|
191
|
-
}
|
|
192
|
-
else if (block.type === 'tool_use') {
|
|
193
|
-
// Count tool use input
|
|
194
|
-
charCount += JSON.stringify(block.input).length;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return Promise.resolve(Math.ceil(charCount / 4));
|
|
178
|
+
return Promise.resolve(countMessageTokens(messages));
|
|
200
179
|
}
|
|
201
180
|
sleep(ms) {
|
|
202
181
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -186,11 +186,7 @@ export declare abstract class OpenAICompatibleProvider implements LLMProvider {
|
|
|
186
186
|
arguments: string;
|
|
187
187
|
}>): StreamChunk[];
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* @remarks
|
|
192
|
-
* Most providers don't have a native token counting endpoint.
|
|
193
|
-
* This uses a rough approximation of ~4 characters per token.
|
|
189
|
+
* Count tokens in messages using tiktoken (cl100k_base encoding)
|
|
194
190
|
*/
|
|
195
191
|
countTokens(messages: Message[]): Promise<number>;
|
|
196
192
|
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
import { ProviderError } from '../errors.js';
|
|
25
|
+
import { countMessageTokens } from '../utils/tokenizer.js';
|
|
25
26
|
// Default configuration
|
|
26
27
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
27
28
|
const DEFAULT_TIMEOUT = 120000;
|
|
@@ -349,35 +350,9 @@ export class OpenAICompatibleProvider {
|
|
|
349
350
|
return results;
|
|
350
351
|
}
|
|
351
352
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* @remarks
|
|
355
|
-
* Most providers don't have a native token counting endpoint.
|
|
356
|
-
* This uses a rough approximation of ~4 characters per token.
|
|
353
|
+
* Count tokens in messages using tiktoken (cl100k_base encoding)
|
|
357
354
|
*/
|
|
358
355
|
countTokens(messages) {
|
|
359
|
-
|
|
360
|
-
for (const msg of messages) {
|
|
361
|
-
if (typeof msg.content === 'string') {
|
|
362
|
-
charCount += msg.content.length;
|
|
363
|
-
}
|
|
364
|
-
else if (Array.isArray(msg.content)) {
|
|
365
|
-
for (const block of msg.content) {
|
|
366
|
-
if (block.type === 'text') {
|
|
367
|
-
charCount += block.text.length;
|
|
368
|
-
}
|
|
369
|
-
else if (block.type === 'tool_use') {
|
|
370
|
-
charCount += JSON.stringify(block.input).length;
|
|
371
|
-
}
|
|
372
|
-
else if (block.type === 'tool_result') {
|
|
373
|
-
charCount +=
|
|
374
|
-
typeof block.content === 'string'
|
|
375
|
-
? block.content.length
|
|
376
|
-
: JSON.stringify(block.content).length;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return Promise.resolve(Math.ceil(charCount / 4));
|
|
356
|
+
return Promise.resolve(countMessageTokens(messages));
|
|
382
357
|
}
|
|
383
358
|
}
|
|
@@ -45,7 +45,7 @@ export declare class RateLimitedProvider implements LLMProvider {
|
|
|
45
45
|
*/
|
|
46
46
|
countTokens(messages: Message[]): Promise<number>;
|
|
47
47
|
/**
|
|
48
|
-
* Estimate tokens from messages
|
|
48
|
+
* Estimate tokens from messages using tiktoken
|
|
49
49
|
*/
|
|
50
50
|
private estimateTokens;
|
|
51
51
|
}
|