@tyvm/knowhow 0.0.144 ā 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/bin/knowhow.js +22 -20
- package/package.json +1 -1
- package/scripts/migrate-usage-json.js +147 -0
- package/src/agents/base/base.ts +65 -10
- package/src/agents/tools/patch.ts +84 -2
- package/src/agents/tools/textSearch.ts +98 -3
- package/src/chat/CliChatService.ts +16 -0
- package/src/chat/modules/AgentModule.ts +159 -29
- package/src/chat/modules/SessionsModule.ts +13 -5
- package/src/chat/types.ts +15 -0
- package/src/chat.ts +11 -0
- package/src/cli.ts +2 -0
- package/src/clients/anthropic.ts +80 -1
- package/src/clients/contextLimits.ts +40 -0
- package/src/clients/fireworks.ts +3 -0
- package/src/clients/gemini.ts +67 -5
- package/src/clients/http.ts +85 -4
- package/src/clients/index.ts +4 -0
- package/src/clients/openai.ts +186 -35
- package/src/clients/pricing/fireworks.ts +50 -1
- package/src/clients/pricing/index.ts +1 -1
- package/src/clients/pricing/models.ts +3 -1
- package/src/clients/pricing/openai.ts +2 -2
- package/src/clients/pricing/qwen.ts +80 -0
- package/src/clients/qwen.ts +16 -0
- package/src/clients/types.ts +99 -8
- package/src/clients/xai.ts +96 -8
- package/src/commands/agent.ts +29 -1
- package/src/commands/services.ts +49 -3
- package/src/index.ts +9 -0
- package/src/plugins/plugins.ts +16 -0
- package/src/processors/Base64ImageDetector.ts +46 -7
- package/src/processors/GemmaToolProcessor.ts +163 -0
- package/src/processors/TokenCompressor.ts +40 -8
- package/src/processors/ToolResponseCache.ts +17 -8
- package/src/processors/index.ts +1 -0
- 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/AgentSyncFs.ts +37 -0
- package/src/services/LazyToolsService.ts +7 -0
- package/src/services/Mcp.ts +9 -2
- package/src/services/RuntimeReloadService.ts +31 -0
- package/src/services/SessionManager.ts +14 -0
- package/src/services/Tools.ts +14 -0
- package/src/services/TracingService.ts +116 -0
- package/src/services/index.ts +8 -0
- package/src/services/modules/computerUse.ts +216 -0
- package/src/services/modules/index.ts +90 -9
- package/src/services/modules/types.ts +31 -1
- package/src/types.ts +3 -0
- package/src/util/Trace.ts +236 -0
- package/tests/clients/contextLimits.test.ts +4 -0
- package/tests/clients/fireworks.test.ts +65 -0
- package/tests/clients/openai.images.test.ts +105 -0
- package/tests/clients/pricing.test.ts +6 -0
- package/tests/clients/reasoningEffort.test.ts +79 -0
- package/tests/compressor/toolResponseCache.test.ts +2 -1
- package/tests/compressor/usageBase64.json +159 -0
- package/tests/compressor/usageBase64.test.ts +175 -0
- package/tests/manual/clients/completions.test.ts +27 -10
- package/tests/patching/fixtures/transcript-mangles/agent-sync-fs-nested-catch.json +9 -0
- package/tests/patching/fixtures/transcript-mangles/automation-functions-crossed.json +9 -0
- package/tests/patching/fixtures/transcript-mangles/fruit-ninja-confirmed-retirement.json +8 -0
- package/tests/patching/patchFileOutput.test.ts +112 -1
- package/tests/patching/transcriptMangleRegression.test.ts +125 -0
- package/tests/processors/Base64ImageDetector.test.ts +36 -1
- 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/agents/base/base.d.ts +8 -1
- package/ts_build/src/agents/base/base.js +37 -3
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/patch.d.ts +1 -0
- package/ts_build/src/agents/tools/patch.js +35 -2
- package/ts_build/src/agents/tools/patch.js.map +1 -1
- package/ts_build/src/agents/tools/textSearch.js +56 -2
- package/ts_build/src/agents/tools/textSearch.js.map +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.d.ts +14 -1
- package/ts_build/src/chat/modules/AgentModule.js +86 -24
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +5 -6
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +12 -0
- package/ts_build/src/chat.js +7 -0
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.d.ts +2 -1
- package/ts_build/src/cli.js +1 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +1 -0
- package/ts_build/src/clients/anthropic.js +40 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/contextLimits.js +25 -0
- package/ts_build/src/clients/contextLimits.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 +56 -4
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/http.d.ts +2 -0
- package/ts_build/src/clients/http.js +66 -9
- package/ts_build/src/clients/http.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +1 -0
- package/ts_build/src/clients/index.js +4 -0
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +6 -1
- package/ts_build/src/clients/openai.js +135 -35
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +4 -1
- package/ts_build/src/clients/pricing/fireworks.js +33 -1
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
- package/ts_build/src/clients/pricing/index.d.ts +1 -1
- package/ts_build/src/clients/pricing/index.js +6 -2
- package/ts_build/src/clients/pricing/index.js.map +1 -1
- package/ts_build/src/clients/pricing/models.d.ts +2 -1
- package/ts_build/src/clients/pricing/models.js +4 -1
- package/ts_build/src/clients/pricing/models.js.map +1 -1
- package/ts_build/src/clients/pricing/openai.js +2 -2
- package/ts_build/src/clients/pricing/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/qwen.d.ts +5 -0
- package/ts_build/src/clients/pricing/qwen.js +30 -0
- package/ts_build/src/clients/pricing/qwen.js.map +1 -0
- package/ts_build/src/clients/qwen.d.ts +4 -0
- package/ts_build/src/clients/qwen.js +15 -0
- package/ts_build/src/clients/qwen.js.map +1 -0
- package/ts_build/src/clients/types.d.ts +16 -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 +2 -0
- package/ts_build/src/clients/xai.js +79 -13
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +15 -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/index.d.ts +2 -0
- package/ts_build/src/index.js +1 -0
- package/ts_build/src/index.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/Base64ImageDetector.js +22 -3
- package/ts_build/src/processors/Base64ImageDetector.js.map +1 -1
- package/ts_build/src/processors/GemmaToolProcessor.d.ts +14 -0
- package/ts_build/src/processors/GemmaToolProcessor.js +97 -0
- package/ts_build/src/processors/GemmaToolProcessor.js.map +1 -0
- 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/index.d.ts +1 -0
- package/ts_build/src/processors/index.js +3 -1
- package/ts_build/src/processors/index.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/AgentSyncFs.js +17 -0
- package/ts_build/src/services/AgentSyncFs.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/SessionManager.js +12 -0
- package/ts_build/src/services/SessionManager.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +3 -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/computerUse.d.ts +163 -0
- package/ts_build/src/services/modules/computerUse.js +3 -0
- package/ts_build/src/services/modules/computerUse.js.map +1 -0
- package/ts_build/src/services/modules/index.d.ts +6 -0
- package/ts_build/src/services/modules/index.js +49 -6
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +7 -0
- package/ts_build/src/types.d.ts +3 -0
- package/ts_build/src/types.js.map +1 -1
- 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/src/workers/tools/index.d.ts +1 -1
- package/ts_build/tests/clients/contextLimits.test.js +2 -0
- package/ts_build/tests/clients/contextLimits.test.js.map +1 -1
- 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/openai.images.test.d.ts +1 -0
- package/ts_build/tests/clients/openai.images.test.js +94 -0
- package/ts_build/tests/clients/openai.images.test.js.map +1 -0
- package/ts_build/tests/clients/pricing.test.js +4 -0
- package/ts_build/tests/clients/pricing.test.js.map +1 -1
- 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/compressor/usageBase64.test.d.ts +1 -0
- package/ts_build/tests/compressor/usageBase64.test.js +109 -0
- package/ts_build/tests/compressor/usageBase64.test.js.map +1 -0
- 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/patching/patchFileOutput.test.js +82 -0
- package/ts_build/tests/patching/patchFileOutput.test.js.map +1 -1
- package/ts_build/tests/patching/transcriptMangleRegression.test.d.ts +1 -0
- package/ts_build/tests/patching/transcriptMangleRegression.test.js +104 -0
- package/ts_build/tests/patching/transcriptMangleRegression.test.js.map +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js +24 -0
- package/ts_build/tests/processors/Base64ImageDetector.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/x-home.png +0 -0
|
@@ -80,6 +80,13 @@ export class LazyToolsService extends ToolsService {
|
|
|
80
80
|
return this.tools; // Returns filtered subset
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/** Rebuild the lazy catalog while preserving enable/disable preferences. */
|
|
84
|
+
resetTools(): void {
|
|
85
|
+
this.allTools = [];
|
|
86
|
+
super.resetTools();
|
|
87
|
+
this.registerLazyTools();
|
|
88
|
+
}
|
|
89
|
+
|
|
83
90
|
// Enable tools matching glob patterns
|
|
84
91
|
enableTools(patterns: string[]) {
|
|
85
92
|
for (const pattern of patterns) {
|
package/src/services/Mcp.ts
CHANGED
|
@@ -388,8 +388,15 @@ export class McpService {
|
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
async closeAll() {
|
|
391
|
-
await this.closeTransports();
|
|
392
|
-
await this.closeClients();
|
|
391
|
+
await Promise.allSettled([this.closeTransports()]);
|
|
392
|
+
await Promise.allSettled([this.closeClients()]);
|
|
393
|
+
// Connections are recreated from freshly-read configuration after reload.
|
|
394
|
+
this.clients = [];
|
|
395
|
+
this.transports = [];
|
|
396
|
+
this.connected = [];
|
|
397
|
+
this.config = [];
|
|
398
|
+
this.tools = [];
|
|
399
|
+
this.toolAliases = {};
|
|
393
400
|
}
|
|
394
401
|
|
|
395
402
|
getClientIndex(clientName: string) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface RuntimeReloadResult {
|
|
2
|
+
tools: number;
|
|
3
|
+
mcps: number;
|
|
4
|
+
modules: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Coordinates in-place runtime refreshes and coalesces concurrent requests. */
|
|
8
|
+
export class RuntimeReloadService {
|
|
9
|
+
private handler?: () => Promise<RuntimeReloadResult>;
|
|
10
|
+
private pending?: Promise<RuntimeReloadResult>;
|
|
11
|
+
|
|
12
|
+
configure(handler: () => Promise<RuntimeReloadResult>): void {
|
|
13
|
+
this.handler = handler;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
isConfigured(): boolean {
|
|
17
|
+
return !!this.handler;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reload(): Promise<RuntimeReloadResult> {
|
|
21
|
+
if (!this.handler) {
|
|
22
|
+
return Promise.reject(new Error("Runtime reload is unavailable for this command"));
|
|
23
|
+
}
|
|
24
|
+
if (!this.pending) {
|
|
25
|
+
this.pending = this.handler().finally(() => {
|
|
26
|
+
this.pending = undefined;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return this.pending;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -65,6 +65,10 @@ export class SessionManager {
|
|
|
65
65
|
threads,
|
|
66
66
|
currentThread: 0,
|
|
67
67
|
lastUpdated: Date.now(),
|
|
68
|
+
model: taskInfo.model,
|
|
69
|
+
provider: taskInfo.provider,
|
|
70
|
+
reasoningEffort: taskInfo.reasoningEffort,
|
|
71
|
+
summarizeReasoning: taskInfo.summarizeReasoning,
|
|
68
72
|
};
|
|
69
73
|
|
|
70
74
|
fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
|
|
@@ -101,6 +105,16 @@ export class SessionManager {
|
|
|
101
105
|
session.knowhowMessageId = taskInfo.knowhowMessageId;
|
|
102
106
|
session.knowhowTaskId = taskInfo.knowhowTaskId;
|
|
103
107
|
session.chatSessionId = taskInfo.chatSessionId;
|
|
108
|
+
|
|
109
|
+
// Keep the persisted run configuration in sync (captures mid-run
|
|
110
|
+
// model/provider fallbacks) so a resume restores the exact setup.
|
|
111
|
+
if (taskInfo.model !== undefined) session.model = taskInfo.model;
|
|
112
|
+
if (taskInfo.provider !== undefined)
|
|
113
|
+
session.provider = taskInfo.provider;
|
|
114
|
+
if (taskInfo.reasoningEffort !== undefined)
|
|
115
|
+
session.reasoningEffort = taskInfo.reasoningEffort;
|
|
116
|
+
if (taskInfo.summarizeReasoning !== undefined)
|
|
117
|
+
session.summarizeReasoning = taskInfo.summarizeReasoning;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
|
package/src/services/Tools.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface ToolCallContext {
|
|
|
34
34
|
caller?: any;
|
|
35
35
|
/** The task id of the calling agent, if known. */
|
|
36
36
|
taskId?: string;
|
|
37
|
+
/** The task id of the parent agent that spawned the calling agent, if any. */
|
|
38
|
+
parentTaskId?: string;
|
|
37
39
|
/** Arbitrary additional per-call context. */
|
|
38
40
|
[key: string]: any;
|
|
39
41
|
}
|
|
@@ -46,6 +48,7 @@ export interface ToolContext {
|
|
|
46
48
|
Plugins?: PluginService;
|
|
47
49
|
Mcp?: McpService;
|
|
48
50
|
Behaviors?: BehaviorsService;
|
|
51
|
+
ComputerUse?: import("./modules/computerUse").ComputerUseService;
|
|
49
52
|
metadata?: { [key: string]: any };
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -76,6 +79,17 @@ export class ToolsService {
|
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Remove every registered tool and handler while retaining service context,
|
|
84
|
+
* wrappers, and overrides. Runtime reload uses this before rebuilding the
|
|
85
|
+
* catalog from built-ins, MCP servers, and configured modules.
|
|
86
|
+
*/
|
|
87
|
+
resetTools(): void {
|
|
88
|
+
this.tools = [];
|
|
89
|
+
this.functions = {};
|
|
90
|
+
this.originalFunctions = {};
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
getContext(): ToolContext {
|
|
80
94
|
return this.context;
|
|
81
95
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TracingService ā a zero-dependency, no-op-by-default tracing abstraction
|
|
3
|
+
* for the @tyvm/knowhow core package.
|
|
4
|
+
*
|
|
5
|
+
* The core package ships with NO OpenTelemetry dependencies. This service
|
|
6
|
+
* defines the interface that the decorators in `src/util/Trace.ts` call into,
|
|
7
|
+
* and holds a singleton "current tracer" that starts as a no-op.
|
|
8
|
+
*
|
|
9
|
+
* A module (e.g. @tyvm/knowhow-module-tracing) can call
|
|
10
|
+
* `TracingService.register(impl)` during its `init()` to swap in a real OTEL
|
|
11
|
+
* implementation. After that, every @Trace / @TraceAll / tracify call in the
|
|
12
|
+
* knowhow codebase will produce real spans ā with zero changes to the core.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface SpanHandle {
|
|
16
|
+
/** Set a string/number/boolean attribute on the span. */
|
|
17
|
+
setAttribute(key: string, value: string | number | boolean): void;
|
|
18
|
+
/** Mark the span as errored and record the exception. */
|
|
19
|
+
recordError(err: unknown): void;
|
|
20
|
+
/** End the span (records wall-clock end time). */
|
|
21
|
+
end(): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TracerImpl {
|
|
25
|
+
/**
|
|
26
|
+
* Start a span. The implementation is responsible for making it a child of
|
|
27
|
+
* the currently-active span (via OpenTelemetry context propagation or
|
|
28
|
+
* equivalent).
|
|
29
|
+
*
|
|
30
|
+
* @param name Span name, e.g. "ClassName.methodName"
|
|
31
|
+
* @param attributes Initial attributes to attach
|
|
32
|
+
*/
|
|
33
|
+
startSpan(
|
|
34
|
+
name: string,
|
|
35
|
+
attributes?: Record<string, string | number | boolean>
|
|
36
|
+
): SpanHandle;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Run `fn` with the given span active so that child spans created inside it
|
|
40
|
+
* are automatically parented to this one.
|
|
41
|
+
*
|
|
42
|
+
* If context propagation is not supported, implementations can simply call
|
|
43
|
+
* `fn()` directly.
|
|
44
|
+
*/
|
|
45
|
+
withSpan<T>(span: SpanHandle, fn: () => T): T;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// āāā No-op implementation used until a real tracer is registered āāāāāāāāāāāāā
|
|
49
|
+
|
|
50
|
+
const noopSpan: SpanHandle = {
|
|
51
|
+
setAttribute() {},
|
|
52
|
+
recordError() {},
|
|
53
|
+
end() {},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const noopTracer: TracerImpl = {
|
|
57
|
+
startSpan() {
|
|
58
|
+
return noopSpan;
|
|
59
|
+
},
|
|
60
|
+
withSpan(_span, fn) {
|
|
61
|
+
return fn();
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// āāā Singleton state āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
66
|
+
|
|
67
|
+
let _impl: TracerImpl = noopTracer;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* TracingService ā access point for the current tracer implementation.
|
|
71
|
+
*
|
|
72
|
+
* Usage in decorators / instrumentation code:
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { TracingService } from '../services/TracingService';
|
|
75
|
+
* const span = TracingService.startSpan('MyClass.myMethod');
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* Usage in a module to plug in a real OTEL tracer:
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { TracingService } from '@tyvm/knowhow/...';
|
|
81
|
+
* TracingService.register(myOtelTracerImpl);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const TracingService = {
|
|
85
|
+
/**
|
|
86
|
+
* Register a real tracer implementation. Called by the tracing module during
|
|
87
|
+
* `init()`. The registration is global and immediate ā all subsequent
|
|
88
|
+
* decorator/tracify calls will use this implementation.
|
|
89
|
+
*/
|
|
90
|
+
register(impl: TracerImpl): void {
|
|
91
|
+
_impl = impl;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/** Reset to the no-op tracer (useful for tests). */
|
|
95
|
+
reset(): void {
|
|
96
|
+
_impl = noopTracer;
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/** Whether a real (non-no-op) tracer has been registered. */
|
|
100
|
+
isEnabled(): boolean {
|
|
101
|
+
return _impl !== noopTracer;
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/** Start a span using the current implementation. */
|
|
105
|
+
startSpan(
|
|
106
|
+
name: string,
|
|
107
|
+
attributes?: Record<string, string | number | boolean>
|
|
108
|
+
): SpanHandle {
|
|
109
|
+
return _impl.startSpan(name, attributes);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/** Run a function with a span active (context propagation). */
|
|
113
|
+
withSpan<T>(span: SpanHandle, fn: () => T): T {
|
|
114
|
+
return _impl.withSpan(span, fn);
|
|
115
|
+
},
|
|
116
|
+
};
|
package/src/services/index.ts
CHANGED
|
@@ -15,8 +15,10 @@ import { SessionManager } from "./SessionManager";
|
|
|
15
15
|
import { TaskRegistry } from "./TaskRegistry";
|
|
16
16
|
import { MediaProcessorService } from "./MediaProcessorService";
|
|
17
17
|
import { BehaviorsService } from "./BehaviorsService";
|
|
18
|
+
import { RuntimeReloadService } from "./RuntimeReloadService";
|
|
18
19
|
|
|
19
20
|
import { ConversionService } from "./conversion/ConversionService";
|
|
21
|
+
import { TracingService } from "./TracingService";
|
|
20
22
|
|
|
21
23
|
export * from "./AgentService";
|
|
22
24
|
export * from "./EventService";
|
|
@@ -38,7 +40,11 @@ export * from "./SyncedAgentWatcher";
|
|
|
38
40
|
export * from "./SyncerService";
|
|
39
41
|
export * from "./watchers";
|
|
40
42
|
export { Clients } from "../clients";
|
|
43
|
+
export * from "../util/Trace";
|
|
44
|
+
export { TracingService } from "./TracingService";
|
|
45
|
+
export type { TracerImpl, SpanHandle } from "./TracingService";
|
|
41
46
|
export * from "./conversion";
|
|
47
|
+
export * from "./RuntimeReloadService";
|
|
42
48
|
export { BehaviorsService } from "./BehaviorsService";
|
|
43
49
|
|
|
44
50
|
let Singletons = {} as {
|
|
@@ -56,6 +62,7 @@ let Singletons = {} as {
|
|
|
56
62
|
MediaProcessor: MediaProcessorService;
|
|
57
63
|
Conversion: ConversionService;
|
|
58
64
|
Behaviors: BehaviorsService;
|
|
65
|
+
RuntimeReload: RuntimeReloadService;
|
|
59
66
|
};
|
|
60
67
|
|
|
61
68
|
export const services = (): typeof Singletons => {
|
|
@@ -88,6 +95,7 @@ export const services = (): typeof Singletons => {
|
|
|
88
95
|
Tools,
|
|
89
96
|
knowhowApiClient: new KnowhowSimpleClient(),
|
|
90
97
|
Behaviors,
|
|
98
|
+
RuntimeReload: new RuntimeReloadService(),
|
|
91
99
|
};
|
|
92
100
|
|
|
93
101
|
Singletons.Tools.setContext({
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, driver-agnostic interface for the computer-use service.
|
|
3
|
+
*
|
|
4
|
+
* The concrete implementation lives in `@tyvm/knowhow-module-computer-use`, but
|
|
5
|
+
* we declare the shape here so that `ToolContext` / `ModuleContext` can carry a
|
|
6
|
+
* `ComputerUse` service without the core knowhow package taking a runtime
|
|
7
|
+
* dependency on the module.
|
|
8
|
+
*
|
|
9
|
+
* An adapter module (e.g. `@tyvm/knowhow-module-computer-use-nutjs`) can
|
|
10
|
+
* register a `ComputerDriver` against the service in its `register` phase; the
|
|
11
|
+
* base module then `init()`s and picks the best available driver.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface Point {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Size {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Region extends Point, Size {}
|
|
25
|
+
|
|
26
|
+
export type MouseButton = "left" | "right" | "middle";
|
|
27
|
+
|
|
28
|
+
export interface OverlayPrimitive {
|
|
29
|
+
kind: "rect" | "circle" | "line" | "point";
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
width?: number;
|
|
33
|
+
height?: number;
|
|
34
|
+
x2?: number;
|
|
35
|
+
y2?: number;
|
|
36
|
+
color?: string;
|
|
37
|
+
lineWidth?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Display {
|
|
41
|
+
id: number;
|
|
42
|
+
bounds: Region;
|
|
43
|
+
scaleFactor: number;
|
|
44
|
+
primary: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ScreenshotOptions {
|
|
48
|
+
region?: Region;
|
|
49
|
+
displayId?: number;
|
|
50
|
+
format?: "png" | "jpeg";
|
|
51
|
+
scale?: number;
|
|
52
|
+
/** Native capture downscale. Unlike output encoding scale, drivers may apply
|
|
53
|
+
* this before raw pixels cross the process boundary. */
|
|
54
|
+
captureScale?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DriverCapabilities {
|
|
58
|
+
input: boolean;
|
|
59
|
+
capture: boolean;
|
|
60
|
+
windows: boolean;
|
|
61
|
+
reason?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface AccessibilityElement {
|
|
65
|
+
id: string;
|
|
66
|
+
role: string;
|
|
67
|
+
subrole?: string;
|
|
68
|
+
title?: string;
|
|
69
|
+
description?: string;
|
|
70
|
+
value?: string;
|
|
71
|
+
enabled?: boolean;
|
|
72
|
+
focused?: boolean;
|
|
73
|
+
bounds?: Region;
|
|
74
|
+
actions: string[];
|
|
75
|
+
childCount: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AccessibilityOptions {
|
|
79
|
+
maxDepth?: number;
|
|
80
|
+
maxElements?: number;
|
|
81
|
+
interactiveOnly?: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The engine seam. Concrete drivers (Rust core, CLI adapter, nut.js demo)
|
|
86
|
+
* implement this. Registered with the service via `registerDriver`.
|
|
87
|
+
*/
|
|
88
|
+
export interface ComputerDriver {
|
|
89
|
+
readonly name: string;
|
|
90
|
+
/**
|
|
91
|
+
* Lower numbers win when multiple drivers are registered. The default Rust
|
|
92
|
+
* core registers at 100; a fallback CLI adapter at 500; an experimental demo
|
|
93
|
+
* adapter can register at e.g. 10 to force selection.
|
|
94
|
+
*/
|
|
95
|
+
readonly priority?: number;
|
|
96
|
+
|
|
97
|
+
capabilities(): Promise<DriverCapabilities>;
|
|
98
|
+
|
|
99
|
+
// screen
|
|
100
|
+
getDisplays(): Promise<Display[]>;
|
|
101
|
+
screenSize(): Promise<Size>;
|
|
102
|
+
screenshot(opts?: ScreenshotOptions): Promise<Buffer>;
|
|
103
|
+
pixelColor(p: Point): Promise<string>;
|
|
104
|
+
|
|
105
|
+
// mouse
|
|
106
|
+
mousePosition(): Promise<Point>;
|
|
107
|
+
moveMouse(p: Point, opts?: { duration?: number }): Promise<void>;
|
|
108
|
+
click(button?: MouseButton, opts?: { double?: boolean }): Promise<void>;
|
|
109
|
+
mouseDown(button?: MouseButton): Promise<void>;
|
|
110
|
+
mouseUp(button?: MouseButton): Promise<void>;
|
|
111
|
+
drag(
|
|
112
|
+
from: Point,
|
|
113
|
+
to: Point,
|
|
114
|
+
opts?: { button?: MouseButton; duration?: number }
|
|
115
|
+
): Promise<void>;
|
|
116
|
+
scroll(dx: number, dy: number): Promise<void>;
|
|
117
|
+
|
|
118
|
+
// keyboard
|
|
119
|
+
typeText(text: string, opts?: { delay?: number }): Promise<void>;
|
|
120
|
+
pressKey(key: string): Promise<void>;
|
|
121
|
+
hotkey(...keys: string[]): Promise<void>;
|
|
122
|
+
keyDown(key: string): Promise<void>;
|
|
123
|
+
keyUp(key: string): Promise<void>;
|
|
124
|
+
|
|
125
|
+
// windows (optional / capability-gated)
|
|
126
|
+
getActiveWindow?(): Promise<{ title: string; app?: string; bounds?: Region } | null>;
|
|
127
|
+
listWindows?(): Promise<Array<{ title: string; app?: string; bounds?: Region }>>;
|
|
128
|
+
focusWindow?(match: string): Promise<boolean>;
|
|
129
|
+
|
|
130
|
+
accessibilityTrusted?(): Promise<boolean>;
|
|
131
|
+
accessibilityElements?(options?: AccessibilityOptions): Promise<AccessibilityElement[]>;
|
|
132
|
+
setAccessibilityValue?(id: string, value: string): Promise<void>;
|
|
133
|
+
performAccessibilityAction?(id: string, action: string): Promise<void>;
|
|
134
|
+
|
|
135
|
+
// click-through visual debugging (optional / platform capability-gated)
|
|
136
|
+
showOverlay?(primitives: OverlayPrimitive[]): Promise<void>;
|
|
137
|
+
clearOverlay?(): Promise<void>;
|
|
138
|
+
|
|
139
|
+
dispose?(): Promise<void>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The registered service (`ComputerUse`) surfaced to tools/CLI/MCP. It owns
|
|
144
|
+
* driver selection and provides the normalized, platform-agnostic verbs.
|
|
145
|
+
*
|
|
146
|
+
* We keep this as an interface (not a class) in the core package so the module
|
|
147
|
+
* owns the concrete implementation; anything reading `context.ComputerUse`
|
|
148
|
+
* codes against this contract.
|
|
149
|
+
*/
|
|
150
|
+
export interface ComputerUseService {
|
|
151
|
+
/** Register a driver (called by the base module and any adapter modules). */
|
|
152
|
+
registerDriver(driver: ComputerDriver): void;
|
|
153
|
+
/** List the names of all registered drivers. */
|
|
154
|
+
listDrivers(): string[];
|
|
155
|
+
/** Resolve/select the active driver (highest priority + capable). */
|
|
156
|
+
getDriver(): Promise<ComputerDriver>;
|
|
157
|
+
|
|
158
|
+
capabilities(): Promise<DriverCapabilities>;
|
|
159
|
+
getDisplays(): Promise<Display[]>;
|
|
160
|
+
screenSize(): Promise<Size>;
|
|
161
|
+
/** Returns encoded image bytes (PNG/JPEG) honoring service defaults. */
|
|
162
|
+
screenshot(opts?: ScreenshotOptions): Promise<Buffer>;
|
|
163
|
+
/** Convenience: base64 data (no data: prefix) for multimodal messages. */
|
|
164
|
+
screenshotBase64(opts?: ScreenshotOptions): Promise<string>;
|
|
165
|
+
pixelColor(p: Point): Promise<string>;
|
|
166
|
+
|
|
167
|
+
mousePosition(): Promise<Point>;
|
|
168
|
+
moveMouse(p: Point, opts?: { duration?: number }): Promise<void>;
|
|
169
|
+
click(button?: MouseButton, opts?: { double?: boolean }): Promise<void>;
|
|
170
|
+
drag(
|
|
171
|
+
from: Point,
|
|
172
|
+
to: Point,
|
|
173
|
+
opts?: { button?: MouseButton; duration?: number }
|
|
174
|
+
): Promise<void>;
|
|
175
|
+
scroll(dx: number, dy: number): Promise<void>;
|
|
176
|
+
/** Smooth stepped scrolling (repeat a small delta with a pause between). */
|
|
177
|
+
scrollBy?(
|
|
178
|
+
dx: number,
|
|
179
|
+
dy: number,
|
|
180
|
+
opts?: { repeat?: number; intervalMs?: number }
|
|
181
|
+
): Promise<void>;
|
|
182
|
+
|
|
183
|
+
typeText(text: string, opts?: { delay?: number }): Promise<void>;
|
|
184
|
+
pressKey(key: string): Promise<void>;
|
|
185
|
+
hotkey(...keys: string[]): Promise<void>;
|
|
186
|
+
|
|
187
|
+
/** Screenshot with an optional labeled grid and/or crosshair overlay. */
|
|
188
|
+
screenshotAnnotated?(
|
|
189
|
+
opts?: ScreenshotOptions & {
|
|
190
|
+
grid?: boolean;
|
|
191
|
+
gridStep?: number;
|
|
192
|
+
crosshair?: Point;
|
|
193
|
+
}
|
|
194
|
+
): Promise<Buffer>;
|
|
195
|
+
|
|
196
|
+
getActiveWindow(): Promise<{
|
|
197
|
+
title: string;
|
|
198
|
+
app?: string;
|
|
199
|
+
bounds?: Region;
|
|
200
|
+
} | null>;
|
|
201
|
+
listWindows(): Promise<
|
|
202
|
+
Array<{ title: string; app?: string; bounds?: Region }>
|
|
203
|
+
>;
|
|
204
|
+
focusWindow(match: string): Promise<boolean>;
|
|
205
|
+
|
|
206
|
+
accessibilityTrusted(): Promise<boolean>;
|
|
207
|
+
accessibilityElements(options?: AccessibilityOptions): Promise<AccessibilityElement[]>;
|
|
208
|
+
selectAccessibilityOption(id: string, option: string): Promise<void>;
|
|
209
|
+
setAccessibilityValue(id: string, value: string): Promise<void>;
|
|
210
|
+
performAccessibilityAction(id: string, action: string): Promise<void>;
|
|
211
|
+
|
|
212
|
+
showOverlay(primitives: OverlayPrimitive[]): Promise<void>;
|
|
213
|
+
clearOverlay(): Promise<void>;
|
|
214
|
+
|
|
215
|
+
dispose(): Promise<void>;
|
|
216
|
+
}
|
|
@@ -39,6 +39,13 @@ function globalResolvePaths(): string[] {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export class ModulesService {
|
|
42
|
+
private _loadedModules: {
|
|
43
|
+
modulePath: string;
|
|
44
|
+
resolvedPath: string;
|
|
45
|
+
module: KnowhowModule;
|
|
46
|
+
params: any;
|
|
47
|
+
}[] = [];
|
|
48
|
+
|
|
42
49
|
async getDefaultContext() {
|
|
43
50
|
return { ...services() };
|
|
44
51
|
}
|
|
@@ -75,6 +82,22 @@ export class ModulesService {
|
|
|
75
82
|
// modules resolve against cwd/.knowhow/node_modules first. See localResolvePaths()
|
|
76
83
|
// and globalResolvePaths() above.
|
|
77
84
|
|
|
85
|
+
// Two-phase load:
|
|
86
|
+
// Phase 1 (register): resolve + require EVERY module and run its optional
|
|
87
|
+
// `register()`, which may (a) register CLI subcommands on Program and
|
|
88
|
+
// (b) inject shared services into `context` (e.g. `ComputerUse`) so that
|
|
89
|
+
// sibling/adapter modules loaded later can consume them.
|
|
90
|
+
// Phase 2 (init): run every loaded module's `init()`, now that all
|
|
91
|
+
// services registered during phase 1 are present. This lets, e.g., a
|
|
92
|
+
// `-nutjs` adapter module register a driver against the base
|
|
93
|
+
// computer-use service during phase 1, and the base module resolve the
|
|
94
|
+
// best driver during phase 2.
|
|
95
|
+
const loadedModules: {
|
|
96
|
+
modulePath: string;
|
|
97
|
+
resolvedPath: string;
|
|
98
|
+
module: KnowhowModule;
|
|
99
|
+
}[] = [];
|
|
100
|
+
|
|
78
101
|
for (const modulePath of allModulePaths) {
|
|
79
102
|
// Build an ordered list of candidate resolutions for this module.
|
|
80
103
|
// Relative paths resolve to a single candidate (relative to cwd), while
|
|
@@ -113,6 +136,7 @@ export class ModulesService {
|
|
|
113
136
|
// global install).
|
|
114
137
|
let importedModule: KnowhowModule;
|
|
115
138
|
let loaded = false;
|
|
139
|
+
let selectedResolvedPath = modulePath;
|
|
116
140
|
const errors: { candidate: string; error: Error }[] = [];
|
|
117
141
|
for (const resolvedPath of candidates) {
|
|
118
142
|
try {
|
|
@@ -122,15 +146,17 @@ export class ModulesService {
|
|
|
122
146
|
"ModulesService",
|
|
123
147
|
`š Loading module: ${modulePath} (resolved: ${resolvedPath})`
|
|
124
148
|
);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
149
|
+
// Phase 1: register (optional). Injects services into `context` and
|
|
150
|
+
// registers CLI commands. Must be idempotent (may run in the early
|
|
151
|
+
// Program-only CLI phase AND the full-services phase).
|
|
152
|
+
if (typeof importedModule.register === "function") {
|
|
153
|
+
await importedModule.register({
|
|
154
|
+
config,
|
|
155
|
+
cwd: process.cwd(),
|
|
156
|
+
context: context as ModuleContext,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
selectedResolvedPath = resolvedPath;
|
|
134
160
|
loaded = true;
|
|
135
161
|
break;
|
|
136
162
|
} catch (err: any) {
|
|
@@ -152,6 +178,35 @@ export class ModulesService {
|
|
|
152
178
|
);
|
|
153
179
|
continue;
|
|
154
180
|
}
|
|
181
|
+
loadedModules.push({
|
|
182
|
+
modulePath,
|
|
183
|
+
resolvedPath: selectedResolvedPath,
|
|
184
|
+
module: importedModule,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Phase 2: init every successfully-loaded module now that all `register`
|
|
189
|
+
// phases have run and injected their services into `context`.
|
|
190
|
+
for (const { modulePath, resolvedPath, module: importedModule } of loadedModules) {
|
|
191
|
+
const initParams = { config, cwd: process.cwd(), context: context as ModuleContext };
|
|
192
|
+
try {
|
|
193
|
+
await importedModule.init({
|
|
194
|
+
config,
|
|
195
|
+
cwd: process.cwd(),
|
|
196
|
+
context: context as ModuleContext,
|
|
197
|
+
});
|
|
198
|
+
context.Events?.log(
|
|
199
|
+
"ModulesService",
|
|
200
|
+
`ā
Module initialized: ${modulePath} (tools: ${importedModule.tools.length}, agents: ${importedModule.agents.length}, plugins: ${importedModule.plugins.length}, clients: ${importedModule.clients.length})`
|
|
201
|
+
);
|
|
202
|
+
} catch (err: any) {
|
|
203
|
+
process.stderr.write(
|
|
204
|
+
`\nā ļø Failed to init module "${modulePath}": ${err?.message || err}\n\n`
|
|
205
|
+
);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
this._loadedModules.push({ modulePath, resolvedPath, module: importedModule, params: initParams });
|
|
209
|
+
|
|
155
210
|
// Only register tools/agents/plugins/clients if the relevant services
|
|
156
211
|
// are available in context (they may not be during early CLI command registration)
|
|
157
212
|
if (context.Agents) {
|
|
@@ -188,6 +243,32 @@ export class ModulesService {
|
|
|
188
243
|
}
|
|
189
244
|
}
|
|
190
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Call `destroy()` on every successfully-initialized module, in reverse
|
|
248
|
+
* initialization order (last-in, first-out). Errors are caught and logged
|
|
249
|
+
* so one broken module does not prevent others from cleaning up.
|
|
250
|
+
*/
|
|
251
|
+
async destroyModules() {
|
|
252
|
+
const toDestroy = [...this._loadedModules].reverse();
|
|
253
|
+
// Clear first so repeated shutdown/reload attempts cannot destroy twice.
|
|
254
|
+
this._loadedModules = [];
|
|
255
|
+
for (const { modulePath, resolvedPath, module: mod, params } of toDestroy) {
|
|
256
|
+
if (typeof mod.destroy === "function") {
|
|
257
|
+
try {
|
|
258
|
+
await mod.destroy(params);
|
|
259
|
+
} catch (err: any) {
|
|
260
|
+
process.stderr.write(
|
|
261
|
+
`\nā ļø Error in module destroy "${modulePath}": ${err?.message || err}\n\n`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Re-require the entry point so source/configuration changes are visible.
|
|
266
|
+
if (require.cache[resolvedPath]) {
|
|
267
|
+
delete require.cache[resolvedPath];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
191
272
|
async loadModulesFromConfig(context?: ModuleContext) {
|
|
192
273
|
const config = await getConfig();
|
|
193
274
|
|
|
@@ -14,7 +14,9 @@ import { TunnelHandler } from "@tyvm/knowhow-tunnel";
|
|
|
14
14
|
import { EventService } from "../EventService";
|
|
15
15
|
import { ConversionService } from "../conversion/ConversionService";
|
|
16
16
|
import { BehaviorsService } from "../BehaviorsService";
|
|
17
|
+
import { ComputerUseService } from "./computerUse";
|
|
17
18
|
|
|
19
|
+
import { TracingService as TracingServiceType } from "../TracingService";
|
|
18
20
|
/*
|
|
19
21
|
*
|
|
20
22
|
* A a module should allow the dynamic composition of npm modules that are installed globally by referencing an array of config
|
|
@@ -62,11 +64,39 @@ export interface ModuleContext {
|
|
|
62
64
|
Conversion?: ConversionService;
|
|
63
65
|
Tunnel?: TunnelHandler;
|
|
64
66
|
Program?: Command;
|
|
65
|
-
Behaviors?: BehaviorsService
|
|
67
|
+
Behaviors?: BehaviorsService;
|
|
68
|
+
ComputerUse?: ComputerUseService;
|
|
69
|
+
[key: string]: any;
|
|
70
|
+
Tracing?: typeof TracingServiceType;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
export interface KnowhowModule {
|
|
74
|
+
/**
|
|
75
|
+
* Phase 1 (optional): called first, potentially with ONLY `Program` in
|
|
76
|
+
* context (e.g. during early CLI command registration). Use this to:
|
|
77
|
+
* - register CLI subcommands on `context.Program`
|
|
78
|
+
* - inject services into the shared context (e.g.
|
|
79
|
+
* `context.Tools?.addContext("ComputerUse", svc)`) so that OTHER modules
|
|
80
|
+
* (or a sibling adapter module) can consume them during their own init.
|
|
81
|
+
*
|
|
82
|
+
* `register` must be side-effect-light and MUST NOT assume the full service
|
|
83
|
+
* graph is present ā only `Program` is guaranteed during the early phase.
|
|
84
|
+
* It may be called more than once (early CLI phase + full-services phase);
|
|
85
|
+
* implementations should be idempotent.
|
|
86
|
+
*/
|
|
87
|
+
register?: (params: InitParams) => Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Phase 2: called with the full service graph available. Use this to
|
|
90
|
+
* actually connect/start things (open drivers, spin up watchers, read
|
|
91
|
+
* services registered by other modules' `register` phase, etc.).
|
|
92
|
+
*/
|
|
69
93
|
init: (params: InitParams) => Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Phase 3 (optional): called when the CLI is shutting down. Use this to
|
|
96
|
+
* cleanly release resources ā close connections, flush buffers, stop
|
|
97
|
+
* watchers, etc. Mirror of `init`.
|
|
98
|
+
*/
|
|
99
|
+
destroy?: (params: InitParams) => Promise<void>;
|
|
70
100
|
commands: ModuleChatCommand[];
|
|
71
101
|
tools: ModuleTool[];
|
|
72
102
|
agents: ModuleAgent[];
|