@phuetz/code-buddy 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/README.fr.md +56 -0
  2. package/README.md +74 -16
  3. package/codebuddy-runtime.json +5 -5
  4. package/dist/agent/codebuddy-agent.d.ts +18 -0
  5. package/dist/agent/codebuddy-agent.js +45 -8
  6. package/dist/agent/execution/agent-executor.d.ts +0 -9
  7. package/dist/agent/execution/agent-executor.js +176 -12
  8. package/dist/agent/execution/context-pipeline.d.ts +9 -0
  9. package/dist/agent/execution/context-pipeline.js +64 -2
  10. package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
  11. package/dist/agent/execution/tool-loop-guard.js +165 -0
  12. package/dist/agent/execution/tool-selection-strategy.d.ts +7 -0
  13. package/dist/agent/execution/tool-selection-strategy.js +32 -2
  14. package/dist/agent/facades/session-facade.d.ts +0 -6
  15. package/dist/agent/facades/session-facade.js +0 -45
  16. package/dist/agent/hermes-claw-migrate.d.ts +0 -21
  17. package/dist/agent/hermes-claw-migrate.js +9 -4
  18. package/dist/agent/hermes-protocol-gateways.js +8 -1
  19. package/dist/agent/repo-profiler.js +17 -4
  20. package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
  21. package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
  22. package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
  23. package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
  24. package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
  25. package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
  26. package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
  27. package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
  28. package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
  29. package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
  30. package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
  31. package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
  32. package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
  33. package/dist/agent/self-improvement/proposal-store.js +79 -17
  34. package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
  35. package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
  36. package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
  37. package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
  38. package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
  39. package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
  40. package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
  41. package/dist/agent/self-improvement/skill-engine.js +385 -13
  42. package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
  43. package/dist/agent/self-improvement/skill-gate.js +17 -2
  44. package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
  45. package/dist/agent/self-improvement/skill-mutator.js +51 -15
  46. package/dist/agent/self-improvement/skill-types.d.ts +2 -1
  47. package/dist/agent/self-improvement/types.d.ts +11 -0
  48. package/dist/agent/tool-handler.d.ts +1 -1
  49. package/dist/agent/tool-handler.js +28 -9
  50. package/dist/auth/profile-manager.js +1 -1
  51. package/dist/channels/companion-channel-turn.d.ts +53 -4
  52. package/dist/channels/companion-channel-turn.js +218 -16
  53. package/dist/channels/slash-parity.d.ts +4 -6
  54. package/dist/channels/slash-parity.js +9 -44
  55. package/dist/cli/session-commands.d.ts +9 -0
  56. package/dist/cli/session-commands.js +45 -5
  57. package/dist/cli/session-picker.d.ts +43 -0
  58. package/dist/cli/session-picker.js +128 -0
  59. package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
  60. package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
  61. package/dist/codebuddy/fleet-tool-defs.d.ts +2 -0
  62. package/dist/codebuddy/fleet-tool-defs.js +55 -0
  63. package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
  64. package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
  65. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
  66. package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
  67. package/dist/codebuddy/providers/provider-openai-compat.d.ts +7 -0
  68. package/dist/codebuddy/providers/provider-openai-compat.js +29 -1
  69. package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
  70. package/dist/codebuddy/ragchat-tool-defs.js +19 -0
  71. package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
  72. package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
  73. package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
  74. package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
  75. package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
  76. package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
  77. package/dist/codebuddy/tools.d.ts +0 -6
  78. package/dist/codebuddy/tools.js +25 -3
  79. package/dist/commands/cli/daemon-commands.js +3 -1
  80. package/dist/commands/cli/evolve-command.js +21 -5
  81. package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
  82. package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
  83. package/dist/commands/cli/fleet-commands.js +29 -0
  84. package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
  85. package/dist/commands/cli/fleet-mission-commands.js +72 -0
  86. package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
  87. package/dist/commands/cli/fleet-rooms-commands.js +289 -0
  88. package/dist/commands/cli/tools-commands.js +2 -1
  89. package/dist/commands/cli/triage-command.d.ts +8 -0
  90. package/dist/commands/cli/triage-command.js +72 -0
  91. package/dist/commands/cli/utility-commands.js +25 -1
  92. package/dist/commands/client-dispatcher.d.ts +7 -0
  93. package/dist/commands/client-dispatcher.js +73 -21
  94. package/dist/commands/cron-cli/index.d.ts +3 -1
  95. package/dist/commands/cron-cli/index.js +25 -0
  96. package/dist/commands/device-auth.d.ts +10 -0
  97. package/dist/commands/device-auth.js +91 -0
  98. package/dist/commands/enhanced-command-handler.d.ts +9 -0
  99. package/dist/commands/enhanced-command-handler.js +28 -6
  100. package/dist/commands/handlers/agent-handlers.d.ts +1 -1
  101. package/dist/commands/handlers/agent-handlers.js +4 -4
  102. package/dist/commands/handlers/channel-handlers.js +53 -2
  103. package/dist/commands/handlers/export-handlers.d.ts +3 -2
  104. package/dist/commands/handlers/export-handlers.js +55 -28
  105. package/dist/commands/handlers/extra-handlers.js +5 -1
  106. package/dist/commands/handlers/grill-me-handler.js +26 -18
  107. package/dist/commands/handlers/memory-handlers.d.ts +6 -2
  108. package/dist/commands/handlers/memory-handlers.js +35 -15
  109. package/dist/commands/handlers/missing-handlers.d.ts +2 -2
  110. package/dist/commands/handlers/missing-handlers.js +12 -4
  111. package/dist/commands/handlers/resources-handler.d.ts +12 -0
  112. package/dist/commands/handlers/resources-handler.js +52 -0
  113. package/dist/commands/handlers/starter-handlers.d.ts +1 -1
  114. package/dist/commands/handlers/starter-handlers.js +32 -2
  115. package/dist/commands/handlers/switch-handler.d.ts +1 -1
  116. package/dist/commands/handlers/switch-handler.js +15 -0
  117. package/dist/commands/handlers/test-handlers.js +4 -86
  118. package/dist/commands/handlers/ui-handlers.js +11 -5
  119. package/dist/commands/login-chatgpt.d.ts +8 -0
  120. package/dist/commands/login-chatgpt.js +17 -0
  121. package/dist/commands/mcp-import.d.ts +11 -0
  122. package/dist/commands/mcp-import.js +67 -0
  123. package/dist/commands/mcp-invoke.d.ts +2 -0
  124. package/dist/commands/mcp-invoke.js +68 -0
  125. package/dist/commands/mcp.d.ts +13 -0
  126. package/dist/commands/mcp.js +66 -21
  127. package/dist/commands/pipeline.d.ts +7 -1
  128. package/dist/commands/pipeline.js +104 -7
  129. package/dist/commands/resources.d.ts +2 -0
  130. package/dist/commands/resources.js +64 -0
  131. package/dist/commands/skills-cli/index.js +35 -12
  132. package/dist/commands/slash/builtin-commands.js +16 -3
  133. package/dist/commands/slash/index.d.ts +3 -1
  134. package/dist/commands/slash/index.js +2 -0
  135. package/dist/commands/slash/surfaces.d.ts +53 -0
  136. package/dist/commands/slash/surfaces.js +177 -0
  137. package/dist/commands/slash/types.d.ts +17 -0
  138. package/dist/commands/try.d.ts +1 -1
  139. package/dist/commands/try.js +1 -1
  140. package/dist/companion/companion-identity.d.ts +43 -0
  141. package/dist/companion/companion-identity.js +136 -0
  142. package/dist/companion/companion-toolset.d.ts +66 -0
  143. package/dist/companion/companion-toolset.js +368 -0
  144. package/dist/companion/companion-turn.d.ts +10 -2
  145. package/dist/companion/companion-turn.js +38 -1
  146. package/dist/config/code-exec-policy.d.ts +25 -0
  147. package/dist/config/code-exec-policy.js +39 -0
  148. package/dist/config/model-tools.d.ts +9 -0
  149. package/dist/config/model-tools.js +103 -0
  150. package/dist/context/context-manager-v2.js +2 -2
  151. package/dist/context/lm-resizer-compressor.js +3 -2
  152. package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
  153. package/dist/context/lm-resizer-diagnostics.js +30 -0
  154. package/dist/context/tool-output-masking.js +3 -4
  155. package/dist/curator/curator.d.ts +6 -0
  156. package/dist/curator/curator.js +53 -3
  157. package/dist/daemon/cron-agent-bridge.d.ts +6 -0
  158. package/dist/daemon/cron-agent-bridge.js +31 -4
  159. package/dist/doctor/index.d.ts +11 -2
  160. package/dist/doctor/index.js +84 -21
  161. package/dist/doctor/integrations.d.ts +197 -0
  162. package/dist/doctor/integrations.js +241 -0
  163. package/dist/doctor/triage.d.ts +108 -0
  164. package/dist/doctor/triage.js +304 -0
  165. package/dist/errors/crash-handler.js +2 -0
  166. package/dist/fleet/collaboration.d.ts +44 -0
  167. package/dist/fleet/collaboration.js +170 -0
  168. package/dist/fleet/fleet-listener.d.ts +1 -0
  169. package/dist/fleet/fleet-listener.js +87 -46
  170. package/dist/fleet/fleet-registry.d.ts +2 -0
  171. package/dist/fleet/resource-catalog.d.ts +245 -0
  172. package/dist/fleet/resource-catalog.js +211 -0
  173. package/dist/fleet/rooms/room-access.d.ts +138 -0
  174. package/dist/fleet/rooms/room-access.js +196 -0
  175. package/dist/fleet/rooms/room-client.d.ts +158 -0
  176. package/dist/fleet/rooms/room-client.js +685 -0
  177. package/dist/fleet/rooms/room-event.d.ts +139 -0
  178. package/dist/fleet/rooms/room-event.js +330 -0
  179. package/dist/fleet/rooms/room-filter.d.ts +48 -0
  180. package/dist/fleet/rooms/room-filter.js +152 -0
  181. package/dist/fleet/rooms/room-hub.d.ts +154 -0
  182. package/dist/fleet/rooms/room-hub.js +363 -0
  183. package/dist/fleet/rooms/room-identity.d.ts +26 -0
  184. package/dist/fleet/rooms/room-identity.js +82 -0
  185. package/dist/fleet/rooms/room-lock.d.ts +34 -0
  186. package/dist/fleet/rooms/room-lock.js +180 -0
  187. package/dist/fleet/rooms/room-observations.d.ts +7 -0
  188. package/dist/fleet/rooms/room-observations.js +61 -0
  189. package/dist/fleet/rooms/room-server.d.ts +28 -0
  190. package/dist/fleet/rooms/room-server.js +75 -0
  191. package/dist/fleet/rooms/room-store.d.ts +119 -0
  192. package/dist/fleet/rooms/room-store.js +448 -0
  193. package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
  194. package/dist/fleet/rooms/room-ws-bridge.js +137 -0
  195. package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
  196. package/dist/fleet/sensory-mission-bridge.js +136 -0
  197. package/dist/git/worktree-sessions.js +1 -0
  198. package/dist/harness/contract.d.ts +4 -4
  199. package/dist/harness/fleet-supervisor.d.ts +16 -0
  200. package/dist/harness/fleet-supervisor.js +64 -0
  201. package/dist/harness/index.d.ts +4 -0
  202. package/dist/harness/index.js +4 -0
  203. package/dist/harness/mission-runner.d.ts +3 -0
  204. package/dist/harness/mission-runner.js +53 -0
  205. package/dist/harness/mission-store.d.ts +93 -0
  206. package/dist/harness/mission-store.js +297 -0
  207. package/dist/harness/tool-harness.d.ts +56 -0
  208. package/dist/harness/tool-harness.js +153 -0
  209. package/dist/hooks/use-enhanced-input.js +48 -4
  210. package/dist/hooks/use-input-handler.d.ts +1 -0
  211. package/dist/hooks/use-input-handler.js +82 -22
  212. package/dist/hooks/use-input-history.js +1 -0
  213. package/dist/identity/lisa-introspection.js +5 -2
  214. package/dist/identity/operational-self-model.d.ts +2 -0
  215. package/dist/identity/operational-self-model.js +4 -0
  216. package/dist/index.js +52 -6
  217. package/dist/mcp/client.d.ts +11 -0
  218. package/dist/mcp/client.js +68 -3
  219. package/dist/mcp/config.d.ts +20 -0
  220. package/dist/mcp/config.js +121 -8
  221. package/dist/mcp/import-normalize.d.ts +12 -0
  222. package/dist/mcp/import-normalize.js +114 -0
  223. package/dist/mcp/mcp-oauth-constants.d.ts +8 -0
  224. package/dist/mcp/mcp-oauth-constants.js +9 -0
  225. package/dist/mcp/mcp-oauth-provider.d.ts +80 -0
  226. package/dist/mcp/mcp-oauth-provider.js +241 -0
  227. package/dist/mcp/mcp-oauth.d.ts +60 -1
  228. package/dist/mcp/mcp-oauth.js +241 -65
  229. package/dist/mcp/transports.d.ts +14 -5
  230. package/dist/mcp/transports.js +112 -101
  231. package/dist/mcp/types.d.ts +5 -0
  232. package/dist/media/comfyui-recipe-contract.d.ts +7 -7
  233. package/dist/memory/facts-memory.d.ts +11 -1
  234. package/dist/memory/facts-memory.js +47 -8
  235. package/dist/memory/memory-candidate-queue.d.ts +2 -2
  236. package/dist/memory/memory-candidate-queue.js +6 -6
  237. package/dist/memory/persistent-memory.d.ts +13 -8
  238. package/dist/memory/persistent-memory.js +77 -30
  239. package/dist/nodes/device-node.js +5 -9
  240. package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
  241. package/dist/observability/run-event-writer.d.ts +21 -0
  242. package/dist/observability/run-event-writer.js +81 -0
  243. package/dist/observability/run-store.d.ts +5 -0
  244. package/dist/observability/run-store.js +18 -13
  245. package/dist/observability/run-trajectory.js +1 -1
  246. package/dist/optimization/prompt-cache.d.ts +19 -2
  247. package/dist/optimization/prompt-cache.js +41 -6
  248. package/dist/orchestration/orchestrator.d.ts +21 -0
  249. package/dist/orchestration/orchestrator.js +122 -15
  250. package/dist/persistence/session-content.d.ts +8 -0
  251. package/dist/persistence/session-content.js +76 -0
  252. package/dist/persistence/session-handoff.d.ts +64 -0
  253. package/dist/persistence/session-handoff.js +80 -0
  254. package/dist/persistence/session-history.d.ts +4 -0
  255. package/dist/persistence/session-history.js +38 -0
  256. package/dist/persistence/session-store.d.ts +9 -0
  257. package/dist/persistence/session-store.js +48 -20
  258. package/dist/personas/persona-manager.js +17 -1
  259. package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
  260. package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
  261. package/dist/plugins/plugin-manager.js +10 -1
  262. package/dist/prompts/prompt-manager.js +11 -4
  263. package/dist/prompts/system-base.js +6 -2
  264. package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
  265. package/dist/protocols/a2a/codebuddy-executor.js +9 -8
  266. package/dist/protocols/a2a/index.d.ts +1 -1
  267. package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
  268. package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
  269. package/dist/protocols/a2a/peer-config.d.ts +20 -0
  270. package/dist/protocols/a2a/peer-config.js +55 -0
  271. package/dist/providers/codex-oauth.js +30 -6
  272. package/dist/providers/model-provider-compat.js +2 -1
  273. package/dist/sandbox/os-sandbox.d.ts +1 -1
  274. package/dist/scheduler/cron-scheduler.d.ts +15 -2
  275. package/dist/scheduler/cron-scheduler.js +35 -4
  276. package/dist/scheduler/job-notepad.d.ts +69 -0
  277. package/dist/scheduler/job-notepad.js +339 -0
  278. package/dist/security/audit-logger.d.ts +1 -1
  279. package/dist/security/compute-confinement.d.ts +4 -0
  280. package/dist/security/compute-confinement.js +89 -0
  281. package/dist/security/session-encryption.d.ts +4 -0
  282. package/dist/security/session-encryption.js +31 -0
  283. package/dist/sensory/voice-loop.d.ts +17 -0
  284. package/dist/sensory/voice-loop.js +72 -15
  285. package/dist/server/auth/device-session-context.d.ts +9 -0
  286. package/dist/server/auth/device-session-context.js +13 -0
  287. package/dist/server/auth/device-store.d.ts +89 -0
  288. package/dist/server/auth/device-store.js +261 -0
  289. package/dist/server/auth/device-token.d.ts +3 -0
  290. package/dist/server/auth/device-token.js +6 -0
  291. package/dist/server/auth/jwt.js +4 -0
  292. package/dist/server/index.d.ts +0 -10
  293. package/dist/server/index.js +28 -0
  294. package/dist/server/middleware/auth.js +3 -2
  295. package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
  296. package/dist/server/routes/a2a-jsonrpc.js +66 -0
  297. package/dist/server/routes/a2a-protocol.d.ts +12 -0
  298. package/dist/server/routes/a2a-protocol.js +1 -1
  299. package/dist/server/routes/device-auth.d.ts +4 -0
  300. package/dist/server/routes/device-auth.js +40 -0
  301. package/dist/server/types.d.ts +4 -0
  302. package/dist/server/websocket/handler.d.ts +13 -0
  303. package/dist/server/websocket/handler.js +102 -17
  304. package/dist/services/prompt-builder.d.ts +2 -0
  305. package/dist/services/prompt-builder.js +16 -6
  306. package/dist/services/runtime-settings-context.d.ts +81 -0
  307. package/dist/services/runtime-settings-context.js +79 -0
  308. package/dist/skills/executor.js +2 -0
  309. package/dist/skills/hub.js +16 -14
  310. package/dist/skills/local-inventory.d.ts +21 -0
  311. package/dist/skills/local-inventory.js +72 -0
  312. package/dist/skills/skill-loader.js +2 -2
  313. package/dist/skills/skill-usage-store.d.ts +49 -0
  314. package/dist/skills/skill-usage-store.js +121 -0
  315. package/dist/testing/ai-integration-tests.js +8 -5
  316. package/dist/themes/theme-manager.d.ts +12 -0
  317. package/dist/themes/theme-manager.js +29 -0
  318. package/dist/themes/theme-schema.d.ts +24 -24
  319. package/dist/tools/a2a-call-tool.d.ts +34 -0
  320. package/dist/tools/a2a-call-tool.js +75 -0
  321. package/dist/tools/bash/streaming-executor.d.ts +1 -0
  322. package/dist/tools/bash/streaming-executor.js +78 -90
  323. package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
  324. package/dist/tools/code-exec-preflight-runner.js +254 -0
  325. package/dist/tools/code-exec-preflight.d.ts +44 -0
  326. package/dist/tools/code-exec-preflight.js +501 -0
  327. package/dist/tools/code-exec-tool.d.ts +48 -1
  328. package/dist/tools/code-exec-tool.js +191 -47
  329. package/dist/tools/comment-watcher.js +29 -12
  330. package/dist/tools/core-code-inspection.d.ts +56 -0
  331. package/dist/tools/core-code-inspection.js +86 -0
  332. package/dist/tools/create-skill-tool.d.ts +2 -3
  333. package/dist/tools/create-skill-tool.js +16 -75
  334. package/dist/tools/cronjob-tool.d.ts +1 -0
  335. package/dist/tools/cronjob-tool.js +1 -0
  336. package/dist/tools/env-doctor-tool.d.ts +2 -0
  337. package/dist/tools/env-doctor-tool.js +4 -3
  338. package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
  339. package/dist/tools/execute-code-rpc-invoker.js +35 -19
  340. package/dist/tools/execute-code-runner.d.ts +2 -0
  341. package/dist/tools/execute-code-runner.js +52 -17
  342. package/dist/tools/fleet-room-tool.d.ts +104 -0
  343. package/dist/tools/fleet-room-tool.js +124 -0
  344. package/dist/tools/integration-tool-hints.d.ts +4 -0
  345. package/dist/tools/integration-tool-hints.js +19 -0
  346. package/dist/tools/list-peers-tool.d.ts +3 -0
  347. package/dist/tools/list-peers-tool.js +11 -4
  348. package/dist/tools/media-generation-tool.d.ts +16 -1
  349. package/dist/tools/media-generation-tool.js +341 -9
  350. package/dist/tools/merge-conflict-tool.js +53 -25
  351. package/dist/tools/metadata.d.ts +1 -5
  352. package/dist/tools/metadata.js +30 -28
  353. package/dist/tools/peer-tool-invoke-tool.d.ts +61 -0
  354. package/dist/tools/peer-tool-invoke-tool.js +396 -0
  355. package/dist/tools/ragchat-tool.d.ts +39 -0
  356. package/dist/tools/ragchat-tool.js +105 -0
  357. package/dist/tools/registry/fleet-tools.d.ts +21 -3
  358. package/dist/tools/registry/fleet-tools.js +137 -4
  359. package/dist/tools/registry/index.d.ts +1 -1
  360. package/dist/tools/registry/index.js +2 -2
  361. package/dist/tools/registry/knowledge-tools.d.ts +2 -2
  362. package/dist/tools/registry/knowledge-tools.js +2 -2
  363. package/dist/tools/registry/lessons-tools.d.ts +7 -0
  364. package/dist/tools/registry/lessons-tools.js +43 -3
  365. package/dist/tools/registry/memory-tools.d.ts +1 -1
  366. package/dist/tools/registry/memory-tools.js +8 -8
  367. package/dist/tools/registry/research-tools.d.ts +1 -0
  368. package/dist/tools/registry/research-tools.js +4 -13
  369. package/dist/tools/registry/self-describe-tools.d.ts +1 -1
  370. package/dist/tools/registry/self-describe-tools.js +43 -30
  371. package/dist/tools/resource-catalog-tool.d.ts +38 -0
  372. package/dist/tools/resource-catalog-tool.js +46 -0
  373. package/dist/tools/skills-inspection-tool.js +30 -1
  374. package/dist/tools/stream-tool-output.d.ts +3 -0
  375. package/dist/tools/stream-tool-output.js +33 -0
  376. package/dist/tools/tool-call-scheduler.d.ts +10 -0
  377. package/dist/tools/tool-call-scheduler.js +58 -0
  378. package/dist/tools/tool-effect.d.ts +5 -0
  379. package/dist/tools/tool-effect.js +26 -0
  380. package/dist/tools/tool-search.d.ts +12 -11
  381. package/dist/tools/tool-search.js +67 -26
  382. package/dist/tools/types.d.ts +6 -0
  383. package/dist/ui/components/ChatHistory.d.ts +1 -1
  384. package/dist/ui/components/ChatHistory.js +2 -2
  385. package/dist/ui/components/ChatInput.d.ts +2 -1
  386. package/dist/ui/components/ChatInput.js +42 -88
  387. package/dist/ui/components/ChatInterface.d.ts +4 -2
  388. package/dist/ui/components/ChatInterface.js +27 -63
  389. package/dist/ui/components/CommandSuggestions.d.ts +0 -1
  390. package/dist/ui/components/CommandSuggestions.js +4 -6
  391. package/dist/ui/components/FileAutocomplete.js +1 -1
  392. package/dist/ui/components/KeyboardHelp.js +8 -6
  393. package/dist/ui/components/ModelSelection.js +6 -5
  394. package/dist/ui/components/StatusBar.d.ts +2 -1
  395. package/dist/ui/components/StatusBar.js +8 -7
  396. package/dist/ui/context/theme-context.js +3 -2
  397. package/dist/ui/index.d.ts +1 -1
  398. package/dist/ui/index.js +1 -1
  399. package/dist/ui/utils/markdown-renderer.js +4 -13
  400. package/dist/utils/atomic-write.d.ts +22 -0
  401. package/dist/utils/atomic-write.js +29 -0
  402. package/dist/utils/bounded-output.d.ts +16 -0
  403. package/dist/utils/bounded-output.js +63 -0
  404. package/dist/utils/config-validation/schema.d.ts +2 -2
  405. package/dist/utils/device-store-file.d.ts +4 -0
  406. package/dist/utils/device-store-file.js +40 -0
  407. package/dist/utils/export-manager.d.ts +9 -0
  408. package/dist/utils/export-manager.js +19 -0
  409. package/dist/utils/first-use-hints.d.ts +25 -0
  410. package/dist/utils/first-use-hints.js +58 -0
  411. package/dist/utils/graceful-shutdown.d.ts +8 -0
  412. package/dist/utils/graceful-shutdown.js +23 -7
  413. package/dist/utils/history-manager.js +1 -1
  414. package/dist/utils/update-notifier.js +16 -18
  415. package/dist/wizard/environment-detection.d.ts +5 -1
  416. package/dist/wizard/environment-detection.js +49 -7
  417. package/dist/wizard/onboarding.d.ts +5 -1
  418. package/dist/wizard/onboarding.js +90 -30
  419. package/dist/wizard/provider-onboarding.d.ts +2 -0
  420. package/dist/wizard/provider-onboarding.js +55 -17
  421. package/package.json +7 -3
@@ -376,6 +376,26 @@ const DEFAULT_MODEL_CONFIGS = [
376
376
  patchFormat: 'search_replace',
377
377
  strengths: ['french'],
378
378
  },
379
+ // Lemonade GGUF ids (`Qwen3.6-35B-A3B-MTP-GGUF`) must win over the hosted
380
+ // `qwen3.6*` row below. That hosted row is a 262k OpenRouter ceiling with the
381
+ // default (standard) prompt profile — in the agent loop it drowns the model
382
+ // in schemas and the model shells `list_peers` via `bash` instead of calling
383
+ // `peer_tool_invoke` (mission 8: 33.7 tok/s raw tools OK, 9× bash in-loop).
384
+ // Match is case-insensitive (`matchModel` /i). Keep the same lite surface as
385
+ // `gemma4*` which completed the fleet recipe.
386
+ {
387
+ model: 'Qwen3.6-*-GGUF*',
388
+ strengths: ['code', 'french'],
389
+ supportsReasoning: true,
390
+ supportsToolCalls: true,
391
+ supportsVision: false,
392
+ contextWindow: 32768,
393
+ maxOutputTokens: 8192,
394
+ maxToolRounds: 20,
395
+ disabledTools: ['apply_patch', 'browser', 'computer_control'],
396
+ patchFormat: 'search_replace',
397
+ promptProfile: 'lite',
398
+ },
379
399
  // Qwen 3.5 → 3.7 hébergés (262 144 chez tous les fournisseurs OpenRouter). Un runtime local
380
400
  // qui en sert moins abaisse la valeur via la découverte Ollama/LM Studio.
381
401
  {
@@ -896,6 +916,56 @@ const DEFAULT_MODEL_CONFIGS = [
896
916
  patchFormat: 'full_file',
897
917
  promptProfile: 'lite',
898
918
  },
919
+ {
920
+ model: 'qwen2.5:14b*',
921
+ strengths: ['french'],
922
+ supportsReasoning: false,
923
+ supportsToolCalls: true,
924
+ supportsVision: false,
925
+ contextWindow: 32768,
926
+ maxOutputTokens: 4096,
927
+ patchFormat: 'search_replace',
928
+ },
929
+ {
930
+ model: 'qwen2.5:32b*',
931
+ strengths: ['french'],
932
+ supportsReasoning: false,
933
+ supportsToolCalls: true,
934
+ supportsVision: false,
935
+ contextWindow: 32768,
936
+ maxOutputTokens: 8192,
937
+ patchFormat: 'search_replace',
938
+ },
939
+ {
940
+ model: 'qwen2.5:72b*',
941
+ strengths: ['french'],
942
+ supportsReasoning: false,
943
+ supportsToolCalls: true,
944
+ supportsVision: false,
945
+ contextWindow: 32768,
946
+ maxOutputTokens: 8192,
947
+ patchFormat: 'search_replace',
948
+ },
949
+ {
950
+ model: 'qwen2.5-coder:14b*',
951
+ strengths: ['french', 'code'],
952
+ supportsReasoning: false,
953
+ supportsToolCalls: true,
954
+ supportsVision: false,
955
+ contextWindow: 32768,
956
+ maxOutputTokens: 4096,
957
+ patchFormat: 'search_replace',
958
+ },
959
+ {
960
+ model: 'qwen2.5-coder:32b*',
961
+ strengths: ['french', 'code'],
962
+ supportsReasoning: false,
963
+ supportsToolCalls: true,
964
+ supportsVision: false,
965
+ contextWindow: 32768,
966
+ maxOutputTokens: 8192,
967
+ patchFormat: 'search_replace',
968
+ },
899
969
  {
900
970
  model: 'qwen2.5*',
901
971
  strengths: ['french'],
@@ -904,6 +974,7 @@ const DEFAULT_MODEL_CONFIGS = [
904
974
  // tool_calls) via Ollama, so the agent can't execute them — it stays
905
975
  // chat-only. Verified flaky against scripts/autonomy-lab/ (unlike qwen3,
906
976
  // which does emit structured calls). Use qwen3+ for autonomous editing.
977
+ // Explicitly split by size: 14b+ are promoted above, <14b stay gated.
907
978
  supportsToolCalls: false,
908
979
  supportsVision: false,
909
980
  contextWindow: 32768,
@@ -962,9 +1033,37 @@ const DEFAULT_MODEL_CONFIGS = [
962
1033
  patchFormat: 'full_file',
963
1034
  promptProfile: 'lite',
964
1035
  },
1036
+ {
1037
+ model: 'llama3.1:70b*',
1038
+ supportsReasoning: false,
1039
+ supportsToolCalls: true,
1040
+ supportsVision: false,
1041
+ contextWindow: 131072,
1042
+ maxOutputTokens: 8192,
1043
+ patchFormat: 'search_replace',
1044
+ },
1045
+ {
1046
+ model: 'llama3.1:405b*',
1047
+ supportsReasoning: false,
1048
+ supportsToolCalls: true,
1049
+ supportsVision: false,
1050
+ contextWindow: 131072,
1051
+ maxOutputTokens: 8192,
1052
+ patchFormat: 'search_replace',
1053
+ },
1054
+ {
1055
+ model: 'llama3.3*',
1056
+ supportsReasoning: false,
1057
+ supportsToolCalls: true,
1058
+ supportsVision: false,
1059
+ contextWindow: 131072,
1060
+ maxOutputTokens: 8192,
1061
+ patchFormat: 'search_replace',
1062
+ },
965
1063
  {
966
1064
  model: 'llama3*',
967
1065
  supportsReasoning: false,
1066
+ // Explicitly split by size: 70b+ promoted above, 8b and below stay gated.
968
1067
  supportsToolCalls: false,
969
1068
  supportsVision: false,
970
1069
  contextWindow: 8192,
@@ -1169,6 +1268,10 @@ export function resetRuntimeModelContextCache() {
1169
1268
  export function findModelToolConfig(modelName, customConfigs) {
1170
1269
  return findConfigMatch(modelName, [...(customConfigs || []), ...DEFAULT_MODEL_CONFIGS])?.config ?? null;
1171
1270
  }
1271
+ /** Read-only view of the built-in table (consistency tests, e.g. P5 `codeExecEvidence`). */
1272
+ export function listDefaultModelToolConfigs() {
1273
+ return DEFAULT_MODEL_CONFIGS.map((config) => Object.freeze({ ...config }));
1274
+ }
1172
1275
  export function getModelToolConfig(modelName, customConfigs) {
1173
1276
  // Use cache for default config lookups (hot path)
1174
1277
  if (!customConfigs && _configCache.has(modelName)) {
@@ -12,6 +12,7 @@
12
12
  * - Content-type-aware Compression
13
13
  * - Key Information Preservation
14
14
  */
15
+ import { truncateOutput } from '../utils/bounded-output.js';
15
16
  import { createHash } from 'node:crypto';
16
17
  import * as fs from 'fs';
17
18
  import * as path from 'path';
@@ -703,8 +704,7 @@ export class ContextManagerV2 {
703
704
  if (msg.content.length > MAX_TOOL_RESULT_LENGTH) {
704
705
  return {
705
706
  ...msg,
706
- content: msg.content.substring(0, MAX_TOOL_RESULT_LENGTH) +
707
- '\n... [truncated for context limits]',
707
+ content: truncateOutput(msg.content, MAX_TOOL_RESULT_LENGTH),
708
708
  };
709
709
  }
710
710
  }
@@ -44,8 +44,9 @@ function stringArray(value) {
44
44
  /** Resolve lm-resizer: explicit env -> stable install -> PATH -> mutable DEV build. */
45
45
  export function resolveLmResizerBin() {
46
46
  const env = process.env.CODEBUDDY_LM_RESIZER_BIN;
47
- if (env && existsSync(env))
48
- return env;
47
+ // An explicit choice must not silently fall through to an older binary.
48
+ if (env?.trim())
49
+ return env.trim();
49
50
  const stable = join(homedir(), '.local', 'bin', 'lm-resizer');
50
51
  if (existsSync(stable))
51
52
  return stable;
@@ -0,0 +1,9 @@
1
+ /** Read-only host probe. Availability alone does not establish protocol support. */
2
+ export declare function diagnoseLmResizer(): Promise<{
3
+ binary: string;
4
+ enabled: boolean;
5
+ available: boolean;
6
+ toolOutputSupported: boolean;
7
+ version: string;
8
+ warning: string;
9
+ }>;
@@ -0,0 +1,30 @@
1
+ import { execFile } from 'child_process';
2
+ import { promisify } from 'util';
3
+ import { isLmResizerEnabled, resolveLmResizerBin } from './lm-resizer-compressor.js';
4
+ const run = promisify(execFile);
5
+ /** Read-only host probe. Availability alone does not establish protocol support. */
6
+ export async function diagnoseLmResizer() {
7
+ const binary = resolveLmResizerBin();
8
+ const result = { binary, enabled: isLmResizerEnabled(), available: false, toolOutputSupported: false, version: 'unknown', warning: '' };
9
+ const options = { timeout: 3000, maxBuffer: 64 * 1024, windowsHide: true };
10
+ try {
11
+ await run(binary, ['--help'], options);
12
+ result.available = true;
13
+ try {
14
+ result.version = (await run(binary, ['--version'], options)).stdout.trim();
15
+ }
16
+ catch { /* Older CLI has no version flag. */ }
17
+ try {
18
+ const probe = await run(binary, ['tool-output', '--help'], options);
19
+ result.toolOutputSupported = /tool-output/.test(probe.stdout);
20
+ }
21
+ catch { /* Old protocol: retain raw observations. */ }
22
+ if (!result.toolOutputSupported)
23
+ result.warning = 'This binary lacks the tool-output protocol. Buddy keeps raw observations; install a compatible release to enable observation compression.';
24
+ }
25
+ catch {
26
+ result.warning = 'LM Resizer is unavailable on the host. Buddy keeps raw observations.';
27
+ }
28
+ return result;
29
+ }
30
+ //# sourceMappingURL=lm-resizer-diagnostics.js.map
@@ -9,6 +9,7 @@
9
9
  * Inspired by Gemini CLI's toolOutputMaskingService.ts
10
10
  */
11
11
  import { logger } from '../utils/logger.js';
12
+ import { truncateOutput } from '../utils/bounded-output.js';
12
13
  /** Tokens worth of newest tool outputs to protect from masking */
13
14
  const PROTECTION_THRESHOLD_CHARS = 200_000; // ~50K tokens * 4 chars/token
14
15
  /** Only trigger masking if total prunable chars exceeds this */
@@ -41,14 +42,12 @@ function generatePreview(content) {
41
42
  // Short enough — use char-based preview
42
43
  if (content.length <= SHORT_PREVIEW_CHARS * 2)
43
44
  return content;
44
- return content.substring(0, SHORT_PREVIEW_CHARS) +
45
- '\n...\n' +
46
- content.substring(content.length - SHORT_PREVIEW_CHARS);
45
+ return truncateOutput(content, SHORT_PREVIEW_CHARS * 2);
47
46
  }
48
47
  const head = lines.slice(0, HEAD_PREVIEW_LINES).join('\n');
49
48
  const tail = lines.slice(-TAIL_PREVIEW_LINES).join('\n');
50
49
  const omitted = lines.length - HEAD_PREVIEW_LINES - TAIL_PREVIEW_LINES;
51
- return `${head}\n\n... (${omitted} lines omitted) ...\n\n${tail}`;
50
+ return truncateOutput(`${head}\n\n... (${omitted} lines omitted) ...\n\n${tail}`, 4000);
52
51
  }
53
52
  /**
54
53
  * Apply tool output masking to conversation messages.
@@ -63,6 +63,12 @@ export interface CuratorDeps {
63
63
  }>>;
64
64
  /** Ledger de performance modèles (défaut : ~/.codebuddy/fleet-model-performance.jsonl). */
65
65
  modelLedgerPath?: string;
66
+ /** Activité des skills (défaut : readSkillActivity(), journal ~/.codebuddy/skill-usage). */
67
+ skillActivity?: () => Map<string, {
68
+ lastActivityAt: string;
69
+ }>;
70
+ /** Skills référencées par une tâche planifiée (défaut : jobs.json du scheduler cron). */
71
+ scheduledSkills?: () => Promise<Set<string>>;
66
72
  }
67
73
  export declare function runCuratorScan(cwd?: string, deps?: CuratorDeps): Promise<CuratorReport>;
68
74
  /**
@@ -81,6 +81,23 @@ async function scanAuthoredSkills(deps, cwd, now) {
81
81
  const patches = [];
82
82
  let authored = 0;
83
83
  let pinned = 0;
84
+ let scheduled = 0;
85
+ let activity = new Map();
86
+ try {
87
+ activity = deps.skillActivity
88
+ ? deps.skillActivity()
89
+ : (await import('../skills/skill-usage-store.js')).readSkillActivity();
90
+ }
91
+ catch {
92
+ // Télémétrie absente : repli sur la date de modification.
93
+ }
94
+ let scheduledSkills = new Set();
95
+ try {
96
+ scheduledSkills = deps.scheduledSkills ? await deps.scheduledSkills() : await readScheduledSkillNames();
97
+ }
98
+ catch {
99
+ // Pas de scheduler lisible : aucune protection supplémentaire.
100
+ }
84
101
  let entries = [];
85
102
  try {
86
103
  entries = await fs.readdir(skillsDir);
@@ -103,12 +120,22 @@ async function scanAuthoredSkills(deps, cwd, now) {
103
120
  pinned++;
104
121
  continue; // une skill épinglée n'est jamais proposée
105
122
  }
106
- const ageDays = (now.getTime() - stat.mtime.getTime()) / DAY_MS;
123
+ if (scheduledSkills.has(entry)) {
124
+ scheduled++;
125
+ continue; // utilisée par une tâche planifiée : jamais dormante
126
+ }
127
+ // Ancre = dernière activité (vue/usage) ou dernière modification, la plus récente.
128
+ const lastActivity = Date.parse(activity.get(entry)?.lastActivityAt ?? '');
129
+ const anchor = Math.max(stat.mtime.getTime(), Number.isFinite(lastActivity) ? lastActivity : 0);
130
+ const ageDays = (now.getTime() - anchor) / DAY_MS;
107
131
  if (ageDays >= STALE_SKILL_DAYS) {
132
+ const usedBefore = Number.isFinite(lastActivity);
108
133
  patches.push({
109
134
  kind: 'REVIEW_SKILL',
110
135
  target: entry,
111
- reason: `non modifiée depuis ${Math.round(ageDays)}j et non épinglée`,
136
+ reason: usedBefore
137
+ ? `aucune activité ni modification depuis ${Math.round(ageDays)}j, non épinglée`
138
+ : `jamais utilisée et non modifiée depuis ${Math.round(ageDays)}j, non épinglée`,
112
139
  autoAppliable: false, // juger l'utilité d'une skill = décision humaine
113
140
  howToApply: `buddy improve skills-pin ${entry} (garder) ou buddy improve skills-consolidate (fusionner/archiver)`,
114
141
  });
@@ -122,11 +149,34 @@ async function scanAuthoredSkills(deps, cwd, now) {
122
149
  section: {
123
150
  name: 'Skills authored',
124
151
  ok: true,
125
- summary: `${authored} authored (${pinned} épinglée(s)), ${patches.length} dormante(s) ≥ ${STALE_SKILL_DAYS}j à réviser`,
152
+ summary: `${authored} authored (${pinned} épinglée(s), ${scheduled} planifiée(s)), ${patches.length} dormante(s) ≥ ${STALE_SKILL_DAYS}j sans activité à réviser`,
126
153
  },
127
154
  patches,
128
155
  };
129
156
  }
157
+ /** Noms de skills référencées par une tâche cron `skill` (lecture seule de jobs.json). */
158
+ async function readScheduledSkillNames() {
159
+ const root = process.env.CODEBUDDY_CRON_HOME
160
+ ? path.resolve(process.env.CODEBUDDY_CRON_HOME)
161
+ : path.join(os.homedir(), '.codebuddy', 'cron');
162
+ const names = new Set();
163
+ let raw = '';
164
+ try {
165
+ raw = await fs.readFile(path.join(root, 'jobs.json'), 'utf-8');
166
+ }
167
+ catch {
168
+ return names;
169
+ }
170
+ const jobs = JSON.parse(raw);
171
+ if (!Array.isArray(jobs))
172
+ return names;
173
+ for (const job of jobs) {
174
+ const task = job?.task;
175
+ if (task?.type === 'skill' && typeof task.skill === 'string' && task.skill.trim())
176
+ names.add(task.skill.trim());
177
+ }
178
+ return names;
179
+ }
130
180
  async function scanCkg(deps) {
131
181
  const getStats = deps.ckgStats ??
132
182
  (async () => {
@@ -25,6 +25,11 @@ export interface BridgeConfig {
25
25
  * Left undefined, the bridge behaves exactly as before (no run records).
26
26
  */
27
27
  runStore?: RunStore;
28
+ /**
29
+ * Directory of per-job notepad files. Defaults to `$CODEBUDDY_CRON_HOME/notepads`
30
+ * (or `~/.codebuddy/cron/notepads`). Isolated from jobs.json.
31
+ */
32
+ notepadDir?: string;
28
33
  }
29
34
  export interface JobExecutionResult {
30
35
  jobId: string;
@@ -50,6 +55,7 @@ export interface JobExecutionResult {
50
55
  export declare class CronAgentBridge extends EventEmitter {
51
56
  private config;
52
57
  private activeJobs;
58
+ private notepad;
53
59
  constructor(config: BridgeConfig);
54
60
  /**
55
61
  * Create a task executor function for the CronScheduler
@@ -10,6 +10,7 @@ import { executeHermesLifecycleHook } from '../hooks/hermes-lifecycle-hooks.js';
10
10
  import { evaluateCronPreCheck } from '../scheduler/pre-check-runner.js';
11
11
  import { runWatchdog } from '../scheduler/watchdog-handlers.js';
12
12
  import { collectDeliveryTargets, resolveDeliveryBody } from '../scheduler/scheduled-delivery.js';
13
+ import { JobNotepadStore, defaultNotepadDir, isContinuityEnabled, } from '../scheduler/job-notepad.js';
13
14
  const DEFAULT_BRIDGE_CONFIG = {
14
15
  maxToolRounds: 20,
15
16
  jobTimeoutMs: 300000, // 5 minutes
@@ -20,9 +21,11 @@ const DEFAULT_BRIDGE_CONFIG = {
20
21
  export class CronAgentBridge extends EventEmitter {
21
22
  config;
22
23
  activeJobs = new Map();
24
+ notepad;
23
25
  constructor(config) {
24
26
  super();
25
27
  this.config = { ...DEFAULT_BRIDGE_CONFIG, ...config };
28
+ this.notepad = new JobNotepadStore(config.notepadDir ?? defaultNotepadDir());
26
29
  }
27
30
  /**
28
31
  * Create a task executor function for the CronScheduler
@@ -44,11 +47,13 @@ export class CronAgentBridge extends EventEmitter {
44
47
  try {
45
48
  // Pre-check gate: skip expensive LLM work when nothing changed.
46
49
  // Watchdog jobs are non-LLM monitors, so they bypass the pre-check.
50
+ // Fingerprint is applied only after a skip or a successful task — a
51
+ // failed run must leave lastFingerprint alone so an unchanged source retries.
52
+ let pendingFingerprint;
47
53
  if (job.preCheck && job.task.type !== 'watchdog') {
48
54
  const preCheckResult = await evaluateCronPreCheck(job.preCheck);
49
55
  if (typeof preCheckResult.fingerprint === 'string') {
50
- // Persisted by the scheduler's persistJobs() after this returns.
51
- job.preCheck.lastFingerprint = preCheckResult.fingerprint;
56
+ pendingFingerprint = preCheckResult.fingerprint;
52
57
  }
53
58
  this.emit('job:precheck', {
54
59
  jobId: job.id,
@@ -57,6 +62,9 @@ export class CronAgentBridge extends EventEmitter {
57
62
  evidence: preCheckResult.evidence,
58
63
  });
59
64
  if (!preCheckResult.shouldRun) {
65
+ if (pendingFingerprint !== undefined) {
66
+ job.preCheck.lastFingerprint = pendingFingerprint;
67
+ }
60
68
  const duration = Date.now() - startTime;
61
69
  const skipOutput = `Skipped by pre-check: ${preCheckResult.reason}`;
62
70
  this.recordRunEvent(recordedRunId, 'decision', {
@@ -139,6 +147,17 @@ export class CronAgentBridge extends EventEmitter {
139
147
  // data channel, capped at 64KB in the script runner). For all other
140
148
  // task types, the full output serves as outputData.
141
149
  const outputData = job.task.type === 'script' ? scriptStdout : output;
150
+ if (pendingFingerprint !== undefined && job.preCheck) {
151
+ job.preCheck.lastFingerprint = pendingFingerprint;
152
+ }
153
+ if (isContinuityEnabled(job.continuity)) {
154
+ try {
155
+ await this.notepad.saveLastSuccessfulOutput(job.id, outputData ?? output);
156
+ }
157
+ catch (err) {
158
+ logger.warn('Cron task succeeded but its continuity output could not be saved', { jobId: job.id, error: String(err) });
159
+ }
160
+ }
142
161
  const result = {
143
162
  jobId: job.id,
144
163
  runId: recordedRunId ?? `run-${Date.now()}`,
@@ -253,10 +272,18 @@ export class CronAgentBridge extends EventEmitter {
253
272
  logger.debug(`Cron job ${job.id}: could not load session ${job.resolvedSessionId}, starting fresh`);
254
273
  }
255
274
  }
256
- // Prepend inputData from parent chained job if available
257
275
  let message = job.task.message;
276
+ const prefixes = [];
277
+ if (isContinuityEnabled(job.continuity)) {
278
+ const section = await this.notepad.renderSection(job.id);
279
+ if (section)
280
+ prefixes.push(section.trimEnd());
281
+ }
258
282
  if (inputData) {
259
- message = `[Chained job context — output from parent job]:\n${inputData}\n\n[Task]:\n${message}`;
283
+ prefixes.push(`[Chained job context — output from parent job]:\n${inputData}`);
284
+ }
285
+ if (prefixes.length > 0) {
286
+ message = `${prefixes.join('\n\n')}\n\n[Task]:\n${message}`;
260
287
  }
261
288
  const entries = await agent.processUserMessage(message);
262
289
  const assistantEntries = entries.filter(e => e.type === 'assistant');
@@ -32,7 +32,10 @@ export interface DoctorSummary {
32
32
  * so `doctor` on a fresh machine reports 0 warnings for missing optional tools.
33
33
  */
34
34
  export declare function summarizeDoctorChecks(checks: DoctorCheck[]): DoctorSummary;
35
- export declare function checkTtsProviders(): DoctorCheck[];
35
+ /** PATH lookup without a child process (used by `buddy triage`). */
36
+ export declare function commandExistsOnPath(cmd: string, env?: NodeJS.ProcessEnv): boolean;
37
+ type CommandLookup = (cmd: string) => boolean;
38
+ export declare function checkTtsProviders(lookup?: CommandLookup): DoctorCheck[];
36
39
  /**
37
40
  * Check the server configuration derived from environment/defaults. Explicit
38
41
  * CLI flags are diagnosed again from the effective config when the server
@@ -47,5 +50,11 @@ export type { OllamaModelCandidate } from '../wizard/environment-detection.js';
47
50
  export declare function resolveOllamaModel(models: readonly string[], settings?: OllamaSelectionSettings): string | undefined;
48
51
  /** Both persisted model fields must point at a model currently served by Ollama. */
49
52
  export declare function isOllamaSelectionCurrent(models: readonly string[], settings?: OllamaSelectionSettings): boolean;
50
- export declare function runDoctorChecks(cwd?: string): Promise<DoctorCheck[]>;
53
+ export interface DoctorRunOptions {
54
+ /** Skip every network call: live key validation, OAuth refresh/discovery, local runtime probes. */
55
+ offline?: boolean;
56
+ /** Never start a child process: PATH/.git filesystem lookups, sandbox probe skipped (`buddy triage`). */
57
+ noSubprocess?: boolean;
58
+ }
59
+ export declare function runDoctorChecks(cwd?: string, options?: DoctorRunOptions): Promise<DoctorCheck[]>;
51
60
  export declare function runFixes(checks: DoctorCheck[]): Promise<FixResult[]>;
@@ -43,8 +43,34 @@ function commandExists(cmd) {
43
43
  return false;
44
44
  }
45
45
  }
46
- function getCommandAvailability(cmd) {
47
- return commandExists(cmd) ? 'installed' : 'not found';
46
+ /** PATH lookup without a child process (used by `buddy triage`). */
47
+ export function commandExistsOnPath(cmd, env = process.env) {
48
+ const executable = (file) => {
49
+ try {
50
+ if (!statSync(file).isFile())
51
+ return false;
52
+ accessSync(file, fsConstants.X_OK);
53
+ return true;
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ };
59
+ if (cmd.includes('/') || cmd.includes('\\'))
60
+ return executable(cmd);
61
+ const extensions = process.platform === 'win32'
62
+ ? ['', ...(env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)]
63
+ : [''];
64
+ for (const dir of (env.PATH ?? env.Path ?? '').split(process.platform === 'win32' ? ';' : ':')) {
65
+ if (!dir)
66
+ continue;
67
+ if (extensions.some((ext) => executable(join(dir, cmd + ext))))
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+ function getCommandAvailability(cmd, lookup = commandExists) {
73
+ return lookup(cmd) ? 'installed' : 'not found';
48
74
  }
49
75
  function checkNodeVersion() {
50
76
  const major = parseInt(process.version.slice(1), 10);
@@ -79,7 +105,7 @@ async function checkNativeSqlite() {
79
105
  };
80
106
  }
81
107
  }
82
- function checkDependencies() {
108
+ function checkDependencies(lookup = commandExists) {
83
109
  const checks = [];
84
110
  // Optionality is INTRINSIC to the tool, never derived from whether it is
85
111
  // installed: deriving `optional: !installed` would silently reclassify ANY
@@ -131,7 +157,7 @@ function checkDependencies() {
131
157
  },
132
158
  ];
133
159
  for (const dep of externalTools) {
134
- const installed = getCommandAvailability(dep.cmd) === 'installed';
160
+ const installed = getCommandAvailability(dep.cmd, lookup) === 'installed';
135
161
  checks.push({
136
162
  name: dep.label,
137
163
  status: installed ? 'ok' : dep.level,
@@ -140,7 +166,7 @@ function checkDependencies() {
140
166
  });
141
167
  }
142
168
  const audioPlayers = ['ffplay', 'aplay', 'mpv'];
143
- const found = audioPlayers.filter(cmd => commandExists(cmd));
169
+ const found = audioPlayers.filter(cmd => lookup(cmd));
144
170
  checks.push({
145
171
  name: 'Audio playback',
146
172
  status: found.length > 0 ? 'ok' : 'warn',
@@ -171,7 +197,7 @@ function checkApiKeys() {
171
197
  * credentials present (user might be using API keys instead — non-fatal).
172
198
  * `error` only when the file is corrupt or refresh fails.
173
199
  */
174
- async function checkChatGptOAuth() {
200
+ async function checkChatGptOAuth(offline = false) {
175
201
  try {
176
202
  const { hasCodexCredentials, getChatGptAuth, getCodexAuthFilePath } = await import('../providers/codex-oauth.js');
177
203
  if (!hasCodexCredentials()) {
@@ -181,6 +207,14 @@ async function checkChatGptOAuth() {
181
207
  message: `not signed in (run \`buddy login\` to use your ChatGPT subscription) — file: ${getCodexAuthFilePath()}`,
182
208
  };
183
209
  }
210
+ if (offline) {
211
+ // getChatGptAuth() may refresh (network + token rotation): never offline.
212
+ return {
213
+ name: 'ChatGPT OAuth',
214
+ status: 'ok',
215
+ message: 'credential file present (token refresh and model discovery skipped: --offline)',
216
+ };
217
+ }
184
218
  const auth = await getChatGptAuth();
185
219
  if (!auth) {
186
220
  return {
@@ -283,8 +317,8 @@ function checkStaleLockFiles(cwd) {
283
317
  }
284
318
  return checks;
285
319
  }
286
- export function checkTtsProviders() {
287
- const pocketLauncher = ['pocket-tts', 'uvx'].find((command) => commandExists(command));
320
+ export function checkTtsProviders(lookup = commandExists) {
321
+ const pocketLauncher = ['pocket-tts', 'uvx'].find((command) => lookup(command));
288
322
  const available = [];
289
323
  if (pocketLauncher)
290
324
  available.push(`Pocket TTS (via ${pocketLauncher})`);
@@ -377,7 +411,10 @@ function checkProfilePermissions() {
377
411
  message: `${dir} writable (mode ${mode.toString(8)})`,
378
412
  };
379
413
  }
380
- function checkNativeSandbox() {
414
+ function checkNativeSandbox(noSubprocess = false) {
415
+ if (noSubprocess) {
416
+ return { name: 'Native sandbox (kernel)', status: 'ok', message: 'not probed (no child process in this mode); run `buddy doctor` to probe bwrap/Landlock', optional: true };
417
+ }
381
418
  const caps = detectNativeSandboxCapabilities();
382
419
  return {
383
420
  name: 'Native sandbox (kernel)',
@@ -386,10 +423,26 @@ function checkNativeSandbox() {
386
423
  optional: true,
387
424
  };
388
425
  }
389
- function checkGit(cwd) {
390
- if (!commandExists('git')) {
426
+ function insideGitWorkTree(cwd) {
427
+ let dir = cwd;
428
+ for (;;) {
429
+ if (existsSync(join(dir, '.git')))
430
+ return true;
431
+ const parent = join(dir, '..');
432
+ if (parent === dir)
433
+ return false;
434
+ dir = parent;
435
+ }
436
+ }
437
+ function checkGit(cwd, noSubprocess = false) {
438
+ if (!(noSubprocess ? commandExistsOnPath('git') : commandExists('git'))) {
391
439
  return { name: 'Git', status: 'error', message: 'git not found' };
392
440
  }
441
+ if (noSubprocess) {
442
+ return insideGitWorkTree(cwd)
443
+ ? { name: 'Git', status: 'ok', message: 'installed, inside a git repo (.git found)' }
444
+ : { name: 'Git', status: 'warn', message: 'installed, but not inside a git repo (no .git found)' };
445
+ }
393
446
  try {
394
447
  execSync('git rev-parse --is-inside-work-tree', { cwd, stdio: 'ignore' });
395
448
  return { name: 'Git', status: 'ok', message: 'installed, inside a git repo' };
@@ -605,9 +658,9 @@ export function isOllamaSelectionCurrent(models, settings = {}) {
605
658
  * it" (env var / onboarded settings / OAuth / API key), because a running
606
659
  * Ollama that was never selected still dead-ends the first chat.
607
660
  */
608
- async function checkProviderReadiness() {
661
+ async function checkProviderReadiness(offline = false) {
609
662
  const { detectEnvironment } = await import('../wizard/environment-detection.js');
610
- const snap = await detectEnvironment();
663
+ const snap = await detectEnvironment({ offline });
611
664
  const oauthOrKey = snap.capabilities.some((c) => c.available && (c.kind === 'oauth' || c.kind === 'api-key'));
612
665
  const ollama = snap.capabilities.find((c) => c.id === 'ollama');
613
666
  const ollamaModels = ollama?.models?.length ?? 0;
@@ -651,6 +704,13 @@ async function checkProviderReadiness() {
651
704
  };
652
705
  }
653
706
  const configured = oauthOrKey || ((envLocal || onboardedLocal) && ollamaModels > 0);
707
+ if (offline && !configured && (envLocal || onboardedLocal)) {
708
+ return {
709
+ name: 'AI provider ready',
710
+ status: 'warn',
711
+ message: `local ${p || 'runtime'} configured but not probed (--offline); run \`buddy doctor\` without --offline to verify it`,
712
+ };
713
+ }
654
714
  if (configured) {
655
715
  const rec = snap.recommended;
656
716
  return {
@@ -737,30 +797,33 @@ function checkProviderFailoverHealth() {
737
797
  message: lines.slice(1).join('; ') || `${down.length} provider(s) benched`,
738
798
  };
739
799
  }
740
- export async function runDoctorChecks(cwd) {
800
+ export async function runDoctorChecks(cwd, options = {}) {
741
801
  const dir = cwd ?? process.cwd();
802
+ const offline = options.offline === true;
803
+ const noSubprocess = options.noSubprocess === true;
804
+ const lookup = noSubprocess ? (cmd) => commandExistsOnPath(cmd) : commandExists;
742
805
  const { checkLlmKeysLive } = await import('./llm-key-check.js');
743
806
  return [
744
- await checkProviderReadiness(),
807
+ await checkProviderReadiness(offline),
745
808
  checkNodeVersion(),
746
809
  await checkNativeSqlite(),
747
- ...checkDependencies(),
810
+ ...checkDependencies(lookup),
748
811
  ...checkApiKeys(),
749
812
  // Validation LIVE des clés configurées (endpoint /models, 0 token) :
750
813
  // distingue clé invalide (401/403 → error) de quota épuisé (429 → warn).
751
- ...(await checkLlmKeysLive()),
752
- await checkChatGptOAuth(),
814
+ ...(offline ? [] : await checkLlmKeysLive()),
815
+ await checkChatGptOAuth(offline),
753
816
  ...checkConfigFiles(dir),
754
817
  // Accidents de collage dans .env (commande shell, guillemets, doublons) —
755
818
  // détectés sans jamais afficher les valeurs. src/doctor/env-sanity.ts.
756
819
  ...(await import('./env-sanity.js')).checkEnvSanity(dir),
757
820
  ...checkStaleLockFiles(dir),
758
- ...checkTtsProviders(),
821
+ ...checkTtsProviders(lookup),
759
822
  checkServerExposureEnvironment(),
760
823
  checkProfilePermissions(),
761
824
  checkDiskSpace(dir),
762
- checkGit(dir),
763
- checkNativeSandbox(),
825
+ checkGit(dir, noSubprocess),
826
+ checkNativeSandbox(noSubprocess),
764
827
  checkProviderFailoverHealth(),
765
828
  ];
766
829
  }