@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
@@ -0,0 +1,12 @@
1
+ import type { MCPServerConfig } from './types.js';
2
+ export type MCPImportSource = 'hermes' | 'openclaw';
3
+ export interface MCPImportResult {
4
+ servers: MCPServerConfig[];
5
+ warnings: string[];
6
+ rejected: string[];
7
+ }
8
+ /** Supported subset: exact names and star globs. Unsupported exclusions must never broaden exposure. */
9
+ export declare function validateMCPToolFilter(filter: MCPServerConfig['toolFilter']): void;
10
+ export declare function mcpToolAllowed(name: string, filter: MCPServerConfig['toolFilter']): boolean;
11
+ /** Pure import: never resolve env, connect, run commands or transfer credentials. */
12
+ export declare function normalizeMCPImports(input: unknown, source: MCPImportSource): MCPImportResult;
@@ -0,0 +1,114 @@
1
+ import { SECRET_PATTERNS } from '../security/secret-patterns.js';
2
+ const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
3
+ const sensitiveKey = /KEY|TOKEN|SECRET|PASSWORD|AUTHORIZATION|COOKIE/i;
4
+ const reference = /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/;
5
+ const hasSecret = (value) => SECRET_PATTERNS.some(p => new RegExp(p.pattern.source, p.pattern.flags.replace('g', '')).test(value));
6
+ /** Supported subset: exact names and star globs. Unsupported exclusions must never broaden exposure. */
7
+ export function validateMCPToolFilter(filter) {
8
+ if (!filter)
9
+ return;
10
+ for (const patterns of [filter.include, filter.exclude]) {
11
+ if (patterns !== undefined && (!Array.isArray(patterns) || patterns.some(p => typeof p !== 'string' || !/^[A-Za-z0-9_.:*-]+$/.test(p)))) {
12
+ throw new Error('Unsupported MCP tool filter; only names and * are supported');
13
+ }
14
+ }
15
+ }
16
+ export function mcpToolAllowed(name, filter) {
17
+ validateMCPToolFilter(filter);
18
+ const matches = (pattern) => new RegExp(`^${pattern.split('*').map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('.*')}$`).test(name);
19
+ return (!filter?.include || filter.include.some(matches)) && !filter?.exclude?.some(matches);
20
+ }
21
+ /** Pure import: never resolve env, connect, run commands or transfer credentials. */
22
+ export function normalizeMCPImports(input, source) {
23
+ const result = { servers: [], warnings: [], rejected: [] };
24
+ if (!record(input)) {
25
+ result.rejected.push('MCP server map must be an object');
26
+ return result;
27
+ }
28
+ for (const [name, raw] of Object.entries(input)) {
29
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(name) || !record(raw)) {
30
+ result.rejected.push('Invalid MCP server entry');
31
+ continue;
32
+ }
33
+ const warn = (message) => result.warnings.push(`${name}: ${message}`);
34
+ try {
35
+ const clean = (key, value) => {
36
+ if (typeof value !== 'string')
37
+ throw new Error('Expected string configuration value');
38
+ if (reference.test(value) || /^Bearer \$\{[A-Za-z_][A-Za-z0-9_]*\}$/i.test(value))
39
+ return value;
40
+ if (sensitiveKey.test(key) || /^(?:--)?[^=]*(?:token|secret|password|authorization|api[_-]?key)[^=]*=/i.test(value) || hasSecret(value)) {
41
+ const envName = `MCP_IMPORT_${name}_${key}`.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase();
42
+ warn(`secret not copied; configure environment ${envName}`);
43
+ return '${' + envName + '}';
44
+ }
45
+ return value;
46
+ };
47
+ const map = (key) => {
48
+ if (raw[key] === undefined)
49
+ return undefined;
50
+ if (!record(raw[key]))
51
+ throw new Error(`Invalid ${key}`);
52
+ return Object.fromEntries(Object.entries(raw[key]).map(([k, v]) => [k, clean(`${key}_${k}`, v)]));
53
+ };
54
+ const type = raw.transport ?? raw.type;
55
+ let transport;
56
+ if (type === 'stdio' || (type === undefined && typeof raw.command === 'string')) {
57
+ if (typeof raw.command !== 'string' || !raw.command.trim())
58
+ throw new Error('stdio requires command');
59
+ if (raw.args !== undefined && (!Array.isArray(raw.args) || raw.args.some(a => typeof a !== 'string')))
60
+ throw new Error('Invalid args');
61
+ const args = (raw.args ?? []);
62
+ transport = { type: 'stdio', inheritEnv: false, command: clean('command', raw.command), args: args.map((v, i) => clean(sensitiveKey.test(args[i - 1] ?? '') ? `arg_secret_${i}` : `arg_${i}`, v)), env: map('env') };
63
+ if (raw.cwd !== undefined)
64
+ transport.cwd = clean('cwd', raw.cwd);
65
+ }
66
+ else {
67
+ const mapped = type === undefined ? (source === 'hermes' ? 'streamable_http' : 'sse')
68
+ : type === 'http' || type === 'streamable-http' || type === 'streamable_http' ? 'streamable_http'
69
+ : type === 'sse' || type === 'sse_sdk' ? 'sse' : undefined;
70
+ if (!mapped)
71
+ throw new Error('Unsupported transport');
72
+ if (typeof raw.url !== 'string')
73
+ throw new Error('Remote transport requires URL');
74
+ if (!reference.test(raw.url)) {
75
+ const url = new URL(raw.url);
76
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.hash || url.search)
77
+ throw new Error('Remote URL must not contain credentials, query or fragment; use environment reference');
78
+ }
79
+ transport = { type: mapped, url: raw.url, headers: map('headers') };
80
+ if (type === undefined && source === 'hermes')
81
+ warn('streamable HTTP selected; implicit SSE fallback is not imported');
82
+ }
83
+ const server = { name, transport, enabled: raw.enabled !== false };
84
+ const filter = raw.toolFilter ?? raw.tool_filter ?? raw.tools;
85
+ if (filter !== undefined) {
86
+ if (!record(filter))
87
+ throw new Error('Invalid tool filter');
88
+ const allowedKeys = filter === raw.tools ? ['include', 'exclude', 'resources', 'prompts'] : ['include', 'exclude'];
89
+ if (Object.keys(filter).some(key => !allowedKeys.includes(key)))
90
+ throw new Error('Unsupported tool filter fields');
91
+ if (filter.resources !== undefined || filter.prompts !== undefined)
92
+ warn('resource/prompt utilities are not imported; Buddy imports native tools only');
93
+ if (filter.include !== undefined || filter.exclude !== undefined) {
94
+ server.toolFilter = { ...(filter.include !== undefined ? { include: typeof filter.include === 'string' ? [filter.include] : filter.include } : {}),
95
+ ...(filter.exclude !== undefined ? { exclude: typeof filter.exclude === 'string' ? [filter.exclude] : filter.exclude } : {}) };
96
+ validateMCPToolFilter(server.toolFilter);
97
+ // OpenClaw treats an empty include as unrestricted, unlike Hermes.
98
+ if (source === 'openclaw' && server.toolFilter.include?.length === 0)
99
+ delete server.toolFilter.include;
100
+ }
101
+ }
102
+ if (raw.auth !== undefined || raw.oauth !== undefined) {
103
+ server.enabled = false;
104
+ warn('authentication settings not imported; server disabled until operator configures authentication in Buddy');
105
+ }
106
+ result.servers.push(server);
107
+ }
108
+ catch {
109
+ result.rejected.push(`${name}: unsupported or unsafe configuration; no values copied`);
110
+ }
111
+ }
112
+ return result;
113
+ }
114
+ //# sourceMappingURL=import-normalize.js.map
@@ -1,9 +1,12 @@
1
1
  import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
2
2
  import { EventEmitter } from "events";
3
- export type TransportType = 'stdio' | 'http' | 'sse' | 'streamable_http';
3
+ export type TransportType = 'stdio' | 'http' | 'sse' | 'sse_sdk' | 'legacy_rpc' | 'streamable_http';
4
4
  export interface TransportConfig {
5
5
  type: TransportType;
6
6
  command?: string;
7
+ cwd?: string;
8
+ /** Imported stdio servers receive SDK baseline env plus explicit declarations only. */
9
+ inheritEnv?: boolean;
7
10
  args?: string[];
8
11
  env?: Record<string, string>;
9
12
  url?: string;
@@ -31,9 +34,9 @@ export declare class HttpTransport extends EventEmitter implements MCPTransport
31
34
  disconnect(): Promise<void>;
32
35
  getType(): TransportType;
33
36
  }
34
- export declare class SSETransport extends EventEmitter implements MCPTransport {
37
+ export declare class SSETransport implements MCPTransport {
35
38
  private config;
36
- private connected;
39
+ private transport?;
37
40
  constructor(config: TransportConfig);
38
41
  connect(): Promise<Transport>;
39
42
  disconnect(): Promise<void>;
@@ -41,10 +44,12 @@ export declare class SSETransport extends EventEmitter implements MCPTransport {
41
44
  }
42
45
  export declare class StreamableHttpTransport extends EventEmitter implements MCPTransport {
43
46
  private config;
44
- private connected;
47
+ private transport?;
45
48
  constructor(config: TransportConfig);
46
49
  connect(): Promise<Transport>;
47
50
  disconnect(): Promise<void>;
48
51
  getType(): TransportType;
49
52
  }
50
- export declare function createTransport(config: TransportConfig): MCPTransport;
53
+ /** Resolve references only at connection time; missing credentials fail closed. */
54
+ export declare function resolveMCPTransport(config: TransportConfig): TransportConfig;
55
+ export declare function createTransport(input: TransportConfig): MCPTransport;
@@ -1,4 +1,6 @@
1
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
1
+ import { SSEClientTransport as SDKSSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
2
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
+ import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
2
4
  import { EventEmitter } from "events";
3
5
  import axios from "axios";
4
6
  import { logger } from '../utils/logger.js';
@@ -14,7 +16,7 @@ export class StdioTransport {
14
16
  async connect() {
15
17
  // Create transport with environment variables to suppress verbose output
16
18
  const env = {
17
- ...process.env,
19
+ ...(this.config.inheritEnv === false ? getDefaultEnvironment() : process.env),
18
20
  ...this.config.env,
19
21
  // Try to suppress verbose output from mcp-remote
20
22
  MCP_REMOTE_QUIET: '1',
@@ -25,6 +27,7 @@ export class StdioTransport {
25
27
  const transportConfig = {
26
28
  command: this.config.command,
27
29
  args: this.config.args || [],
30
+ cwd: this.config.cwd,
28
31
  env,
29
32
  // The SDK default is stderr:'inherit', which lets a noisy MCP server
30
33
  // (startup banners, progress logs) write raw bytes into the CLI's own
@@ -69,6 +72,7 @@ export class HttpTransport extends EventEmitter {
69
72
  async connect() {
70
73
  this.client = axios.create({
71
74
  baseURL: this.config.url,
75
+ maxRedirects: 0,
72
76
  headers: {
73
77
  'Content-Type': 'application/json',
74
78
  ...this.config.headers
@@ -93,35 +97,28 @@ export class HttpTransport extends EventEmitter {
93
97
  return 'http';
94
98
  }
95
99
  }
96
- export class SSETransport extends EventEmitter {
100
+ export class SSETransport {
97
101
  config;
98
- connected = false;
102
+ transport;
99
103
  constructor(config) {
100
- super();
101
104
  this.config = config;
102
- if (!config.url) {
103
- throw new Error('URL is required for SSE MCP transport. Specify the server URL in your MCP configuration.');
104
- }
105
+ if (!config.url)
106
+ throw new Error('URL is required for SSE MCP transport');
105
107
  }
106
108
  async connect() {
107
- return new Promise((resolve, reject) => {
108
- try {
109
- // For Node.js environment, we'll use a simple HTTP-based approach
110
- // In a real implementation, you'd use a proper SSE library like 'eventsource'
111
- this.connected = true;
112
- resolve(new SSEClientTransport(this.config.url));
113
- }
114
- catch (error) {
115
- reject(error);
116
- }
109
+ this.transport = new SDKSSEClientTransport(new URL(this.config.url), {
110
+ requestInit: { headers: this.config.headers, redirect: 'error' },
111
+ eventSourceInit: { fetch: (url, init) => {
112
+ const headers = new Headers(init?.headers);
113
+ for (const [key, value] of Object.entries(this.config.headers ?? {}))
114
+ headers.set(key, value);
115
+ return fetch(url, { ...init, headers, redirect: 'error' });
116
+ } },
117
117
  });
118
+ return this.transport;
118
119
  }
119
- async disconnect() {
120
- this.connected = false;
121
- }
122
- getType() {
123
- return 'sse';
124
- }
120
+ async disconnect() { await this.transport?.close(); this.transport = undefined; }
121
+ getType() { return 'sse'; }
125
122
  }
126
123
  // Custom HTTP Transport implementation
127
124
  class HttpClientTransport extends EventEmitter {
@@ -156,105 +153,70 @@ class HttpClientTransport extends EventEmitter {
156
153
  }
157
154
  }
158
155
  }
159
- // Custom SSE Transport implementation
160
- class SSEClientTransport extends EventEmitter {
161
- url;
162
- onclose;
163
- onerror;
164
- onmessage;
165
- sessionId;
166
- constructor(url) {
167
- super();
168
- this.url = url;
169
- }
170
- async start() {
171
- // SSE transport is event-driven, so we're always "started"
172
- }
173
- async close() {
174
- // Nothing to close for basic SSE transport
175
- }
176
- async send(message, _options) {
177
- // For bidirectional communication over SSE, we typically use HTTP POST
178
- // for sending messages and SSE for receiving
179
- try {
180
- const response = await axios.post(this.url.replace('/sse', '/rpc'), message, {
181
- headers: { 'Content-Type': 'application/json' }
182
- });
183
- if (this.onmessage && response.data) {
184
- this.onmessage(response.data);
185
- }
186
- }
187
- catch (error) {
188
- const err = new Error(`SSE transport error: ${error}`);
189
- if (this.onerror) {
190
- this.onerror(err);
191
- }
192
- throw err;
193
- }
194
- }
195
- }
196
156
  export class StreamableHttpTransport extends EventEmitter {
197
157
  config;
198
- connected = false;
158
+ transport;
199
159
  constructor(config) {
200
160
  super();
201
161
  this.config = config;
202
- if (!config.url) {
162
+ if (!config.url)
203
163
  throw new Error('URL is required for streamable_http transport');
204
- }
205
164
  }
206
165
  async connect() {
207
- return new Promise((resolve, reject) => {
208
- try {
209
- this.connected = true;
210
- resolve(new StreamableHttpClientTransport(this.config.url, this.config.headers));
211
- }
212
- catch (error) {
213
- reject(error);
214
- }
166
+ const url = new URL(this.config.url);
167
+ if (!['http:', 'https:'].includes(url.protocol)) {
168
+ throw new Error('Streamable HTTP MCP requires an HTTP(S) URL');
169
+ }
170
+ this.transport = new StreamableHTTPClientTransport(url, {
171
+ requestInit: { headers: this.config.headers, redirect: 'error' },
215
172
  });
173
+ return this.transport;
216
174
  }
217
175
  async disconnect() {
218
- this.connected = false;
176
+ const transport = this.transport;
177
+ this.transport = undefined;
178
+ await transport?.close();
219
179
  }
220
180
  getType() {
221
181
  return 'streamable_http';
222
182
  }
223
183
  }
224
- // Custom Streamable HTTP Transport implementation for GitHub Copilot MCP
225
- class StreamableHttpClientTransport extends EventEmitter {
226
- url;
227
- headers;
228
- onclose;
229
- onerror;
230
- onmessage;
231
- sessionId;
232
- constructor(url, headers) {
233
- super();
234
- this.url = url;
235
- this.headers = headers;
236
- }
237
- async start() {
238
- // Streamable HTTP transport is connection-less, so we're always "started"
239
- }
240
- async close() {
241
- // Nothing to close for streamable HTTP transport
242
- }
243
- async send(_message, _options) {
244
- logger.warn('StreamableHttpTransport: SSE endpoints require persistent connections, not suitable for MCP request-response pattern');
245
- logger.debug('StreamableHttpTransport: Message that would be sent', { message: _message });
246
- // For now, throw an error to indicate the transport type is not compatible
247
- // with the MCP protocol's request-response pattern
248
- throw new Error('StreamableHttpTransport: SSE endpoints are not compatible with MCP request-response pattern. GitHub Copilot MCP may require a different integration approach.');
184
+ /** Resolve references only at connection time; missing credentials fail closed. */
185
+ export function resolveMCPTransport(config) {
186
+ const resolve = (value) => value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, key) => {
187
+ const result = process.env[key];
188
+ if (!result)
189
+ throw new Error(`Missing MCP environment reference: ${key}`);
190
+ return result;
191
+ });
192
+ const map = (values) => values && Object.fromEntries(Object.entries(values).map(([k, v]) => [k, resolve(v)]));
193
+ const resolved = { ...config, command: config.command && resolve(config.command), cwd: config.cwd && resolve(config.cwd),
194
+ args: config.args?.map(resolve), env: map(config.env), headers: map(config.headers), url: config.url && resolve(config.url) };
195
+ if (resolved.url) {
196
+ let url;
197
+ try {
198
+ url = new URL(resolved.url);
199
+ }
200
+ catch {
201
+ throw new Error('Invalid MCP endpoint configuration');
202
+ }
203
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash)
204
+ throw new Error('MCP requires HTTP(S) without embedded credentials, query or fragment');
249
205
  }
206
+ return resolved;
250
207
  }
251
- export function createTransport(config) {
208
+ export function createTransport(input) {
209
+ const config = resolveMCPTransport(input);
252
210
  switch (config.type) {
253
211
  case 'stdio':
254
212
  return new StdioTransport(config);
255
213
  case 'http':
214
+ logger.warn('MCP http /rpc transport is deprecated; use legacy_rpc explicitly or streamable_http for MCP HTTP');
215
+ return new HttpTransport(config);
216
+ case 'legacy_rpc':
256
217
  return new HttpTransport(config);
257
218
  case 'sse':
219
+ case 'sse_sdk':
258
220
  return new SSETransport(config);
259
221
  case 'streamable_http':
260
222
  return new StreamableHttpTransport(config);
@@ -15,6 +15,11 @@ export interface MCPServerConfig {
15
15
  maxRetries?: number;
16
16
  /** Optional: Enable/disable this server (default: true) */
17
17
  enabled?: boolean;
18
+ /** Exact names and star globs; exclusions win. Empty include denies every tool. */
19
+ toolFilter?: {
20
+ include?: string[];
21
+ exclude?: string[];
22
+ };
18
23
  }
19
24
  export type ServerStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
20
25
  export interface MCPTool {
@@ -473,12 +473,6 @@ export declare const comfyUIRecipeSchema: z.ZodObject<{
473
473
  name?: string | undefined;
474
474
  url?: string | undefined;
475
475
  };
476
- schemaVersion: 1;
477
- fallback: {
478
- id: string;
479
- reason?: string | undefined;
480
- version?: string | undefined;
481
- }[];
482
476
  resources: {
483
477
  tier: "high" | "low" | "balanced" | "extreme";
484
478
  maxConcurrency: number;
@@ -486,6 +480,12 @@ export declare const comfyUIRecipeSchema: z.ZodObject<{
486
480
  minRamMiB: number;
487
481
  estimatedSeconds?: number | undefined;
488
482
  };
483
+ schemaVersion: 1;
484
+ fallback: {
485
+ id: string;
486
+ reason?: string | undefined;
487
+ version?: string | undefined;
488
+ }[];
489
489
  bindings: {
490
490
  prompt: {
491
491
  input: string;
@@ -573,7 +573,6 @@ export declare const comfyUIRecipeSchema: z.ZodObject<{
573
573
  name?: string | undefined;
574
574
  url?: string | undefined;
575
575
  };
576
- schemaVersion: 1;
577
576
  resources: {
578
577
  tier: "high" | "low" | "balanced" | "extreme";
579
578
  minVramMiB: number;
@@ -581,6 +580,7 @@ export declare const comfyUIRecipeSchema: z.ZodObject<{
581
580
  maxConcurrency?: number | undefined;
582
581
  estimatedSeconds?: number | undefined;
583
582
  };
583
+ schemaVersion: 1;
584
584
  bindings: {
585
585
  prompt: {
586
586
  input: string;
@@ -63,8 +63,18 @@ export declare const ReconciliationActionSchema: z.ZodObject<{
63
63
  } | undefined;
64
64
  }>;
65
65
  export type ReconciliationAction = z.infer<typeof ReconciliationActionSchema>;
66
+ /** Keep concurrent command sessions on their own provider, including an explicit offline scope. */
67
+ export declare function withFactsMemorySessionClient<T>(client: CodeBuddyClient | null, action: () => T): T;
68
+ /**
69
+ * Async generators execute on next/return/throw, not when they are created.
70
+ * Scope every resumption so model/tool/cleanup work belongs to this agent,
71
+ * while consumers outside a yielded event keep their own async context.
72
+ */
73
+ export declare function bindFactsMemorySession<T>(client: CodeBuddyClient, iterator: AsyncGenerator<T, void, unknown>): AsyncGenerator<T, void, unknown>;
74
+ export declare function setFactsMemorySessionClient(client: CodeBuddyClient | null): void;
66
75
  export declare class FactsMemoryService {
67
- private client;
76
+ private readonly explicitClient;
77
+ private fallbackClient;
68
78
  constructor(client?: CodeBuddyClient);
69
79
  isAvailable(): Promise<boolean>;
70
80
  private getClient;
@@ -1,3 +1,4 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
1
2
  import { z } from 'zod';
2
3
  import { logger } from '../utils/logger.js';
3
4
  import { CodeBuddyClient } from '../codebuddy/client.js';
@@ -30,20 +31,58 @@ export const ReconciliationActionSchema = z.object({
30
31
  targetIndex: z.number().optional(),
31
32
  fact: FactSchema.optional()
32
33
  });
34
+ /**
35
+ * Client of the live session (the provider and endpoint the user actually
36
+ * started with). Without it, reconciliation fell back to environment
37
+ * auto-detection and could send project memories to a different, cloud
38
+ * provider than the local session (e.g. xAI because GROK_API_KEY was set while
39
+ * the CLI ran on --base-url http://127.0.0.1:11434/v1).
40
+ */
41
+ let sessionClient = null;
42
+ const sessionScope = new AsyncLocalStorage();
43
+ /** Keep concurrent command sessions on their own provider, including an explicit offline scope. */
44
+ export function withFactsMemorySessionClient(client, action) {
45
+ return sessionScope.run({ client }, action);
46
+ }
47
+ /**
48
+ * Async generators execute on next/return/throw, not when they are created.
49
+ * Scope every resumption so model/tool/cleanup work belongs to this agent,
50
+ * while consumers outside a yielded event keep their own async context.
51
+ */
52
+ export function bindFactsMemorySession(client, iterator) {
53
+ return {
54
+ next(...args) { return withFactsMemorySessionClient(client, () => iterator.next(...args)); },
55
+ return(value) { return withFactsMemorySessionClient(client, () => iterator.return(value)); },
56
+ throw(error) { return withFactsMemorySessionClient(client, () => iterator.throw(error)); },
57
+ [Symbol.asyncIterator]() { return this; },
58
+ async [Symbol.asyncDispose]() {
59
+ await withFactsMemorySessionClient(client, () => iterator.return());
60
+ },
61
+ };
62
+ }
63
+ export function setFactsMemorySessionClient(client) {
64
+ sessionClient = client;
65
+ }
33
66
  export class FactsMemoryService {
34
- client = null;
67
+ explicitClient;
68
+ fallbackClient = null;
35
69
  constructor(client) {
36
- if (client) {
37
- this.client = client;
38
- }
70
+ this.explicitClient = client ?? null;
39
71
  }
40
72
  async isAvailable() {
41
73
  const client = await this.getClient();
42
74
  return client !== null;
43
75
  }
44
76
  async getClient() {
45
- if (this.client)
46
- return this.client;
77
+ if (this.explicitClient)
78
+ return this.explicitClient;
79
+ const scoped = sessionScope.getStore();
80
+ if (scoped)
81
+ return scoped.client;
82
+ if (sessionClient)
83
+ return sessionClient;
84
+ if (this.fallbackClient)
85
+ return this.fallbackClient;
47
86
  // Skip auto-detecting client in unit tests to prevent timeouts/real API calls
48
87
  if (process.env.NODE_ENV === 'test' || process.env.VITEST) {
49
88
  return null;
@@ -53,8 +92,8 @@ export class FactsMemoryService {
53
92
  logger.warn('[FactsMemory] No LLM provider configuration found.');
54
93
  return null;
55
94
  }
56
- this.client = new CodeBuddyClient(detected.apiKey, detected.defaultModel, detected.baseURL);
57
- return this.client;
95
+ this.fallbackClient = new CodeBuddyClient(detected.apiKey, detected.defaultModel, detected.baseURL);
96
+ return this.fallbackClient;
58
97
  }
59
98
  /**
60
99
  * Extract atom facts from a conversation or context block.
@@ -72,7 +72,7 @@ export interface MemoryCandidateStats {
72
72
  total: number;
73
73
  byStatus: Record<MemoryCandidateStatus, number>;
74
74
  }
75
- export declare function getMemoryCandidateQueue(workDir?: string): MemoryCandidateQueue;
75
+ export declare function getMemoryCandidateQueue(workDir?: string, botId?: string): MemoryCandidateQueue;
76
76
  export declare function resetMemoryCandidateQueues(): void;
77
77
  export declare class MemoryCandidateQueue {
78
78
  private workDir;
@@ -80,7 +80,7 @@ export declare class MemoryCandidateQueue {
80
80
  private filePath;
81
81
  private candidates;
82
82
  private loaded;
83
- constructor(workDir?: string, memoryManager?: PersistentMemoryManager | undefined);
83
+ constructor(workDir?: string, memoryManager?: PersistentMemoryManager | undefined, botId?: string);
84
84
  propose(input: ProposeMemoryCandidateInput): ProposeMemoryCandidateResult;
85
85
  list(status?: MemoryCandidateStatus): MemoryCandidate[];
86
86
  get(id: string): MemoryCandidate | null;
@@ -22,10 +22,10 @@ const VALID_CATEGORIES = [
22
22
  'custom',
23
23
  ];
24
24
  const registry = new Map();
25
- export function getMemoryCandidateQueue(workDir = process.cwd()) {
26
- const key = path.resolve(workDir);
25
+ export function getMemoryCandidateQueue(workDir = process.cwd(), botId) {
26
+ const key = JSON.stringify([path.resolve(workDir), botId ?? '']);
27
27
  if (!registry.has(key)) {
28
- registry.set(key, new MemoryCandidateQueue(key));
28
+ registry.set(key, new MemoryCandidateQueue(path.resolve(workDir), botId ? getMemoryManager(undefined, botId, workDir) : undefined, botId));
29
29
  if (registry.size > 20) {
30
30
  const firstKey = registry.keys().next().value;
31
31
  if (firstKey)
@@ -43,10 +43,10 @@ export class MemoryCandidateQueue {
43
43
  filePath;
44
44
  candidates = [];
45
45
  loaded = false;
46
- constructor(workDir = process.cwd(), memoryManager) {
46
+ constructor(workDir = process.cwd(), memoryManager, botId) {
47
47
  this.workDir = workDir;
48
48
  this.memoryManager = memoryManager;
49
- this.filePath = path.join(workDir, '.codebuddy', 'memory-candidates.json');
49
+ this.filePath = path.join(workDir, '.codebuddy', botId ? `memory-candidates-${encodeURIComponent(botId)}.json` : 'memory-candidates.json');
50
50
  }
51
51
  propose(input) {
52
52
  this.load();
@@ -122,7 +122,7 @@ export class MemoryCandidateQueue {
122
122
  throw new Error('Accepted memory key cannot be empty.');
123
123
  if (!value)
124
124
  throw new Error('Accepted memory value cannot be empty.');
125
- const manager = this.memoryManager ?? getMemoryManager();
125
+ const manager = this.memoryManager ?? getMemoryManager(undefined, undefined, this.workDir);
126
126
  await manager.initialize();
127
127
  const write = await manager.remember(key, value, {
128
128
  scope,
@@ -53,6 +53,15 @@ export interface MemoryWriteResult {
53
53
  category?: MemoryCategory;
54
54
  usage: MemoryUsage;
55
55
  message: string;
56
+ /**
57
+ * Outcome of the optional LLM fact reconciliation, separate from the primary
58
+ * write: `failed` means the memory was still written directly and existing
59
+ * memories were restored unchanged.
60
+ */
61
+ reconciliation?: {
62
+ status: 'applied' | 'skipped' | 'failed';
63
+ reason?: string;
64
+ };
56
65
  }
57
66
  export declare class MemoryWriteRejectedError extends Error {
58
67
  readonly code: 'memory_limit_exceeded' | 'memory_security_rejected';
@@ -164,6 +173,8 @@ export declare class PersistentMemoryManager extends EventEmitter {
164
173
  * poking the private maps. Does NOT reinforce (unlike getRelevantMemories).
165
174
  */
166
175
  listMemories(scope: MemoryScope): Memory[];
176
+ /** Guard the result, including model renames/omissions, rather than trusting DELETE alone. */
177
+ private guardReconciliation;
167
178
  /**
168
179
  * Ebbinghaus forgetting pass (recoverable): memories whose retention decayed
169
180
  * below the threshold are appended to a sibling `*.archive.md`, then removed.
@@ -250,12 +261,6 @@ export declare class PersistentMemoryManager extends EventEmitter {
250
261
  };
251
262
  private assertScopeReadable;
252
263
  }
253
- /**
254
- * Get the memory manager. With no `botId`, returns the global singleton (default
255
- * — CLI / desktop / server behavior unchanged). With a `botId` (multi-bot
256
- * channels), returns a per-bot instance whose memory files live under
257
- * `~/.codebuddy/bots/<botId>/`, so bots never share each other's `remember` facts.
258
- */
259
- export declare function getMemoryManager(config?: Partial<MemoryConfig>, botId?: string): PersistentMemoryManager;
264
+ export declare function getMemoryManager(config?: Partial<MemoryConfig>, botId?: string, cwd?: string): PersistentMemoryManager;
260
265
  export declare function resetMemoryManagerForTests(): void;
261
- export declare function initializeMemory(config?: Partial<MemoryConfig>): Promise<PersistentMemoryManager>;
266
+ export declare function initializeMemory(config?: Partial<MemoryConfig>, cwd?: string): Promise<PersistentMemoryManager>;