@tyvm/knowhow 0.0.145 → 0.0.147

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 (320) hide show
  1. package/autodoc/cli-reference.md +36 -7
  2. package/bin/knowhow.js +8 -6
  3. package/package.json +1 -1
  4. package/src/agents/base/base.ts +24 -0
  5. package/src/agents/historyRollback.ts +36 -0
  6. package/src/agents/tools/startAgentTask.ts +41 -9
  7. package/src/auth/browserLogin.ts +9 -3
  8. package/src/auth/keyAuth.ts +115 -0
  9. package/src/auth/keyManager.ts +166 -0
  10. package/src/chat/CliChatService.ts +16 -0
  11. package/src/chat/modules/AgentModule.ts +144 -56
  12. package/src/chat/modules/AskModule.ts +2 -0
  13. package/src/chat/modules/BaseChatModule.ts +0 -1
  14. package/src/chat/modules/CustomCommandsModule.ts +2 -0
  15. package/src/chat/modules/InternalChatModule.ts +2 -0
  16. package/src/chat/modules/RemoteSyncModule.ts +4 -0
  17. package/src/chat/modules/RendererModule.ts +2 -0
  18. package/src/chat/modules/SearchModule.ts +2 -0
  19. package/src/chat/modules/SessionsModule.ts +110 -39
  20. package/src/chat/modules/SetupModule.ts +2 -0
  21. package/src/chat/modules/ShellCommandModule.ts +2 -0
  22. package/src/chat/modules/SystemModule.ts +2 -0
  23. package/src/chat/types.ts +5 -2
  24. package/src/cli.ts +1 -1
  25. package/src/clients/anthropic.ts +24 -0
  26. package/src/clients/fireworks.ts +3 -0
  27. package/src/clients/gemini.ts +8 -0
  28. package/src/clients/index.ts +2 -0
  29. package/src/clients/openai.ts +7 -4
  30. package/src/clients/types.ts +30 -7
  31. package/src/clients/xai.ts +23 -4
  32. package/src/commands/agent.ts +11 -39
  33. package/src/commands/agents.ts +403 -53
  34. package/src/commands/misc.ts +3 -2
  35. package/src/commands/services.ts +49 -3
  36. package/src/login.ts +89 -28
  37. package/src/plugins/plugins.ts +16 -0
  38. package/src/processors/TokenCompressor.ts +40 -8
  39. package/src/processors/ToolResponseCache.ts +17 -8
  40. package/src/processors/tools/boundedToolResponse.ts +49 -0
  41. package/src/processors/tools/grepToolResponse.ts +164 -38
  42. package/src/processors/tools/index.ts +2 -0
  43. package/src/processors/tools/jqToolResponse.ts +40 -10
  44. package/src/processors/tools/listStoredToolResponses.ts +51 -10
  45. package/src/processors/tools/tailToolResponse.ts +85 -17
  46. package/src/services/AgentService.ts +2 -0
  47. package/src/services/AgentSyncFs.ts +65 -0
  48. package/src/services/AgentSyncKnowhowWeb.ts +22 -4
  49. package/src/services/BehaviorsService.ts +2 -0
  50. package/src/services/DockerService.ts +2 -0
  51. package/src/services/EmbeddingService.ts +2 -0
  52. package/src/services/EmbeddingsService.ts +3 -0
  53. package/src/services/KnowhowClient.ts +68 -7
  54. package/src/services/LazyToolsService.ts +16 -0
  55. package/src/services/Mcp.ts +11 -2
  56. package/src/services/McpServer.ts +2 -0
  57. package/src/services/MessageProcessor.ts +2 -0
  58. package/src/services/MinimalToolsService.ts +2 -0
  59. package/src/services/RuntimeReloadService.ts +31 -0
  60. package/src/services/SessionManager.ts +5 -0
  61. package/src/services/SkillsService.ts +2 -0
  62. package/src/services/SyncedAgentWatcher.ts +2 -0
  63. package/src/services/SyncerService.ts +23 -8
  64. package/src/services/TaskRegistry.ts +2 -0
  65. package/src/services/Tools.ts +13 -0
  66. package/src/services/TracingService.ts +116 -0
  67. package/src/services/index.ts +10 -0
  68. package/src/services/modules/index.ts +49 -3
  69. package/src/services/modules/types.ts +8 -0
  70. package/src/types.ts +2 -0
  71. package/src/util/Trace.ts +236 -0
  72. package/src/utils/http.ts +18 -2
  73. package/tests/clients/fireworks.test.ts +65 -0
  74. package/tests/clients/reasoningEffort.test.ts +79 -0
  75. package/tests/compressor/toolResponseCache.test.ts +2 -1
  76. package/tests/manual/clients/completions.test.ts +27 -10
  77. package/tests/processors/TokenCompressor.test.ts +17 -3
  78. package/tests/processors/tools/boundedRetrievalTools.test.ts +68 -0
  79. package/tests/processors/tools/grepToolResponse.test.ts +88 -1
  80. package/tests/unit/agents/historyRollback.test.ts +53 -0
  81. package/tests/unit/auth/keyAuth.test.ts +102 -0
  82. package/tests/unit/auth/keyManager.test.ts +67 -0
  83. package/tests/unit/chat/historyCommandArgs.test.ts +37 -0
  84. package/tests/unit/commands/agents.test.ts +68 -0
  85. package/tests/unit/login.test.ts +119 -0
  86. package/tests/unit/plugins/pluginLoading.test.ts +31 -0
  87. package/tests/unit/services/AgentSyncKnowhowWeb.test.ts +38 -0
  88. package/tests/unit/services/LazyToolsService.test.ts +29 -0
  89. package/tests/unit/services/RuntimeReloadService.test.ts +29 -0
  90. package/tests/unit/services/SessionManager.test.ts +43 -0
  91. package/tests/unit/services/SyncerService.test.ts +45 -0
  92. package/tests/unit/util/Trace.test.ts +119 -0
  93. package/tests/unit/utils/http.test.ts +43 -0
  94. package/ts_build/package.json +1 -1
  95. package/ts_build/src/agents/base/base.d.ts +1 -0
  96. package/ts_build/src/agents/base/base.js +31 -2
  97. package/ts_build/src/agents/base/base.js.map +1 -1
  98. package/ts_build/src/agents/historyRollback.d.ts +2 -0
  99. package/ts_build/src/agents/historyRollback.js +22 -0
  100. package/ts_build/src/agents/historyRollback.js.map +1 -0
  101. package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -0
  102. package/ts_build/src/agents/tools/startAgentTask.js +36 -9
  103. package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
  104. package/ts_build/src/auth/browserLogin.d.ts +1 -0
  105. package/ts_build/src/auth/browserLogin.js +5 -2
  106. package/ts_build/src/auth/browserLogin.js.map +1 -1
  107. package/ts_build/src/auth/keyAuth.d.ts +5 -0
  108. package/ts_build/src/auth/keyAuth.js +79 -0
  109. package/ts_build/src/auth/keyAuth.js.map +1 -0
  110. package/ts_build/src/auth/keyManager.d.ts +14 -0
  111. package/ts_build/src/auth/keyManager.js +145 -0
  112. package/ts_build/src/auth/keyManager.js.map +1 -0
  113. package/ts_build/src/chat/CliChatService.js +15 -0
  114. package/ts_build/src/chat/CliChatService.js.map +1 -1
  115. package/ts_build/src/chat/modules/AgentModule.d.ts +13 -1
  116. package/ts_build/src/chat/modules/AgentModule.js +96 -43
  117. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  118. package/ts_build/src/chat/modules/AskModule.js +12 -2
  119. package/ts_build/src/chat/modules/AskModule.js.map +1 -1
  120. package/ts_build/src/chat/modules/BaseChatModule.js.map +1 -1
  121. package/ts_build/src/chat/modules/CustomCommandsModule.js +12 -2
  122. package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -1
  123. package/ts_build/src/chat/modules/InternalChatModule.js +16 -2
  124. package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
  125. package/ts_build/src/chat/modules/RemoteSyncModule.js +19 -2
  126. package/ts_build/src/chat/modules/RemoteSyncModule.js.map +1 -1
  127. package/ts_build/src/chat/modules/RendererModule.js +17 -2
  128. package/ts_build/src/chat/modules/RendererModule.js.map +1 -1
  129. package/ts_build/src/chat/modules/SearchModule.js +12 -2
  130. package/ts_build/src/chat/modules/SearchModule.js.map +1 -1
  131. package/ts_build/src/chat/modules/SessionsModule.d.ts +6 -0
  132. package/ts_build/src/chat/modules/SessionsModule.js +114 -24
  133. package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
  134. package/ts_build/src/chat/modules/SetupModule.js +17 -2
  135. package/ts_build/src/chat/modules/SetupModule.js.map +1 -1
  136. package/ts_build/src/chat/modules/ShellCommandModule.js +12 -2
  137. package/ts_build/src/chat/modules/ShellCommandModule.js.map +1 -1
  138. package/ts_build/src/chat/modules/SystemModule.js +12 -2
  139. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  140. package/ts_build/src/chat/types.d.ts +5 -2
  141. package/ts_build/src/cli.js +1 -1
  142. package/ts_build/src/cli.js.map +1 -1
  143. package/ts_build/src/clients/anthropic.d.ts +1 -0
  144. package/ts_build/src/clients/anthropic.js +12 -0
  145. package/ts_build/src/clients/anthropic.js.map +1 -1
  146. package/ts_build/src/clients/fireworks.js +1 -0
  147. package/ts_build/src/clients/fireworks.js.map +1 -1
  148. package/ts_build/src/clients/gemini.js +8 -0
  149. package/ts_build/src/clients/gemini.js.map +1 -1
  150. package/ts_build/src/clients/index.js +19 -4
  151. package/ts_build/src/clients/index.js.map +1 -1
  152. package/ts_build/src/clients/openai.js +4 -2
  153. package/ts_build/src/clients/openai.js.map +1 -1
  154. package/ts_build/src/clients/types.d.ts +4 -1
  155. package/ts_build/src/clients/types.js +14 -0
  156. package/ts_build/src/clients/types.js.map +1 -1
  157. package/ts_build/src/clients/xai.d.ts +1 -0
  158. package/ts_build/src/clients/xai.js +23 -4
  159. package/ts_build/src/clients/xai.js.map +1 -1
  160. package/ts_build/src/commands/agent.js +7 -22
  161. package/ts_build/src/commands/agent.js.map +1 -1
  162. package/ts_build/src/commands/agents.d.ts +1 -1
  163. package/ts_build/src/commands/agents.js +174 -16
  164. package/ts_build/src/commands/agents.js.map +1 -1
  165. package/ts_build/src/commands/misc.js +3 -2
  166. package/ts_build/src/commands/misc.js.map +1 -1
  167. package/ts_build/src/commands/services.d.ts +2 -0
  168. package/ts_build/src/commands/services.js +45 -3
  169. package/ts_build/src/commands/services.js.map +1 -1
  170. package/ts_build/src/login.d.ts +1 -1
  171. package/ts_build/src/login.js +53 -15
  172. package/ts_build/src/login.js.map +1 -1
  173. package/ts_build/src/plugins/plugins.d.ts +1 -0
  174. package/ts_build/src/plugins/plugins.js +14 -0
  175. package/ts_build/src/plugins/plugins.js.map +1 -1
  176. package/ts_build/src/processors/TokenCompressor.js +15 -6
  177. package/ts_build/src/processors/TokenCompressor.js.map +1 -1
  178. package/ts_build/src/processors/ToolResponseCache.d.ts +3 -3
  179. package/ts_build/src/processors/ToolResponseCache.js +8 -8
  180. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  181. package/ts_build/src/processors/tools/boundedToolResponse.d.ts +7 -0
  182. package/ts_build/src/processors/tools/boundedToolResponse.js +27 -0
  183. package/ts_build/src/processors/tools/boundedToolResponse.js.map +1 -0
  184. package/ts_build/src/processors/tools/grepToolResponse.d.ts +4 -0
  185. package/ts_build/src/processors/tools/grepToolResponse.js +101 -25
  186. package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -1
  187. package/ts_build/src/processors/tools/index.d.ts +2 -2
  188. package/ts_build/src/processors/tools/index.js.map +1 -1
  189. package/ts_build/src/processors/tools/jqToolResponse.d.ts +5 -1
  190. package/ts_build/src/processors/tools/jqToolResponse.js +26 -9
  191. package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -1
  192. package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +7 -1
  193. package/ts_build/src/processors/tools/listStoredToolResponses.js +39 -9
  194. package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -1
  195. package/ts_build/src/processors/tools/tailToolResponse.d.ts +5 -0
  196. package/ts_build/src/processors/tools/tailToolResponse.js +56 -13
  197. package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -1
  198. package/ts_build/src/services/AgentService.js +18 -2
  199. package/ts_build/src/services/AgentService.js.map +1 -1
  200. package/ts_build/src/services/AgentSyncFs.d.ts +3 -0
  201. package/ts_build/src/services/AgentSyncFs.js +62 -15
  202. package/ts_build/src/services/AgentSyncFs.js.map +1 -1
  203. package/ts_build/src/services/AgentSyncKnowhowWeb.d.ts +2 -0
  204. package/ts_build/src/services/AgentSyncKnowhowWeb.js +28 -6
  205. package/ts_build/src/services/AgentSyncKnowhowWeb.js.map +1 -1
  206. package/ts_build/src/services/BehaviorsService.js +12 -2
  207. package/ts_build/src/services/BehaviorsService.js.map +1 -1
  208. package/ts_build/src/services/DockerService.js +20 -8
  209. package/ts_build/src/services/DockerService.js.map +1 -1
  210. package/ts_build/src/services/EmbeddingService.js +16 -2
  211. package/ts_build/src/services/EmbeddingService.js.map +1 -1
  212. package/ts_build/src/services/EmbeddingsService.js +12 -2
  213. package/ts_build/src/services/EmbeddingsService.js.map +1 -1
  214. package/ts_build/src/services/KnowhowClient.d.ts +7 -2
  215. package/ts_build/src/services/KnowhowClient.js +108 -10
  216. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  217. package/ts_build/src/services/LazyToolsService.d.ts +2 -0
  218. package/ts_build/src/services/LazyToolsService.js +26 -2
  219. package/ts_build/src/services/LazyToolsService.js.map +1 -1
  220. package/ts_build/src/services/Mcp.js +20 -4
  221. package/ts_build/src/services/Mcp.js.map +1 -1
  222. package/ts_build/src/services/McpServer.js +17 -2
  223. package/ts_build/src/services/McpServer.js.map +1 -1
  224. package/ts_build/src/services/MessageProcessor.js +16 -2
  225. package/ts_build/src/services/MessageProcessor.js.map +1 -1
  226. package/ts_build/src/services/MinimalToolsService.js +16 -2
  227. package/ts_build/src/services/MinimalToolsService.js.map +1 -1
  228. package/ts_build/src/services/RuntimeReloadService.d.ts +12 -0
  229. package/ts_build/src/services/RuntimeReloadService.js +26 -0
  230. package/ts_build/src/services/RuntimeReloadService.js.map +1 -0
  231. package/ts_build/src/services/SessionManager.js +19 -2
  232. package/ts_build/src/services/SessionManager.js.map +1 -1
  233. package/ts_build/src/services/SkillsService.js +16 -2
  234. package/ts_build/src/services/SkillsService.js.map +1 -1
  235. package/ts_build/src/services/SyncedAgentWatcher.js +16 -2
  236. package/ts_build/src/services/SyncedAgentWatcher.js.map +1 -1
  237. package/ts_build/src/services/SyncerService.d.ts +1 -0
  238. package/ts_build/src/services/SyncerService.js +27 -5
  239. package/ts_build/src/services/SyncerService.js.map +1 -1
  240. package/ts_build/src/services/TaskRegistry.js +12 -2
  241. package/ts_build/src/services/TaskRegistry.js.map +1 -1
  242. package/ts_build/src/services/Tools.d.ts +1 -0
  243. package/ts_build/src/services/Tools.js +21 -2
  244. package/ts_build/src/services/Tools.js.map +1 -1
  245. package/ts_build/src/services/TracingService.d.ts +16 -0
  246. package/ts_build/src/services/TracingService.js +35 -0
  247. package/ts_build/src/services/TracingService.js.map +1 -0
  248. package/ts_build/src/services/index.d.ts +8 -0
  249. package/ts_build/src/services/index.js +9 -1
  250. package/ts_build/src/services/index.js.map +1 -1
  251. package/ts_build/src/services/modules/index.d.ts +6 -0
  252. package/ts_build/src/services/modules/index.js +29 -3
  253. package/ts_build/src/services/modules/index.js.map +1 -1
  254. package/ts_build/src/services/modules/types.d.ts +3 -0
  255. package/ts_build/src/types.d.ts +1 -0
  256. package/ts_build/src/types.js.map +1 -1
  257. package/ts_build/src/util/Trace.d.ts +11 -0
  258. package/ts_build/src/util/Trace.js +107 -0
  259. package/ts_build/src/util/Trace.js.map +1 -0
  260. package/ts_build/src/utils/http.d.ts +6 -2
  261. package/ts_build/src/utils/http.js +8 -2
  262. package/ts_build/src/utils/http.js.map +1 -1
  263. package/ts_build/tests/clients/fireworks.test.d.ts +1 -0
  264. package/ts_build/tests/clients/fireworks.test.js +65 -0
  265. package/ts_build/tests/clients/fireworks.test.js.map +1 -0
  266. package/ts_build/tests/clients/reasoningEffort.test.d.ts +1 -0
  267. package/ts_build/tests/clients/reasoningEffort.test.js +68 -0
  268. package/ts_build/tests/clients/reasoningEffort.test.js.map +1 -0
  269. package/ts_build/tests/compressor/toolResponseCache.test.js +1 -1
  270. package/ts_build/tests/compressor/toolResponseCache.test.js.map +1 -1
  271. package/ts_build/tests/manual/clients/completions.test.js +19 -6
  272. package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
  273. package/ts_build/tests/processors/TokenCompressor.test.js +11 -2
  274. package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
  275. package/ts_build/tests/processors/tools/boundedRetrievalTools.test.d.ts +1 -0
  276. package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js +57 -0
  277. package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js.map +1 -0
  278. package/ts_build/tests/processors/tools/grepToolResponse.test.js +37 -0
  279. package/ts_build/tests/processors/tools/grepToolResponse.test.js.map +1 -1
  280. package/ts_build/tests/unit/agents/historyRollback.test.d.ts +1 -0
  281. package/ts_build/tests/unit/agents/historyRollback.test.js +42 -0
  282. package/ts_build/tests/unit/agents/historyRollback.test.js.map +1 -0
  283. package/ts_build/tests/unit/auth/keyAuth.test.d.ts +1 -0
  284. package/ts_build/tests/unit/auth/keyAuth.test.js +77 -0
  285. package/ts_build/tests/unit/auth/keyAuth.test.js.map +1 -0
  286. package/ts_build/tests/unit/auth/keyManager.test.d.ts +1 -0
  287. package/ts_build/tests/unit/auth/keyManager.test.js +52 -0
  288. package/ts_build/tests/unit/auth/keyManager.test.js.map +1 -0
  289. package/ts_build/tests/unit/chat/historyCommandArgs.test.d.ts +1 -0
  290. package/ts_build/tests/unit/chat/historyCommandArgs.test.js +29 -0
  291. package/ts_build/tests/unit/chat/historyCommandArgs.test.js.map +1 -0
  292. package/ts_build/tests/unit/commands/agents.test.d.ts +1 -0
  293. package/ts_build/tests/unit/commands/agents.test.js +91 -0
  294. package/ts_build/tests/unit/commands/agents.test.js.map +1 -0
  295. package/ts_build/tests/unit/login.test.d.ts +1 -0
  296. package/ts_build/tests/unit/login.test.js +97 -0
  297. package/ts_build/tests/unit/login.test.js.map +1 -0
  298. package/ts_build/tests/unit/plugins/pluginLoading.test.js +59 -0
  299. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  300. package/ts_build/tests/unit/services/AgentSyncKnowhowWeb.test.d.ts +1 -0
  301. package/ts_build/tests/unit/services/AgentSyncKnowhowWeb.test.js +32 -0
  302. package/ts_build/tests/unit/services/AgentSyncKnowhowWeb.test.js.map +1 -0
  303. package/ts_build/tests/unit/services/LazyToolsService.test.d.ts +1 -0
  304. package/ts_build/tests/unit/services/LazyToolsService.test.js +27 -0
  305. package/ts_build/tests/unit/services/LazyToolsService.test.js.map +1 -0
  306. package/ts_build/tests/unit/services/RuntimeReloadService.test.d.ts +1 -0
  307. package/ts_build/tests/unit/services/RuntimeReloadService.test.js +26 -0
  308. package/ts_build/tests/unit/services/RuntimeReloadService.test.js.map +1 -0
  309. package/ts_build/tests/unit/services/SessionManager.test.d.ts +1 -0
  310. package/ts_build/tests/unit/services/SessionManager.test.js +72 -0
  311. package/ts_build/tests/unit/services/SessionManager.test.js.map +1 -0
  312. package/ts_build/tests/unit/services/SyncerService.test.d.ts +1 -0
  313. package/ts_build/tests/unit/services/SyncerService.test.js +40 -0
  314. package/ts_build/tests/unit/services/SyncerService.test.js.map +1 -0
  315. package/ts_build/tests/unit/util/Trace.test.d.ts +1 -0
  316. package/ts_build/tests/unit/util/Trace.test.js +107 -0
  317. package/ts_build/tests/unit/util/Trace.test.js.map +1 -0
  318. package/ts_build/tests/unit/utils/http.test.d.ts +1 -0
  319. package/ts_build/tests/unit/utils/http.test.js +69 -0
  320. package/ts_build/tests/unit/utils/http.test.js.map +1 -0
@@ -3,6 +3,7 @@
3
3
  * Handles synchronization via filesystem files in .knowhow/processes/agents/taskId/
4
4
  */
5
5
  import { BaseAgent } from "../agents/base/base";
6
+ import { TraceAll } from "../util/Trace";
6
7
  import { promises as fs } from "fs";
7
8
  import * as path from "path";
8
9
  import { watch } from "fs";
@@ -19,6 +20,7 @@ export interface FsSyncOptions {
19
20
  * AgentSyncFs handles filesystem-based synchronization for agent tasks
20
21
  * Creates files in .knowhow/processes/agents/{taskId}/ for status and input
21
22
  */
23
+ @TraceAll()
22
24
  export class AgentSyncFs {
23
25
  /** Shared cleanup interval across all instances to avoid duplicate cleanup runs */
24
26
  private static sharedCleanupInterval: NodeJS.Timeout | null = null;
@@ -128,6 +130,66 @@ export class AgentSyncFs {
128
130
  }
129
131
  }
130
132
 
133
+ /**
134
+ * Persist the remote identity alongside the local task. Keeping the local
135
+ * slug and remote UUID as separate fields lets resume reconnect without ever
136
+ * mistaking one for the other.
137
+ */
138
+ static async persistRemoteIdentity(
139
+ taskId: string,
140
+ remoteTaskId: string,
141
+ remoteMessageId?: string
142
+ ): Promise<void> {
143
+ const taskPath = path.join(AgentSyncFs.sharedBasePath, taskId);
144
+ await AgentSyncFs.writeRemoteIdentity(
145
+ taskPath,
146
+ remoteTaskId,
147
+ remoteMessageId
148
+ );
149
+ }
150
+
151
+ async setRemoteIdentity(
152
+ remoteTaskId: string,
153
+ remoteMessageId?: string
154
+ ): Promise<void> {
155
+ if (!this.taskPath) return;
156
+ await AgentSyncFs.writeRemoteIdentity(this.taskPath, remoteTaskId, remoteMessageId);
157
+ }
158
+
159
+ private static async writeRemoteIdentity(
160
+ taskPath: string,
161
+ remoteTaskId: string,
162
+ remoteMessageId?: string
163
+ ): Promise<void> {
164
+ try {
165
+ const metadataPath = path.join(taskPath, "metadata.json");
166
+ let metadata: any = {};
167
+ try {
168
+ metadata = JSON.parse(await fs.readFile(metadataPath, "utf8"));
169
+ } catch {
170
+ // The normal path already created metadata, but recover if it was
171
+ // removed or malformed while the task was running.
172
+ }
173
+
174
+ metadata.remoteTaskId = remoteTaskId;
175
+ // Preserve the established session field name for compatibility with
176
+ // callers that already read knowhowTaskId.
177
+ metadata.knowhowTaskId = remoteTaskId;
178
+ if (remoteMessageId) {
179
+ metadata.remoteMessageId = remoteMessageId;
180
+ metadata.knowhowMessageId = remoteMessageId;
181
+ }
182
+
183
+ await fs.writeFile(
184
+ metadataPath,
185
+ JSON.stringify(metadata, null, 2),
186
+ "utf8"
187
+ );
188
+ } catch (error) {
189
+ console.error(`❌ Failed to persist remote task identity:`, error);
190
+ }
191
+ }
192
+
131
193
  /**
132
194
  * Append a token usage entry (including the full message chain sent for
133
195
  * that call) to usage.json. This is intended for debugging cache-hit
@@ -217,6 +279,9 @@ export class AgentSyncFs {
217
279
  // thread messages via `_reasoning_details` / `reasoning_summary`.)
218
280
  metadata.reasoningEffort = agent.getReasoningEffort?.();
219
281
  metadata.summarizeReasoning = agent.getSummarizeReasoning?.();
282
+ // Tool schemas are part of the provider cache prefix. Preserve the exact
283
+ // request-visible list so resume/fork can recreate the same request.
284
+ metadata.enabledTools = agent.getEnabledToolNames();
220
285
  metadata.inProgress = inProgress;
221
286
  metadata.lastUpdate = new Date().toISOString();
222
287
 
@@ -10,6 +10,7 @@ import {
10
10
  } from "./KnowhowClient";
11
11
  import { BaseAgent } from "../agents/base/base";
12
12
  import { wait } from "../utils";
13
+ import { TraceAll } from "../util/Trace";
13
14
 
14
15
  export interface SyncOptions {
15
16
  messageId?: string;
@@ -27,6 +28,7 @@ export interface TaskSyncState {
27
28
  * AgentSynchronization handles all communication with the Knowhow API
28
29
  * for task creation, updates, status polling, and message synchronization
29
30
  */
31
+ @TraceAll()
30
32
  export class AgentSyncKnowhowWeb {
31
33
  private client: KnowhowSimpleClient;
32
34
  private baseUrl: string;
@@ -36,6 +38,7 @@ export class AgentSyncKnowhowWeb {
36
38
  private agent: BaseAgent | undefined;
37
39
  private threadUpdateHandler: ((...args: any[]) => void) | undefined;
38
40
  private doneHandler: ((...args: any[]) => void) | undefined;
41
+ private syncErrorLogged = false;
39
42
  /**
40
43
  * Tracks the most recent in-flight thread update API call.
41
44
  * The done handler awaits this before sending the finalization call,
@@ -48,6 +51,17 @@ export class AgentSyncKnowhowWeb {
48
51
  this.client = new KnowhowSimpleClient(baseUrl);
49
52
  }
50
53
 
54
+ /**
55
+ * Remote outages can make both the update and pending-message requests fail
56
+ * on every thread event. Report the first failure for this task, then stay
57
+ * quiet so an unavailable optional sync service does not flood the chat.
58
+ */
59
+ private reportSyncErrorOnce(message: string, error: unknown): void {
60
+ if (this.syncErrorLogged) return;
61
+ this.syncErrorLogged = true;
62
+ console.error(message, error);
63
+ }
64
+
51
65
  /**
52
66
  * Create a new chat task in Knowhow
53
67
  */
@@ -68,7 +82,7 @@ export class AgentSyncKnowhowWeb {
68
82
  console.log(`✅ Created Knowhow chat task: ${knowhowTaskId}`);
69
83
  return knowhowTaskId;
70
84
  } catch (error) {
71
- console.error(`❌ Failed to create Knowhow chat task:`, error);
85
+ this.reportSyncErrorOnce(`❌ Failed to create Knowhow chat task:`, error);
72
86
  return undefined;
73
87
  }
74
88
  }
@@ -95,7 +109,7 @@ export class AgentSyncKnowhowWeb {
95
109
  });
96
110
  console.log(`✅ Updated Knowhow chat task: ${knowhowTaskId}`);
97
111
  } catch (error) {
98
- console.error(`❌ Failed to update Knowhow chat task:`, error);
112
+ this.reportSyncErrorOnce(`❌ Failed to update Knowhow chat task:`, error);
99
113
  }
100
114
  }
101
115
 
@@ -167,7 +181,10 @@ export class AgentSyncKnowhowWeb {
167
181
  await this.client.markMessagesAsProcessed(knowhowTaskId, messageIds);
168
182
  console.log(`✅ Marked ${messageIds.length} message(s) as processed`);
169
183
  } catch (error) {
170
- console.error(`❌ Error checking/processing pending messages:`, error);
184
+ this.reportSyncErrorOnce(
185
+ `❌ Error checking/processing pending messages:`,
186
+ error
187
+ );
171
188
  // Continue execution even if synchronization fails
172
189
  }
173
190
  }
@@ -226,7 +243,7 @@ export class AgentSyncKnowhowWeb {
226
243
  }
227
244
  // Still paused, continue waiting
228
245
  } catch (error) {
229
- console.error(`❌ Error polling task status:`, error);
246
+ this.reportSyncErrorOnce(`❌ Error polling task status:`, error);
230
247
  // Continue polling even on errors
231
248
  }
232
249
  }
@@ -350,6 +367,7 @@ export class AgentSyncKnowhowWeb {
350
367
  this.eventHandlersSetup = false;
351
368
  this.finalizationPromise = null;
352
369
  this.pendingThreadUpdatePromise = null;
370
+ this.syncErrorLogged = false;
353
371
  }
354
372
 
355
373
  /**
@@ -4,6 +4,7 @@ import * as os from "os";
4
4
  import { KnowhowSimpleClient } from "./KnowhowClient";
5
5
  import { createHash } from "crypto";
6
6
  import { getConfig } from "../config";
7
+ import { TraceAll } from "../util/Trace";
7
8
 
8
9
  const BEHAVIORS_DIR = ".knowhow/behaviors";
9
10
  const LOCAL_SKILLS_DIRS = [".agents/skills", ".knowhow/skills"];
@@ -30,6 +31,7 @@ export interface Behavior {
30
31
  * In-memory singleton for behaviors/skills.
31
32
  * Call initFromDisk() once at startup; tools then read from memory.
32
33
  */
34
+ @TraceAll()
33
35
  export class BehaviorsService {
34
36
  private behaviors: Behavior[] = [];
35
37
  private skills: Behavior[] = [];
@@ -4,6 +4,7 @@ import * as os from "os";
4
4
  import { Config } from "../types";
5
5
  import { updateConfig } from "../config";
6
6
  import * as crypto from "crypto";
7
+ import { TraceAll } from "../util/Trace";
7
8
 
8
9
  export interface DockerWorkerOptions {
9
10
  workspaceDir: string;
@@ -14,6 +15,7 @@ export interface DockerWorkerOptions {
14
15
  unshare?: boolean;
15
16
  }
16
17
 
18
+ @TraceAll()
17
19
  export class DockerService {
18
20
  private static readonly IMAGE_NAME = "knowhow-worker";
19
21
  private static readonly CONTAINER_PREFIX = "knowhow-worker";
@@ -1,4 +1,5 @@
1
1
  import * as path from "path";
2
+ import { TraceAll } from "../util/Trace";
2
3
  import { globSync } from "glob";
3
4
 
4
5
  import {
@@ -38,6 +39,7 @@ interface EmbeddingServiceContext {
38
39
  plugins: PluginService;
39
40
  }
40
41
 
42
+ @TraceAll()
41
43
  export class EmbeddingService {
42
44
  private config: Config;
43
45
  private embeddingClient: GenericClient;
@@ -20,6 +20,9 @@ export interface EmbeddingsResolver {
20
20
  * Modules can register a resolver for a given remoteType (e.g. "github", "s3")
21
21
  * via registerResolver(), and the core upload()/download() functions delegate to it.
22
22
  */
23
+ import { TraceAll } from "../util/Trace";
24
+
25
+ @TraceAll()
23
26
  export class EmbeddingsService {
24
27
  private resolvers = new Map<string, EmbeddingsResolver>();
25
28
 
@@ -1,6 +1,10 @@
1
1
  import { createHash } from "crypto";
2
- import http from "../utils/http";
2
+ import { TraceAll } from "../util/Trace";
3
+ import http, { HTTP_UNAUTHORIZED_HANDLER, RefreshableHeaders } from "../utils/http";
3
4
  import fs from "fs";
5
+ import { getConfigSync } from "../config";
6
+ import { exchangePublicKeyJwt, storeJwt } from "../auth/keyAuth";
7
+ import { keyPairExists } from "../auth/keyManager";
4
8
  import { Message } from "../clients/types";
5
9
  import path from "path";
6
10
  import {
@@ -141,17 +145,32 @@ export function loadKnowhowJwt(): string {
141
145
  export const KNOWHOW_API_URL =
142
146
  process.env.KNOWHOW_API_URL || "https://api.knowhow.tyvm.ai";
143
147
 
148
+ @TraceAll()
144
149
  export class KnowhowSimpleClient {
145
- headers: Record<string, string> = {};
150
+ headers: RefreshableHeaders = {};
146
151
  jwtValidated = false;
152
+ private readonly canRefreshJwt: boolean;
153
+ private refreshPromise?: Promise<void>;
147
154
 
148
155
  constructor(
149
156
  private baseUrl = KNOWHOW_API_URL,
150
- private jwt = loadKnowhowJwt()
157
+ jwt?: string
151
158
  ) {
152
- this.setJwt(jwt);
159
+ // Explicit tokens (including KNOWHOW_JWT worker credentials) have no
160
+ // corresponding local identity and must never trigger interactive renewal.
161
+ this.canRefreshJwt = jwt === undefined && !process.env.KNOWHOW_JWT;
162
+ this.jwt = jwt ?? loadKnowhowJwt();
163
+ this.setJwt(this.jwt);
164
+ if (this.canRefreshJwt) {
165
+ Object.defineProperty(this.headers, HTTP_UNAUTHORIZED_HANDLER, {
166
+ enumerable: false,
167
+ value: () => this.renewJwt(true),
168
+ });
169
+ }
153
170
  }
154
171
 
172
+ private jwt: string;
173
+
155
174
  /**
156
175
  * Reload the JWT from disk (useful after login refreshes the token).
157
176
  */
@@ -165,9 +184,45 @@ export class KnowhowSimpleClient {
165
184
 
166
185
  setJwt(jwt: string) {
167
186
  this.jwt = jwt;
168
- this.headers = {
169
- Authorization: `Bearer ${this.jwt}`,
170
- };
187
+ // Keep this object stable: the HTTP wrapper retries with the same reference.
188
+ this.headers.Authorization = `Bearer ${this.jwt}`;
189
+ }
190
+
191
+ private jwtExpiresSoon(): boolean {
192
+ try {
193
+ const payload = JSON.parse(Buffer.from(this.jwt.split(".")[1], "base64url").toString("utf8"));
194
+ return typeof payload.exp === "number" && payload.exp * 1000 - Date.now() < 5 * 60 * 1000;
195
+ } catch {
196
+ return false;
197
+ }
198
+ }
199
+
200
+ private async renewJwt(force = false): Promise<void> {
201
+ if (!this.canRefreshJwt) return;
202
+ if (!force && !this.jwtExpiresSoon()) return;
203
+ if (this.refreshPromise) return this.refreshPromise;
204
+
205
+ this.refreshPromise = (async () => {
206
+ const config = getConfigSync();
207
+ const identityPath = config.cliIdentityPath;
208
+ if (!config.orgId || !keyPairExists(identityPath)) {
209
+ throw new Error("JWT expired and the CLI identity is unavailable. Please run `knowhow login` again.");
210
+ }
211
+ try {
212
+ const freshJwt = await exchangePublicKeyJwt(config.orgId, this.baseUrl, identityPath);
213
+ storeJwt(freshJwt);
214
+ this.setJwt(freshJwt);
215
+ this.jwtValidated = true;
216
+ } catch (error) {
217
+ throw new Error(
218
+ "Unable to renew the CLI session; the identity may have been revoked. Please run `knowhow login` again.",
219
+ { cause: error }
220
+ );
221
+ }
222
+ })().finally(() => {
223
+ this.refreshPromise = undefined;
224
+ });
225
+ return this.refreshPromise;
171
226
  }
172
227
 
173
228
  async checkJwt() {
@@ -175,6 +230,8 @@ export class KnowhowSimpleClient {
175
230
  throw new Error("No JWT found. Please login first.");
176
231
  }
177
232
 
233
+ await this.renewJwt();
234
+
178
235
  if (!this.jwtValidated) {
179
236
  try {
180
237
  this.jwtValidated = true;
@@ -186,6 +243,10 @@ export class KnowhowSimpleClient {
186
243
  return org.organizationId === orgId;
187
244
  });
188
245
  } catch (error) {
246
+ this.jwtValidated = false;
247
+ if (error instanceof Error && error.message.includes("renew the CLI session")) {
248
+ throw error;
249
+ }
189
250
  throw new Error("Invalid JWT. Please login again.");
190
251
  }
191
252
  }
@@ -1,4 +1,5 @@
1
1
  import { minimatch } from "minimatch";
2
+ import { TraceAll } from "../util/Trace";
2
3
  import { ToolsService, ToolContext, ToolCallContext } from "./Tools";
3
4
  import { Tool } from "../clients/types";
4
5
  import { ToolCall } from "../clients/types";
@@ -21,6 +22,7 @@ import { definitions } from "../agents/tools/lazy/definitions";
21
22
  * In web sessions where you get a new ToolService each interaction, then the cache busting is going to happen with each enable
22
23
  *
23
24
  */
25
+ @TraceAll()
24
26
  export class LazyToolsService extends ToolsService {
25
27
  private allTools: Tool[] = [];
26
28
  // Start with lazy tools enabled by default
@@ -80,6 +82,13 @@ export class LazyToolsService extends ToolsService {
80
82
  return this.tools; // Returns filtered subset
81
83
  }
82
84
 
85
+ /** Rebuild the lazy catalog while preserving enable/disable preferences. */
86
+ resetTools(): void {
87
+ this.allTools = [];
88
+ super.resetTools();
89
+ this.registerLazyTools();
90
+ }
91
+
83
92
  // Enable tools matching glob patterns
84
93
  enableTools(patterns: string[]) {
85
94
  for (const pattern of patterns) {
@@ -97,6 +106,13 @@ export class LazyToolsService extends ToolsService {
97
106
  };
98
107
  }
99
108
 
109
+ /** Replace glob state with an exact persisted request-visible tool list. */
110
+ restoreEnabledTools(names: string[]): void {
111
+ this.enabledPatterns = [...new Set(names)];
112
+ this.disabledPatterns = [];
113
+ this.updateVisibleTools();
114
+ }
115
+
100
116
  // Disable tools matching glob patterns
101
117
  disableTools(patterns: string[]) {
102
118
  for (const pattern of patterns) {
@@ -6,6 +6,7 @@ import fs from "fs";
6
6
  import { McpConfig } from "../types";
7
7
  import { Tool } from "../clients";
8
8
  import { getConfig } from "../config";
9
+ import { TraceAll } from "../util/Trace";
9
10
  import { ToolsService } from "./Tools";
10
11
  import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
11
12
  import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -36,6 +37,7 @@ export * from "./McpWebsocketTransport";
36
37
  * Each of the tools are namespaced with a prefix: mcp_index_servername_toolName
37
38
  * This services handles calls made to the namespaced function name, and finds the proper client to call the tool on.
38
39
  */
40
+ @TraceAll()
39
41
  export class McpService {
40
42
  connected = [];
41
43
  transports: Transport[] = [];
@@ -388,8 +390,15 @@ export class McpService {
388
390
  }
389
391
 
390
392
  async closeAll() {
391
- await this.closeTransports();
392
- await this.closeClients();
393
+ await Promise.allSettled([this.closeTransports()]);
394
+ await Promise.allSettled([this.closeClients()]);
395
+ // Connections are recreated from freshly-read configuration after reload.
396
+ this.clients = [];
397
+ this.transports = [];
398
+ this.connected = [];
399
+ this.config = [];
400
+ this.tools = [];
401
+ this.toolAliases = {};
393
402
  }
394
403
 
395
404
  getClientIndex(clientName: string) {
@@ -6,9 +6,11 @@ import { Tool, ToolProp } from "../clients/types";
6
6
  import { ToolsService } from "./Tools";
7
7
  import { MCPWebSocketTransport } from "./McpWebsocketTransport";
8
8
  import { WebSocket } from "ws";
9
+ import { TraceAll } from "../util/Trace";
9
10
 
10
11
  export type McpServerConfig = { name: string; version: string };
11
12
 
13
+ @TraceAll()
12
14
  export class McpServerService {
13
15
  server: McpServer | null = null;
14
16
  constructor(private toolsService: ToolsService) {}
@@ -1,4 +1,5 @@
1
1
  import { Message } from "../clients/types";
2
+ import { TraceAll } from "../util/Trace";
2
3
 
3
4
  export type ProcessorLifecycle =
4
5
  | "initial_call"
@@ -17,6 +18,7 @@ export interface ProcessorRegistration {
17
18
  priority: number;
18
19
  }
19
20
 
21
+ @TraceAll()
20
22
  export class MessageProcessor {
21
23
  private processors: Map<ProcessorLifecycle, ProcessorRegistration[]> =
22
24
  new Map();
@@ -5,6 +5,7 @@ import { inspectTools } from "../agents/tools/minimal/inspectTools";
5
5
  import { minimalDefinitions } from "../agents/tools/minimal/definitions";
6
6
  import { finalAnswer } from "../agents/tools/finalAnswer";
7
7
  import { includedTools } from "../agents/tools/list";
8
+ import { TraceAll } from "../util/Trace";
8
9
 
9
10
  /*
10
11
  * MinimalToolsService
@@ -19,6 +20,7 @@ import { includedTools } from "../agents/tools/list";
19
20
  * - Works on all providers and older models (no provider-specific defer_loading needed).
20
21
  * - Agents discover tools via inspectTools() and call them via callTool().
21
22
  */
23
+ @TraceAll()
22
24
  export class MinimalToolsService extends ToolsService {
23
25
  /** Full catalog of every registered tool (base + extended). */
24
26
  private allToolsCatalog: Tool[] = [];
@@ -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
+ }
@@ -5,10 +5,12 @@ import * as fs from "fs";
5
5
  import * as fsPromises from "fs/promises";
6
6
  import * as path from "path";
7
7
  import { TaskInfo, ChatSession } from "../chat/types";
8
+ import { TraceAll } from "../util/Trace";
8
9
 
9
10
  /**
10
11
  * SessionManager handles saving, loading, and managing agent sessions
11
12
  */
13
+ @TraceAll()
12
14
  export class SessionManager {
13
15
  private sessionsDir: string;
14
16
 
@@ -69,6 +71,7 @@ export class SessionManager {
69
71
  provider: taskInfo.provider,
70
72
  reasoningEffort: taskInfo.reasoningEffort,
71
73
  summarizeReasoning: taskInfo.summarizeReasoning,
74
+ enabledTools: taskInfo.enabledTools,
72
75
  };
73
76
 
74
77
  fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
@@ -115,6 +118,8 @@ export class SessionManager {
115
118
  session.reasoningEffort = taskInfo.reasoningEffort;
116
119
  if (taskInfo.summarizeReasoning !== undefined)
117
120
  session.summarizeReasoning = taskInfo.summarizeReasoning;
121
+ if (taskInfo.enabledTools !== undefined)
122
+ session.enabledTools = taskInfo.enabledTools;
118
123
  }
119
124
 
120
125
  fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
@@ -4,6 +4,7 @@ import * as os from "os";
4
4
  import { createHash } from "crypto";
5
5
  import * as https from "https";
6
6
  import { KnowhowSimpleClient } from "./KnowhowClient";
7
+ import { TraceAll } from "../util/Trace";
7
8
 
8
9
  // Default local install dir (relative to cwd)
9
10
  const LOCAL_SKILLS_DIR = ".knowhow/skills";
@@ -233,6 +234,7 @@ export function parseSkillRef(
233
234
  return { owner, repo, skill, sourceUrl };
234
235
  }
235
236
 
237
+ @TraceAll()
236
238
  export class SkillsService {
237
239
  private cwd: string;
238
240
  private isGlobal: boolean;
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { Message } from "../clients/types";
7
7
  import { EventService } from "./EventService";
8
+ import { TraceAll } from "../util/Trace";
8
9
 
9
10
  export interface SyncedAgentWatcher {
10
11
  /** Start watching for changes, emitting agent events */
@@ -73,6 +74,7 @@ export interface AttachableAgent {
73
74
  * - agentEvents and eventTypes are delegated to the watcher
74
75
  * - "done" is emitted by the watcher when the remote agent completes
75
76
  */
77
+ @TraceAll()
76
78
  export class WatcherBackedAgent implements AttachableAgent {
77
79
  public name: string;
78
80
  public agentEvents: EventService;
@@ -3,6 +3,7 @@
3
3
  * Hides complexity of choosing and managing sync backends.
4
4
  */
5
5
  import { BaseAgent } from "../agents/base/base";
6
+ import { TraceAll } from "../util/Trace";
6
7
  import { AgentSyncFs } from "./AgentSyncFs";
7
8
  import { AgentSyncKnowhowWeb } from "./AgentSyncKnowhowWeb";
8
9
 
@@ -11,12 +12,13 @@ export interface SyncerOptions {
11
12
  prompt: string;
12
13
  /** If provided → use web sync (unless syncFs is set) */
13
14
  messageId?: string;
15
+ /** Existing remote Knowhow task UUID. Never inferred from the local taskId. */
16
+ remoteTaskId?: string;
14
17
  /** Force fs sync even if messageId is provided */
15
18
  syncFs?: boolean;
16
19
  /**
17
- * Push the agent's work to a remote Knowhow task. When set (and there is no
18
- * messageId), the `taskId` is treated as the remote task ID to attach to and
19
- * push updates to, rather than creating a new remote task.
20
+ * Push the agent's work to the existing `remoteTaskId`. The local taskId is
21
+ * always a filesystem identity and is never used as a remote identifier.
20
22
  */
21
23
  syncRemote?: boolean;
22
24
  /** Agent name to persist in metadata.json */
@@ -44,8 +46,9 @@ export interface AgentSyncer {
44
46
  * Decision logic:
45
47
  * - Always sets up AgentSyncFs (primary local syncer)
46
48
  * - If messageId is present AND syncFs is not forced → creates a new remote task via AgentSyncKnowhowWeb
47
- * - If syncRemote is set (and no messageId) → attaches to the remote task identified by taskId
49
+ * - If syncRemote and a valid remoteTaskId are set → attaches to that UUID
48
50
  */
51
+ @TraceAll()
49
52
  export class SyncerService implements AgentSyncer {
50
53
  private fsSync: AgentSyncFs;
51
54
  private webSync: AgentSyncKnowhowWeb;
@@ -75,14 +78,16 @@ export class SyncerService implements AgentSyncer {
75
78
 
76
79
  // Determine whether to ATTACH to an already-existing remote task and push
77
80
  // local work to it (no creation needed). This is the "--sync-remote" path:
78
- // the caller wants to push work to the remote task identified by taskId.
81
+ // the caller wants to push work to an explicitly identified remote task.
79
82
  // We only auto-attach when there is NO messageId (messageId always creates
80
83
  // a fresh remote task above).
81
84
  //
82
85
  // This is orthogonal to fs sync: we can push to the remote task AND keep
83
86
  // the local .knowhow/processes/agents/<id>/ files in sync at the same time.
84
87
  const shouldAttachExisting =
85
- !!options.syncRemote && !options.messageId && !!options.taskId;
88
+ !!options.syncRemote &&
89
+ !options.messageId &&
90
+ isUuid(options.remoteTaskId);
86
91
 
87
92
  // Always create fs sync task first
88
93
  console.log(
@@ -104,6 +109,7 @@ export class SyncerService implements AgentSyncer {
104
109
 
105
110
  if (knowhowTaskId) {
106
111
  this.useWebSync = true;
112
+ await this.fsSync.setRemoteIdentity(knowhowTaskId, options.messageId);
107
113
  this.createdTaskId = knowhowTaskId;
108
114
  console.log(`🌐 Web sync task created: ${knowhowTaskId}`);
109
115
  }
@@ -112,9 +118,10 @@ export class SyncerService implements AgentSyncer {
112
118
  // Attach live web sync to an existing remote task (push updates to it).
113
119
  if (!shouldUseWebSync && shouldAttachExisting) {
114
120
  this.useWebSync = true;
115
- this.createdTaskId = options.taskId;
121
+ await this.fsSync.setRemoteIdentity(options.remoteTaskId!);
122
+ this.createdTaskId = options.remoteTaskId;
116
123
  console.log(
117
- `🌐 Attaching web sync to existing remote task: ${options.taskId}`
124
+ `🌐 Attaching web sync to existing remote task: ${options.remoteTaskId}`
118
125
  );
119
126
  }
120
127
 
@@ -174,3 +181,11 @@ export class SyncerService implements AgentSyncer {
174
181
  return this.useWebSync ? this.createdTaskId : undefined;
175
182
  }
176
183
  }
184
+
185
+ /** Remote task IDs are UUIDs; local filesystem IDs are timestamped slugs. */
186
+ function isUuid(value: string | undefined): value is string {
187
+ return !!value &&
188
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
189
+ value
190
+ );
191
+ }
@@ -2,10 +2,12 @@
2
2
  * Task Registry Service - Manages active agent tasks
3
3
  */
4
4
  import { TaskInfo } from "../chat/types";
5
+ import { TraceAll } from "../util/Trace";
5
6
 
6
7
  /**
7
8
  * TaskRegistry manages the in-memory registry of active agent tasks
8
9
  */
10
+ @TraceAll()
9
11
  export class TaskRegistry {
10
12
  private tasks = new Map<string, TaskInfo>();
11
13