@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
@@ -1,10 +1,15 @@
1
+ import { A2A_CALL_TOOL_DEF } from './a2a-call-tool-defs.js';
2
+ import { RESOURCE_CATALOG_TOOL_DEF } from './resource-catalog-tool-defs.js';
3
+ import { RAGCHAT_TOOL_DEF } from './ragchat-tool-defs.js';
4
+ import { integrationToolHints } from '../tools/integration-tool-hints.js';
1
5
  /**
2
6
  * Grok Tools
3
7
  *
4
8
  * Main entry point for tool definitions and management.
5
9
  * Tools are now organized in modular files under tool-definitions/.
6
10
  */
7
- import { setDeferredMCPSchemas } from "../tools/deferred-schema-state.js";
11
+ import { TOOL_METADATA as SEARCH_TOOL_METADATA } from '../tools/metadata.js';
12
+ import { setDeferredMCPSchemas, resolveDeferredSchemas as resolveSelectedSchemas } from "../tools/deferred-schema-state.js";
8
13
  import { MCPManager } from "../mcp/client.js";
9
14
  import { loadMCPConfig } from "../mcp/config.js";
10
15
  import { getToolSelector, selectRelevantTools } from "../tools/tool-selector.js";
@@ -137,6 +142,7 @@ export function getBuiltinToolNames() {
137
142
  MERGE_CONFLICT_TOOLS,
138
143
  VULN_SCANNER_TOOLS,
139
144
  SESSION_TOOLS,
145
+ [RAGCHAT_TOOL_DEF, RESOURCE_CATALOG_TOOL_DEF, A2A_CALL_TOOL_DEF],
140
146
  FLEET_TOOLS,
141
147
  CODE_EXPLORER_TOOLS,
142
148
  WINDOWS_TOOLS,
@@ -269,6 +275,7 @@ export function initializeToolRegistry() {
269
275
  registerGroup(SESSION_TOOLS);
270
276
  // Fleet tools — peer_delegate, list_peers (Phase (d).17). Always available;
271
277
  // peer_delegate returns a clear error when no peers are connected.
278
+ registerGroup([RAGCHAT_TOOL_DEF, RESOURCE_CATALOG_TOOL_DEF, A2A_CALL_TOOL_DEF]);
272
279
  registerGroup(FLEET_TOOLS);
273
280
  // CodeExplorer tools
274
281
  registerGroup(CODE_EXPLORER_TOOLS);
@@ -593,6 +600,8 @@ export async function getAllCodeBuddyTools() {
593
600
  initToolSearchIndex(allTools.map((t) => ({
594
601
  name: t.function.name,
595
602
  description: t.function.description ?? '',
603
+ parameters: t.function.parameters,
604
+ keywords: SEARCH_TOOL_METADATA.find(meta => meta.name === t.function.name)?.keywords,
596
605
  })));
597
606
  }
598
607
  catch {
@@ -603,6 +612,12 @@ export async function getAllCodeBuddyTools() {
603
612
  // ============================================================================
604
613
  // Tool Selection (RAG-based)
605
614
  // ============================================================================
615
+ /** Hydrate only names already exposed by assembly/selection; never add permissions. */
616
+ function hydrateExposedToolSchemas(tools) {
617
+ const resolved = new Map(resolveSelectedSchemas(tools.map(tool => tool.function.name))
618
+ .map(tool => [tool.function.name, tool]));
619
+ return tools.map(tool => resolved.get(tool.function.name) ?? tool);
620
+ }
606
621
  /**
607
622
  * Get relevant tools for a specific query using RAG-based selection
608
623
  *
@@ -621,7 +636,7 @@ export async function getRelevantTools(query, options = {}) {
621
636
  // If RAG is disabled, return all tools
622
637
  if (!useRAG) {
623
638
  return {
624
- selectedTools: allTools,
639
+ selectedTools: hydrateExposedToolSchemas(allTools),
625
640
  scores: new Map(allTools.map(t => [t.function.name, 1])),
626
641
  classification: {
627
642
  categories: ['file_read', 'file_write', 'system'],
@@ -633,7 +648,14 @@ export async function getRelevantTools(query, options = {}) {
633
648
  originalTokens: 0
634
649
  };
635
650
  }
636
- return selectRelevantTools(query, allTools, maxTools, options.alwaysInclude);
651
+ const preferred = integrationToolHints(query, allTools.map(tool => tool.function.name));
652
+ const selection = selectRelevantTools(query, allTools, maxTools, [
653
+ ...(options.alwaysInclude ?? ['view_file', 'bash']), ...preferred,
654
+ ]);
655
+ // Once selected, a deferred MCP tool must expose its argument schema.
656
+ // Empty stubs otherwise force valid models to emit {} for required inputs.
657
+ selection.selectedTools = hydrateExposedToolSchemas(selection.selectedTools);
658
+ return selection;
637
659
  }
638
660
  /**
639
661
  * Classify a query to understand what types of tools are needed
@@ -144,16 +144,17 @@ export function registerDaemonCommands(program) {
144
144
  const { getCronAgentBridge } = await import('../../daemon/cron-agent-bridge.js');
145
145
  const { getCronScheduler } = await import('../../scheduler/cron-scheduler.js');
146
146
  const { RunStore } = await import('../../observability/run-store.js');
147
+ const scheduler = getCronScheduler();
147
148
  const bridge = getCronAgentBridge({
148
149
  apiKey,
149
150
  baseURL: process.env.GROK_BASE_URL,
150
151
  model: process.env.GROK_MODEL || 'grok-3-latest',
151
152
  maxToolRounds: 20,
152
153
  jobTimeoutMs: 300000,
154
+ notepadDir: scheduler.notepadDir,
153
155
  // Scheduled executions become first-class run records + artifacts.
154
156
  runStore: RunStore.getInstance(),
155
157
  });
156
- const scheduler = getCronScheduler();
157
158
  scheduler.setTaskExecutor(bridge.createTaskExecutor());
158
159
  }
159
160
  }
@@ -175,6 +176,7 @@ export function registerDaemonCommands(program) {
175
176
  model: process.env.GROK_MODEL || 'grok-3-latest',
176
177
  maxToolRounds: 20,
177
178
  jobTimeoutMs: 600000,
179
+ notepadDir: scheduler.notepadDir,
178
180
  runStore: RunStore.getInstance(),
179
181
  });
180
182
  scheduler.setTaskExecutor(bridge.createTaskExecutor());
@@ -129,6 +129,20 @@ export function registerEvolveCommands(program) {
129
129
  process.exitCode = 1;
130
130
  return;
131
131
  }
132
+ if (!v.passedAll || v.regressions.length || !/^[a-f0-9]{40,64}$/.test(v.sha)) {
133
+ logger.error('Variant is not eligible: missing verified commit, failed checks, or regressions.');
134
+ process.exitCode = 1;
135
+ return;
136
+ }
137
+ try {
138
+ if (git(['rev-parse', '--verify', `${v.branch}^{commit}`]).trim() !== v.sha)
139
+ throw new Error('Variant branch changed since evaluation');
140
+ }
141
+ catch (error) {
142
+ logger.error(error instanceof Error ? error.message : String(error));
143
+ process.exitCode = 1;
144
+ return;
145
+ }
132
146
  if (!options.confirm) {
133
147
  logger.info(`Preview: keep ${v.id} (${v.branch}) → merge into '${current}'.`);
134
148
  logger.info(` fitness=${v.score.toFixed(3)} passedAll=${v.passedAll} regressions=[${v.regressions.join(', ')}]`);
@@ -136,7 +150,7 @@ export function registerEvolveCommands(program) {
136
150
  return;
137
151
  }
138
152
  try {
139
- execFileSync('git', ['merge', '--no-ff', '-m', `evolve: keep ${v.id} (${v.detail ?? ''})`, v.branch], {
153
+ execFileSync('git', ['merge', '--no-ff', '-m', `evolve: keep ${v.id} (${v.detail ?? ''})`, v.sha], {
140
154
  stdio: 'inherit',
141
155
  });
142
156
  logger.info(`Merged ${v.branch} into '${current}'. Review the result and run your full validation.`);
@@ -155,6 +169,7 @@ export function registerEvolveCommands(program) {
155
169
  .option('--rounds <n>', 'Candidates per goal (fan-out), or max auto-weaknesses', '1')
156
170
  .option('--concurrency <n>', 'How many candidates to evaluate at once', '2')
157
171
  .option('--baseline <ref>', 'Baseline ref to branch from + rank against', 'main')
172
+ .option('--eval-task <ids...>', 'Opt in to whole-agent LLM eval tasks; default objective is the offline harness benchmark')
158
173
  .option('--model <model>', 'Model for the mutator agent + planner')
159
174
  .option('--model-bandit', 'Pick the mutator model per-cycle with the cost-aware UCB bandit (opt-in; overrides --model per cycle)')
160
175
  .option('--no-plan', 'Skip deliberate planning (use the ad-hoc mutator prompt)')
@@ -173,11 +188,12 @@ export function registerEvolveCommands(program) {
173
188
  }
174
189
  const { runEvolutionRound, agentMutator, formatEvolveRoundSummary } = await import('../../agent/self-improvement/evolution/evolution-engine.js');
175
190
  const { makeLlmVariantPlanner } = await import('../../agent/self-improvement/evolution/variant-planner.js');
176
- const { computeFitness, defaultDeterministicComponents } = await import('../../agent/self-improvement/evolution/variant-fitness.js');
177
- const baselineRef = options.baseline ?? 'main';
191
+ const { defaultDeterministicComponents, evalTasksComponent, harnessTasksComponent } = await import('../../agent/self-improvement/evolution/variant-fitness.js');
192
+ const { scoreReferenceInWorktree } = await import('../../agent/self-improvement/evolution/worktree-scorer.js');
193
+ const baselineRef = git(['rev-parse', '--verify', `${options.baseline ?? 'main'}^{commit}`]).trim();
178
194
  const rounds = Math.max(1, Number(options.rounds ?? '1') || 1);
179
195
  const concurrency = Math.max(1, Number(options.concurrency ?? '2') || 2);
180
- const components = defaultDeterministicComponents();
196
+ const components = [...defaultDeterministicComponents(true), options.evalTask ? evalTasksComponent(options.evalTask) : harnessTasksComponent()];
181
197
  const mutate = agentMutator(options.model ? { model: options.model } : {});
182
198
  // Deliberate planner (default on). --no-plan → a planner that returns null → mutator's ad-hoc prompt.
183
199
  const planner = options.plan === false
@@ -219,7 +235,7 @@ export function registerEvolveCommands(program) {
219
235
  logger.info(`Selected ${weaknesses.length} weakness(es): ${weaknesses.map((w) => w.goal.slice(0, 60)).join(' | ')}`);
220
236
  }
221
237
  logger.info(`Scoring baseline (${baselineRef})…`);
222
- const baseline = await computeFitness({ checkoutDir: process.cwd() }, components);
238
+ const baseline = (await scoreReferenceInWorktree(baselineRef, { components })).report;
223
239
  logger.info(` baseline fitness=${baseline.score.toFixed(3)}`);
224
240
  // Compounding: build each candidate on top of the current best elite (guarded by reachability
225
241
  // in the engine → falls back to baseline if that elite branch was pruned). Fitness stays vs baseline.
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerFleetCollaborationCommands(fleet: Command): void;
@@ -0,0 +1,57 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ export function registerFleetCollaborationCommands(fleet) {
3
+ const run = async (options, goal) => {
4
+ const controller = new AbortController();
5
+ const stop = () => controller.abort();
6
+ process.once('SIGINT', stop);
7
+ process.once('SIGTERM', stop);
8
+ try {
9
+ const { parseCollaborationConfig, runCollaboration } = await import('../../fleet/collaboration.js');
10
+ const config = parseCollaborationConfig(JSON.parse(await readFile(options.config, 'utf8')));
11
+ const report = await runCollaboration(config, {
12
+ goal, timeoutMs: Number(options.timeout), signal: controller.signal,
13
+ onProgress: options.json ? undefined : message => { process.stderr.write(`${message}\n`); },
14
+ });
15
+ if (options.json)
16
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
17
+ else {
18
+ process.stdout.write(`Fleet: ${report.status}\n`);
19
+ for (const peer of report.peers) {
20
+ process.stdout.write(`\n${peer.id} — ${peer.status}${peer.model ? ` (${peer.model})` : ''}\n${peer.error ?? peer.text ?? 'Authenticated and ready for peer.chat'}\n`);
21
+ }
22
+ if (report.synthesis)
23
+ process.stdout.write(`\nSynthesis (${report.synthesis.peer})\n${report.synthesis.text}\n`);
24
+ if (report.synthesisError)
25
+ process.stdout.write(`\nSynthesis: ${report.synthesisError}\n`);
26
+ }
27
+ if (report.status !== 'complete')
28
+ process.exitCode = 1;
29
+ }
30
+ catch (error) {
31
+ // Syntax errors can quote raw configuration, which may contain misplaced secrets.
32
+ const message = error instanceof SyntaxError ? 'Invalid fleet JSON configuration' : error instanceof Error ? error.message : 'Fleet operation failed';
33
+ if (options.json)
34
+ process.stdout.write(`${JSON.stringify({ status: 'failed', error: message })}\n`);
35
+ else
36
+ process.stderr.write(`${message}\n`);
37
+ process.exitCode = 1;
38
+ }
39
+ finally {
40
+ process.removeListener('SIGINT', stop);
41
+ process.removeListener('SIGTERM', stop);
42
+ }
43
+ };
44
+ fleet.command('check')
45
+ .description('Verify authenticated peers and chat readiness without calling an LLM')
46
+ .requiredOption('--config <file>', 'Fleet JSON configuration (token environment variable names only)')
47
+ .option('--timeout <ms>', 'Per-request timeout, 1000–300000 ms', '10000')
48
+ .option('--json', 'Structured report')
49
+ .action((options) => run(options));
50
+ fleet.command('collaborate <goal>')
51
+ .description('Collect parallel peer reviews, then synthesize them (no remote file edits)')
52
+ .requiredOption('--config <file>', 'Fleet JSON configuration (2–8 peers)')
53
+ .option('--timeout <ms>', 'Per-request timeout, 1000–300000 ms', '120000')
54
+ .option('--json', 'Structured report')
55
+ .action((goal, options) => run(options, goal));
56
+ }
57
+ //# sourceMappingURL=fleet-collaboration-commands.js.map
@@ -4,6 +4,9 @@
4
4
  * These commands expose the same dispatch profile policy decisions used by
5
5
  * Cowork/Fleet so an operator can inspect a route before running it.
6
6
  */
7
+ import { registerFleetMissionCommands } from './fleet-mission-commands.js';
8
+ import { registerFleetCollaborationCommands } from './fleet-collaboration-commands.js';
9
+ import { registerFleetRoomsCommands } from './fleet-rooms-commands.js';
7
10
  import { createTokenCommand, registerFleetTokenCommand } from '../token.js';
8
11
  import { DEFAULT_DISPATCH_POLICY_PREVIEW_TOOLS, FLEET_DISPATCH_PROFILES, FLEET_DISPATCH_PROFILE_GUIDANCE, buildHermesToolsetDescriptor, getDispatchToolPolicy, normalizeDispatchProfile, previewDispatchToolDecisions, } from '../../fleet/dispatch-profile.js';
9
12
  function resolveFleetServerUrl(serverUrl) {
@@ -68,6 +71,32 @@ export function registerFleetCommands(program) {
68
71
  const fleet = program
69
72
  .command('fleet')
70
73
  .description('Inspect Fleet routing, toolsets, and dispatch policy decisions');
74
+ registerFleetMissionCommands(fleet);
75
+ registerFleetRoomsCommands(fleet);
76
+ registerFleetCollaborationCommands(fleet);
77
+ fleet.command('supervise <manifest> <operation>')
78
+ .description('Run a fixed local fleet operation through the Code Buddy harness')
79
+ .option('--json', 'Output the structured harness result')
80
+ .action(async (manifest, operation, options) => {
81
+ const controller = new AbortController();
82
+ const stop = () => controller.abort();
83
+ process.once('SIGINT', stop);
84
+ process.once('SIGTERM', stop);
85
+ try {
86
+ const { runSupervisor } = await import('../../harness/fleet-supervisor.js');
87
+ const result = await runSupervisor(manifest, operation, { signal: controller.signal });
88
+ console.log(options.json ? JSON.stringify({ kind: 'fleet_supervisor_result', operation, ...result }) : result.output ?? result.error ?? '');
89
+ if (!result.success)
90
+ process.exitCode = 1;
91
+ }
92
+ catch (error) {
93
+ printFleetServerError(error);
94
+ }
95
+ finally {
96
+ process.removeListener('SIGINT', stop);
97
+ process.removeListener('SIGTERM', stop);
98
+ }
99
+ });
71
100
  fleet
72
101
  .command('status')
73
102
  .description('Show Fleet status from the configured Code Buddy server')
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ /** All paths/manifests are operator input. No provider or server is booted. */
3
+ export declare function registerFleetMissionCommands(fleet: Command): void;
@@ -0,0 +1,72 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { MissionStore } from '../../harness/mission-store.js';
5
+ import { runMission } from '../../harness/mission-runner.js';
6
+ import { parseSupervisorManifest } from '../../harness/fleet-supervisor.js';
7
+ /** All paths/manifests are operator input. No provider or server is booted. */
8
+ export function registerFleetMissionCommands(fleet) {
9
+ const group = fleet.command('mission').description('Durable local mission ownership, handoff and results');
10
+ const output = (action) => async (...args) => {
11
+ try {
12
+ console.log(JSON.stringify(await action(...args.filter(a => typeof a === 'string'))));
13
+ }
14
+ catch (error) {
15
+ console.error(error instanceof Error ? error.message : String(error));
16
+ process.exitCode = 1;
17
+ }
18
+ };
19
+ group.command('list <store>')
20
+ .description('List mission metadata, with bounded pages and unreadable-record notices')
21
+ .option('--limit <count>', 'Maximum records examined in this page (1..100)', '25')
22
+ .option('--cursor <hash>', 'Continue after the previous page nextCursor')
23
+ .action((directory, options) => {
24
+ try {
25
+ console.log(JSON.stringify(new MissionStore(directory).list({ limit: Number(options.limit), cursor: options.cursor })));
26
+ }
27
+ catch (error) {
28
+ console.error(error instanceof Error ? error.message : String(error));
29
+ process.exitCode = 1;
30
+ }
31
+ });
32
+ group.command('create <store> <id> <manifest> <operation>').action(output(async (directory, id, manifestPath, name) => {
33
+ const file = path.resolve(manifestPath);
34
+ const manifest = parseSupervisorManifest(JSON.parse(await fs.readFile(file, 'utf8')), path.dirname(file), 43200000);
35
+ const op = manifest.operations.get(name);
36
+ if (!op)
37
+ throw new Error('Unknown manifest operation');
38
+ return new MissionStore(directory).create(id, { command: op.command, args: op.args, timeoutMs: op.timeoutMs, workspace: manifest.workspace });
39
+ }));
40
+ group.command('show <store> <id>').action(output((directory, id) => new MissionStore(directory).get(id)));
41
+ group.command('claim <store> <id> <owner>').action(output((directory, id, owner) => new MissionStore(directory).claim(id, owner)));
42
+ group.command('renew <store> <id> <owner> <generation>').action(output((directory, id, owner, generation) => new MissionStore(directory).renew(id, { owner: owner, generation: generation })));
43
+ group.command('run <store> <id> <owner> <generation>').action(output(async (directory, id, owner, generation) => {
44
+ const controller = new AbortController();
45
+ const stop = () => controller.abort();
46
+ process.once('SIGINT', stop);
47
+ process.once('SIGTERM', stop);
48
+ try {
49
+ const mission = await runMission(new MissionStore(directory), id, { owner: owner, generation: generation }, controller.signal);
50
+ if (!mission.result?.success)
51
+ process.exitCode = 1;
52
+ return mission;
53
+ }
54
+ finally {
55
+ process.removeListener('SIGINT', stop);
56
+ process.removeListener('SIGTERM', stop);
57
+ }
58
+ }));
59
+ group.command('handoff <store> <id> <owner> <generation> <capsule>').action(output(async (directory, id, owner, generation, capsule) => new MissionStore(directory).handoff(id, { owner: owner, generation: generation }, JSON.parse(await fs.readFile(capsule, 'utf8')))));
60
+ group.command('reconcile <store> <id> <generation> <resolution> <evidence>').action(output((directory, id, generation, resolution, evidence) => new MissionStore(directory).reconcile(id, generation, resolution, evidence)));
61
+ group.command('ack <store> <id> <consumer>').action(output((directory, id, consumer) => new MissionStore(directory).acknowledge(id, consumer)));
62
+ const head = (workspace) => execFileSync('git', ['rev-parse', '--verify', 'HEAD'], { cwd: workspace, encoding: 'utf8', timeout: 5000 }).trim();
63
+ group.command('submit <store> <id> <owner> <generation>').action(output((directory, id, owner, generation) => {
64
+ const store = new MissionStore(directory);
65
+ return store.submit(id, { owner: owner, generation: generation }, head(store.get(id).operation.workspace));
66
+ }));
67
+ group.command('approve <store> <id> <reviewer> <commit>').action(output((directory, id, reviewer, commit) => {
68
+ const store = new MissionStore(directory);
69
+ return store.approve(id, reviewer, commit);
70
+ }));
71
+ }
72
+ //# sourceMappingURL=fleet-mission-commands.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `buddy fleet rooms` — persistent signed messages between fleet members.
3
+ *
4
+ * Messages are data: `read` and `tail` print sanitized text and never run
5
+ * anything. Credentials come from the environment by default
6
+ * (`CODEBUDDY_FLEET_ROOMS_URL`, `CODEBUDDY_FLEET_API_KEY` / `CODEBUDDY_FLEET_TOKEN`);
7
+ * the member secret stays in its 0600 identity file and is never printed.
8
+ */
9
+ import type { Command } from 'commander';
10
+ export declare function registerFleetRoomsCommands(fleet: Command): void;
@@ -0,0 +1,289 @@
1
+ /**
2
+ * `buddy fleet rooms` — persistent signed messages between fleet members.
3
+ *
4
+ * Messages are data: `read` and `tail` print sanitized text and never run
5
+ * anything. Credentials come from the environment by default
6
+ * (`CODEBUDDY_FLEET_ROOMS_URL`, `CODEBUDDY_FLEET_API_KEY` / `CODEBUDDY_FLEET_TOKEN`);
7
+ * the member secret stays in its 0600 identity file and is never printed.
8
+ */
9
+ const collect = (value, previous) => [...previous, value];
10
+ function addConnectionOptions(command) {
11
+ return command
12
+ .option('--url <url>', 'hub WebSocket URL (default $CODEBUDDY_FLEET_ROOMS_URL)')
13
+ .option('--api-key <key>', 'hub API key with fleet:listen (prefer $CODEBUDDY_FLEET_API_KEY)')
14
+ .option('--jwt <token>', 'hub JWT with fleet:listen (prefer $CODEBUDDY_FLEET_TOKEN)')
15
+ .option('--audience <url>', 'hub URL to sign for when reached through a proxy')
16
+ .option('--identity <file>', 'member identity file (default $CODEBUDDY_FLEET_ROOMS_IDENTITY)')
17
+ .option('--json', 'machine-readable output');
18
+ }
19
+ function fail(error) {
20
+ console.error(error instanceof Error ? error.message : String(error));
21
+ process.exitCode = 1;
22
+ }
23
+ async function connect(options) {
24
+ const url = options.url ?? process.env.CODEBUDDY_FLEET_ROOMS_URL;
25
+ if (!url)
26
+ throw new Error('hub URL required: --url or CODEBUDDY_FLEET_ROOMS_URL');
27
+ const apiKey = options.apiKey ?? process.env.CODEBUDDY_FLEET_API_KEY;
28
+ const jwt = options.jwt ?? process.env.CODEBUDDY_FLEET_TOKEN;
29
+ const [{ FleetRoomClient }, { loadRoomIdentity }] = await Promise.all([
30
+ import('../../fleet/rooms/room-client.js'),
31
+ import('../../fleet/rooms/room-identity.js'),
32
+ ]);
33
+ const identity = loadRoomIdentity(options.identity);
34
+ const client = new FleetRoomClient({
35
+ url,
36
+ secretKey: identity.secretKey,
37
+ ...(apiKey ? { apiKey } : jwt ? { jwt } : {}),
38
+ ...(options.audience ? { audience: options.audience } : {}),
39
+ });
40
+ try {
41
+ const session = await client.connect();
42
+ return { client, session };
43
+ }
44
+ catch (error) {
45
+ await client.close();
46
+ throw error;
47
+ }
48
+ }
49
+ function toJson(message) {
50
+ return {
51
+ seq: message.seq,
52
+ id: message.event.id,
53
+ room: message.room,
54
+ author: message.author,
55
+ createdAt: message.event.created_at,
56
+ mentions: message.mentions,
57
+ ...(message.thread ? { thread: message.thread } : {}),
58
+ text: message.text,
59
+ };
60
+ }
61
+ function printMessage(message) {
62
+ const when = new Date(message.event.created_at * 1000).toISOString();
63
+ const reply = message.thread ? ` ↳${message.thread.parent.slice(0, 8)}` : '';
64
+ console.log(`#${message.seq} ${when} ${message.author.slice(0, 12)}${reply} [${message.event.id.slice(0, 8)}]\n ${message.text.replace(/\n/g, '\n ')}`);
65
+ }
66
+ async function readCursor(file, room) {
67
+ const fs = await import('node:fs/promises');
68
+ let raw;
69
+ try {
70
+ raw = await fs.readFile(file, 'utf8');
71
+ }
72
+ catch (error) {
73
+ if (error.code === 'ENOENT')
74
+ return undefined;
75
+ throw error;
76
+ }
77
+ const parsed = JSON.parse(raw);
78
+ if (parsed.version !== 1 || parsed.room !== room || typeof parsed.storeId !== 'string' ||
79
+ !Number.isSafeInteger(parsed.throughSeq) || parsed.throughSeq < 0) {
80
+ throw new Error(`cursor file ${file} does not belong to room ${room}`);
81
+ }
82
+ return { storeId: parsed.storeId, throughSeq: parsed.throughSeq };
83
+ }
84
+ async function writeCursor(file, room, cursor) {
85
+ const { writeJsonAtomic } = await import('../../utils/atomic-write.js');
86
+ await writeJsonAtomic(file, { version: 1, room, storeId: cursor.storeId, throughSeq: cursor.throughSeq });
87
+ }
88
+ function filterFor(room, options, pubkey) {
89
+ const filter = { '#h': [room], kinds: [9] };
90
+ if (options.mentionsMe)
91
+ filter['#p'] = [pubkey];
92
+ if (options.limit !== undefined) {
93
+ const limit = Number(options.limit);
94
+ if (!Number.isSafeInteger(limit) || limit < 0 || limit > 500)
95
+ throw new Error('--limit must be an integer between 0 and 500');
96
+ filter.limit = limit;
97
+ }
98
+ return filter;
99
+ }
100
+ export function registerFleetRoomsCommands(fleet) {
101
+ const rooms = fleet
102
+ .command('rooms')
103
+ .description('Persistent signed messages between fleet members (read as data, never executed)');
104
+ const identity = rooms.command('identity').description('Member key used to sign room messages');
105
+ identity.command('init')
106
+ .description('Create this member key (0600 file); prints only the public key')
107
+ .option('--identity <file>', 'identity file (default $CODEBUDDY_FLEET_ROOMS_IDENTITY)')
108
+ .option('--force', 'rotate: keep the old file aside and create a new key')
109
+ .option('--json', 'machine-readable output')
110
+ .action(async (options) => {
111
+ try {
112
+ const { createRoomIdentity } = await import('../../fleet/rooms/room-identity.js');
113
+ const created = createRoomIdentity(options.identity, { force: options.force === true });
114
+ console.log(options.json
115
+ ? JSON.stringify({ publicKey: created.publicKey, path: created.path })
116
+ : `Room key created: ${created.publicKey}\nAsk the hub operator to add it to rooms.json.`);
117
+ }
118
+ catch (error) {
119
+ fail(error);
120
+ }
121
+ });
122
+ identity.command('show')
123
+ .description('Print this member public key')
124
+ .option('--identity <file>', 'identity file (default $CODEBUDDY_FLEET_ROOMS_IDENTITY)')
125
+ .option('--json', 'machine-readable output')
126
+ .action(async (options) => {
127
+ try {
128
+ const { loadRoomIdentity } = await import('../../fleet/rooms/room-identity.js');
129
+ const loaded = loadRoomIdentity(options.identity);
130
+ console.log(options.json ? JSON.stringify({ publicKey: loaded.publicKey, path: loaded.path }) : loaded.publicKey);
131
+ }
132
+ catch (error) {
133
+ fail(error);
134
+ }
135
+ });
136
+ addConnectionOptions(rooms.command('post <room> <text...>'))
137
+ .description('Sign and publish a message; returns once the hub stored it durably')
138
+ .option('--mention <pubkey>', 'notify a member (repeatable)', collect, [])
139
+ .option('--reply-to <id>', 'answer this message')
140
+ .option('--reply-root <id>', 'thread root when answering a nested reply')
141
+ .action(async (room, words, options) => {
142
+ let client;
143
+ try {
144
+ const connected = await connect(options);
145
+ client = connected.client;
146
+ const replyTo = options.replyTo
147
+ ? { root: options.replyRoot ?? options.replyTo, parent: options.replyTo }
148
+ : undefined;
149
+ const ack = await connected.client.publish({
150
+ room,
151
+ content: words.join(' '),
152
+ mentions: options.mention,
153
+ ...(replyTo ? { replyTo } : {}),
154
+ });
155
+ console.log(options.json ? JSON.stringify(ack) : `${ack.duplicate ? 'Already stored' : 'Stored'} #${ack.seq} ${ack.id}`);
156
+ }
157
+ catch (error) {
158
+ fail(error);
159
+ }
160
+ finally {
161
+ await client?.close();
162
+ }
163
+ });
164
+ addConnectionOptions(rooms.command('read <room>'))
165
+ .description('Print stored messages; with --cursor, only those after the saved cursor')
166
+ .option('--limit <n>', 'newest messages to show without a cursor (0..500, default 100)')
167
+ .option('--mentions-me', 'only messages mentioning this member')
168
+ .option('--cursor <file>', 'resume after, then update, the cursor saved in this file')
169
+ .action(async (room, options) => {
170
+ let client;
171
+ try {
172
+ const connected = await connect(options);
173
+ client = connected.client;
174
+ const previous = options.cursor ? await readCursor(options.cursor, room) : undefined;
175
+ const result = await connected.client.fetch([filterFor(room, options, connected.client.pubkey)], previous);
176
+ if (options.cursor)
177
+ await writeCursor(options.cursor, room, result.cursor);
178
+ if (options.json) {
179
+ console.log(JSON.stringify({
180
+ messages: result.messages.map(toJson),
181
+ cursor: result.cursor,
182
+ gap: result.gap,
183
+ epochChanged: result.epochChanged,
184
+ truncated: result.truncated,
185
+ }));
186
+ }
187
+ else {
188
+ if (result.gap)
189
+ console.log('! some messages after your cursor were evicted by retention');
190
+ if (result.epochChanged)
191
+ console.log('! the hub ledger changed generation; showing current history');
192
+ if (result.truncated)
193
+ console.log('! more messages remain; read again with --cursor to continue');
194
+ for (const message of result.messages)
195
+ printMessage(message);
196
+ if (result.messages.length === 0)
197
+ console.log('(no messages)');
198
+ }
199
+ }
200
+ catch (error) {
201
+ fail(error);
202
+ }
203
+ finally {
204
+ await client?.close();
205
+ }
206
+ });
207
+ addConnectionOptions(rooms.command('tail <room>'))
208
+ .description('Follow a room live (reconnects and catches up automatically) until Ctrl+C')
209
+ .option('--mentions-me', 'only messages mentioning this member')
210
+ .option('--cursor <file>', 'resume after, and keep updating, the cursor saved in this file')
211
+ .action(async (room, options) => {
212
+ let client;
213
+ let finish = () => { };
214
+ const done = new Promise(resolve => { finish = resolve; });
215
+ let pendingWrite;
216
+ let latestCursor;
217
+ let caughtUp = false;
218
+ const persist = (cursor) => {
219
+ if (!options.cursor)
220
+ return;
221
+ latestCursor = cursor;
222
+ if (pendingWrite)
223
+ return;
224
+ const file = options.cursor;
225
+ pendingWrite = (async () => {
226
+ while (latestCursor) {
227
+ const next = latestCursor;
228
+ latestCursor = undefined;
229
+ await writeCursor(file, room, next);
230
+ }
231
+ })().catch(error => { fail(error); finish(); }).finally(() => {
232
+ pendingWrite = undefined;
233
+ if (latestCursor)
234
+ persist(latestCursor);
235
+ });
236
+ };
237
+ const disconnected = () => { caughtUp = false; };
238
+ const exhausted = () => { fail(new Error('Room reconnection attempts exhausted')); finish(); };
239
+ const reconnectError = (error) => {
240
+ if (error.code === 'ROOM_AUTH_REFUSED') {
241
+ fail(error);
242
+ finish();
243
+ }
244
+ };
245
+ try {
246
+ client = (await connect(options)).client;
247
+ const previous = options.cursor ? await readCursor(options.cursor, room) : undefined;
248
+ process.once('SIGINT', finish);
249
+ process.once('SIGTERM', finish);
250
+ client.on('disconnected', disconnected);
251
+ client.on('reconnect-exhausted', exhausted);
252
+ client.on('reconnect-error', reconnectError);
253
+ const handle = client.subscribe('tail', [filterFor(room, { ...options, limit: '20' }, client.pubkey)], {
254
+ onMessage: (message) => {
255
+ if (options.json)
256
+ console.log(JSON.stringify(toJson(message)));
257
+ else
258
+ printMessage(message);
259
+ const cursor = handle.cursor();
260
+ if (caughtUp && cursor)
261
+ persist(cursor);
262
+ },
263
+ onEose: (info) => {
264
+ caughtUp = true;
265
+ persist({ storeId: info.storeId, throughSeq: info.throughSeq });
266
+ },
267
+ onClosed: (message) => {
268
+ fail(new Error(message));
269
+ finish();
270
+ },
271
+ }, previous);
272
+ await done;
273
+ }
274
+ catch (error) {
275
+ fail(error);
276
+ }
277
+ finally {
278
+ process.removeListener('SIGINT', finish);
279
+ process.removeListener('SIGTERM', finish);
280
+ client?.off('disconnected', disconnected);
281
+ client?.off('reconnect-exhausted', exhausted);
282
+ client?.off('reconnect-error', reconnectError);
283
+ await client?.close();
284
+ while (pendingWrite)
285
+ await pendingWrite;
286
+ }
287
+ });
288
+ }
289
+ //# sourceMappingURL=fleet-rooms-commands.js.map