@tyvm/knowhow 0.0.129 → 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 +4 -4
- package/src/agents/base/base.ts +40 -5
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/list.ts +2 -0
- package/src/agents/tools/skills.ts +79 -0
- package/src/chat/modules/AgentModule.ts +84 -1
- package/src/cli.ts +4 -0
- package/src/clients/anthropic.ts +59 -1
- package/src/clients/openai.ts +28 -5
- package/src/clients/pricing/openai.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 +6 -2
- 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/services/BehaviorsService.ts +432 -0
- package/src/services/KnowhowClient.ts +61 -0
- package/src/services/SkillsService.ts +622 -0
- package/src/services/Tools.ts +2 -0
- package/src/services/index.ts +8 -2
- package/src/services/modules/types.ts +2 -0
- package/ts_build/package.json +4 -4
- package/ts_build/src/agents/base/base.d.ts +9 -1
- package/ts_build/src/agents/base/base.js +29 -4
- package/ts_build/src/agents/base/base.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/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 +69 -0
- 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/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/openai.js +3 -0
- package/ts_build/src/clients/pricing/openai.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/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 +8 -0
- package/ts_build/src/services/KnowhowClient.js +20 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- 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 +3 -0
- package/ts_build/src/services/index.js +8 -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tyvm/knowhow",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.130",
|
|
4
4
|
"description": "ai cli with plugins and agents",
|
|
5
5
|
"main": "ts_build/src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@babel/preset-typescript": "^7.23.3",
|
|
38
|
-
"typescript": "^5.0.0",
|
|
39
38
|
"@types/diff": "^5.2.1",
|
|
40
39
|
"@types/express": "^4.17.13",
|
|
41
40
|
"@types/jest": "^29.5.13",
|
|
@@ -47,10 +46,11 @@
|
|
|
47
46
|
"ts-jest": "^29.1.1",
|
|
48
47
|
"ts-node": "^10.7.0",
|
|
49
48
|
"tslint": "^6.1.3",
|
|
50
|
-
"tslint-config-prettier": "^1.18.0"
|
|
49
|
+
"tslint-config-prettier": "^1.18.0",
|
|
50
|
+
"typescript": "^5.0.0"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@anthropic-ai/sdk": "^0.
|
|
53
|
+
"@anthropic-ai/sdk": "^0.111.0",
|
|
54
54
|
"@google/genai": "^0.14.1",
|
|
55
55
|
"@inquirer/editor": "^4.2.18",
|
|
56
56
|
"@modelcontextprotocol/sdk": "^1.13.3",
|
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) {
|
|
@@ -715,10 +742,10 @@ export abstract class BaseAgent implements IAgent {
|
|
|
715
742
|
return new Promise<T>((resolve, reject) => {
|
|
716
743
|
let interrupted = false;
|
|
717
744
|
|
|
718
|
-
this._interruptResolve = (
|
|
745
|
+
this._interruptResolve = () => {
|
|
719
746
|
interrupted = true;
|
|
720
747
|
this._interruptResolve = null;
|
|
721
|
-
resolve(
|
|
748
|
+
resolve(interruptValue);
|
|
722
749
|
};
|
|
723
750
|
|
|
724
751
|
promise
|
|
@@ -751,7 +778,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
751
778
|
interrupt(message = "User interrupted this action you were waiting on") {
|
|
752
779
|
this.log(`Interrupting current operation: ${message}`);
|
|
753
780
|
if (this._interruptResolve) {
|
|
754
|
-
this._interruptResolve(
|
|
781
|
+
this._interruptResolve();
|
|
755
782
|
} else {
|
|
756
783
|
this.log("No active interruptible operation to interrupt", "warn");
|
|
757
784
|
}
|
|
@@ -848,6 +875,8 @@ export abstract class BaseAgent implements IAgent {
|
|
|
848
875
|
tools: this.getEnabledTools(),
|
|
849
876
|
tool_choice: "auto",
|
|
850
877
|
long_ttl_cache: this.runTime() > 300_000,
|
|
878
|
+
...(this.reasoningEffort !== undefined && { reasoning_effort: this.reasoningEffort }),
|
|
879
|
+
...(this.summarizeReasoning !== undefined && { reasoning_summary: this.summarizeReasoning }),
|
|
851
880
|
},
|
|
852
881
|
{ interruptValue: interruptResponse }
|
|
853
882
|
);
|
|
@@ -1230,7 +1259,13 @@ export abstract class BaseAgent implements IAgent {
|
|
|
1230
1259
|
toolCalls: ToolCall[],
|
|
1231
1260
|
response: CompletionResponse
|
|
1232
1261
|
): { role: string; content: string } | null {
|
|
1233
|
-
|
|
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);
|
|
1234
1269
|
const totalArgLength = toolCalls.reduce(
|
|
1235
1270
|
(sum, tc) => sum + (tc.function?.arguments?.length || 0),
|
|
1236
1271
|
0
|
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,79 @@
|
|
|
1
|
+
import { ToolsService } from "../../services/Tools";
|
|
2
|
+
import { services } from "../../services";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* List all skills currently loaded in memory.
|
|
6
|
+
* Returns a formatted string of name: description lines.
|
|
7
|
+
*/
|
|
8
|
+
export async function listSkills(this: ToolsService): Promise<string> {
|
|
9
|
+
const toolService =
|
|
10
|
+
this instanceof ToolsService ? (this as ToolsService) : services().Tools;
|
|
11
|
+
|
|
12
|
+
const { Behaviors } = toolService.getContext();
|
|
13
|
+
|
|
14
|
+
const skills = Behaviors.listSkills();
|
|
15
|
+
if (skills.length === 0) {
|
|
16
|
+
return "No skills available. Run `knowhow behaviors download --skills-only` to fetch skills.";
|
|
17
|
+
}
|
|
18
|
+
return skills.map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load full instructions for a named skill from memory.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadSkill(
|
|
25
|
+
this: ToolsService,
|
|
26
|
+
name: string
|
|
27
|
+
): Promise<string> {
|
|
28
|
+
const toolService =
|
|
29
|
+
this instanceof ToolsService ? (this as ToolsService) : services().Tools;
|
|
30
|
+
|
|
31
|
+
const { Behaviors } = toolService.getContext();
|
|
32
|
+
|
|
33
|
+
const skill = Behaviors.findSkill(name);
|
|
34
|
+
if (!skill) {
|
|
35
|
+
const names = Behaviors.listSkills()
|
|
36
|
+
.map((s) => s.name)
|
|
37
|
+
.join(", ");
|
|
38
|
+
return `Skill "${name}" not found. Available skills: ${names || "none"}`;
|
|
39
|
+
}
|
|
40
|
+
return `## Skill: ${skill.name}\n\n${skill.instructions}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Tool definitions for listSkills + loadSkill */
|
|
44
|
+
export const skillsToolDefinitions = [
|
|
45
|
+
{
|
|
46
|
+
type: "function" as const,
|
|
47
|
+
function: {
|
|
48
|
+
name: "listSkills",
|
|
49
|
+
description:
|
|
50
|
+
"List all available skills by name and description. Call this to discover what skills are available before loading one.",
|
|
51
|
+
parameters: {
|
|
52
|
+
type: "object" as const,
|
|
53
|
+
positional: true,
|
|
54
|
+
properties: {} as Record<string, never>,
|
|
55
|
+
required: [] as string[],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: "function" as const,
|
|
61
|
+
function: {
|
|
62
|
+
name: "loadSkill",
|
|
63
|
+
description:
|
|
64
|
+
"Load the full instructions for a named skill. Use this when you need to follow a specific skill's guidance.",
|
|
65
|
+
parameters: {
|
|
66
|
+
type: "object" as const,
|
|
67
|
+
positional: true,
|
|
68
|
+
properties: {
|
|
69
|
+
name: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description:
|
|
72
|
+
"The skill name to load (must match exactly or approximately)",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ["name"] as string[],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
];
|
|
@@ -18,7 +18,7 @@ import { BaseChatModule } from "./BaseChatModule";
|
|
|
18
18
|
import { services } from "../../services/index";
|
|
19
19
|
import { BaseAgent } from "../../agents/index";
|
|
20
20
|
import { ChatCommand, ChatMode, ChatContext, ChatService } from "../types";
|
|
21
|
-
import { Message } from "../../clients/types";
|
|
21
|
+
import { Message, CompletionOptions } from "../../clients/types";
|
|
22
22
|
import { ChatInteraction } from "../../types";
|
|
23
23
|
import { Marked } from "../../utils/index";
|
|
24
24
|
import { TokenCompressor } from "../../processors/TokenCompressor";
|
|
@@ -52,6 +52,11 @@ export class AgentModule extends BaseChatModule {
|
|
|
52
52
|
/** Currently active synced agent watcher (for FS or Web agents) */
|
|
53
53
|
private activeSyncedWatcher: SyncedAgentWatcher | undefined;
|
|
54
54
|
|
|
55
|
+
/** Reasoning effort to apply to agents started from this module */
|
|
56
|
+
private reasoningEffort: CompletionOptions["reasoning_effort"] | undefined = undefined;
|
|
57
|
+
/** Whether to request a reasoning summary from supporting models */
|
|
58
|
+
private summarizeReasoning: boolean = true;
|
|
59
|
+
|
|
55
60
|
/** Stored wire params for rewireAgentRendering */
|
|
56
61
|
private _wireAgentEvents: EventService | undefined;
|
|
57
62
|
private _wireTaskId: string | undefined;
|
|
@@ -138,6 +143,78 @@ export class AgentModule extends BaseChatModule {
|
|
|
138
143
|
}
|
|
139
144
|
},
|
|
140
145
|
},
|
|
146
|
+
{
|
|
147
|
+
name: "reasoning_effort",
|
|
148
|
+
description:
|
|
149
|
+
"Set the reasoning effort for the agent (none, low, medium, high). Use 'none' to disable thinking on models that support it.",
|
|
150
|
+
handler: async (args: string[]): Promise<void> => {
|
|
151
|
+
const validEfforts: CompletionOptions["reasoning_effort"][] = [
|
|
152
|
+
"none",
|
|
153
|
+
"low",
|
|
154
|
+
"medium",
|
|
155
|
+
"high",
|
|
156
|
+
];
|
|
157
|
+
const effort = args[0] as CompletionOptions["reasoning_effort"];
|
|
158
|
+
if (!effort || !validEfforts.includes(effort)) {
|
|
159
|
+
console.log(
|
|
160
|
+
`Current reasoning effort: ${this.reasoningEffort ?? "(not set — model default)"}`
|
|
161
|
+
);
|
|
162
|
+
console.log(
|
|
163
|
+
`Usage: /reasoning_effort <${validEfforts.join("|")}>`
|
|
164
|
+
);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this.reasoningEffort = effort;
|
|
168
|
+
const agent = (this.chatService?.getContext()?.selectedAgent as BaseAgent);
|
|
169
|
+
if (agent) agent.setReasoningEffort(effort);
|
|
170
|
+
console.log(`Reasoning effort set to: ${effort}`);
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "effort",
|
|
175
|
+
description: "Alias for /reasoning_effort",
|
|
176
|
+
handler: async (args: string[]): Promise<void> => {
|
|
177
|
+
const validEfforts: CompletionOptions["reasoning_effort"][] = [
|
|
178
|
+
"none",
|
|
179
|
+
"low",
|
|
180
|
+
"medium",
|
|
181
|
+
"high",
|
|
182
|
+
];
|
|
183
|
+
const effort = args[0] as CompletionOptions["reasoning_effort"];
|
|
184
|
+
if (!effort || !validEfforts.includes(effort)) {
|
|
185
|
+
console.log(
|
|
186
|
+
`Current reasoning effort: ${this.reasoningEffort ?? "(not set — model default)"}`
|
|
187
|
+
);
|
|
188
|
+
console.log(
|
|
189
|
+
`Usage: /effort <${validEfforts.join("|")}>`
|
|
190
|
+
);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
this.reasoningEffort = effort;
|
|
194
|
+
const agent = (this.chatService?.getContext()?.selectedAgent as BaseAgent);
|
|
195
|
+
if (agent) agent.setReasoningEffort(effort);
|
|
196
|
+
console.log(`Reasoning effort set to: ${effort}`);
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "reasoning_summary",
|
|
201
|
+
description:
|
|
202
|
+
"Toggle whether the agent displays a reasoning summary (true/false). Supported by reasoning models only.",
|
|
203
|
+
handler: async (args: string[]): Promise<void> => {
|
|
204
|
+
const val = args[0];
|
|
205
|
+
if (val !== "true" && val !== "false") {
|
|
206
|
+
console.log(
|
|
207
|
+
`Current reasoning summary: ${this.summarizeReasoning}`
|
|
208
|
+
);
|
|
209
|
+
console.log(`Usage: /reasoning_summary <true|false>`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.summarizeReasoning = val === "true";
|
|
213
|
+
const agent = (this.chatService?.getContext()?.selectedAgent as BaseAgent);
|
|
214
|
+
if (agent) agent.setSummarizeReasoning(this.summarizeReasoning);
|
|
215
|
+
console.log(`Reasoning summary set to: ${this.summarizeReasoning}`);
|
|
216
|
+
},
|
|
217
|
+
},
|
|
141
218
|
{
|
|
142
219
|
name: "detach",
|
|
143
220
|
description: "Detach from the currently attached agent",
|
|
@@ -754,6 +831,12 @@ export class AgentModule extends BaseChatModule {
|
|
|
754
831
|
]);
|
|
755
832
|
}
|
|
756
833
|
|
|
834
|
+
// Apply module-level reasoning settings to the agent
|
|
835
|
+
if (this.reasoningEffort !== undefined) {
|
|
836
|
+
agent.setReasoningEffort(this.reasoningEffort);
|
|
837
|
+
}
|
|
838
|
+
agent.setSummarizeReasoning(this.summarizeReasoning);
|
|
839
|
+
|
|
757
840
|
// Set up message processors like in original startAgent
|
|
758
841
|
|
|
759
842
|
// Register an override for askHuman to use CLI input method
|
package/src/cli.ts
CHANGED
|
@@ -38,6 +38,8 @@ import {
|
|
|
38
38
|
} from "./commands/misc";
|
|
39
39
|
import { addConvertCommand } from "./commands/convert";
|
|
40
40
|
import { addReplayCommand } from "./commands/replay";
|
|
41
|
+
import { addBehaviorsCommand } from "./commands/behaviors";
|
|
42
|
+
import { addSkillsCommand } from "./commands/skills";
|
|
41
43
|
|
|
42
44
|
// Handle unhandled promise rejections gracefully — particularly from MCP SDK
|
|
43
45
|
// which fires errors via event emitters that can bypass Promise.allSettled.
|
|
@@ -102,6 +104,8 @@ async function main() {
|
|
|
102
104
|
addMcpCommands(program);
|
|
103
105
|
addConvertCommand(program);
|
|
104
106
|
addReplayCommand(program);
|
|
107
|
+
addBehaviorsCommand(program);
|
|
108
|
+
addSkillsCommand(program);
|
|
105
109
|
|
|
106
110
|
// Load global modules early (before parse) so they can register CLI subcommands.
|
|
107
111
|
// We pass only the Program in context — no services are spun up at this stage.
|
package/src/clients/anthropic.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import { wait } from "../utils";
|
|
3
|
-
import { AnthropicTextPricing } from "./pricing";
|
|
3
|
+
import { AnthropicTextPricing, AnthropicModels } from "./pricing";
|
|
4
4
|
import { ContextLimits } from "./contextLimits";
|
|
5
5
|
import { ModelModality } from "./types";
|
|
6
6
|
import { Models } from "../types";
|
|
@@ -24,6 +24,16 @@ import {
|
|
|
24
24
|
|
|
25
25
|
type MessageParam = Anthropic.MessageParam;
|
|
26
26
|
type Usage = Anthropic.Usage;
|
|
27
|
+
type ThinkingParam = Anthropic.Messages.ThinkingConfigParam;
|
|
28
|
+
|
|
29
|
+
const alwaysOnModels: readonly string[] = [
|
|
30
|
+
AnthropicModels.Sonnet5, AnthropicModels.Fable5,
|
|
31
|
+
AnthropicModels.Mythos5, AnthropicModels.MythosPreview,
|
|
32
|
+
];
|
|
33
|
+
const optInModels: readonly string[] = [
|
|
34
|
+
AnthropicModels.Opus4_8, AnthropicModels.Opus4_7,
|
|
35
|
+
AnthropicModels.Opus4_6, AnthropicModels.Sonnet4_6,
|
|
36
|
+
];
|
|
27
37
|
|
|
28
38
|
export class GenericAnthropicClient implements GenericClient {
|
|
29
39
|
private client: Anthropic;
|
|
@@ -48,6 +58,36 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Determine the Anthropic `thinking` param from CompletionOptions.
|
|
63
|
+
* Returns undefined if the model does not support thinking.
|
|
64
|
+
*/
|
|
65
|
+
getThinkingParam(
|
|
66
|
+
model: string,
|
|
67
|
+
reasoning_effort?: CompletionOptions["reasoning_effort"],
|
|
68
|
+
reasoning_summary?: boolean,
|
|
69
|
+
): ThinkingParam | undefined {
|
|
70
|
+
const effortIsNone = reasoning_effort === "none";
|
|
71
|
+
const withSummary = reasoning_summary ? ({ display: "summarized" } as const) : {};
|
|
72
|
+
|
|
73
|
+
if (alwaysOnModels.includes(model)) {
|
|
74
|
+
return effortIsNone
|
|
75
|
+
? { type: "disabled" }
|
|
76
|
+
: { type: "adaptive", ...withSummary };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (optInModels.includes(model)) {
|
|
80
|
+
// Only enable when explicitly requested (not "none" and not undefined).
|
|
81
|
+
if (!effortIsNone && reasoning_effort !== undefined) {
|
|
82
|
+
return { type: "adaptive", ...withSummary };
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Model does not support thinking.
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
51
91
|
/**
|
|
52
92
|
* Clean JSON Schema for Anthropic API compatibility.
|
|
53
93
|
* Removes unsupported fields like additionalProperties, $ref, $defs, positional.
|
|
@@ -360,6 +400,13 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
360
400
|
options: CompletionOptions
|
|
361
401
|
): Promise<CompletionResponse> {
|
|
362
402
|
const longTtl = !!options.long_ttl_cache;
|
|
403
|
+
|
|
404
|
+
const thinkingParam = this.getThinkingParam(
|
|
405
|
+
options.model,
|
|
406
|
+
options.reasoning_effort,
|
|
407
|
+
options.reasoning_summary,
|
|
408
|
+
);
|
|
409
|
+
|
|
363
410
|
const systemMessage = options.messages
|
|
364
411
|
.filter((msg) => msg.role === "system")
|
|
365
412
|
.map((msg) => msg.content || "")
|
|
@@ -382,6 +429,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
382
429
|
]
|
|
383
430
|
: undefined,
|
|
384
431
|
max_tokens: options.max_tokens || 8000,
|
|
432
|
+
...(thinkingParam && { thinking: thinkingParam }),
|
|
385
433
|
...(tools.length && {
|
|
386
434
|
tool_choice: { type: "auto" },
|
|
387
435
|
tools,
|
|
@@ -431,6 +479,16 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
431
479
|
total_tokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0),
|
|
432
480
|
cache_creation_input_tokens: response.usage.cache_creation_input_tokens ?? 0,
|
|
433
481
|
cache_read_input_tokens: response.usage.cache_read_input_tokens ?? 0,
|
|
482
|
+
...((response.usage as any).thinking_input_tokens != null ||
|
|
483
|
+
(response.usage as any).output_tokens_details?.thinking_tokens != null
|
|
484
|
+
? {
|
|
485
|
+
output_tokens_details: {
|
|
486
|
+
thinking_tokens:
|
|
487
|
+
(response.usage as any).thinking_input_tokens ??
|
|
488
|
+
(response.usage as any).output_tokens_details?.thinking_tokens ?? 0,
|
|
489
|
+
},
|
|
490
|
+
}
|
|
491
|
+
: {}),
|
|
434
492
|
} : undefined,
|
|
435
493
|
usd_cost: this.calculateCost(options.model, response.usage),
|
|
436
494
|
};
|
package/src/clients/openai.ts
CHANGED
|
@@ -107,7 +107,11 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
resolveReasoningEffort(options: CompletionOptions): "low" | "medium" | "high" {
|
|
110
|
-
|
|
110
|
+
const effort = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
|
|
111
|
+
// "none" is not a valid SDK ReasoningEffort — callers that need "none" should
|
|
112
|
+
// check isReasoningDisabled() separately and skip the reasoning params.
|
|
113
|
+
if (effort === "none") return "low";
|
|
114
|
+
return effort;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
/**
|
|
@@ -116,7 +120,8 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
116
120
|
* If the requested level is not supported, picks the lowest supported level.
|
|
117
121
|
*/
|
|
118
122
|
resolveReasoningEffortForModel(options: CompletionOptions): string {
|
|
119
|
-
const
|
|
123
|
+
const raw = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
|
|
124
|
+
const requested = raw === "none" ? "none" : raw;
|
|
120
125
|
const pricing = OpenAiTextPricing[options.model];
|
|
121
126
|
const supportedLevels = pricing?.reasoningLevels;
|
|
122
127
|
if (!supportedLevels || supportedLevels.length === 0) {
|
|
@@ -130,6 +135,11 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
130
135
|
return supportedLevels[0];
|
|
131
136
|
}
|
|
132
137
|
|
|
138
|
+
/** Returns true when the caller explicitly requested thinking/reasoning to be disabled. */
|
|
139
|
+
isReasoningDisabled(options: CompletionOptions): boolean {
|
|
140
|
+
return options.reasoning_effort === "none";
|
|
141
|
+
}
|
|
142
|
+
|
|
133
143
|
async createChatCompletion(
|
|
134
144
|
options: CompletionOptions
|
|
135
145
|
): Promise<CompletionResponse> {
|
|
@@ -154,7 +164,7 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
154
164
|
model: options.model,
|
|
155
165
|
messages: openaiMessages,
|
|
156
166
|
max_tokens: options.max_tokens,
|
|
157
|
-
...(OpenAiReasoningModels.includes(options.model) && {
|
|
167
|
+
...(OpenAiReasoningModels.includes(options.model) && !this.isReasoningDisabled(options) && {
|
|
158
168
|
max_tokens: undefined,
|
|
159
169
|
max_completion_tokens: Math.max(options.max_tokens ?? 0, 16_000),
|
|
160
170
|
reasoning_effort: this.resolveReasoningEffort(options),
|
|
@@ -186,6 +196,11 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
186
196
|
prompt_tokens_details: {
|
|
187
197
|
cached_tokens: response.usage.prompt_tokens_details?.cached_tokens ?? 0,
|
|
188
198
|
},
|
|
199
|
+
...(response.usage.completion_tokens_details?.reasoning_tokens != null && {
|
|
200
|
+
output_tokens_details: {
|
|
201
|
+
reasoning_tokens: response.usage.completion_tokens_details.reasoning_tokens,
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
189
204
|
} : undefined,
|
|
190
205
|
usd_cost: usdCost,
|
|
191
206
|
};
|
|
@@ -350,9 +365,12 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
350
365
|
input,
|
|
351
366
|
...(instructions && { instructions }),
|
|
352
367
|
// Don't limit max_output_tokens for Responses API - codex truncates tool call arguments when limited
|
|
353
|
-
...(OpenAiReasoningModels.includes(options.model) && {
|
|
368
|
+
...(OpenAiReasoningModels.includes(options.model) && !this.isReasoningDisabled(options) && {
|
|
354
369
|
max_output_tokens: Math.max(options.max_tokens || 0, 16000),
|
|
355
|
-
reasoning: {
|
|
370
|
+
reasoning: {
|
|
371
|
+
effort: this.resolveReasoningEffortForModel(options),
|
|
372
|
+
...(options.reasoning_summary && { summary: "auto" }),
|
|
373
|
+
},
|
|
356
374
|
}),
|
|
357
375
|
...(tools?.length && {
|
|
358
376
|
tools,
|
|
@@ -372,6 +390,11 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
372
390
|
cached_tokens:
|
|
373
391
|
response.usage.input_tokens_details?.cached_tokens ?? 0,
|
|
374
392
|
},
|
|
393
|
+
...((response.usage as any).output_tokens_details?.reasoning_tokens != null && {
|
|
394
|
+
output_tokens_details: {
|
|
395
|
+
reasoning_tokens: (response.usage as any).output_tokens_details.reasoning_tokens,
|
|
396
|
+
},
|
|
397
|
+
}),
|
|
375
398
|
}
|
|
376
399
|
: undefined;
|
|
377
400
|
|
|
@@ -167,6 +167,9 @@ export const OpenAiResponsesOnlyModels: string[] = [
|
|
|
167
167
|
OpenAiModels.GPT_55_Pro,
|
|
168
168
|
OpenAiModels.GPT_55,
|
|
169
169
|
OpenAiModels.GPT_5_Pro,
|
|
170
|
+
OpenAiModels.GPT_56_Sol,
|
|
171
|
+
OpenAiModels.GPT_56_Terra,
|
|
172
|
+
OpenAiModels.GPT_56_Luna,
|
|
170
173
|
OpenAiModels.o1,
|
|
171
174
|
OpenAiModels.o1_Pro,
|
|
172
175
|
];
|
package/src/clients/types.ts
CHANGED
|
@@ -86,10 +86,26 @@ export interface CompletionOptions extends RetryOptions {
|
|
|
86
86
|
tools?: Tool[];
|
|
87
87
|
tool_choice?: "auto" | "none";
|
|
88
88
|
max_tokens?: number;
|
|
89
|
-
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Reasoning effort level for models that support it.
|
|
91
|
+
* Maps to: OpenAI reasoning_effort/reasoning.effort, xAI reasoning.effort,
|
|
92
|
+
* Gemini thinkingLevel/thinkingBudget, Anthropic thinking mode.
|
|
93
|
+
*
|
|
94
|
+
* "none" = disable thinking entirely (Anthropic: thinking.type="disabled",
|
|
95
|
+
* OpenAI: reasoning.effort="none"). Only supported by models that
|
|
96
|
+
* allow disabling thinking (e.g. claude-sonnet-5).
|
|
97
|
+
* "low" = minimal thinking
|
|
98
|
+
* "medium" = balanced (default for most models)
|
|
99
|
+
* "high" = maximum reasoning
|
|
100
|
+
*/
|
|
101
|
+
reasoning_effort?: "none" | "low" | "medium" | "high";
|
|
102
|
+
/**
|
|
103
|
+
* When true, request a summarized view of the model's thinking process.
|
|
104
|
+
* - Anthropic: thinking.display = "summarized"
|
|
105
|
+
* - OpenAI: reasoning.summary = "auto"
|
|
106
|
+
* Defaults to false (no summary displayed).
|
|
107
|
+
*/
|
|
108
|
+
reasoning_summary?: boolean;
|
|
93
109
|
/**
|
|
94
110
|
* When true, hints to the client that this task is long-running and it should
|
|
95
111
|
* use a long-TTL cache where available.
|
|
@@ -115,6 +131,13 @@ export interface TokenUsage {
|
|
|
115
131
|
output_tokens?: number;
|
|
116
132
|
/** Convenience total (prompt + completion) */
|
|
117
133
|
total_tokens?: number;
|
|
134
|
+
/** Breakdown of output tokens — thinking/reasoning tokens are billed as output
|
|
135
|
+
* but should NOT be counted toward the visible-content heuristics. */
|
|
136
|
+
output_tokens_details?: {
|
|
137
|
+
/** Anthropic: thinking_tokens, OpenAI: reasoning_tokens */
|
|
138
|
+
thinking_tokens?: number;
|
|
139
|
+
reasoning_tokens?: number;
|
|
140
|
+
};
|
|
118
141
|
/** Cache details */
|
|
119
142
|
prompt_tokens_details?: {
|
|
120
143
|
/** Tokens served from the prompt cache (reduces cost) */
|
package/src/clients/xai.ts
CHANGED
|
@@ -76,9 +76,9 @@ export class GenericXAIClient implements GenericClient {
|
|
|
76
76
|
model: options.model,
|
|
77
77
|
messages: xaiMessages,
|
|
78
78
|
max_tokens: options.max_tokens,
|
|
79
|
-
...(XaiReasoningModels.includes(options.model) && options.reasoning_effort && {
|
|
79
|
+
...(XaiReasoningModels.includes(options.model) && options.reasoning_effort && options.reasoning_effort !== "none" && {
|
|
80
80
|
// grok-3-mini models support reasoning_effort: "low" | "medium" | "high"
|
|
81
|
-
reasoning_effort: options.reasoning_effort,
|
|
81
|
+
reasoning_effort: options.reasoning_effort as "low" | "medium" | "high",
|
|
82
82
|
}),
|
|
83
83
|
...(options.tools && {
|
|
84
84
|
tools: options.tools,
|
package/src/commands/agent.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { readPromptFile } from "../ai";
|
|
3
3
|
import { AgentModule } from "../chat/modules/AgentModule";
|
|
4
|
+
import { BehaviorsService, Behavior } from "../services/BehaviorsService";
|
|
4
5
|
import { AskModule } from "../chat/modules/AskModule";
|
|
5
6
|
import { SearchModule } from "../chat/modules/SearchModule";
|
|
6
7
|
import { SessionsModule } from "../chat/modules/SessionsModule";
|
|
@@ -131,11 +132,29 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
await agentModule.initialize(chatService);
|
|
135
|
+
|
|
136
|
+
// Match a behavior from local disk (.knowhow/behaviors/)
|
|
137
|
+
let behaviorSystemPrompt: string | undefined;
|
|
138
|
+
let behaviorModel: string | undefined;
|
|
139
|
+
const behaviorsSvc = new BehaviorsService();
|
|
140
|
+
const matchedBehavior = behaviorsSvc.matchBehaviorLocal(input);
|
|
141
|
+
if (matchedBehavior) {
|
|
142
|
+
console.log(`🎯 Matched behavior: ${matchedBehavior.name}`);
|
|
143
|
+
if (matchedBehavior.instructions) {
|
|
144
|
+
behaviorSystemPrompt = matchedBehavior.instructions;
|
|
145
|
+
}
|
|
146
|
+
if (matchedBehavior.model) {
|
|
147
|
+
behaviorModel = matchedBehavior.model;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
134
151
|
const { taskCompleted } = await agentModule.setupAgent({
|
|
135
152
|
...options,
|
|
136
153
|
input,
|
|
137
154
|
maxTimeLimit: parseInt(options.maxTimeLimit, 10),
|
|
138
155
|
maxSpendLimit: parseFloat(options.maxSpendLimit),
|
|
156
|
+
...(behaviorSystemPrompt ? { systemPrompt: behaviorSystemPrompt } : {}),
|
|
157
|
+
...(behaviorModel ? { model: behaviorModel } : {}),
|
|
139
158
|
run: true,
|
|
140
159
|
});
|
|
141
160
|
await taskCompleted;
|