@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tyvm/knowhow",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.131",
|
|
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
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AgentContext, BaseAgent } from "../base/base";
|
|
2
2
|
import { Message } from "../../clients/types";
|
|
3
3
|
import { Assistant, Config } from "../../types";
|
|
4
|
-
import { EventService, ToolsService } from "../../services";
|
|
5
4
|
import { MessageProcessor } from "../../services/MessageProcessor";
|
|
6
5
|
|
|
7
6
|
export class ConfigAgent extends BaseAgent {
|
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
|
+
];
|
|
@@ -3,7 +3,7 @@ import * as fs from "fs";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
5
|
|
|
6
|
-
interface StartAgentTaskParams {
|
|
6
|
+
export interface StartAgentTaskParams {
|
|
7
7
|
messageId?: string;
|
|
8
8
|
syncFs?: boolean;
|
|
9
9
|
taskId?: string;
|
|
@@ -14,6 +14,8 @@ interface StartAgentTaskParams {
|
|
|
14
14
|
agentName?: string;
|
|
15
15
|
maxTimeLimit?: number;
|
|
16
16
|
maxSpendLimit?: number;
|
|
17
|
+
/** When true, wait for the agent subprocess to exit before resolving (for generate pipelines). */
|
|
18
|
+
waitForCompletion?: boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const PROCESSES_DIR = path.join(process.cwd(), ".knowhow", "processes");
|
|
@@ -63,6 +65,7 @@ export async function startAgentTask(params: StartAgentTaskParams): Promise<stri
|
|
|
63
65
|
agentName,
|
|
64
66
|
maxTimeLimit,
|
|
65
67
|
maxSpendLimit,
|
|
68
|
+
waitForCompletion,
|
|
66
69
|
} = params;
|
|
67
70
|
if (!prompt) {
|
|
68
71
|
throw new Error("prompt is required to create a chat task");
|
|
@@ -156,24 +159,25 @@ export async function startAgentTask(params: StartAgentTaskParams): Promise<stri
|
|
|
156
159
|
`To check status, read: ${agentTaskDir}/status.txt\n`
|
|
157
160
|
: "";
|
|
158
161
|
|
|
159
|
-
// Give the agent 5 seconds to finish before detaching
|
|
160
|
-
const detachTime = 5 * 1000;
|
|
161
|
-
const tid = setTimeout(() => {
|
|
162
|
-
try { child.unref(); } catch {}
|
|
163
|
-
done(
|
|
164
|
-
`Agent started (pid=${pid}), running in background.\n` +
|
|
165
|
-
`Logs: ${logPath}\n` +
|
|
166
|
-
syncFsNote
|
|
167
|
-
);
|
|
168
|
-
}, detachTime);
|
|
169
|
-
|
|
170
162
|
child.once("exit", (code) => {
|
|
171
|
-
clearTimeout(tid);
|
|
172
163
|
done(
|
|
173
164
|
`Agent finished with exit code ${code}.\nLogs: ${logPath}\n` +
|
|
174
165
|
syncFsNote
|
|
175
166
|
);
|
|
176
167
|
});
|
|
168
|
+
|
|
169
|
+
if (!waitForCompletion) {
|
|
170
|
+
// Give the agent 5 seconds to finish before detaching (fire-and-forget mode)
|
|
171
|
+
const detachTime = 5 * 1000;
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
try { child.unref(); } catch {}
|
|
174
|
+
done(
|
|
175
|
+
`Agent started (pid=${pid}), running in background.\n` +
|
|
176
|
+
`Logs: ${logPath}\n` +
|
|
177
|
+
syncFsNote
|
|
178
|
+
);
|
|
179
|
+
}, detachTime);
|
|
180
|
+
}
|
|
177
181
|
});
|
|
178
182
|
}
|
|
179
183
|
|
package/src/ai.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import { readFile } from "./utils";
|
|
3
3
|
|
|
4
|
+
/** Options forwarded to startAgentTask when a generation source has an `agent` field */
|
|
5
|
+
export type AgentOptions = {
|
|
6
|
+
syncFs?: boolean;
|
|
7
|
+
taskId?: string;
|
|
8
|
+
maxTimeLimit?: number;
|
|
9
|
+
maxSpendLimit?: number;
|
|
10
|
+
};
|
|
4
11
|
import OpenAI from "openai";
|
|
5
12
|
import { Assistant } from "./types";
|
|
6
13
|
import { convertToText } from "./conversion";
|
|
@@ -46,17 +53,24 @@ export function readPromptFile(promptFile: string, input: string) {
|
|
|
46
53
|
return input;
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
export async function singlePrompt(
|
|
50
|
-
|
|
56
|
+
export async function singlePrompt(
|
|
57
|
+
userPrompt: string,
|
|
58
|
+
model = "",
|
|
59
|
+
agent = "",
|
|
60
|
+
agentOptions?: AgentOptions
|
|
61
|
+
) {
|
|
51
62
|
if (agent) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
// Route through the full AgentModule pipeline (renderer, sync-fs, limits).
|
|
64
|
+
// This ensures generate agent runs produce readable output and are observable
|
|
65
|
+
// via `knowhow agents list/tail/status`, matching `knowhow agent --input` behaviour.
|
|
66
|
+
const { startAgentTask } = await import("./agents/tools/startAgentTask");
|
|
67
|
+
return startAgentTask({
|
|
68
|
+
agentName: agent,
|
|
69
|
+
prompt: userPrompt,
|
|
70
|
+
model: model || undefined,
|
|
71
|
+
waitForCompletion: true,
|
|
72
|
+
...(agentOptions ?? {}),
|
|
73
|
+
});
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
if (!model) {
|
|
@@ -100,6 +114,11 @@ function isContextLengthError(err: any): boolean {
|
|
|
100
114
|
* Recursively summarize an array of texts using a split-and-summarize approach.
|
|
101
115
|
* When the combined texts exceed the context window (either by estimate or actual API error),
|
|
102
116
|
* split the array in half, summarize each half recursively, then combine.
|
|
117
|
+
*
|
|
118
|
+
* NOTE: when an `agent` is provided the recursive split path is skipped — agents
|
|
119
|
+
* can handle large inputs themselves (they can read files). Splitting would create
|
|
120
|
+
* multiple isolated agent runs each writing partial output. Instead the combined
|
|
121
|
+
* content is passed directly to the agent.
|
|
103
122
|
*/
|
|
104
123
|
async function summarizeTextsRecursive(
|
|
105
124
|
texts: string[],
|
|
@@ -107,7 +126,8 @@ async function summarizeTextsRecursive(
|
|
|
107
126
|
model: string,
|
|
108
127
|
agent: string,
|
|
109
128
|
contextLimit: number,
|
|
110
|
-
depth = 0
|
|
129
|
+
depth = 0,
|
|
130
|
+
agentOptions?: AgentOptions
|
|
111
131
|
): Promise<string> {
|
|
112
132
|
const indent = " ".repeat(depth);
|
|
113
133
|
|
|
@@ -115,7 +135,7 @@ async function summarizeTextsRecursive(
|
|
|
115
135
|
if (texts.length === 1) {
|
|
116
136
|
const content = template.replaceAll("{text}", texts[0]);
|
|
117
137
|
console.log(`${indent}summarizeTexts[depth=${depth}]: single text, ~${estimateTokens(content)} tokens`);
|
|
118
|
-
return singlePrompt(content, model, agent);
|
|
138
|
+
return singlePrompt(content, model, agent, agentOptions);
|
|
119
139
|
}
|
|
120
140
|
|
|
121
141
|
// Check if combined fits in context window by estimate
|
|
@@ -127,7 +147,7 @@ async function summarizeTextsRecursive(
|
|
|
127
147
|
// Try single combined prompt — if context error, fall through to split
|
|
128
148
|
console.log(`${indent}summarizeTexts[depth=${depth}]: ${texts.length} texts, ~${estimatedTokens} tokens, trying combined`);
|
|
129
149
|
try {
|
|
130
|
-
return await singlePrompt(combinedContent, model, agent);
|
|
150
|
+
return await singlePrompt(combinedContent, model, agent, agentOptions);
|
|
131
151
|
} catch (err: any) {
|
|
132
152
|
if (!isContextLengthError(err)) throw err;
|
|
133
153
|
console.log(`${indent}summarizeTexts[depth=${depth}]: API rejected (context too long), splitting in half`);
|
|
@@ -136,14 +156,22 @@ async function summarizeTextsRecursive(
|
|
|
136
156
|
console.log(`${indent}summarizeTexts[depth=${depth}]: ${texts.length} texts, ~${estimatedTokens} tokens exceeds limit, splitting in half`);
|
|
137
157
|
}
|
|
138
158
|
|
|
159
|
+
// When an agent is assigned, don't split — agents can handle large inputs and
|
|
160
|
+
// splitting would create multiple isolated agent runs that each write partial output.
|
|
161
|
+
// Instead, pass the combined content as-is even if it may exceed context limit.
|
|
162
|
+
if (agent) {
|
|
163
|
+
console.log(`${indent}summarizeTexts[depth=${depth}]: agent mode — skipping split, passing combined content to agent`);
|
|
164
|
+
return singlePrompt(combinedContent, model, agent, agentOptions);
|
|
165
|
+
}
|
|
166
|
+
|
|
139
167
|
// Split texts in half and recurse
|
|
140
168
|
const mid = Math.ceil(texts.length / 2);
|
|
141
169
|
const left = texts.slice(0, mid);
|
|
142
170
|
const right = texts.slice(mid);
|
|
143
171
|
|
|
144
172
|
const [leftSummary, rightSummary] = await Promise.all([
|
|
145
|
-
summarizeTextsRecursive(left, template, model, agent, contextLimit, depth + 1),
|
|
146
|
-
summarizeTextsRecursive(right, template, model, agent, contextLimit, depth + 1),
|
|
173
|
+
summarizeTextsRecursive(left, template, model, agent, contextLimit, depth + 1, agentOptions),
|
|
174
|
+
summarizeTextsRecursive(right, template, model, agent, contextLimit, depth + 1, agentOptions),
|
|
147
175
|
]);
|
|
148
176
|
|
|
149
177
|
// Combine the two halves with a final summary prompt
|
|
@@ -153,27 +181,28 @@ async function summarizeTextsRecursive(
|
|
|
153
181
|
console.log(`${indent}summarizeTexts[depth=${depth}]: combining halves, ~${finalEstimate} tokens`);
|
|
154
182
|
|
|
155
183
|
if (finalEstimate < contextLimit) {
|
|
156
|
-
return singlePrompt(finalContent, model, agent);
|
|
184
|
+
return singlePrompt(finalContent, model, agent, agentOptions);
|
|
157
185
|
}
|
|
158
186
|
|
|
159
187
|
// If even the combined summaries are too long, recurse one more level
|
|
160
|
-
return summarizeTextsRecursive([leftSummary, rightSummary], template, model, agent, contextLimit, depth + 1);
|
|
188
|
+
return summarizeTextsRecursive([leftSummary, rightSummary], template, model, agent, contextLimit, depth + 1, agentOptions);
|
|
161
189
|
}
|
|
162
190
|
|
|
163
191
|
export async function summarizeTexts(
|
|
164
192
|
texts: string[],
|
|
165
193
|
template: string,
|
|
166
194
|
model = "",
|
|
167
|
-
agent = ""
|
|
195
|
+
agent = "",
|
|
196
|
+
agentOptions?: AgentOptions
|
|
168
197
|
) {
|
|
169
198
|
const effectiveModel = model || Models.openai.GPT_54_Nano;
|
|
170
199
|
const contextLimit = getModelContextLimit(effectiveModel);
|
|
171
200
|
|
|
172
201
|
console.log(
|
|
173
|
-
`summarizeTexts: ${texts.length} text(s), context limit: ${contextLimit}, model: ${effectiveModel}`
|
|
202
|
+
`summarizeTexts: ${texts.length} text(s), context limit: ${contextLimit}, model: ${effectiveModel}${agent ? `, agent: ${agent}` : ""}`
|
|
174
203
|
);
|
|
175
204
|
|
|
176
|
-
return summarizeTextsRecursive(texts, template, model, agent, contextLimit).catch((err) => {
|
|
205
|
+
return summarizeTextsRecursive(texts, template, model, agent, contextLimit, 0, agentOptions).catch((err) => {
|
|
177
206
|
return `Texts of combined length ${texts.reduce((a, t) => a + t.length, 0)} could not be summarized due to error: ${err.message}`;
|
|
178
207
|
});
|
|
179
208
|
}
|
|
@@ -193,23 +222,25 @@ export async function summarizeFiles(
|
|
|
193
222
|
files: string[],
|
|
194
223
|
template: string,
|
|
195
224
|
model = "",
|
|
196
|
-
agent = ""
|
|
225
|
+
agent = "",
|
|
226
|
+
agentOptions?: AgentOptions
|
|
197
227
|
) {
|
|
198
228
|
const texts = [];
|
|
199
229
|
for (const file of files) {
|
|
200
230
|
const text = `file: ${file}\n` + (await convertToText(file));
|
|
201
231
|
texts.push(text);
|
|
202
232
|
}
|
|
203
|
-
return summarizeTexts(texts, template, model, agent);
|
|
233
|
+
return summarizeTexts(texts, template, model, agent, agentOptions);
|
|
204
234
|
}
|
|
205
235
|
|
|
206
236
|
export async function summarizeFile(
|
|
207
237
|
file: string,
|
|
208
238
|
template: string,
|
|
209
239
|
model = "",
|
|
210
|
-
agent = ""
|
|
240
|
+
agent = "",
|
|
241
|
+
agentOptions?: AgentOptions
|
|
211
242
|
) {
|
|
212
|
-
return await summarizeFiles([file], template, model, agent);
|
|
243
|
+
return await summarizeFiles([file], template, model, agent, agentOptions);
|
|
213
244
|
}
|
|
214
245
|
|
|
215
246
|
/*
|
|
@@ -336,12 +336,18 @@ export class CliChatService implements ChatService {
|
|
|
336
336
|
return fullPrompt;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
async startChatLoop(): Promise<void> {
|
|
339
|
+
async startChatLoop(initialCommand?: string): Promise<void> {
|
|
340
340
|
// Display available commands like the original
|
|
341
341
|
const availableCommands = this.getCommandsForActiveModes();
|
|
342
342
|
const commandNames = availableCommands.map((cmd) => `/${cmd.name}`);
|
|
343
343
|
console.log("Commands:", commandNames.join(", "));
|
|
344
344
|
|
|
345
|
+
// If an initial command was provided (e.g. from CLI), process it before
|
|
346
|
+
// entering the interactive loop so the user lands in the right state.
|
|
347
|
+
if (initialCommand) {
|
|
348
|
+
await this.processInput(initialCommand.trim());
|
|
349
|
+
}
|
|
350
|
+
|
|
345
351
|
while (true) {
|
|
346
352
|
// Recompute available commands each iteration so mode changes are reflected in autocomplete
|
|
347
353
|
const currentCommandNames = this.getCommandsForActiveModes().map(
|