@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
@@ -5,12 +5,18 @@
5
5
  *
6
6
  * @module agent/self-improvement/skill-engine
7
7
  */
8
+ import path from 'node:path';
9
+ import { createHash } from 'node:crypto';
10
+ import { evaluateSkillBehavior } from './skill-behavior-gate.js';
11
+ import { SKILL_BEHAVIOR_TASKS } from './skill-behavior-benchmark.js';
8
12
  import { EvolutionaryArchive } from './evolutionary-archive.js';
9
13
  import { resolveAutonomy } from './engine.js';
10
14
  import { PendingProposalStore } from './proposal-store.js';
15
+ import { SkillApplyJournal } from './skill-apply-journal.js';
11
16
  import { validateSkillProposal } from './skill-gate.js';
12
- import { LiveSkillMutator } from './skill-mutator.js';
17
+ import { ensureFrontmatter, isAuthoredSkillName, LiveSkillMutator } from './skill-mutator.js';
13
18
  export class SkillImprovementEngine {
19
+ evaluateBehavior;
14
20
  scenarios;
15
21
  proposer;
16
22
  mutator;
@@ -18,27 +24,64 @@ export class SkillImprovementEngine {
18
24
  autonomy;
19
25
  now;
20
26
  proposals;
27
+ applyJournal;
28
+ workDir;
29
+ readInstalledSkill;
21
30
  covered = new Set();
22
31
  attempted = new Set();
23
32
  constructor(options) {
24
33
  this.scenarios = options.scenarios;
34
+ this.evaluateBehavior = options.evaluateBehavior ?? ((content, id) => evaluateSkillBehavior(content, SKILL_BEHAVIOR_TASKS[id] ?? []));
25
35
  this.proposer = options.proposer;
26
- this.mutator = options.mutator ?? new LiveSkillMutator();
27
36
  this.archive = options.archive ?? new EvolutionaryArchive({ workDir: options.workDir });
37
+ this.workDir = options.workDir ?? this.archive.workDir;
38
+ this.mutator = options.mutator ?? new LiveSkillMutator(path.join(this.workDir, '.codebuddy', 'skills'));
28
39
  this.autonomy = options.autonomy ?? resolveAutonomy();
29
40
  this.now = options.now ?? (() => new Date());
30
- this.proposals = options.proposalStore ?? new PendingProposalStore({ workDir: options.workDir });
41
+ this.proposals = options.proposalStore ?? new PendingProposalStore({ workDir: this.workDir });
42
+ this.applyJournal = options.applyJournal ?? new SkillApplyJournal({ workDir: this.workDir, now: this.now });
43
+ this.readInstalledSkill = options.readInstalledSkill ?? ((name) => this.mutator.readInstalled?.(name) ?? null);
31
44
  }
32
45
  async runCycle() {
33
46
  const startedAt = this.now().toISOString();
34
47
  const base = { kind: 'skill_improvement_cycle', startedAt, autonomy: this.autonomy };
48
+ const pendingPeek = this.peekPendingRecords();
49
+ if (!pendingPeek.ok) {
50
+ return {
51
+ ...base,
52
+ selectedScenarioId: null,
53
+ proposalId: null,
54
+ gate: null,
55
+ applied: false,
56
+ notes: ['apply journal unreadable; refusing to install', pendingPeek.error],
57
+ };
58
+ }
59
+ if (this.autonomy !== 'auto-apply') {
60
+ const pending = pendingPeek.records[0];
61
+ if (pending) {
62
+ return {
63
+ ...base,
64
+ selectedScenarioId: pending.scenarioId,
65
+ proposalId: pending.proposalId,
66
+ gate: null,
67
+ applied: false,
68
+ applyPhase: pending.phase,
69
+ notes: ['pending skill apply requires auto-apply; not installed'],
70
+ };
71
+ }
72
+ }
73
+ else {
74
+ const resumed = this.resumePending(base, pendingPeek.records);
75
+ if (resumed)
76
+ return resumed;
77
+ }
35
78
  for (const scenario of this.scenarios) {
36
79
  if (this.covered.has(scenario.id) || this.attempted.has(scenario.id))
37
80
  continue;
38
81
  if (this.autonomy === 'auto-apply') {
39
82
  const pending = this.proposals.loadSkill(scenario.id);
40
83
  if (pending) {
41
- const appliedPending = this.finishCycle(base, scenario, pending.proposal, true);
84
+ const appliedPending = await this.finishCycle(base, scenario, pending.proposal, true);
42
85
  if (appliedPending.applied)
43
86
  this.proposals.remove('skill', scenario.id);
44
87
  return appliedPending;
@@ -76,10 +119,26 @@ export class SkillImprovementEngine {
76
119
  }
77
120
  return results;
78
121
  }
79
- finishCycle(base, scenario, proposal, keepOnAccept) {
122
+ async finishCycle(base, scenario, proposal, keepOnAccept) {
123
+ let gate = validateSkillProposal(proposal, scenario, this.mutator, { keepOnAccept: false });
124
+ let behavior;
125
+ if (gate.accepted && keepOnAccept) {
126
+ const content = proposal.spec.content;
127
+ try {
128
+ behavior = await this.evaluateBehavior(content, scenario.id);
129
+ }
130
+ catch (error) {
131
+ behavior = { accepted: false, wins: 0, losses: 0, tested: 0, cases: [], error: error instanceof Error ? error.message : String(error) };
132
+ }
133
+ if (content !== proposal.spec.content)
134
+ behavior = { ...behavior, accepted: false, error: 'Proposal changed during behavioral validation' };
135
+ if (!behavior.accepted) {
136
+ this.attempted.add(scenario.id);
137
+ return { ...base, selectedScenarioId: scenario.id, proposalId: proposal.id, gate: { ...gate, accepted: false, reasons: ['Behavioral validation did not demonstrate a gain without regression'] }, behavior, applied: false, notes: [behavior.error ?? 'No verified behavioral improvement; proposal not installed'] };
138
+ }
139
+ return this.settleApplication(base, scenario, proposal, behavior, gate);
140
+ }
80
141
  this.attempted.add(scenario.id);
81
- const gate = validateSkillProposal(proposal, scenario, this.mutator, { keepOnAccept });
82
- const applied = gate.accepted && !!gate.appliedRef;
83
142
  if (gate.accepted && !keepOnAccept) {
84
143
  this.proposals.saveSkill({
85
144
  scenarioId: scenario.id,
@@ -88,8 +147,181 @@ export class SkillImprovementEngine {
88
147
  gate,
89
148
  });
90
149
  }
91
- if (applied) {
92
- this.covered.add(scenario.id);
150
+ return {
151
+ ...base,
152
+ selectedScenarioId: scenario.id,
153
+ proposalId: proposal.id,
154
+ gate,
155
+ applied: false,
156
+ notes: gate.reasons,
157
+ ...(behavior ? { behavior } : {}),
158
+ };
159
+ }
160
+ peekPendingRecords() {
161
+ try {
162
+ return { ok: true, records: this.applyJournal.listPending() };
163
+ }
164
+ catch (error) {
165
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
166
+ }
167
+ }
168
+ resumePending(base, pending) {
169
+ const record = pending.find((item) => !this.attempted.has(item.scenarioId));
170
+ if (!record)
171
+ return null;
172
+ const scenario = this.scenarios.find((item) => item.id === record.scenarioId);
173
+ if (!scenario) {
174
+ return {
175
+ ...base,
176
+ selectedScenarioId: record.scenarioId,
177
+ proposalId: record.proposalId,
178
+ gate: null,
179
+ applied: false,
180
+ notes: ['pending apply scenario is missing; not installed'],
181
+ };
182
+ }
183
+ const identityError = savedIdentityMismatch(record, scenario, record.proposal);
184
+ if (identityError) {
185
+ this.attempted.add(scenario.id);
186
+ return {
187
+ ...base,
188
+ selectedScenarioId: scenario.id,
189
+ proposalId: record.proposalId,
190
+ gate: null,
191
+ applied: false,
192
+ applyPhase: 'conflict',
193
+ notes: [identityError],
194
+ };
195
+ }
196
+ const coverage = validateSkillProposal(record.proposal, scenario, this.mutator, { keepOnAccept: false });
197
+ if (!coverage.accepted) {
198
+ this.attempted.add(scenario.id);
199
+ return {
200
+ ...base,
201
+ selectedScenarioId: scenario.id,
202
+ proposalId: record.proposal.id,
203
+ gate: coverage,
204
+ applied: false,
205
+ notes: coverage.reasons,
206
+ };
207
+ }
208
+ if (!record.behavior) {
209
+ this.attempted.add(scenario.id);
210
+ return {
211
+ ...base,
212
+ selectedScenarioId: scenario.id,
213
+ proposalId: record.proposal.id,
214
+ gate: {
215
+ ...coverage,
216
+ accepted: false,
217
+ rejectionReason: 'behavior-required',
218
+ reasons: ['pending apply is missing a behavioral receipt'],
219
+ },
220
+ applied: false,
221
+ notes: ['pending apply is missing a behavioral receipt'],
222
+ };
223
+ }
224
+ return this.settleApplication(base, scenario, record.proposal, record.behavior, coverage, record);
225
+ }
226
+ settleApplication(base, scenario, proposal, behavior, coverageGate, existing) {
227
+ const name = proposal.spec.name;
228
+ let record = existing ?? this.applyJournal.get(scenario.id);
229
+ const computedSha = sha256Hex(intendedSkillBody(proposal));
230
+ if (record) {
231
+ const identityError = savedIdentityMismatch(record, scenario, proposal);
232
+ if (identityError) {
233
+ this.attempted.add(scenario.id);
234
+ return {
235
+ ...base,
236
+ selectedScenarioId: scenario.id,
237
+ proposalId: proposal.id,
238
+ gate: coverageGate,
239
+ applied: false,
240
+ applyPhase: 'conflict',
241
+ notes: [identityError],
242
+ behavior,
243
+ };
244
+ }
245
+ }
246
+ const intendedSha256 = record?.intendedSha256 ?? computedSha;
247
+ let gate = coverageGate;
248
+ if (!record) {
249
+ try {
250
+ record = this.applyJournal.upsert({
251
+ scenarioId: scenario.id,
252
+ proposalId: proposal.id,
253
+ proposal,
254
+ intendedSha256,
255
+ phase: 'intent',
256
+ behavior,
257
+ });
258
+ }
259
+ catch (error) {
260
+ return {
261
+ ...base,
262
+ selectedScenarioId: scenario.id,
263
+ proposalId: proposal.id,
264
+ gate: coverageGate,
265
+ applied: false,
266
+ applyPhase: 'intent',
267
+ notes: [
268
+ 'apply journal write failed before installation; skill not installed',
269
+ error instanceof Error ? error.message : String(error),
270
+ ],
271
+ behavior,
272
+ };
273
+ }
274
+ }
275
+ const installed = this.ensureInstalled(proposal, scenario, behavior, intendedSha256);
276
+ if (installed.kind === 'conflict') {
277
+ this.markConflict(scenario.id, proposal, intendedSha256, behavior);
278
+ this.attempted.add(scenario.id);
279
+ return this.conflictResult(base, scenario, proposal, coverageGate, behavior, name);
280
+ }
281
+ if (installed.kind === 'failed') {
282
+ return {
283
+ ...base,
284
+ selectedScenarioId: scenario.id,
285
+ proposalId: proposal.id,
286
+ gate: coverageGate,
287
+ applied: false,
288
+ applyPhase: 'intent',
289
+ notes: ['installation pending', installed.error],
290
+ behavior,
291
+ };
292
+ }
293
+ if (installed.kind === 'rejected') {
294
+ this.attempted.add(scenario.id);
295
+ if (record.phase === 'intent' && this.peekInstalled(name) === null) {
296
+ this.applyJournal.remove(scenario.id);
297
+ }
298
+ return {
299
+ ...base,
300
+ selectedScenarioId: scenario.id,
301
+ proposalId: proposal.id,
302
+ gate: installed.gate,
303
+ applied: false,
304
+ notes: installed.gate.reasons,
305
+ behavior,
306
+ };
307
+ }
308
+ try {
309
+ record = this.applyJournal.upsert({
310
+ scenarioId: scenario.id,
311
+ proposalId: proposal.id,
312
+ proposal,
313
+ intendedSha256,
314
+ phase: 'installed',
315
+ appliedRef: installed.appliedRef,
316
+ behavior,
317
+ });
318
+ }
319
+ catch {
320
+ /* disk has the skill; retry still sees identical content + intent/installed */
321
+ }
322
+ gate = installed.gate;
323
+ const appliedRef = record?.appliedRef ?? gate.appliedRef ?? name;
324
+ try {
93
325
  this.archive.append({
94
326
  proposalId: proposal.id,
95
327
  kind: 'skill',
@@ -97,17 +329,157 @@ export class SkillImprovementEngine {
97
329
  experienceId: proposal.experienceId,
98
330
  delta: 1,
99
331
  scoreAfter: 1,
100
- appliedRef: gate.appliedRef,
332
+ appliedRef,
333
+ evidence: {
334
+ artifactSha256: intendedSha256,
335
+ benchmarkSha256: sha256Hex(JSON.stringify(SKILL_BEHAVIOR_TASKS[scenario.id] ?? [])),
336
+ wins: behavior.wins,
337
+ losses: behavior.losses,
338
+ cases: behavior.cases,
339
+ },
101
340
  });
102
341
  }
342
+ catch (error) {
343
+ return {
344
+ ...base,
345
+ selectedScenarioId: scenario.id,
346
+ proposalId: proposal.id,
347
+ gate: { ...gate, accepted: true, appliedRef },
348
+ applied: false,
349
+ proofPending: true,
350
+ applyPhase: 'installed',
351
+ notes: ['installed, proof pending', error instanceof Error ? error.message : String(error)],
352
+ behavior,
353
+ };
354
+ }
355
+ this.applyJournal.remove(scenario.id);
356
+ this.proposals.remove('skill', scenario.id);
357
+ this.covered.add(scenario.id);
358
+ this.attempted.add(scenario.id);
103
359
  return {
104
360
  ...base,
105
361
  selectedScenarioId: scenario.id,
106
362
  proposalId: proposal.id,
107
- gate,
108
- applied,
109
- notes: gate.reasons,
363
+ gate: {
364
+ ...gate,
365
+ accepted: true,
366
+ validationLevel: 'behavioral',
367
+ appliedRef,
368
+ reasons: gate.appliedRef
369
+ ? gate.reasons
370
+ : ['accepted and installed (auto-apply): firewall-clean + covers the scenario'],
371
+ },
372
+ applied: true,
373
+ applyPhase: 'archived',
374
+ notes: gate.appliedRef
375
+ ? gate.reasons
376
+ : ['accepted and installed (auto-apply): firewall-clean + covers the scenario'],
377
+ behavior,
110
378
  };
111
379
  }
380
+ ensureInstalled(proposal, scenario, behavior, intendedSha256) {
381
+ const name = proposal.spec.name;
382
+ if (!isAuthoredSkillName(name)) {
383
+ return {
384
+ kind: 'rejected',
385
+ gate: {
386
+ proposalId: proposal.id,
387
+ scenarioId: scenario.id,
388
+ accepted: false,
389
+ reasons: ['not an authored skill name'],
390
+ },
391
+ };
392
+ }
393
+ const disk = this.peekInstalled(name);
394
+ if (disk !== null && sha256Hex(disk) !== intendedSha256) {
395
+ return { kind: 'conflict' };
396
+ }
397
+ const dryGate = validateSkillProposal(proposal, scenario, this.dryMutator(), {
398
+ keepOnAccept: true,
399
+ behavior,
400
+ });
401
+ if (!dryGate.accepted) {
402
+ return { kind: 'rejected', gate: dryGate };
403
+ }
404
+ if (disk !== null && sha256Hex(disk) === intendedSha256) {
405
+ return { kind: 'ready', appliedRef: dryGate.appliedRef ?? name, gate: dryGate };
406
+ }
407
+ if (this.mutator.has(name))
408
+ return { kind: 'conflict' };
409
+ try {
410
+ const gate = validateSkillProposal(proposal, scenario, this.mutator, { keepOnAccept: true, behavior });
411
+ if (!gate.accepted || !gate.appliedRef)
412
+ return { kind: 'rejected', gate };
413
+ return { kind: 'ready', appliedRef: gate.appliedRef, gate };
414
+ }
415
+ catch (error) {
416
+ return { kind: 'failed', error: error instanceof Error ? error.message : String(error) };
417
+ }
418
+ }
419
+ dryMutator() {
420
+ const port = {
421
+ create: (spec) => ({ name: spec.name }),
422
+ remove: () => false,
423
+ has: (name) => this.mutator.has(name),
424
+ };
425
+ if (this.mutator.readInstalled) {
426
+ port.readInstalled = (name) => this.mutator.readInstalled(name);
427
+ }
428
+ return port;
429
+ }
430
+ peekInstalled(name) {
431
+ if (!isAuthoredSkillName(name))
432
+ return null;
433
+ return this.readInstalledSkill(name);
434
+ }
435
+ markConflict(scenarioId, proposal, intendedSha256, behavior, appliedRef) {
436
+ try {
437
+ this.applyJournal.upsert({
438
+ scenarioId,
439
+ proposalId: proposal.id,
440
+ proposal,
441
+ intendedSha256,
442
+ phase: 'conflict',
443
+ behavior,
444
+ ...(appliedRef ? { appliedRef } : {}),
445
+ });
446
+ }
447
+ catch {
448
+ /* conflict is also represented by the on-disk mismatch */
449
+ }
450
+ }
451
+ conflictResult(base, scenario, proposal, gate, behavior, name) {
452
+ return {
453
+ ...base,
454
+ selectedScenarioId: scenario.id,
455
+ proposalId: proposal.id,
456
+ gate: { ...gate, appliedRef: gate.appliedRef ?? (this.mutator.has(name) ? name : undefined) },
457
+ applied: false,
458
+ applyPhase: 'conflict',
459
+ notes: ['installed artifact differs from intended; not archived'],
460
+ behavior,
461
+ };
462
+ }
463
+ }
464
+ function intendedSkillBody(proposal) {
465
+ return ensureFrontmatter(proposal.spec.name, proposal.spec.description, proposal.spec.content);
466
+ }
467
+ function sha256Hex(value) {
468
+ return createHash('sha256').update(value).digest('hex');
469
+ }
470
+ function savedIdentityMismatch(record, scenario, proposal) {
471
+ if (record.proposalId !== proposal.id)
472
+ return 'pending apply proposalId does not match proposal.id';
473
+ if (record.scenarioId !== scenario.id)
474
+ return 'pending apply scenarioId does not match scenario';
475
+ if (proposal.targetScenarioId !== scenario.id)
476
+ return 'pending apply targetScenarioId does not match scenario';
477
+ if (record.scenarioId !== proposal.targetScenarioId) {
478
+ return 'pending apply scenarioId does not match targetScenarioId';
479
+ }
480
+ if (record.intendedSha256 !== sha256Hex(intendedSkillBody(proposal))) {
481
+ return 'pending apply intendedSha256 does not match proposal artifact';
482
+ }
483
+ return null;
112
484
  }
113
485
  //# sourceMappingURL=skill-engine.js.map
@@ -9,10 +9,12 @@
9
9
  *
10
10
  * @module agent/self-improvement/skill-gate
11
11
  */
12
+ import type { SkillBehaviorResult } from './skill-behavior-gate.js';
12
13
  import { type SkillMutatorPort } from './skill-mutator.js';
13
14
  import type { SkillBenchmarkScenario, SkillGateOutcome, SkillProposal } from './skill-types.js';
14
15
  export interface ValidateSkillOptions {
15
16
  keepOnAccept: boolean;
17
+ behavior?: SkillBehaviorResult;
16
18
  }
17
19
  /** Deterministic coverage check: the skill content surfaces all expected guidance. */
18
20
  export declare function coversScenario(content: string, scenario: SkillBenchmarkScenario): boolean;
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * @module agent/self-improvement/skill-gate
11
11
  */
12
- import { safetyGateSkill } from './skill-mutator.js';
12
+ import { ensureFrontmatter, safetyGateSkill } from './skill-mutator.js';
13
13
  /** Deterministic coverage check: the skill content surfaces all expected guidance. */
14
14
  export function coversScenario(content, scenario) {
15
15
  const lower = content.toLowerCase();
@@ -30,6 +30,14 @@ export function validateSkillProposal(proposal, scenario, mutator, options) {
30
30
  reasons: ['SG1: skill content is empty or too short, or missing name'],
31
31
  };
32
32
  }
33
+ try {
34
+ ensureFrontmatter(proposal.spec.name, proposal.spec.description, content);
35
+ if (!/^#{1,6}\s+\S/m.test(content))
36
+ throw new Error('skill must have a Markdown title');
37
+ }
38
+ catch (error) {
39
+ return { ...base, accepted: false, rejectionReason: 'static-scan', reasons: [`SG1: ${error instanceof Error ? error.message : String(error)}`] };
40
+ }
33
41
  // SG2: static scan + skill firewall (prompt-injection / exfiltration / safety)
34
42
  const safety = safetyGateSkill(content);
35
43
  if (!safety.ok) {
@@ -63,6 +71,12 @@ export function validateSkillProposal(proposal, scenario, mutator, options) {
63
71
  reasons: [`SG4: held-out secret coverage failed, missing: ${JSON.stringify(missingHeldOut)}`],
64
72
  };
65
73
  }
74
+ if (options.keepOnAccept) {
75
+ const evidence = options.behavior;
76
+ if (!evidence?.accepted || evidence.tested < 1 || evidence.cases.length !== evidence.tested || evidence.wins < 1 || evidence.losses !== 0 || !evidence.cases.every(item => item.after) || evidence.cases.filter(item => !item.before && item.after).length !== evidence.wins) {
77
+ return { ...base, accepted: false, rejectionReason: 'behavior-required', reasons: ['Installation requires a measured behavioral gain without regression'] };
78
+ }
79
+ }
66
80
  // Accepted. Install (auto-apply) or just report (propose-only).
67
81
  let appliedRef;
68
82
  if (options.keepOnAccept) {
@@ -71,9 +85,10 @@ export function validateSkillProposal(proposal, scenario, mutator, options) {
71
85
  return {
72
86
  ...base,
73
87
  accepted: true,
88
+ validationLevel: options.keepOnAccept ? 'behavioral' : 'coverage',
74
89
  reasons: options.keepOnAccept
75
90
  ? ['accepted and installed (auto-apply): firewall-clean + covers the scenario']
76
- : ['accepted (propose-only): firewall-clean + covers the scenario, not installed'],
91
+ : ['coverage validated only; behavioral verification is required before the improvement engine installs this proposal'],
77
92
  ...(appliedRef ? { appliedRef } : {}),
78
93
  };
79
94
  }
@@ -49,6 +49,8 @@ export interface SkillMutatorPort {
49
49
  };
50
50
  remove(name: string): boolean;
51
51
  has(name: string): boolean;
52
+ /** Read SKILL.md from this mutator's actual root. Engine must not guess paths. */
53
+ readInstalled?(name: string): string | null;
52
54
  }
53
55
  export interface MutationResult {
54
56
  ok: boolean;
@@ -64,8 +66,11 @@ export declare class LiveSkillMutator implements SkillMutatorPort {
64
66
  private reload;
65
67
  private readContent;
66
68
  has(name: string): boolean;
69
+ readInstalled(name: string): string | null;
67
70
  isPinned(name: string): boolean;
68
- create(spec: SkillSpec): {
71
+ create(spec: SkillSpec, options?: {
72
+ overwrite?: boolean;
73
+ }): {
69
74
  name: string;
70
75
  };
71
76
  /** Full re-author of an existing authored skill, re-gated. Refuses pinned. */
@@ -13,6 +13,8 @@
13
13
  import fs from 'fs';
14
14
  import path from 'path';
15
15
  import os from 'os';
16
+ import * as yaml from 'yaml';
17
+ import { parseSkillFile, validateSkill } from '../../skills/parser.js';
16
18
  import { randomUUID } from 'crypto';
17
19
  import { getSkillRegistry } from '../../skills/registry.js';
18
20
  import { logger } from '../../utils/logger.js';
@@ -61,10 +63,18 @@ export function isAuthoredSkillName(name) {
61
63
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
62
64
  /** Ensure the content begins with YAML frontmatter carrying name + description. */
63
65
  export function ensureFrontmatter(name, description, content) {
64
- if (FRONTMATTER_RE.test(content))
65
- return content;
66
- const desc = description.replace(/"/g, "'").replace(/\r?\n/g, ' ').trim();
67
- return `---\nname: ${name}\ndescription: "${desc}"\n---\n\n${content.trim()}\n`;
66
+ const match = content.match(FRONTMATTER_RE);
67
+ const metadata = match ? yaml.parse(match[1]) : {};
68
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata))
69
+ throw new Error('Invalid skill metadata');
70
+ if (metadata.name !== undefined && metadata.name !== name)
71
+ throw new Error('Skill frontmatter identity differs from requested name');
72
+ const body = match ? content.slice(match[0].length) : content;
73
+ const result = `---\n${yaml.stringify({ ...metadata, name, description: description.trim() || metadata.description })}---\n\n${body.trim()}\n`;
74
+ const validation = validateSkill(parseSkillFile(result, 'authored-skill', 'workspace'));
75
+ if (!validation.valid)
76
+ throw new Error(validation.errors.join('; '));
77
+ return result;
68
78
  }
69
79
  export function readPinned(content) {
70
80
  const m = content.match(FRONTMATTER_RE);
@@ -187,13 +197,18 @@ export class LiveSkillMutator {
187
197
  return false;
188
198
  return fs.existsSync(this.skillFile(name));
189
199
  }
200
+ readInstalled(name) {
201
+ if (!isAuthoredSkillName(name))
202
+ return null;
203
+ return this.readContent(name);
204
+ }
190
205
  isPinned(name) {
191
206
  if (!isSafeSkillSegment(name))
192
207
  return false;
193
208
  const c = this.readContent(name);
194
209
  return c ? readPinned(c) : false;
195
210
  }
196
- create(spec) {
211
+ create(spec, options = {}) {
197
212
  // Backstops durs, miroir de LiveToolMutator.register : (a) namespace
198
213
  // authored-* obligatoire — sans lui, un spec nommé comme une skill user/
199
214
  // bundled l'écraserait ; (b) re-gate de sûreté — les appelants légitimes
@@ -203,6 +218,12 @@ export class LiveSkillMutator {
203
218
  if (!isAuthoredSkillName(spec.name)) {
204
219
  throw new Error(`refusing to create skill "${spec.name}": authored skills must be named "${AUTHORED_SKILL_PREFIX}*" (never shadow a user/bundled skill)`);
205
220
  }
221
+ if (this.has(spec.name)) {
222
+ if (this.isPinned(spec.name))
223
+ throw new Error('Skill is pinned');
224
+ if (!options.overwrite)
225
+ throw new Error('Skill already exists; explicit overwrite required');
226
+ }
206
227
  const content = ensureFrontmatter(spec.name, spec.description, spec.content);
207
228
  const gate = safetyGateSkill(content);
208
229
  if (!gate.ok) {
@@ -210,9 +231,24 @@ export class LiveSkillMutator {
210
231
  }
211
232
  const dir = this.dirFor(spec.name);
212
233
  fs.mkdirSync(dir, { recursive: true });
213
- writeFileAtomicSync(this.skillFile(spec.name), content);
214
- this.reload(spec.name);
215
- return { name: spec.name };
234
+ const file = this.skillFile(spec.name);
235
+ const previous = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : undefined;
236
+ try {
237
+ writeFileAtomicSync(file, content);
238
+ const registry = getSkillRegistry();
239
+ const installed = registry.registerSkillFileSync(file, 'workspace');
240
+ if (registry.get(spec.name) !== installed)
241
+ throw new Error('Skill registry refused installation');
242
+ return { name: spec.name };
243
+ }
244
+ catch (error) {
245
+ if (previous !== undefined)
246
+ writeFileAtomicSync(file, previous);
247
+ else
248
+ fs.rmSync(file, { force: true });
249
+ this.reload(spec.name);
250
+ throw error;
251
+ }
216
252
  }
217
253
  /** Full re-author of an existing authored skill, re-gated. Refuses pinned. */
218
254
  update(name, newContent, description = '') {
@@ -222,13 +258,13 @@ export class LiveSkillMutator {
222
258
  return { ok: false, reasons: ['skill does not exist'] };
223
259
  if (this.isPinned(name))
224
260
  return { ok: false, reasons: ['skill is pinned'] };
225
- const withFm = ensureFrontmatter(name, description, newContent);
226
- const gate = safetyGateSkill(withFm);
227
- if (!gate.ok)
228
- return gate;
229
- writeFileAtomicSync(this.skillFile(name), withFm);
230
- this.reload(name);
231
- return { ok: true, reasons: [] };
261
+ try {
262
+ this.create({ name, description: description || parseSkillFile(this.readContent(name), this.skillFile(name), 'workspace').metadata.description, content: newContent }, { overwrite: true });
263
+ return { ok: true, reasons: [] };
264
+ }
265
+ catch (error) {
266
+ return { ok: false, reasons: [error instanceof Error ? error.message : String(error)] };
267
+ }
232
268
  }
233
269
  /** Find/replace within an authored skill body (exact; fail on multiple unless replaceAll). */
234
270
  patch(name, oldStr, newStr, opts = {}) {
@@ -33,9 +33,10 @@ export interface SkillProposal {
33
33
  experienceId?: string;
34
34
  spec: SkillSpec;
35
35
  }
36
- export type SkillGateRejection = 'static-scan' | 'firewall' | 'coverage-fail';
36
+ export type SkillGateRejection = 'static-scan' | 'firewall' | 'coverage-fail' | 'behavior-required';
37
37
  export interface SkillGateOutcome {
38
38
  accepted: boolean;
39
+ validationLevel?: 'coverage' | 'behavioral';
39
40
  proposalId: string;
40
41
  scenarioId: string;
41
42
  rejectionReason?: SkillGateRejection;