@phuetz/code-buddy 2.0.0 → 2.2.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 (421) hide show
  1. package/README.fr.md +56 -0
  2. package/README.md +74 -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 +176 -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.d.ts +7 -0
  13. package/dist/agent/execution/tool-selection-strategy.js +32 -2
  14. package/dist/agent/facades/session-facade.d.ts +0 -6
  15. package/dist/agent/facades/session-facade.js +0 -45
  16. package/dist/agent/hermes-claw-migrate.d.ts +0 -21
  17. package/dist/agent/hermes-claw-migrate.js +9 -4
  18. package/dist/agent/hermes-protocol-gateways.js +8 -1
  19. package/dist/agent/repo-profiler.js +17 -4
  20. package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
  21. package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
  22. package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
  23. package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
  24. package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
  25. package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
  26. package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
  27. package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
  28. package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
  29. package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
  30. package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
  31. package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
  32. package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
  33. package/dist/agent/self-improvement/proposal-store.js +79 -17
  34. package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
  35. package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
  36. package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
  37. package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
  38. package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
  39. package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
  40. package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
  41. package/dist/agent/self-improvement/skill-engine.js +385 -13
  42. package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
  43. package/dist/agent/self-improvement/skill-gate.js +17 -2
  44. package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
  45. package/dist/agent/self-improvement/skill-mutator.js +51 -15
  46. package/dist/agent/self-improvement/skill-types.d.ts +2 -1
  47. package/dist/agent/self-improvement/types.d.ts +11 -0
  48. package/dist/agent/tool-handler.d.ts +1 -1
  49. package/dist/agent/tool-handler.js +28 -9
  50. package/dist/auth/profile-manager.js +1 -1
  51. package/dist/channels/companion-channel-turn.d.ts +53 -4
  52. package/dist/channels/companion-channel-turn.js +218 -16
  53. package/dist/channels/slash-parity.d.ts +4 -6
  54. package/dist/channels/slash-parity.js +9 -44
  55. package/dist/cli/session-commands.d.ts +9 -0
  56. package/dist/cli/session-commands.js +45 -5
  57. package/dist/cli/session-picker.d.ts +43 -0
  58. package/dist/cli/session-picker.js +128 -0
  59. package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
  60. package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
  61. package/dist/codebuddy/fleet-tool-defs.d.ts +2 -0
  62. package/dist/codebuddy/fleet-tool-defs.js +55 -0
  63. package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
  64. package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
  65. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
  66. package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
  67. package/dist/codebuddy/providers/provider-openai-compat.d.ts +7 -0
  68. package/dist/codebuddy/providers/provider-openai-compat.js +29 -1
  69. package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
  70. package/dist/codebuddy/ragchat-tool-defs.js +19 -0
  71. package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
  72. package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
  73. package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
  74. package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
  75. package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
  76. package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
  77. package/dist/codebuddy/tools.d.ts +0 -6
  78. package/dist/codebuddy/tools.js +25 -3
  79. package/dist/commands/cli/daemon-commands.js +3 -1
  80. package/dist/commands/cli/evolve-command.js +21 -5
  81. package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
  82. package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
  83. package/dist/commands/cli/fleet-commands.js +29 -0
  84. package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
  85. package/dist/commands/cli/fleet-mission-commands.js +72 -0
  86. package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
  87. package/dist/commands/cli/fleet-rooms-commands.js +289 -0
  88. package/dist/commands/cli/tools-commands.js +2 -1
  89. package/dist/commands/cli/triage-command.d.ts +8 -0
  90. package/dist/commands/cli/triage-command.js +72 -0
  91. package/dist/commands/cli/utility-commands.js +25 -1
  92. package/dist/commands/client-dispatcher.d.ts +7 -0
  93. package/dist/commands/client-dispatcher.js +73 -21
  94. package/dist/commands/cron-cli/index.d.ts +3 -1
  95. package/dist/commands/cron-cli/index.js +25 -0
  96. package/dist/commands/device-auth.d.ts +10 -0
  97. package/dist/commands/device-auth.js +91 -0
  98. package/dist/commands/enhanced-command-handler.d.ts +9 -0
  99. package/dist/commands/enhanced-command-handler.js +28 -6
  100. package/dist/commands/handlers/agent-handlers.d.ts +1 -1
  101. package/dist/commands/handlers/agent-handlers.js +4 -4
  102. package/dist/commands/handlers/channel-handlers.js +53 -2
  103. package/dist/commands/handlers/export-handlers.d.ts +3 -2
  104. package/dist/commands/handlers/export-handlers.js +55 -28
  105. package/dist/commands/handlers/extra-handlers.js +5 -1
  106. package/dist/commands/handlers/grill-me-handler.js +26 -18
  107. package/dist/commands/handlers/memory-handlers.d.ts +6 -2
  108. package/dist/commands/handlers/memory-handlers.js +35 -15
  109. package/dist/commands/handlers/missing-handlers.d.ts +2 -2
  110. package/dist/commands/handlers/missing-handlers.js +12 -4
  111. package/dist/commands/handlers/resources-handler.d.ts +12 -0
  112. package/dist/commands/handlers/resources-handler.js +52 -0
  113. package/dist/commands/handlers/starter-handlers.d.ts +1 -1
  114. package/dist/commands/handlers/starter-handlers.js +32 -2
  115. package/dist/commands/handlers/switch-handler.d.ts +1 -1
  116. package/dist/commands/handlers/switch-handler.js +15 -0
  117. package/dist/commands/handlers/test-handlers.js +4 -86
  118. package/dist/commands/handlers/ui-handlers.js +11 -5
  119. package/dist/commands/login-chatgpt.d.ts +8 -0
  120. package/dist/commands/login-chatgpt.js +17 -0
  121. package/dist/commands/mcp-import.d.ts +11 -0
  122. package/dist/commands/mcp-import.js +67 -0
  123. package/dist/commands/mcp-invoke.d.ts +2 -0
  124. package/dist/commands/mcp-invoke.js +68 -0
  125. package/dist/commands/mcp.d.ts +13 -0
  126. package/dist/commands/mcp.js +66 -21
  127. package/dist/commands/pipeline.d.ts +7 -1
  128. package/dist/commands/pipeline.js +104 -7
  129. package/dist/commands/resources.d.ts +2 -0
  130. package/dist/commands/resources.js +64 -0
  131. package/dist/commands/skills-cli/index.js +35 -12
  132. package/dist/commands/slash/builtin-commands.js +16 -3
  133. package/dist/commands/slash/index.d.ts +3 -1
  134. package/dist/commands/slash/index.js +2 -0
  135. package/dist/commands/slash/surfaces.d.ts +53 -0
  136. package/dist/commands/slash/surfaces.js +177 -0
  137. package/dist/commands/slash/types.d.ts +17 -0
  138. package/dist/commands/try.d.ts +1 -1
  139. package/dist/commands/try.js +1 -1
  140. package/dist/companion/companion-identity.d.ts +43 -0
  141. package/dist/companion/companion-identity.js +136 -0
  142. package/dist/companion/companion-toolset.d.ts +66 -0
  143. package/dist/companion/companion-toolset.js +368 -0
  144. package/dist/companion/companion-turn.d.ts +10 -2
  145. package/dist/companion/companion-turn.js +38 -1
  146. package/dist/config/code-exec-policy.d.ts +25 -0
  147. package/dist/config/code-exec-policy.js +39 -0
  148. package/dist/config/model-tools.d.ts +9 -0
  149. package/dist/config/model-tools.js +103 -0
  150. package/dist/context/context-manager-v2.js +2 -2
  151. package/dist/context/lm-resizer-compressor.js +3 -2
  152. package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
  153. package/dist/context/lm-resizer-diagnostics.js +30 -0
  154. package/dist/context/tool-output-masking.js +3 -4
  155. package/dist/curator/curator.d.ts +6 -0
  156. package/dist/curator/curator.js +53 -3
  157. package/dist/daemon/cron-agent-bridge.d.ts +6 -0
  158. package/dist/daemon/cron-agent-bridge.js +31 -4
  159. package/dist/doctor/index.d.ts +11 -2
  160. package/dist/doctor/index.js +84 -21
  161. package/dist/doctor/integrations.d.ts +197 -0
  162. package/dist/doctor/integrations.js +241 -0
  163. package/dist/doctor/triage.d.ts +108 -0
  164. package/dist/doctor/triage.js +304 -0
  165. package/dist/errors/crash-handler.js +2 -0
  166. package/dist/fleet/collaboration.d.ts +44 -0
  167. package/dist/fleet/collaboration.js +170 -0
  168. package/dist/fleet/fleet-listener.d.ts +1 -0
  169. package/dist/fleet/fleet-listener.js +87 -46
  170. package/dist/fleet/fleet-registry.d.ts +2 -0
  171. package/dist/fleet/resource-catalog.d.ts +245 -0
  172. package/dist/fleet/resource-catalog.js +211 -0
  173. package/dist/fleet/rooms/room-access.d.ts +138 -0
  174. package/dist/fleet/rooms/room-access.js +196 -0
  175. package/dist/fleet/rooms/room-client.d.ts +158 -0
  176. package/dist/fleet/rooms/room-client.js +685 -0
  177. package/dist/fleet/rooms/room-event.d.ts +139 -0
  178. package/dist/fleet/rooms/room-event.js +330 -0
  179. package/dist/fleet/rooms/room-filter.d.ts +48 -0
  180. package/dist/fleet/rooms/room-filter.js +152 -0
  181. package/dist/fleet/rooms/room-hub.d.ts +154 -0
  182. package/dist/fleet/rooms/room-hub.js +363 -0
  183. package/dist/fleet/rooms/room-identity.d.ts +26 -0
  184. package/dist/fleet/rooms/room-identity.js +82 -0
  185. package/dist/fleet/rooms/room-lock.d.ts +34 -0
  186. package/dist/fleet/rooms/room-lock.js +180 -0
  187. package/dist/fleet/rooms/room-observations.d.ts +7 -0
  188. package/dist/fleet/rooms/room-observations.js +61 -0
  189. package/dist/fleet/rooms/room-server.d.ts +28 -0
  190. package/dist/fleet/rooms/room-server.js +75 -0
  191. package/dist/fleet/rooms/room-store.d.ts +119 -0
  192. package/dist/fleet/rooms/room-store.js +448 -0
  193. package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
  194. package/dist/fleet/rooms/room-ws-bridge.js +137 -0
  195. package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
  196. package/dist/fleet/sensory-mission-bridge.js +136 -0
  197. package/dist/git/worktree-sessions.js +1 -0
  198. package/dist/harness/contract.d.ts +4 -4
  199. package/dist/harness/fleet-supervisor.d.ts +16 -0
  200. package/dist/harness/fleet-supervisor.js +64 -0
  201. package/dist/harness/index.d.ts +4 -0
  202. package/dist/harness/index.js +4 -0
  203. package/dist/harness/mission-runner.d.ts +3 -0
  204. package/dist/harness/mission-runner.js +53 -0
  205. package/dist/harness/mission-store.d.ts +93 -0
  206. package/dist/harness/mission-store.js +297 -0
  207. package/dist/harness/tool-harness.d.ts +56 -0
  208. package/dist/harness/tool-harness.js +153 -0
  209. package/dist/hooks/use-enhanced-input.js +48 -4
  210. package/dist/hooks/use-input-handler.d.ts +1 -0
  211. package/dist/hooks/use-input-handler.js +82 -22
  212. package/dist/hooks/use-input-history.js +1 -0
  213. package/dist/identity/lisa-introspection.js +5 -2
  214. package/dist/identity/operational-self-model.d.ts +2 -0
  215. package/dist/identity/operational-self-model.js +4 -0
  216. package/dist/index.js +52 -6
  217. package/dist/mcp/client.d.ts +11 -0
  218. package/dist/mcp/client.js +68 -3
  219. package/dist/mcp/config.d.ts +20 -0
  220. package/dist/mcp/config.js +121 -8
  221. package/dist/mcp/import-normalize.d.ts +12 -0
  222. package/dist/mcp/import-normalize.js +114 -0
  223. package/dist/mcp/mcp-oauth-constants.d.ts +8 -0
  224. package/dist/mcp/mcp-oauth-constants.js +9 -0
  225. package/dist/mcp/mcp-oauth-provider.d.ts +80 -0
  226. package/dist/mcp/mcp-oauth-provider.js +241 -0
  227. package/dist/mcp/mcp-oauth.d.ts +60 -1
  228. package/dist/mcp/mcp-oauth.js +241 -65
  229. package/dist/mcp/transports.d.ts +14 -5
  230. package/dist/mcp/transports.js +112 -101
  231. package/dist/mcp/types.d.ts +5 -0
  232. package/dist/media/comfyui-recipe-contract.d.ts +7 -7
  233. package/dist/memory/facts-memory.d.ts +11 -1
  234. package/dist/memory/facts-memory.js +47 -8
  235. package/dist/memory/memory-candidate-queue.d.ts +2 -2
  236. package/dist/memory/memory-candidate-queue.js +6 -6
  237. package/dist/memory/persistent-memory.d.ts +13 -8
  238. package/dist/memory/persistent-memory.js +77 -30
  239. package/dist/nodes/device-node.js +5 -9
  240. package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
  241. package/dist/observability/run-event-writer.d.ts +21 -0
  242. package/dist/observability/run-event-writer.js +81 -0
  243. package/dist/observability/run-store.d.ts +5 -0
  244. package/dist/observability/run-store.js +18 -13
  245. package/dist/observability/run-trajectory.js +1 -1
  246. package/dist/optimization/prompt-cache.d.ts +19 -2
  247. package/dist/optimization/prompt-cache.js +41 -6
  248. package/dist/orchestration/orchestrator.d.ts +21 -0
  249. package/dist/orchestration/orchestrator.js +122 -15
  250. package/dist/persistence/session-content.d.ts +8 -0
  251. package/dist/persistence/session-content.js +76 -0
  252. package/dist/persistence/session-handoff.d.ts +64 -0
  253. package/dist/persistence/session-handoff.js +80 -0
  254. package/dist/persistence/session-history.d.ts +4 -0
  255. package/dist/persistence/session-history.js +38 -0
  256. package/dist/persistence/session-store.d.ts +9 -0
  257. package/dist/persistence/session-store.js +48 -20
  258. package/dist/personas/persona-manager.js +17 -1
  259. package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
  260. package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
  261. package/dist/plugins/plugin-manager.js +10 -1
  262. package/dist/prompts/prompt-manager.js +11 -4
  263. package/dist/prompts/system-base.js +6 -2
  264. package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
  265. package/dist/protocols/a2a/codebuddy-executor.js +9 -8
  266. package/dist/protocols/a2a/index.d.ts +1 -1
  267. package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
  268. package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
  269. package/dist/protocols/a2a/peer-config.d.ts +20 -0
  270. package/dist/protocols/a2a/peer-config.js +55 -0
  271. package/dist/providers/codex-oauth.js +30 -6
  272. package/dist/providers/model-provider-compat.js +2 -1
  273. package/dist/sandbox/os-sandbox.d.ts +1 -1
  274. package/dist/scheduler/cron-scheduler.d.ts +15 -2
  275. package/dist/scheduler/cron-scheduler.js +35 -4
  276. package/dist/scheduler/job-notepad.d.ts +69 -0
  277. package/dist/scheduler/job-notepad.js +339 -0
  278. package/dist/security/audit-logger.d.ts +1 -1
  279. package/dist/security/compute-confinement.d.ts +4 -0
  280. package/dist/security/compute-confinement.js +89 -0
  281. package/dist/security/session-encryption.d.ts +4 -0
  282. package/dist/security/session-encryption.js +31 -0
  283. package/dist/sensory/voice-loop.d.ts +17 -0
  284. package/dist/sensory/voice-loop.js +72 -15
  285. package/dist/server/auth/device-session-context.d.ts +9 -0
  286. package/dist/server/auth/device-session-context.js +13 -0
  287. package/dist/server/auth/device-store.d.ts +89 -0
  288. package/dist/server/auth/device-store.js +261 -0
  289. package/dist/server/auth/device-token.d.ts +3 -0
  290. package/dist/server/auth/device-token.js +6 -0
  291. package/dist/server/auth/jwt.js +4 -0
  292. package/dist/server/index.d.ts +0 -10
  293. package/dist/server/index.js +28 -0
  294. package/dist/server/middleware/auth.js +3 -2
  295. package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
  296. package/dist/server/routes/a2a-jsonrpc.js +66 -0
  297. package/dist/server/routes/a2a-protocol.d.ts +12 -0
  298. package/dist/server/routes/a2a-protocol.js +1 -1
  299. package/dist/server/routes/device-auth.d.ts +4 -0
  300. package/dist/server/routes/device-auth.js +40 -0
  301. package/dist/server/types.d.ts +4 -0
  302. package/dist/server/websocket/handler.d.ts +13 -0
  303. package/dist/server/websocket/handler.js +102 -17
  304. package/dist/services/prompt-builder.d.ts +2 -0
  305. package/dist/services/prompt-builder.js +16 -6
  306. package/dist/services/runtime-settings-context.d.ts +81 -0
  307. package/dist/services/runtime-settings-context.js +79 -0
  308. package/dist/skills/executor.js +2 -0
  309. package/dist/skills/hub.js +16 -14
  310. package/dist/skills/local-inventory.d.ts +21 -0
  311. package/dist/skills/local-inventory.js +72 -0
  312. package/dist/skills/skill-loader.js +2 -2
  313. package/dist/skills/skill-usage-store.d.ts +49 -0
  314. package/dist/skills/skill-usage-store.js +121 -0
  315. package/dist/testing/ai-integration-tests.js +8 -5
  316. package/dist/themes/theme-manager.d.ts +12 -0
  317. package/dist/themes/theme-manager.js +29 -0
  318. package/dist/themes/theme-schema.d.ts +24 -24
  319. package/dist/tools/a2a-call-tool.d.ts +34 -0
  320. package/dist/tools/a2a-call-tool.js +75 -0
  321. package/dist/tools/bash/streaming-executor.d.ts +1 -0
  322. package/dist/tools/bash/streaming-executor.js +78 -90
  323. package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
  324. package/dist/tools/code-exec-preflight-runner.js +254 -0
  325. package/dist/tools/code-exec-preflight.d.ts +44 -0
  326. package/dist/tools/code-exec-preflight.js +501 -0
  327. package/dist/tools/code-exec-tool.d.ts +48 -1
  328. package/dist/tools/code-exec-tool.js +191 -47
  329. package/dist/tools/comment-watcher.js +29 -12
  330. package/dist/tools/core-code-inspection.d.ts +56 -0
  331. package/dist/tools/core-code-inspection.js +86 -0
  332. package/dist/tools/create-skill-tool.d.ts +2 -3
  333. package/dist/tools/create-skill-tool.js +16 -75
  334. package/dist/tools/cronjob-tool.d.ts +1 -0
  335. package/dist/tools/cronjob-tool.js +1 -0
  336. package/dist/tools/env-doctor-tool.d.ts +2 -0
  337. package/dist/tools/env-doctor-tool.js +4 -3
  338. package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
  339. package/dist/tools/execute-code-rpc-invoker.js +35 -19
  340. package/dist/tools/execute-code-runner.d.ts +2 -0
  341. package/dist/tools/execute-code-runner.js +52 -17
  342. package/dist/tools/fleet-room-tool.d.ts +104 -0
  343. package/dist/tools/fleet-room-tool.js +124 -0
  344. package/dist/tools/integration-tool-hints.d.ts +4 -0
  345. package/dist/tools/integration-tool-hints.js +19 -0
  346. package/dist/tools/list-peers-tool.d.ts +3 -0
  347. package/dist/tools/list-peers-tool.js +11 -4
  348. package/dist/tools/media-generation-tool.d.ts +16 -1
  349. package/dist/tools/media-generation-tool.js +341 -9
  350. package/dist/tools/merge-conflict-tool.js +53 -25
  351. package/dist/tools/metadata.d.ts +1 -5
  352. package/dist/tools/metadata.js +30 -28
  353. package/dist/tools/peer-tool-invoke-tool.d.ts +61 -0
  354. package/dist/tools/peer-tool-invoke-tool.js +396 -0
  355. package/dist/tools/ragchat-tool.d.ts +39 -0
  356. package/dist/tools/ragchat-tool.js +105 -0
  357. package/dist/tools/registry/fleet-tools.d.ts +21 -3
  358. package/dist/tools/registry/fleet-tools.js +137 -4
  359. package/dist/tools/registry/index.d.ts +1 -1
  360. package/dist/tools/registry/index.js +2 -2
  361. package/dist/tools/registry/knowledge-tools.d.ts +2 -2
  362. package/dist/tools/registry/knowledge-tools.js +2 -2
  363. package/dist/tools/registry/lessons-tools.d.ts +7 -0
  364. package/dist/tools/registry/lessons-tools.js +43 -3
  365. package/dist/tools/registry/memory-tools.d.ts +1 -1
  366. package/dist/tools/registry/memory-tools.js +8 -8
  367. package/dist/tools/registry/research-tools.d.ts +1 -0
  368. package/dist/tools/registry/research-tools.js +4 -13
  369. package/dist/tools/registry/self-describe-tools.d.ts +1 -1
  370. package/dist/tools/registry/self-describe-tools.js +43 -30
  371. package/dist/tools/resource-catalog-tool.d.ts +38 -0
  372. package/dist/tools/resource-catalog-tool.js +46 -0
  373. package/dist/tools/skills-inspection-tool.js +30 -1
  374. package/dist/tools/stream-tool-output.d.ts +3 -0
  375. package/dist/tools/stream-tool-output.js +33 -0
  376. package/dist/tools/tool-call-scheduler.d.ts +10 -0
  377. package/dist/tools/tool-call-scheduler.js +58 -0
  378. package/dist/tools/tool-effect.d.ts +5 -0
  379. package/dist/tools/tool-effect.js +26 -0
  380. package/dist/tools/tool-search.d.ts +12 -11
  381. package/dist/tools/tool-search.js +67 -26
  382. package/dist/tools/types.d.ts +6 -0
  383. package/dist/ui/components/ChatHistory.d.ts +1 -1
  384. package/dist/ui/components/ChatHistory.js +2 -2
  385. package/dist/ui/components/ChatInput.d.ts +2 -1
  386. package/dist/ui/components/ChatInput.js +42 -88
  387. package/dist/ui/components/ChatInterface.d.ts +4 -2
  388. package/dist/ui/components/ChatInterface.js +27 -63
  389. package/dist/ui/components/CommandSuggestions.d.ts +0 -1
  390. package/dist/ui/components/CommandSuggestions.js +4 -6
  391. package/dist/ui/components/FileAutocomplete.js +1 -1
  392. package/dist/ui/components/KeyboardHelp.js +8 -6
  393. package/dist/ui/components/ModelSelection.js +6 -5
  394. package/dist/ui/components/StatusBar.d.ts +2 -1
  395. package/dist/ui/components/StatusBar.js +8 -7
  396. package/dist/ui/context/theme-context.js +3 -2
  397. package/dist/ui/index.d.ts +1 -1
  398. package/dist/ui/index.js +1 -1
  399. package/dist/ui/utils/markdown-renderer.js +4 -13
  400. package/dist/utils/atomic-write.d.ts +22 -0
  401. package/dist/utils/atomic-write.js +29 -0
  402. package/dist/utils/bounded-output.d.ts +16 -0
  403. package/dist/utils/bounded-output.js +63 -0
  404. package/dist/utils/config-validation/schema.d.ts +2 -2
  405. package/dist/utils/device-store-file.d.ts +4 -0
  406. package/dist/utils/device-store-file.js +40 -0
  407. package/dist/utils/export-manager.d.ts +9 -0
  408. package/dist/utils/export-manager.js +19 -0
  409. package/dist/utils/first-use-hints.d.ts +25 -0
  410. package/dist/utils/first-use-hints.js +58 -0
  411. package/dist/utils/graceful-shutdown.d.ts +8 -0
  412. package/dist/utils/graceful-shutdown.js +23 -7
  413. package/dist/utils/history-manager.js +1 -1
  414. package/dist/utils/update-notifier.js +16 -18
  415. package/dist/wizard/environment-detection.d.ts +5 -1
  416. package/dist/wizard/environment-detection.js +49 -7
  417. package/dist/wizard/onboarding.d.ts +5 -1
  418. package/dist/wizard/onboarding.js +90 -30
  419. package/dist/wizard/provider-onboarding.d.ts +2 -0
  420. package/dist/wizard/provider-onboarding.js +55 -17
  421. package/package.json +7 -3
@@ -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
@@ -8,7 +8,8 @@ import { buildDispatchToolFilter, getDispatchToolPolicy, normalizeDispatchProfil
8
8
  import { getBuiltinToolNames } from '../../codebuddy/tools.js';
9
9
  import { buildBrowserOperatorSessionDraft, buildInternetScoutPlan, renderBrowserOperatorSessionDraft, renderInternetScoutPlan, } from '../../browser-automation/index.js';
10
10
  import { installResearchScriptSkillCandidate, listMaterializedResearchScriptSkillCandidates, readMaterializedResearchScriptSkillCandidate, } from '../../agent/research-script-skill-candidate.js';
11
- import { getActiveToolMetadata, resolveToolEffect } from '../../tools/metadata.js';
11
+ import { getActiveToolMetadata } from '../../tools/metadata.js';
12
+ import { resolveToolEffect } from '../../tools/tool-effect.js';
12
13
  function formatList(values) {
13
14
  return values.length > 0 ? values.join(', ') : 'none';
14
15
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * buddy triage — write a local, redacted support bundle (src/doctor/triage.ts).
3
+ *
4
+ * Never sends anything and never launches an agent: the command to hand the
5
+ * prompt to a local agent is printed for the user to review and run.
6
+ */
7
+ import type { Command } from 'commander';
8
+ export declare function registerTriageCommand(program: Command): void;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * buddy triage — write a local, redacted support bundle (src/doctor/triage.ts).
3
+ *
4
+ * Never sends anything and never launches an agent: the command to hand the
5
+ * prompt to a local agent is printed for the user to review and run.
6
+ */
7
+ import { readFileSync } from 'node:fs';
8
+ import { dirname, join, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ function buddyVersion() {
11
+ try {
12
+ const here = dirname(fileURLToPath(import.meta.url));
13
+ for (const candidate of [join(here, '..', '..', '..', 'package.json'), join(here, '..', '..', 'package.json')]) {
14
+ try {
15
+ const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
16
+ if (pkg.version && pkg.name?.includes('buddy'))
17
+ return pkg.version;
18
+ }
19
+ catch { /* try next */ }
20
+ }
21
+ }
22
+ catch { /* fall through */ }
23
+ return 'unknown';
24
+ }
25
+ export function registerTriageCommand(program) {
26
+ program
27
+ .command('triage')
28
+ .description('Write a local redacted support bundle and a ≤ 8 KiB prompt (no network, launches nothing)')
29
+ .option('--json', 'Print a JSON summary on stdout')
30
+ .option('--out <dir>', 'Parent directory for the bundle (default ~/.codebuddy/triage)')
31
+ .option('--log-lines <n>', 'Maximum log lines to include', '200')
32
+ .action(async (options) => {
33
+ const { writeTriageBundle } = await import('../../doctor/triage.js');
34
+ const directory = program.opts().directory;
35
+ const cwd = resolve(process.cwd(), directory || process.cwd());
36
+ const logLines = Math.max(0, Math.min(2000, Number.parseInt(options.logLines ?? '200', 10) || 0));
37
+ try {
38
+ const result = await writeTriageBundle({
39
+ cwd,
40
+ ...(options.out ? { outParent: resolve(process.cwd(), options.out) } : {}),
41
+ logLines,
42
+ buddyVersion: buddyVersion(),
43
+ });
44
+ if (options.json) {
45
+ console.log(JSON.stringify({
46
+ dir: result.dir,
47
+ files: { json: result.jsonPath, prompt: result.promptPath },
48
+ promptBytes: result.promptBytes,
49
+ redactions: result.redactions,
50
+ withheld: result.withheld,
51
+ networkUsed: false,
52
+ agentLaunched: false,
53
+ suggestedCommand: result.suggestedCommand,
54
+ }, null, 2));
55
+ return;
56
+ }
57
+ console.log('\nCode Buddy triage (local only — nothing was sent, no agent was started)\n');
58
+ console.log(` Bundle : ${result.dir}`);
59
+ console.log(` Prompt : ${result.promptPath} (${result.promptBytes} bytes)`);
60
+ console.log(` Redactions : ${result.redactions}`);
61
+ if (result.withheld.length > 0)
62
+ console.log(` Withheld : ${result.withheld.join(', ')} (secret scan still matched)`);
63
+ console.log('\nReview prompt.md, then hand it to a local agent yourself:');
64
+ console.log(` ${result.suggestedCommand}\n`);
65
+ }
66
+ catch (error) {
67
+ console.error(`triage failed: ${error instanceof Error ? error.message : String(error)}`);
68
+ process.exitCode = 1;
69
+ }
70
+ });
71
+ }
72
+ //# sourceMappingURL=triage-command.js.map
@@ -19,9 +19,31 @@ export function registerUtilityCommands(program) {
19
19
  .description('Diagnose Code Buddy environment, dependencies, and configuration')
20
20
  .option('-v, --verbose', 'Show all checks including passing ones')
21
21
  .option('--fix', 'Auto-fix issues that can be resolved automatically')
22
+ .option('--json', 'Print a stable JSON report (includes the Integrations section)')
23
+ .option('--offline', 'Skip every network call (live key checks, OAuth refresh, local runtime probes)')
24
+ .option('--integrations', 'Also check LM Resizer, Code Explorer, MCP config, resource catalog and skills (no network)')
22
25
  .action(async (options) => {
23
26
  const { runDoctorChecks, runFixes, summarizeDoctorChecks } = await import('../../doctor/index.js');
24
- const checks = await runDoctorChecks(resolveCommandDirectory(program));
27
+ const cwd = resolveCommandDirectory(program);
28
+ const offline = options.offline === true;
29
+ const coreChecks = await runDoctorChecks(cwd, { offline });
30
+ const withIntegrations = options.json === true || options.integrations === true;
31
+ const integrationChecks = withIntegrations
32
+ ? await (await import('../../doctor/integrations.js')).runIntegrationChecks(cwd)
33
+ : [];
34
+ const checks = [...coreChecks, ...integrationChecks];
35
+ if (options.json) {
36
+ const { buildDoctorJsonReport } = await import('../../doctor/integrations.js');
37
+ const fixes = options.fix ? await runFixes(checks.filter((c) => c.fixable)) : undefined;
38
+ const summary = summarizeDoctorChecks(checks);
39
+ const report = buildDoctorJsonReport(checks, summary, { offline, ...(fixes ? { fixes } : {}) });
40
+ console.log(JSON.stringify(report, null, 2));
41
+ const readinessJson = checks.find((c) => c.name === 'AI provider ready');
42
+ if (summary.errors > 0 || (readinessJson && readinessJson.status !== 'ok') || fixes?.some((f) => !f.success)) {
43
+ process.exitCode = 1;
44
+ }
45
+ return;
46
+ }
25
47
  console.log('\n🔍 Code Buddy Doctor\n');
26
48
  const icons = { ok: '✅', warn: '⚠️', error: '❌' };
27
49
  // Headline verdict: the first question a newcomer has is "can I chat yet?"
@@ -39,6 +61,8 @@ export function registerUtilityCommands(program) {
39
61
  for (const check of checks) {
40
62
  if (check.name === 'AI provider ready')
41
63
  continue; // already shown as the headline
64
+ if (integrationChecks.length > 0 && check === integrationChecks[0])
65
+ console.log('\n Integrations (no network):');
42
66
  if (options.verbose || check.status !== 'ok') {
43
67
  const fixTag = check.fixable ? ' [fixable]' : '';
44
68
  console.log(` ${icons[check.status]} ${check.name}: ${check.message}${fixTag}`);
@@ -37,6 +37,13 @@ export declare class ClientCommandDispatcher {
37
37
  * result handling (add entry to chat, pass prompt to AI, clear input).
38
38
  */
39
39
  private static delegateToEnhanced;
40
+ /** Session-only /switch overrides, keyed by the live agent. */
41
+ private static readonly switchState;
42
+ /**
43
+ * Back /switch with the running agent so it changes the model used by the
44
+ * next turns (session only, unlike /model which also saves the preference).
45
+ */
46
+ private static createSwitchModelProvider;
40
47
  private static handleEnhancedCommand;
41
48
  private static handleModelSwitch;
42
49
  private static handleShellBypass;
@@ -3,7 +3,9 @@ import { getEnhancedCommandHandler } from "./enhanced-command-handler.js";
3
3
  import { GitWorkflowHandler } from "./workflow/git-workflow.js";
4
4
  import { getErrorMessage } from "../types/index.js";
5
5
  import { ConfirmationService } from "../utils/confirmation-service.js";
6
+ import { isModelCompatibleWithProvider } from "../providers/model-provider-compat.js";
6
7
  import { updateCurrentModel } from "../utils/model-config.js";
8
+ import { setSwitchModelProvider } from "./handlers/switch-handler.js";
7
9
  export class ClientCommandDispatcher {
8
10
  /**
9
11
  * Dispatches a command input to the appropriate handler.
@@ -48,6 +50,8 @@ export class ClientCommandDispatcher {
48
50
  context.clearInput();
49
51
  }
50
52
  static async handleSlashCommand(input, context) {
53
+ // Keep the historical plural spelling on the same live-agent path.
54
+ input = input.replace(/^\/models(?=\s|$)/, '/model');
51
55
  const slashManager = getSlashCommandManager();
52
56
  const result = slashManager.execute(input);
53
57
  if (result.success && result.prompt) {
@@ -108,18 +112,22 @@ export class ClientCommandDispatcher {
108
112
  const args = originalInput.trim().split(/\s+/).slice(1);
109
113
  if (args.length === 0) {
110
114
  context.setShowModelSelection(true);
111
- context.setSelectedModelIndex(0);
115
+ context.setSelectedModelIndex(Math.max(0, context.availableModels.findIndex(option => option.model === context.agent.getCurrentModel())));
112
116
  context.clearInput();
113
117
  return true;
114
118
  }
115
- // With args, delegate to enhanced handler for model switching
116
- return await this.delegateToEnhanced(token, originalInput, context);
119
+ if (args[0] === 'auto' || args[0] === 'list') {
120
+ return await this.delegateToEnhanced(token, originalInput, context);
121
+ }
122
+ await this.handleModelSwitch(originalInput, context);
123
+ return true;
117
124
  }
118
125
  // __CLEAR_CHAT__ needs UI state resets beyond what the handler provides
119
126
  if (token === "__CLEAR_CHAT__") {
120
127
  const handled = await this.delegateToEnhanced(token, originalInput, context);
121
128
  if (handled) {
122
129
  // Apply UI-specific side effects
130
+ context.agent.clearChat();
123
131
  context.setChatHistory([]);
124
132
  context.setIsProcessing(false);
125
133
  context.setIsStreaming(false);
@@ -149,11 +157,13 @@ export class ClientCommandDispatcher {
149
157
  getContextStats: () => context.agent.getContextStats(),
150
158
  formatContextStats: () => context.agent.formatContextStats(),
151
159
  getCurrentModel: () => context.agent.getCurrentModel(),
160
+ getMemoryScope: () => context.agent.getMemoryScope(),
152
161
  getCurrentSessionId: () => context.agent.getCurrentSessionId?.() ?? null,
153
162
  getContextMemoryMetrics: () => context.agent.getContextMemoryMetrics(),
154
163
  getCompressionStats: () => context.agent.getCompressionStats(),
155
164
  getContextBudgetBreakdown: () => context.agent.getContextBudgetBreakdown(),
156
165
  });
166
+ setSwitchModelProvider(this.createSwitchModelProvider(context));
157
167
  const args = overrideArgs ?? originalInput.trim().split(" ").slice(1);
158
168
  const handlerResult = await enhancedHandler.handleCommand(token, args, originalInput);
159
169
  if (handlerResult.handled) {
@@ -173,30 +183,72 @@ export class ClientCommandDispatcher {
173
183
  this.finishCommandWithMessage(context, `Command ${commandName} is registered but has no conversation-loop handler yet. Use /help to see working commands.`);
174
184
  return true;
175
185
  }
186
+ /** Session-only /switch overrides, keyed by the live agent. */
187
+ static switchState = new WeakMap();
188
+ /**
189
+ * Back /switch with the running agent so it changes the model used by the
190
+ * next turns (session only, unlike /model which also saves the preference).
191
+ */
192
+ static createSwitchModelProvider(context) {
193
+ const agent = context.agent;
194
+ const state = () => {
195
+ let current = this.switchState.get(agent);
196
+ if (!current) {
197
+ current = { baseModel: agent.getCurrentModel(), switched: null };
198
+ this.switchState.set(agent, current);
199
+ }
200
+ return current;
201
+ };
202
+ return {
203
+ getAvailableModels: () => context.availableModels.map(option => option.model),
204
+ getCurrentModel: () => agent.getCurrentModel(),
205
+ getSwitchedModel: () => this.switchState.get(agent)?.switched ?? null,
206
+ setSwitchedModel: (model) => {
207
+ const current = state();
208
+ if (model === null) {
209
+ if (current.switched !== null)
210
+ agent.setModel(current.baseModel);
211
+ this.switchState.delete(agent);
212
+ return;
213
+ }
214
+ const provider = agent.getClient().getCurrentProvider?.();
215
+ if (!isModelCompatibleWithProvider(model, provider)) {
216
+ throw new Error(`Model ${model} is incompatible with the active provider ${provider}. Choose a model for this connection.`);
217
+ }
218
+ agent.setModel(model);
219
+ current.switched = model;
220
+ },
221
+ };
222
+ }
176
223
  static async handleEnhancedCommand(token, originalInput, context) {
177
224
  return await this.delegateToEnhanced(token, originalInput, context);
178
225
  }
179
226
  static async handleModelSwitch(input, context) {
180
- const modelArg = input.trim().split(" ")[1];
181
- const modelNames = context.availableModels.map((m) => m.model);
182
- if (modelArg !== undefined && modelNames.includes(modelArg)) {
183
- context.agent.setModel(modelArg);
184
- updateCurrentModel(modelArg);
185
- const confirmEntry = {
186
- type: "assistant",
187
- content: `✓ Switched to model: ${modelArg}`,
188
- timestamp: new Date(),
189
- };
190
- context.setChatHistory((prev) => [...prev, confirmEntry]);
227
+ const args = input.trim().split(/\s+/).slice(1);
228
+ const model = args[0];
229
+ if (!model || args.length !== 1) {
230
+ this.finishCommandWithMessage(context, 'Usage: /model <model-name>');
231
+ return;
191
232
  }
192
- else {
193
- const errorEntry = {
194
- type: "assistant",
195
- content: `Invalid model: ${modelArg}\n\nAvailable models: ${modelNames.join(", ")}`,
196
- timestamp: new Date(),
197
- };
198
- context.setChatHistory((prev) => [...prev, errorEntry]);
233
+ const provider = context.agent.getClient().getCurrentProvider?.();
234
+ if (!isModelCompatibleWithProvider(model, provider)) {
235
+ this.finishCommandWithMessage(context, `Model ${model} is incompatible with the active provider ${provider}. Choose a model for this connection.`);
236
+ return;
237
+ }
238
+ // Change the running client and its context/token limits before announcing success.
239
+ context.agent.setModel(model);
240
+ // An explicit /model choice becomes the new base for later /switch auto.
241
+ this.switchState.delete(context.agent);
242
+ try {
243
+ updateCurrentModel(model);
244
+ }
245
+ catch (error) {
246
+ this.finishCommandWithMessage(context, `Model active for this session: ${model}. Could not save the preference: ${getErrorMessage(error)}`);
247
+ return;
199
248
  }
249
+ context.setChatHistory(prev => [...prev, {
250
+ type: 'assistant', content: `Switched to model: ${model}`, timestamp: new Date(),
251
+ }]);
200
252
  context.clearInput();
201
253
  }
202
254
  static async handleShellBypass(command, context) {
@@ -38,6 +38,7 @@ export interface CronAddOptions {
38
38
  /** Job-level chain target: id (or id prefix) to run on successful completion. */
39
39
  then?: string;
40
40
  preCheck?: string;
41
+ continuity?: string;
41
42
  deliver?: string[];
42
43
  format?: string;
43
44
  }
@@ -56,9 +57,10 @@ export interface CronJobSpec {
56
57
  task: CronJob['task'];
57
58
  delivery?: CronJob['delivery'];
58
59
  preCheck?: CronJob['preCheck'];
60
+ continuity?: CronJob['continuity'];
59
61
  then?: string;
60
62
  }
61
- export type CronJobUpdates = Partial<Pick<CronJob, 'name' | 'type' | 'schedule' | 'task' | 'delivery' | 'preCheck' | 'then'>>;
63
+ export type CronJobUpdates = Partial<Pick<CronJob, 'name' | 'type' | 'schedule' | 'task' | 'delivery' | 'preCheck' | 'then' | 'continuity'>>;
62
64
  export type CronJobSpecResult = {
63
65
  spec: CronJobSpec;
64
66
  } | {