@tyvm/knowhow 0.0.128 → 0.0.130
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/package.json +5 -4
- package/scripts/cache-miss-debug.ts +365 -0
- package/src/agents/base/base.ts +159 -59
- package/src/agents/tools/execCommand.ts +26 -6
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/list.ts +2 -0
- package/src/agents/tools/minimal/callTool.ts +45 -0
- package/src/agents/tools/minimal/definitions.ts +50 -0
- package/src/agents/tools/minimal/index.ts +3 -0
- package/src/agents/tools/minimal/inspectTools.ts +27 -0
- package/src/agents/tools/skills.ts +79 -0
- package/src/chat/modules/AgentModule.ts +111 -9
- package/src/cli.ts +4 -0
- package/src/clients/anthropic.ts +59 -1
- package/src/clients/contextLimits.ts +9 -0
- package/src/clients/index.ts +6 -0
- package/src/clients/meta.ts +18 -0
- package/src/clients/openai.ts +28 -5
- package/src/clients/pricing/anthropic.ts +13 -0
- package/src/clients/pricing/fireworks.ts +18 -12
- package/src/clients/pricing/index.ts +1 -0
- package/src/clients/pricing/meta.ts +12 -0
- package/src/clients/pricing/openai.ts +16 -0
- package/src/clients/pricing/xai.ts +3 -0
- package/src/clients/types.ts +27 -4
- package/src/clients/xai.ts +2 -2
- package/src/commands/agent.ts +19 -0
- package/src/commands/behaviors.ts +47 -0
- package/src/commands/services.ts +7 -3
- package/src/commands/skills.ts +71 -0
- package/src/index.ts +2 -0
- package/src/plugins/PluginBase.ts +0 -2
- package/src/plugins/SkillsPlugin.ts +11 -124
- package/src/plugins/language.ts +0 -1
- package/src/plugins/plugins.ts +24 -13
- package/src/plugins/types.ts +8 -2
- package/src/processors/MinimalToolsMessageProcessor.ts +113 -0
- package/src/processors/TokenCompressor.ts +21 -4
- package/src/processors/index.ts +7 -1
- package/src/services/AgentSyncFs.ts +49 -0
- package/src/services/AgentSyncKnowhowWeb.ts +1 -1
- package/src/services/BehaviorsService.ts +432 -0
- package/src/services/KnowhowClient.ts +62 -1
- package/src/services/LazyToolsService.ts +12 -0
- package/src/services/MinimalToolsService.ts +167 -0
- package/src/services/SkillsService.ts +622 -0
- package/src/services/Tools.ts +11 -0
- package/src/services/index.ts +9 -2
- package/src/services/modules/types.ts +2 -0
- package/src/worker.ts +7 -0
- package/tests/processors/TokenCompressor.test.ts +45 -0
- package/tests/unit/services/MinimalToolsService.test.ts +424 -0
- package/ts_build/package.json +5 -4
- package/ts_build/src/agents/base/base.d.ts +13 -2
- package/ts_build/src/agents/base/base.js +91 -35
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.d.ts +1 -1
- package/ts_build/src/agents/tools/execCommand.js +18 -5
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +2 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/minimal/callTool.d.ts +5 -0
- package/ts_build/src/agents/tools/minimal/callTool.js +28 -0
- package/ts_build/src/agents/tools/minimal/callTool.js.map +1 -0
- package/ts_build/src/agents/tools/minimal/definitions.d.ts +4 -0
- package/ts_build/src/agents/tools/minimal/definitions.js +46 -0
- package/ts_build/src/agents/tools/minimal/definitions.js.map +1 -0
- package/ts_build/src/agents/tools/minimal/index.d.ts +3 -0
- package/ts_build/src/agents/tools/minimal/index.js +20 -0
- package/ts_build/src/agents/tools/minimal/index.js.map +1 -0
- package/ts_build/src/agents/tools/minimal/inspectTools.d.ts +2 -0
- package/ts_build/src/agents/tools/minimal/inspectTools.js +15 -0
- package/ts_build/src/agents/tools/minimal/inspectTools.js.map +1 -0
- package/ts_build/src/agents/tools/skills.d.ts +33 -0
- package/ts_build/src/agents/tools/skills.js +62 -0
- package/ts_build/src/agents/tools/skills.js.map +1 -0
- package/ts_build/src/chat/modules/AgentModule.d.ts +2 -0
- package/ts_build/src/chat/modules/AgentModule.js +77 -2
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +1 -1
- package/ts_build/src/cli.js +4 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +2 -0
- package/ts_build/src/clients/anthropic.js +35 -0
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/contextLimits.js +8 -0
- package/ts_build/src/clients/contextLimits.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +1 -0
- package/ts_build/src/clients/index.js +6 -0
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/meta.d.ts +4 -0
- package/ts_build/src/clients/meta.js +15 -0
- package/ts_build/src/clients/meta.js.map +1 -0
- package/ts_build/src/clients/openai.d.ts +1 -0
- package/ts_build/src/clients/openai.js +25 -5
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/anthropic.d.ts +4 -0
- package/ts_build/src/clients/pricing/anthropic.js +12 -0
- package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +2 -5
- package/ts_build/src/clients/pricing/fireworks.js +13 -11
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
- package/ts_build/src/clients/pricing/index.d.ts +1 -0
- package/ts_build/src/clients/pricing/index.js +5 -2
- package/ts_build/src/clients/pricing/index.js.map +1 -1
- package/ts_build/src/clients/pricing/meta.d.ts +6 -0
- package/ts_build/src/clients/pricing/meta.js +8 -0
- package/ts_build/src/clients/pricing/meta.js.map +1 -0
- package/ts_build/src/clients/pricing/openai.d.ts +3 -0
- package/ts_build/src/clients/pricing/openai.js +15 -0
- package/ts_build/src/clients/pricing/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/xai.d.ts +1 -0
- package/ts_build/src/clients/pricing/xai.js +3 -0
- package/ts_build/src/clients/pricing/xai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +6 -1
- package/ts_build/src/clients/xai.js +1 -1
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +16 -0
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/behaviors.d.ts +2 -0
- package/ts_build/src/commands/behaviors.js +43 -0
- package/ts_build/src/commands/behaviors.js.map +1 -0
- package/ts_build/src/commands/services.js +4 -2
- package/ts_build/src/commands/services.js.map +1 -1
- package/ts_build/src/commands/skills.d.ts +2 -0
- package/ts_build/src/commands/skills.js +61 -0
- package/ts_build/src/commands/skills.js.map +1 -0
- package/ts_build/src/index.d.ts +2 -0
- package/ts_build/src/index.js +5 -1
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/plugins/PluginBase.d.ts +0 -2
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/SkillsPlugin.d.ts +1 -6
- package/ts_build/src/plugins/SkillsPlugin.js +6 -135
- package/ts_build/src/plugins/SkillsPlugin.js.map +1 -1
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +4 -1
- package/ts_build/src/plugins/plugins.js +17 -11
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/types.d.ts +3 -2
- package/ts_build/src/processors/MinimalToolsMessageProcessor.d.ts +7 -0
- package/ts_build/src/processors/MinimalToolsMessageProcessor.js +61 -0
- package/ts_build/src/processors/MinimalToolsMessageProcessor.js.map +1 -0
- package/ts_build/src/processors/TokenCompressor.js +5 -2
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/index.d.ts +2 -1
- package/ts_build/src/processors/index.js +3 -1
- package/ts_build/src/processors/index.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.d.ts +2 -0
- package/ts_build/src/services/AgentSyncFs.js +30 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -1
- package/ts_build/src/services/AgentSyncKnowhowWeb.js +1 -1
- package/ts_build/src/services/BehaviorsService.d.ts +46 -0
- package/ts_build/src/services/BehaviorsService.js +358 -0
- package/ts_build/src/services/BehaviorsService.js.map +1 -0
- package/ts_build/src/services/KnowhowClient.d.ts +9 -1
- package/ts_build/src/services/KnowhowClient.js +20 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/MinimalToolsService.d.ts +14 -0
- package/ts_build/src/services/MinimalToolsService.js +80 -0
- package/ts_build/src/services/MinimalToolsService.js.map +1 -0
- package/ts_build/src/services/SkillsService.d.ts +53 -0
- package/ts_build/src/services/SkillsService.js +478 -0
- package/ts_build/src/services/SkillsService.js.map +1 -0
- package/ts_build/src/services/Tools.d.ts +2 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/index.d.ts +4 -0
- package/ts_build/src/services/index.js +9 -2
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/index.d.ts +4 -2
- package/ts_build/src/services/modules/types.d.ts +2 -0
- package/ts_build/src/types.d.ts +8 -0
- package/ts_build/src/worker.js +5 -0
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +27 -0
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
- package/ts_build/tests/unit/services/MinimalToolsService.test.d.ts +1 -0
- package/ts_build/tests/unit/services/MinimalToolsService.test.js +320 -0
- package/ts_build/tests/unit/services/MinimalToolsService.test.js.map +1 -0
package/src/agents/base/base.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "events"; // kept for reference; agentEvents now uses EventService
|
|
2
2
|
import {
|
|
3
3
|
CompletionResponse,
|
|
4
|
+
CompletionOptions,
|
|
4
5
|
GenericClient,
|
|
5
6
|
Message,
|
|
6
7
|
MessageContent,
|
|
@@ -26,6 +27,8 @@ export { Message, Tool, ToolCall };
|
|
|
26
27
|
export interface ModelPreference {
|
|
27
28
|
model: string;
|
|
28
29
|
provider: keyof typeof Clients.clients;
|
|
30
|
+
reasoning_effort?: CompletionOptions["reasoning_effort"];
|
|
31
|
+
reasoning_summary?: boolean;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export interface AgentContext {
|
|
@@ -60,6 +63,8 @@ export abstract class BaseAgent implements IAgent {
|
|
|
60
63
|
protected turnCount = 0;
|
|
61
64
|
protected totalCostUsd = 0;
|
|
62
65
|
protected currentThread = 0;
|
|
66
|
+
protected reasoningEffort: CompletionOptions["reasoning_effort"] | undefined = undefined;
|
|
67
|
+
protected summarizeReasoning: boolean | undefined = undefined;
|
|
63
68
|
protected totalInputTokens = 0;
|
|
64
69
|
protected totalOutputTokens = 0;
|
|
65
70
|
protected totalCacheReadTokens = 0;
|
|
@@ -69,7 +74,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
69
74
|
protected compressMinMessages = 30;
|
|
70
75
|
|
|
71
76
|
// Interrupt support: resolves the currently awaited tool call or completion
|
|
72
|
-
private _interruptResolve: ((
|
|
77
|
+
private _interruptResolve: (() => void) | null = null;
|
|
73
78
|
|
|
74
79
|
protected threads = [] as Message[][];
|
|
75
80
|
|
|
@@ -233,6 +238,12 @@ export abstract class BaseAgent implements IAgent {
|
|
|
233
238
|
updatePreferences(value: ModelPreference) {
|
|
234
239
|
this.setModel(value.model);
|
|
235
240
|
this.setProvider(value.provider);
|
|
241
|
+
if (value.reasoning_effort !== undefined) {
|
|
242
|
+
this.reasoningEffort = value.reasoning_effort;
|
|
243
|
+
}
|
|
244
|
+
if (value.reasoning_summary !== undefined) {
|
|
245
|
+
this.summarizeReasoning = value.reasoning_summary;
|
|
246
|
+
}
|
|
236
247
|
}
|
|
237
248
|
|
|
238
249
|
nextModel() {
|
|
@@ -262,6 +273,22 @@ export abstract class BaseAgent implements IAgent {
|
|
|
262
273
|
this.client = null; // Reset client to force re-fetch
|
|
263
274
|
}
|
|
264
275
|
|
|
276
|
+
setReasoningEffort(effort: CompletionOptions["reasoning_effort"]) {
|
|
277
|
+
this.reasoningEffort = effort;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
getReasoningEffort(): CompletionOptions["reasoning_effort"] {
|
|
281
|
+
return this.reasoningEffort;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
setSummarizeReasoning(value: boolean) {
|
|
285
|
+
this.summarizeReasoning = value;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
getSummarizeReasoning(): boolean | undefined {
|
|
289
|
+
return this.summarizeReasoning;
|
|
290
|
+
}
|
|
291
|
+
|
|
265
292
|
getClient() {
|
|
266
293
|
if (!this.client) {
|
|
267
294
|
if (this.provider) {
|
|
@@ -305,14 +332,16 @@ export abstract class BaseAgent implements IAgent {
|
|
|
305
332
|
// Short response (≤ 3 words) that matches a termination word/phrase exactly
|
|
306
333
|
const wordCount = trimmed.split(/\s+/).filter(Boolean).length;
|
|
307
334
|
if (wordCount <= 3) {
|
|
308
|
-
const terminationPattern =
|
|
335
|
+
const terminationPattern =
|
|
336
|
+
/^(done|complete|completed|finished|final\s*answer|task\s*complete|all\s*done|that'?s\s*(all|it)|ok(ay)?|yes)[.!]*$/i;
|
|
309
337
|
if (terminationPattern.test(trimmed)) return true;
|
|
310
338
|
}
|
|
311
339
|
|
|
312
340
|
// Check if the first 1-3 words indicate task completion (for longer responses)
|
|
313
341
|
// e.g. "Task complete: ...", "All done.", "No further changes needed.", "Confirmed complete."
|
|
314
342
|
const firstWords = trimmed.split(/\s+/).slice(0, 3).join(" ");
|
|
315
|
-
const firstWordPattern =
|
|
343
|
+
const firstWordPattern =
|
|
344
|
+
/^(task\s*(complete|completed|done|finished)|all\s*done|no\s*(further|more|additional|changes|action)|confirmed?\s*(complete|done|finished|one\s*last)|nothing\s*(more|further|else)|standing\s*by|everything\s*is|still\s*confirmed|acknowledged|done\s*and|complete\s*(and|\.)|completed\s*successfully|no\s*additional|verified\s*and)/i;
|
|
316
345
|
if (firstWordPattern.test(firstWords)) return true;
|
|
317
346
|
|
|
318
347
|
// If easyFinalAnswer mode is on, also match response starting with "✅" or numbered confirmation lists
|
|
@@ -418,15 +447,17 @@ export abstract class BaseAgent implements IAgent {
|
|
|
418
447
|
return this.totalCostUsd;
|
|
419
448
|
}
|
|
420
449
|
|
|
421
|
-
adjustTokenUsage(usage: any) {
|
|
450
|
+
adjustTokenUsage(usage: any, messages?: Message[]) {
|
|
422
451
|
if (!usage) return;
|
|
423
452
|
// Support both OpenAI-style (prompt_tokens/completion_tokens) and Anthropic-style (input_tokens/output_tokens)
|
|
424
453
|
const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? 0;
|
|
425
454
|
const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? 0;
|
|
426
455
|
|
|
427
456
|
const cacheReadTokens =
|
|
428
|
-
usage.cache_read_input_tokens ??
|
|
429
|
-
usage.
|
|
457
|
+
usage.cache_read_input_tokens ??
|
|
458
|
+
usage.cache_read_tokens ??
|
|
459
|
+
usage.prompt_tokens_details?.cached_tokens ??
|
|
460
|
+
0;
|
|
430
461
|
const cacheWriteTokens =
|
|
431
462
|
usage.cache_creation_input_tokens ?? usage.cache_write_tokens ?? 0;
|
|
432
463
|
|
|
@@ -436,6 +467,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
436
467
|
this.totalCacheWriteTokens += cacheWriteTokens;
|
|
437
468
|
|
|
438
469
|
this.agentEvents.emit(this.eventTypes.tokenUsage, {
|
|
470
|
+
timestamp: new Date().toISOString(),
|
|
439
471
|
inputTokens,
|
|
440
472
|
outputTokens,
|
|
441
473
|
cacheReadTokens,
|
|
@@ -444,6 +476,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
444
476
|
totalOutputTokens: this.totalOutputTokens,
|
|
445
477
|
totalCacheReadTokens: this.totalCacheReadTokens,
|
|
446
478
|
totalCacheWriteTokens: this.totalCacheWriteTokens,
|
|
479
|
+
messages,
|
|
447
480
|
});
|
|
448
481
|
}
|
|
449
482
|
|
|
@@ -456,6 +489,37 @@ export abstract class BaseAgent implements IAgent {
|
|
|
456
489
|
};
|
|
457
490
|
}
|
|
458
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Centralized helper for making an AI completion call while ensuring
|
|
494
|
+
* cost (totalCostUsd) and token usage (input/output/cache) are always
|
|
495
|
+
* updated consistently, regardless of which part of the agent triggers
|
|
496
|
+
* the completion (main call loop, task breakdown, compression, etc).
|
|
497
|
+
*
|
|
498
|
+
* Optionally supports being interrupted via makeInterruptible by passing
|
|
499
|
+
* an `interruptValue` in options - if the operation is interrupted, the
|
|
500
|
+
* interrupt value is returned and used for cost/usage tracking (which,
|
|
501
|
+
* for a typical interrupt stub with no usage/cost, is effectively a
|
|
502
|
+
* no-op).
|
|
503
|
+
*/
|
|
504
|
+
protected async createAgentCompletion(
|
|
505
|
+
params: Parameters<GenericClient["createChatCompletion"]>[0],
|
|
506
|
+
options: {
|
|
507
|
+
interruptValue?: CompletionResponse;
|
|
508
|
+
} = {}
|
|
509
|
+
): Promise<CompletionResponse> {
|
|
510
|
+
const { interruptValue } = options;
|
|
511
|
+
|
|
512
|
+
const callPromise = this.getClient().createChatCompletion(params);
|
|
513
|
+
const response = interruptValue
|
|
514
|
+
? await this.makeInterruptible(callPromise, interruptValue)
|
|
515
|
+
: await callPromise;
|
|
516
|
+
|
|
517
|
+
this.adjustTotalCostUsd(response?.usd_cost);
|
|
518
|
+
this.adjustTokenUsage(response?.usage, params.messages);
|
|
519
|
+
|
|
520
|
+
return response;
|
|
521
|
+
}
|
|
522
|
+
|
|
459
523
|
startNewThread(messages: Message[]) {
|
|
460
524
|
this.currentThread++;
|
|
461
525
|
this.agentEvents.emit(this.eventTypes.newThread, messages);
|
|
@@ -488,7 +552,14 @@ export abstract class BaseAgent implements IAgent {
|
|
|
488
552
|
toolCallId: toolCall.id,
|
|
489
553
|
functionName: toolCall.function?.name,
|
|
490
554
|
functionArgs: {},
|
|
491
|
-
toolMessages: [
|
|
555
|
+
toolMessages: [
|
|
556
|
+
{
|
|
557
|
+
role: "tool",
|
|
558
|
+
tool_call_id: toolCall.id,
|
|
559
|
+
name: toolCall.function?.name,
|
|
560
|
+
content: interruptMsg,
|
|
561
|
+
},
|
|
562
|
+
],
|
|
492
563
|
};
|
|
493
564
|
|
|
494
565
|
const { functionResp, toolMessages } = await this.makeInterruptible(
|
|
@@ -671,29 +742,31 @@ export abstract class BaseAgent implements IAgent {
|
|
|
671
742
|
return new Promise<T>((resolve, reject) => {
|
|
672
743
|
let interrupted = false;
|
|
673
744
|
|
|
674
|
-
this._interruptResolve = (
|
|
745
|
+
this._interruptResolve = () => {
|
|
675
746
|
interrupted = true;
|
|
676
747
|
this._interruptResolve = null;
|
|
677
|
-
resolve(
|
|
748
|
+
resolve(interruptValue);
|
|
678
749
|
};
|
|
679
750
|
|
|
680
|
-
promise
|
|
681
|
-
|
|
682
|
-
this._interruptResolve
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
751
|
+
promise
|
|
752
|
+
.then((result) => {
|
|
753
|
+
if (this._interruptResolve) {
|
|
754
|
+
this._interruptResolve = null;
|
|
755
|
+
resolve(result);
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
.catch((err) => {
|
|
759
|
+
if (this._interruptResolve) {
|
|
760
|
+
this._interruptResolve = null;
|
|
761
|
+
}
|
|
762
|
+
// Only swallow the error if interrupt() was explicitly called.
|
|
763
|
+
// Otherwise re-throw so callers see the real error.
|
|
764
|
+
if (interrupted) {
|
|
765
|
+
resolve(interruptValue);
|
|
766
|
+
} else {
|
|
767
|
+
reject(err);
|
|
768
|
+
}
|
|
769
|
+
});
|
|
697
770
|
});
|
|
698
771
|
}
|
|
699
772
|
|
|
@@ -705,7 +778,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
705
778
|
interrupt(message = "User interrupted this action you were waiting on") {
|
|
706
779
|
this.log(`Interrupting current operation: ${message}`);
|
|
707
780
|
if (this._interruptResolve) {
|
|
708
|
-
this._interruptResolve(
|
|
781
|
+
this._interruptResolve();
|
|
709
782
|
} else {
|
|
710
783
|
this.log("No active interruptible operation to interrupt", "warn");
|
|
711
784
|
}
|
|
@@ -782,20 +855,30 @@ export abstract class BaseAgent implements IAgent {
|
|
|
782
855
|
);
|
|
783
856
|
|
|
784
857
|
const interruptResponse: CompletionResponse = {
|
|
785
|
-
choices: [
|
|
858
|
+
choices: [
|
|
859
|
+
{
|
|
860
|
+
message: {
|
|
861
|
+
role: "assistant",
|
|
862
|
+
content: "User interrupted this AI completion. Please continue.",
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
],
|
|
786
866
|
model,
|
|
787
867
|
usage: undefined,
|
|
788
868
|
usd_cost: 0,
|
|
789
869
|
};
|
|
790
|
-
|
|
791
|
-
|
|
870
|
+
|
|
871
|
+
const response = await this.createAgentCompletion(
|
|
872
|
+
{
|
|
792
873
|
model,
|
|
793
874
|
messages,
|
|
794
875
|
tools: this.getEnabledTools(),
|
|
795
876
|
tool_choice: "auto",
|
|
796
877
|
long_ttl_cache: this.runTime() > 300_000,
|
|
797
|
-
|
|
798
|
-
|
|
878
|
+
...(this.reasoningEffort !== undefined && { reasoning_effort: this.reasoningEffort }),
|
|
879
|
+
...(this.summarizeReasoning !== undefined && { reasoning_summary: this.summarizeReasoning }),
|
|
880
|
+
},
|
|
881
|
+
{ interruptValue: interruptResponse }
|
|
799
882
|
);
|
|
800
883
|
|
|
801
884
|
// If the agent was paused while the completion was in-flight, wait here
|
|
@@ -829,8 +912,6 @@ export abstract class BaseAgent implements IAgent {
|
|
|
829
912
|
}
|
|
830
913
|
}
|
|
831
914
|
|
|
832
|
-
this.adjustTotalCostUsd(response?.usd_cost);
|
|
833
|
-
this.adjustTokenUsage(response?.usage);
|
|
834
915
|
this.log("agent response cost: " + response?.usd_cost);
|
|
835
916
|
|
|
836
917
|
// Typically, there's only one choice in the array, but you could have many
|
|
@@ -847,7 +928,6 @@ export abstract class BaseAgent implements IAgent {
|
|
|
847
928
|
|
|
848
929
|
this.logMessages([lastMessage]);
|
|
849
930
|
|
|
850
|
-
const toolCalls = lastMessage.tool_calls;
|
|
851
931
|
if (lastMessage.tool_calls) {
|
|
852
932
|
// About to call a tool, process the messages
|
|
853
933
|
// We could add all the tool calls, and do this once
|
|
@@ -856,6 +936,11 @@ export abstract class BaseAgent implements IAgent {
|
|
|
856
936
|
"pre_tools"
|
|
857
937
|
);
|
|
858
938
|
|
|
939
|
+
// Re-derive toolCalls from the freshly processed messages, since
|
|
940
|
+
// pre_tools processors (e.g. MinimalToolsMessageProcessor) may have
|
|
941
|
+
// rewritten the tool_calls on a deep-copied message array.
|
|
942
|
+
const toolCalls = messages[messages.length - 1].tool_calls;
|
|
943
|
+
|
|
859
944
|
this.updateCurrentThread(messages);
|
|
860
945
|
|
|
861
946
|
const truncationWarning = this.detectTruncatedToolCalls(
|
|
@@ -942,7 +1027,9 @@ export abstract class BaseAgent implements IAgent {
|
|
|
942
1027
|
firstMessage.content &&
|
|
943
1028
|
this.isTerminationResponse(firstMessage.content)
|
|
944
1029
|
) {
|
|
945
|
-
this.log(
|
|
1030
|
+
this.log(
|
|
1031
|
+
`Termination word detected: "${firstMessage.content.trim()}", treating as finalAnswer`
|
|
1032
|
+
);
|
|
946
1033
|
this.status = this.eventTypes.done;
|
|
947
1034
|
this.agentEvents.emit(this.eventTypes.done, firstMessage.content);
|
|
948
1035
|
return firstMessage.content;
|
|
@@ -1001,7 +1088,9 @@ export abstract class BaseAgent implements IAgent {
|
|
|
1001
1088
|
this.logStatus();
|
|
1002
1089
|
|
|
1003
1090
|
const continuation = `<Workflow>
|
|
1004
|
-
Task terminates after you call on of these tools: ${JSON.stringify(
|
|
1091
|
+
Task terminates after you call on of these tools: ${JSON.stringify(
|
|
1092
|
+
this.requiredToolNames
|
|
1093
|
+
)}.\n
|
|
1005
1094
|
User likely only sees output from the required tool call.
|
|
1006
1095
|
<TaskStatus>${statusMessage}</TaskStatus>
|
|
1007
1096
|
</Workflow>`;
|
|
@@ -1048,7 +1137,12 @@ export abstract class BaseAgent implements IAgent {
|
|
|
1048
1137
|
|
|
1049
1138
|
this.log(`Agent failed: ${e}`, "error");
|
|
1050
1139
|
|
|
1051
|
-
if (
|
|
1140
|
+
if (
|
|
1141
|
+
e != null &&
|
|
1142
|
+
typeof e === "object" &&
|
|
1143
|
+
"response" in e &&
|
|
1144
|
+
"data" in (e as any).response
|
|
1145
|
+
) {
|
|
1052
1146
|
this.log(
|
|
1053
1147
|
`Error response data: ${JSON.stringify(e.response.data, null, 2)}`,
|
|
1054
1148
|
"error"
|
|
@@ -1165,7 +1259,13 @@ export abstract class BaseAgent implements IAgent {
|
|
|
1165
1259
|
toolCalls: ToolCall[],
|
|
1166
1260
|
response: CompletionResponse
|
|
1167
1261
|
): { role: string; content: string } | null {
|
|
1168
|
-
|
|
1262
|
+
// Subtract thinking/reasoning tokens — they're billed as output tokens but
|
|
1263
|
+
// don't produce visible argument content, so including them inflates the heuristic.
|
|
1264
|
+
const rawOutputTokens: number = response?.usage?.completion_tokens || 0;
|
|
1265
|
+
const thinkingTokens: number =
|
|
1266
|
+
(response?.usage?.output_tokens_details?.thinking_tokens ?? 0) +
|
|
1267
|
+
(response?.usage?.output_tokens_details?.reasoning_tokens ?? 0);
|
|
1268
|
+
const outputTokens = Math.max(0, rawOutputTokens - thinkingTokens);
|
|
1169
1269
|
const totalArgLength = toolCalls.reduce(
|
|
1170
1270
|
(sum, tc) => sum + (tc.function?.arguments?.length || 0),
|
|
1171
1271
|
0
|
|
@@ -1223,20 +1323,20 @@ export abstract class BaseAgent implements IAgent {
|
|
|
1223
1323
|
|
|
1224
1324
|
const model = this.getModel();
|
|
1225
1325
|
|
|
1226
|
-
const
|
|
1326
|
+
const taskBreakdownMessages = [
|
|
1327
|
+
...messages,
|
|
1328
|
+
{
|
|
1329
|
+
role: "user",
|
|
1330
|
+
content: taskPrompt,
|
|
1331
|
+
},
|
|
1332
|
+
] as Message[];
|
|
1333
|
+
|
|
1334
|
+
const response = await this.createAgentCompletion({
|
|
1227
1335
|
model,
|
|
1228
|
-
messages:
|
|
1229
|
-
...messages,
|
|
1230
|
-
{
|
|
1231
|
-
role: "user",
|
|
1232
|
-
content: taskPrompt,
|
|
1233
|
-
},
|
|
1234
|
-
],
|
|
1336
|
+
messages: taskBreakdownMessages,
|
|
1235
1337
|
max_tokens: 2000,
|
|
1236
1338
|
});
|
|
1237
1339
|
|
|
1238
|
-
this.adjustTotalCostUsd(response.usd_cost);
|
|
1239
|
-
|
|
1240
1340
|
this.taskBreakdown = response.choices[0].message.content;
|
|
1241
1341
|
this.log(`task breakdown cost: ${response.usd_cost}`);
|
|
1242
1342
|
return this.taskBreakdown;
|
|
@@ -1268,19 +1368,19 @@ export abstract class BaseAgent implements IAgent {
|
|
|
1268
1368
|
|
|
1269
1369
|
const model = this.getModel();
|
|
1270
1370
|
|
|
1271
|
-
const
|
|
1371
|
+
const compressMessagesPayload = [
|
|
1372
|
+
...messages,
|
|
1373
|
+
{
|
|
1374
|
+
role: "user",
|
|
1375
|
+
content: toCompressPrompt,
|
|
1376
|
+
},
|
|
1377
|
+
] as Message[];
|
|
1378
|
+
|
|
1379
|
+
const response = await this.createAgentCompletion({
|
|
1272
1380
|
model,
|
|
1273
|
-
messages:
|
|
1274
|
-
...messages,
|
|
1275
|
-
{
|
|
1276
|
-
role: "user",
|
|
1277
|
-
content: toCompressPrompt,
|
|
1278
|
-
},
|
|
1279
|
-
],
|
|
1381
|
+
messages: compressMessagesPayload,
|
|
1280
1382
|
});
|
|
1281
1383
|
|
|
1282
|
-
this.adjustTotalCostUsd(response.usd_cost);
|
|
1283
|
-
|
|
1284
1384
|
const summaries = response.choices.map((c) => c.message.content);
|
|
1285
1385
|
this.summaries.push(...summaries);
|
|
1286
1386
|
|
|
@@ -259,7 +259,7 @@ const execWithTimeout = async (
|
|
|
259
259
|
// Public tool
|
|
260
260
|
export async function execCommand(
|
|
261
261
|
command: string,
|
|
262
|
-
timeout?: number,
|
|
262
|
+
timeout?: number | string,
|
|
263
263
|
continueInBackground?: boolean,
|
|
264
264
|
logFileName?: string
|
|
265
265
|
): Promise<string> {
|
|
@@ -272,17 +272,37 @@ export async function execCommand(
|
|
|
272
272
|
this instanceof ToolsService ? this : services().Tools
|
|
273
273
|
) as ToolsService;
|
|
274
274
|
|
|
275
|
+
// Coerce timeout to a number. Agents (and some tool-call serializers) often
|
|
276
|
+
// pass numeric arguments as strings (e.g. "120000"). Node's exec()/setTimeout
|
|
277
|
+
// reject non-integer values with "The value of \"timeout\" is out of range.
|
|
278
|
+
// It must be an unsigned integer", so normalize here before any use.
|
|
279
|
+
let normalizedTimeout: number | undefined;
|
|
280
|
+
const rawTimeout = typeof timeout === "string" ? Number(timeout.trim()) : timeout;
|
|
281
|
+
if (typeof rawTimeout !== "number" || !Number.isFinite(rawTimeout)) {
|
|
282
|
+
normalizedTimeout = undefined;
|
|
283
|
+
} else if (rawTimeout === -1) {
|
|
284
|
+
// -1 is the sentinel for "wait indefinitely" — leave as-is.
|
|
285
|
+
normalizedTimeout = -1;
|
|
286
|
+
} else {
|
|
287
|
+
// Clamp to a non-negative integer so the underlying APIs never throw.
|
|
288
|
+
normalizedTimeout = Math.max(0, Math.floor(rawTimeout));
|
|
289
|
+
}
|
|
290
|
+
|
|
275
291
|
// Detect suspiciously small timeout values — agents often pass seconds instead of ms.
|
|
276
292
|
// Any value < 100 is almost certainly a mistake (100ms is still very fast for a shell cmd).
|
|
277
293
|
// We auto-correct to seconds and include a warning in the output so the agent learns.
|
|
278
|
-
let correctedTimeout =
|
|
294
|
+
let correctedTimeout = normalizedTimeout;
|
|
279
295
|
let timeoutWarning = "";
|
|
280
|
-
if (
|
|
281
|
-
|
|
296
|
+
if (
|
|
297
|
+
normalizedTimeout !== undefined &&
|
|
298
|
+
normalizedTimeout !== -1 &&
|
|
299
|
+
normalizedTimeout < 100
|
|
300
|
+
) {
|
|
301
|
+
correctedTimeout = normalizedTimeout * 1000;
|
|
282
302
|
timeoutWarning =
|
|
283
|
-
`⚠️ Warning: timeout was ${
|
|
303
|
+
`⚠️ Warning: timeout was ${normalizedTimeout}ms which is extremely small and likely a mistake. ` +
|
|
284
304
|
`The timeout unit is milliseconds, not seconds. ` +
|
|
285
|
-
`Auto-corrected to ${correctedTimeout}ms (${
|
|
305
|
+
`Auto-corrected to ${correctedTimeout}ms (${normalizedTimeout}s).\n\n`;
|
|
286
306
|
}
|
|
287
307
|
|
|
288
308
|
const context = toolService.getContext();
|
package/src/agents/tools/list.ts
CHANGED
|
@@ -7,6 +7,7 @@ import * as language from "./language/definitions";
|
|
|
7
7
|
import * as mcp from "./mcp/definitions";
|
|
8
8
|
import { googleSearchDefinition } from "./googleSearch";
|
|
9
9
|
import { startAgentTaskDefinition } from "./startAgentTask";
|
|
10
|
+
import { skillsToolDefinitions } from "./skills";
|
|
10
11
|
|
|
11
12
|
function getPluginNames(): string {
|
|
12
13
|
try {
|
|
@@ -681,4 +682,5 @@ export const includedTools = [
|
|
|
681
682
|
...ycmd.definitions,
|
|
682
683
|
...language.definitions,
|
|
683
684
|
...mcp.definitions,
|
|
685
|
+
...skillsToolDefinitions,
|
|
684
686
|
] as Tool[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { minimatch } from "minimatch";
|
|
2
|
+
import { MinimalToolsService } from "../../../services/MinimalToolsService";
|
|
3
|
+
import { ToolsService } from "../../../services/Tools";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Calls any tool by name with the given args.
|
|
7
|
+
* This is the escape hatch that lets agents call tools outside the base tool set
|
|
8
|
+
* without changing the `tools:` array sent to the AI (preserving cache stability).
|
|
9
|
+
*/
|
|
10
|
+
export async function callTool(
|
|
11
|
+
this: ToolsService,
|
|
12
|
+
args: { name: string; args: Record<string, any> }
|
|
13
|
+
): Promise<any> {
|
|
14
|
+
const service = this as unknown as MinimalToolsService;
|
|
15
|
+
const { name, args: toolArgs } = args;
|
|
16
|
+
|
|
17
|
+
const allTools = service.getAllTools ? service.getAllTools() : service.getTools();
|
|
18
|
+
|
|
19
|
+
// Fuzzy match: exact, endsWith, or glob
|
|
20
|
+
const matchedTool =
|
|
21
|
+
allTools.find((t) => t.function.name === name) ||
|
|
22
|
+
allTools.find((t) => t.function.name.endsWith(name)) ||
|
|
23
|
+
allTools.find((t) => minimatch(t.function.name, name, { nocase: true }));
|
|
24
|
+
|
|
25
|
+
if (!matchedTool) {
|
|
26
|
+
const available = allTools.map((t) => t.function.name).join(", ");
|
|
27
|
+
return `Error: Tool "${name}" not found. Use inspectTools() to browse available tools. Available: ${available}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const toolName = matchedTool.function.name;
|
|
31
|
+
|
|
32
|
+
// Build a synthetic ToolCall object and delegate to the base callTool
|
|
33
|
+
const syntheticToolCall = {
|
|
34
|
+
id: `call_minimal_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
|
|
35
|
+
type: "function" as const,
|
|
36
|
+
function: {
|
|
37
|
+
name: toolName,
|
|
38
|
+
arguments: JSON.stringify(toolArgs),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Use base ToolsService.callTool with the matched tool name allowed
|
|
43
|
+
const result = await service.callToolByName(syntheticToolCall, toolName);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Tool } from "../../../clients/types";
|
|
2
|
+
|
|
3
|
+
export const callToolDefinition: Tool = {
|
|
4
|
+
type: "function",
|
|
5
|
+
function: {
|
|
6
|
+
name: "callTool",
|
|
7
|
+
description:
|
|
8
|
+
"Call any available tool by name with the given arguments. Use listStoredToolResponses + inspectTools to discover available tools and their argument schemas before calling. This is the primary way to call tools that are not in the base tool set.",
|
|
9
|
+
parameters: {
|
|
10
|
+
type: "object",
|
|
11
|
+
positional: false,
|
|
12
|
+
properties: {
|
|
13
|
+
name: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "The name of the tool to call.",
|
|
16
|
+
},
|
|
17
|
+
args: {
|
|
18
|
+
type: "object",
|
|
19
|
+
description:
|
|
20
|
+
"The arguments to pass to the tool. Should match the tool's parameter schema.",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["name", "args"],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const inspectToolsDefinition: Tool = {
|
|
29
|
+
type: "function",
|
|
30
|
+
function: {
|
|
31
|
+
name: "inspectTools",
|
|
32
|
+
description:
|
|
33
|
+
"Get the full schema/definition for tools matching the given glob patterns. Use this to discover what arguments a tool needs before calling it via callTool. Example patterns: ['read*'] finds tools starting with 'read', ['*File'] finds tools ending with 'File'.",
|
|
34
|
+
parameters: {
|
|
35
|
+
type: "object",
|
|
36
|
+
positional: true,
|
|
37
|
+
properties: {
|
|
38
|
+
patterns: {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: { type: "string" },
|
|
41
|
+
description:
|
|
42
|
+
"Array of glob patterns to filter tool names. If omitted, returns all available tools.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: [],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const minimalDefinitions = [callToolDefinition, inspectToolsDefinition];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { minimatch } from "minimatch";
|
|
2
|
+
import { MinimalToolsService } from "../../../services/MinimalToolsService";
|
|
3
|
+
import { ToolsService } from "../../../services/Tools";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the full tool definitions (schema) for all tools matching the given patterns.
|
|
7
|
+
* Used by agents to discover argument shapes before calling via callTool.
|
|
8
|
+
*/
|
|
9
|
+
export function inspectTools(
|
|
10
|
+
this: ToolsService,
|
|
11
|
+
patterns?: string[]
|
|
12
|
+
): object[] {
|
|
13
|
+
const service = this as unknown as MinimalToolsService;
|
|
14
|
+
const allTools = service.getAllTools ? service.getAllTools() : service.getTools();
|
|
15
|
+
|
|
16
|
+
if (!patterns || patterns.length === 0) {
|
|
17
|
+
return allTools.map((t) => t.function);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return allTools
|
|
21
|
+
.filter((t) =>
|
|
22
|
+
patterns.some((pattern) =>
|
|
23
|
+
minimatch(t.function.name, pattern, { nocase: true })
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
.map((t) => t.function);
|
|
27
|
+
}
|