@tyvm/knowhow 0.0.104 → 0.0.106

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.
Files changed (233) hide show
  1. package/CONFIG.md +8 -5
  2. package/package.json +3 -2
  3. package/scripts/check-model-pricing.ts +509 -0
  4. package/scripts/compare-openrouter-coverage.ts +576 -0
  5. package/src/agents/base/base.ts +127 -2
  6. package/src/agents/tools/execCommand.ts +4 -0
  7. package/src/agents/tools/executeScript/definition.ts +1 -1
  8. package/src/agents/tools/index.ts +0 -1
  9. package/src/agents/tools/list.ts +3 -43
  10. package/src/agents/tools/writeFile.ts +1 -1
  11. package/src/auth/browserLogin.ts +9 -4
  12. package/src/chat/modules/RemoteSyncModule.ts +3 -0
  13. package/src/cli.ts +31 -1
  14. package/src/clients/cerebras.ts +10 -0
  15. package/src/clients/contextLimits.ts +7 -2
  16. package/src/clients/copilot.ts +23 -0
  17. package/src/clients/deepseek.ts +16 -0
  18. package/src/clients/fireworks.ts +15 -0
  19. package/src/clients/gemini.ts +45 -2
  20. package/src/clients/github.ts +16 -0
  21. package/src/clients/groq.ts +15 -0
  22. package/src/clients/http.ts +190 -6
  23. package/src/clients/index.ts +215 -9
  24. package/src/clients/llama.ts +16 -0
  25. package/src/clients/mistral.ts +16 -0
  26. package/src/clients/nvidia.ts +16 -0
  27. package/src/clients/openai.ts +41 -11
  28. package/src/clients/openrouter.ts +17 -0
  29. package/src/clients/pricing/anthropic.ts +105 -78
  30. package/src/clients/pricing/cerebras.ts +11 -0
  31. package/src/clients/pricing/copilot.ts +60 -0
  32. package/src/clients/pricing/deepseek.ts +15 -0
  33. package/src/clients/pricing/fireworks.ts +32 -0
  34. package/src/clients/pricing/github.ts +69 -0
  35. package/src/clients/pricing/google.ts +245 -206
  36. package/src/clients/pricing/groq.ts +56 -0
  37. package/src/clients/pricing/index.ts +43 -6
  38. package/src/clients/pricing/llama.ts +18 -0
  39. package/src/clients/pricing/mistral.ts +34 -0
  40. package/src/clients/pricing/models.ts +23 -0
  41. package/src/clients/pricing/nvidia.ts +102 -0
  42. package/src/clients/pricing/openai.ts +347 -171
  43. package/src/clients/pricing/openrouter.ts +36 -0
  44. package/src/clients/pricing/types.ts +110 -0
  45. package/src/clients/pricing/xai.ts +123 -66
  46. package/src/clients/types.ts +4 -0
  47. package/src/clients/xai.ts +152 -2
  48. package/src/fileSync.ts +8 -2
  49. package/src/login.ts +11 -3
  50. package/src/services/AgentSyncFs.ts +36 -12
  51. package/src/services/KnowhowClient.ts +11 -0
  52. package/src/services/LazyToolsService.ts +6 -0
  53. package/src/services/S3.ts +0 -7
  54. package/src/services/SyncedAgentWatcher.ts +13 -298
  55. package/src/services/index.ts +1 -0
  56. package/src/services/modules/index.ts +11 -2
  57. package/src/services/watchers/FsSyncer.ts +155 -0
  58. package/src/services/watchers/RemoteSyncer.ts +153 -0
  59. package/src/services/watchers/index.ts +2 -0
  60. package/src/types.ts +56 -279
  61. package/src/worker.ts +174 -0
  62. package/tests/clients/pricing.test.ts +37 -0
  63. package/tests/manual/clients/completions.json +838 -226
  64. package/tests/manual/clients/completions.test.ts +46 -31
  65. package/ts_build/package.json +3 -2
  66. package/ts_build/src/agents/base/base.d.ts +17 -1
  67. package/ts_build/src/agents/base/base.js +82 -1
  68. package/ts_build/src/agents/base/base.js.map +1 -1
  69. package/ts_build/src/agents/tools/execCommand.js +3 -0
  70. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  71. package/ts_build/src/agents/tools/executeScript/definition.js +1 -1
  72. package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -1
  73. package/ts_build/src/agents/tools/index.d.ts +0 -1
  74. package/ts_build/src/agents/tools/index.js +0 -1
  75. package/ts_build/src/agents/tools/index.js.map +1 -1
  76. package/ts_build/src/agents/tools/list.js +3 -38
  77. package/ts_build/src/agents/tools/list.js.map +1 -1
  78. package/ts_build/src/agents/tools/visionTool.d.ts +1 -1
  79. package/ts_build/src/agents/tools/writeFile.js +1 -1
  80. package/ts_build/src/agents/tools/writeFile.js.map +1 -1
  81. package/ts_build/src/ai.d.ts +1 -1
  82. package/ts_build/src/auth/browserLogin.d.ts +2 -1
  83. package/ts_build/src/auth/browserLogin.js +10 -3
  84. package/ts_build/src/auth/browserLogin.js.map +1 -1
  85. package/ts_build/src/chat/modules/RemoteSyncModule.js +1 -0
  86. package/ts_build/src/chat/modules/RemoteSyncModule.js.map +1 -1
  87. package/ts_build/src/cli.js +19 -0
  88. package/ts_build/src/cli.js.map +1 -1
  89. package/ts_build/src/clients/anthropic.d.ts +1 -82
  90. package/ts_build/src/clients/cerebras.d.ts +4 -0
  91. package/ts_build/src/clients/cerebras.js +14 -0
  92. package/ts_build/src/clients/cerebras.js.map +1 -0
  93. package/ts_build/src/clients/contextLimits.js +7 -2
  94. package/ts_build/src/clients/contextLimits.js.map +1 -1
  95. package/ts_build/src/clients/copilot.d.ts +4 -0
  96. package/ts_build/src/clients/copilot.js +15 -0
  97. package/ts_build/src/clients/copilot.js.map +1 -0
  98. package/ts_build/src/clients/deepseek.d.ts +4 -0
  99. package/ts_build/src/clients/deepseek.js +15 -0
  100. package/ts_build/src/clients/deepseek.js.map +1 -0
  101. package/ts_build/src/clients/fireworks.d.ts +4 -0
  102. package/ts_build/src/clients/fireworks.js +15 -0
  103. package/ts_build/src/clients/fireworks.js.map +1 -0
  104. package/ts_build/src/clients/gemini.d.ts +1 -0
  105. package/ts_build/src/clients/gemini.js +28 -1
  106. package/ts_build/src/clients/gemini.js.map +1 -1
  107. package/ts_build/src/clients/github.d.ts +4 -0
  108. package/ts_build/src/clients/github.js +15 -0
  109. package/ts_build/src/clients/github.js.map +1 -0
  110. package/ts_build/src/clients/groq.d.ts +4 -0
  111. package/ts_build/src/clients/groq.js +15 -0
  112. package/ts_build/src/clients/groq.js.map +1 -0
  113. package/ts_build/src/clients/http.d.ts +22 -1
  114. package/ts_build/src/clients/http.js +132 -7
  115. package/ts_build/src/clients/http.js.map +1 -1
  116. package/ts_build/src/clients/index.d.ts +22 -0
  117. package/ts_build/src/clients/index.js +150 -5
  118. package/ts_build/src/clients/index.js.map +1 -1
  119. package/ts_build/src/clients/llama.d.ts +4 -0
  120. package/ts_build/src/clients/llama.js +15 -0
  121. package/ts_build/src/clients/llama.js.map +1 -0
  122. package/ts_build/src/clients/mistral.d.ts +4 -0
  123. package/ts_build/src/clients/mistral.js +15 -0
  124. package/ts_build/src/clients/mistral.js.map +1 -0
  125. package/ts_build/src/clients/nvidia.d.ts +4 -0
  126. package/ts_build/src/clients/nvidia.js +15 -0
  127. package/ts_build/src/clients/nvidia.js.map +1 -0
  128. package/ts_build/src/clients/openai.d.ts +4 -206
  129. package/ts_build/src/clients/openai.js +27 -9
  130. package/ts_build/src/clients/openai.js.map +1 -1
  131. package/ts_build/src/clients/openrouter.d.ts +4 -0
  132. package/ts_build/src/clients/openrouter.js +15 -0
  133. package/ts_build/src/clients/openrouter.js.map +1 -0
  134. package/ts_build/src/clients/pricing/anthropic.d.ts +26 -78
  135. package/ts_build/src/clients/pricing/anthropic.js +75 -78
  136. package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
  137. package/ts_build/src/clients/pricing/cerebras.d.ts +4 -0
  138. package/ts_build/src/clients/pricing/cerebras.js +11 -0
  139. package/ts_build/src/clients/pricing/cerebras.js.map +1 -0
  140. package/ts_build/src/clients/pricing/copilot.d.ts +5 -0
  141. package/ts_build/src/clients/pricing/copilot.js +35 -0
  142. package/ts_build/src/clients/pricing/copilot.js.map +1 -0
  143. package/ts_build/src/clients/pricing/deepseek.d.ts +5 -0
  144. package/ts_build/src/clients/pricing/deepseek.js +10 -0
  145. package/ts_build/src/clients/pricing/deepseek.js.map +1 -0
  146. package/ts_build/src/clients/pricing/fireworks.d.ts +5 -0
  147. package/ts_build/src/clients/pricing/fireworks.js +21 -0
  148. package/ts_build/src/clients/pricing/fireworks.js.map +1 -0
  149. package/ts_build/src/clients/pricing/github.d.ts +4 -0
  150. package/ts_build/src/clients/pricing/github.js +58 -0
  151. package/ts_build/src/clients/pricing/github.js.map +1 -0
  152. package/ts_build/src/clients/pricing/google.d.ts +59 -6
  153. package/ts_build/src/clients/pricing/google.js +214 -167
  154. package/ts_build/src/clients/pricing/google.js.map +1 -1
  155. package/ts_build/src/clients/pricing/groq.d.ts +5 -0
  156. package/ts_build/src/clients/pricing/groq.js +41 -0
  157. package/ts_build/src/clients/pricing/groq.js.map +1 -0
  158. package/ts_build/src/clients/pricing/index.d.ts +17 -6
  159. package/ts_build/src/clients/pricing/index.js +65 -10
  160. package/ts_build/src/clients/pricing/index.js.map +1 -1
  161. package/ts_build/src/clients/pricing/llama.d.ts +4 -0
  162. package/ts_build/src/clients/pricing/llama.js +14 -0
  163. package/ts_build/src/clients/pricing/llama.js.map +1 -0
  164. package/ts_build/src/clients/pricing/mistral.d.ts +5 -0
  165. package/ts_build/src/clients/pricing/mistral.js +23 -0
  166. package/ts_build/src/clients/pricing/mistral.js.map +1 -0
  167. package/ts_build/src/clients/pricing/models.d.ts +9 -0
  168. package/ts_build/src/clients/pricing/models.js +19 -0
  169. package/ts_build/src/clients/pricing/models.js.map +1 -0
  170. package/ts_build/src/clients/pricing/nvidia.d.ts +8 -0
  171. package/ts_build/src/clients/pricing/nvidia.js +96 -0
  172. package/ts_build/src/clients/pricing/nvidia.js.map +1 -0
  173. package/ts_build/src/clients/pricing/openai.d.ts +86 -197
  174. package/ts_build/src/clients/pricing/openai.js +294 -168
  175. package/ts_build/src/clients/pricing/openai.js.map +1 -1
  176. package/ts_build/src/clients/pricing/openrouter.d.ts +4 -0
  177. package/ts_build/src/clients/pricing/openrouter.js +29 -0
  178. package/ts_build/src/clients/pricing/openrouter.js.map +1 -0
  179. package/ts_build/src/clients/pricing/types.d.ts +46 -0
  180. package/ts_build/src/clients/pricing/types.js +49 -0
  181. package/ts_build/src/clients/pricing/types.js.map +1 -0
  182. package/ts_build/src/clients/pricing/xai.d.ts +39 -64
  183. package/ts_build/src/clients/pricing/xai.js +93 -60
  184. package/ts_build/src/clients/pricing/xai.js.map +1 -1
  185. package/ts_build/src/clients/types.d.ts +1 -0
  186. package/ts_build/src/clients/xai.d.ts +2 -58
  187. package/ts_build/src/clients/xai.js +123 -2
  188. package/ts_build/src/clients/xai.js.map +1 -1
  189. package/ts_build/src/fileSync.js +7 -2
  190. package/ts_build/src/fileSync.js.map +1 -1
  191. package/ts_build/src/login.js +8 -2
  192. package/ts_build/src/login.js.map +1 -1
  193. package/ts_build/src/services/AgentSyncFs.js +1 -0
  194. package/ts_build/src/services/AgentSyncFs.js.map +1 -1
  195. package/ts_build/src/services/KnowhowClient.d.ts +1 -0
  196. package/ts_build/src/services/KnowhowClient.js +7 -0
  197. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  198. package/ts_build/src/services/LazyToolsService.d.ts +1 -0
  199. package/ts_build/src/services/LazyToolsService.js +3 -0
  200. package/ts_build/src/services/LazyToolsService.js.map +1 -1
  201. package/ts_build/src/services/S3.js +0 -7
  202. package/ts_build/src/services/S3.js.map +1 -1
  203. package/ts_build/src/services/SyncedAgentWatcher.d.ts +0 -51
  204. package/ts_build/src/services/SyncedAgentWatcher.js +1 -282
  205. package/ts_build/src/services/SyncedAgentWatcher.js.map +1 -1
  206. package/ts_build/src/services/index.d.ts +1 -0
  207. package/ts_build/src/services/index.js +1 -0
  208. package/ts_build/src/services/index.js.map +1 -1
  209. package/ts_build/src/services/modules/index.js +41 -1
  210. package/ts_build/src/services/modules/index.js.map +1 -1
  211. package/ts_build/src/services/watchers/FsSyncer.d.ts +27 -0
  212. package/ts_build/src/services/watchers/FsSyncer.js +135 -0
  213. package/ts_build/src/services/watchers/FsSyncer.js.map +1 -0
  214. package/ts_build/src/services/watchers/RemoteSyncer.d.ts +28 -0
  215. package/ts_build/src/services/watchers/RemoteSyncer.js +126 -0
  216. package/ts_build/src/services/watchers/RemoteSyncer.js.map +1 -0
  217. package/ts_build/src/services/watchers/index.d.ts +2 -0
  218. package/ts_build/src/services/watchers/index.js +19 -0
  219. package/ts_build/src/services/watchers/index.js.map +1 -0
  220. package/ts_build/src/types.d.ts +163 -124
  221. package/ts_build/src/types.js +33 -213
  222. package/ts_build/src/types.js.map +1 -1
  223. package/ts_build/src/worker.d.ts +4 -0
  224. package/ts_build/src/worker.js +140 -0
  225. package/ts_build/src/worker.js.map +1 -1
  226. package/ts_build/tests/clients/pricing.test.js +21 -0
  227. package/ts_build/tests/clients/pricing.test.js.map +1 -1
  228. package/ts_build/tests/manual/clients/completions.test.js +27 -24
  229. package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
  230. package/src/clients/pricing/catalog.ts +0 -287
  231. package/ts_build/src/clients/pricing/catalog.d.ts +0 -28
  232. package/ts_build/src/clients/pricing/catalog.js +0 -179
  233. package/ts_build/src/clients/pricing/catalog.js.map +0 -1
@@ -121,7 +121,11 @@ export class AgentSyncFs {
121
121
  /**
122
122
  * Update metadata file with current agent state
123
123
  */
124
- private async updateMetadata(agent: BaseAgent, inProgress: boolean, result?: string): Promise<void> {
124
+ private async updateMetadata(
125
+ agent: BaseAgent,
126
+ inProgress: boolean,
127
+ result?: string
128
+ ): Promise<void> {
125
129
  if (!this.taskPath) return;
126
130
 
127
131
  try {
@@ -137,7 +141,8 @@ export class AgentSyncFs {
137
141
 
138
142
  metadata.threads = agent.getThreads();
139
143
  metadata.totalCostUsd = agent.getTotalCostUsd();
140
- metadata.agentName = agent.name;
144
+ metadata.tokenUsage = agent.getTokenUsage();
145
+ metadata.agentName = agent.name;
141
146
  metadata.inProgress = inProgress;
142
147
  metadata.lastUpdate = new Date().toISOString();
143
148
 
@@ -147,7 +152,11 @@ export class AgentSyncFs {
147
152
  await this.writeStatus("completed");
148
153
  }
149
154
 
150
- await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
155
+ await fs.writeFile(
156
+ metadataPath,
157
+ JSON.stringify(metadata, null, 2),
158
+ "utf8"
159
+ );
151
160
  } catch (error) {
152
161
  console.error(`❌ Failed to update metadata:`, error);
153
162
  }
@@ -206,7 +215,9 @@ export class AgentSyncFs {
206
215
  // Check for new input/messages
207
216
  const input = await this.readInput();
208
217
  if (input && input !== this.lastInputContent && input.trim() !== "") {
209
- console.log(`📬 New message received via filesystem for task ${this.taskId}`);
218
+ console.log(
219
+ `📬 New message received via filesystem for task ${this.taskId}`
220
+ );
210
221
  this.lastInputContent = input;
211
222
 
212
223
  agent.addPendingUserMessage({
@@ -234,7 +245,7 @@ export class AgentSyncFs {
234
245
  await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
235
246
 
236
247
  const status = await this.readStatus();
237
-
248
+
238
249
  if (status === "killed") {
239
250
  console.log(`🛑 Agent task ${this.taskId} killed while paused`);
240
251
  await agent.kill();
@@ -314,7 +325,10 @@ export class AgentSyncFs {
314
325
  console.error(`❌ Error during threadUpdate sync:`, error);
315
326
  }
316
327
  };
317
- agent.agentEvents.on(agent.eventTypes.threadUpdate, this.threadUpdateHandler);
328
+ agent.agentEvents.on(
329
+ agent.eventTypes.threadUpdate,
330
+ this.threadUpdateHandler
331
+ );
318
332
 
319
333
  // Listen to completion event to finalize task (store reference for cleanup)
320
334
  this.doneHandler = (result: string) => {
@@ -323,7 +337,9 @@ export class AgentSyncFs {
323
337
  return;
324
338
  }
325
339
 
326
- console.log(`🎯 [AgentSyncFs] Done event received for task: ${this.taskId}`);
340
+ console.log(
341
+ `🎯 [AgentSyncFs] Done event received for task: ${this.taskId}`
342
+ );
327
343
 
328
344
  // Store finalization promise so callers can await it (same pattern as AgentSyncKnowhowWeb)
329
345
  this.finalizationPromise = (async () => {
@@ -332,7 +348,9 @@ export class AgentSyncFs {
332
348
  // This prevents the race where a pending "inProgress: true" metadata write
333
349
  // overwrites the finalization write.
334
350
  if (this.pendingThreadUpdatePromise) {
335
- console.log(`⏳ [AgentSyncFs] Awaiting pending thread update before finalizing...`);
351
+ console.log(
352
+ `⏳ [AgentSyncFs] Awaiting pending thread update before finalizing...`
353
+ );
336
354
  await this.pendingThreadUpdatePromise.catch(() => {
337
355
  // Ignore errors in pending update — we still want to finalize
338
356
  });
@@ -379,7 +397,7 @@ export class AgentSyncFs {
379
397
  private static async cleanupOldTasks(): Promise<void> {
380
398
  try {
381
399
  const agentsPath = AgentSyncFs.sharedBasePath;
382
-
400
+
383
401
  // Check if directory exists
384
402
  try {
385
403
  await fs.access(agentsPath);
@@ -396,7 +414,7 @@ export class AgentSyncFs {
396
414
  if (!entry.isDirectory()) continue;
397
415
 
398
416
  const taskPath = path.join(agentsPath, entry.name);
399
-
417
+
400
418
  try {
401
419
  const stats = await fs.stat(taskPath);
402
420
  const age = now - stats.mtimeMs;
@@ -449,11 +467,17 @@ export class AgentSyncFs {
449
467
  // Remove old event listeners from the agent before resetting
450
468
  if (this.agent) {
451
469
  if (this.threadUpdateHandler) {
452
- this.agent.agentEvents.removeListener(this.agent.eventTypes.threadUpdate, this.threadUpdateHandler);
470
+ this.agent.agentEvents.removeListener(
471
+ this.agent.eventTypes.threadUpdate,
472
+ this.threadUpdateHandler
473
+ );
453
474
  this.threadUpdateHandler = undefined;
454
475
  }
455
476
  if (this.doneHandler) {
456
- this.agent.agentEvents.removeListener(this.agent.eventTypes.done, this.doneHandler);
477
+ this.agent.agentEvents.removeListener(
478
+ this.agent.eventTypes.done,
479
+ this.doneHandler
480
+ );
457
481
  this.doneHandler = undefined;
458
482
  }
459
483
  this.agent = undefined;
@@ -144,6 +144,17 @@ export class KnowhowSimpleClient {
144
144
  this.setJwt(jwt);
145
145
  }
146
146
 
147
+ /**
148
+ * Reload the JWT from disk (useful after login refreshes the token).
149
+ */
150
+ refreshJwt() {
151
+ const freshJwt = loadKnowhowJwt();
152
+ if (freshJwt) {
153
+ this.setJwt(freshJwt);
154
+ this.jwtValidated = false;
155
+ }
156
+ }
157
+
147
158
  setJwt(jwt: string) {
148
159
  this.jwt = jwt;
149
160
  this.headers = {
@@ -57,6 +57,12 @@ export class LazyToolsService extends ToolsService {
57
57
  this.updateVisibleTools();
58
58
  }
59
59
 
60
+ // Override addTool (singular) to also route through allTools so that
61
+ // module-registered tools (via ModulesService.addTool) are tracked correctly.
62
+ addTool(tool: Tool) {
63
+ this.addTools([tool]);
64
+ }
65
+
60
66
  // Override getTools to return only enabled tools
61
67
  getTools() {
62
68
  return this.tools; // Returns filtered subset
@@ -1,5 +1,4 @@
1
1
  import * as fs from "fs";
2
- import * as crypto from "crypto";
3
2
  import { createWriteStream, createReadStream } from "fs";
4
3
  import { pipeline, Readable } from "stream";
5
4
  import * as util from "util";
@@ -14,17 +13,11 @@ export class S3Service {
14
13
  try {
15
14
  const fileContent = fs.readFileSync(filePath);
16
15
  const fileStats = await fs.promises.stat(filePath);
17
- const sha256Base64 = crypto
18
- .createHash("sha256")
19
- .update(fileContent)
20
- .digest("base64");
21
16
 
22
17
  const response = await fetch(presignedUrl, {
23
18
  method: "PUT",
24
19
  headers: {
25
20
  "Content-Length": String(fileStats.size),
26
- "x-amz-checksum-sha256": sha256Base64,
27
- "x-amz-sdk-checksum-algorithm": "SHA256",
28
21
  },
29
22
  body: fileContent,
30
23
  // @ts-ignore
@@ -5,11 +5,6 @@
5
5
 
6
6
  import { Message } from "../clients/types";
7
7
  import { EventService } from "./EventService";
8
- import * as fs from "fs";
9
- import * as fsPromises from "fs/promises";
10
- import * as path from "path";
11
- import { messagesToRenderEvents } from "../chat/renderer/messagesToRenderEvents";
12
- import { KnowhowSimpleClient } from "./KnowhowClient";
13
8
 
14
9
  export interface SyncedAgentWatcher {
15
10
  /** Start watching for changes, emitting agent events */
@@ -27,7 +22,13 @@ export interface SyncedAgentWatcher {
27
22
  /** EventService that emits agent lifecycle events (toolCall, toolUsed, agentSay, threadUpdate, done) */
28
23
  agentEvents: EventService;
29
24
  /** Event type constants mirroring BaseAgent.eventTypes */
30
- eventTypes: { done: string; toolCall: string; toolUsed: string; agentSay: string; threadUpdate: string };
25
+ eventTypes: {
26
+ done: string;
27
+ toolCall: string;
28
+ toolUsed: string;
29
+ agentSay: string;
30
+ threadUpdate: string;
31
+ };
31
32
  /** Pause the remote agent */
32
33
  pause(): Promise<void>;
33
34
  /** Unpause/resume the remote agent */
@@ -44,7 +45,12 @@ export interface SyncedAgentWatcher {
44
45
  export interface AttachableAgent {
45
46
  name: string;
46
47
  agentEvents: EventService;
47
- eventTypes: { done: string; toolCall?: string; toolUsed?: string; agentSay?: string };
48
+ eventTypes: {
49
+ done: string;
50
+ toolCall?: string;
51
+ toolUsed?: string;
52
+ agentSay?: string;
53
+ };
48
54
  getTotalCostUsd(): number;
49
55
  pause(): void | Promise<void>;
50
56
  unpause(): void | Promise<void>;
@@ -104,294 +110,3 @@ export class WatcherBackedAgent implements AttachableAgent {
104
110
  });
105
111
  }
106
112
  }
107
-
108
- /**
109
- * Watches an agent running in another process via the filesystem.
110
- * Reads .knowhow/processes/agents/<taskId>/metadata.json for changes.
111
- * Sends messages by writing to .knowhow/processes/agents/<taskId>/input.txt
112
- */
113
- export class FsSyncedAgentWatcher implements SyncedAgentWatcher {
114
- public taskId: string = "";
115
- private taskPath: string = "";
116
- private watcher: fs.FSWatcher | null = null;
117
- private lastThreadLength: number = 0;
118
- public agentName: string = "unknown";
119
- private debounceTimer: NodeJS.Timeout | null = null;
120
- public agentEvents = new EventService();
121
- public eventTypes = {
122
- done: "done",
123
- toolCall: "tool:pre_call",
124
- toolUsed: "tool:post_call",
125
- agentSay: "agent:say",
126
- threadUpdate: "thread_update",
127
- };
128
-
129
- async startWatching(taskId: string): Promise<void> {
130
- this.taskId = taskId;
131
- this.taskPath = path.join(".knowhow/processes/agents", taskId);
132
-
133
- // Load initial state to track current thread length (for delta rendering)
134
- const metadata = await this.readMetadata();
135
- if (metadata) {
136
- const threads: any[][] = metadata.threads || [];
137
- const lastThread = threads[threads.length - 1] || [];
138
- this.agentName = metadata.agentName || taskId;
139
- this.lastThreadLength = lastThread.length;
140
- }
141
-
142
- // Watch the directory for metadata.json changes
143
- try {
144
- this.watcher = fs.watch(this.taskPath, (event, filename) => {
145
- if (filename === "metadata.json" || filename === null) {
146
- // Debounce rapid file writes
147
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
148
- this.debounceTimer = setTimeout(() => {
149
- this.onMetadataChanged().catch(() => {});
150
- }, 200);
151
- }
152
- });
153
- } catch (err: any) {
154
- console.warn(`⚠️ Could not watch ${this.taskPath}: ${err.message}`);
155
- }
156
-
157
- console.log(`👁️ Watching fs-synced agent: ${taskId} (${this.agentName})`);
158
- console.log(
159
- ` Type /logs 20 to see recent messages, or type to send a message`
160
- );
161
- }
162
-
163
- private async onMetadataChanged(): Promise<void> {
164
- const metadata = await this.readMetadata();
165
- if (!metadata?.threads) return;
166
-
167
- const threads: any[][] = metadata.threads;
168
- const lastThread = threads[threads.length - 1] || [];
169
-
170
- // Only render NEW messages since last check
171
- const newMessages = lastThread.slice(this.lastThreadLength);
172
- if (newMessages.length > 0) {
173
- const renderEvents = messagesToRenderEvents(
174
- newMessages,
175
- this.taskId,
176
- this.agentName
177
- );
178
- for (const event of renderEvents) {
179
- if (event.type === "toolCall") {
180
- this.agentEvents.emit(this.eventTypes.toolCall, {
181
- toolCall: (event as any).toolCall,
182
- });
183
- } else if (event.type === "toolResult") {
184
- this.agentEvents.emit(this.eventTypes.toolUsed, {
185
- toolCall: (event as any).toolCall,
186
- functionResp: (event as any).result,
187
- });
188
- } else if (event.type === "agentMessage") {
189
- this.agentEvents.emit(this.eventTypes.agentSay, {
190
- message: (event as any).message,
191
- });
192
- }
193
- }
194
- this.agentEvents.emit(this.eventTypes.threadUpdate, lastThread);
195
- this.lastThreadLength = lastThread.length;
196
- }
197
-
198
- // Emit done if the agent has completed and has a result
199
- const status = metadata.status;
200
- const result = metadata.result;
201
- if ((status === "completed" || status === "killed") && result != null) {
202
- this.stopWatching();
203
- this.agentEvents.emit(this.eventTypes.done, result);
204
- }
205
- }
206
-
207
- async sendMessage(message: string): Promise<void> {
208
- const inputPath = path.join(this.taskPath, "input.txt");
209
- await fsPromises.writeFile(inputPath, message, "utf8");
210
- }
211
-
212
- async getThreads(): Promise<any[][]> {
213
- const metadata = await this.readMetadata();
214
- return metadata?.threads || [];
215
- }
216
-
217
- stopWatching(): void {
218
- if (this.debounceTimer) {
219
- clearTimeout(this.debounceTimer);
220
- this.debounceTimer = null;
221
- }
222
- this.watcher?.close();
223
- this.watcher = null;
224
- console.log(`🔌 Stopped watching agent: ${this.taskId}`);
225
- }
226
-
227
- async pause(): Promise<void> {
228
- const statusPath = path.join(this.taskPath, "status.txt");
229
- await fsPromises.writeFile(statusPath, "paused", "utf8");
230
- console.log(`⏸️ Paused remote agent: ${this.taskId}`);
231
- }
232
-
233
- async unpause(): Promise<void> {
234
- const statusPath = path.join(this.taskPath, "status.txt");
235
- await fsPromises.writeFile(statusPath, "running", "utf8");
236
- console.log(`▶️ Unpaused remote agent: ${this.taskId}`);
237
- }
238
-
239
- async kill(): Promise<void> {
240
- const statusPath = path.join(this.taskPath, "status.txt");
241
- await fsPromises.writeFile(statusPath, "killed", "utf8");
242
- console.log(`🛑 Killed remote agent: ${this.taskId}`);
243
- }
244
-
245
- private async readMetadata(): Promise<any> {
246
- try {
247
- const metaPath = path.join(this.taskPath, "metadata.json");
248
- const content = await fsPromises.readFile(metaPath, "utf8");
249
- return JSON.parse(content);
250
- } catch {
251
- return null;
252
- }
253
- }
254
- }
255
-
256
- /**
257
- * Watches an agent running on Knowhow Web via polling the API.
258
- * Polls GET /tasks/<taskId> every 3 seconds for thread updates.
259
- * Sends messages via the client's sendMessageToAgent method.
260
- */
261
- export class WebSyncedAgentWatcher implements SyncedAgentWatcher {
262
- public taskId: string = "";
263
- private client: KnowhowSimpleClient;
264
- private pollInterval: NodeJS.Timeout | null = null;
265
- private lastThreadLength: number = 0;
266
- public agentName: string = "remote-agent";
267
- private stopped: boolean = false;
268
- public agentEvents = new EventService();
269
- public eventTypes = {
270
- done: "done",
271
- toolCall: "tool:pre_call",
272
- toolUsed: "tool:post_call",
273
- agentSay: "agent:say",
274
- threadUpdate: "thread_update",
275
- };
276
-
277
- constructor(client?: KnowhowSimpleClient) {
278
- this.client = client || new KnowhowSimpleClient();
279
- }
280
-
281
- async startWatching(taskId: string): Promise<void> {
282
- this.taskId = taskId;
283
- this.stopped = false;
284
-
285
- // Load initial state to track current thread length
286
- try {
287
- const details = await this.client.getTaskDetails(taskId);
288
- const threads: any[][] = details?.data?.threads || [];
289
- const lastThread = threads[threads.length - 1] || [];
290
- this.agentName = "remote-agent";
291
- this.lastThreadLength = lastThread.length;
292
- } catch (err: any) {
293
- console.warn(
294
- `⚠️ Could not load initial state for task ${taskId}: ${err.message}`
295
- );
296
- }
297
-
298
- // Poll every 3 seconds for updates
299
- this.pollInterval = setInterval(async () => {
300
- if (!this.stopped) {
301
- await this.onPoll().catch(() => {});
302
- }
303
- }, 3000);
304
-
305
- console.log(`🌐 Watching web-synced agent: ${taskId} (${this.agentName})`);
306
- console.log(
307
- ` Type /logs 20 to see recent messages, or type to send a message`
308
- );
309
- }
310
-
311
- private async onPoll(): Promise<void> {
312
- if (this.stopped) return;
313
- try {
314
- const details = await this.client.getTaskDetails(this.taskId);
315
- const threads: any[][] = details?.data?.threads || [];
316
- const lastThread = threads[threads.length - 1] || [];
317
-
318
- const newMessages = lastThread.slice(this.lastThreadLength);
319
- if (newMessages.length > 0) {
320
- const renderEvents = messagesToRenderEvents(
321
- newMessages,
322
- this.taskId,
323
- this.agentName
324
- );
325
- for (const event of renderEvents) {
326
- if (event.type === "toolCall") {
327
- this.agentEvents.emit(this.eventTypes.toolCall, {
328
- toolCall: (event as any).toolCall,
329
- });
330
- } else if (event.type === "toolResult") {
331
- this.agentEvents.emit(this.eventTypes.toolUsed, {
332
- toolCall: (event as any).toolCall,
333
- functionResp: (event as any).result,
334
- });
335
- } else if (event.type === "agentMessage") {
336
- this.agentEvents.emit(this.eventTypes.agentSay, {
337
- message: (event as any).message,
338
- });
339
- }
340
- }
341
- this.agentEvents.emit(this.eventTypes.threadUpdate, lastThread);
342
- this.lastThreadLength = lastThread.length;
343
- }
344
-
345
- // Stop polling and emit done if task is complete with a result
346
- const status = details?.data?.status;
347
- const result = details?.data?.result;
348
- if (status === "completed" || status === "killed") {
349
- this.stopWatching();
350
- if (result != null) {
351
- this.agentEvents.emit(this.eventTypes.done, result);
352
- } else {
353
- console.log(`\n✅ Remote agent ${this.taskId} status: ${status} (no result)`);
354
- }
355
- }
356
- } catch {
357
- // Silently continue on poll errors
358
- }
359
- }
360
-
361
- async sendMessage(message: string): Promise<void> {
362
- await this.client.sendMessageToAgent(this.taskId, message);
363
- }
364
-
365
- async getThreads(): Promise<any[][]> {
366
- try {
367
- const details = await this.client.getTaskDetails(this.taskId);
368
- return details?.data?.threads || [];
369
- } catch {
370
- return [];
371
- }
372
- }
373
-
374
- stopWatching(): void {
375
- this.stopped = true;
376
- if (this.pollInterval) {
377
- clearInterval(this.pollInterval);
378
- this.pollInterval = null;
379
- }
380
- console.log(`🔌 Stopped watching web agent: ${this.taskId}`);
381
- }
382
-
383
- async pause(): Promise<void> {
384
- await this.client.pauseAgent(this.taskId);
385
- console.log(`⏸️ Paused remote web agent: ${this.taskId}`);
386
- }
387
-
388
- async unpause(): Promise<void> {
389
- await this.client.resumeAgent(this.taskId);
390
- console.log(`▶️ Unpaused remote web agent: ${this.taskId}`);
391
- }
392
-
393
- async kill(): Promise<void> {
394
- await this.client.killAgent(this.taskId);
395
- console.log(`🛑 Killed remote web agent: ${this.taskId}`);
396
- }
397
- }
@@ -32,6 +32,7 @@ export * from "./SessionManager";
32
32
  export * from "./TaskRegistry";
33
33
  export * from "./SyncedAgentWatcher";
34
34
  export * from "./SyncerService";
35
+ export * from "./watchers";
35
36
  export { Clients } from "../clients";
36
37
 
37
38
  let Singletons = {} as {
@@ -2,7 +2,7 @@ import { getConfig, getGlobalConfig } from "../../config";
2
2
  import { KnowhowModule, ModuleContext } from "./types";
3
3
  import { ToolsService } from "../Tools";
4
4
  import { services } from "../";
5
- import { EventService } from "../EventService";
5
+ import * as path from "path";
6
6
 
7
7
  export class ModulesService {
8
8
  async loadModulesFromConfig(context?: ModuleContext) {
@@ -35,8 +35,17 @@ export class ModulesService {
35
35
  ];
36
36
 
37
37
  for (const modulePath of allModulePaths) {
38
- const importedModule = require(modulePath) as KnowhowModule;
38
+ // Resolve relative paths relative to process.cwd() so that paths like
39
+ // "../../packages/knowhow-module-load-webpage" in knowhow.json work
40
+ // regardless of where the compiled output lives.
41
+ const resolvedPath = modulePath.startsWith(".")
42
+ ? path.resolve(process.cwd(), modulePath)
43
+ : modulePath;
44
+ const rawModule = require(resolvedPath);
45
+ const importedModule = (rawModule.default || rawModule) as KnowhowModule;
46
+ console.log(`🔌 Loading module: ${modulePath} (resolved: ${resolvedPath})`);
39
47
  await importedModule.init({ config, cwd: process.cwd(), context });
48
+ console.log(`✅ Module initialized: ${modulePath} (tools: ${importedModule.tools.length}, agents: ${importedModule.agents.length}, plugins: ${importedModule.plugins.length}, clients: ${importedModule.clients.length})`);
40
49
 
41
50
  for (const agent of importedModule.agents) {
42
51
  agentService.registerAgent(agent);