@tyvm/knowhow 0.0.129 → 0.0.131
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/configurable/ConfigAgent.ts +0 -1
- 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/agents/tools/startAgentTask.ts +17 -13
- package/src/ai.ts +55 -24
- package/src/chat/CliChatService.ts +7 -1
- package/src/chat/modules/AgentModule.ts +156 -15
- package/src/chat/renderer/PlainRenderer.ts +218 -0
- package/src/chat/renderer/index.ts +1 -0
- package/src/chat/renderer/loadRenderer.ts +2 -0
- package/src/chat.ts +2 -2
- package/src/cli.ts +10 -0
- package/src/clients/anthropic.ts +68 -2
- package/src/clients/fireworks.ts +10 -1
- package/src/clients/index.ts +5 -3
- package/src/clients/meta.ts +9 -0
- package/src/clients/openai.ts +28 -5
- package/src/clients/pricing/anthropic.ts +11 -0
- package/src/clients/pricing/fireworks.ts +12 -0
- 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 +70 -18
- package/src/commands/agents.ts +556 -0
- package/src/commands/behaviors.ts +47 -0
- package/src/commands/hash.ts +124 -0
- package/src/commands/misc.ts +157 -3
- package/src/commands/services.ts +6 -2
- package/src/commands/skills.ts +107 -0
- package/src/config.ts +51 -1
- package/src/generate.ts +507 -0
- package/src/index.ts +34 -216
- 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 +464 -0
- package/src/services/KnowhowClient.ts +61 -0
- package/src/services/SkillsService.ts +820 -0
- package/src/services/Tools.ts +2 -0
- package/src/services/index.ts +8 -2
- package/src/services/modules/types.ts +2 -0
- package/src/types.ts +36 -4
- 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/configurable/ConfigAgent.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/agents/tools/startAgentTask.d.ts +2 -2
- package/ts_build/src/agents/tools/startAgentTask.js +13 -12
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/ai.d.ts +10 -4
- package/ts_build/src/ai.js +27 -25
- package/ts_build/src/ai.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +1 -1
- package/ts_build/src/chat/CliChatService.js +4 -1
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +2 -0
- package/ts_build/src/chat/modules/AgentModule.js +130 -4
- 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/chat/renderer/PlainRenderer.d.ts +28 -0
- package/ts_build/src/chat/renderer/PlainRenderer.js +169 -0
- package/ts_build/src/chat/renderer/PlainRenderer.js.map +1 -0
- package/ts_build/src/chat/renderer/index.d.ts +1 -0
- package/ts_build/src/chat/renderer/index.js +3 -1
- package/ts_build/src/chat/renderer/index.js.map +1 -1
- package/ts_build/src/chat/renderer/loadRenderer.js +2 -0
- package/ts_build/src/chat/renderer/loadRenderer.js.map +1 -1
- package/ts_build/src/chat.d.ts +1 -1
- package/ts_build/src/chat.js +2 -2
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.js +9 -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 +39 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/fireworks.js +7 -0
- package/ts_build/src/clients/fireworks.js.map +1 -1
- package/ts_build/src/clients/index.js +3 -1
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/meta.d.ts +2 -0
- package/ts_build/src/clients/meta.js +5 -0
- package/ts_build/src/clients/meta.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/anthropic.d.ts +3 -0
- package/ts_build/src/clients/pricing/anthropic.js +6 -0
- package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +1 -0
- package/ts_build/src/clients/pricing/fireworks.js +5 -1
- package/ts_build/src/clients/pricing/fireworks.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 +58 -15
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/agents.d.ts +2 -0
- package/ts_build/src/commands/agents.js +460 -0
- package/ts_build/src/commands/agents.js.map +1 -0
- 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/hash.d.ts +2 -0
- package/ts_build/src/commands/hash.js +117 -0
- package/ts_build/src/commands/hash.js.map +1 -0
- package/ts_build/src/commands/misc.js +108 -2
- package/ts_build/src/commands/misc.js.map +1 -1
- 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 +85 -0
- package/ts_build/src/commands/skills.js.map +1 -0
- package/ts_build/src/config.js +34 -0
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/generate.d.ts +16 -0
- package/ts_build/src/generate.js +304 -0
- package/ts_build/src/generate.js.map +1 -0
- package/ts_build/src/index.d.ts +3 -3
- package/ts_build/src/index.js +13 -118
- 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 +48 -0
- package/ts_build/src/services/BehaviorsService.js +382 -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 +60 -0
- package/ts_build/src/services/SkillsService.js +641 -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/ts_build/src/types.d.ts +14 -4
- package/ts_build/src/types.js.map +1 -1
|
@@ -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",
|
|
@@ -656,22 +733,24 @@ export class AgentModule extends BaseChatModule {
|
|
|
656
733
|
run?: boolean; // whether to run immediately
|
|
657
734
|
taskId?: string; // optional pre-generated taskId
|
|
658
735
|
}) {
|
|
659
|
-
if (!agentConstructors[options.agentName as AgentName]) {
|
|
660
|
-
throw new Error(
|
|
661
|
-
`Agent "${
|
|
662
|
-
options.agentName
|
|
663
|
-
}" not found. Available agents: ${Object.keys(agentConstructors).join(
|
|
664
|
-
", "
|
|
665
|
-
)}`
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
736
|
const { input, chatHistory = [], agentName } = options;
|
|
670
737
|
const agentContext = services().Agents.getAgentContext();
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
738
|
+
|
|
739
|
+
// Resolve agent: built-in agents first, then fall back to ConfigAgents from knowhow.json
|
|
740
|
+
let agent: BaseAgent;
|
|
741
|
+
if (agentConstructors[options.agentName as AgentName]) {
|
|
742
|
+
agent = createAgent(options.agentName as AgentName, agentContext) as BaseAgent;
|
|
743
|
+
} else {
|
|
744
|
+
// Try to find a ConfigAgent registered via loadAgentsFromConfig (from knowhow.json agents[])
|
|
745
|
+
try {
|
|
746
|
+
const configAgent = services().Agents.getAgent(options.agentName);
|
|
747
|
+
agent = configAgent as unknown as BaseAgent;
|
|
748
|
+
} catch {
|
|
749
|
+
throw new Error(
|
|
750
|
+
`Agent "${options.agentName}" not found. Available agents: ${Object.keys(agentConstructors).join(", ")} (plus any agents defined in knowhow.json)`
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
675
754
|
|
|
676
755
|
let done = false;
|
|
677
756
|
let output = "Done";
|
|
@@ -754,6 +833,12 @@ export class AgentModule extends BaseChatModule {
|
|
|
754
833
|
]);
|
|
755
834
|
}
|
|
756
835
|
|
|
836
|
+
// Apply module-level reasoning settings to the agent
|
|
837
|
+
if (this.reasoningEffort !== undefined) {
|
|
838
|
+
agent.setReasoningEffort(this.reasoningEffort);
|
|
839
|
+
}
|
|
840
|
+
agent.setSummarizeReasoning(this.summarizeReasoning);
|
|
841
|
+
|
|
757
842
|
// Set up message processors like in original startAgent
|
|
758
843
|
|
|
759
844
|
// Register an override for askHuman to use CLI input method
|
|
@@ -832,7 +917,63 @@ export class AgentModule extends BaseChatModule {
|
|
|
832
917
|
agent.agentEvents.on(agent.eventTypes.agentLog, agentLogHandler);
|
|
833
918
|
agent.agentEvents.on(agent.eventTypes.agentStatus, agentStatusHandler);
|
|
834
919
|
|
|
920
|
+
// Wire tool call / tool result / agent message rendering so that all
|
|
921
|
+
// renderer types (including PlainRenderer for non-TTY) receive events
|
|
922
|
+
// even when running via the CLI `knowhow agent` command (which uses
|
|
923
|
+
// run:true and does NOT go through attachedAgentChatLoop / wireAgentRendering).
|
|
924
|
+
const toolCallHandler = (data: any) => {
|
|
925
|
+
this.renderer.render({
|
|
926
|
+
type: "toolCall",
|
|
927
|
+
taskId,
|
|
928
|
+
agentName: agent.name,
|
|
929
|
+
toolCall: data.toolCall,
|
|
930
|
+
});
|
|
931
|
+
};
|
|
932
|
+
const toolResultHandler = (data: any) => {
|
|
933
|
+
this.renderer.render({
|
|
934
|
+
type: "toolResult",
|
|
935
|
+
taskId,
|
|
936
|
+
agentName: agent.name,
|
|
937
|
+
toolCall: data.toolCall,
|
|
938
|
+
result: data.functionResp,
|
|
939
|
+
});
|
|
940
|
+
};
|
|
941
|
+
const agentSayHandler = (data: any) => {
|
|
942
|
+
this.renderer.render({
|
|
943
|
+
type: "agentMessage",
|
|
944
|
+
taskId,
|
|
945
|
+
agentName: agent.name,
|
|
946
|
+
message: data.message,
|
|
947
|
+
role: "assistant",
|
|
948
|
+
});
|
|
949
|
+
};
|
|
950
|
+
if (agent.eventTypes.toolCall) {
|
|
951
|
+
agent.agentEvents.on(agent.eventTypes.toolCall, toolCallHandler);
|
|
952
|
+
}
|
|
953
|
+
if (agent.eventTypes.toolUsed) {
|
|
954
|
+
agent.agentEvents.on(agent.eventTypes.toolUsed, toolResultHandler);
|
|
955
|
+
}
|
|
956
|
+
if (agent.eventTypes.agentSay) {
|
|
957
|
+
agent.agentEvents.on(agent.eventTypes.agentSay, agentSayHandler);
|
|
958
|
+
}
|
|
959
|
+
// Track the active task on the renderer so it doesn't filter out events
|
|
960
|
+
this.renderer.setActiveTaskId(taskId);
|
|
961
|
+
this.activeAgentTaskId = taskId;
|
|
962
|
+
|
|
835
963
|
const taskCompleted = new Promise<string>((resolve) => {
|
|
964
|
+
// Remove tool rendering listeners
|
|
965
|
+
if (agent.eventTypes.toolCall) {
|
|
966
|
+
agent.agentEvents.removeListener(agent.eventTypes.toolCall, toolCallHandler);
|
|
967
|
+
}
|
|
968
|
+
if (agent.eventTypes.toolUsed) {
|
|
969
|
+
agent.agentEvents.removeListener(agent.eventTypes.toolUsed, toolResultHandler);
|
|
970
|
+
}
|
|
971
|
+
if (agent.eventTypes.agentSay) {
|
|
972
|
+
agent.agentEvents.removeListener(agent.eventTypes.agentSay, agentSayHandler);
|
|
973
|
+
}
|
|
974
|
+
// Clear active task on renderer
|
|
975
|
+
this.renderer.setActiveTaskId(undefined);
|
|
976
|
+
this.activeAgentTaskId = undefined;
|
|
836
977
|
agent.agentEvents.once(agent.eventTypes.done, async (doneMsg) => {
|
|
837
978
|
console.log("🎯 [AgentModule] Task Completed");
|
|
838
979
|
done = true;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlainRenderer - A plain-text, append-only renderer for non-TTY environments.
|
|
3
|
+
*
|
|
4
|
+
* Used automatically when stdout is not a TTY (e.g. redirected to a file,
|
|
5
|
+
* piped, or run in background). Emits simple timestamped log lines with no
|
|
6
|
+
* ANSI codes, no cursor movement, and no spinner — safe for logfiles.
|
|
7
|
+
*
|
|
8
|
+
* Activate explicitly via:
|
|
9
|
+
* knowhow agent --renderer plain ...
|
|
10
|
+
* or it is selected automatically when !process.stdout.isTTY and no renderer
|
|
11
|
+
* is configured.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
AgentRenderer,
|
|
16
|
+
LogEvent,
|
|
17
|
+
AgentStatusEvent,
|
|
18
|
+
ToolCallEvent,
|
|
19
|
+
ToolResultEvent,
|
|
20
|
+
AgentMessageEvent,
|
|
21
|
+
AgentDoneEvent,
|
|
22
|
+
RenderEvent,
|
|
23
|
+
} from "./types";
|
|
24
|
+
import { EventEmitter } from "events";
|
|
25
|
+
|
|
26
|
+
function ts(): string {
|
|
27
|
+
return new Date().toISOString();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function truncate(s: string, n: number): string {
|
|
31
|
+
return s.length > n ? s.slice(0, n - 1) + "…" : s;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatArgs(raw: string): string {
|
|
35
|
+
try {
|
|
36
|
+
const obj = JSON.parse(raw || "{}");
|
|
37
|
+
const entries = Object.entries(obj);
|
|
38
|
+
if (!entries.length) return "{}";
|
|
39
|
+
return entries
|
|
40
|
+
.map(([k, v]) => {
|
|
41
|
+
const val = typeof v === "string" ? v : JSON.stringify(v);
|
|
42
|
+
return `${k}=${truncate(val, 80)}`;
|
|
43
|
+
})
|
|
44
|
+
.join(", ");
|
|
45
|
+
} catch {
|
|
46
|
+
return truncate(raw || "", 120);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function formatResult(result: any): string {
|
|
51
|
+
try {
|
|
52
|
+
const s = typeof result === "string" ? result : JSON.stringify(result);
|
|
53
|
+
return truncate(s.replace(/\s+/g, " "), 200);
|
|
54
|
+
} catch {
|
|
55
|
+
return truncate(String(result), 200);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class PlainRenderer implements AgentRenderer {
|
|
60
|
+
private activeTaskId: string | undefined;
|
|
61
|
+
private emitter = new EventEmitter();
|
|
62
|
+
private paused = false;
|
|
63
|
+
private bufferedEvents: RenderEvent[] = [];
|
|
64
|
+
|
|
65
|
+
setActiveTaskId(taskId: string | undefined): void {
|
|
66
|
+
this.activeTaskId = taskId;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getActiveTaskId(): string | undefined {
|
|
70
|
+
return this.activeTaskId;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private isActiveTask(taskId: string): boolean {
|
|
74
|
+
if (!this.activeTaskId) return true;
|
|
75
|
+
return taskId === this.activeTaskId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onLog(handler: (event: LogEvent) => void): void {
|
|
79
|
+
this.emitter.on("log", handler);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
onAgentStatus(handler: (event: AgentStatusEvent) => void): void {
|
|
83
|
+
this.emitter.on("agentStatus", handler);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onToolCall(handler: (event: ToolCallEvent) => void): void {
|
|
87
|
+
this.emitter.on("toolCall", handler);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
onToolResult(handler: (event: ToolResultEvent) => void): void {
|
|
91
|
+
this.emitter.on("toolResult", handler);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onAgentMessage(handler: (event: AgentMessageEvent) => void): void {
|
|
95
|
+
this.emitter.on("agentMessage", handler);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onAgentDone(handler: (event: AgentDoneEvent) => void): void {
|
|
99
|
+
this.emitter.on("agentDone", handler);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pause(): void {
|
|
103
|
+
this.paused = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
resume(): void {
|
|
107
|
+
this.paused = false;
|
|
108
|
+
const buffered = this.bufferedEvents.splice(0);
|
|
109
|
+
for (const event of buffered) {
|
|
110
|
+
this.render(event);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
render(event: RenderEvent): void {
|
|
115
|
+
if (!this.isActiveTask(event.taskId)) return;
|
|
116
|
+
|
|
117
|
+
if (this.paused) {
|
|
118
|
+
this.bufferedEvents.push(event);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.emitter.emit(event.type, event);
|
|
123
|
+
|
|
124
|
+
switch (event.type) {
|
|
125
|
+
case "log":
|
|
126
|
+
this._log(event);
|
|
127
|
+
break;
|
|
128
|
+
case "agentStatus":
|
|
129
|
+
this._status(event);
|
|
130
|
+
break;
|
|
131
|
+
case "toolCall":
|
|
132
|
+
this._toolCall(event);
|
|
133
|
+
break;
|
|
134
|
+
case "toolResult":
|
|
135
|
+
this._toolResult(event);
|
|
136
|
+
break;
|
|
137
|
+
case "agentMessage":
|
|
138
|
+
this._message(event);
|
|
139
|
+
break;
|
|
140
|
+
case "agentDone":
|
|
141
|
+
this._done(event);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private _write(line: string): void {
|
|
147
|
+
process.stdout.write(line + "\n");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private _log(e: LogEvent): void {
|
|
151
|
+
const level = e.level.toUpperCase().padEnd(5);
|
|
152
|
+
this._write(`${ts()} [${level}] [${e.agentName}] ${e.message}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private _status(e: AgentStatusEvent): void {
|
|
156
|
+
const cost = `$${e.details.totalCostUsd.toFixed(3)}`;
|
|
157
|
+
const elapsed = `${Math.floor(e.details.elapsedMs / 1000)}s`;
|
|
158
|
+
const parts = [`cost=${cost}`, `elapsed=${elapsed}`];
|
|
159
|
+
if (e.details.remainingTurns !== undefined) {
|
|
160
|
+
parts.push(`turns_left=${e.details.remainingTurns}`);
|
|
161
|
+
}
|
|
162
|
+
if (e.details.remainingTimeMs !== undefined) {
|
|
163
|
+
parts.push(`time_left=${Math.floor(e.details.remainingTimeMs / 1000)}s`);
|
|
164
|
+
}
|
|
165
|
+
this._write(
|
|
166
|
+
`${ts()} [STATUS] [${e.agentName}] ${e.statusMessage} | ${parts.join(" | ")}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private _toolCall(e: ToolCallEvent): void {
|
|
171
|
+
const args = formatArgs(e.toolCall.function.arguments);
|
|
172
|
+
this._write(
|
|
173
|
+
`${ts()} [TOOL->] [${e.agentName}] ${e.toolCall.function.name}(${args})`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private _toolResult(e: ToolResultEvent): void {
|
|
178
|
+
const result = formatResult(e.result);
|
|
179
|
+
this._write(
|
|
180
|
+
`${ts()} [TOOL<-] [${e.agentName}] ${e.toolCall.function.name} => ${result}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private _message(e: AgentMessageEvent): void {
|
|
185
|
+
if (e.role !== "assistant") return;
|
|
186
|
+
// Print the message with line-by-line prefix
|
|
187
|
+
const lines = e.message.split("\n");
|
|
188
|
+
for (const line of lines) {
|
|
189
|
+
if (line.trim()) {
|
|
190
|
+
this._write(`${ts()} [MSG ] [${e.agentName}] ${line}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private _done(e: AgentDoneEvent): void {
|
|
196
|
+
const cost = e.totalCost ? ` | total_cost=$${e.totalCost.toFixed(4)}` : "";
|
|
197
|
+
this._write(`${ts()} [DONE ] [${e.agentName}] Agent finished${cost}`);
|
|
198
|
+
// Print final output lines
|
|
199
|
+
const lines = e.output.split("\n");
|
|
200
|
+
for (const line of lines) {
|
|
201
|
+
if (line.trim()) {
|
|
202
|
+
this._write(`${ts()} [OUT ] [${e.agentName}] ${line}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
logMessages(events: RenderEvent[], count: number = 20): void {
|
|
208
|
+
const recent = events.slice(-count);
|
|
209
|
+
this._write(`\n[LOGS ] Last ${recent.length} events:`);
|
|
210
|
+
this._write("-".repeat(80));
|
|
211
|
+
for (const event of recent) {
|
|
212
|
+
this.render(event);
|
|
213
|
+
}
|
|
214
|
+
this._write("-".repeat(80));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export default PlainRenderer;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
2
|
export { ConsoleRenderer } from "./ConsoleRenderer";
|
|
3
3
|
export { FancyRenderer } from "./FancyRenderer";
|
|
4
|
+
export { PlainRenderer } from "./PlainRenderer";
|
|
4
5
|
export { CompactRenderer } from "./CompactRenderer";
|
|
5
6
|
export { loadRenderer, loadRootModule, loadChatModule } from "./loadRenderer";
|
|
@@ -12,6 +12,7 @@ import path from "path";
|
|
|
12
12
|
import fs from "fs";
|
|
13
13
|
import { AgentRenderer } from "./types";
|
|
14
14
|
import { ConsoleRenderer } from "./ConsoleRenderer";
|
|
15
|
+
import { PlainRenderer } from "./PlainRenderer";
|
|
15
16
|
import { FancyRenderer } from "./FancyRenderer";
|
|
16
17
|
import { CompactRenderer } from "./CompactRenderer";
|
|
17
18
|
|
|
@@ -19,6 +20,7 @@ import { CompactRenderer } from "./CompactRenderer";
|
|
|
19
20
|
const BUILTIN_RENDERERS: Record<string, () => AgentRenderer> = {
|
|
20
21
|
basic: () => new ConsoleRenderer(),
|
|
21
22
|
fancy: () => new FancyRenderer(),
|
|
23
|
+
plain: () => new PlainRenderer(),
|
|
22
24
|
compact: () => new CompactRenderer(),
|
|
23
25
|
};
|
|
24
26
|
|
package/src/chat.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { getConfig } from "./config.js";
|
|
|
21
21
|
import { loadRenderer, loadRootModule, loadChatModule } from "./chat/renderer/loadRenderer.js";
|
|
22
22
|
import { AgentModule } from "./chat/modules/AgentModule.js";
|
|
23
23
|
|
|
24
|
-
async function main() {
|
|
24
|
+
async function main(initialCommand?: string) {
|
|
25
25
|
try {
|
|
26
26
|
// Load configuration and plugins
|
|
27
27
|
let config;
|
|
@@ -108,7 +108,7 @@ async function main() {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// Start the chat loop
|
|
111
|
-
await chatService.startChatLoop();
|
|
111
|
+
await chatService.startChatLoop(initialCommand);
|
|
112
112
|
} catch (error) {
|
|
113
113
|
console.error("Error starting chat:", error);
|
|
114
114
|
process.exit(1);
|
package/src/cli.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node --no-node-snapshot
|
|
2
|
+
// Mark this process as running the Knowhow CLI (not just the SDK)
|
|
3
|
+
process.env.KNOWHOW_CLI = "1";
|
|
2
4
|
import { Command } from "commander";
|
|
3
5
|
import { version } from "../package.json";
|
|
4
6
|
import { logger } from "./logger";
|
|
@@ -38,6 +40,10 @@ import {
|
|
|
38
40
|
} from "./commands/misc";
|
|
39
41
|
import { addConvertCommand } from "./commands/convert";
|
|
40
42
|
import { addReplayCommand } from "./commands/replay";
|
|
43
|
+
import { addBehaviorsCommand } from "./commands/behaviors";
|
|
44
|
+
import { addSkillsCommand } from "./commands/skills";
|
|
45
|
+
import { addAgentsCommand } from "./commands/agents";
|
|
46
|
+
import { addHashCommand } from "./commands/hash";
|
|
41
47
|
|
|
42
48
|
// Handle unhandled promise rejections gracefully — particularly from MCP SDK
|
|
43
49
|
// which fires errors via event emitters that can bypass Promise.allSettled.
|
|
@@ -102,6 +108,10 @@ async function main() {
|
|
|
102
108
|
addMcpCommands(program);
|
|
103
109
|
addConvertCommand(program);
|
|
104
110
|
addReplayCommand(program);
|
|
111
|
+
addBehaviorsCommand(program);
|
|
112
|
+
addSkillsCommand(program);
|
|
113
|
+
addAgentsCommand(program);
|
|
114
|
+
addHashCommand(program);
|
|
105
115
|
|
|
106
116
|
// Load global modules early (before parse) so they can register CLI subcommands.
|
|
107
117
|
// 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
|
};
|
|
@@ -461,7 +519,15 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
461
519
|
const rawP = this.pricesPerMillion()[model];
|
|
462
520
|
// Fall back to pricing file for unknown/newer models
|
|
463
521
|
const fallback = AnthropicTextPricing[model as keyof typeof AnthropicTextPricing];
|
|
464
|
-
|
|
522
|
+
// Second fallback: short aliases like "claude-sonnet-4-5" don't have their
|
|
523
|
+
// own entry but map to a dated version like "claude-sonnet-4-5-20250929".
|
|
524
|
+
// Find the first pricing entry whose key starts with the short model name.
|
|
525
|
+
const aliasFallback = !rawP && !fallback
|
|
526
|
+
? Object.entries(AnthropicTextPricing).find(([key]) =>
|
|
527
|
+
key.startsWith(model + "-")
|
|
528
|
+
)?.[1]
|
|
529
|
+
: undefined;
|
|
530
|
+
const p: any = rawP || fallback || aliasFallback || undefined;
|
|
465
531
|
if (!p) return undefined;
|
|
466
532
|
|
|
467
533
|
const inputTokens = usage.input_tokens ?? 0;
|
package/src/clients/fireworks.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from "./http";
|
|
2
|
-
import { FireworksTextPricing } from "./pricing/fireworks";
|
|
2
|
+
import { FireworksTextPricing, FireworksEmbeddingPricing } from "./pricing/fireworks";
|
|
3
3
|
import { CompletionOptions, CompletionResponse } from "./types";
|
|
4
4
|
|
|
5
5
|
type ModelInfo = { id: string; object: string; owned_by: string };
|
|
@@ -22,6 +22,15 @@ export class GenericFireworksClient extends HttpClient {
|
|
|
22
22
|
* Our pricing map is the authoritative source of truth for available models.
|
|
23
23
|
*/
|
|
24
24
|
async getModels(_type = "all"): Promise<ModelInfo[]> {
|
|
25
|
+
// Return embedding/reranker models when requested
|
|
26
|
+
if (_type === "embedding") {
|
|
27
|
+
return Object.keys(FireworksEmbeddingPricing).map((id) => ({
|
|
28
|
+
id,
|
|
29
|
+
object: "model",
|
|
30
|
+
owned_by: "fireworks",
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
return Object.keys(FireworksTextPricing).map((id) => ({
|
|
26
35
|
id,
|
|
27
36
|
object: "model",
|
package/src/clients/index.ts
CHANGED
|
@@ -310,9 +310,11 @@ export class AIClient {
|
|
|
310
310
|
|
|
311
311
|
if (!client) {
|
|
312
312
|
if (entry.provider === "knowhow") {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
if (process.env.KNOWHOW_CLI) {
|
|
314
|
+
console.warn(
|
|
315
|
+
`⚠️ Knowhow provider is not logged in. Run 'knowhow login' to enable Knowhow models.`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
316
318
|
}
|
|
317
319
|
continue;
|
|
318
320
|
}
|
package/src/clients/meta.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HttpClient } from "./http";
|
|
2
2
|
import { MetaTextPricing } from "./pricing/meta";
|
|
3
|
+
import { ModelModality } from "./types";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Meta Model API client — OpenAI-compatible API
|
|
@@ -15,4 +16,12 @@ export class GenericMetaClient extends HttpClient {
|
|
|
15
16
|
if (apiKey) this.setJwt(apiKey);
|
|
16
17
|
this.setPrices(MetaTextPricing);
|
|
17
18
|
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Meta Model API only provides chat completion models — no embeddings, images, audio, or video.
|
|
22
|
+
*/
|
|
23
|
+
async getModels(type: ModelModality | "all" = "all") {
|
|
24
|
+
if (type === "embedding") return [];
|
|
25
|
+
return super.getModels(type as string);
|
|
26
|
+
}
|
|
18
27
|
}
|