@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
@@ -133,6 +133,10 @@ export interface JwtPayload {
133
133
  type?: 'api_key' | 'user';
134
134
  /** Optional operator role stamped by `buddy token --role`. */
135
135
  role?: 'user' | 'admin';
136
+ /** Android device proof claims; absent on legacy tokens. */
137
+ amr?: string[];
138
+ profile?: 'agent' | 'companion';
139
+ identity?: 'owner';
136
140
  }
137
141
  export interface AuthenticatedRequest {
138
142
  auth: {
@@ -26,6 +26,13 @@ interface ConnectionState {
26
26
  keyId?: string;
27
27
  /** Paired device id when authenticated via the device-pairing flow. */
28
28
  deviceId?: string;
29
+ /** Signed Android identity, available to companion extensions. */
30
+ profile?: 'agent' | 'companion';
31
+ identity?: 'owner';
32
+ amr?: readonly string[];
33
+ deviceAuthExpiresAt?: number;
34
+ /** Server configuration secret, used only for the new device token path. */
35
+ deviceJwtSecret?: string;
29
36
  scopes: string[];
30
37
  /** No-auth network clients remain transport-visible but cannot run agent chat. */
31
38
  anonymousRemote?: boolean;
@@ -77,6 +84,9 @@ export interface WebSocketExtensionPrincipal {
77
84
  /** Server-derived principal id; request payloads cannot override it. */
78
85
  readonly id: string;
79
86
  readonly source: string;
87
+ readonly profile?: 'agent' | 'companion';
88
+ readonly identity?: 'owner';
89
+ readonly amr?: readonly string[];
80
90
  readonly scopes: readonly string[];
81
91
  readonly loopback: boolean;
82
92
  readonly secure: boolean;
@@ -179,6 +189,7 @@ export declare function produceCompanionReply(message: string, options?: {
179
189
  history?: CompanionHistoryTurn[];
180
190
  /** Photos the phone attached — already validated by `validateChatAttachments`. */
181
191
  attachments?: ValidatedChatAttachment[];
192
+ userId?: string;
182
193
  }): Promise<string | {
183
194
  text: string;
184
195
  image?: {
@@ -186,6 +197,8 @@ export declare function produceCompanionReply(message: string, options?: {
186
197
  data: string;
187
198
  };
188
199
  kind?: 'selfie' | 'text';
200
+ imagePath?: string;
201
+ historySuffix?: string;
189
202
  }>;
190
203
  /**
191
204
  * Setup WebSocket server
@@ -7,6 +7,10 @@ import { validateApiKey } from '../auth/api-keys.js';
7
7
  import { logger } from "../../utils/logger.js";
8
8
  import { isOriginAllowed } from '../origin-check.js';
9
9
  import { verifyToken } from '../auth/jwt.js';
10
+ import { getDeviceAuthStore } from '../auth/device-store.js';
11
+ import { withDeviceSessionIdentity } from '../auth/device-session-context.js';
12
+ import { getPermissionModeManager } from '../../security/permission-modes.js';
13
+ import { ConfirmationService } from '../../utils/confirmation-service.js';
10
14
  import { isDirectLoopbackRequest } from '../middleware/auth.js';
11
15
  import { authenticateDevice, getGatewayPairingStore, isDevicePairingRequired } from '../../gateway/device-pairing.js';
12
16
  import { gatewayServerVersion, GATEWAY_PROTOCOL_VERSION } from '../../gateway/protocol.js';
@@ -84,6 +88,7 @@ function extensionPrincipal(state) {
84
88
  return Object.freeze({
85
89
  id,
86
90
  source,
91
+ ...sessionIdentityClaims(state),
87
92
  scopes: Object.freeze(state.authenticated ? [...state.scopes] : []),
88
93
  loopback: state.loopback === true,
89
94
  secure: state.secure === true,
@@ -138,7 +143,16 @@ function cleanupWebSocketExtensions(state) {
138
143
  }
139
144
  listeners.clear();
140
145
  }
141
- function resetWebSocketExtensionsForIdentityChange(state) {
146
+ function resetWebSocketExtensionsForIdentityChange(state, deviceIdentity = false) {
147
+ if (deviceIdentity || state.profile !== undefined) {
148
+ state.agent?.dispose?.();
149
+ state.agent = undefined;
150
+ state.agentInitializing = undefined;
151
+ }
152
+ state.profile = undefined;
153
+ state.identity = undefined;
154
+ state.amr = undefined;
155
+ state.deviceAuthExpiresAt = undefined;
142
156
  state.approvalCapable = false;
143
157
  cleanupWebSocketExtensions(state);
144
158
  state.extensionsCleaned = false;
@@ -146,6 +160,17 @@ function resetWebSocketExtensionsForIdentityChange(state) {
146
160
  // companion conversation.
147
161
  state.companionHistory = undefined;
148
162
  }
163
+ /** Conditional spreads preserve the exact legacy principal and auth payload. */
164
+ function sessionIdentityClaims(state) {
165
+ return {
166
+ ...(state.profile ? { profile: state.profile } : {}),
167
+ ...(state.identity ? { identity: state.identity } : {}),
168
+ ...(state.amr ? { amr: state.amr } : {}),
169
+ };
170
+ }
171
+ function deviceSessionIsActive(state) {
172
+ return state.deviceAuthExpiresAt === undefined || (state.deviceAuthExpiresAt > Date.now() && !!state.deviceId && getDeviceAuthStore().isActive(state.deviceId));
173
+ }
149
174
  /**
150
175
  * Add a message type without leaking the underlying socket or ConnectionState.
151
176
  * The returned function removes exactly this registration and is idempotent.
@@ -432,24 +457,40 @@ messageHandlers.set('authenticate', async (ws, state, payload) => {
432
457
  if (token) {
433
458
  // JWT_SECRET is required - if not set, authentication will fail (secure by default)
434
459
  const jwtSecret = process.env.JWT_SECRET;
435
- if (!jwtSecret) {
460
+ const configuredToken = state.deviceJwtSecret ? verifyToken(token, state.deviceJwtSecret) : null;
461
+ const deviceToken = Array.isArray(configuredToken?.amr) && configuredToken.amr.includes('device') ? configuredToken : null;
462
+ if (!jwtSecret && !deviceToken) {
436
463
  sendError(ws, 'CONFIG_ERROR', 'Server JWT configuration missing');
437
464
  return;
438
465
  }
439
- const decoded = verifyToken(token, jwtSecret);
466
+ const decoded = deviceToken ?? verifyToken(token, jwtSecret);
440
467
  if (decoded) {
441
- resetWebSocketExtensionsForIdentityChange(state);
468
+ const isDevice = Array.isArray(decoded.amr) && decoded.amr.includes('device');
469
+ if (isDevice && (!Number.isFinite(decoded.exp) || decoded.exp * 1000 <= Date.now() || !getDeviceAuthStore().isActive(decoded.sub))) {
470
+ sendError(ws, 'AUTH_FAILED', 'Invalid credentials');
471
+ return;
472
+ }
473
+ resetWebSocketExtensionsForIdentityChange(state, decoded.profile !== undefined);
442
474
  state.authenticated = true;
443
475
  state.userId = decoded.userId ?? decoded.sub;
444
476
  state.keyId = undefined;
445
- state.deviceId = undefined;
477
+ state.deviceId = isDevice ? decoded.sub : undefined;
478
+ state.deviceAuthExpiresAt = isDevice ? decoded.exp * 1000 : undefined;
479
+ state.profile = decoded.profile === 'agent' || decoded.profile === 'companion' ? decoded.profile : undefined;
480
+ state.identity = decoded.identity === 'owner' ? 'owner' : undefined;
481
+ state.amr = Array.isArray(decoded.amr) ? Object.freeze(decoded.amr.filter((method) => typeof method === 'string')) : undefined;
446
482
  state.scopes = decoded.scopes || ['chat'];
447
483
  state.anonymousRemote = false;
448
484
  if (approvalCapable === true)
449
485
  state.approvalCapable = true;
486
+ if (state.profile === 'agent') {
487
+ // Native Android is an approval surface even when the PWA is disabled.
488
+ state.approvalCapable = true;
489
+ wireMobileConfirmationBridge({ broadcast, collectApprovalSurfaceIds, registerExtension: registerWebSocketExtension });
490
+ }
450
491
  send(ws, {
451
492
  type: 'authenticated',
452
- payload: { userId: state.userId, scopes: state.scopes },
493
+ payload: { userId: state.userId, scopes: state.scopes, ...sessionIdentityClaims(state) },
453
494
  timestamp: new Date().toISOString(),
454
495
  });
455
496
  return;
@@ -507,17 +548,22 @@ export async function produceCompanionReply(message, options = {}) {
507
548
  const result = await runCompanionTurn(message, {
508
549
  surface: 'mobile',
509
550
  includeImageBytes: true,
551
+ ...(options.userId ? { userId: options.userId } : {}),
510
552
  ...(options.history ? { history: options.history } : {}),
511
553
  ...(options.attachments?.length ? { attachments: options.attachments } : {}),
512
554
  });
513
- if (result.image || result.kind === 'selfie') {
555
+ if (result.image || result.kind === 'selfie' || result.imagePath) {
514
556
  return {
515
557
  text: result.text,
516
558
  ...(result.image ? { image: result.image } : {}),
517
559
  kind: result.kind,
560
+ ...(result.imagePath ? { imagePath: result.imagePath } : {}),
561
+ ...(result.historySuffix ? { historySuffix: result.historySuffix } : {}),
518
562
  };
519
563
  }
520
- return result.text;
564
+ return result.historySuffix
565
+ ? { text: result.text, kind: 'text', historySuffix: result.historySuffix }
566
+ : result.text;
521
567
  }
522
568
  /** Persistence identity for this connection, or undefined (memory-only). */
523
569
  function companionHistoryIdentity(state) {
@@ -535,7 +581,9 @@ function companionHistoryFor(state) {
535
581
  }
536
582
  /** Record one companion exchange; never stores image bytes. */
537
583
  function rememberCompanionTurn(state, userText, produced) {
538
- const assistantText = typeof produced === 'string' ? produced : produced.text;
584
+ const baseAssistantText = typeof produced === 'string' ? produced : produced.text;
585
+ const historySuffix = typeof produced === 'string' ? '' : (produced.historySuffix ?? '');
586
+ const assistantText = `${baseAssistantText}${historySuffix}`.trim();
539
587
  const kind = typeof produced === 'string' ? undefined : produced.kind;
540
588
  state.companionHistory = appendCompanionHistory(companionHistoryFor(state), [
541
589
  { role: 'user', content: userText },
@@ -638,7 +686,8 @@ messageHandlers.set('chat', async (ws, state, payload) => {
638
686
  }
639
687
  const turn = { cancelled: false, abortDelivered: false };
640
688
  state.activeTurn = turn;
641
- const assistant = typeof assistantRaw === 'string' ? assistantRaw.trim() : 'agent';
689
+ const assistant = state.profile === 'agent' ? 'agent'
690
+ : typeof assistantRaw === 'string' ? assistantRaw.trim() : state.profile ?? 'agent';
642
691
  const peerId = typeof peerIdRaw === 'string' ? peerIdRaw.trim() : '';
643
692
  // Photos are accepted only for the companion; every other assistant keeps the
644
693
  // exact payload contract it had.
@@ -689,6 +738,7 @@ messageHandlers.set('chat', async (ws, state, payload) => {
689
738
  const history = companionHistoryFor(state);
690
739
  const produced = await produceCompanionReply(userText, {
691
740
  history,
741
+ userId: state.userId,
692
742
  ...(imageAttachments.length ? { attachments: imageAttachments } : {}),
693
743
  });
694
744
  spoken = typeof produced === 'string' ? produced : produced.text;
@@ -1069,13 +1119,22 @@ messageHandlers.set('peer:request', async (ws, state, payload) => {
1069
1119
  sendError(ws, 'UNAUTHORIZED', 'Authentication required');
1070
1120
  return;
1071
1121
  }
1072
- if (!state.scopes.includes('peer:invoke')) {
1073
- sendError(ws, 'FORBIDDEN', 'peer:invoke scope required');
1074
- return;
1075
- }
1076
1122
  // payload is the request frame { id, method, params, traceId?, depth? }
1077
1123
  const frame = (payload ?? {});
1078
1124
  const requestId = frame.id ?? '';
1125
+ if (!state.scopes.includes('peer:invoke')) {
1126
+ // Match the rejected RPC so callers fail immediately instead of timing out.
1127
+ send(ws, {
1128
+ type: 'peer:response',
1129
+ payload: {
1130
+ id: requestId || 'unknown',
1131
+ ok: false,
1132
+ error: { code: 'FORBIDDEN', message: 'peer:invoke scope required' },
1133
+ },
1134
+ timestamp: new Date().toISOString(),
1135
+ });
1136
+ return;
1137
+ }
1079
1138
  if (!checkRateLimit(state, 'peerRequestCount', 'peerWindowStart', RATE_LIMITS.peerRequestsPerMinute, 60_000)) {
1080
1139
  send(ws, {
1081
1140
  type: 'peer:response',
@@ -1165,9 +1224,28 @@ async function processMessage(ws, state, data) {
1165
1224
  ...(id ? { id } : {}),
1166
1225
  ...(requestId ? { requestId } : {}),
1167
1226
  };
1227
+ const invoke = async () => {
1228
+ if (type !== 'authenticate' && !deviceSessionIsActive(state)) {
1229
+ state.authenticated = false;
1230
+ state.approvalCapable = false;
1231
+ abortActiveTurn(state);
1232
+ sendError(ws, 'AUTH_FAILED', 'Invalid credentials', id);
1233
+ return;
1234
+ }
1235
+ const run = () => handler(ws, state, payload ?? {}, envelope);
1236
+ const withIdentity = () => state.profile || state.identity || state.amr
1237
+ ? withDeviceSessionIdentity({
1238
+ ...sessionIdentityClaims(state), ...(state.deviceId ? { deviceId: state.deviceId } : {}),
1239
+ }, run)
1240
+ : run();
1241
+ if (state.profile === 'agent') {
1242
+ return getPermissionModeManager().withModeAsync('default', () => ConfirmationService.getInstance().withApprovalContextAsync(`ws:${state.id}`, withIdentity));
1243
+ }
1244
+ return withIdentity();
1245
+ };
1168
1246
  if (laneBypassMessageTypes.has(type)) {
1169
1247
  try {
1170
- await handler(ws, state, payload ?? {}, envelope);
1248
+ await invoke();
1171
1249
  }
1172
1250
  catch (error) {
1173
1251
  sendError(ws, 'HANDLER_ERROR', error instanceof Error ? error.message : String(error), id);
@@ -1185,14 +1263,14 @@ async function processMessage(ws, state, data) {
1185
1263
  ? payload
1186
1264
  : {};
1187
1265
  const peerRequestId = typeof frame.id === 'string' ? frame.id : 'unknown';
1188
- await enqueuePeerHandler(state, () => enqueueMessage(`${sessionKey}:peer:${peerRequestId}`, () => handler(ws, state, payload ?? {}, envelope), {
1266
+ await enqueuePeerHandler(state, () => enqueueMessage(`${sessionKey}:peer:${peerRequestId}`, invoke, {
1189
1267
  parallel: true,
1190
1268
  // Channel default is 120s; cold local Ollama peer.chat exceeded it (GK17).
1191
1269
  timeout: PEER_REQUEST_LANE_TIMEOUT_MS,
1192
1270
  }));
1193
1271
  }
1194
1272
  else {
1195
- await enqueueMessage(sessionKey, () => handler(ws, state, payload ?? {}, envelope));
1273
+ await enqueueMessage(sessionKey, invoke);
1196
1274
  }
1197
1275
  }
1198
1276
  catch (error) {
@@ -1267,6 +1345,7 @@ export async function setupWebSocket(server, config) {
1267
1345
  const state = {
1268
1346
  id: generateConnectionId(),
1269
1347
  authenticated: !config.authEnabled, // Auto-auth if auth disabled
1348
+ deviceJwtSecret: config.jwtSecret,
1270
1349
  scopes: config.authEnabled
1271
1350
  ? []
1272
1351
  : [
@@ -1416,6 +1495,8 @@ function toBroadcastTarget(state) {
1416
1495
  export function collectApprovalSurfaceIds() {
1417
1496
  const ids = [];
1418
1497
  for (const state of connections.values()) {
1498
+ if (!isMobilePwaEnabled() && state.profile !== 'agent')
1499
+ continue;
1419
1500
  if (!state.authenticated)
1420
1501
  continue;
1421
1502
  if (state.anonymousRemote)
@@ -1424,6 +1505,8 @@ export function collectApprovalSurfaceIds() {
1424
1505
  continue;
1425
1506
  if (!state.scopes.includes('tools'))
1426
1507
  continue;
1508
+ if (!deviceSessionIsActive(state))
1509
+ continue;
1427
1510
  ids.push(state.id);
1428
1511
  }
1429
1512
  return ids;
@@ -1434,6 +1517,8 @@ export function broadcast(message, scopeFilter, targetFilter) {
1434
1517
  for (const [ws, state] of connections.entries()) {
1435
1518
  if (!state.authenticated)
1436
1519
  continue;
1520
+ if (!deviceSessionIsActive(state))
1521
+ continue;
1437
1522
  if (scopeFilter && !state.scopes.includes(scopeFilter))
1438
1523
  continue;
1439
1524
  if (targetFilter && !targetFilter(toBroadcastTarget(state)))
@@ -130,6 +130,8 @@ export declare class PromptBuilder {
130
130
  * would otherwise tempt the LLM to hallucinate JSON tool calls.
131
131
  */
132
132
  buildForQuery(message: string, systemPromptId: string | undefined, modelName: string, customInstructions: string | null): Promise<string>;
133
+ /** Keep prompt reads bound to the same project/bot manager as tool writes. */
134
+ setPersistentMemory(manager: PersistentMemoryManager): void;
133
135
  /**
134
136
  * Update configuration
135
137
  */
@@ -515,16 +515,20 @@ export class PromptBuilder {
515
515
  const { getCodeExplorerManager } = await import('../plugins/code-explorer/CodeExplorerManager.js');
516
516
  const fresh = getCodeExplorerManager(this.config.cwd).getFreshness();
517
517
  if (fresh.stale) {
518
- const behind = fresh.commitsBehind !== undefined ? `${fresh.commitsBehind} commit(s)` : 'some commits';
518
+ const state = fresh.unverified
519
+ ? 'could not be verified against Git HEAD'
520
+ : 'does not match Git HEAD';
519
521
  staleNote =
520
- `\n⚠️ The Code Explorer index is STALE built at commit ${fresh.lastCommit?.slice(0, 8) ?? '?'}, ` +
521
- `now ${behind} behind HEAD. Its answers may miss recent code; treat relationship results as ` +
522
- `approximate and suggest re-running \`code-explorer analyze --incremental\` if they look wrong.`;
522
+ `\n⚠️ The Code Explorer index ${state}. Its answers may miss current code; ` +
523
+ `treat relationship results as approximate and suggest re-running ` +
524
+ `\`code-explorer analyze --incremental\` if they look wrong.`;
523
525
  }
524
526
  }
525
527
  catch { /* freshness best-effort */ }
526
528
  const codeExplorerBlock = `<code_explorer_priority>\n` +
527
- `Code Explorer is connected. For ANY question about code relationships ` +
529
+ `Code Explorer is connected through MCP on the host. Use these MCP tools directly; ` +
530
+ `do not run the host code-explorer binary through Bash, which may execute inside an isolated container. ` +
531
+ `For questions about code relationships — ` +
528
532
  `callers/callees, blast radius / impact ("what breaks if I change X"), dead code, cycles, ` +
529
533
  `coupling, complexity — PREFER its MCP tools (\`${p}impact\`, ` +
530
534
  `\`${p}context\`, \`${p}query\`, \`${p}find_cycles\`, …) ` +
@@ -921,6 +925,10 @@ Output formatting discipline:
921
925
  });
922
926
  return this.buildSystemPrompt(systemPromptId, modelName, customInstructions, gates, message);
923
927
  }
928
+ /** Keep prompt reads bound to the same project/bot manager as tool writes. */
929
+ setPersistentMemory(manager) {
930
+ this.persistentMemory = manager;
931
+ }
924
932
  /**
925
933
  * Update configuration
926
934
  */
@@ -0,0 +1,73 @@
1
+ export interface RuntimeSettingsEvidence {
2
+ surface?: string;
3
+ model?: string;
4
+ provider?: string;
5
+ maxToolRounds?: number;
6
+ }
7
+ /** Explicit allowlist: never serialize configuration objects, env, endpoints or credentials. */
8
+ export declare function getRuntimeSettingsSnapshot(evidence?: RuntimeSettingsEvidence): {
9
+ guidance: string;
10
+ fleet?: {
11
+ supported: boolean;
12
+ configuredConnections: number;
13
+ statusTool: string;
14
+ statusArguments: {
15
+ includeCapabilities: boolean;
16
+ };
17
+ delegationTool: string;
18
+ routingTool: string;
19
+ connectionCommand: string;
20
+ scope: string;
21
+ } | undefined;
22
+ theme: {
23
+ active: string;
24
+ available: (string | undefined)[];
25
+ customOverrides: {
26
+ colors: string[];
27
+ avatars: string[];
28
+ };
29
+ lastPreferenceSave: boolean | null;
30
+ changeCommand: string;
31
+ statusCommand: string;
32
+ };
33
+ permissionMode?: import("../security/permission-modes.js").PermissionMode | undefined;
34
+ programmaticToolCalling: {
35
+ guidance?: string | undefined;
36
+ tool: string;
37
+ policy: import("../config/code-exec-policy.js").CodeExecPolicy;
38
+ source: "default" | "model" | "env";
39
+ };
40
+ maxToolRounds?: number | undefined;
41
+ surface: string;
42
+ model: string;
43
+ provider: string;
44
+ } | {
45
+ guidance: string;
46
+ fleet?: {
47
+ supported: boolean;
48
+ configuredConnections: number;
49
+ statusTool: string;
50
+ statusArguments: {
51
+ includeCapabilities: boolean;
52
+ };
53
+ delegationTool: string;
54
+ routingTool: string;
55
+ connectionCommand: string;
56
+ scope: string;
57
+ } | undefined;
58
+ theme: null;
59
+ permissionMode?: import("../security/permission-modes.js").PermissionMode | undefined;
60
+ programmaticToolCalling: {
61
+ guidance?: string | undefined;
62
+ tool: string;
63
+ policy: import("../config/code-exec-policy.js").CodeExecPolicy;
64
+ source: "default" | "model" | "env";
65
+ };
66
+ maxToolRounds?: number | undefined;
67
+ surface: string;
68
+ model: string;
69
+ provider: string;
70
+ };
71
+ export declare function formatRuntimeSettingsContext(evidence: RuntimeSettingsEvidence): string;
72
+ /** Ensure operational questions can reach the existing tools even with RAG selection. */
73
+ export declare function runtimeInspectionTools(query: string): string[];
@@ -0,0 +1,69 @@
1
+ import { getThemeManager } from '../themes/theme-manager.js';
2
+ import { getFleetRegistry } from '../fleet/fleet-registry.js';
3
+ import { getPermissionModeManager } from '../security/permission-modes.js';
4
+ import { CODE_EXEC_OFF_NOTICE, CODE_EXEC_PREFER_HINT, resolveCodeExecPolicy } from '../config/code-exec-policy.js';
5
+ function identifier(value) {
6
+ return typeof value === 'string' && /^[a-zA-Z0-9_.:/-]{1,160}$/.test(value) ? value : undefined;
7
+ }
8
+ /** P5: effective code_exec policy and its short model-facing guidance. */
9
+ function programmaticToolCallingSnapshot(model) {
10
+ const resolved = resolveCodeExecPolicy(identifier(model));
11
+ return {
12
+ tool: 'code_exec',
13
+ policy: resolved.policy,
14
+ source: resolved.source,
15
+ ...(resolved.policy === 'prefer' ? { guidance: CODE_EXEC_PREFER_HINT } : {}),
16
+ ...(resolved.policy === 'off' ? { guidance: CODE_EXEC_OFF_NOTICE } : {}),
17
+ };
18
+ }
19
+ /** Explicit allowlist: never serialize configuration objects, env, endpoints or credentials. */
20
+ export function getRuntimeSettingsSnapshot(evidence = {}) {
21
+ const cli = evidence.surface === 'cli';
22
+ const manager = cli ? getThemeManager() : undefined;
23
+ return {
24
+ surface: identifier(evidence.surface) ?? 'unknown',
25
+ model: identifier(evidence.model) ?? 'unknown',
26
+ provider: identifier(evidence.provider) ?? 'unknown',
27
+ ...(Number.isSafeInteger(evidence.maxToolRounds) && evidence.maxToolRounds > 0
28
+ ? { maxToolRounds: evidence.maxToolRounds } : {}),
29
+ programmaticToolCalling: programmaticToolCallingSnapshot(evidence.model),
30
+ ...(cli ? { permissionMode: getPermissionModeManager().getMode() } : {}),
31
+ ...(manager ? {
32
+ theme: {
33
+ active: identifier(manager.getCurrentTheme().id) ?? 'custom',
34
+ available: manager.getAvailableThemes().map(theme => identifier(theme.id)).filter(Boolean),
35
+ customOverrides: manager.getOverrideKeys(),
36
+ lastPreferenceSave: manager.getPreferenceSaveStatus(),
37
+ changeCommand: '/theme <id>',
38
+ statusCommand: '/theme status',
39
+ },
40
+ } : { theme: null }),
41
+ ...(cli ? { fleet: {
42
+ supported: true,
43
+ configuredConnections: getFleetRegistry().list().length,
44
+ statusTool: 'list_peers',
45
+ statusArguments: { includeCapabilities: true },
46
+ delegationTool: 'peer_delegate',
47
+ routingTool: 'route_peer',
48
+ connectionCommand: '/fleet listen <ws-url> --name <id>',
49
+ scope: 'Configured connections only; an empty registry does not prove no other Buddy is running on the network. Do not claim local-only capability. Probe list_peers before asserting availability.',
50
+ } } : {}),
51
+ guidance: 'Live settings at observation time. Unknown values are not inferred from saved preferences. A null theme means no CLI theme is attested for this surface. Theme changes require /theme <id>; editing a config file alone does not apply a live change.',
52
+ };
53
+ }
54
+ export function formatRuntimeSettingsContext(evidence) {
55
+ return '<runtime_settings ephemeral="true">\n' +
56
+ JSON.stringify(getRuntimeSettingsSnapshot(evidence)) + '\n</runtime_settings>';
57
+ }
58
+ /** Ensure operational questions can reach the existing tools even with RAG selection. */
59
+ export function runtimeInspectionTools(query) {
60
+ const text = query.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
61
+ if (/\b(fleet|flotte|peers?|autres? (?:code[ -]?)?budd(?:y|ies)|other (?:code[ -]?)?budd(?:y|ies)|instances? (?:actives?|buddy)|buddy.*ensemble)\b/.test(text)) {
62
+ return ['list_peers', 'route_peer', 'peer_delegate'];
63
+ }
64
+ if (/\b(theme|parametres?|parametrage|settings|configuration actuelle|current configuration)\b/.test(text)) {
65
+ return ['self_describe'];
66
+ }
67
+ return [];
68
+ }
69
+ //# sourceMappingURL=runtime-settings-context.js.map
@@ -6,6 +6,7 @@
6
6
  import { EventEmitter } from 'events';
7
7
  import { SkillVariableResolver } from './skill-enhancements.js';
8
8
  import { resolveBashInjections } from './bash-injection.js';
9
+ import { recordSkillActivity } from './skill-usage-store.js';
9
10
  export const DEFAULT_EXECUTOR_CONFIG = {
10
11
  timeout: 60000,
11
12
  stepMode: false,
@@ -29,6 +30,7 @@ export class SkillExecutor extends EventEmitter {
29
30
  const startTime = Date.now();
30
31
  const toolCalls = [];
31
32
  this.emit('skill:start', skill, context);
33
+ recordSkillActivity(skill.metadata.name, 'use', { source: 'executor' });
32
34
  try {
33
35
  // Check requirements
34
36
  const reqCheck = this.checkRequirements(skill, context);
@@ -23,19 +23,21 @@ export { generateSkillSigningKeyPair, signSkillContent, signRegistryIndexPayload
23
23
  // ============================================================================
24
24
  // Constants
25
25
  // ============================================================================
26
- const DEFAULT_HUB_CONFIG = {
27
- registryUrl: 'https://hub.codebuddy.dev/api/v1',
28
- cacheDir: path.join(os.homedir(), '.codebuddy', 'hub', 'cache'),
29
- skillsDir: path.join(os.homedir(), '.codebuddy', 'skills', 'managed'),
30
- lockfilePath: path.join(os.homedir(), '.codebuddy', 'hub', 'lock.json'),
31
- tapsPath: path.join(os.homedir(), '.codebuddy', 'hub', 'taps.json'),
32
- trustedKeysPath: path.join(os.homedir(), '.codebuddy', 'hub', 'trusted-keys.json'),
33
- requireSignedInstalls: false,
34
- githubApiBaseUrl: 'https://api.github.com',
35
- githubRawBaseUrl: 'https://raw.githubusercontent.com',
36
- autoUpdate: false,
37
- checkIntervalMs: 24 * 60 * 60 * 1000, // 24 hours
38
- };
26
+ function defaultHubConfig() {
27
+ return {
28
+ registryUrl: 'https://hub.codebuddy.dev/api/v1',
29
+ cacheDir: path.join(os.homedir(), '.codebuddy', 'hub', 'cache'),
30
+ skillsDir: path.join(os.homedir(), '.codebuddy', 'skills', 'managed'),
31
+ lockfilePath: path.join(os.homedir(), '.codebuddy', 'hub', 'lock.json'),
32
+ tapsPath: path.join(os.homedir(), '.codebuddy', 'hub', 'taps.json'),
33
+ trustedKeysPath: path.join(os.homedir(), '.codebuddy', 'hub', 'trusted-keys.json'),
34
+ requireSignedInstalls: false,
35
+ githubApiBaseUrl: 'https://api.github.com',
36
+ githubRawBaseUrl: 'https://raw.githubusercontent.com',
37
+ autoUpdate: false,
38
+ checkIntervalMs: 24 * 60 * 60 * 1000, // 24 hours
39
+ };
40
+ }
39
41
  const LOCKFILE_VERSION = 1;
40
42
  const TAPS_FILE_VERSION = 1;
41
43
  const TRUSTED_KEYS_FILE_VERSION = 1;
@@ -107,7 +109,7 @@ export class SkillsHub extends EventEmitter {
107
109
  cacheTtlMs = 5 * 60 * 1000; // 5 minutes
108
110
  constructor(config = {}) {
109
111
  super();
110
- this.config = { ...DEFAULT_HUB_CONFIG, ...config };
112
+ this.config = { ...defaultHubConfig(), ...config };
111
113
  this.lockfile = this.readLockfile();
112
114
  this.ensureDirectories();
113
115
  }
@@ -0,0 +1,21 @@
1
+ import type { InstalledSkillStatus } from './hub.js';
2
+ import type { SkillRequirements } from './types.js';
3
+ export interface LocalSkillEntry {
4
+ name: string;
5
+ description: string;
6
+ source: string;
7
+ path?: string;
8
+ version?: string;
9
+ requires?: SkillRequirements;
10
+ available: boolean;
11
+ reason?: 'disabled' | 'missing-file' | 'integrity-mismatch' | 'invalid-skill';
12
+ }
13
+ /** Read the same local skill tiers without activating skills or starting watchers. */
14
+ export declare function readLocalSkillInventory(installed: InstalledSkillStatus[]): Promise<{
15
+ entries: LocalSkillEntry[];
16
+ errors: Array<{
17
+ path: string;
18
+ message: string;
19
+ }>;
20
+ }>;
21
+ export declare function readLocalSkillContent(skill: LocalSkillEntry): Promise<string>;
@@ -0,0 +1,72 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { getBundledSkillsPath } from './index.js';
3
+ import { SkillRegistry } from './registry.js';
4
+ import { SkillManager } from './skill-manager.js';
5
+ /** Read the same local skill tiers without activating skills or starting watchers. */
6
+ export async function readLocalSkillInventory(installed) {
7
+ const registry = new SkillRegistry({ bundledPath: getBundledSkillsPath(), watchEnabled: false });
8
+ const errors = [];
9
+ registry.on('skill:error', (path, error) => errors.push({ path, message: error.message }));
10
+ await registry.load();
11
+ const entries = new Map();
12
+ // Built-in legacy specializations still exposed by /skill. Do not load custom
13
+ // files through the legacy parser: SKILL.md files must pass registry validation.
14
+ const manager = new SkillManager();
15
+ for (const name of manager.getAvailableSkills()) {
16
+ const skill = manager.getSkill(name);
17
+ entries.set(name, { name, description: skill.description, source: 'builtin', available: true });
18
+ }
19
+ for (const skill of registry.list()) {
20
+ entries.set(skill.metadata.name, {
21
+ name: skill.metadata.name,
22
+ description: skill.metadata.description,
23
+ source: skill.tier,
24
+ path: skill.sourcePath,
25
+ version: skill.metadata.version,
26
+ requires: skill.metadata.requires,
27
+ available: skill.enabled !== false,
28
+ ...(skill.enabled === false ? { reason: 'disabled' } : {}),
29
+ });
30
+ }
31
+ for (const skill of installed) {
32
+ let previous = entries.get(skill.name);
33
+ let reason = skill.enabled === false ? 'disabled'
34
+ : !skill.exists ? 'missing-file'
35
+ : !skill.integrityOk ? 'integrity-mismatch' : undefined;
36
+ if (!reason) {
37
+ try {
38
+ const parsed = await registry.registerSkillFile(skill.path, 'managed');
39
+ if (registry.get(parsed.metadata.name)?.sourcePath !== skill.path) {
40
+ reason = 'invalid-skill';
41
+ }
42
+ else {
43
+ previous = { name: skill.name, description: parsed.metadata.description,
44
+ source: 'hub', available: true, requires: parsed.metadata.requires };
45
+ }
46
+ }
47
+ catch (error) {
48
+ reason = 'invalid-skill';
49
+ errors.push({ path: skill.path, message: error instanceof Error ? error.message : String(error) });
50
+ }
51
+ }
52
+ // Hub lifecycle state takes precedence; never present a disabled or altered
53
+ // installed skill as usable via a same-name bundled fallback.
54
+ entries.set(skill.name, {
55
+ name: skill.name,
56
+ description: previous?.description ?? '',
57
+ requires: previous?.requires,
58
+ source: 'hub',
59
+ path: skill.path,
60
+ version: skill.version,
61
+ available: !reason,
62
+ ...(reason ? { reason } : {}),
63
+ });
64
+ }
65
+ return { entries: [...entries.values()], errors };
66
+ }
67
+ export async function readLocalSkillContent(skill) {
68
+ if (skill.path)
69
+ return readFile(skill.path, 'utf-8');
70
+ return new SkillManager().getSkill(skill.name)?.systemPrompt ?? '';
71
+ }
72
+ //# sourceMappingURL=local-inventory.js.map