@tyvm/knowhow 0.0.145 → 0.0.146
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 +1 -1
- package/src/chat/CliChatService.ts +16 -0
- package/src/chat/modules/AgentModule.ts +30 -23
- package/src/chat/modules/SessionsModule.ts +7 -5
- package/src/chat/types.ts +3 -2
- package/src/clients/anthropic.ts +24 -0
- package/src/clients/fireworks.ts +3 -0
- package/src/clients/gemini.ts +8 -0
- package/src/clients/openai.ts +7 -4
- package/src/clients/types.ts +30 -7
- package/src/clients/xai.ts +23 -4
- package/src/commands/agent.ts +10 -1
- package/src/commands/services.ts +49 -3
- package/src/plugins/plugins.ts +16 -0
- package/src/processors/TokenCompressor.ts +40 -8
- package/src/processors/ToolResponseCache.ts +17 -8
- package/src/processors/tools/boundedToolResponse.ts +49 -0
- package/src/processors/tools/grepToolResponse.ts +164 -38
- package/src/processors/tools/index.ts +2 -0
- package/src/processors/tools/jqToolResponse.ts +40 -10
- package/src/processors/tools/listStoredToolResponses.ts +51 -10
- package/src/processors/tools/tailToolResponse.ts +85 -17
- package/src/services/LazyToolsService.ts +7 -0
- package/src/services/Mcp.ts +9 -2
- package/src/services/RuntimeReloadService.ts +31 -0
- package/src/services/Tools.ts +11 -0
- package/src/services/TracingService.ts +116 -0
- package/src/services/index.ts +8 -0
- package/src/services/modules/index.ts +45 -2
- package/src/services/modules/types.ts +8 -0
- package/src/util/Trace.ts +236 -0
- package/tests/clients/fireworks.test.ts +65 -0
- package/tests/clients/reasoningEffort.test.ts +79 -0
- package/tests/compressor/toolResponseCache.test.ts +2 -1
- package/tests/manual/clients/completions.test.ts +27 -10
- package/tests/processors/TokenCompressor.test.ts +17 -3
- package/tests/processors/tools/boundedRetrievalTools.test.ts +68 -0
- package/tests/processors/tools/grepToolResponse.test.ts +88 -1
- package/tests/unit/plugins/pluginLoading.test.ts +31 -0
- package/tests/unit/services/RuntimeReloadService.test.ts +29 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/chat/CliChatService.js +15 -0
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +13 -19
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +1 -6
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +3 -2
- package/ts_build/src/clients/anthropic.d.ts +1 -0
- package/ts_build/src/clients/anthropic.js +12 -0
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/fireworks.js +1 -0
- package/ts_build/src/clients/fireworks.js.map +1 -1
- package/ts_build/src/clients/gemini.js +8 -0
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/openai.js +4 -2
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +4 -1
- package/ts_build/src/clients/types.js +14 -0
- package/ts_build/src/clients/types.js.map +1 -1
- package/ts_build/src/clients/xai.d.ts +1 -0
- package/ts_build/src/clients/xai.js +23 -4
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +6 -1
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/services.d.ts +2 -0
- package/ts_build/src/commands/services.js +45 -3
- package/ts_build/src/commands/services.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +1 -0
- package/ts_build/src/plugins/plugins.js +14 -0
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.js +15 -6
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.d.ts +3 -3
- package/ts_build/src/processors/ToolResponseCache.js +8 -8
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/src/processors/tools/boundedToolResponse.d.ts +7 -0
- package/ts_build/src/processors/tools/boundedToolResponse.js +27 -0
- package/ts_build/src/processors/tools/boundedToolResponse.js.map +1 -0
- package/ts_build/src/processors/tools/grepToolResponse.d.ts +4 -0
- package/ts_build/src/processors/tools/grepToolResponse.js +101 -25
- package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -1
- package/ts_build/src/processors/tools/index.d.ts +2 -2
- package/ts_build/src/processors/tools/index.js.map +1 -1
- package/ts_build/src/processors/tools/jqToolResponse.d.ts +5 -1
- package/ts_build/src/processors/tools/jqToolResponse.js +26 -9
- package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -1
- package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +7 -1
- package/ts_build/src/processors/tools/listStoredToolResponses.js +39 -9
- package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -1
- package/ts_build/src/processors/tools/tailToolResponse.d.ts +5 -0
- package/ts_build/src/processors/tools/tailToolResponse.js +56 -13
- package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.d.ts +1 -0
- package/ts_build/src/services/LazyToolsService.js +5 -0
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/Mcp.js +8 -2
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/RuntimeReloadService.d.ts +12 -0
- package/ts_build/src/services/RuntimeReloadService.js +26 -0
- package/ts_build/src/services/RuntimeReloadService.js.map +1 -0
- package/ts_build/src/services/Tools.d.ts +1 -0
- package/ts_build/src/services/Tools.js +5 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/TracingService.d.ts +16 -0
- package/ts_build/src/services/TracingService.js +35 -0
- package/ts_build/src/services/TracingService.js.map +1 -0
- package/ts_build/src/services/index.d.ts +6 -0
- package/ts_build/src/services/index.js +7 -1
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/index.d.ts +5 -0
- package/ts_build/src/services/modules/index.js +28 -2
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +3 -0
- package/ts_build/src/util/Trace.d.ts +13 -0
- package/ts_build/src/util/Trace.js +107 -0
- package/ts_build/src/util/Trace.js.map +1 -0
- package/ts_build/tests/clients/fireworks.test.d.ts +1 -0
- package/ts_build/tests/clients/fireworks.test.js +65 -0
- package/ts_build/tests/clients/fireworks.test.js.map +1 -0
- package/ts_build/tests/clients/reasoningEffort.test.d.ts +1 -0
- package/ts_build/tests/clients/reasoningEffort.test.js +68 -0
- package/ts_build/tests/clients/reasoningEffort.test.js.map +1 -0
- package/ts_build/tests/compressor/toolResponseCache.test.js +1 -1
- package/ts_build/tests/compressor/toolResponseCache.test.js.map +1 -1
- package/ts_build/tests/manual/clients/completions.test.js +19 -6
- package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +11 -2
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.d.ts +1 -0
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js +57 -0
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js.map +1 -0
- package/ts_build/tests/processors/tools/grepToolResponse.test.js +37 -0
- package/ts_build/tests/processors/tools/grepToolResponse.test.js.map +1 -1
- package/ts_build/tests/unit/plugins/pluginLoading.test.js +59 -0
- package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
- package/ts_build/tests/unit/services/RuntimeReloadService.test.d.ts +1 -0
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js +26 -0
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js.map +1 -0
package/package.json
CHANGED
|
@@ -43,6 +43,22 @@ export class CliChatService implements ChatService {
|
|
|
43
43
|
chatHistory: this.chatHistory,
|
|
44
44
|
plugins,
|
|
45
45
|
};
|
|
46
|
+
this.registerCommand({
|
|
47
|
+
name: "reload",
|
|
48
|
+
description: "Reload configuration, tools, MCP connections, and modules",
|
|
49
|
+
handler: async () => {
|
|
50
|
+
try {
|
|
51
|
+
console.log("Reloading configuration...");
|
|
52
|
+
const result = await services().RuntimeReload.reload();
|
|
53
|
+
console.log(
|
|
54
|
+
`✓ Reloaded ${result.tools} tools, ${result.mcps} MCP servers, and ${result.modules} modules`
|
|
55
|
+
);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
console.error(`⚠ Reload failed: ${message}`);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
46
62
|
this.loadInputHistory();
|
|
47
63
|
|
|
48
64
|
// Set up callback to add entries to inputHistory immediately when user presses Enter
|
|
@@ -18,7 +18,12 @@ 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 {
|
|
21
|
+
import {
|
|
22
|
+
Message,
|
|
23
|
+
CompletionOptions,
|
|
24
|
+
REASONING_EFFORTS,
|
|
25
|
+
isReasoningEffort,
|
|
26
|
+
} from "../../clients/types";
|
|
22
27
|
import { ChatInteraction } from "../../types";
|
|
23
28
|
import { Marked } from "../../utils/index";
|
|
24
29
|
import { TokenCompressor } from "../../processors/TokenCompressor";
|
|
@@ -173,21 +178,15 @@ export class AgentModule extends BaseChatModule {
|
|
|
173
178
|
{
|
|
174
179
|
name: "reasoning_effort",
|
|
175
180
|
description:
|
|
176
|
-
|
|
181
|
+
`Set the reasoning effort for the agent (${REASONING_EFFORTS.join(", ")}). Individual models support different subsets.`,
|
|
177
182
|
handler: async (args: string[]): Promise<void> => {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
"low",
|
|
181
|
-
"medium",
|
|
182
|
-
"high",
|
|
183
|
-
];
|
|
184
|
-
const effort = args[0] as CompletionOptions["reasoning_effort"];
|
|
185
|
-
if (!effort || !validEfforts.includes(effort)) {
|
|
183
|
+
const effort = args[0];
|
|
184
|
+
if (!isReasoningEffort(effort)) {
|
|
186
185
|
console.log(
|
|
187
186
|
`Current reasoning effort: ${this.reasoningEffort ?? "(not set — model default)"}`
|
|
188
187
|
);
|
|
189
188
|
console.log(
|
|
190
|
-
`Usage: /reasoning_effort <${
|
|
189
|
+
`Usage: /reasoning_effort <${REASONING_EFFORTS.join("|")}>`
|
|
191
190
|
);
|
|
192
191
|
return;
|
|
193
192
|
}
|
|
@@ -201,19 +200,13 @@ export class AgentModule extends BaseChatModule {
|
|
|
201
200
|
name: "effort",
|
|
202
201
|
description: "Alias for /reasoning_effort",
|
|
203
202
|
handler: async (args: string[]): Promise<void> => {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
"low",
|
|
207
|
-
"medium",
|
|
208
|
-
"high",
|
|
209
|
-
];
|
|
210
|
-
const effort = args[0] as CompletionOptions["reasoning_effort"];
|
|
211
|
-
if (!effort || !validEfforts.includes(effort)) {
|
|
203
|
+
const effort = args[0];
|
|
204
|
+
if (!isReasoningEffort(effort)) {
|
|
212
205
|
console.log(
|
|
213
206
|
`Current reasoning effort: ${this.reasoningEffort ?? "(not set — model default)"}`
|
|
214
207
|
);
|
|
215
208
|
console.log(
|
|
216
|
-
`Usage: /effort <${
|
|
209
|
+
`Usage: /effort <${REASONING_EFFORTS.join("|")}>`
|
|
217
210
|
);
|
|
218
211
|
return;
|
|
219
212
|
}
|
|
@@ -247,8 +240,15 @@ export class AgentModule extends BaseChatModule {
|
|
|
247
240
|
description: "Detach from the currently attached agent",
|
|
248
241
|
modes: ["agent:attached"],
|
|
249
242
|
handler: async (_args: string[]): Promise<void> => {
|
|
243
|
+
const agent = this.attachedAgent;
|
|
250
244
|
console.log("Detached from agent");
|
|
251
|
-
|
|
245
|
+
// Only detach if we are still attached to THIS agent. If the user
|
|
246
|
+
// killed this agent and started a new one while this agent was
|
|
247
|
+
// finishing a long tool call, detachFromAgent() would rip out the
|
|
248
|
+
// new agent's rendering and reset the mode.
|
|
249
|
+
if (agent && this.attachedAgent === agent) {
|
|
250
|
+
this.detachFromAgent();
|
|
251
|
+
}
|
|
252
252
|
},
|
|
253
253
|
},
|
|
254
254
|
{
|
|
@@ -1468,8 +1468,15 @@ export class AgentModule extends BaseChatModule {
|
|
|
1468
1468
|
}
|
|
1469
1469
|
|
|
1470
1470
|
resolve("done");
|
|
1471
|
-
// Exit agent:attached mode so the prompt resets back to the default
|
|
1472
|
-
|
|
1471
|
+
// Exit agent:attached mode so the prompt resets back to the default.
|
|
1472
|
+
// Only detach if we are still attached to THIS agent. If the user
|
|
1473
|
+
// killed this agent and started a new one (or /attached to a different
|
|
1474
|
+
// one) while this agent was finishing a long tool call, calling
|
|
1475
|
+
// detachFromAgent() unconditionally would rip out the new agent's
|
|
1476
|
+
// rendering listeners and reset the mode, making new output invisible.
|
|
1477
|
+
if (this.attachedAgent === agent) {
|
|
1478
|
+
this.detachFromAgent();
|
|
1479
|
+
}
|
|
1473
1480
|
});
|
|
1474
1481
|
});
|
|
1475
1482
|
|
|
@@ -214,7 +214,6 @@ export class SessionsModule extends BaseChatModule {
|
|
|
214
214
|
// ── Case 1: in-memory running task ──────────────────────────────────────
|
|
215
215
|
if (taskRegistry.has(id)) {
|
|
216
216
|
const taskInfo = taskRegistry.get(id)!;
|
|
217
|
-
const renderer = this.agentModule.getRenderer();
|
|
218
217
|
const context = this.chatService?.getContext();
|
|
219
218
|
const selectedAgent = taskInfo.agent;
|
|
220
219
|
|
|
@@ -222,13 +221,9 @@ export class SessionsModule extends BaseChatModule {
|
|
|
222
221
|
context.selectedAgent = selectedAgent;
|
|
223
222
|
context.agentMode = true;
|
|
224
223
|
context.currentAgent = taskInfo.agentName;
|
|
225
|
-
context.activeAgentTaskId = id;
|
|
226
224
|
context.currentModel = selectedAgent.getModel();
|
|
227
225
|
context.currentProvider = selectedAgent.getProvider();
|
|
228
226
|
}
|
|
229
|
-
this.agentModule.setActiveAgentTaskId(id);
|
|
230
|
-
renderer.setActiveTaskId(id);
|
|
231
|
-
if (this.chatService) this.chatService.setMode("agent:attached");
|
|
232
227
|
|
|
233
228
|
console.log(`🔄 Attached to running task: ${id}`);
|
|
234
229
|
console.log(` Agent : ${taskInfo.agentName}`);
|
|
@@ -237,6 +232,13 @@ export class SessionsModule extends BaseChatModule {
|
|
|
237
232
|
console.log(
|
|
238
233
|
` Type /logs to see recent messages, or /detach to detach.`
|
|
239
234
|
);
|
|
235
|
+
|
|
236
|
+
// Route through attachedAgentChatLoop so that:
|
|
237
|
+
// 1. wireAgentRendering is called (new output becomes visible)
|
|
238
|
+
// 2. attachedAgent is set (so /poke, /kill, /compact work)
|
|
239
|
+
// 3. a done listener is registered (mode resets when agent finishes)
|
|
240
|
+
// Do NOT pass initialInput — agent is already running, no need to call it again.
|
|
241
|
+
await this.agentModule.attachedAgentChatLoop(id, selectedAgent as any);
|
|
240
242
|
return;
|
|
241
243
|
}
|
|
242
244
|
|
package/src/chat/types.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { ChatInteraction, Config } from "../types";
|
|
5
5
|
import { BaseAgent } from "../agents/base/base";
|
|
6
|
+
import { ReasoningEffort } from "../clients/types";
|
|
6
7
|
import { ToolsService } from "../services";
|
|
7
8
|
import { AgentRenderer } from "./renderer/types";
|
|
8
9
|
|
|
@@ -101,7 +102,7 @@ export interface TaskInfo {
|
|
|
101
102
|
// restore the exact configuration instead of falling back to agent defaults.
|
|
102
103
|
model?: string;
|
|
103
104
|
provider?: string;
|
|
104
|
-
reasoningEffort?:
|
|
105
|
+
reasoningEffort?: ReasoningEffort;
|
|
105
106
|
summarizeReasoning?: boolean;
|
|
106
107
|
}
|
|
107
108
|
|
|
@@ -123,7 +124,7 @@ export interface ChatSession {
|
|
|
123
124
|
// Persisted run configuration for exact resume restoration.
|
|
124
125
|
model?: string;
|
|
125
126
|
provider?: string;
|
|
126
|
-
reasoningEffort?:
|
|
127
|
+
reasoningEffort?: ReasoningEffort;
|
|
127
128
|
summarizeReasoning?: boolean;
|
|
128
129
|
}
|
|
129
130
|
|
package/src/clients/anthropic.ts
CHANGED
|
@@ -88,6 +88,25 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
88
88
|
return undefined;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/** Map provider-neutral effort names to Anthropic's output_config.effort. */
|
|
92
|
+
getEffortOutputConfig(
|
|
93
|
+
model: string,
|
|
94
|
+
reasoningEffort?: CompletionOptions["reasoning_effort"]
|
|
95
|
+
): Anthropic.OutputConfig | undefined {
|
|
96
|
+
if (
|
|
97
|
+
reasoningEffort === undefined ||
|
|
98
|
+
reasoningEffort === "none" ||
|
|
99
|
+
(!alwaysOnModels.includes(model) && !optInModels.includes(model))
|
|
100
|
+
) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
// Anthropic uses `low` for the provider-neutral `minimal` level.
|
|
106
|
+
effort: reasoningEffort === "minimal" ? "low" : reasoningEffort,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
91
110
|
/**
|
|
92
111
|
* Clean JSON Schema for Anthropic API compatibility.
|
|
93
112
|
* Removes unsupported fields like additionalProperties, $ref, $defs, positional.
|
|
@@ -423,6 +442,10 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
423
442
|
options.reasoning_effort,
|
|
424
443
|
options.reasoning_summary,
|
|
425
444
|
);
|
|
445
|
+
const effortOutputConfig = this.getEffortOutputConfig(
|
|
446
|
+
options.model,
|
|
447
|
+
options.reasoning_effort
|
|
448
|
+
);
|
|
426
449
|
|
|
427
450
|
const systemMessage = options.messages
|
|
428
451
|
.filter((msg) => msg.role === "system")
|
|
@@ -447,6 +470,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
447
470
|
: undefined,
|
|
448
471
|
max_tokens: options.max_tokens || 8000,
|
|
449
472
|
...(thinkingParam && { thinking: thinkingParam }),
|
|
473
|
+
...(effortOutputConfig && { output_config: effortOutputConfig }),
|
|
450
474
|
...(tools.length && {
|
|
451
475
|
tool_choice: { type: "auto" },
|
|
452
476
|
tools,
|
package/src/clients/fireworks.ts
CHANGED
|
@@ -51,6 +51,9 @@ export class GenericFireworksClient extends HttpClient {
|
|
|
51
51
|
...options,
|
|
52
52
|
// Strip Anthropic-specific field not accepted by Fireworks
|
|
53
53
|
long_ttl_cache: undefined,
|
|
54
|
+
// This is translated by the OpenAI/Anthropic clients, but is not part of
|
|
55
|
+
// Fireworks' OpenAI-compatible chat completion schema.
|
|
56
|
+
reasoning_summary: undefined,
|
|
54
57
|
};
|
|
55
58
|
|
|
56
59
|
if (sanitized.tools) {
|
package/src/clients/gemini.ts
CHANGED
|
@@ -434,9 +434,13 @@ export class GenericGeminiClient implements GenericClient {
|
|
|
434
434
|
// Gemini 3.x — use thinkingLevel
|
|
435
435
|
if (GoogleThinkingLevelModels.includes(model)) {
|
|
436
436
|
const levelMap: Record<string, string> = {
|
|
437
|
+
none: "minimal",
|
|
438
|
+
minimal: "minimal",
|
|
437
439
|
low: "low",
|
|
438
440
|
medium: "medium",
|
|
439
441
|
high: "high",
|
|
442
|
+
xhigh: "high",
|
|
443
|
+
max: "high",
|
|
440
444
|
};
|
|
441
445
|
return {
|
|
442
446
|
thinkingLevel: levelMap[effort] ?? "low",
|
|
@@ -447,9 +451,13 @@ export class GenericGeminiClient implements GenericClient {
|
|
|
447
451
|
if (GoogleThinkingBudgetModels.includes(model)) {
|
|
448
452
|
// Map effort to token budget
|
|
449
453
|
const budgetMap: Record<string, number> = {
|
|
454
|
+
none: 0,
|
|
455
|
+
minimal: 512,
|
|
450
456
|
low: 1024,
|
|
451
457
|
medium: 8192,
|
|
452
458
|
high: -1, // dynamic
|
|
459
|
+
xhigh: -1,
|
|
460
|
+
max: -1,
|
|
453
461
|
};
|
|
454
462
|
return {
|
|
455
463
|
thinkingBudget: budgetMap[effort] ?? 1024,
|
package/src/clients/openai.ts
CHANGED
|
@@ -112,9 +112,10 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
112
112
|
|
|
113
113
|
resolveReasoningEffort(options: CompletionOptions): "low" | "medium" | "high" {
|
|
114
114
|
const effort = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
if (effort === "none") return "low";
|
|
115
|
+
// Chat Completions only accepts low/medium/high. Preserve newer values on
|
|
116
|
+
// the Responses API, but map them to the nearest legacy level here.
|
|
117
|
+
if (effort === "none" || effort === "minimal") return "low";
|
|
118
|
+
if (effort === "xhigh" || effort === "max") return "high";
|
|
118
119
|
return effort;
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -125,7 +126,9 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
125
126
|
*/
|
|
126
127
|
resolveReasoningEffortForModel(options: CompletionOptions): string {
|
|
127
128
|
const raw = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
|
|
128
|
-
|
|
129
|
+
// Anthropic calls its highest level `max`; OpenAI calls it `xhigh`.
|
|
130
|
+
// Accept both provider-neutral CLI spellings.
|
|
131
|
+
const requested = raw === "max" ? "xhigh" : raw;
|
|
129
132
|
const pricing = OpenAiTextPricing[options.model];
|
|
130
133
|
const supportedLevels = pricing?.reasoningLevels;
|
|
131
134
|
if (!supportedLevels || supportedLevels.length === 0) {
|
package/src/clients/types.ts
CHANGED
|
@@ -136,6 +136,26 @@ export interface RetryOptions {
|
|
|
136
136
|
signal?: AbortSignal;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Every reasoning-effort spelling currently exposed by a supported provider.
|
|
141
|
+
* Individual models may support only a subset; provider clients are responsible
|
|
142
|
+
* for translating or clamping values where their APIs require it.
|
|
143
|
+
*/
|
|
144
|
+
export const REASONING_EFFORTS = [
|
|
145
|
+
"none",
|
|
146
|
+
"minimal",
|
|
147
|
+
"low",
|
|
148
|
+
"medium",
|
|
149
|
+
"high",
|
|
150
|
+
"xhigh",
|
|
151
|
+
"max",
|
|
152
|
+
] as const;
|
|
153
|
+
export type ReasoningEffort = (typeof REASONING_EFFORTS)[number];
|
|
154
|
+
|
|
155
|
+
export function isReasoningEffort(value: unknown): value is ReasoningEffort {
|
|
156
|
+
return typeof value === "string" && REASONING_EFFORTS.includes(value as ReasoningEffort);
|
|
157
|
+
}
|
|
158
|
+
|
|
139
159
|
export interface CompletionOptions extends RetryOptions {
|
|
140
160
|
model: string;
|
|
141
161
|
messages: Message[];
|
|
@@ -147,14 +167,17 @@ export interface CompletionOptions extends RetryOptions {
|
|
|
147
167
|
* Maps to: OpenAI reasoning_effort/reasoning.effort, xAI reasoning.effort,
|
|
148
168
|
* Gemini thinkingLevel/thinkingBudget, Anthropic thinking mode.
|
|
149
169
|
*
|
|
150
|
-
* "none"
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* "
|
|
154
|
-
* "
|
|
155
|
-
* "
|
|
170
|
+
* "none" = disable thinking where supported
|
|
171
|
+
* "minimal" = provider-defined minimum (for example Gemini/OpenAI)
|
|
172
|
+
* "low" = low reasoning
|
|
173
|
+
* "medium" = balanced reasoning
|
|
174
|
+
* "high" = high reasoning
|
|
175
|
+
* "xhigh" = extra-high reasoning (for example OpenAI/xAI/Anthropic)
|
|
176
|
+
* "max" = maximum reasoning (Anthropic)
|
|
177
|
+
*
|
|
178
|
+
* A model may support only a subset of these provider-level values.
|
|
156
179
|
*/
|
|
157
|
-
reasoning_effort?:
|
|
180
|
+
reasoning_effort?: ReasoningEffort;
|
|
158
181
|
/**
|
|
159
182
|
* When true, request a summarized view of the model's thinking process.
|
|
160
183
|
* - Anthropic: thinking.display = "summarized"
|
package/src/clients/xai.ts
CHANGED
|
@@ -52,6 +52,15 @@ export class GenericXAIClient implements GenericClient {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
resolveLegacyReasoningEffort(
|
|
56
|
+
effort: CompletionOptions["reasoning_effort"]
|
|
57
|
+
): "low" | "medium" | "high" | undefined {
|
|
58
|
+
if (effort === undefined || effort === "none") return undefined;
|
|
59
|
+
if (effort === "minimal") return "low";
|
|
60
|
+
if (effort === "xhigh" || effort === "max") return "high";
|
|
61
|
+
return effort;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
async createChatCompletion(
|
|
56
65
|
options: CompletionOptions
|
|
57
66
|
): Promise<CompletionResponse> {
|
|
@@ -72,13 +81,14 @@ export class GenericXAIClient implements GenericClient {
|
|
|
72
81
|
return msg as ChatCompletionMessageParam;
|
|
73
82
|
});
|
|
74
83
|
|
|
84
|
+
const reasoningEffort = this.resolveLegacyReasoningEffort(options.reasoning_effort);
|
|
75
85
|
const response = await this.client.chat.completions.create({
|
|
76
86
|
model: options.model,
|
|
77
87
|
messages: xaiMessages,
|
|
78
88
|
max_tokens: options.max_tokens,
|
|
79
|
-
...(XaiReasoningModels.includes(options.model) &&
|
|
89
|
+
...(XaiReasoningModels.includes(options.model) && reasoningEffort && {
|
|
80
90
|
// grok-3-mini models support reasoning_effort: "low" | "medium" | "high"
|
|
81
|
-
reasoning_effort:
|
|
91
|
+
reasoning_effort: reasoningEffort,
|
|
82
92
|
}),
|
|
83
93
|
...(options.tools && {
|
|
84
94
|
tools: options.tools,
|
|
@@ -206,8 +216,17 @@ export class GenericXAIClient implements GenericClient {
|
|
|
206
216
|
// Resolve reasoning effort, clamping to supported levels if defined in pricing
|
|
207
217
|
const pricing = XaiTextPricing[options.model];
|
|
208
218
|
const supportedLevels = pricing?.reasoningLevels;
|
|
209
|
-
|
|
210
|
-
|
|
219
|
+
const requestedEffort = options.reasoning_effort;
|
|
220
|
+
let reasoningEffort: string | undefined;
|
|
221
|
+
if (requestedEffort !== "none") {
|
|
222
|
+
reasoningEffort =
|
|
223
|
+
requestedEffort === "minimal"
|
|
224
|
+
? "low"
|
|
225
|
+
: requestedEffort === "max"
|
|
226
|
+
? "xhigh"
|
|
227
|
+
: requestedEffort;
|
|
228
|
+
}
|
|
229
|
+
if (requestedEffort !== "none" && supportedLevels?.length) {
|
|
211
230
|
if (!reasoningEffort || !supportedLevels.includes(reasoningEffort)) {
|
|
212
231
|
reasoningEffort = supportedLevels[0];
|
|
213
232
|
}
|
package/src/commands/agent.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { SessionsModule } from "../chat/modules/SessionsModule";
|
|
|
8
8
|
import { SetupModule } from "../chat/modules/SetupModule";
|
|
9
9
|
import { PlainRenderer } from "../chat/renderer/PlainRenderer";
|
|
10
10
|
import { loadRenderer } from "../chat/renderer/loadRenderer";
|
|
11
|
+
import { isReasoningEffort, REASONING_EFFORTS } from "../clients/types";
|
|
11
12
|
import { getConfig } from "../config";
|
|
12
13
|
|
|
13
14
|
async function setupRenderer(chatService: any, rendererSpecifier: string): Promise<void> {
|
|
@@ -57,7 +58,7 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
|
|
|
57
58
|
.option("--agent-name <name>", "Which agent to use", "Patcher")
|
|
58
59
|
.option(
|
|
59
60
|
"--reasoning-effort <effort>",
|
|
60
|
-
|
|
61
|
+
`Reasoning effort: ${REASONING_EFFORTS.join(", ")}. Individual models support different subsets.`
|
|
61
62
|
)
|
|
62
63
|
.option(
|
|
63
64
|
"--max-time-limit <minutes>",
|
|
@@ -103,6 +104,14 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
|
|
|
103
104
|
)
|
|
104
105
|
.action(async (options) => {
|
|
105
106
|
try {
|
|
107
|
+
if (
|
|
108
|
+
options.reasoningEffort !== undefined &&
|
|
109
|
+
!isReasoningEffort(options.reasoningEffort)
|
|
110
|
+
) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Invalid reasoning effort "${options.reasoningEffort}". Expected one of: ${REASONING_EFFORTS.join(", ")}.`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
106
115
|
const { setupServices } = await import("./services");
|
|
107
116
|
await setupServices();
|
|
108
117
|
const chatService = getChatService();
|
package/src/commands/services.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { includedTools } from "../agents/tools/list";
|
|
2
2
|
import * as allTools from "../agents/tools";
|
|
3
|
-
import { LazyToolsService, services, MinimalToolsService } from "../services";
|
|
3
|
+
import { LazyToolsService, services, MinimalToolsService, TracingService } from "../services";
|
|
4
4
|
import { agents } from "../agents";
|
|
5
5
|
import { ModulesService } from "../services/modules";
|
|
6
6
|
import { getConfig } from "../config";
|
|
@@ -19,8 +19,10 @@ export async function setupServices() {
|
|
|
19
19
|
Events,
|
|
20
20
|
MediaProcessor,
|
|
21
21
|
Behaviors,
|
|
22
|
+
RuntimeReload,
|
|
22
23
|
} = services();
|
|
23
24
|
|
|
25
|
+
await Plugins.refreshConfiguredState();
|
|
24
26
|
// cli uses LazyTools to keep context slim
|
|
25
27
|
const Tools = new LazyToolsService();
|
|
26
28
|
await Behaviors.initFromConfig();
|
|
@@ -80,7 +82,7 @@ export async function setupServices() {
|
|
|
80
82
|
|
|
81
83
|
console.log("📦 Loading modules from config...");
|
|
82
84
|
const modulesService = new ModulesService();
|
|
83
|
-
|
|
85
|
+
const moduleContext = {
|
|
84
86
|
Agents,
|
|
85
87
|
Embeddings,
|
|
86
88
|
Plugins,
|
|
@@ -89,7 +91,51 @@ export async function setupServices() {
|
|
|
89
91
|
MediaProcessor,
|
|
90
92
|
Behaviors,
|
|
91
93
|
Events,
|
|
94
|
+
Tracing: TracingService,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Call destroy() on all modules when the process is shutting down so they
|
|
98
|
+
// can flush buffers (e.g. OTEL spans), close connections, etc.
|
|
99
|
+
const destroyAll = async () => {
|
|
100
|
+
await modulesService.destroyModules();
|
|
101
|
+
};
|
|
102
|
+
process.on("beforeExit", destroyAll);
|
|
103
|
+
process.on("SIGINT", async () => {
|
|
104
|
+
await destroyAll();
|
|
105
|
+
process.exit(0);
|
|
106
|
+
});
|
|
107
|
+
await modulesService.loadModulesFromConfig(moduleContext);
|
|
108
|
+
|
|
109
|
+
RuntimeReload.configure(async () => {
|
|
110
|
+
await modulesService.destroyModules();
|
|
111
|
+
await Plugins.refreshConfiguredState();
|
|
112
|
+
await Mcp.closeAll();
|
|
113
|
+
|
|
114
|
+
Tools.resetTools();
|
|
115
|
+
Tools.defineTools(includedTools, allTools);
|
|
116
|
+
try {
|
|
117
|
+
const agentCallDef = AllTools.getTool?.("agentCall");
|
|
118
|
+
const agentCallFn = AllTools.getFunction?.("agentCall");
|
|
119
|
+
if (agentCallDef) Tools.addTool(agentCallDef);
|
|
120
|
+
if (agentCallFn) Tools.setFunction("agentCall", agentCallFn);
|
|
121
|
+
} catch (_) {}
|
|
122
|
+
|
|
123
|
+
await Behaviors.initFromConfig();
|
|
124
|
+
await Mcp.connectToConfigured(Tools);
|
|
125
|
+
await Clients.registerConfiguredModels();
|
|
126
|
+
await modulesService.loadModulesFromConfig(moduleContext);
|
|
127
|
+
|
|
128
|
+
const config = await getConfig();
|
|
129
|
+
return {
|
|
130
|
+
tools: Tools.getTools().length,
|
|
131
|
+
mcps: (config.mcps || []).length,
|
|
132
|
+
modules: (config.modules || []).length,
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
process.on("SIGTERM", async () => {
|
|
136
|
+
await destroyAll();
|
|
137
|
+
process.exit(0);
|
|
92
138
|
});
|
|
93
139
|
|
|
94
|
-
return { Tools, Clients };
|
|
140
|
+
return { Tools, Clients, modulesService };
|
|
95
141
|
}
|
package/src/plugins/plugins.ts
CHANGED
|
@@ -52,6 +52,22 @@ export class PluginService {
|
|
|
52
52
|
return true;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/** Apply the enabled/disabled state from the current Knowhow config. */
|
|
56
|
+
async refreshConfiguredState(): Promise<void> {
|
|
57
|
+
const config = await getConfig();
|
|
58
|
+
const disabled = new Set(getDisabledPlugins(config.plugins));
|
|
59
|
+
|
|
60
|
+
for (const [key, plugin] of this.pluginMap) {
|
|
61
|
+
if (disabled.has(key)) {
|
|
62
|
+
plugin.disable();
|
|
63
|
+
} else {
|
|
64
|
+
// Configuration is authoritative when services start or reload. This
|
|
65
|
+
// also re-enables a plugin after it is removed from `disabled`.
|
|
66
|
+
plugin.enable();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
/* -------- existing public API (updated for compatibility) ---------------------- */
|
|
56
72
|
|
|
57
73
|
listPlugins() {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CompressionMetadata,
|
|
8
8
|
JsonCompressorStorage,
|
|
9
9
|
} from "./JsonCompressor";
|
|
10
|
+
import { paginateToolResponse } from "./tools/boundedToolResponse";
|
|
10
11
|
|
|
11
12
|
export interface KeyInfo {
|
|
12
13
|
key: string;
|
|
@@ -419,15 +420,25 @@ export class TokenCompressor implements JsonCompressorStorage {
|
|
|
419
420
|
if (toolsService) {
|
|
420
421
|
toolsService.addTools([expandTokensDefinition]);
|
|
421
422
|
toolsService.addFunctions({
|
|
422
|
-
[this.toolName]: (
|
|
423
|
+
[this.toolName]: (
|
|
424
|
+
key: string,
|
|
425
|
+
fromLine?: number,
|
|
426
|
+
toLine?: number,
|
|
427
|
+
characterOffset?: number,
|
|
428
|
+
maxCharacters?: number
|
|
429
|
+
) => {
|
|
423
430
|
// Auto-stitch: follow any NEXT_CHUNK_KEY chain and return the full,
|
|
424
431
|
// reassembled content so the agent never has to chase nested keys.
|
|
425
432
|
const data = this.retrieveFullString(key);
|
|
426
433
|
|
|
427
434
|
if (data === null) {
|
|
428
|
-
return
|
|
429
|
-
"
|
|
430
|
-
|
|
435
|
+
return paginateToolResponse(
|
|
436
|
+
`Error: No data found for key "${key}". Available keys: ${this.getStorageKeys().join(
|
|
437
|
+
", "
|
|
438
|
+
)}`,
|
|
439
|
+
{ characterOffset, maxCharacters },
|
|
440
|
+
"available-key list"
|
|
441
|
+
);
|
|
431
442
|
}
|
|
432
443
|
|
|
433
444
|
// Optional ranged read: return only the requested 1-based, inclusive
|
|
@@ -435,7 +446,11 @@ export class TokenCompressor implements JsonCompressorStorage {
|
|
|
435
446
|
const hasRange =
|
|
436
447
|
typeof fromLine === "number" || typeof toLine === "number";
|
|
437
448
|
if (!hasRange) {
|
|
438
|
-
return
|
|
449
|
+
return paginateToolResponse(
|
|
450
|
+
data,
|
|
451
|
+
{ characterOffset, maxCharacters },
|
|
452
|
+
`expanded value for ${key}`
|
|
453
|
+
);
|
|
439
454
|
}
|
|
440
455
|
|
|
441
456
|
const lines = data.split("\n");
|
|
@@ -447,14 +462,22 @@ export class TokenCompressor implements JsonCompressorStorage {
|
|
|
447
462
|
);
|
|
448
463
|
|
|
449
464
|
if (start > end) {
|
|
450
|
-
return
|
|
465
|
+
return paginateToolResponse(
|
|
466
|
+
`Error: Invalid line range for key "${key}": fromLine (${start}) is greater than toLine (${end}). Content has ${totalLines} lines.`,
|
|
467
|
+
{ characterOffset, maxCharacters },
|
|
468
|
+
"line-range error"
|
|
469
|
+
);
|
|
451
470
|
}
|
|
452
471
|
|
|
453
472
|
const numbered: string[] = [];
|
|
454
473
|
for (let i = start; i <= end; i++) {
|
|
455
474
|
numbered.push(`${i}: ${lines[i - 1]}`);
|
|
456
475
|
}
|
|
457
|
-
return
|
|
476
|
+
return paginateToolResponse(
|
|
477
|
+
numbered.join("\n"),
|
|
478
|
+
{ characterOffset, maxCharacters },
|
|
479
|
+
`expanded lines ${start}-${end} for ${key}`
|
|
480
|
+
);
|
|
458
481
|
},
|
|
459
482
|
});
|
|
460
483
|
}
|
|
@@ -559,7 +582,7 @@ export const expandTokensDefinition: Tool = {
|
|
|
559
582
|
function: {
|
|
560
583
|
name: "expandTokens",
|
|
561
584
|
description:
|
|
562
|
-
"Retrieve
|
|
585
|
+
"Retrieve a bounded page of compressed data. Chunk chains are automatically reassembled, but output is limited to 20,000 characters by default. Use characterOffset to continue, or fromLine/toLine for line-based selection. Prefer jqToolResponse, grepToolResponse, or tailToolResponse when possible.",
|
|
563
586
|
parameters: {
|
|
564
587
|
type: "object",
|
|
565
588
|
positional: true,
|
|
@@ -578,6 +601,15 @@ export const expandTokensDefinition: Tool = {
|
|
|
578
601
|
description:
|
|
579
602
|
"Optional 1-based end line (inclusive). Defaults to the end of the content when omitted.",
|
|
580
603
|
},
|
|
604
|
+
characterOffset: {
|
|
605
|
+
type: "number",
|
|
606
|
+
description:
|
|
607
|
+
"Optional zero-based character offset within the expanded value or selected line range.",
|
|
608
|
+
},
|
|
609
|
+
maxCharacters: {
|
|
610
|
+
type: "number",
|
|
611
|
+
description: "Response size (default 20000, hard maximum 50000).",
|
|
612
|
+
},
|
|
581
613
|
},
|
|
582
614
|
required: ["key"],
|
|
583
615
|
},
|