@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
@@ -42,7 +42,7 @@ function mapFactCategoryToMemoryCategory(cat) {
42
42
  }
43
43
  const DEFAULT_CONFIG = {
44
44
  projectMemoryPath: ".codebuddy/CODEBUDDY_MEMORY.md",
45
- userMemoryPath: path.join(os.homedir(), ".codebuddy", "memory.md"),
45
+ get userMemoryPath() { return path.join(os.homedir(), ".codebuddy", "memory.md"); },
46
46
  autoCapture: true,
47
47
  maxMemories: 100,
48
48
  relevanceThreshold: 0.5,
@@ -215,6 +215,10 @@ export class PersistentMemoryManager extends EventEmitter {
215
215
  constructor(config = {}) {
216
216
  super();
217
217
  this.config = { ...DEFAULT_CONFIG, ...config };
218
+ // Freeze relative paths at construction; later process/session cwd changes
219
+ // must not redirect a delayed metadata flush to another project.
220
+ this.config.projectMemoryPath = path.resolve(this.config.projectMemoryPath);
221
+ this.config.userMemoryPath = path.resolve(this.config.userMemoryPath);
218
222
  }
219
223
  /**
220
224
  * Initialize memory system, loading existing memories.
@@ -446,6 +450,7 @@ export class PersistentMemoryManager extends EventEmitter {
446
450
  }
447
451
  const previousMemories = cloneMemoryMap(memories);
448
452
  let resultKey = normalizedKey;
453
+ let reconciliation = { status: 'skipped', reason: 'no LLM provider available' };
449
454
  try {
450
455
  const { FactsMemoryService } = await import('./facts-memory.js');
451
456
  const service = new FactsMemoryService();
@@ -485,6 +490,7 @@ export class PersistentMemoryManager extends EventEmitter {
485
490
  tags: mergeMemoryTags(prior?.tags, fact.source, tags)
486
491
  });
487
492
  }
493
+ await this.guardReconciliation(previousMemories, memories, scope, normalizedKey);
488
494
  if (!memories.has(normalizedKey)) {
489
495
  const newKeys = Array.from(memories.keys()).filter((memoryKey) => !previousMemories.has(memoryKey));
490
496
  const reconciledKey = newKeys[newKeys.length - 1];
@@ -493,6 +499,7 @@ export class PersistentMemoryManager extends EventEmitter {
493
499
  }
494
500
  resultKey = reconciledKey;
495
501
  }
502
+ reconciliation = { status: 'applied' };
496
503
  }
497
504
  else {
498
505
  // Fallback to default direct write
@@ -501,6 +508,8 @@ export class PersistentMemoryManager extends EventEmitter {
501
508
  }
502
509
  catch (err) {
503
510
  const message = err instanceof Error ? err.message : String(err);
511
+ reconciliation = { status: 'failed', reason: message };
512
+ resultKey = normalizedKey;
504
513
  logger.warn(`[FactsMemory] Failed to reconcile remember, falling back to default behavior: ${message}`);
505
514
  memories.clear();
506
515
  for (const [memoryKey, memory] of previousMemories) {
@@ -543,6 +552,7 @@ export class PersistentMemoryManager extends EventEmitter {
543
552
  message: status === 'updated'
544
553
  ? `Updated "${resultKey}" in ${scope} memory.`
545
554
  : `Stored "${resultKey}" in ${scope} memory.`,
555
+ reconciliation,
546
556
  };
547
557
  }
548
558
  async replace(key, value, options = {}) {
@@ -826,6 +836,45 @@ export class PersistentMemoryManager extends EventEmitter {
826
836
  ...(m.tags ? { tags: [...m.tags] } : {}),
827
837
  }));
828
838
  }
839
+ /** Guard the result, including model renames/omissions, rather than trusting DELETE alone. */
840
+ async guardReconciliation(previous, next, scope, explicitKey) {
841
+ const removed = [];
842
+ const protection = resolveForgettingConfig();
843
+ for (const [key, memory] of previous) {
844
+ const replacement = next.get(key);
845
+ if (isProtectedMemory(memory, protection)) {
846
+ if (key !== explicitKey || !replacement)
847
+ next.set(key, memory);
848
+ else
849
+ next.set(key, { ...replacement, category: memory.category, tags: mergeMemoryTags(memory.tags, undefined, replacement.tags) });
850
+ }
851
+ else if (!replacement || replacement.value !== memory.value || replacement.category !== memory.category) {
852
+ removed.push(memory);
853
+ }
854
+ }
855
+ if (!removed.length)
856
+ return;
857
+ const archivePath = this.getArchivePath(scope);
858
+ const now = new Date();
859
+ const lines = removed.map(memory => {
860
+ const tags = memory.tags?.length ? ` [${memory.tags.join(', ')}]` : '';
861
+ const ageDays = Math.max(0, Math.round((now.getTime() - memory.createdAt.getTime()) / 86_400_000));
862
+ return `- **${memory.key}** (${memory.category}${tags}, accessed ${memory.accessCount}×, age ${ageDays}d, retention 1.000): ${escapeArchiveValue(memory.value)}`;
863
+ });
864
+ // Same recoverable format as forgetting. Durable archive precedes deletion;
865
+ // failures propagate to the caller's snapshot rollback/direct-write fallback.
866
+ await withSessionLock(archivePath, async () => {
867
+ let existing = '';
868
+ try {
869
+ existing = await fs.readFile(archivePath, 'utf8');
870
+ }
871
+ catch (error) {
872
+ if (error.code !== 'ENOENT')
873
+ throw error;
874
+ }
875
+ await writeFileAtomic(archivePath, `${existing}\n## Forgotten ${now.toISOString()}\n${lines.join('\n')}\n`, { mode: 0o600 });
876
+ });
877
+ }
829
878
  /**
830
879
  * Ebbinghaus forgetting pass (recoverable): memories whose retention decayed
831
880
  * below the threshold are appended to a sibling `*.archive.md`, then removed.
@@ -1225,6 +1274,7 @@ export class PersistentMemoryManager extends EventEmitter {
1225
1274
  async autoCapture(message, response) {
1226
1275
  if (!this.config.autoCapture)
1227
1276
  return;
1277
+ const beforeCapture = cloneMemoryMap(this.projectMemories);
1228
1278
  try {
1229
1279
  const { FactsMemoryService } = await import('./facts-memory.js');
1230
1280
  const service = new FactsMemoryService();
@@ -1271,9 +1321,14 @@ export class PersistentMemoryManager extends EventEmitter {
1271
1321
  tags: mergeMemoryTags(prior?.tags, fact.source, ['auto-captured'])
1272
1322
  });
1273
1323
  }
1324
+ await this.guardReconciliation(priorProjectMemories, this.projectMemories, 'project');
1325
+ this.assertScopeWithinLimit('project');
1274
1326
  await this.saveMemories("project");
1275
1327
  }
1276
1328
  catch (err) {
1329
+ this.projectMemories.clear();
1330
+ for (const [key, memory] of beforeCapture)
1331
+ this.projectMemories.set(key, memory);
1277
1332
  const reason = err instanceof Error ? err.message : String(err);
1278
1333
  if (err instanceof FactsExtractionError) {
1279
1334
  logger.warn(`[FactsMemory] Failed to autoCapture facts: ${reason}`);
@@ -1424,40 +1479,32 @@ function unescapeArchiveValue(value) {
1424
1479
  .slice(ARCHIVE_ESCAPED_VALUE_PREFIX.length)
1425
1480
  .replace(/\\(\\|n)/g, (_match, escaped) => escaped === "n" ? "\n" : "\\");
1426
1481
  }
1427
- // Default singleton instance (no bot scope) + per-bot instances.
1428
- let memoryManagerInstance = null;
1429
- const memoryManagerByBot = new Map();
1430
- /**
1431
- * Get the memory manager. With no `botId`, returns the global singleton (default
1432
- * CLI / desktop / server behavior unchanged). With a `botId` (multi-bot
1433
- * channels), returns a per-bot instance whose memory files live under
1434
- * `~/.codebuddy/bots/<botId>/`, so bots never share each other's `remember` facts.
1435
- */
1436
- export function getMemoryManager(config, botId) {
1437
- if (!botId) {
1438
- if (!memoryManagerInstance) {
1439
- memoryManagerInstance = new PersistentMemoryManager(config);
1440
- }
1441
- return memoryManagerInstance;
1442
- }
1443
- let inst = memoryManagerByBot.get(botId);
1444
- if (!inst) {
1445
- const botDir = path.join(os.homedir(), '.codebuddy', 'bots', botId);
1446
- inst = new PersistentMemoryManager({
1447
- ...(config ?? {}),
1448
- userMemoryPath: path.join(botDir, 'memory.md'),
1449
- projectMemoryPath: path.join(botDir, 'CODEBUDDY_MEMORY.md'),
1482
+ // A project manager is shared by its tools, prompt reader and review queue.
1483
+ const memoryManagers = new Map();
1484
+ export function getMemoryManager(config, botId, cwd = process.cwd()) {
1485
+ const root = path.resolve(cwd);
1486
+ // Bot memory deliberately remains per-bot (historical channel contract).
1487
+ const key = botId ? `bot:${botId}` : `project:${root}`;
1488
+ let manager = memoryManagers.get(key);
1489
+ if (!manager) {
1490
+ const botDir = botId ? path.join(os.homedir(), '.codebuddy', 'bots', botId) : undefined;
1491
+ manager = new PersistentMemoryManager({
1492
+ projectMemoryPath: path.join(root, '.codebuddy', 'CODEBUDDY_MEMORY.md'),
1493
+ ...config,
1494
+ ...(botDir ? {
1495
+ userMemoryPath: path.join(botDir, 'memory.md'),
1496
+ projectMemoryPath: path.join(botDir, 'CODEBUDDY_MEMORY.md'),
1497
+ } : {}),
1450
1498
  });
1451
- memoryManagerByBot.set(botId, inst);
1499
+ memoryManagers.set(key, manager);
1452
1500
  }
1453
- return inst;
1501
+ return manager;
1454
1502
  }
1455
1503
  export function resetMemoryManagerForTests() {
1456
- memoryManagerInstance = null;
1457
- memoryManagerByBot.clear();
1504
+ memoryManagers.clear();
1458
1505
  }
1459
- export async function initializeMemory(config) {
1460
- const manager = getMemoryManager(config);
1506
+ export async function initializeMemory(config, cwd) {
1507
+ const manager = getMemoryManager(config, undefined, cwd);
1461
1508
  await manager.initialize();
1462
1509
  return manager;
1463
1510
  }
@@ -10,7 +10,7 @@ import * as os from 'os';
10
10
  import * as crypto from 'crypto';
11
11
  import { logger } from '../utils/logger.js';
12
12
  import { getPlatformCommands } from './platform-commands.js';
13
- import { readJsonAtomicSync, writeJsonAtomicSync } from '../utils/atomic-write.js';
13
+ import { readDeviceStoreFile, updateDeviceStoreFile } from '../utils/device-store-file.js';
14
14
  function validCoordinates(lat, lon) {
15
15
  const latitude = typeof lat === 'number'
16
16
  ? lat
@@ -74,13 +74,9 @@ export class DeviceNodeManager {
74
74
  // ==========================================================================
75
75
  loadDevices() {
76
76
  try {
77
- const data = readJsonAtomicSync(DEVICES_FILE, null, {
78
- mode: 0o600,
79
- isValid: (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value) &&
80
- value.version === DEVICES_VERSION &&
81
- Array.isArray(value.devices)),
82
- });
83
- if (data) {
77
+ // The shared envelope also carries revocations; never restore stale backups.
78
+ const data = readDeviceStoreFile(DEVICES_FILE);
79
+ if (data.version === DEVICES_VERSION && Array.isArray(data.devices)) {
84
80
  for (const d of data.devices) {
85
81
  this.devices.set(d.id, d);
86
82
  }
@@ -96,7 +92,7 @@ export class DeviceNodeManager {
96
92
  version: DEVICES_VERSION,
97
93
  devices: Array.from(this.devices.values()),
98
94
  };
99
- writeJsonAtomicSync(DEVICES_FILE, data, { mode: 0o600 });
95
+ updateDeviceStoreFile(DEVICES_FILE, envelope => Object.assign(envelope, data));
100
96
  }
101
97
  catch (err) {
102
98
  logger.warn('Failed to save devices', {
@@ -1,16 +1,29 @@
1
- import { buildMobileSupervisionSnapshot, evaluateMobileSupervisionAction, } from './mobile-supervision-snapshot.js';
1
+ import { buildMobileSupervisionSnapshot, MOBILE_SUPERVISION_ALLOWED_ACTIONS, MOBILE_SUPERVISION_BLOCKED_ACTIONS, evaluateMobileSupervisionAction, } from './mobile-supervision-snapshot.js';
2
2
  export const MOBILE_SUPERVISION_GATEWAY_CONTRACT_SCHEMA_VERSION = 1;
3
3
  export async function buildMobileSupervisionGatewayContract(query, options = {}) {
4
- const snapshot = await buildMobileSupervisionSnapshot(query, options);
4
+ // A contract-only preview must not query run/session databases. Besides
5
+ // unnecessary I/O, loading a host-native SQLite store in Electron can abort
6
+ // the process before a rejected promise can be caught by the IPC bridge.
7
+ const snapshot = options.includeSnapshot === false
8
+ ? undefined
9
+ : await buildMobileSupervisionSnapshot(query, options);
10
+ const policy = snapshot ?? {
11
+ allowedActions: [...MOBILE_SUPERVISION_ALLOWED_ACTIONS],
12
+ blockedActions: [...MOBILE_SUPERVISION_BLOCKED_ACTIONS],
13
+ safety: {
14
+ autoDispatch: false, localApprovalRequired: true, outreachDisabled: true,
15
+ remoteExecutionDisabled: true, redaction: 'secrets-redacted',
16
+ },
17
+ };
5
18
  const basePath = normalizeBasePath(options.basePath);
6
19
  const auth = buildGatewayAuth();
7
- const endpoints = buildGatewayEndpoints(snapshot, basePath, auth);
20
+ const endpoints = buildGatewayEndpoints(policy, basePath, auth);
8
21
  return {
9
22
  schemaVersion: MOBILE_SUPERVISION_GATEWAY_CONTRACT_SCHEMA_VERSION,
10
23
  generatedAt: new Date().toISOString(),
11
24
  mode: 'contract_only',
12
25
  basePath,
13
- query: snapshot.query,
26
+ query: snapshot?.query ?? query.trim(),
14
27
  auth,
15
28
  transport: {
16
29
  exposure: 'local_first',
@@ -18,11 +31,11 @@ export async function buildMobileSupervisionGatewayContract(query, options = {})
18
31
  remoteExecution: 'disabled',
19
32
  },
20
33
  endpoints,
21
- blockedOperations: snapshot.blockedActions.map(action => ({
34
+ blockedOperations: policy.blockedActions.map(action => ({
22
35
  action,
23
- policy: evaluateMobileSupervisionAction(snapshot, action),
36
+ policy: evaluateMobileSupervisionAction(policy, action),
24
37
  })),
25
- snapshot: options.includeSnapshot === false ? undefined : snapshot,
38
+ snapshot,
26
39
  };
27
40
  }
28
41
  export function renderMobileSupervisionGatewayContract(contract) {
@@ -0,0 +1,21 @@
1
+ import type { WriteStream } from 'node:fs';
2
+ export interface RunPersistenceStatus {
3
+ state: 'pending' | 'flushed' | 'failed';
4
+ received: number;
5
+ written: number;
6
+ error?: string;
7
+ }
8
+ /** Tracks stream acknowledgements separately from in-memory observability. */
9
+ export declare class RunEventWriter {
10
+ private readonly stream;
11
+ private readonly onFailure;
12
+ private received;
13
+ private written;
14
+ private failure?;
15
+ constructor(stream: WriteStream, onFailure: (error: Error) => void);
16
+ private fail;
17
+ write(line: string): void;
18
+ status(): RunPersistenceStatus;
19
+ /** A stream flush, not an fsync guarantee. Rejects on any prior lost event. */
20
+ flush(): Promise<RunPersistenceStatus>;
21
+ }
@@ -0,0 +1,81 @@
1
+ /** Tracks stream acknowledgements separately from in-memory observability. */
2
+ export class RunEventWriter {
3
+ stream;
4
+ onFailure;
5
+ received = 0;
6
+ written = 0;
7
+ failure;
8
+ constructor(stream, onFailure) {
9
+ this.stream = stream;
10
+ this.onFailure = onFailure;
11
+ stream.on('error', error => this.fail(error));
12
+ }
13
+ fail(error) {
14
+ if (this.failure)
15
+ return;
16
+ this.failure = error;
17
+ this.onFailure(error);
18
+ }
19
+ write(line) {
20
+ this.received++;
21
+ if (this.failure)
22
+ return;
23
+ if (this.stream.writableLength + Buffer.byteLength(line) > 1024 * 1024) {
24
+ this.fail(new Error('Run event queue exceeded 1 MiB; journal is incomplete'));
25
+ return;
26
+ }
27
+ try {
28
+ this.stream.write(line, error => {
29
+ if (error)
30
+ this.fail(error);
31
+ else
32
+ this.written++;
33
+ });
34
+ }
35
+ catch (error) {
36
+ this.fail(error instanceof Error ? error : new Error(String(error)));
37
+ }
38
+ }
39
+ status() {
40
+ return {
41
+ state: this.failure ? 'failed' : this.received === this.written ? 'flushed' : 'pending',
42
+ received: this.received,
43
+ written: this.written,
44
+ ...(this.failure ? { error: this.failure.message } : {}),
45
+ };
46
+ }
47
+ /** A stream flush, not an fsync guarantee. Rejects on any prior lost event. */
48
+ async flush() {
49
+ if (this.failure)
50
+ throw this.failure;
51
+ if (!this.stream.writableFinished) {
52
+ await new Promise((resolve, reject) => {
53
+ const cleanup = () => {
54
+ this.stream.removeListener('error', failed);
55
+ this.stream.removeListener('finish', finished);
56
+ this.stream.removeListener('close', closed);
57
+ };
58
+ const failed = (error) => { cleanup(); reject(error); };
59
+ const finished = () => { cleanup(); resolve(); };
60
+ const closed = () => {
61
+ if (this.stream.writableFinished)
62
+ finished();
63
+ else
64
+ failed(new Error('Run event stream closed before flush'));
65
+ };
66
+ this.stream.once('error', failed);
67
+ this.stream.once('close', closed);
68
+ if (this.stream.destroyed)
69
+ closed();
70
+ else if (this.stream.writableEnded)
71
+ this.stream.once('finish', finished);
72
+ else
73
+ this.stream.write('', error => error ? failed(error) : finished());
74
+ }).catch(error => { this.fail(error); throw error; });
75
+ }
76
+ if (this.failure)
77
+ throw this.failure;
78
+ return this.status();
79
+ }
80
+ }
81
+ //# sourceMappingURL=run-event-writer.js.map
@@ -9,6 +9,7 @@
9
9
  * Written in append mode for performance — no full-file parsing per event.
10
10
  * Automatic pruning keeps the 30 most recent runs.
11
11
  */
12
+ import { type RunPersistenceStatus } from './run-event-writer.js';
12
13
  export type RunEventType = 'run_start' | 'run_end' | 'step_start' | 'step_end' | 'tool_call' | 'tool_result' | 'patch_created' | 'patch_applied' | 'decision' | 'error' | 'metric' | 'lesson_added' | 'lesson_candidate_proposed' | 'skill_selected' | 'context_snapshot' | 'pause_suggested';
13
14
  export interface RunEvent {
14
15
  ts: number;
@@ -57,6 +58,7 @@ export interface RunSummary {
57
58
  metadata?: RunMetadata;
58
59
  }
59
60
  export interface RunRecord {
61
+ persistence?: RunPersistenceStatus;
60
62
  summary: RunSummary;
61
63
  metrics: Partial<RunMetrics>;
62
64
  artifacts: string[];
@@ -160,6 +162,7 @@ export declare class RunStore {
160
162
  private runsDir;
161
163
  /** File handles for active run event streams */
162
164
  private handles;
165
+ private eventWriters;
163
166
  /** Immediate in-process view of events, avoiding read-after-write stream races. */
164
167
  private eventBuffers;
165
168
  /** In-memory event counts per run */
@@ -205,6 +208,8 @@ export declare class RunStore {
205
208
  /**
206
209
  * Emit an event for a run. Thread-safe: writes are serialized by the writable stream.
207
210
  */
211
+ getPersistenceStatus(runId: string): RunPersistenceStatus | undefined;
212
+ flushRun(runId: string): Promise<RunPersistenceStatus>;
208
213
  emit(runId: string, event: Omit<RunEvent, 'ts' | 'runId'>): void;
209
214
  /**
210
215
  * End a run and flush the event stream.
@@ -9,6 +9,7 @@
9
9
  * Written in append mode for performance — no full-file parsing per event.
10
10
  * Automatic pruning keeps the 30 most recent runs.
11
11
  */
12
+ import { RunEventWriter } from './run-event-writer.js';
12
13
  import fs from 'fs';
13
14
  import path from 'path';
14
15
  import os from 'os';
@@ -35,6 +36,7 @@ export class RunStore {
35
36
  runsDir;
36
37
  /** File handles for active run event streams */
37
38
  handles = new Map();
39
+ eventWriters = new Map();
38
40
  /** Immediate in-process view of events, avoiding read-after-write stream races. */
39
41
  eventBuffers = new Map();
40
42
  /** In-memory event counts per run */
@@ -129,12 +131,9 @@ export class RunStore {
129
131
  const eventsPath = path.join(runDir, 'events.jsonl');
130
132
  fs.writeFileSync(eventsPath, '', { flag: 'a' }); // ensure file exists
131
133
  const ws = fs.createWriteStream(eventsPath, { flags: 'a', encoding: 'utf-8' });
132
- ws.on('error', (err) => {
133
- logger.debug('RunStore: event stream error', {
134
- runId,
135
- err: err instanceof Error ? err.message : String(err),
136
- });
137
- });
134
+ this.eventWriters.set(runId, new RunEventWriter(ws, error => {
135
+ logger.warn('RunStore: journal persistence failed', { runId, error: error.message });
136
+ }));
138
137
  this.handles.set(runId, ws);
139
138
  // Emit run_start event
140
139
  this.emit(runId, { type: 'run_start', data: { objective, metadata } });
@@ -198,6 +197,15 @@ export class RunStore {
198
197
  /**
199
198
  * Emit an event for a run. Thread-safe: writes are serialized by the writable stream.
200
199
  */
200
+ getPersistenceStatus(runId) {
201
+ return this.eventWriters.get(runId)?.status();
202
+ }
203
+ async flushRun(runId) {
204
+ const writer = this.eventWriters.get(runId);
205
+ if (!writer)
206
+ throw new Error(`No active journal writer for run ${runId}`);
207
+ return writer.flush();
208
+ }
201
209
  emit(runId, event) {
202
210
  const ws = this.handles.get(runId);
203
211
  if (!ws)
@@ -211,12 +219,7 @@ export class RunStore {
211
219
  if (buffer) {
212
220
  buffer.push(fullEvent);
213
221
  }
214
- try {
215
- ws.write(JSON.stringify(fullEvent) + '\n');
216
- }
217
- catch (err) {
218
- logger.debug('RunStore: failed to write event', { runId, err });
219
- }
222
+ this.eventWriters.get(runId)?.write(JSON.stringify(fullEvent) + '\n');
220
223
  // Update in-memory count
221
224
  const count = (this.eventCounts.get(runId) || 0) + 1;
222
225
  this.eventCounts.set(runId, count);
@@ -396,7 +399,8 @@ export class RunStore {
396
399
  catch {
397
400
  // Ignore
398
401
  }
399
- return { summary, metrics, artifacts };
402
+ const persistence = this.getPersistenceStatus(runId);
403
+ return { summary, metrics, artifacts, ...(persistence ? { persistence } : {}) };
400
404
  }
401
405
  /**
402
406
  * List runs, most recent first.
@@ -1055,6 +1059,7 @@ export class RunStore {
1055
1059
  if (ws) {
1056
1060
  ws.destroy();
1057
1061
  this.handles.delete(s.runId);
1062
+ this.eventWriters.delete(s.runId);
1058
1063
  }
1059
1064
  // Remove directory after a short delay to let the stream fully close
1060
1065
  setTimeout(() => {
@@ -5,7 +5,7 @@
5
5
  * The CLI is only a presenter. No new telemetry is written.
6
6
  */
7
7
  import os from 'node:os';
8
- import { resolveToolEffect } from '../tools/metadata.js';
8
+ import { resolveToolEffect } from '../tools/tool-effect.js';
9
9
  export const RUN_TRAJECTORY_SCHEMA_VERSION = 1;
10
10
  export const RUN_TRAJECTORY_KIND = 'run_trajectory';
11
11
  export function unlogged(reason) {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Prompt Caching Module
3
3
  *
4
- * Implements prompt caching for LLM API calls to reduce costs and latency.
5
- * Research shows up to 90% cost reduction and 80% latency improvement.
4
+ * Tracks local prompt repetition and exact component changes. Cache hits and
5
+ * savings here are heuristics, not measurements of a provider's prompt cache.
6
6
  *
7
7
  * References:
8
8
  * - OpenAI Prompt Caching: https://platform.openai.com/docs/guides/prompt-caching
@@ -31,6 +31,19 @@ export interface CacheStats {
31
31
  estimatedCostSaved: number;
32
32
  entries: number;
33
33
  }
34
+ /** Consecutive raw observations within this manager, not provider cache usage. */
35
+ export interface PrefixStabilityStats {
36
+ system: {
37
+ observations: number;
38
+ unchanged: number;
39
+ changes: number;
40
+ };
41
+ tools: {
42
+ observations: number;
43
+ unchanged: number;
44
+ changes: number;
45
+ };
46
+ }
34
47
  /**
35
48
  * Cache configuration
36
49
  */
@@ -58,11 +71,13 @@ export declare class PromptCacheManager extends EventEmitter {
58
71
  private systemPromptHash;
59
72
  private toolsHash;
60
73
  private contextHashes;
74
+ private prefixStats;
61
75
  constructor(config?: Partial<CacheConfig>);
62
76
  /**
63
77
  * Generate hash for content
64
78
  */
65
79
  private hash;
80
+ private observePrefix;
66
81
  /**
67
82
  * Estimate token count (rough approximation)
68
83
  */
@@ -103,6 +118,8 @@ export declare class PromptCacheManager extends EventEmitter {
103
118
  * Get cache statistics
104
119
  */
105
120
  getStats(): CacheStats;
121
+ /** Includes short components below the cache threshold; stores no prompt text. */
122
+ getPrefixStats(): PrefixStabilityStats;
106
123
  /**
107
124
  * Format stats for display
108
125
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Prompt Caching Module
3
3
  *
4
- * Implements prompt caching for LLM API calls to reduce costs and latency.
5
- * Research shows up to 90% cost reduction and 80% latency improvement.
4
+ * Tracks local prompt repetition and exact component changes. Cache hits and
5
+ * savings here are heuristics, not measurements of a provider's prompt cache.
6
6
  *
7
7
  * References:
8
8
  * - OpenAI Prompt Caching: https://platform.openai.com/docs/guides/prompt-caching
@@ -42,6 +42,11 @@ export class PromptCacheManager extends EventEmitter {
42
42
  systemPromptHash = null;
43
43
  toolsHash = null;
44
44
  contextHashes = new Map();
45
+ // Fixed-size diagnostics: retain only the previous component hashes and counts.
46
+ prefixStats = {
47
+ system: { observations: 0, unchanged: 0, changes: 0 },
48
+ tools: { observations: 0, unchanged: 0, changes: 0 },
49
+ };
45
50
  constructor(config = {}) {
46
51
  super();
47
52
  this.config = { ...DEFAULT_CACHE_CONFIG, ...config };
@@ -52,6 +57,23 @@ export class PromptCacheManager extends EventEmitter {
52
57
  hash(content) {
53
58
  return createHash("sha256").update(content).digest("hex").slice(0, 16);
54
59
  }
60
+ observePrefix(type, hash) {
61
+ if (!this.config.enabled)
62
+ return;
63
+ const previous = type === 'system' ? this.systemPromptHash : this.toolsHash;
64
+ const stats = this.prefixStats[type];
65
+ stats.observations++;
66
+ if (previous !== null) {
67
+ if (previous === hash)
68
+ stats.unchanged++;
69
+ else
70
+ stats.changes++;
71
+ }
72
+ if (type === 'system')
73
+ this.systemPromptHash = hash;
74
+ else
75
+ this.toolsHash = hash;
76
+ }
55
77
  /**
56
78
  * Estimate token count (rough approximation)
57
79
  */
@@ -96,9 +118,9 @@ export class PromptCacheManager extends EventEmitter {
96
118
  cacheSystemPrompt(prompt) {
97
119
  const hash = this.hash(prompt);
98
120
  const tokens = this.estimateTokens(prompt);
121
+ this.observePrefix('system', hash);
99
122
  if (tokens >= this.config.minTokensToCache) {
100
123
  this.addEntry(hash, tokens, "system");
101
- this.systemPromptHash = hash;
102
124
  }
103
125
  return hash;
104
126
  }
@@ -109,9 +131,9 @@ export class PromptCacheManager extends EventEmitter {
109
131
  const content = JSON.stringify(tools);
110
132
  const hash = this.hash(content);
111
133
  const tokens = this.estimateTokens(content);
134
+ this.observePrefix('tools', hash);
112
135
  if (tokens >= this.config.minTokensToCache) {
113
136
  this.addEntry(hash, tokens, "tools");
114
- this.toolsHash = hash;
115
137
  }
116
138
  return hash;
117
139
  }
@@ -176,6 +198,13 @@ export class PromptCacheManager extends EventEmitter {
176
198
  getStats() {
177
199
  return { ...this.stats };
178
200
  }
201
+ /** Includes short components below the cache threshold; stores no prompt text. */
202
+ getPrefixStats() {
203
+ return {
204
+ system: { ...this.prefixStats.system },
205
+ tools: { ...this.prefixStats.tools },
206
+ };
207
+ }
179
208
  /**
180
209
  * Format stats for display
181
210
  */
@@ -184,8 +213,10 @@ export class PromptCacheManager extends EventEmitter {
184
213
  "📦 Prompt Cache Statistics",
185
214
  `├─ Entries: ${this.stats.entries}`,
186
215
  `├─ Hit Rate: ${(this.stats.hitRate * 100).toFixed(1)}%`,
187
- `├─ Tokens Saved: ${this.stats.totalTokensSaved.toLocaleString()}`,
188
- `└─ Est. Cost Saved: $${this.stats.estimatedCostSaved.toFixed(4)}`,
216
+ `├─ Est. Reused Tokens: ${this.stats.totalTokensSaved.toLocaleString()}`,
217
+ `├─ Est. Cost Saved (heuristic): $${this.stats.estimatedCostSaved.toFixed(4)}`,
218
+ `└─ Prefix Changes (local): system ${this.prefixStats.system.changes}, tools ${this.prefixStats.tools.changes}`,
219
+ 'Local repetition estimates; provider cache usage is not measured.',
189
220
  ];
190
221
  return lines.join("\n");
191
222
  }
@@ -197,6 +228,10 @@ export class PromptCacheManager extends EventEmitter {
197
228
  this.systemPromptHash = null;
198
229
  this.toolsHash = null;
199
230
  this.contextHashes.clear();
231
+ this.prefixStats = {
232
+ system: { observations: 0, unchanged: 0, changes: 0 },
233
+ tools: { observations: 0, unchanged: 0, changes: 0 },
234
+ };
200
235
  logger.debug("Prompt cache cleared");
201
236
  }
202
237
  /**