@phuetz/code-buddy 2.0.0 → 2.1.0

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 (403) hide show
  1. package/README.fr.md +56 -0
  2. package/README.md +73 -16
  3. package/codebuddy-runtime.json +5 -5
  4. package/dist/agent/codebuddy-agent.d.ts +18 -0
  5. package/dist/agent/codebuddy-agent.js +45 -8
  6. package/dist/agent/execution/agent-executor.d.ts +0 -9
  7. package/dist/agent/execution/agent-executor.js +165 -12
  8. package/dist/agent/execution/context-pipeline.d.ts +9 -0
  9. package/dist/agent/execution/context-pipeline.js +64 -2
  10. package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
  11. package/dist/agent/execution/tool-loop-guard.js +165 -0
  12. package/dist/agent/execution/tool-selection-strategy.js +19 -2
  13. package/dist/agent/facades/session-facade.d.ts +0 -6
  14. package/dist/agent/facades/session-facade.js +0 -45
  15. package/dist/agent/hermes-claw-migrate.d.ts +0 -21
  16. package/dist/agent/hermes-claw-migrate.js +9 -4
  17. package/dist/agent/hermes-protocol-gateways.js +8 -1
  18. package/dist/agent/repo-profiler.js +17 -4
  19. package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
  20. package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
  21. package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
  22. package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
  23. package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
  24. package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
  25. package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
  26. package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
  27. package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
  28. package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
  29. package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
  30. package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
  31. package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
  32. package/dist/agent/self-improvement/proposal-store.js +79 -17
  33. package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
  34. package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
  35. package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
  36. package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
  37. package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
  38. package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
  39. package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
  40. package/dist/agent/self-improvement/skill-engine.js +385 -13
  41. package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
  42. package/dist/agent/self-improvement/skill-gate.js +17 -2
  43. package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
  44. package/dist/agent/self-improvement/skill-mutator.js +51 -15
  45. package/dist/agent/self-improvement/skill-types.d.ts +2 -1
  46. package/dist/agent/self-improvement/types.d.ts +11 -0
  47. package/dist/agent/tool-handler.d.ts +1 -1
  48. package/dist/agent/tool-handler.js +28 -9
  49. package/dist/auth/profile-manager.js +1 -1
  50. package/dist/channels/companion-channel-turn.d.ts +53 -4
  51. package/dist/channels/companion-channel-turn.js +218 -16
  52. package/dist/channels/slash-parity.d.ts +4 -6
  53. package/dist/channels/slash-parity.js +9 -44
  54. package/dist/cli/session-commands.d.ts +9 -0
  55. package/dist/cli/session-commands.js +45 -5
  56. package/dist/cli/session-picker.d.ts +43 -0
  57. package/dist/cli/session-picker.js +128 -0
  58. package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
  59. package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
  60. package/dist/codebuddy/fleet-tool-defs.d.ts +1 -0
  61. package/dist/codebuddy/fleet-tool-defs.js +24 -0
  62. package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
  63. package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
  64. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
  65. package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
  66. package/dist/codebuddy/providers/provider-openai-compat.js +1 -1
  67. package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
  68. package/dist/codebuddy/ragchat-tool-defs.js +19 -0
  69. package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
  70. package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
  71. package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
  72. package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
  73. package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
  74. package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
  75. package/dist/codebuddy/tools.d.ts +0 -6
  76. package/dist/codebuddy/tools.js +25 -3
  77. package/dist/commands/cli/daemon-commands.js +3 -1
  78. package/dist/commands/cli/evolve-command.js +21 -5
  79. package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
  80. package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
  81. package/dist/commands/cli/fleet-commands.js +29 -0
  82. package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
  83. package/dist/commands/cli/fleet-mission-commands.js +72 -0
  84. package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
  85. package/dist/commands/cli/fleet-rooms-commands.js +289 -0
  86. package/dist/commands/cli/tools-commands.js +2 -1
  87. package/dist/commands/cli/triage-command.d.ts +8 -0
  88. package/dist/commands/cli/triage-command.js +72 -0
  89. package/dist/commands/cli/utility-commands.js +25 -1
  90. package/dist/commands/client-dispatcher.d.ts +7 -0
  91. package/dist/commands/client-dispatcher.js +73 -21
  92. package/dist/commands/cron-cli/index.d.ts +3 -1
  93. package/dist/commands/cron-cli/index.js +25 -0
  94. package/dist/commands/device-auth.d.ts +10 -0
  95. package/dist/commands/device-auth.js +91 -0
  96. package/dist/commands/enhanced-command-handler.d.ts +9 -0
  97. package/dist/commands/enhanced-command-handler.js +28 -6
  98. package/dist/commands/handlers/agent-handlers.d.ts +1 -1
  99. package/dist/commands/handlers/agent-handlers.js +4 -4
  100. package/dist/commands/handlers/channel-handlers.js +53 -2
  101. package/dist/commands/handlers/export-handlers.d.ts +3 -2
  102. package/dist/commands/handlers/export-handlers.js +55 -28
  103. package/dist/commands/handlers/extra-handlers.js +5 -1
  104. package/dist/commands/handlers/grill-me-handler.js +26 -18
  105. package/dist/commands/handlers/memory-handlers.d.ts +6 -2
  106. package/dist/commands/handlers/memory-handlers.js +35 -15
  107. package/dist/commands/handlers/missing-handlers.d.ts +2 -2
  108. package/dist/commands/handlers/missing-handlers.js +4 -4
  109. package/dist/commands/handlers/resources-handler.d.ts +12 -0
  110. package/dist/commands/handlers/resources-handler.js +52 -0
  111. package/dist/commands/handlers/starter-handlers.d.ts +1 -1
  112. package/dist/commands/handlers/starter-handlers.js +32 -2
  113. package/dist/commands/handlers/switch-handler.d.ts +1 -1
  114. package/dist/commands/handlers/switch-handler.js +15 -0
  115. package/dist/commands/handlers/test-handlers.js +4 -86
  116. package/dist/commands/handlers/ui-handlers.js +11 -5
  117. package/dist/commands/login-chatgpt.d.ts +8 -0
  118. package/dist/commands/login-chatgpt.js +17 -0
  119. package/dist/commands/mcp-import.d.ts +11 -0
  120. package/dist/commands/mcp-import.js +67 -0
  121. package/dist/commands/mcp-invoke.d.ts +2 -0
  122. package/dist/commands/mcp-invoke.js +68 -0
  123. package/dist/commands/mcp.js +4 -0
  124. package/dist/commands/pipeline.d.ts +7 -1
  125. package/dist/commands/pipeline.js +104 -7
  126. package/dist/commands/resources.d.ts +2 -0
  127. package/dist/commands/resources.js +64 -0
  128. package/dist/commands/skills-cli/index.js +35 -12
  129. package/dist/commands/slash/builtin-commands.js +16 -3
  130. package/dist/commands/slash/index.d.ts +3 -1
  131. package/dist/commands/slash/index.js +2 -0
  132. package/dist/commands/slash/surfaces.d.ts +53 -0
  133. package/dist/commands/slash/surfaces.js +177 -0
  134. package/dist/commands/slash/types.d.ts +17 -0
  135. package/dist/commands/try.d.ts +1 -1
  136. package/dist/commands/try.js +1 -1
  137. package/dist/companion/companion-identity.d.ts +43 -0
  138. package/dist/companion/companion-identity.js +136 -0
  139. package/dist/companion/companion-toolset.d.ts +66 -0
  140. package/dist/companion/companion-toolset.js +368 -0
  141. package/dist/companion/companion-turn.d.ts +10 -2
  142. package/dist/companion/companion-turn.js +38 -1
  143. package/dist/config/code-exec-policy.d.ts +25 -0
  144. package/dist/config/code-exec-policy.js +39 -0
  145. package/dist/config/model-tools.d.ts +9 -0
  146. package/dist/config/model-tools.js +4 -0
  147. package/dist/context/context-manager-v2.js +2 -2
  148. package/dist/context/lm-resizer-compressor.js +3 -2
  149. package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
  150. package/dist/context/lm-resizer-diagnostics.js +30 -0
  151. package/dist/context/tool-output-masking.js +3 -4
  152. package/dist/curator/curator.d.ts +6 -0
  153. package/dist/curator/curator.js +53 -3
  154. package/dist/daemon/cron-agent-bridge.d.ts +6 -0
  155. package/dist/daemon/cron-agent-bridge.js +31 -4
  156. package/dist/doctor/index.d.ts +11 -2
  157. package/dist/doctor/index.js +84 -21
  158. package/dist/doctor/integrations.d.ts +179 -0
  159. package/dist/doctor/integrations.js +227 -0
  160. package/dist/doctor/triage.d.ts +108 -0
  161. package/dist/doctor/triage.js +304 -0
  162. package/dist/fleet/collaboration.d.ts +44 -0
  163. package/dist/fleet/collaboration.js +170 -0
  164. package/dist/fleet/fleet-listener.d.ts +1 -0
  165. package/dist/fleet/fleet-listener.js +87 -46
  166. package/dist/fleet/fleet-registry.d.ts +2 -0
  167. package/dist/fleet/resource-catalog.d.ts +245 -0
  168. package/dist/fleet/resource-catalog.js +211 -0
  169. package/dist/fleet/rooms/room-access.d.ts +138 -0
  170. package/dist/fleet/rooms/room-access.js +196 -0
  171. package/dist/fleet/rooms/room-client.d.ts +158 -0
  172. package/dist/fleet/rooms/room-client.js +685 -0
  173. package/dist/fleet/rooms/room-event.d.ts +139 -0
  174. package/dist/fleet/rooms/room-event.js +330 -0
  175. package/dist/fleet/rooms/room-filter.d.ts +48 -0
  176. package/dist/fleet/rooms/room-filter.js +152 -0
  177. package/dist/fleet/rooms/room-hub.d.ts +154 -0
  178. package/dist/fleet/rooms/room-hub.js +363 -0
  179. package/dist/fleet/rooms/room-identity.d.ts +26 -0
  180. package/dist/fleet/rooms/room-identity.js +82 -0
  181. package/dist/fleet/rooms/room-lock.d.ts +34 -0
  182. package/dist/fleet/rooms/room-lock.js +180 -0
  183. package/dist/fleet/rooms/room-observations.d.ts +7 -0
  184. package/dist/fleet/rooms/room-observations.js +61 -0
  185. package/dist/fleet/rooms/room-server.d.ts +28 -0
  186. package/dist/fleet/rooms/room-server.js +75 -0
  187. package/dist/fleet/rooms/room-store.d.ts +119 -0
  188. package/dist/fleet/rooms/room-store.js +448 -0
  189. package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
  190. package/dist/fleet/rooms/room-ws-bridge.js +137 -0
  191. package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
  192. package/dist/fleet/sensory-mission-bridge.js +136 -0
  193. package/dist/git/worktree-sessions.js +1 -0
  194. package/dist/harness/contract.d.ts +4 -4
  195. package/dist/harness/fleet-supervisor.d.ts +16 -0
  196. package/dist/harness/fleet-supervisor.js +64 -0
  197. package/dist/harness/index.d.ts +4 -0
  198. package/dist/harness/index.js +4 -0
  199. package/dist/harness/mission-runner.d.ts +3 -0
  200. package/dist/harness/mission-runner.js +53 -0
  201. package/dist/harness/mission-store.d.ts +93 -0
  202. package/dist/harness/mission-store.js +297 -0
  203. package/dist/harness/tool-harness.d.ts +56 -0
  204. package/dist/harness/tool-harness.js +153 -0
  205. package/dist/hooks/use-enhanced-input.js +48 -4
  206. package/dist/hooks/use-input-handler.d.ts +1 -0
  207. package/dist/hooks/use-input-handler.js +82 -22
  208. package/dist/hooks/use-input-history.js +1 -0
  209. package/dist/identity/lisa-introspection.js +5 -2
  210. package/dist/identity/operational-self-model.d.ts +2 -0
  211. package/dist/identity/operational-self-model.js +4 -0
  212. package/dist/index.js +52 -6
  213. package/dist/mcp/client.js +6 -0
  214. package/dist/mcp/config.d.ts +20 -0
  215. package/dist/mcp/config.js +121 -8
  216. package/dist/mcp/import-normalize.d.ts +12 -0
  217. package/dist/mcp/import-normalize.js +114 -0
  218. package/dist/mcp/transports.d.ts +10 -5
  219. package/dist/mcp/transports.js +61 -99
  220. package/dist/mcp/types.d.ts +5 -0
  221. package/dist/media/comfyui-recipe-contract.d.ts +7 -7
  222. package/dist/memory/facts-memory.d.ts +11 -1
  223. package/dist/memory/facts-memory.js +47 -8
  224. package/dist/memory/memory-candidate-queue.d.ts +2 -2
  225. package/dist/memory/memory-candidate-queue.js +6 -6
  226. package/dist/memory/persistent-memory.d.ts +13 -8
  227. package/dist/memory/persistent-memory.js +77 -30
  228. package/dist/nodes/device-node.js +5 -9
  229. package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
  230. package/dist/observability/run-event-writer.d.ts +21 -0
  231. package/dist/observability/run-event-writer.js +81 -0
  232. package/dist/observability/run-store.d.ts +5 -0
  233. package/dist/observability/run-store.js +18 -13
  234. package/dist/observability/run-trajectory.js +1 -1
  235. package/dist/optimization/prompt-cache.d.ts +19 -2
  236. package/dist/optimization/prompt-cache.js +41 -6
  237. package/dist/orchestration/orchestrator.d.ts +21 -0
  238. package/dist/orchestration/orchestrator.js +122 -15
  239. package/dist/persistence/session-content.d.ts +8 -0
  240. package/dist/persistence/session-content.js +76 -0
  241. package/dist/persistence/session-handoff.d.ts +64 -0
  242. package/dist/persistence/session-handoff.js +80 -0
  243. package/dist/persistence/session-history.d.ts +4 -0
  244. package/dist/persistence/session-history.js +38 -0
  245. package/dist/persistence/session-store.d.ts +9 -0
  246. package/dist/persistence/session-store.js +48 -20
  247. package/dist/personas/persona-manager.js +17 -1
  248. package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
  249. package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
  250. package/dist/plugins/plugin-manager.js +10 -1
  251. package/dist/prompts/prompt-manager.js +11 -4
  252. package/dist/prompts/system-base.js +6 -2
  253. package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
  254. package/dist/protocols/a2a/codebuddy-executor.js +9 -8
  255. package/dist/protocols/a2a/index.d.ts +1 -1
  256. package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
  257. package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
  258. package/dist/protocols/a2a/peer-config.d.ts +20 -0
  259. package/dist/protocols/a2a/peer-config.js +55 -0
  260. package/dist/providers/codex-oauth.js +30 -6
  261. package/dist/providers/model-provider-compat.js +2 -1
  262. package/dist/sandbox/os-sandbox.d.ts +1 -1
  263. package/dist/scheduler/cron-scheduler.d.ts +15 -2
  264. package/dist/scheduler/cron-scheduler.js +35 -4
  265. package/dist/scheduler/job-notepad.d.ts +69 -0
  266. package/dist/scheduler/job-notepad.js +339 -0
  267. package/dist/security/audit-logger.d.ts +1 -1
  268. package/dist/security/compute-confinement.d.ts +4 -0
  269. package/dist/security/compute-confinement.js +89 -0
  270. package/dist/security/session-encryption.d.ts +4 -0
  271. package/dist/security/session-encryption.js +31 -0
  272. package/dist/sensory/voice-loop.d.ts +17 -0
  273. package/dist/sensory/voice-loop.js +72 -15
  274. package/dist/server/auth/device-session-context.d.ts +9 -0
  275. package/dist/server/auth/device-session-context.js +13 -0
  276. package/dist/server/auth/device-store.d.ts +89 -0
  277. package/dist/server/auth/device-store.js +261 -0
  278. package/dist/server/auth/device-token.d.ts +3 -0
  279. package/dist/server/auth/device-token.js +6 -0
  280. package/dist/server/auth/jwt.js +4 -0
  281. package/dist/server/index.d.ts +0 -10
  282. package/dist/server/index.js +28 -0
  283. package/dist/server/middleware/auth.js +3 -2
  284. package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
  285. package/dist/server/routes/a2a-jsonrpc.js +66 -0
  286. package/dist/server/routes/a2a-protocol.d.ts +12 -0
  287. package/dist/server/routes/a2a-protocol.js +1 -1
  288. package/dist/server/routes/device-auth.d.ts +4 -0
  289. package/dist/server/routes/device-auth.js +40 -0
  290. package/dist/server/types.d.ts +4 -0
  291. package/dist/server/websocket/handler.d.ts +13 -0
  292. package/dist/server/websocket/handler.js +102 -17
  293. package/dist/services/prompt-builder.d.ts +2 -0
  294. package/dist/services/prompt-builder.js +13 -5
  295. package/dist/services/runtime-settings-context.d.ts +73 -0
  296. package/dist/services/runtime-settings-context.js +69 -0
  297. package/dist/skills/executor.js +2 -0
  298. package/dist/skills/hub.js +16 -14
  299. package/dist/skills/local-inventory.d.ts +21 -0
  300. package/dist/skills/local-inventory.js +72 -0
  301. package/dist/skills/skill-loader.js +2 -2
  302. package/dist/skills/skill-usage-store.d.ts +49 -0
  303. package/dist/skills/skill-usage-store.js +121 -0
  304. package/dist/testing/ai-integration-tests.js +8 -5
  305. package/dist/themes/theme-manager.d.ts +12 -0
  306. package/dist/themes/theme-manager.js +29 -0
  307. package/dist/themes/theme-schema.d.ts +24 -24
  308. package/dist/tools/a2a-call-tool.d.ts +34 -0
  309. package/dist/tools/a2a-call-tool.js +75 -0
  310. package/dist/tools/bash/streaming-executor.d.ts +1 -0
  311. package/dist/tools/bash/streaming-executor.js +78 -90
  312. package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
  313. package/dist/tools/code-exec-preflight-runner.js +254 -0
  314. package/dist/tools/code-exec-preflight.d.ts +44 -0
  315. package/dist/tools/code-exec-preflight.js +501 -0
  316. package/dist/tools/code-exec-tool.d.ts +48 -1
  317. package/dist/tools/code-exec-tool.js +191 -47
  318. package/dist/tools/comment-watcher.js +29 -12
  319. package/dist/tools/core-code-inspection.d.ts +56 -0
  320. package/dist/tools/core-code-inspection.js +86 -0
  321. package/dist/tools/create-skill-tool.d.ts +2 -3
  322. package/dist/tools/create-skill-tool.js +16 -75
  323. package/dist/tools/cronjob-tool.d.ts +1 -0
  324. package/dist/tools/cronjob-tool.js +1 -0
  325. package/dist/tools/env-doctor-tool.d.ts +2 -0
  326. package/dist/tools/env-doctor-tool.js +4 -3
  327. package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
  328. package/dist/tools/execute-code-rpc-invoker.js +35 -19
  329. package/dist/tools/execute-code-runner.d.ts +2 -0
  330. package/dist/tools/execute-code-runner.js +52 -17
  331. package/dist/tools/fleet-room-tool.d.ts +104 -0
  332. package/dist/tools/fleet-room-tool.js +124 -0
  333. package/dist/tools/integration-tool-hints.d.ts +4 -0
  334. package/dist/tools/integration-tool-hints.js +19 -0
  335. package/dist/tools/list-peers-tool.d.ts +3 -0
  336. package/dist/tools/list-peers-tool.js +11 -4
  337. package/dist/tools/media-generation-tool.d.ts +16 -1
  338. package/dist/tools/media-generation-tool.js +341 -9
  339. package/dist/tools/merge-conflict-tool.js +53 -25
  340. package/dist/tools/metadata.d.ts +1 -5
  341. package/dist/tools/metadata.js +21 -28
  342. package/dist/tools/ragchat-tool.d.ts +39 -0
  343. package/dist/tools/ragchat-tool.js +105 -0
  344. package/dist/tools/registry/fleet-tools.d.ts +9 -0
  345. package/dist/tools/registry/fleet-tools.js +22 -1
  346. package/dist/tools/registry/knowledge-tools.d.ts +2 -2
  347. package/dist/tools/registry/knowledge-tools.js +2 -2
  348. package/dist/tools/registry/lessons-tools.d.ts +7 -0
  349. package/dist/tools/registry/lessons-tools.js +43 -3
  350. package/dist/tools/registry/memory-tools.d.ts +1 -1
  351. package/dist/tools/registry/memory-tools.js +8 -8
  352. package/dist/tools/registry/research-tools.d.ts +1 -0
  353. package/dist/tools/registry/research-tools.js +4 -13
  354. package/dist/tools/registry/self-describe-tools.d.ts +1 -1
  355. package/dist/tools/registry/self-describe-tools.js +43 -30
  356. package/dist/tools/resource-catalog-tool.d.ts +38 -0
  357. package/dist/tools/resource-catalog-tool.js +46 -0
  358. package/dist/tools/skills-inspection-tool.js +30 -1
  359. package/dist/tools/stream-tool-output.d.ts +3 -0
  360. package/dist/tools/stream-tool-output.js +33 -0
  361. package/dist/tools/tool-call-scheduler.d.ts +10 -0
  362. package/dist/tools/tool-call-scheduler.js +58 -0
  363. package/dist/tools/tool-effect.d.ts +5 -0
  364. package/dist/tools/tool-effect.js +26 -0
  365. package/dist/tools/tool-search.d.ts +12 -11
  366. package/dist/tools/tool-search.js +67 -26
  367. package/dist/tools/types.d.ts +6 -0
  368. package/dist/ui/components/ChatHistory.d.ts +1 -1
  369. package/dist/ui/components/ChatHistory.js +2 -2
  370. package/dist/ui/components/ChatInput.d.ts +2 -1
  371. package/dist/ui/components/ChatInput.js +42 -88
  372. package/dist/ui/components/ChatInterface.d.ts +4 -2
  373. package/dist/ui/components/ChatInterface.js +27 -63
  374. package/dist/ui/components/CommandSuggestions.d.ts +0 -1
  375. package/dist/ui/components/CommandSuggestions.js +4 -6
  376. package/dist/ui/components/FileAutocomplete.js +1 -1
  377. package/dist/ui/components/KeyboardHelp.js +8 -6
  378. package/dist/ui/components/ModelSelection.js +6 -5
  379. package/dist/ui/components/StatusBar.d.ts +2 -1
  380. package/dist/ui/components/StatusBar.js +8 -7
  381. package/dist/ui/context/theme-context.js +3 -2
  382. package/dist/ui/index.d.ts +1 -1
  383. package/dist/ui/index.js +1 -1
  384. package/dist/ui/utils/markdown-renderer.js +4 -13
  385. package/dist/utils/atomic-write.d.ts +22 -0
  386. package/dist/utils/atomic-write.js +29 -0
  387. package/dist/utils/bounded-output.d.ts +16 -0
  388. package/dist/utils/bounded-output.js +63 -0
  389. package/dist/utils/device-store-file.d.ts +4 -0
  390. package/dist/utils/device-store-file.js +40 -0
  391. package/dist/utils/export-manager.d.ts +9 -0
  392. package/dist/utils/export-manager.js +19 -0
  393. package/dist/utils/first-use-hints.d.ts +25 -0
  394. package/dist/utils/first-use-hints.js +58 -0
  395. package/dist/utils/history-manager.js +1 -1
  396. package/dist/utils/update-notifier.js +16 -18
  397. package/dist/wizard/environment-detection.d.ts +5 -1
  398. package/dist/wizard/environment-detection.js +49 -7
  399. package/dist/wizard/onboarding.d.ts +5 -1
  400. package/dist/wizard/onboarding.js +90 -30
  401. package/dist/wizard/provider-onboarding.d.ts +2 -0
  402. package/dist/wizard/provider-onboarding.js +55 -17
  403. package/package.json +7 -3
@@ -10,90 +10,31 @@
10
10
  * commands it uses repeatedly and codify them as reusable skills.
11
11
  *
12
12
  * Skill file location:
13
- * .codebuddy/skills/workspace/<slug>/SKILL.md
13
+ * .codebuddy/skills/<authored-slug>/SKILL.md
14
14
  *
15
15
  * The tool generates valid SKILL.md frontmatter and validates that
16
16
  * the slug does not already exist before writing.
17
17
  */
18
- import * as fs from 'fs/promises';
19
18
  import * as path from 'path';
20
- import { existsSync } from 'fs';
21
- import { validateGeneratedCode, formatValidationReport } from '../security/code-validator.js';
19
+ import * as yaml from 'yaml';
20
+ import { LiveSkillMutator, toAuthoredSkillName } from '../agent/self-improvement/skill-mutator.js';
22
21
  export class CreateSkillTool {
23
- get workspaceDir() {
24
- return path.join(process.cwd(), '.codebuddy', 'skills', 'workspace');
25
- }
26
- async execute(input) {
27
- const { name, description, body, tags = [], env = {}, requires = [], overwrite = false, } = input;
28
- if (!name?.trim()) {
29
- return { success: false, error: 'name is required' };
30
- }
31
- if (!description?.trim()) {
32
- return { success: false, error: 'description is required' };
33
- }
34
- if (!body?.trim()) {
35
- return { success: false, error: 'body is required' };
36
- }
37
- // Derive a filesystem-safe slug
38
- const slug = name
39
- .toLowerCase()
40
- .replace(/[^a-z0-9]+/g, '-')
41
- .replace(/^-+|-+$/g, '');
42
- const skillDir = path.join(this.workspaceDir, slug);
43
- const skillFile = path.join(skillDir, 'SKILL.md');
44
- // Check for existing skill
45
- if (existsSync(skillFile) && !overwrite) {
46
- return {
47
- success: false,
48
- error: `Skill "${slug}" already exists. Set overwrite: true to replace it.`,
49
- };
50
- }
51
- // Build YAML frontmatter
52
- const fm = ['---', `name: ${name}`, `version: 1.0.0`, `description: ${description}`];
53
- if (tags.length > 0) {
54
- fm.push(`tags: [${tags.join(', ')}]`);
55
- }
56
- if (Object.keys(env).length > 0) {
57
- fm.push('env:');
58
- for (const [k, v] of Object.entries(env)) {
59
- fm.push(` ${k}: ${v}`);
60
- }
61
- }
62
- if (requires.length > 0) {
63
- fm.push(`requires: [${requires.join(', ')}]`);
64
- }
65
- fm.push('---');
66
- const skillContent = `${fm.join('\n')}\n\n${body.trim()}\n`;
67
- // Validate skill body for security (prevent malicious code in LLM-generated skills)
68
- const validation = validateGeneratedCode(body);
69
- if (!validation.safe) {
70
- return {
71
- success: false,
72
- error: `Code validation failed:\n${formatValidationReport(validation)}\nSkill creation blocked for security reasons.`,
73
- };
74
- }
75
- // Write to disk
22
+ async execute(input, cwd = process.cwd()) {
76
23
  try {
77
- await fs.mkdir(skillDir, { recursive: true });
78
- await fs.writeFile(skillFile, skillContent, 'utf-8');
24
+ for (const field of ['name', 'description', 'body']) {
25
+ if (typeof input[field] !== 'string' || !input[field].trim())
26
+ return { success: false, error: `${field} is required` };
27
+ }
28
+ const name = toAuthoredSkillName(input.name);
29
+ const root = path.join(cwd, '.codebuddy', 'skills');
30
+ // Serialize metadata, including names containing ':' or YAML control characters.
31
+ const content = `---\n${yaml.stringify({ name, description: input.description, tags: input.tags ?? [], env: input.env ?? {}, requires: input.requires ?? [] })}---\n\n${input.body.trim()}\n`;
32
+ new LiveSkillMutator(root).create({ name, description: input.description, content }, { overwrite: input.overwrite === true });
33
+ return { success: true, output: `Skill created and loaded: ${name}\nPath: ${path.join(root, name, 'SKILL.md')}`, data: { name, loaded: true } };
79
34
  }
80
- catch (err) {
81
- return {
82
- success: false,
83
- error: `Failed to write skill: ${err instanceof Error ? err.message : String(err)}`,
84
- };
35
+ catch (error) {
36
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
85
37
  }
86
- return {
87
- success: true,
88
- output: [
89
- `✅ Skill created: ${name}`,
90
- ` Path: ${skillFile}`,
91
- ` Tags: ${tags.join(', ') || 'none'}`,
92
- '',
93
- 'The skill is now available in the workspace registry.',
94
- 'Run `buddy hub search ${name}` or `/skills` to verify.',
95
- ].join('\n'),
96
- };
97
38
  }
98
39
  getSchema() {
99
40
  return {
@@ -14,6 +14,7 @@ export interface CronjobToolInput extends Record<string, unknown> {
14
14
  skillRequest?: unknown;
15
15
  then?: unknown;
16
16
  preCheck?: unknown;
17
+ continuity?: unknown;
17
18
  deliver?: unknown;
18
19
  format?: unknown;
19
20
  }
@@ -50,6 +50,7 @@ function buildCreateOptions(input) {
50
50
  skillRequest: normalizeString(input.skillRequest),
51
51
  then: normalizeString(input.then),
52
52
  preCheck: normalizeJsonOption(input.preCheck),
53
+ continuity: normalizeJsonOption(input.continuity),
53
54
  deliver: normalizeDeliver(input.deliver),
54
55
  format: normalizeString(input.format),
55
56
  };
@@ -1,3 +1,4 @@
1
+ import { diagnoseLmResizer } from '../context/lm-resizer-diagnostics.js';
1
2
  import type { ToolResult } from '../types/index.js';
2
3
  export interface EnvDoctorData {
3
4
  root: string;
@@ -6,6 +7,7 @@ export interface EnvDoctorData {
6
7
  npmScripts: string[];
7
8
  tools: Record<string, boolean>;
8
9
  configFiles: string[];
10
+ lmResizer?: Awaited<ReturnType<typeof diagnoseLmResizer>>;
9
11
  }
10
12
  export declare class EnvDoctorTool {
11
13
  readonly name = "env_doctor";
@@ -1,3 +1,4 @@
1
+ import { diagnoseLmResizer } from '../context/lm-resizer-diagnostics.js';
1
2
  import { execFile } from 'child_process';
2
3
  import * as fs from 'fs/promises';
3
4
  import * as path from 'path';
@@ -17,7 +18,7 @@ catch {
17
18
  return false;
18
19
  } }
19
20
  async function hasTool(name) { try {
20
- await execFileAsync('which', [name], { timeout: 3000 });
21
+ await execFileAsync(process.platform === 'win32' ? 'where.exe' : 'which', [name], { timeout: 3000 });
21
22
  return true;
22
23
  }
23
24
  catch {
@@ -43,8 +44,8 @@ export class EnvDoctorTool {
43
44
  for (const file of CONFIG_FILES)
44
45
  if (await exists(path.join(root, file)))
45
46
  configFiles.push(file);
46
- const data = { root, nodeVersion: process.version, nodeModulesPresent: await exists(path.join(root, 'node_modules')), npmScripts, tools: { git: await hasTool('git'), docker: await hasTool('docker') }, configFiles };
47
- return { success: true, output: `Node ${process.version}; ${npmScripts.length} npm scripts; node_modules ${data.nodeModulesPresent ? 'present' : 'missing'}`, data };
47
+ const data = { root, nodeVersion: process.version, nodeModulesPresent: await exists(path.join(root, 'node_modules')), npmScripts, tools: { git: await hasTool('git'), docker: await hasTool('docker') }, configFiles, lmResizer: await diagnoseLmResizer() };
48
+ return { success: true, output: `Node ${process.version}; ${npmScripts.length} npm scripts; node_modules ${data.nodeModulesPresent ? 'present' : 'missing'}\nLM Resizer (host): ${data.lmResizer?.binary}; tool-output=${data.lmResizer?.toolOutputSupported}; enabled=${data.lmResizer?.enabled}. ${data.lmResizer?.warning}\nHost binaries may not be available in the Bash sandbox; use configured MCP integrations or the host observation optimizer.`, data };
48
49
  }
49
50
  catch (error) {
50
51
  return { success: false, error: error instanceof Error ? error.message : String(error) };
@@ -39,7 +39,7 @@ export interface ExecuteCodeRpcInvokeResult {
39
39
  error?: string;
40
40
  truncated?: boolean;
41
41
  }
42
- export type ExecuteCodeRpcInvoker = (request: ExecuteCodeRpcInvokeRequest) => Promise<ExecuteCodeRpcInvokeResult>;
42
+ export type ExecuteCodeRpcInvoker = (request: ExecuteCodeRpcInvokeRequest, signal?: AbortSignal) => Promise<ExecuteCodeRpcInvokeResult>;
43
43
  /** Truthy-flag parse — the single master gate. Read once, in the parent. */
44
44
  export declare function isExecuteCodeToolRpcEnabled(raw?: string | undefined): boolean;
45
45
  export declare function getExecuteCodeToolRpcAllowlist(raw?: string | undefined): Set<string>;
@@ -30,6 +30,7 @@
30
30
  * `peer-tool-bridge.ts` pattern.
31
31
  */
32
32
  import { spawn } from 'child_process';
33
+ import { constants } from 'node:fs';
33
34
  import { getRipgrepPath } from '../utils/ripgrep-path.js';
34
35
  import * as fs from 'fs/promises';
35
36
  import * as path from 'path';
@@ -93,9 +94,10 @@ export function createExecuteCodeRpcInvoker(options) {
93
94
  const combinedAllowlist = new Set([...baseAllowlist, ...extraTools]);
94
95
  const isFleetSafe = options.isFleetSafe ?? ((name) => getToolRegistry().isFleetSafe(name));
95
96
  const workspaceRoot = path.resolve(options.workspaceRoot);
96
- return async (request) => {
97
+ return async (request, signal) => {
97
98
  const tool = request.tool;
98
99
  try {
100
+ signal?.throwIfAborted();
99
101
  if (!combinedAllowlist.has(tool)) {
100
102
  return {
101
103
  ok: false,
@@ -114,7 +116,7 @@ export function createExecuteCodeRpcInvoker(options) {
114
116
  if (!executor) {
115
117
  return { ok: false, error: `UNKNOWN_EXECUTE_CODE_RPC_TOOL: no executor for "${tool}"` };
116
118
  }
117
- const { output, truncated } = await executor(request.args, workspaceRoot);
119
+ const { output, truncated } = await executor(request.args, workspaceRoot, signal);
118
120
  logger.debug('[execute-code-rpc] tool executed', { tool, truncated });
119
121
  return { ok: true, output, truncated };
120
122
  }
@@ -133,22 +135,34 @@ function assertInsideWorkspace(target, workspaceRoot) {
133
135
  }
134
136
  return absolute;
135
137
  }
136
- const execViewFile = async (args, workspaceRoot) => {
138
+ /** Resolve both sides physically; lexical containment alone follows escaping links. */
139
+ async function resolveWorkspacePath(target, workspaceRoot) {
140
+ const lexical = assertInsideWorkspace(target, workspaceRoot);
141
+ const root = await fs.realpath(workspaceRoot);
142
+ const resolved = await fs.realpath(lexical);
143
+ return assertInsideWorkspace(resolved, root);
144
+ }
145
+ const execViewFile = async (args, workspaceRoot, signal) => {
137
146
  const filePath = args.file_path ?? args.path;
138
147
  if (typeof filePath !== 'string' || filePath.length === 0) {
139
148
  throw new Error('view_file: missing string file_path');
140
149
  }
141
- const resolved = assertInsideWorkspace(filePath, workspaceRoot);
142
- const stat = await fs.stat(resolved);
143
- if (!stat.isFile()) {
144
- throw new Error(`view_file: ${filePath} is not a regular file`);
145
- }
146
- const truncated = stat.size > READ_TRUNCATE_BYTES;
147
- const limit = Math.min(stat.size, READ_TRUNCATE_BYTES);
148
- if (limit <= 0)
149
- return { output: '', truncated };
150
- const handle = await fs.open(resolved, 'r');
150
+ const resolved = await resolveWorkspacePath(filePath, workspaceRoot);
151
+ signal?.throwIfAborted();
152
+ const handle = await fs.open(resolved, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
151
153
  try {
154
+ // On Linux verify the opened descriptor too, before reading, against path swaps.
155
+ if (process.platform === 'linux') {
156
+ assertInsideWorkspace(await fs.realpath(`/proc/self/fd/${handle.fd}`), await fs.realpath(workspaceRoot));
157
+ }
158
+ const stat = await handle.stat();
159
+ if (!stat.isFile())
160
+ throw new Error(`view_file: ${filePath} is not a regular file`);
161
+ const truncated = stat.size > READ_TRUNCATE_BYTES;
162
+ const limit = Math.min(stat.size, READ_TRUNCATE_BYTES);
163
+ signal?.throwIfAborted();
164
+ if (limit <= 0)
165
+ return { output: '', truncated };
152
166
  const buffer = Buffer.allocUnsafe(limit);
153
167
  const { bytesRead } = await handle.read(buffer, 0, limit, 0);
154
168
  return { output: buffer.subarray(0, bytesRead).toString('utf-8'), truncated };
@@ -157,12 +171,13 @@ const execViewFile = async (args, workspaceRoot) => {
157
171
  await handle.close();
158
172
  }
159
173
  };
160
- const execListDirectory = async (args, workspaceRoot) => {
174
+ const execListDirectory = async (args, workspaceRoot, signal) => {
161
175
  const dirPath = args.path ?? args.directory ?? '.';
162
176
  if (typeof dirPath !== 'string') {
163
177
  throw new Error('list_directory: path must be a string');
164
178
  }
165
- const resolved = assertInsideWorkspace(dirPath, workspaceRoot);
179
+ const resolved = await resolveWorkspacePath(dirPath, workspaceRoot);
180
+ signal?.throwIfAborted();
166
181
  const entries = await fs.readdir(resolved, { withFileTypes: true });
167
182
  const lines = entries
168
183
  .map((entry) => {
@@ -174,7 +189,7 @@ const execListDirectory = async (args, workspaceRoot) => {
174
189
  const visible = truncated ? lines.slice(0, LIST_DIRECTORY_MAX_ENTRIES) : lines;
175
190
  return { output: visible.join('\n'), truncated };
176
191
  };
177
- const execSearch = async (args, workspaceRoot) => {
192
+ const execSearch = async (args, workspaceRoot, signal) => {
178
193
  const query = args.query ?? args.pattern;
179
194
  const dirPath = args.path ?? '.';
180
195
  if (typeof query !== 'string' || query.length === 0) {
@@ -183,10 +198,11 @@ const execSearch = async (args, workspaceRoot) => {
183
198
  if (typeof dirPath !== 'string') {
184
199
  throw new Error('search: path must be a string');
185
200
  }
186
- const resolved = assertInsideWorkspace(dirPath, workspaceRoot);
201
+ const resolved = await resolveWorkspacePath(dirPath, workspaceRoot);
202
+ signal?.throwIfAborted();
187
203
  return await new Promise((resolve, reject) => {
188
- const rgArgs = ['--no-heading', '--line-number', '--color', 'never', '--max-count', '50', '--', query, resolved];
189
- const proc = spawn(getRipgrepPath(), rgArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
204
+ const rgArgs = ['--no-config', '--no-follow', '--no-heading', '--line-number', '--color', 'never', '--max-count', '50', '--', query, resolved];
205
+ const proc = spawn(getRipgrepPath(), rgArgs, { stdio: ['ignore', 'pipe', 'pipe'], signal });
190
206
  let stdout = '';
191
207
  let stderr = '';
192
208
  let lineCount = 0;
@@ -38,6 +38,8 @@ export interface ExecuteCodeRunnerOptions {
38
38
  * cannot exfiltrate secrets — even during pre-accept scoring.
39
39
  */
40
40
  envMode?: 'inherit' | 'isolate';
41
+ /** Required OS confinement for authored computation; never falls back to unrestricted execution. */
42
+ confinement?: 'compute';
41
43
  }
42
44
  export interface ExecuteCodeResult {
43
45
  kind: 'execute_code_result';
@@ -2,6 +2,7 @@ import { spawn } from 'child_process';
2
2
  import { randomUUID } from 'crypto';
3
3
  import fs from 'fs/promises';
4
4
  import path from 'path';
5
+ import { confineComputeInvocation } from '../security/compute-confinement.js';
5
6
  import { logger } from '../utils/logger.js';
6
7
  import { isExecuteCodeToolRpcEnabled, } from './execute-code-rpc-invoker.js';
7
8
  /**
@@ -58,10 +59,11 @@ export async function executeCode(input, options = {}) {
58
59
  // the responder is ALWAYS active so every request gets a structured
59
60
  // reply (denial when off / tool refused) — never a hang.
60
61
  const rpcEnabled = (options.rpcEnabled ?? isExecuteCodeToolRpcEnabled()) && !!options.rpcInvoke;
61
- const rpcSupported = language === 'javascript' || language === 'typescript' || language === 'python';
62
+ const rpcSupported = options.confinement !== 'compute' && (language === 'javascript' || language === 'typescript' || language === 'python');
62
63
  const rpcDir = path.join(runDir, RPC_DIR_NAME);
63
64
  const rpcMaxCalls = Math.max(1, options.rpcMaxCalls ?? RPC_DEFAULT_MAX_CALLS);
64
65
  const rpcCallTimeoutMs = Math.max(1_000, options.rpcCallTimeoutMs ?? RPC_DEFAULT_CALL_TIMEOUT_MS);
66
+ await fs.mkdir(runDir, { recursive: true });
65
67
  let scriptCode = code;
66
68
  if (rpcSupported) {
67
69
  await fs.mkdir(rpcDir, { recursive: true });
@@ -90,7 +92,11 @@ export async function executeCode(input, options = {}) {
90
92
  const childEnv = options.envMode === 'isolate'
91
93
  ? buildIsolatedEnv(runDir, { ...env, ...runnerEnv })
92
94
  : { ...process.env, ...env, ...runnerEnv };
93
- const child = spawn(invocation.command, [...invocation.args, scriptPath, ...scriptArgs], {
95
+ const commandArgs = [...invocation.args, scriptPath, ...scriptArgs];
96
+ const launch = options.confinement === 'compute'
97
+ ? confineComputeInvocation(invocation.command, commandArgs, runDir)
98
+ : { command: invocation.command, args: commandArgs };
99
+ const child = spawn(launch.command, launch.args, {
94
100
  cwd: runDir,
95
101
  env: childEnv,
96
102
  windowsHide: true,
@@ -125,8 +131,7 @@ export async function executeCode(input, options = {}) {
125
131
  clearTimeout(timer);
126
132
  if (forceKillTimer)
127
133
  clearTimeout(forceKillTimer);
128
- rpcPoller?.stop();
129
- resolve({ exitCode, signal });
134
+ void Promise.resolve(rpcPoller?.stop()).then(() => resolve({ exitCode, signal }));
130
135
  });
131
136
  });
132
137
  const completedAt = (options.now ?? (() => new Date()))().toISOString();
@@ -292,11 +297,19 @@ function startRpcResponder(options) {
292
297
  const seen = new Set();
293
298
  let callCount = 0;
294
299
  let stopped = false;
300
+ const controller = new AbortController();
295
301
  let scanning = false;
302
+ let activeScan = Promise.resolve();
296
303
  const writeResponse = async (id, payload) => {
304
+ if (stopped)
305
+ return;
297
306
  const finalPath = path.join(options.rpcDir, `${id}.res.json`);
298
307
  const tmpPath = path.join(options.rpcDir, `${id}.res.json.tmp`);
299
308
  await fs.writeFile(tmpPath, JSON.stringify(payload), 'utf8');
309
+ if (stopped) {
310
+ await fs.rm(tmpPath, { force: true });
311
+ return;
312
+ }
300
313
  await fs.rename(tmpPath, finalPath);
301
314
  };
302
315
  const handleRequest = async (id, reqPath) => {
@@ -308,6 +321,8 @@ function startRpcResponder(options) {
308
321
  await writeResponse(id, { ok: false, error: 'RPC_BAD_REQUEST: could not parse request JSON' });
309
322
  return;
310
323
  }
324
+ if (stopped)
325
+ return;
311
326
  if (!options.enabled || !options.invoke) {
312
327
  await writeResponse(id, {
313
328
  ok: false,
@@ -331,14 +346,22 @@ function startRpcResponder(options) {
331
346
  await writeResponse(id, { ok: false, error: 'RPC_BAD_REQUEST: missing string "tool"' });
332
347
  return;
333
348
  }
349
+ const callController = new AbortController();
350
+ const abort = () => callController.abort();
351
+ controller.signal.addEventListener('abort', abort, { once: true });
334
352
  try {
335
- const result = await withTimeout(options.invoke({ tool, args }), options.callTimeoutMs, `RPC_TOOL_TIMEOUT: tool "${tool}" exceeded ${options.callTimeoutMs}ms`);
353
+ if (stopped)
354
+ return;
355
+ const result = await withTimeout(options.invoke({ tool, args }, callController.signal), options.callTimeoutMs, `RPC_TOOL_TIMEOUT: tool "${tool}" exceeded ${options.callTimeoutMs}ms`, callController);
336
356
  await writeResponse(id, result);
337
357
  }
338
358
  catch (error) {
339
359
  const message = error instanceof Error ? error.message : String(error);
340
360
  await writeResponse(id, { ok: false, error: message });
341
361
  }
362
+ finally {
363
+ controller.signal.removeEventListener('abort', abort);
364
+ }
342
365
  };
343
366
  const scan = async () => {
344
367
  if (scanning || stopped)
@@ -347,6 +370,8 @@ function startRpcResponder(options) {
347
370
  try {
348
371
  const entries = await fs.readdir(options.rpcDir).catch(() => []);
349
372
  for (const entry of entries) {
373
+ if (stopped)
374
+ break;
350
375
  if (!entry.endsWith('.req.json'))
351
376
  continue;
352
377
  const id = entry.slice(0, -'.req.json'.length);
@@ -366,26 +391,33 @@ function startRpcResponder(options) {
366
391
  }
367
392
  };
368
393
  const interval = setInterval(() => {
369
- void scan();
394
+ if (!scanning && !stopped)
395
+ activeScan = scan();
370
396
  }, RPC_POLL_INTERVAL_MS);
371
397
  interval.unref?.();
372
398
  return {
373
- stop() {
399
+ async stop() {
374
400
  stopped = true;
401
+ controller.abort();
375
402
  clearInterval(interval);
403
+ await activeScan;
376
404
  },
377
405
  };
378
406
  }
379
- function withTimeout(promise, ms, message) {
407
+ function withTimeout(promise, ms, message, controller) {
380
408
  return new Promise((resolve, reject) => {
381
- const timer = setTimeout(() => reject(new Error(message)), ms);
409
+ const abort = () => { clearTimeout(timer); reject(new Error('RPC_CANCELLED')); };
410
+ const timer = setTimeout(() => {
411
+ reject(new Error(message));
412
+ controller.abort();
413
+ }, ms);
382
414
  timer.unref?.();
383
- promise.then((value) => {
384
- clearTimeout(timer);
385
- resolve(value);
386
- }, (error) => {
415
+ controller.signal.addEventListener('abort', abort, { once: true });
416
+ if (controller.signal.aborted)
417
+ abort();
418
+ promise.then(resolve, reject).finally(() => {
387
419
  clearTimeout(timer);
388
- reject(error instanceof Error ? error : new Error(String(error)));
420
+ controller.signal.removeEventListener('abort', abort);
389
421
  });
390
422
  });
391
423
  }
@@ -501,10 +533,12 @@ function buildRpcHelper(language) {
501
533
  ].join('\n');
502
534
  }
503
535
  // javascript / typescript
536
+ // Block-scoped dynamic imports avoid collisions with names in the user's module.
504
537
  return [
505
- "import { writeFileSync, renameSync, existsSync, readFileSync } from 'node:fs';",
506
- "import { join } from 'node:path';",
507
- "import { randomUUID } from 'node:crypto';",
538
+ '{',
539
+ "const { writeFileSync, renameSync, existsSync, readFileSync } = await import('node:fs');",
540
+ "const { join } = await import('node:path');",
541
+ "const { randomUUID } = await import('node:crypto');",
508
542
  'globalThis.codebuddyToolCall = function codebuddyToolCall(tool, args) {',
509
543
  ' const dir = process.env.CODEBUDDY_EXECUTE_CODE_RPC_DIR;',
510
544
  " if (!dir) return { ok: false, error: 'EXECUTE_CODE_TOOL_RPC_UNAVAILABLE' };",
@@ -524,6 +558,7 @@ function buildRpcHelper(language) {
524
558
  ' }',
525
559
  " return { ok: false, error: 'RPC_RESPONSE_TIMEOUT' };",
526
560
  '};',
561
+ '}',
527
562
  '',
528
563
  ].join('\n');
529
564
  }
@@ -0,0 +1,104 @@
1
+ import { z } from 'zod';
2
+ import type { ToolResult } from '../types/index.js';
3
+ export declare const fleetRoomInputSchema: z.ZodDiscriminatedUnion<"action", [z.ZodObject<{
4
+ action: z.ZodLiteral<"send">;
5
+ content: z.ZodEffects<z.ZodString, string, string>;
6
+ }, "strict", z.ZodTypeAny, {
7
+ content: string;
8
+ action: "send";
9
+ }, {
10
+ content: string;
11
+ action: "send";
12
+ }>, z.ZodObject<{
13
+ action: z.ZodLiteral<"history">;
14
+ limit: z.ZodOptional<z.ZodNumber>;
15
+ cursor: z.ZodOptional<z.ZodObject<{
16
+ storeId: z.ZodString;
17
+ throughSeq: z.ZodNumber;
18
+ }, "strict", z.ZodTypeAny, {
19
+ storeId: string;
20
+ throughSeq: number;
21
+ }, {
22
+ storeId: string;
23
+ throughSeq: number;
24
+ }>>;
25
+ }, "strict", z.ZodTypeAny, {
26
+ action: "history";
27
+ limit?: number | undefined;
28
+ cursor?: {
29
+ storeId: string;
30
+ throughSeq: number;
31
+ } | undefined;
32
+ }, {
33
+ action: "history";
34
+ limit?: number | undefined;
35
+ cursor?: {
36
+ storeId: string;
37
+ throughSeq: number;
38
+ } | undefined;
39
+ }>, z.ZodObject<{
40
+ action: z.ZodLiteral<"status">;
41
+ }, "strict", z.ZodTypeAny, {
42
+ action: "status";
43
+ }, {
44
+ action: "status";
45
+ }>]>;
46
+ interface Cursor {
47
+ storeId: string;
48
+ throughSeq: number;
49
+ }
50
+ interface Message {
51
+ seq: number;
52
+ room: string;
53
+ author: string;
54
+ text: string;
55
+ event: {
56
+ id: string;
57
+ created_at: number;
58
+ };
59
+ }
60
+ export interface FleetRoomToolClient {
61
+ connect(): Promise<{
62
+ pubkey: string;
63
+ name: string;
64
+ rooms: unknown[];
65
+ }>;
66
+ publish(input: {
67
+ room: string;
68
+ content: string;
69
+ }): Promise<{
70
+ id: string;
71
+ seq: number;
72
+ duplicate: boolean;
73
+ storeId: string;
74
+ }>;
75
+ fetch(filters: Array<{
76
+ '#h': string[];
77
+ limit: number;
78
+ }>, cursor?: Cursor, options?: {
79
+ maxMessages?: number;
80
+ }): Promise<{
81
+ messages: Message[];
82
+ cursor: Cursor;
83
+ gap: boolean;
84
+ epochChanged: boolean;
85
+ truncated?: boolean;
86
+ }>;
87
+ close(): Promise<void>;
88
+ }
89
+ export interface FleetRoomToolConfig {
90
+ url: string;
91
+ room: string;
92
+ identityPath: string;
93
+ apiKey?: string;
94
+ jwt?: string;
95
+ }
96
+ export interface FleetRoomToolDependencies {
97
+ env?: NodeJS.ProcessEnv;
98
+ createClient?: (config: FleetRoomToolConfig) => Promise<FleetRoomToolClient>;
99
+ /** Test seam; runtime budget is 15 seconds plus at most 1 second cleanup. */
100
+ timeoutMs?: number;
101
+ }
102
+ /** One bounded client lifecycle per invocation; never subscribes a background worker. */
103
+ export declare function executeFleetRoom(input: unknown, deps?: FleetRoomToolDependencies): Promise<ToolResult>;
104
+ export {};