@ai-agent-forge/agent-forge 0.86.7 → 0.88.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 (717) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +10 -11
  3. package/dist/artifacts/artifact-store.d.ts.map +1 -1
  4. package/dist/artifacts/artifact-store.js +9 -5
  5. package/dist/artifacts/artifact-store.js.map +1 -1
  6. package/dist/bundle/.build-manifest.json +430 -413
  7. package/dist/bundle/chunks/{chunk-A7GUOX2H.js → chunk-6ZKDNZHA.js} +5 -1
  8. package/dist/bundle/chunks/chunk-HLJEVRWP.js +13 -0
  9. package/dist/bundle/chunks/chunk-YGHQME4V.js +1456 -0
  10. package/dist/bundle/chunks/image-resize-worker.js +12 -1
  11. package/dist/bundle/chunks/{virtual-modules-3IZ4MYSQ.js → virtual-modules-KZ5DRH7Q.js} +1 -1
  12. package/dist/bundle/cli.js +1 -1
  13. package/dist/bundle/client.js +1 -1
  14. package/dist/bundle/index.js +2 -2
  15. package/dist/bundle/rpc-entry.js +1 -1
  16. package/dist/cli/args.d.ts.map +1 -1
  17. package/dist/cli/args.js +231 -185
  18. package/dist/cli/args.js.map +1 -1
  19. package/dist/cli/auth-check.d.ts.map +1 -1
  20. package/dist/cli/auth-check.js +17 -4
  21. package/dist/cli/auth-check.js.map +1 -1
  22. package/dist/cli/auth-command.d.ts.map +1 -1
  23. package/dist/cli/auth-command.js +15 -9
  24. package/dist/cli/auth-command.js.map +1 -1
  25. package/dist/cli/credential-print.d.ts.map +1 -1
  26. package/dist/cli/credential-print.js +14 -7
  27. package/dist/cli/credential-print.js.map +1 -1
  28. package/dist/cli/experimental/command.d.ts.map +1 -1
  29. package/dist/cli/experimental/command.js +6 -5
  30. package/dist/cli/experimental/command.js.map +1 -1
  31. package/dist/cli/list-models.d.ts.map +1 -1
  32. package/dist/cli/list-models.js +9 -8
  33. package/dist/cli/list-models.js.map +1 -1
  34. package/dist/cli/plugin-manager-controller.d.ts.map +1 -1
  35. package/dist/cli/plugin-manager-controller.js +2 -1
  36. package/dist/cli/plugin-manager-controller.js.map +1 -1
  37. package/dist/cli/serve.d.ts.map +1 -1
  38. package/dist/cli/serve.js +8 -5
  39. package/dist/cli/serve.js.map +1 -1
  40. package/dist/client/remote-session.d.ts.map +1 -1
  41. package/dist/client/remote-session.js +16 -9
  42. package/dist/client/remote-session.js.map +1 -1
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +5 -3
  45. package/dist/config.js.map +1 -1
  46. package/dist/core/agent-capability-contract.d.ts +0 -7
  47. package/dist/core/agent-capability-contract.d.ts.map +1 -1
  48. package/dist/core/agent-capability-contract.js +72 -68
  49. package/dist/core/agent-capability-contract.js.map +1 -1
  50. package/dist/core/agent-session-bash.d.ts.map +1 -1
  51. package/dist/core/agent-session-bash.js +5 -4
  52. package/dist/core/agent-session-bash.js.map +1 -1
  53. package/dist/core/agent-session-compaction.d.ts +6 -10
  54. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  55. package/dist/core/agent-session-compaction.js +35 -133
  56. package/dist/core/agent-session-compaction.js.map +1 -1
  57. package/dist/core/agent-session-context.d.ts.map +1 -1
  58. package/dist/core/agent-session-context.js +18 -13
  59. package/dist/core/agent-session-context.js.map +1 -1
  60. package/dist/core/agent-session-internal.d.ts +0 -6
  61. package/dist/core/agent-session-internal.d.ts.map +1 -1
  62. package/dist/core/agent-session-internal.js.map +1 -1
  63. package/dist/core/agent-session-model.d.ts.map +1 -1
  64. package/dist/core/agent-session-model.js +2 -1
  65. package/dist/core/agent-session-model.js.map +1 -1
  66. package/dist/core/agent-session-retry.d.ts.map +1 -1
  67. package/dist/core/agent-session-retry.js +3 -3
  68. package/dist/core/agent-session-retry.js.map +1 -1
  69. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  70. package/dist/core/agent-session-runtime.js +7 -6
  71. package/dist/core/agent-session-runtime.js.map +1 -1
  72. package/dist/core/agent-session-services.d.ts +1 -1
  73. package/dist/core/agent-session-services.d.ts.map +1 -1
  74. package/dist/core/agent-session-services.js.map +1 -1
  75. package/dist/core/agent-session-tools.d.ts.map +1 -1
  76. package/dist/core/agent-session-tools.js +3 -2
  77. package/dist/core/agent-session-tools.js.map +1 -1
  78. package/dist/core/agent-session.d.ts +40 -22
  79. package/dist/core/agent-session.d.ts.map +1 -1
  80. package/dist/core/agent-session.js +147 -67
  81. package/dist/core/agent-session.js.map +1 -1
  82. package/dist/core/auth-guidance.d.ts.map +1 -1
  83. package/dist/core/auth-guidance.js +11 -5
  84. package/dist/core/auth-guidance.js.map +1 -1
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +9 -6
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/compaction/compaction.d.ts.map +1 -1
  89. package/dist/core/compaction/compaction.js +2 -1
  90. package/dist/core/compaction/compaction.js.map +1 -1
  91. package/dist/core/embedded-sdk.d.ts.map +1 -1
  92. package/dist/core/embedded-sdk.js +10 -6
  93. package/dist/core/embedded-sdk.js.map +1 -1
  94. package/dist/core/export-html/index.d.ts.map +1 -1
  95. package/dist/core/export-html/index.js +9 -4
  96. package/dist/core/export-html/index.js.map +1 -1
  97. package/dist/core/extensions/agent-composition.d.ts.map +1 -1
  98. package/dist/core/extensions/agent-composition.js +26 -19
  99. package/dist/core/extensions/agent-composition.js.map +1 -1
  100. package/dist/core/extensions/bootstrap-agent-loop.d.ts.map +1 -1
  101. package/dist/core/extensions/bootstrap-agent-loop.js +3 -2
  102. package/dist/core/extensions/bootstrap-agent-loop.js.map +1 -1
  103. package/dist/core/extensions/bootstrap-compaction-policy.d.ts.map +1 -1
  104. package/dist/core/extensions/bootstrap-compaction-policy.js +0 -34
  105. package/dist/core/extensions/bootstrap-compaction-policy.js.map +1 -1
  106. package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -1
  107. package/dist/core/extensions/bootstrap-lifecycle.js +20 -17
  108. package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -1
  109. package/dist/core/extensions/bootstrap-overflow-policy.d.ts.map +1 -1
  110. package/dist/core/extensions/bootstrap-overflow-policy.js +2 -1
  111. package/dist/core/extensions/bootstrap-overflow-policy.js.map +1 -1
  112. package/dist/core/extensions/bootstrap-retry-policy.d.ts.map +1 -1
  113. package/dist/core/extensions/bootstrap-retry-policy.js +4 -3
  114. package/dist/core/extensions/bootstrap-retry-policy.js.map +1 -1
  115. package/dist/core/extensions/bootstrap-skills.d.ts.map +1 -1
  116. package/dist/core/extensions/bootstrap-skills.js +7 -6
  117. package/dist/core/extensions/bootstrap-skills.js.map +1 -1
  118. package/dist/core/extensions/bootstrap-tools.d.ts.map +1 -1
  119. package/dist/core/extensions/bootstrap-tools.js +2 -1
  120. package/dist/core/extensions/bootstrap-tools.js.map +1 -1
  121. package/dist/core/extensions/capability-plugin.d.ts.map +1 -1
  122. package/dist/core/extensions/capability-plugin.js +37 -13
  123. package/dist/core/extensions/capability-plugin.js.map +1 -1
  124. package/dist/core/extensions/capability-runtime-events.d.ts.map +1 -1
  125. package/dist/core/extensions/capability-runtime-events.js +17 -13
  126. package/dist/core/extensions/capability-runtime-events.js.map +1 -1
  127. package/dist/core/extensions/capability-runtime-guards.d.ts.map +1 -1
  128. package/dist/core/extensions/capability-runtime-guards.js +16 -15
  129. package/dist/core/extensions/capability-runtime-guards.js.map +1 -1
  130. package/dist/core/extensions/capability-runtime-manifest.d.ts.map +1 -1
  131. package/dist/core/extensions/capability-runtime-manifest.js +52 -30
  132. package/dist/core/extensions/capability-runtime-manifest.js.map +1 -1
  133. package/dist/core/extensions/capability-runtime-validation.d.ts.map +1 -1
  134. package/dist/core/extensions/capability-runtime-validation.js +66 -65
  135. package/dist/core/extensions/capability-runtime-validation.js.map +1 -1
  136. package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
  137. package/dist/core/extensions/capability-runtime.js +147 -127
  138. package/dist/core/extensions/capability-runtime.js.map +1 -1
  139. package/dist/core/extensions/compaction-memory-failure-mode.d.ts.map +1 -1
  140. package/dist/core/extensions/compaction-memory-failure-mode.js +3 -2
  141. package/dist/core/extensions/compaction-memory-failure-mode.js.map +1 -1
  142. package/dist/core/extensions/context-strategy-binding.d.ts.map +1 -1
  143. package/dist/core/extensions/context-strategy-binding.js +7 -4
  144. package/dist/core/extensions/context-strategy-binding.js.map +1 -1
  145. package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -1
  146. package/dist/core/extensions/lifecycle-catalog.js +290 -279
  147. package/dist/core/extensions/lifecycle-catalog.js.map +1 -1
  148. package/dist/core/extensions/lifecycle-delivery-queue.d.ts.map +1 -1
  149. package/dist/core/extensions/lifecycle-delivery-queue.js +3 -2
  150. package/dist/core/extensions/lifecycle-delivery-queue.js.map +1 -1
  151. package/dist/core/extensions/provider-failover.d.ts +7 -3
  152. package/dist/core/extensions/provider-failover.d.ts.map +1 -1
  153. package/dist/core/extensions/provider-failover.js +9 -8
  154. package/dist/core/extensions/provider-failover.js.map +1 -1
  155. package/dist/core/facade/coding-agent-facade.d.ts +6 -6
  156. package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
  157. package/dist/core/facade/coding-agent-facade.js +2 -3
  158. package/dist/core/facade/coding-agent-facade.js.map +1 -1
  159. package/dist/core/http-dispatcher.d.ts.map +1 -1
  160. package/dist/core/http-dispatcher.js +2 -1
  161. package/dist/core/http-dispatcher.js.map +1 -1
  162. package/dist/core/i18n/index.d.ts +14 -0
  163. package/dist/core/i18n/index.d.ts.map +1 -0
  164. package/dist/core/i18n/index.js +67 -0
  165. package/dist/core/i18n/index.js.map +1 -0
  166. package/dist/core/i18n/locales/en/example.d.ts +7 -0
  167. package/dist/core/i18n/locales/en/example.d.ts.map +1 -0
  168. package/dist/core/i18n/locales/en/example.js +7 -0
  169. package/dist/core/i18n/locales/en/example.js.map +1 -0
  170. package/dist/core/i18n/locales/zh-cn/cli.d.ts +8 -0
  171. package/dist/core/i18n/locales/zh-cn/cli.d.ts.map +1 -0
  172. package/dist/core/i18n/locales/zh-cn/cli.js +229 -0
  173. package/dist/core/i18n/locales/zh-cn/cli.js.map +1 -0
  174. package/dist/core/i18n/locales/zh-cn/common.d.ts +6 -0
  175. package/dist/core/i18n/locales/zh-cn/common.d.ts.map +1 -0
  176. package/dist/core/i18n/locales/zh-cn/common.js +6 -0
  177. package/dist/core/i18n/locales/zh-cn/common.js.map +1 -0
  178. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts +13 -0
  179. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts.map +1 -0
  180. package/dist/core/i18n/locales/zh-cn/core-contract-core.js +173 -0
  181. package/dist/core/i18n/locales/zh-cn/core-contract-core.js.map +1 -0
  182. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts +12 -0
  183. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts.map +1 -0
  184. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js +582 -0
  185. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js.map +1 -0
  186. package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts +9 -0
  187. package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts.map +1 -0
  188. package/dist/core/i18n/locales/zh-cn/core-contract-final.js +55 -0
  189. package/dist/core/i18n/locales/zh-cn/core-contract-final.js.map +1 -0
  190. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts +11 -0
  191. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts.map +1 -0
  192. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js +455 -0
  193. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js.map +1 -0
  194. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts +9 -0
  195. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -0
  196. package/dist/core/i18n/locales/zh-cn/core-errors.js +191 -0
  197. package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -0
  198. package/dist/core/i18n/locales/zh-cn/example.d.ts +7 -0
  199. package/dist/core/i18n/locales/zh-cn/example.d.ts.map +1 -0
  200. package/dist/core/i18n/locales/zh-cn/example.js +10 -0
  201. package/dist/core/i18n/locales/zh-cn/example.js.map +1 -0
  202. package/dist/core/i18n/locales/zh-cn/index.d.ts +2 -0
  203. package/dist/core/i18n/locales/zh-cn/index.d.ts.map +1 -0
  204. package/dist/core/i18n/locales/zh-cn/index.js +41 -0
  205. package/dist/core/i18n/locales/zh-cn/index.js.map +1 -0
  206. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts +10 -0
  207. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -0
  208. package/dist/core/i18n/locales/zh-cn/interactive-mode.js +267 -0
  209. package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -0
  210. package/dist/core/i18n/locales/zh-cn/keybindings.d.ts +11 -0
  211. package/dist/core/i18n/locales/zh-cn/keybindings.d.ts.map +1 -0
  212. package/dist/core/i18n/locales/zh-cn/keybindings.js +67 -0
  213. package/dist/core/i18n/locales/zh-cn/keybindings.js.map +1 -0
  214. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts +7 -0
  215. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts.map +1 -0
  216. package/dist/core/i18n/locales/zh-cn/slash-commands.js +39 -0
  217. package/dist/core/i18n/locales/zh-cn/slash-commands.js.map +1 -0
  218. package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts +10 -0
  219. package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts.map +1 -0
  220. package/dist/core/i18n/locales/zh-cn/tui-builtin.js +15 -0
  221. package/dist/core/i18n/locales/zh-cn/tui-builtin.js.map +1 -0
  222. package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts +13 -0
  223. package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts.map +1 -0
  224. package/dist/core/i18n/locales/zh-cn/tui-components-misc.js +102 -0
  225. package/dist/core/i18n/locales/zh-cn/tui-components-misc.js.map +1 -0
  226. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts +12 -0
  227. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts.map +1 -0
  228. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js +286 -0
  229. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js.map +1 -0
  230. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts +9 -0
  231. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts.map +1 -0
  232. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js +79 -0
  233. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js.map +1 -0
  234. package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts +8 -0
  235. package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts.map +1 -0
  236. package/dist/core/i18n/locales/zh-cn/tui-self-owned.js +72 -0
  237. package/dist/core/i18n/locales/zh-cn/tui-self-owned.js.map +1 -0
  238. package/dist/core/keybindings.d.ts +15 -0
  239. package/dist/core/keybindings.d.ts.map +1 -1
  240. package/dist/core/keybindings.js +18 -0
  241. package/dist/core/keybindings.js.map +1 -1
  242. package/dist/core/logger.d.ts.map +1 -1
  243. package/dist/core/logger.js +5 -1
  244. package/dist/core/logger.js.map +1 -1
  245. package/dist/core/model-config.d.ts +0 -1
  246. package/dist/core/model-config.d.ts.map +1 -1
  247. package/dist/core/model-config.js +9 -1
  248. package/dist/core/model-config.js.map +1 -1
  249. package/dist/core/model-registry.d.ts.map +1 -1
  250. package/dist/core/model-registry.js +2 -1
  251. package/dist/core/model-registry.js.map +1 -1
  252. package/dist/core/model-runtime.d.ts.map +1 -1
  253. package/dist/core/model-runtime.js +11 -7
  254. package/dist/core/model-runtime.js.map +1 -1
  255. package/dist/core/package-install-host.d.ts.map +1 -1
  256. package/dist/core/package-install-host.js +4 -3
  257. package/dist/core/package-install-host.js.map +1 -1
  258. package/dist/core/package-manager.d.ts.map +1 -1
  259. package/dist/core/package-manager.js +15 -10
  260. package/dist/core/package-manager.js.map +1 -1
  261. package/dist/core/package-resource-collection.d.ts.map +1 -1
  262. package/dist/core/package-resource-collection.js +2 -1
  263. package/dist/core/package-resource-collection.js.map +1 -1
  264. package/dist/core/package-source.d.ts.map +1 -1
  265. package/dist/core/package-source.js +3 -2
  266. package/dist/core/package-source.js.map +1 -1
  267. package/dist/core/provider-composer.d.ts +2 -2
  268. package/dist/core/provider-composer.d.ts.map +1 -1
  269. package/dist/core/provider-composer.js +29 -21
  270. package/dist/core/provider-composer.js.map +1 -1
  271. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  272. package/dist/core/remote-catalog-provider.js +6 -2
  273. package/dist/core/remote-catalog-provider.js.map +1 -1
  274. package/dist/core/resolve-config-value.d.ts.map +1 -1
  275. package/dist/core/resolve-config-value.js +15 -5
  276. package/dist/core/resolve-config-value.js.map +1 -1
  277. package/dist/core/sdk.d.ts +27 -5
  278. package/dist/core/sdk.d.ts.map +1 -1
  279. package/dist/core/sdk.js +80 -55
  280. package/dist/core/sdk.js.map +1 -1
  281. package/dist/core/session-cwd.d.ts.map +1 -1
  282. package/dist/core/session-cwd.js +5 -2
  283. package/dist/core/session-cwd.js.map +1 -1
  284. package/dist/core/session-export.d.ts.map +1 -1
  285. package/dist/core/session-export.js +2 -1
  286. package/dist/core/session-export.js.map +1 -1
  287. package/dist/core/session-manager.d.ts.map +1 -1
  288. package/dist/core/session-manager.js +39 -29
  289. package/dist/core/session-manager.js.map +1 -1
  290. package/dist/core/session-storage.d.ts.map +1 -1
  291. package/dist/core/session-storage.js +3 -3
  292. package/dist/core/session-storage.js.map +1 -1
  293. package/dist/core/settings-manager.d.ts +20 -20
  294. package/dist/core/settings-manager.d.ts.map +1 -1
  295. package/dist/core/settings-manager.js +47 -59
  296. package/dist/core/settings-manager.js.map +1 -1
  297. package/dist/core/slash-commands.d.ts.map +1 -1
  298. package/dist/core/slash-commands.js +53 -32
  299. package/dist/core/slash-commands.js.map +1 -1
  300. package/dist/core/system-prompt.d.ts.map +1 -1
  301. package/dist/core/system-prompt.js +25 -2
  302. package/dist/core/system-prompt.js.map +1 -1
  303. package/dist/core/tools/bash.d.ts.map +1 -1
  304. package/dist/core/tools/bash.js +11 -7
  305. package/dist/core/tools/bash.js.map +1 -1
  306. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  307. package/dist/core/tools/edit-diff.js +9 -4
  308. package/dist/core/tools/edit-diff.js.map +1 -1
  309. package/dist/core/tools/edit.d.ts.map +1 -1
  310. package/dist/core/tools/edit.js +4 -3
  311. package/dist/core/tools/edit.js.map +1 -1
  312. package/dist/core/tools/index.d.ts.map +1 -1
  313. package/dist/core/tools/index.js +3 -2
  314. package/dist/core/tools/index.js.map +1 -1
  315. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  316. package/dist/core/tools/output-accumulator.js +2 -1
  317. package/dist/core/tools/output-accumulator.js.map +1 -1
  318. package/dist/core/tools/read.d.ts.map +1 -1
  319. package/dist/core/tools/read.js +6 -2
  320. package/dist/core/tools/read.js.map +1 -1
  321. package/dist/core/tools/write.d.ts.map +1 -1
  322. package/dist/core/tools/write.js +2 -1
  323. package/dist/core/tools/write.js.map +1 -1
  324. package/dist/durable/durable-service.d.ts.map +1 -1
  325. package/dist/durable/durable-service.js +25 -18
  326. package/dist/durable/durable-service.js.map +1 -1
  327. package/dist/host/checkpoint-commit.d.ts.map +1 -1
  328. package/dist/host/checkpoint-commit.js +2 -1
  329. package/dist/host/checkpoint-commit.js.map +1 -1
  330. package/dist/host/manual-compaction.d.ts.map +1 -1
  331. package/dist/host/manual-compaction.js +2 -1
  332. package/dist/host/manual-compaction.js.map +1 -1
  333. package/dist/host/output-projection.d.ts +0 -13
  334. package/dist/host/output-projection.d.ts.map +1 -1
  335. package/dist/host/output-projection.js +16 -2
  336. package/dist/host/output-projection.js.map +1 -1
  337. package/dist/host/summary-retry.d.ts.map +1 -1
  338. package/dist/host/summary-retry.js +3 -2
  339. package/dist/host/summary-retry.js.map +1 -1
  340. package/dist/main.d.ts.map +1 -1
  341. package/dist/main.js +97 -60
  342. package/dist/main.js.map +1 -1
  343. package/dist/modes/interactive/components/agents-view.d.ts.map +1 -1
  344. package/dist/modes/interactive/components/agents-view.js +18 -15
  345. package/dist/modes/interactive/components/agents-view.js.map +1 -1
  346. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  347. package/dist/modes/interactive/components/assistant-message.js +6 -5
  348. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  349. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  350. package/dist/modes/interactive/components/bash-execution.js +12 -7
  351. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  352. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  353. package/dist/modes/interactive/components/bordered-loader.js +2 -1
  354. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  355. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  356. package/dist/modes/interactive/components/branch-summary-message.js +6 -5
  357. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  358. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  359. package/dist/modes/interactive/components/compaction-summary-message.js +9 -5
  360. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  361. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  362. package/dist/modes/interactive/components/config-selector.js +18 -15
  363. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  364. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  365. package/dist/modes/interactive/components/custom-entry.js +2 -1
  366. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  367. package/dist/modes/interactive/components/dialog-editor.d.ts.map +1 -1
  368. package/dist/modes/interactive/components/dialog-editor.js +5 -4
  369. package/dist/modes/interactive/components/dialog-editor.js.map +1 -1
  370. package/dist/modes/interactive/components/dialog-input.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/dialog-input.js +2 -1
  372. package/dist/modes/interactive/components/dialog-input.js.map +1 -1
  373. package/dist/modes/interactive/components/dialog-selector.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/dialog-selector.js +4 -3
  375. package/dist/modes/interactive/components/dialog-selector.js.map +1 -1
  376. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/first-time-setup.js +11 -10
  378. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  379. package/dist/modes/interactive/components/footer.d.ts +0 -2
  380. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  381. package/dist/modes/interactive/components/footer.js +8 -10
  382. package/dist/modes/interactive/components/footer.js.map +1 -1
  383. package/dist/modes/interactive/components/index.d.ts +1 -0
  384. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  385. package/dist/modes/interactive/components/index.js +1 -0
  386. package/dist/modes/interactive/components/index.js.map +1 -1
  387. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  388. package/dist/modes/interactive/components/keybinding-hints.js +3 -2
  389. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  390. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/login-dialog.js +12 -11
  392. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  393. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -1
  394. package/dist/modes/interactive/components/mermaid.js +6 -2
  395. package/dist/modes/interactive/components/mermaid.js.map +1 -1
  396. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.js +28 -16
  398. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  399. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/oauth-selector.js +10 -9
  401. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  402. package/dist/modes/interactive/components/plugin-manager-entry.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/plugin-manager-entry.js +22 -13
  404. package/dist/modes/interactive/components/plugin-manager-entry.js.map +1 -1
  405. package/dist/modes/interactive/components/plugin-selection-entry.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/plugin-selection-entry.js +27 -12
  407. package/dist/modes/interactive/components/plugin-selection-entry.js.map +1 -1
  408. package/dist/modes/interactive/components/queue-manager-selector.d.ts +60 -0
  409. package/dist/modes/interactive/components/queue-manager-selector.d.ts.map +1 -0
  410. package/dist/modes/interactive/components/queue-manager-selector.js +166 -0
  411. package/dist/modes/interactive/components/queue-manager-selector.js.map +1 -0
  412. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/scoped-models-selector.js +16 -14
  414. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  415. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/session-selector.js +43 -31
  417. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  418. package/dist/modes/interactive/components/settings-selector.d.ts +4 -6
  419. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/settings-selector.js +115 -120
  421. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  422. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/settings-submenu.js +5 -4
  424. package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
  425. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/show-images-selector.js +3 -2
  427. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  428. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/skill-invocation-message.js +4 -3
  430. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  431. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/status-indicator.js +14 -5
  433. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  434. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  435. package/dist/modes/interactive/components/theme-selector.js +2 -1
  436. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  437. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  438. package/dist/modes/interactive/components/thinking-selector.js +11 -4
  439. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  440. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  441. package/dist/modes/interactive/components/tool-execution.js +8 -3
  442. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  443. package/dist/modes/interactive/components/tree-list.d.ts.map +1 -1
  444. package/dist/modes/interactive/components/tree-list.js +7 -6
  445. package/dist/modes/interactive/components/tree-list.js.map +1 -1
  446. package/dist/modes/interactive/components/tree-render.d.ts.map +1 -1
  447. package/dist/modes/interactive/components/tree-render.js +28 -23
  448. package/dist/modes/interactive/components/tree-render.js.map +1 -1
  449. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/tree-selector.js +9 -7
  451. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  452. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  453. package/dist/modes/interactive/components/user-message-selector.js +5 -4
  454. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  455. package/dist/modes/interactive/components/workflow-view.d.ts.map +1 -1
  456. package/dist/modes/interactive/components/workflow-view.js +36 -21
  457. package/dist/modes/interactive/components/workflow-view.js.map +1 -1
  458. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  459. package/dist/modes/interactive/external-editor.js +2 -1
  460. package/dist/modes/interactive/external-editor.js.map +1 -1
  461. package/dist/modes/interactive/interactive-auth.d.ts.map +1 -1
  462. package/dist/modes/interactive/interactive-auth.js +71 -40
  463. package/dist/modes/interactive/interactive-auth.js.map +1 -1
  464. package/dist/modes/interactive/interactive-commands.d.ts +1 -0
  465. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  466. package/dist/modes/interactive/interactive-commands.js +3 -0
  467. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  468. package/dist/modes/interactive/interactive-dialog-ui.d.ts.map +1 -1
  469. package/dist/modes/interactive/interactive-dialog-ui.js +5 -5
  470. package/dist/modes/interactive/interactive-dialog-ui.js.map +1 -1
  471. package/dist/modes/interactive/interactive-format.d.ts.map +1 -1
  472. package/dist/modes/interactive/interactive-format.js +34 -18
  473. package/dist/modes/interactive/interactive-format.js.map +1 -1
  474. package/dist/modes/interactive/interactive-helpers.d.ts.map +1 -1
  475. package/dist/modes/interactive/interactive-helpers.js +7 -2
  476. package/dist/modes/interactive/interactive-helpers.js.map +1 -1
  477. package/dist/modes/interactive/interactive-mode.d.ts +4 -2
  478. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  479. package/dist/modes/interactive/interactive-mode.js +350 -279
  480. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  481. package/dist/modes/interactive/interactive-selectors.d.ts +9 -0
  482. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  483. package/dist/modes/interactive/interactive-selectors.js +79 -25
  484. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  485. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  486. package/dist/modes/interactive/theme/theme-controller.js +5 -1
  487. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  488. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  489. package/dist/modes/interactive/theme/theme.js +26 -23
  490. package/dist/modes/interactive/theme/theme.js.map +1 -1
  491. package/dist/modes/json-event.d.ts.map +1 -1
  492. package/dist/modes/json-event.js +3 -2
  493. package/dist/modes/json-event.js.map +1 -1
  494. package/dist/modes/print-mode.d.ts.map +1 -1
  495. package/dist/modes/print-mode.js +3 -2
  496. package/dist/modes/print-mode.js.map +1 -1
  497. package/dist/modes/rpc/rpc-client.d.ts +0 -8
  498. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  499. package/dist/modes/rpc/rpc-client.js +3 -14
  500. package/dist/modes/rpc/rpc-client.js.map +1 -1
  501. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  502. package/dist/modes/rpc/rpc-mode.js +0 -9
  503. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  504. package/dist/modes/rpc/rpc-types.d.ts +0 -19
  505. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  506. package/dist/modes/rpc/rpc-types.js.map +1 -1
  507. package/dist/package-manager-cli.d.ts.map +1 -1
  508. package/dist/package-manager-cli.js +28 -13
  509. package/dist/package-manager-cli.js.map +1 -1
  510. package/dist/plugins/ecosystem/catalog.d.ts.map +1 -1
  511. package/dist/plugins/ecosystem/catalog.js +3 -2
  512. package/dist/plugins/ecosystem/catalog.js.map +1 -1
  513. package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
  514. package/dist/plugins/ecosystem/cli.js +2 -1
  515. package/dist/plugins/ecosystem/cli.js.map +1 -1
  516. package/dist/plugins/ecosystem/default-plugin-market.d.ts.map +1 -1
  517. package/dist/plugins/ecosystem/default-plugin-market.js +3 -2
  518. package/dist/plugins/ecosystem/default-plugin-market.js.map +1 -1
  519. package/dist/plugins/ecosystem/distribution.d.ts.map +1 -1
  520. package/dist/plugins/ecosystem/distribution.js +2 -1
  521. package/dist/plugins/ecosystem/distribution.js.map +1 -1
  522. package/dist/plugins/ecosystem/installer.d.ts.map +1 -1
  523. package/dist/plugins/ecosystem/installer.js +3 -2
  524. package/dist/plugins/ecosystem/installer.js.map +1 -1
  525. package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
  526. package/dist/plugins/ecosystem/node-disk-registry-source.js +20 -17
  527. package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
  528. package/dist/plugins/ecosystem/node-distribution-host.d.ts.map +1 -1
  529. package/dist/plugins/ecosystem/node-distribution-host.js +35 -34
  530. package/dist/plugins/ecosystem/node-distribution-host.js.map +1 -1
  531. package/dist/plugins/ecosystem/node-npm-registry-source.d.ts.map +1 -1
  532. package/dist/plugins/ecosystem/node-npm-registry-source.js +35 -34
  533. package/dist/plugins/ecosystem/node-npm-registry-source.js.map +1 -1
  534. package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
  535. package/dist/plugins/ecosystem/node-package-install-host.js +9 -8
  536. package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
  537. package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -1
  538. package/dist/plugins/ecosystem/node-package-install-persistence.js +4 -3
  539. package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -1
  540. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts +9 -2
  541. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  542. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +44 -5
  543. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  544. package/dist/plugins/ecosystem/node-plugin-trust-root.d.ts.map +1 -1
  545. package/dist/plugins/ecosystem/node-plugin-trust-root.js +10 -9
  546. package/dist/plugins/ecosystem/node-plugin-trust-root.js.map +1 -1
  547. package/dist/plugins/ecosystem/node-registry-source.d.ts.map +1 -1
  548. package/dist/plugins/ecosystem/node-registry-source.js +31 -30
  549. package/dist/plugins/ecosystem/node-registry-source.js.map +1 -1
  550. package/dist/plugins/ecosystem/node-release-package-source.d.ts.map +1 -1
  551. package/dist/plugins/ecosystem/node-release-package-source.js +30 -29
  552. package/dist/plugins/ecosystem/node-release-package-source.js.map +1 -1
  553. package/dist/plugins/ecosystem/node-tar-writer.d.ts.map +1 -1
  554. package/dist/plugins/ecosystem/node-tar-writer.js +11 -10
  555. package/dist/plugins/ecosystem/node-tar-writer.js.map +1 -1
  556. package/dist/plugins/ecosystem/node-url-package-source.d.ts.map +1 -1
  557. package/dist/plugins/ecosystem/node-url-package-source.js +21 -20
  558. package/dist/plugins/ecosystem/node-url-package-source.js.map +1 -1
  559. package/dist/plugins/ecosystem/plugin-acquisition.d.ts.map +1 -1
  560. package/dist/plugins/ecosystem/plugin-acquisition.js +3 -2
  561. package/dist/plugins/ecosystem/plugin-acquisition.js.map +1 -1
  562. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts +9 -1
  563. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts.map +1 -1
  564. package/dist/plugins/ecosystem/plugin-bootstrap.js +5 -0
  565. package/dist/plugins/ecosystem/plugin-bootstrap.js.map +1 -1
  566. package/dist/plugins/ecosystem/registry-cli-adapter.d.ts.map +1 -1
  567. package/dist/plugins/ecosystem/registry-cli-adapter.js +2 -1
  568. package/dist/plugins/ecosystem/registry-cli-adapter.js.map +1 -1
  569. package/dist/plugins/ecosystem/registry.d.ts.map +1 -1
  570. package/dist/plugins/ecosystem/registry.js +35 -32
  571. package/dist/plugins/ecosystem/registry.js.map +1 -1
  572. package/dist/plugins/ecosystem/resolver.d.ts.map +1 -1
  573. package/dist/plugins/ecosystem/resolver.js +3 -2
  574. package/dist/plugins/ecosystem/resolver.js.map +1 -1
  575. package/dist/plugins/ecosystem/selection-store.d.ts.map +1 -1
  576. package/dist/plugins/ecosystem/selection-store.js +11 -10
  577. package/dist/plugins/ecosystem/selection-store.js.map +1 -1
  578. package/dist/plugins/ecosystem/selection.d.ts.map +1 -1
  579. package/dist/plugins/ecosystem/selection.js +8 -7
  580. package/dist/plugins/ecosystem/selection.js.map +1 -1
  581. package/dist/plugins/ecosystem/state.d.ts.map +1 -1
  582. package/dist/plugins/ecosystem/state.js +24 -23
  583. package/dist/plugins/ecosystem/state.js.map +1 -1
  584. package/dist/plugins/ecosystem/updates.d.ts.map +1 -1
  585. package/dist/plugins/ecosystem/updates.js +5 -4
  586. package/dist/plugins/ecosystem/updates.js.map +1 -1
  587. package/dist/plugins/mcp-http.d.ts +0 -17
  588. package/dist/plugins/mcp-http.d.ts.map +1 -1
  589. package/dist/plugins/mcp-http.js +30 -7
  590. package/dist/plugins/mcp-http.js.map +1 -1
  591. package/dist/plugins/plugin-supervisor.d.ts.map +1 -1
  592. package/dist/plugins/plugin-supervisor.js +19 -1
  593. package/dist/plugins/plugin-supervisor.js.map +1 -1
  594. package/dist/profile/first-roles.d.ts.map +1 -1
  595. package/dist/profile/first-roles.js +10 -1
  596. package/dist/profile/first-roles.js.map +1 -1
  597. package/dist/profile/goal-plugin.d.ts.map +1 -1
  598. package/dist/profile/goal-plugin.js +24 -5
  599. package/dist/profile/goal-plugin.js.map +1 -1
  600. package/dist/profile/identity.d.ts.map +1 -1
  601. package/dist/profile/identity.js +7 -6
  602. package/dist/profile/identity.js.map +1 -1
  603. package/dist/profile/lock.d.ts.map +1 -1
  604. package/dist/profile/lock.js +15 -1
  605. package/dist/profile/lock.js.map +1 -1
  606. package/dist/profile/plan-plugin.d.ts.map +1 -1
  607. package/dist/profile/plan-plugin.js +26 -13
  608. package/dist/profile/plan-plugin.js.map +1 -1
  609. package/dist/profile/plugin-profiles.d.ts.map +1 -1
  610. package/dist/profile/plugin-profiles.js +2 -1
  611. package/dist/profile/plugin-profiles.js.map +1 -1
  612. package/dist/profile/policy-manifest.d.ts.map +1 -1
  613. package/dist/profile/policy-manifest.js +5 -4
  614. package/dist/profile/policy-manifest.js.map +1 -1
  615. package/dist/profile/profile-registry.d.ts +0 -12
  616. package/dist/profile/profile-registry.d.ts.map +1 -1
  617. package/dist/profile/profile-registry.js +14 -1
  618. package/dist/profile/profile-registry.js.map +1 -1
  619. package/dist/profile/role-catalog.d.ts +0 -1
  620. package/dist/profile/role-catalog.d.ts.map +1 -1
  621. package/dist/profile/role-catalog.js +17 -15
  622. package/dist/profile/role-catalog.js.map +1 -1
  623. package/dist/profile/role-orchestrator.d.ts.map +1 -1
  624. package/dist/profile/role-orchestrator.js +8 -7
  625. package/dist/profile/role-orchestrator.js.map +1 -1
  626. package/dist/profile/role-runner.d.ts.map +1 -1
  627. package/dist/profile/role-runner.js +3 -2
  628. package/dist/profile/role-runner.js.map +1 -1
  629. package/dist/profile/root-facade.d.ts.map +1 -1
  630. package/dist/profile/root-facade.js +2 -1
  631. package/dist/profile/root-facade.js.map +1 -1
  632. package/dist/profile/skill-discovery-wiring.d.ts.map +1 -1
  633. package/dist/profile/skill-discovery-wiring.js +7 -2
  634. package/dist/profile/skill-discovery-wiring.js.map +1 -1
  635. package/dist/profile/state-store.d.ts.map +1 -1
  636. package/dist/profile/state-store.js +13 -9
  637. package/dist/profile/state-store.js.map +1 -1
  638. package/dist/profile/todo-plugin.d.ts.map +1 -1
  639. package/dist/profile/todo-plugin.js +9 -4
  640. package/dist/profile/todo-plugin.js.map +1 -1
  641. package/dist/profiles/builtin-suites.d.ts +13 -6
  642. package/dist/profiles/builtin-suites.d.ts.map +1 -1
  643. package/dist/profiles/builtin-suites.js +15 -6
  644. package/dist/profiles/builtin-suites.js.map +1 -1
  645. package/dist/profiles/suite-loader.d.ts +25 -1
  646. package/dist/profiles/suite-loader.d.ts.map +1 -1
  647. package/dist/profiles/suite-loader.js +34 -9
  648. package/dist/profiles/suite-loader.js.map +1 -1
  649. package/dist/remote/server-entry.d.ts.map +1 -1
  650. package/dist/remote/server-entry.js +3 -2
  651. package/dist/remote/server-entry.js.map +1 -1
  652. package/dist/remote/session-source.d.ts.map +1 -1
  653. package/dist/remote/session-source.js +8 -4
  654. package/dist/remote/session-source.js.map +1 -1
  655. package/dist/share/session-share.d.ts.map +1 -1
  656. package/dist/share/session-share.js +14 -2
  657. package/dist/share/session-share.js.map +1 -1
  658. package/dist/testing/plugin-testkit.d.ts.map +1 -1
  659. package/dist/testing/plugin-testkit.js +4 -3
  660. package/dist/testing/plugin-testkit.js.map +1 -1
  661. package/dist/testing/runtime-delivery-queue.d.ts.map +1 -1
  662. package/dist/testing/runtime-delivery-queue.js +2 -1
  663. package/dist/testing/runtime-delivery-queue.js.map +1 -1
  664. package/dist/testing/session-plugin-testkit.d.ts.map +1 -1
  665. package/dist/testing/session-plugin-testkit.js +10 -7
  666. package/dist/testing/session-plugin-testkit.js.map +1 -1
  667. package/dist/utils/ansi.d.ts.map +1 -1
  668. package/dist/utils/ansi.js +2 -1
  669. package/dist/utils/ansi.js.map +1 -1
  670. package/dist/utils/clipboard.d.ts.map +1 -1
  671. package/dist/utils/clipboard.js +7 -6
  672. package/dist/utils/clipboard.js.map +1 -1
  673. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  674. package/dist/utils/image-resize-worker.js +3 -2
  675. package/dist/utils/image-resize-worker.js.map +1 -1
  676. package/dist/utils/photon.d.ts +2 -2
  677. package/dist/utils/photon.d.ts.map +1 -1
  678. package/dist/utils/photon.js +2 -2
  679. package/dist/utils/photon.js.map +1 -1
  680. package/dist/utils/shell.d.ts.map +1 -1
  681. package/dist/utils/shell.js +9 -8
  682. package/dist/utils/shell.js.map +1 -1
  683. package/dist/utils/tools-manager.d.ts.map +1 -1
  684. package/dist/utils/tools-manager.js +23 -13
  685. package/dist/utils/tools-manager.js.map +1 -1
  686. package/dist/utils/version-check.d.ts.map +1 -1
  687. package/dist/utils/version-check.js +5 -1
  688. package/dist/utils/version-check.js.map +1 -1
  689. package/docs/compaction.md +1 -3
  690. package/docs/docs.json +4 -0
  691. package/docs/index.md +1 -0
  692. package/docs/keybindings.md +3 -0
  693. package/docs/mcp.md +103 -0
  694. package/docs/models.md +14 -22
  695. package/docs/providers.md +0 -1
  696. package/docs/quickstart.md +1 -1
  697. package/docs/rpc.md +1 -26
  698. package/docs/sdk.md +4 -6
  699. package/docs/settings.md +10 -12
  700. package/docs/skills.md +1 -9
  701. package/docs/terminal-setup.md +2 -3
  702. package/docs/usage.md +7 -6
  703. package/examples/extensions/with-deps/package-lock.json +2 -2
  704. package/examples/extensions/with-deps/package.json +1 -1
  705. package/examples/extensions/with-deps/plugin.json +1 -1
  706. package/examples/sdk/10-settings.ts +2 -4
  707. package/examples/sdk/12-full-control.ts +1 -2
  708. package/package.json +1 -3
  709. package/dist/bundle/chunks/chunk-EALJ5DDX.js +0 -1452
  710. package/dist/core/extensions/microcompact.d.ts +0 -80
  711. package/dist/core/extensions/microcompact.d.ts.map +0 -1
  712. package/dist/core/extensions/microcompact.js +0 -134
  713. package/dist/core/extensions/microcompact.js.map +0 -1
  714. package/dist/server/create-harness.d.ts +0 -26
  715. package/dist/server/create-harness.d.ts.map +0 -1
  716. package/dist/server/create-harness.js +0 -106
  717. package/dist/server/create-harness.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"goal-plugin.d.ts","sourceRoot":"","sources":["../../src/profile/goal-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AAExE,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACvG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC7F,OAAO,CACN,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,MAAM,CAAC;IACV,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,GAAG,YAAY,CAiInG","sourcesContent":["/**\n * Goal plugin (2.3b) — Goal schema, evidence, and recovery on top of the 2.3a\n * transition store.\n *\n * Status graph: null → draft → active → achieved, and draft/active → archived.\n * `achieve` requires at least one evidence reference. The plugin holds NO\n * shadow state: every read derives status, revision, title, and evidence from\n * the transition history, so recovery is exactly a store replay.\n */\nimport type { TransitionEvidenceRefV1, TransitionStoreV1 } from \"./state-store.ts\";\n\nexport type GoalStatusV1 = \"draft\" | \"active\" | \"achieved\" | \"archived\";\n\nexport interface GoalV1 {\n\treadonly goalId: string;\n\treadonly owner: string;\n\treadonly title: string;\n\treadonly status: GoalStatusV1;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revision: number;\n}\n\nexport interface GoalPluginV1 {\n\tcreateGoal(input: { readonly goalId: string; readonly owner: string; readonly title: string }): GoalV1;\n\tactivate(goalId: string, owner: string, options?: { readonly operationId?: string }): GoalV1;\n\tachieve(\n\t\tgoalId: string,\n\t\towner: string,\n\t\tevidence: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly operationId?: string },\n\t): GoalV1;\n\tarchive(goalId: string, owner: string): GoalV1;\n\tget(goalId: string, owner: string): GoalV1 | undefined;\n}\n\nexport function createGoalPlugin(options: { readonly transitions: TransitionStoreV1 }): GoalPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst goalHistory = (goalId: string, owner: string) =>\n\t\ttransitions.history(goalId).filter((record) => record.domain === \"goal\" && record.owner === owner);\n\n\tconst current = (goalId: string, owner: string): { status: GoalStatusV1; revision: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\tif (state === undefined || goalHistory(goalId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as GoalStatusV1, revision: state.revision };\n\t};\n\n\tconst titleOf = (goalId: string, owner: string): string => {\n\t\tfor (const record of goalHistory(goalId, owner)) {\n\t\t\tif (record.fromStatus !== null) continue;\n\t\t\tconst payload = record.payload;\n\t\t\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\t\t\tconst title = (payload as Record<string, unknown>).title;\n\t\t\t\tif (typeof title === \"string\") return title;\n\t\t\t}\n\t\t}\n\t\treturn goalId;\n\t};\n\n\tconst goalGet = (goalId: string, owner: string): GoalV1 | undefined => {\n\t\tconst state = current(goalId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = goalHistory(goalId, owner);\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state.status,\n\t\t\tevidence: lastEvidence,\n\t\t\trevision: state.revision,\n\t\t};\n\t};\n\n\t/** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */\n\tconst replayedGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\ttoStatus: GoalStatusV1,\n\t\toperationId?: string,\n\t): GoalV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = goalHistory(goalId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? goalGet(goalId, owner) : undefined;\n\t};\n\n\tconst transitionGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\tfromStatus: GoalStatusV1 | null,\n\t\ttoStatus: GoalStatusV1,\n\t\tgoalOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: { readonly title: string };\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): GoalV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"goal\",\n\t\t\tentityId: goalId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: goalOptions.operationId ?? `${toStatus}:${goalId}`,\n\t\t\tevidence: goalOptions.evidence,\n\t\t\tpayload: goalOptions.payload,\n\t\t});\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state?.status as GoalStatusV1,\n\t\t\tevidence: goalOptions.evidence ?? [],\n\t\t\t// v8 ignore next -- transition 刚写入 currentStatus,必可读到 revision\n\t\t\trevision: state?.revision ?? 0,\n\t\t};\n\t};\n\n\treturn {\n\t\tcreateGoal({ goalId, owner, title }) {\n\t\t\tif (title.trim().length === 0) throw new Error(\"Goal title must be a non-empty string\");\n\t\t\treturn transitionGoal(goalId, owner, null, \"draft\", {\n\t\t\t\tpayload: { title },\n\t\t\t\toperationId: `create:${goalId}`,\n\t\t\t});\n\t\t},\n\t\tactivate(goalId, owner, activateOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"active\", activateOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"draft\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Goal ${goalId} must be in draft status to activate (current: ${existing?.status ?? \"missing\"})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, \"draft\", \"active\", { operationId: activateOptions?.operationId });\n\t\t},\n\t\tachieve(goalId, owner, evidence, achieveOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"achieved\", achieveOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"active\") {\n\t\t\t\tthrow new Error(`Goal ${goalId} must be active to achieve (current: ${existing?.status ?? \"missing\"})`);\n\t\t\t}\n\t\t\tif (evidence.length === 0) throw new Error(\"Achieving a goal requires at least one evidence reference\");\n\t\t\treturn transitionGoal(goalId, owner, \"active\", \"achieved\", {\n\t\t\t\tevidence,\n\t\t\t\toperationId: achieveOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tarchive(goalId, owner) {\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (!existing || existing.status === \"achieved\" || existing.status === \"archived\") {\n\t\t\t\tthrow new Error(`Goal ${goalId} cannot be archived from status ${existing?.status ?? \"missing\"}`);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, existing.status, \"archived\", {});\n\t\t},\n\t\tget(goalId, owner) {\n\t\t\treturn goalGet(goalId, owner);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"goal-plugin.d.ts","sourceRoot":"","sources":["../../src/profile/goal-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AAExE,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACvG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC7F,OAAO,CACN,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,MAAM,CAAC;IACV,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,GAAG,YAAY,CA8InG","sourcesContent":["/**\n * Goal plugin (2.3b) — Goal schema, evidence, and recovery on top of the 2.3a\n * transition store.\n *\n * Status graph: null → draft → active → achieved, and draft/active → archived.\n * `achieve` requires at least one evidence reference. The plugin holds NO\n * shadow state: every read derives status, revision, title, and evidence from\n * the transition history, so recovery is exactly a store replay.\n */\n\nimport { t } from \"../core/i18n/index.ts\";\nimport type { TransitionEvidenceRefV1, TransitionStoreV1 } from \"./state-store.ts\";\n\nexport type GoalStatusV1 = \"draft\" | \"active\" | \"achieved\" | \"archived\";\n\nexport interface GoalV1 {\n\treadonly goalId: string;\n\treadonly owner: string;\n\treadonly title: string;\n\treadonly status: GoalStatusV1;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revision: number;\n}\n\nexport interface GoalPluginV1 {\n\tcreateGoal(input: { readonly goalId: string; readonly owner: string; readonly title: string }): GoalV1;\n\tactivate(goalId: string, owner: string, options?: { readonly operationId?: string }): GoalV1;\n\tachieve(\n\t\tgoalId: string,\n\t\towner: string,\n\t\tevidence: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly operationId?: string },\n\t): GoalV1;\n\tarchive(goalId: string, owner: string): GoalV1;\n\tget(goalId: string, owner: string): GoalV1 | undefined;\n}\n\nexport function createGoalPlugin(options: { readonly transitions: TransitionStoreV1 }): GoalPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst goalHistory = (goalId: string, owner: string) =>\n\t\ttransitions.history(goalId).filter((record) => record.domain === \"goal\" && record.owner === owner);\n\n\tconst current = (goalId: string, owner: string): { status: GoalStatusV1; revision: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\tif (state === undefined || goalHistory(goalId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as GoalStatusV1, revision: state.revision };\n\t};\n\n\tconst titleOf = (goalId: string, owner: string): string => {\n\t\tfor (const record of goalHistory(goalId, owner)) {\n\t\t\tif (record.fromStatus !== null) continue;\n\t\t\tconst payload = record.payload;\n\t\t\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\t\t\tconst title = (payload as Record<string, unknown>).title;\n\t\t\t\tif (typeof title === \"string\") return title;\n\t\t\t}\n\t\t}\n\t\treturn goalId;\n\t};\n\n\tconst goalGet = (goalId: string, owner: string): GoalV1 | undefined => {\n\t\tconst state = current(goalId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = goalHistory(goalId, owner);\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state.status,\n\t\t\tevidence: lastEvidence,\n\t\t\trevision: state.revision,\n\t\t};\n\t};\n\n\t/** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */\n\tconst replayedGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\ttoStatus: GoalStatusV1,\n\t\toperationId?: string,\n\t): GoalV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = goalHistory(goalId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? goalGet(goalId, owner) : undefined;\n\t};\n\n\tconst transitionGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\tfromStatus: GoalStatusV1 | null,\n\t\ttoStatus: GoalStatusV1,\n\t\tgoalOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: { readonly title: string };\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): GoalV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"goal\",\n\t\t\tentityId: goalId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: goalOptions.operationId ?? `${toStatus}:${goalId}`,\n\t\t\tevidence: goalOptions.evidence,\n\t\t\tpayload: goalOptions.payload,\n\t\t});\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state?.status as GoalStatusV1,\n\t\t\tevidence: goalOptions.evidence ?? [],\n\t\t\t// v8 ignore next -- transition 刚写入 currentStatus,必可读到 revision\n\t\t\trevision: state?.revision ?? 0,\n\t\t};\n\t};\n\n\treturn {\n\t\tcreateGoal({ goalId, owner, title }) {\n\t\t\tif (title.trim().length === 0) throw new Error(t(\"Goal title must be a non-empty string\"));\n\t\t\treturn transitionGoal(goalId, owner, null, \"draft\", {\n\t\t\t\tpayload: { title },\n\t\t\t\toperationId: `create:${goalId}`,\n\t\t\t});\n\t\t},\n\t\tactivate(goalId, owner, activateOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"active\", activateOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"draft\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Goal {goalId} must be in draft status to activate (current: {status})\", {\n\t\t\t\t\t\tgoalId,\n\t\t\t\t\t\tstatus: existing?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, \"draft\", \"active\", { operationId: activateOptions?.operationId });\n\t\t},\n\t\tachieve(goalId, owner, evidence, achieveOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"achieved\", achieveOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"active\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Goal {goalId} must be active to achieve (current: {status})\", {\n\t\t\t\t\t\tgoalId,\n\t\t\t\t\t\tstatus: existing?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (evidence.length === 0) throw new Error(t(\"Achieving a goal requires at least one evidence reference\"));\n\t\t\treturn transitionGoal(goalId, owner, \"active\", \"achieved\", {\n\t\t\t\tevidence,\n\t\t\t\toperationId: achieveOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tarchive(goalId, owner) {\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (!existing || existing.status === \"achieved\" || existing.status === \"archived\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Goal {goalId} cannot be archived from status {status}\", {\n\t\t\t\t\t\tgoalId,\n\t\t\t\t\t\tstatus: existing?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, existing.status, \"archived\", {});\n\t\t},\n\t\tget(goalId, owner) {\n\t\t\treturn goalGet(goalId, owner);\n\t\t},\n\t};\n}\n"]}
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Goal plugin (2.3b) — Goal schema, evidence, and recovery on top of the 2.3a
3
+ * transition store.
4
+ *
5
+ * Status graph: null → draft → active → achieved, and draft/active → archived.
6
+ * `achieve` requires at least one evidence reference. The plugin holds NO
7
+ * shadow state: every read derives status, revision, title, and evidence from
8
+ * the transition history, so recovery is exactly a store replay.
9
+ */
10
+ import { t } from "../core/i18n/index.js";
1
11
  export function createGoalPlugin(options) {
2
12
  const transitions = options.transitions;
3
13
  const goalHistory = (goalId, owner) => transitions.history(goalId).filter((record) => record.domain === "goal" && record.owner === owner);
@@ -67,7 +77,7 @@ export function createGoalPlugin(options) {
67
77
  return {
68
78
  createGoal({ goalId, owner, title }) {
69
79
  if (title.trim().length === 0)
70
- throw new Error("Goal title must be a non-empty string");
80
+ throw new Error(t("Goal title must be a non-empty string"));
71
81
  return transitionGoal(goalId, owner, null, "draft", {
72
82
  payload: { title },
73
83
  operationId: `create:${goalId}`,
@@ -79,7 +89,10 @@ export function createGoalPlugin(options) {
79
89
  return replayed;
80
90
  const existing = current(goalId, owner);
81
91
  if (existing?.status !== "draft") {
82
- throw new Error(`Goal ${goalId} must be in draft status to activate (current: ${existing?.status ?? "missing"})`);
92
+ throw new Error(t("Goal {goalId} must be in draft status to activate (current: {status})", {
93
+ goalId,
94
+ status: existing?.status ?? "missing",
95
+ }));
83
96
  }
84
97
  return transitionGoal(goalId, owner, "draft", "active", { operationId: activateOptions?.operationId });
85
98
  },
@@ -89,10 +102,13 @@ export function createGoalPlugin(options) {
89
102
  return replayed;
90
103
  const existing = current(goalId, owner);
91
104
  if (existing?.status !== "active") {
92
- throw new Error(`Goal ${goalId} must be active to achieve (current: ${existing?.status ?? "missing"})`);
105
+ throw new Error(t("Goal {goalId} must be active to achieve (current: {status})", {
106
+ goalId,
107
+ status: existing?.status ?? "missing",
108
+ }));
93
109
  }
94
110
  if (evidence.length === 0)
95
- throw new Error("Achieving a goal requires at least one evidence reference");
111
+ throw new Error(t("Achieving a goal requires at least one evidence reference"));
96
112
  return transitionGoal(goalId, owner, "active", "achieved", {
97
113
  evidence,
98
114
  operationId: achieveOptions?.operationId,
@@ -101,7 +117,10 @@ export function createGoalPlugin(options) {
101
117
  archive(goalId, owner) {
102
118
  const existing = current(goalId, owner);
103
119
  if (!existing || existing.status === "achieved" || existing.status === "archived") {
104
- throw new Error(`Goal ${goalId} cannot be archived from status ${existing?.status ?? "missing"}`);
120
+ throw new Error(t("Goal {goalId} cannot be archived from status {status}", {
121
+ goalId,
122
+ status: existing?.status ?? "missing",
123
+ }));
105
124
  }
106
125
  return transitionGoal(goalId, owner, existing.status, "archived", {});
107
126
  },
@@ -1 +1 @@
1
- {"version":3,"file":"goal-plugin.js","sourceRoot":"","sources":["../../src/profile/goal-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal plugin (2.3b) — Goal schema, evidence, and recovery on top of the 2.3a\n * transition store.\n *\n * Status graph: null → draft → active → achieved, and draft/active → archived.\n * `achieve` requires at least one evidence reference. The plugin holds NO\n * shadow state: every read derives status, revision, title, and evidence from\n * the transition history, so recovery is exactly a store replay.\n */\nimport type { TransitionEvidenceRefV1, TransitionStoreV1 } from \"./state-store.ts\";\n\nexport type GoalStatusV1 = \"draft\" | \"active\" | \"achieved\" | \"archived\";\n\nexport interface GoalV1 {\n\treadonly goalId: string;\n\treadonly owner: string;\n\treadonly title: string;\n\treadonly status: GoalStatusV1;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revision: number;\n}\n\nexport interface GoalPluginV1 {\n\tcreateGoal(input: { readonly goalId: string; readonly owner: string; readonly title: string }): GoalV1;\n\tactivate(goalId: string, owner: string, options?: { readonly operationId?: string }): GoalV1;\n\tachieve(\n\t\tgoalId: string,\n\t\towner: string,\n\t\tevidence: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly operationId?: string },\n\t): GoalV1;\n\tarchive(goalId: string, owner: string): GoalV1;\n\tget(goalId: string, owner: string): GoalV1 | undefined;\n}\n\nexport function createGoalPlugin(options: { readonly transitions: TransitionStoreV1 }): GoalPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst goalHistory = (goalId: string, owner: string) =>\n\t\ttransitions.history(goalId).filter((record) => record.domain === \"goal\" && record.owner === owner);\n\n\tconst current = (goalId: string, owner: string): { status: GoalStatusV1; revision: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\tif (state === undefined || goalHistory(goalId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as GoalStatusV1, revision: state.revision };\n\t};\n\n\tconst titleOf = (goalId: string, owner: string): string => {\n\t\tfor (const record of goalHistory(goalId, owner)) {\n\t\t\tif (record.fromStatus !== null) continue;\n\t\t\tconst payload = record.payload;\n\t\t\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\t\t\tconst title = (payload as Record<string, unknown>).title;\n\t\t\t\tif (typeof title === \"string\") return title;\n\t\t\t}\n\t\t}\n\t\treturn goalId;\n\t};\n\n\tconst goalGet = (goalId: string, owner: string): GoalV1 | undefined => {\n\t\tconst state = current(goalId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = goalHistory(goalId, owner);\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state.status,\n\t\t\tevidence: lastEvidence,\n\t\t\trevision: state.revision,\n\t\t};\n\t};\n\n\t/** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */\n\tconst replayedGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\ttoStatus: GoalStatusV1,\n\t\toperationId?: string,\n\t): GoalV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = goalHistory(goalId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? goalGet(goalId, owner) : undefined;\n\t};\n\n\tconst transitionGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\tfromStatus: GoalStatusV1 | null,\n\t\ttoStatus: GoalStatusV1,\n\t\tgoalOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: { readonly title: string };\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): GoalV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"goal\",\n\t\t\tentityId: goalId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: goalOptions.operationId ?? `${toStatus}:${goalId}`,\n\t\t\tevidence: goalOptions.evidence,\n\t\t\tpayload: goalOptions.payload,\n\t\t});\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state?.status as GoalStatusV1,\n\t\t\tevidence: goalOptions.evidence ?? [],\n\t\t\t// v8 ignore next -- transition 刚写入 currentStatus,必可读到 revision\n\t\t\trevision: state?.revision ?? 0,\n\t\t};\n\t};\n\n\treturn {\n\t\tcreateGoal({ goalId, owner, title }) {\n\t\t\tif (title.trim().length === 0) throw new Error(\"Goal title must be a non-empty string\");\n\t\t\treturn transitionGoal(goalId, owner, null, \"draft\", {\n\t\t\t\tpayload: { title },\n\t\t\t\toperationId: `create:${goalId}`,\n\t\t\t});\n\t\t},\n\t\tactivate(goalId, owner, activateOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"active\", activateOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"draft\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Goal ${goalId} must be in draft status to activate (current: ${existing?.status ?? \"missing\"})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, \"draft\", \"active\", { operationId: activateOptions?.operationId });\n\t\t},\n\t\tachieve(goalId, owner, evidence, achieveOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"achieved\", achieveOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"active\") {\n\t\t\t\tthrow new Error(`Goal ${goalId} must be active to achieve (current: ${existing?.status ?? \"missing\"})`);\n\t\t\t}\n\t\t\tif (evidence.length === 0) throw new Error(\"Achieving a goal requires at least one evidence reference\");\n\t\t\treturn transitionGoal(goalId, owner, \"active\", \"achieved\", {\n\t\t\t\tevidence,\n\t\t\t\toperationId: achieveOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tarchive(goalId, owner) {\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (!existing || existing.status === \"achieved\" || existing.status === \"archived\") {\n\t\t\t\tthrow new Error(`Goal ${goalId} cannot be archived from status ${existing?.status ?? \"missing\"}`);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, existing.status, \"archived\", {});\n\t\t},\n\t\tget(goalId, owner) {\n\t\t\treturn goalGet(goalId, owner);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"goal-plugin.js","sourceRoot":"","sources":["../../src/profile/goal-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal plugin (2.3b) — Goal schema, evidence, and recovery on top of the 2.3a\n * transition store.\n *\n * Status graph: null → draft → active → achieved, and draft/active → archived.\n * `achieve` requires at least one evidence reference. The plugin holds NO\n * shadow state: every read derives status, revision, title, and evidence from\n * the transition history, so recovery is exactly a store replay.\n */\n\nimport { t } from \"../core/i18n/index.ts\";\nimport type { TransitionEvidenceRefV1, TransitionStoreV1 } from \"./state-store.ts\";\n\nexport type GoalStatusV1 = \"draft\" | \"active\" | \"achieved\" | \"archived\";\n\nexport interface GoalV1 {\n\treadonly goalId: string;\n\treadonly owner: string;\n\treadonly title: string;\n\treadonly status: GoalStatusV1;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revision: number;\n}\n\nexport interface GoalPluginV1 {\n\tcreateGoal(input: { readonly goalId: string; readonly owner: string; readonly title: string }): GoalV1;\n\tactivate(goalId: string, owner: string, options?: { readonly operationId?: string }): GoalV1;\n\tachieve(\n\t\tgoalId: string,\n\t\towner: string,\n\t\tevidence: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly operationId?: string },\n\t): GoalV1;\n\tarchive(goalId: string, owner: string): GoalV1;\n\tget(goalId: string, owner: string): GoalV1 | undefined;\n}\n\nexport function createGoalPlugin(options: { readonly transitions: TransitionStoreV1 }): GoalPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst goalHistory = (goalId: string, owner: string) =>\n\t\ttransitions.history(goalId).filter((record) => record.domain === \"goal\" && record.owner === owner);\n\n\tconst current = (goalId: string, owner: string): { status: GoalStatusV1; revision: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\tif (state === undefined || goalHistory(goalId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as GoalStatusV1, revision: state.revision };\n\t};\n\n\tconst titleOf = (goalId: string, owner: string): string => {\n\t\tfor (const record of goalHistory(goalId, owner)) {\n\t\t\tif (record.fromStatus !== null) continue;\n\t\t\tconst payload = record.payload;\n\t\t\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\t\t\tconst title = (payload as Record<string, unknown>).title;\n\t\t\t\tif (typeof title === \"string\") return title;\n\t\t\t}\n\t\t}\n\t\treturn goalId;\n\t};\n\n\tconst goalGet = (goalId: string, owner: string): GoalV1 | undefined => {\n\t\tconst state = current(goalId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = goalHistory(goalId, owner);\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state.status,\n\t\t\tevidence: lastEvidence,\n\t\t\trevision: state.revision,\n\t\t};\n\t};\n\n\t/** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */\n\tconst replayedGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\ttoStatus: GoalStatusV1,\n\t\toperationId?: string,\n\t): GoalV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = goalHistory(goalId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? goalGet(goalId, owner) : undefined;\n\t};\n\n\tconst transitionGoal = (\n\t\tgoalId: string,\n\t\towner: string,\n\t\tfromStatus: GoalStatusV1 | null,\n\t\ttoStatus: GoalStatusV1,\n\t\tgoalOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: { readonly title: string };\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): GoalV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"goal\",\n\t\t\tentityId: goalId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: goalOptions.operationId ?? `${toStatus}:${goalId}`,\n\t\t\tevidence: goalOptions.evidence,\n\t\t\tpayload: goalOptions.payload,\n\t\t});\n\t\tconst state = transitions.currentStatus(\"goal\", goalId);\n\t\treturn {\n\t\t\tgoalId,\n\t\t\towner,\n\t\t\ttitle: titleOf(goalId, owner),\n\t\t\tstatus: state?.status as GoalStatusV1,\n\t\t\tevidence: goalOptions.evidence ?? [],\n\t\t\t// v8 ignore next -- transition 刚写入 currentStatus,必可读到 revision\n\t\t\trevision: state?.revision ?? 0,\n\t\t};\n\t};\n\n\treturn {\n\t\tcreateGoal({ goalId, owner, title }) {\n\t\t\tif (title.trim().length === 0) throw new Error(t(\"Goal title must be a non-empty string\"));\n\t\t\treturn transitionGoal(goalId, owner, null, \"draft\", {\n\t\t\t\tpayload: { title },\n\t\t\t\toperationId: `create:${goalId}`,\n\t\t\t});\n\t\t},\n\t\tactivate(goalId, owner, activateOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"active\", activateOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"draft\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Goal {goalId} must be in draft status to activate (current: {status})\", {\n\t\t\t\t\t\tgoalId,\n\t\t\t\t\t\tstatus: existing?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, \"draft\", \"active\", { operationId: activateOptions?.operationId });\n\t\t},\n\t\tachieve(goalId, owner, evidence, achieveOptions) {\n\t\t\tconst replayed = replayedGoal(goalId, owner, \"achieved\", achieveOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (existing?.status !== \"active\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Goal {goalId} must be active to achieve (current: {status})\", {\n\t\t\t\t\t\tgoalId,\n\t\t\t\t\t\tstatus: existing?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (evidence.length === 0) throw new Error(t(\"Achieving a goal requires at least one evidence reference\"));\n\t\t\treturn transitionGoal(goalId, owner, \"active\", \"achieved\", {\n\t\t\t\tevidence,\n\t\t\t\toperationId: achieveOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tarchive(goalId, owner) {\n\t\t\tconst existing = current(goalId, owner);\n\t\t\tif (!existing || existing.status === \"achieved\" || existing.status === \"archived\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Goal {goalId} cannot be archived from status {status}\", {\n\t\t\t\t\t\tgoalId,\n\t\t\t\t\t\tstatus: existing?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionGoal(goalId, owner, existing.status, \"archived\", {});\n\t\t},\n\t\tget(goalId, owner) {\n\t\t\treturn goalGet(goalId, owner);\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/profile/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,uDAAuD;AACvD,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtF,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,sEAAsE;IACtE,QAAQ,CAAC,kBAAkB,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1F;AAaD,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAQzD;AA2CD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,eAAe,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC9D,2DAA2D;IAC3D,QAAQ,CAAC,kBAAkB,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1F,GAAG,iBAAiB,CAgCpB;AAED,6EAA6E;AAC7E,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAEtG","sourcesContent":["/**\n * Profile identity — minimal Profile identity, resolved manifest digest, and\n * host compatibility facts (2.0a).\n *\n * The identity is fully determined by the resolved inputs: profileId +\n * profileVersion name it; the manifest digest is an FNV-1a digest over the\n * key-order-stable serialization of the sorted resolved plugin manifests; the\n * resolved capability versions are carried as a sorted, JSON-serializable\n * list. The identity is the input for the session-start Profile lock (2.0b).\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { comparePluginVersionsV1 } from \"../plugins/ecosystem/catalog.ts\";\n\n/** One resolved plugin manifest entry of a Profile. */\nexport interface ResolvedPluginManifestV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly manifestPath?: string;\n\treadonly capabilityRefs: readonly { readonly id: string; readonly version: number }[];\n\treadonly selectedAdapter?: { readonly id: string; readonly version: string };\n}\n\n/** The serializable, digest-carrying Profile identity. */\nexport interface ProfileIdentityV1 {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** FNV-1a digest over the stable serialization of the resolved manifests. */\n\treadonly manifestDigest: string;\n\t/** Sorted (id, version) capability references of the resolved set. */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}\n\nfunction stableStringify(value: JsonValue): string {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n\tconst entries = Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record<string, JsonValue>)[key])}`);\n\treturn `{${entries.join(\",\")}}`;\n}\n\n/** FNV-1a 64-bit digest over the key-order-stable JSON serialization. */\nexport function stableJsonDigest(value: JsonValue): string {\n\tconst serialized = stableStringify(value);\n\tlet digest = 14695981039346656037n;\n\tfor (const byte of new TextEncoder().encode(serialized)) {\n\t\tdigest ^= BigInt(byte);\n\t\tdigest = BigInt.asUintN(64, digest * 1099511628211n);\n\t}\n\treturn digest.toString(16).padStart(16, \"0\");\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\nfunction assertPluginEntries(\n\tvalue: unknown,\n\tlabel: string,\n): { pluginId: string; pluginVersion: string; selectedAdapter?: { id: string; version: string } }[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst seen = new Set<string>();\n\tconst entries = value.map((item, index) => {\n\t\tif (item === null || typeof item !== \"object\" || Array.isArray(item)) {\n\t\t\tthrow new Error(`${label}[${index}] must be a plain object`);\n\t\t}\n\t\tconst record = item as Record<string, unknown>;\n\t\tassertNonEmptyString(record.pluginId, `${label}[${index}].pluginId`);\n\t\tassertNonEmptyString(record.pluginVersion, `${label}[${index}].pluginVersion`);\n\t\tconst key = `${record.pluginId}@${record.pluginVersion}`;\n\t\tif (seen.has(key)) throw new Error(`${label} contains duplicate entry: ${key}`);\n\t\tseen.add(key);\n\t\tconst selectedAdapter = record.selectedAdapter;\n\t\tif (selectedAdapter === undefined) return { pluginId: record.pluginId, pluginVersion: record.pluginVersion };\n\t\tif (selectedAdapter === null || typeof selectedAdapter !== \"object\" || Array.isArray(selectedAdapter)) {\n\t\t\tthrow new Error(`${label}[${index}].selectedAdapter must be an object`);\n\t\t}\n\t\tconst adapter = selectedAdapter as Record<string, unknown>;\n\t\tassertNonEmptyString(adapter.id, `${label}[${index}].selectedAdapter.id`);\n\t\tassertNonEmptyString(adapter.version, `${label}[${index}].selectedAdapter.version`);\n\t\treturn {\n\t\t\tpluginId: record.pluginId,\n\t\t\tpluginVersion: record.pluginVersion,\n\t\t\tselectedAdapter: { id: adapter.id, version: adapter.version },\n\t\t};\n\t});\n\treturn entries.sort((left, right) =>\n\t\tleft.pluginId === right.pluginId\n\t\t\t? comparePluginVersionsV1(left.pluginVersion, right.pluginVersion)\n\t\t\t: left.pluginId.localeCompare(right.pluginId),\n\t);\n}\n\n/**\n * Builds the Profile identity from the resolved inputs. The digest is\n * order-independent (sorted before serialization) so the same resolved set\n * always yields the same identity regardless of discovery order.\n */\nexport function buildProfileIdentityV1(input: {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** Resolved plugin manifests participating in the Profile. */\n\treadonly resolvedPlugins: readonly ResolvedPluginManifestV1[];\n\t/** Resolved capability references (id + exact version). */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}): ProfileIdentityV1 {\n\tassertNonEmptyString(input.profileId, \"Profile identity.profileId\");\n\tassertNonEmptyString(input.profileVersion, \"Profile identity.profileVersion\");\n\tconst pluginEntries = assertPluginEntries(input.resolvedPlugins, \"Profile identity.resolvedPlugins\");\n\n\tconst capabilityVersions = [...input.capabilityVersions]\n\t\t.map((ref) => {\n\t\t\tif (typeof ref.version !== \"number\" || !Number.isSafeInteger(ref.version) || ref.version < 1) {\n\t\t\t\tthrow new Error(`Profile identity.capabilityVersions version must be a positive safe integer: ${ref.id}`);\n\t\t\t}\n\t\t\tassertNonEmptyString(ref.id, \"Profile identity.capabilityVersions.id\");\n\t\t\treturn { id: ref.id, version: ref.version };\n\t\t})\n\t\t.sort((left, right) => (left.id === right.id ? left.version - right.version : left.id.localeCompare(right.id)));\n\n\tconst manifestDigest = stableJsonDigest({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tplugins: pluginEntries.map((entry) => ({\n\t\t\tpluginId: entry.pluginId,\n\t\t\tpluginVersion: entry.pluginVersion,\n\t\t\t...(entry.selectedAdapter === undefined ? {} : { selectedAdapter: entry.selectedAdapter }),\n\t\t})),\n\t\tcapabilityVersions: capabilityVersions.map((ref) => ({ id: ref.id, version: ref.version })),\n\t});\n\n\treturn Object.freeze({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tmanifestDigest,\n\t\tcapabilityVersions: Object.freeze(capabilityVersions.map((ref) => Object.freeze({ ...ref }))),\n\t});\n}\n\n/** Host compatibility check: identities match when the digests are equal. */\nexport function profileIdentitiesCompatible(left: ProfileIdentityV1, right: ProfileIdentityV1): boolean {\n\treturn left.manifestDigest === right.manifestDigest;\n}\n"]}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/profile/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,uDAAuD;AACvD,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtF,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,sEAAsE;IACtE,QAAQ,CAAC,kBAAkB,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1F;AAaD,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAQzD;AA4CD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,eAAe,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC9D,2DAA2D;IAC3D,QAAQ,CAAC,kBAAkB,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1F,GAAG,iBAAiB,CAkCpB;AAED,6EAA6E;AAC7E,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAEtG","sourcesContent":["/**\n * Profile identity — minimal Profile identity, resolved manifest digest, and\n * host compatibility facts (2.0a).\n *\n * The identity is fully determined by the resolved inputs: profileId +\n * profileVersion name it; the manifest digest is an FNV-1a digest over the\n * key-order-stable serialization of the sorted resolved plugin manifests; the\n * resolved capability versions are carried as a sorted, JSON-serializable\n * list. The identity is the input for the session-start Profile lock (2.0b).\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { t } from \"../core/i18n/index.ts\";\nimport { comparePluginVersionsV1 } from \"../plugins/ecosystem/catalog.ts\";\n\n/** One resolved plugin manifest entry of a Profile. */\nexport interface ResolvedPluginManifestV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly manifestPath?: string;\n\treadonly capabilityRefs: readonly { readonly id: string; readonly version: number }[];\n\treadonly selectedAdapter?: { readonly id: string; readonly version: string };\n}\n\n/** The serializable, digest-carrying Profile identity. */\nexport interface ProfileIdentityV1 {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** FNV-1a digest over the stable serialization of the resolved manifests. */\n\treadonly manifestDigest: string;\n\t/** Sorted (id, version) capability references of the resolved set. */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}\n\nfunction stableStringify(value: JsonValue): string {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n\tconst entries = Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record<string, JsonValue>)[key])}`);\n\treturn `{${entries.join(\",\")}}`;\n}\n\n/** FNV-1a 64-bit digest over the key-order-stable JSON serialization. */\nexport function stableJsonDigest(value: JsonValue): string {\n\tconst serialized = stableStringify(value);\n\tlet digest = 14695981039346656037n;\n\tfor (const byte of new TextEncoder().encode(serialized)) {\n\t\tdigest ^= BigInt(byte);\n\t\tdigest = BigInt.asUintN(64, digest * 1099511628211n);\n\t}\n\treturn digest.toString(16).padStart(16, \"0\");\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0)\n\t\tthrow new Error(t(\"{label} must be a non-empty string\", { label }));\n}\n\nfunction assertPluginEntries(\n\tvalue: unknown,\n\tlabel: string,\n): { pluginId: string; pluginVersion: string; selectedAdapter?: { id: string; version: string } }[] {\n\tif (!Array.isArray(value)) throw new Error(t(\"{label} must be an array\", { label }));\n\tconst seen = new Set<string>();\n\tconst entries = value.map((item, index) => {\n\t\tif (item === null || typeof item !== \"object\" || Array.isArray(item)) {\n\t\t\tthrow new Error(t(\"{label}[{index}] must be a plain object\", { label, index }));\n\t\t}\n\t\tconst record = item as Record<string, unknown>;\n\t\tassertNonEmptyString(record.pluginId, `${label}[${index}].pluginId`);\n\t\tassertNonEmptyString(record.pluginVersion, `${label}[${index}].pluginVersion`);\n\t\tconst key = `${record.pluginId}@${record.pluginVersion}`;\n\t\tif (seen.has(key)) throw new Error(t(\"{label} contains duplicate entry: {key}\", { label, key }));\n\t\tseen.add(key);\n\t\tconst selectedAdapter = record.selectedAdapter;\n\t\tif (selectedAdapter === undefined) return { pluginId: record.pluginId, pluginVersion: record.pluginVersion };\n\t\tif (selectedAdapter === null || typeof selectedAdapter !== \"object\" || Array.isArray(selectedAdapter)) {\n\t\t\tthrow new Error(t(\"{label}[{index}].selectedAdapter must be an object\", { label, index }));\n\t\t}\n\t\tconst adapter = selectedAdapter as Record<string, unknown>;\n\t\tassertNonEmptyString(adapter.id, `${label}[${index}].selectedAdapter.id`);\n\t\tassertNonEmptyString(adapter.version, `${label}[${index}].selectedAdapter.version`);\n\t\treturn {\n\t\t\tpluginId: record.pluginId,\n\t\t\tpluginVersion: record.pluginVersion,\n\t\t\tselectedAdapter: { id: adapter.id, version: adapter.version },\n\t\t};\n\t});\n\treturn entries.sort((left, right) =>\n\t\tleft.pluginId === right.pluginId\n\t\t\t? comparePluginVersionsV1(left.pluginVersion, right.pluginVersion)\n\t\t\t: left.pluginId.localeCompare(right.pluginId),\n\t);\n}\n\n/**\n * Builds the Profile identity from the resolved inputs. The digest is\n * order-independent (sorted before serialization) so the same resolved set\n * always yields the same identity regardless of discovery order.\n */\nexport function buildProfileIdentityV1(input: {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** Resolved plugin manifests participating in the Profile. */\n\treadonly resolvedPlugins: readonly ResolvedPluginManifestV1[];\n\t/** Resolved capability references (id + exact version). */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}): ProfileIdentityV1 {\n\tassertNonEmptyString(input.profileId, \"Profile identity.profileId\");\n\tassertNonEmptyString(input.profileVersion, \"Profile identity.profileVersion\");\n\tconst pluginEntries = assertPluginEntries(input.resolvedPlugins, \"Profile identity.resolvedPlugins\");\n\n\tconst capabilityVersions = [...input.capabilityVersions]\n\t\t.map((ref) => {\n\t\t\tif (typeof ref.version !== \"number\" || !Number.isSafeInteger(ref.version) || ref.version < 1) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Profile identity.capabilityVersions version must be a positive safe integer: {id}\", { id: ref.id }),\n\t\t\t\t);\n\t\t\t}\n\t\t\tassertNonEmptyString(ref.id, \"Profile identity.capabilityVersions.id\");\n\t\t\treturn { id: ref.id, version: ref.version };\n\t\t})\n\t\t.sort((left, right) => (left.id === right.id ? left.version - right.version : left.id.localeCompare(right.id)));\n\n\tconst manifestDigest = stableJsonDigest({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tplugins: pluginEntries.map((entry) => ({\n\t\t\tpluginId: entry.pluginId,\n\t\t\tpluginVersion: entry.pluginVersion,\n\t\t\t...(entry.selectedAdapter === undefined ? {} : { selectedAdapter: entry.selectedAdapter }),\n\t\t})),\n\t\tcapabilityVersions: capabilityVersions.map((ref) => ({ id: ref.id, version: ref.version })),\n\t});\n\n\treturn Object.freeze({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tmanifestDigest,\n\t\tcapabilityVersions: Object.freeze(capabilityVersions.map((ref) => Object.freeze({ ...ref }))),\n\t});\n}\n\n/** Host compatibility check: identities match when the digests are equal. */\nexport function profileIdentitiesCompatible(left: ProfileIdentityV1, right: ProfileIdentityV1): boolean {\n\treturn left.manifestDigest === right.manifestDigest;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { t } from "../core/i18n/index.js";
1
2
  import { comparePluginVersionsV1 } from "../plugins/ecosystem/catalog.js";
2
3
  function stableStringify(value) {
3
4
  if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
@@ -22,28 +23,28 @@ export function stableJsonDigest(value) {
22
23
  }
23
24
  function assertNonEmptyString(value, label) {
24
25
  if (typeof value !== "string" || value.trim().length === 0)
25
- throw new Error(`${label} must be a non-empty string`);
26
+ throw new Error(t("{label} must be a non-empty string", { label }));
26
27
  }
27
28
  function assertPluginEntries(value, label) {
28
29
  if (!Array.isArray(value))
29
- throw new Error(`${label} must be an array`);
30
+ throw new Error(t("{label} must be an array", { label }));
30
31
  const seen = new Set();
31
32
  const entries = value.map((item, index) => {
32
33
  if (item === null || typeof item !== "object" || Array.isArray(item)) {
33
- throw new Error(`${label}[${index}] must be a plain object`);
34
+ throw new Error(t("{label}[{index}] must be a plain object", { label, index }));
34
35
  }
35
36
  const record = item;
36
37
  assertNonEmptyString(record.pluginId, `${label}[${index}].pluginId`);
37
38
  assertNonEmptyString(record.pluginVersion, `${label}[${index}].pluginVersion`);
38
39
  const key = `${record.pluginId}@${record.pluginVersion}`;
39
40
  if (seen.has(key))
40
- throw new Error(`${label} contains duplicate entry: ${key}`);
41
+ throw new Error(t("{label} contains duplicate entry: {key}", { label, key }));
41
42
  seen.add(key);
42
43
  const selectedAdapter = record.selectedAdapter;
43
44
  if (selectedAdapter === undefined)
44
45
  return { pluginId: record.pluginId, pluginVersion: record.pluginVersion };
45
46
  if (selectedAdapter === null || typeof selectedAdapter !== "object" || Array.isArray(selectedAdapter)) {
46
- throw new Error(`${label}[${index}].selectedAdapter must be an object`);
47
+ throw new Error(t("{label}[{index}].selectedAdapter must be an object", { label, index }));
47
48
  }
48
49
  const adapter = selectedAdapter;
49
50
  assertNonEmptyString(adapter.id, `${label}[${index}].selectedAdapter.id`);
@@ -70,7 +71,7 @@ export function buildProfileIdentityV1(input) {
70
71
  const capabilityVersions = [...input.capabilityVersions]
71
72
  .map((ref) => {
72
73
  if (typeof ref.version !== "number" || !Number.isSafeInteger(ref.version) || ref.version < 1) {
73
- throw new Error(`Profile identity.capabilityVersions version must be a positive safe integer: ${ref.id}`);
74
+ throw new Error(t("Profile identity.capabilityVersions version must be a positive safe integer: {id}", { id: ref.id }));
74
75
  }
75
76
  assertNonEmptyString(ref.id, "Profile identity.capabilityVersions.id");
76
77
  return { id: ref.id, version: ref.version };
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/profile/identity.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Profile identity — minimal Profile identity, resolved manifest digest, and\n * host compatibility facts (2.0a).\n *\n * The identity is fully determined by the resolved inputs: profileId +\n * profileVersion name it; the manifest digest is an FNV-1a digest over the\n * key-order-stable serialization of the sorted resolved plugin manifests; the\n * resolved capability versions are carried as a sorted, JSON-serializable\n * list. The identity is the input for the session-start Profile lock (2.0b).\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { comparePluginVersionsV1 } from \"../plugins/ecosystem/catalog.ts\";\n\n/** One resolved plugin manifest entry of a Profile. */\nexport interface ResolvedPluginManifestV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly manifestPath?: string;\n\treadonly capabilityRefs: readonly { readonly id: string; readonly version: number }[];\n\treadonly selectedAdapter?: { readonly id: string; readonly version: string };\n}\n\n/** The serializable, digest-carrying Profile identity. */\nexport interface ProfileIdentityV1 {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** FNV-1a digest over the stable serialization of the resolved manifests. */\n\treadonly manifestDigest: string;\n\t/** Sorted (id, version) capability references of the resolved set. */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}\n\nfunction stableStringify(value: JsonValue): string {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n\tconst entries = Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record<string, JsonValue>)[key])}`);\n\treturn `{${entries.join(\",\")}}`;\n}\n\n/** FNV-1a 64-bit digest over the key-order-stable JSON serialization. */\nexport function stableJsonDigest(value: JsonValue): string {\n\tconst serialized = stableStringify(value);\n\tlet digest = 14695981039346656037n;\n\tfor (const byte of new TextEncoder().encode(serialized)) {\n\t\tdigest ^= BigInt(byte);\n\t\tdigest = BigInt.asUintN(64, digest * 1099511628211n);\n\t}\n\treturn digest.toString(16).padStart(16, \"0\");\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\nfunction assertPluginEntries(\n\tvalue: unknown,\n\tlabel: string,\n): { pluginId: string; pluginVersion: string; selectedAdapter?: { id: string; version: string } }[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst seen = new Set<string>();\n\tconst entries = value.map((item, index) => {\n\t\tif (item === null || typeof item !== \"object\" || Array.isArray(item)) {\n\t\t\tthrow new Error(`${label}[${index}] must be a plain object`);\n\t\t}\n\t\tconst record = item as Record<string, unknown>;\n\t\tassertNonEmptyString(record.pluginId, `${label}[${index}].pluginId`);\n\t\tassertNonEmptyString(record.pluginVersion, `${label}[${index}].pluginVersion`);\n\t\tconst key = `${record.pluginId}@${record.pluginVersion}`;\n\t\tif (seen.has(key)) throw new Error(`${label} contains duplicate entry: ${key}`);\n\t\tseen.add(key);\n\t\tconst selectedAdapter = record.selectedAdapter;\n\t\tif (selectedAdapter === undefined) return { pluginId: record.pluginId, pluginVersion: record.pluginVersion };\n\t\tif (selectedAdapter === null || typeof selectedAdapter !== \"object\" || Array.isArray(selectedAdapter)) {\n\t\t\tthrow new Error(`${label}[${index}].selectedAdapter must be an object`);\n\t\t}\n\t\tconst adapter = selectedAdapter as Record<string, unknown>;\n\t\tassertNonEmptyString(adapter.id, `${label}[${index}].selectedAdapter.id`);\n\t\tassertNonEmptyString(adapter.version, `${label}[${index}].selectedAdapter.version`);\n\t\treturn {\n\t\t\tpluginId: record.pluginId,\n\t\t\tpluginVersion: record.pluginVersion,\n\t\t\tselectedAdapter: { id: adapter.id, version: adapter.version },\n\t\t};\n\t});\n\treturn entries.sort((left, right) =>\n\t\tleft.pluginId === right.pluginId\n\t\t\t? comparePluginVersionsV1(left.pluginVersion, right.pluginVersion)\n\t\t\t: left.pluginId.localeCompare(right.pluginId),\n\t);\n}\n\n/**\n * Builds the Profile identity from the resolved inputs. The digest is\n * order-independent (sorted before serialization) so the same resolved set\n * always yields the same identity regardless of discovery order.\n */\nexport function buildProfileIdentityV1(input: {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** Resolved plugin manifests participating in the Profile. */\n\treadonly resolvedPlugins: readonly ResolvedPluginManifestV1[];\n\t/** Resolved capability references (id + exact version). */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}): ProfileIdentityV1 {\n\tassertNonEmptyString(input.profileId, \"Profile identity.profileId\");\n\tassertNonEmptyString(input.profileVersion, \"Profile identity.profileVersion\");\n\tconst pluginEntries = assertPluginEntries(input.resolvedPlugins, \"Profile identity.resolvedPlugins\");\n\n\tconst capabilityVersions = [...input.capabilityVersions]\n\t\t.map((ref) => {\n\t\t\tif (typeof ref.version !== \"number\" || !Number.isSafeInteger(ref.version) || ref.version < 1) {\n\t\t\t\tthrow new Error(`Profile identity.capabilityVersions version must be a positive safe integer: ${ref.id}`);\n\t\t\t}\n\t\t\tassertNonEmptyString(ref.id, \"Profile identity.capabilityVersions.id\");\n\t\t\treturn { id: ref.id, version: ref.version };\n\t\t})\n\t\t.sort((left, right) => (left.id === right.id ? left.version - right.version : left.id.localeCompare(right.id)));\n\n\tconst manifestDigest = stableJsonDigest({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tplugins: pluginEntries.map((entry) => ({\n\t\t\tpluginId: entry.pluginId,\n\t\t\tpluginVersion: entry.pluginVersion,\n\t\t\t...(entry.selectedAdapter === undefined ? {} : { selectedAdapter: entry.selectedAdapter }),\n\t\t})),\n\t\tcapabilityVersions: capabilityVersions.map((ref) => ({ id: ref.id, version: ref.version })),\n\t});\n\n\treturn Object.freeze({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tmanifestDigest,\n\t\tcapabilityVersions: Object.freeze(capabilityVersions.map((ref) => Object.freeze({ ...ref }))),\n\t});\n}\n\n/** Host compatibility check: identities match when the digests are equal. */\nexport function profileIdentitiesCompatible(left: ProfileIdentityV1, right: ProfileIdentityV1): boolean {\n\treturn left.manifestDigest === right.manifestDigest;\n}\n"]}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/profile/identity.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Profile identity — minimal Profile identity, resolved manifest digest, and\n * host compatibility facts (2.0a).\n *\n * The identity is fully determined by the resolved inputs: profileId +\n * profileVersion name it; the manifest digest is an FNV-1a digest over the\n * key-order-stable serialization of the sorted resolved plugin manifests; the\n * resolved capability versions are carried as a sorted, JSON-serializable\n * list. The identity is the input for the session-start Profile lock (2.0b).\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { t } from \"../core/i18n/index.ts\";\nimport { comparePluginVersionsV1 } from \"../plugins/ecosystem/catalog.ts\";\n\n/** One resolved plugin manifest entry of a Profile. */\nexport interface ResolvedPluginManifestV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly manifestPath?: string;\n\treadonly capabilityRefs: readonly { readonly id: string; readonly version: number }[];\n\treadonly selectedAdapter?: { readonly id: string; readonly version: string };\n}\n\n/** The serializable, digest-carrying Profile identity. */\nexport interface ProfileIdentityV1 {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** FNV-1a digest over the stable serialization of the resolved manifests. */\n\treadonly manifestDigest: string;\n\t/** Sorted (id, version) capability references of the resolved set. */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}\n\nfunction stableStringify(value: JsonValue): string {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n\tconst entries = Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record<string, JsonValue>)[key])}`);\n\treturn `{${entries.join(\",\")}}`;\n}\n\n/** FNV-1a 64-bit digest over the key-order-stable JSON serialization. */\nexport function stableJsonDigest(value: JsonValue): string {\n\tconst serialized = stableStringify(value);\n\tlet digest = 14695981039346656037n;\n\tfor (const byte of new TextEncoder().encode(serialized)) {\n\t\tdigest ^= BigInt(byte);\n\t\tdigest = BigInt.asUintN(64, digest * 1099511628211n);\n\t}\n\treturn digest.toString(16).padStart(16, \"0\");\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0)\n\t\tthrow new Error(t(\"{label} must be a non-empty string\", { label }));\n}\n\nfunction assertPluginEntries(\n\tvalue: unknown,\n\tlabel: string,\n): { pluginId: string; pluginVersion: string; selectedAdapter?: { id: string; version: string } }[] {\n\tif (!Array.isArray(value)) throw new Error(t(\"{label} must be an array\", { label }));\n\tconst seen = new Set<string>();\n\tconst entries = value.map((item, index) => {\n\t\tif (item === null || typeof item !== \"object\" || Array.isArray(item)) {\n\t\t\tthrow new Error(t(\"{label}[{index}] must be a plain object\", { label, index }));\n\t\t}\n\t\tconst record = item as Record<string, unknown>;\n\t\tassertNonEmptyString(record.pluginId, `${label}[${index}].pluginId`);\n\t\tassertNonEmptyString(record.pluginVersion, `${label}[${index}].pluginVersion`);\n\t\tconst key = `${record.pluginId}@${record.pluginVersion}`;\n\t\tif (seen.has(key)) throw new Error(t(\"{label} contains duplicate entry: {key}\", { label, key }));\n\t\tseen.add(key);\n\t\tconst selectedAdapter = record.selectedAdapter;\n\t\tif (selectedAdapter === undefined) return { pluginId: record.pluginId, pluginVersion: record.pluginVersion };\n\t\tif (selectedAdapter === null || typeof selectedAdapter !== \"object\" || Array.isArray(selectedAdapter)) {\n\t\t\tthrow new Error(t(\"{label}[{index}].selectedAdapter must be an object\", { label, index }));\n\t\t}\n\t\tconst adapter = selectedAdapter as Record<string, unknown>;\n\t\tassertNonEmptyString(adapter.id, `${label}[${index}].selectedAdapter.id`);\n\t\tassertNonEmptyString(adapter.version, `${label}[${index}].selectedAdapter.version`);\n\t\treturn {\n\t\t\tpluginId: record.pluginId,\n\t\t\tpluginVersion: record.pluginVersion,\n\t\t\tselectedAdapter: { id: adapter.id, version: adapter.version },\n\t\t};\n\t});\n\treturn entries.sort((left, right) =>\n\t\tleft.pluginId === right.pluginId\n\t\t\t? comparePluginVersionsV1(left.pluginVersion, right.pluginVersion)\n\t\t\t: left.pluginId.localeCompare(right.pluginId),\n\t);\n}\n\n/**\n * Builds the Profile identity from the resolved inputs. The digest is\n * order-independent (sorted before serialization) so the same resolved set\n * always yields the same identity regardless of discovery order.\n */\nexport function buildProfileIdentityV1(input: {\n\treadonly profileId: string;\n\treadonly profileVersion: string;\n\t/** Resolved plugin manifests participating in the Profile. */\n\treadonly resolvedPlugins: readonly ResolvedPluginManifestV1[];\n\t/** Resolved capability references (id + exact version). */\n\treadonly capabilityVersions: readonly { readonly id: string; readonly version: number }[];\n}): ProfileIdentityV1 {\n\tassertNonEmptyString(input.profileId, \"Profile identity.profileId\");\n\tassertNonEmptyString(input.profileVersion, \"Profile identity.profileVersion\");\n\tconst pluginEntries = assertPluginEntries(input.resolvedPlugins, \"Profile identity.resolvedPlugins\");\n\n\tconst capabilityVersions = [...input.capabilityVersions]\n\t\t.map((ref) => {\n\t\t\tif (typeof ref.version !== \"number\" || !Number.isSafeInteger(ref.version) || ref.version < 1) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Profile identity.capabilityVersions version must be a positive safe integer: {id}\", { id: ref.id }),\n\t\t\t\t);\n\t\t\t}\n\t\t\tassertNonEmptyString(ref.id, \"Profile identity.capabilityVersions.id\");\n\t\t\treturn { id: ref.id, version: ref.version };\n\t\t})\n\t\t.sort((left, right) => (left.id === right.id ? left.version - right.version : left.id.localeCompare(right.id)));\n\n\tconst manifestDigest = stableJsonDigest({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tplugins: pluginEntries.map((entry) => ({\n\t\t\tpluginId: entry.pluginId,\n\t\t\tpluginVersion: entry.pluginVersion,\n\t\t\t...(entry.selectedAdapter === undefined ? {} : { selectedAdapter: entry.selectedAdapter }),\n\t\t})),\n\t\tcapabilityVersions: capabilityVersions.map((ref) => ({ id: ref.id, version: ref.version })),\n\t});\n\n\treturn Object.freeze({\n\t\tprofileId: input.profileId,\n\t\tprofileVersion: input.profileVersion,\n\t\tmanifestDigest,\n\t\tcapabilityVersions: Object.freeze(capabilityVersions.map((ref) => Object.freeze({ ...ref }))),\n\t});\n}\n\n/** Host compatibility check: identities match when the digests are equal. */\nexport function profileIdentitiesCompatible(left: ProfileIdentityV1, right: ProfileIdentityV1): boolean {\n\treturn left.manifestDigest === right.manifestDigest;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/profile/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC;CAC9B;AAED,MAAM,MAAM,yBAAyB,GAClC;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,GAC7B;IACA,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,yBAAyB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtD,QAAQ,CAAC,uBAAuB,EAAE,SAAS,MAAM,EAAE,CAAC;CACnD,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACjC,CAAC;AAEL,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC7B;AAMD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAChC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GACvE,aAAa,CAQf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,GAAG,yBAAyB,CAsB5G;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAChD,OAAO,GAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAC3C,0BAA0B,CAW5B","sourcesContent":["/**\n * Profile lock — pre-creation lock and reopen/reload drift detection (2.0b).\n *\n * A Profile lock freezes the resolved `ProfileIdentityV1` at session creation.\n * Reopen/reload verifies the current resolved identity against the lock:\n * - same manifest digest + profile version → `locked`;\n * - same version but a different resolved set → `drift` with the differing\n * capability/plugin facts;\n * - different profile version → `migration_required` (explicit, never an\n * automatic replacement).\n * Replacing a lock requires explicit authorization and is observable — the\n * locked list can never be silently swapped.\n */\nimport type { ProfileIdentityV1 } from \"./identity.ts\";\nimport { profileIdentitiesCompatible } from \"./identity.ts\";\n\nexport interface ProfileLockV1 {\n\treadonly identity: ProfileIdentityV1;\n\treadonly lockedAt: number;\n\treadonly lockedBy: string;\n\t/** Generation increments on each authorized replacement. */\n\treadonly generation: number;\n\treadonly supersededBy?: never;\n}\n\nexport type ProfileLockVerificationV1 =\n\t| { readonly status: \"locked\" }\n\t| {\n\t\t\treadonly status: \"drift\";\n\t\t\treadonly reason: string;\n\t\t\treadonly missingCapabilityVersions: readonly string[];\n\t\t\treadonly addedCapabilityVersions: readonly string[];\n\t }\n\t| {\n\t\t\treadonly status: \"migration_required\";\n\t\t\treadonly reason: string;\n\t\t\treadonly fromProfileVersion: string;\n\t\t\treadonly toProfileVersion: string;\n\t };\n\nexport interface ProfileLockReplaceResultV1 {\n\treadonly previousGeneration: number;\n\treadonly lock: ProfileLockV1;\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\n/** Creates an immutable Profile lock over a resolved identity (2.0b). */\nexport function createProfileLock(\n\tidentity: ProfileIdentityV1,\n\toptions: { readonly lockedBy?: string; readonly now?: () => number } = {},\n): ProfileLockV1 {\n\tassertNonEmptyString(identity.profileId, \"Profile lock.profileId\");\n\treturn Object.freeze({\n\t\tidentity,\n\t\tlockedAt: options.now?.() ?? 0,\n\t\tlockedBy: options.lockedBy ?? \"host\",\n\t\tgeneration: 1,\n\t});\n}\n\n/**\n * Verifies a current resolved identity against the lock during reopen/reload.\n * The lock itself is never mutated; drift and migration are explicit results.\n */\nexport function verifyProfileLock(lock: ProfileLockV1, current: ProfileIdentityV1): ProfileLockVerificationV1 {\n\tif (current.profileVersion !== lock.identity.profileVersion) {\n\t\treturn Object.freeze({\n\t\t\tstatus: \"migration_required\",\n\t\t\treason: `Profile version changed from ${lock.identity.profileVersion} to ${current.profileVersion}; explicit migration is required`,\n\t\t\tfromProfileVersion: lock.identity.profileVersion,\n\t\t\ttoProfileVersion: current.profileVersion,\n\t\t});\n\t}\n\tif (!profileIdentitiesCompatible(lock.identity, current)) {\n\t\tconst lockedIds = new Set(lock.identity.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst currentIds = new Set(current.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst missing = [...lockedIds].filter((id) => !currentIds.has(id));\n\t\tconst added = [...currentIds].filter((id) => !lockedIds.has(id));\n\t\treturn Object.freeze({\n\t\t\tstatus: \"drift\",\n\t\t\treason: \"Resolved manifest digest differs from the locked profile identity\",\n\t\t\tmissingCapabilityVersions: Object.freeze(missing),\n\t\t\taddedCapabilityVersions: Object.freeze(added),\n\t\t});\n\t}\n\treturn { status: \"locked\" };\n}\n\n/**\n * Replaces a lock with explicit authorization. This is the ONLY way to change\n * a lock — silent replacement does not exist.\n */\nexport function replaceProfileLock(\n\tlock: ProfileLockV1,\n\tidentity: ProfileIdentityV1,\n\tauthorization: { readonly authorizedBy: string },\n\toptions: { readonly now?: () => number } = {},\n): ProfileLockReplaceResultV1 {\n\tassertNonEmptyString(authorization.authorizedBy, \"authorizedBy\");\n\treturn {\n\t\tpreviousGeneration: lock.generation,\n\t\tlock: Object.freeze({\n\t\t\tidentity,\n\t\t\tlockedAt: options.now?.() ?? 0,\n\t\t\tlockedBy: authorization.authorizedBy,\n\t\t\tgeneration: lock.generation + 1,\n\t\t}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/profile/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC;CAC9B;AAED,MAAM,MAAM,yBAAyB,GAClC;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,GAC7B;IACA,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,yBAAyB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtD,QAAQ,CAAC,uBAAuB,EAAE,SAAS,MAAM,EAAE,CAAC;CACnD,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACjC,CAAC;AAEL,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC7B;AAOD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAChC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GACvE,aAAa,CAQf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,GAAG,yBAAyB,CAsB5G;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,iBAAiB,EAC3B,aAAa,EAAE;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAChD,OAAO,GAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAC3C,0BAA0B,CAW5B","sourcesContent":["/**\n * Profile lock — pre-creation lock and reopen/reload drift detection (2.0b).\n *\n * A Profile lock freezes the resolved `ProfileIdentityV1` at session creation.\n * Reopen/reload verifies the current resolved identity against the lock:\n * - same manifest digest + profile version → `locked`;\n * - same version but a different resolved set → `drift` with the differing\n * capability/plugin facts;\n * - different profile version → `migration_required` (explicit, never an\n * automatic replacement).\n * Replacing a lock requires explicit authorization and is observable — the\n * locked list can never be silently swapped.\n */\n\nimport { t } from \"../core/i18n/index.ts\";\nimport type { ProfileIdentityV1 } from \"./identity.ts\";\nimport { profileIdentitiesCompatible } from \"./identity.ts\";\n\nexport interface ProfileLockV1 {\n\treadonly identity: ProfileIdentityV1;\n\treadonly lockedAt: number;\n\treadonly lockedBy: string;\n\t/** Generation increments on each authorized replacement. */\n\treadonly generation: number;\n\treadonly supersededBy?: never;\n}\n\nexport type ProfileLockVerificationV1 =\n\t| { readonly status: \"locked\" }\n\t| {\n\t\t\treadonly status: \"drift\";\n\t\t\treadonly reason: string;\n\t\t\treadonly missingCapabilityVersions: readonly string[];\n\t\t\treadonly addedCapabilityVersions: readonly string[];\n\t }\n\t| {\n\t\t\treadonly status: \"migration_required\";\n\t\t\treadonly reason: string;\n\t\t\treadonly fromProfileVersion: string;\n\t\t\treadonly toProfileVersion: string;\n\t };\n\nexport interface ProfileLockReplaceResultV1 {\n\treadonly previousGeneration: number;\n\treadonly lock: ProfileLockV1;\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0)\n\t\tthrow new Error(t(\"{label} must be a non-empty string\", { label }));\n}\n\n/** Creates an immutable Profile lock over a resolved identity (2.0b). */\nexport function createProfileLock(\n\tidentity: ProfileIdentityV1,\n\toptions: { readonly lockedBy?: string; readonly now?: () => number } = {},\n): ProfileLockV1 {\n\tassertNonEmptyString(identity.profileId, \"Profile lock.profileId\");\n\treturn Object.freeze({\n\t\tidentity,\n\t\tlockedAt: options.now?.() ?? 0,\n\t\tlockedBy: options.lockedBy ?? \"host\",\n\t\tgeneration: 1,\n\t});\n}\n\n/**\n * Verifies a current resolved identity against the lock during reopen/reload.\n * The lock itself is never mutated; drift and migration are explicit results.\n */\nexport function verifyProfileLock(lock: ProfileLockV1, current: ProfileIdentityV1): ProfileLockVerificationV1 {\n\tif (current.profileVersion !== lock.identity.profileVersion) {\n\t\treturn Object.freeze({\n\t\t\tstatus: \"migration_required\",\n\t\t\treason: `Profile version changed from ${lock.identity.profileVersion} to ${current.profileVersion}; explicit migration is required`,\n\t\t\tfromProfileVersion: lock.identity.profileVersion,\n\t\t\ttoProfileVersion: current.profileVersion,\n\t\t});\n\t}\n\tif (!profileIdentitiesCompatible(lock.identity, current)) {\n\t\tconst lockedIds = new Set(lock.identity.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst currentIds = new Set(current.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst missing = [...lockedIds].filter((id) => !currentIds.has(id));\n\t\tconst added = [...currentIds].filter((id) => !lockedIds.has(id));\n\t\treturn Object.freeze({\n\t\t\tstatus: \"drift\",\n\t\t\treason: \"Resolved manifest digest differs from the locked profile identity\",\n\t\t\tmissingCapabilityVersions: Object.freeze(missing),\n\t\t\taddedCapabilityVersions: Object.freeze(added),\n\t\t});\n\t}\n\treturn { status: \"locked\" };\n}\n\n/**\n * Replaces a lock with explicit authorization. This is the ONLY way to change\n * a lock — silent replacement does not exist.\n */\nexport function replaceProfileLock(\n\tlock: ProfileLockV1,\n\tidentity: ProfileIdentityV1,\n\tauthorization: { readonly authorizedBy: string },\n\toptions: { readonly now?: () => number } = {},\n): ProfileLockReplaceResultV1 {\n\tassertNonEmptyString(authorization.authorizedBy, \"authorizedBy\");\n\treturn {\n\t\tpreviousGeneration: lock.generation,\n\t\tlock: Object.freeze({\n\t\t\tidentity,\n\t\t\tlockedAt: options.now?.() ?? 0,\n\t\t\tlockedBy: authorization.authorizedBy,\n\t\t\tgeneration: lock.generation + 1,\n\t\t}),\n\t};\n}\n"]}
@@ -1,7 +1,21 @@
1
+ /**
2
+ * Profile lock — pre-creation lock and reopen/reload drift detection (2.0b).
3
+ *
4
+ * A Profile lock freezes the resolved `ProfileIdentityV1` at session creation.
5
+ * Reopen/reload verifies the current resolved identity against the lock:
6
+ * - same manifest digest + profile version → `locked`;
7
+ * - same version but a different resolved set → `drift` with the differing
8
+ * capability/plugin facts;
9
+ * - different profile version → `migration_required` (explicit, never an
10
+ * automatic replacement).
11
+ * Replacing a lock requires explicit authorization and is observable — the
12
+ * locked list can never be silently swapped.
13
+ */
14
+ import { t } from "../core/i18n/index.js";
1
15
  import { profileIdentitiesCompatible } from "./identity.js";
2
16
  function assertNonEmptyString(value, label) {
3
17
  if (typeof value !== "string" || value.trim().length === 0)
4
- throw new Error(`${label} must be a non-empty string`);
18
+ throw new Error(t("{label} must be a non-empty string", { label }));
5
19
  }
6
20
  /** Creates an immutable Profile lock over a resolved identity (2.0b). */
7
21
  export function createProfileLock(identity, options = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/profile/lock.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Profile lock — pre-creation lock and reopen/reload drift detection (2.0b).\n *\n * A Profile lock freezes the resolved `ProfileIdentityV1` at session creation.\n * Reopen/reload verifies the current resolved identity against the lock:\n * - same manifest digest + profile version → `locked`;\n * - same version but a different resolved set → `drift` with the differing\n * capability/plugin facts;\n * - different profile version → `migration_required` (explicit, never an\n * automatic replacement).\n * Replacing a lock requires explicit authorization and is observable — the\n * locked list can never be silently swapped.\n */\nimport type { ProfileIdentityV1 } from \"./identity.ts\";\nimport { profileIdentitiesCompatible } from \"./identity.ts\";\n\nexport interface ProfileLockV1 {\n\treadonly identity: ProfileIdentityV1;\n\treadonly lockedAt: number;\n\treadonly lockedBy: string;\n\t/** Generation increments on each authorized replacement. */\n\treadonly generation: number;\n\treadonly supersededBy?: never;\n}\n\nexport type ProfileLockVerificationV1 =\n\t| { readonly status: \"locked\" }\n\t| {\n\t\t\treadonly status: \"drift\";\n\t\t\treadonly reason: string;\n\t\t\treadonly missingCapabilityVersions: readonly string[];\n\t\t\treadonly addedCapabilityVersions: readonly string[];\n\t }\n\t| {\n\t\t\treadonly status: \"migration_required\";\n\t\t\treadonly reason: string;\n\t\t\treadonly fromProfileVersion: string;\n\t\t\treadonly toProfileVersion: string;\n\t };\n\nexport interface ProfileLockReplaceResultV1 {\n\treadonly previousGeneration: number;\n\treadonly lock: ProfileLockV1;\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\n/** Creates an immutable Profile lock over a resolved identity (2.0b). */\nexport function createProfileLock(\n\tidentity: ProfileIdentityV1,\n\toptions: { readonly lockedBy?: string; readonly now?: () => number } = {},\n): ProfileLockV1 {\n\tassertNonEmptyString(identity.profileId, \"Profile lock.profileId\");\n\treturn Object.freeze({\n\t\tidentity,\n\t\tlockedAt: options.now?.() ?? 0,\n\t\tlockedBy: options.lockedBy ?? \"host\",\n\t\tgeneration: 1,\n\t});\n}\n\n/**\n * Verifies a current resolved identity against the lock during reopen/reload.\n * The lock itself is never mutated; drift and migration are explicit results.\n */\nexport function verifyProfileLock(lock: ProfileLockV1, current: ProfileIdentityV1): ProfileLockVerificationV1 {\n\tif (current.profileVersion !== lock.identity.profileVersion) {\n\t\treturn Object.freeze({\n\t\t\tstatus: \"migration_required\",\n\t\t\treason: `Profile version changed from ${lock.identity.profileVersion} to ${current.profileVersion}; explicit migration is required`,\n\t\t\tfromProfileVersion: lock.identity.profileVersion,\n\t\t\ttoProfileVersion: current.profileVersion,\n\t\t});\n\t}\n\tif (!profileIdentitiesCompatible(lock.identity, current)) {\n\t\tconst lockedIds = new Set(lock.identity.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst currentIds = new Set(current.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst missing = [...lockedIds].filter((id) => !currentIds.has(id));\n\t\tconst added = [...currentIds].filter((id) => !lockedIds.has(id));\n\t\treturn Object.freeze({\n\t\t\tstatus: \"drift\",\n\t\t\treason: \"Resolved manifest digest differs from the locked profile identity\",\n\t\t\tmissingCapabilityVersions: Object.freeze(missing),\n\t\t\taddedCapabilityVersions: Object.freeze(added),\n\t\t});\n\t}\n\treturn { status: \"locked\" };\n}\n\n/**\n * Replaces a lock with explicit authorization. This is the ONLY way to change\n * a lock — silent replacement does not exist.\n */\nexport function replaceProfileLock(\n\tlock: ProfileLockV1,\n\tidentity: ProfileIdentityV1,\n\tauthorization: { readonly authorizedBy: string },\n\toptions: { readonly now?: () => number } = {},\n): ProfileLockReplaceResultV1 {\n\tassertNonEmptyString(authorization.authorizedBy, \"authorizedBy\");\n\treturn {\n\t\tpreviousGeneration: lock.generation,\n\t\tlock: Object.freeze({\n\t\t\tidentity,\n\t\t\tlockedAt: options.now?.() ?? 0,\n\t\t\tlockedBy: authorization.authorizedBy,\n\t\t\tgeneration: lock.generation + 1,\n\t\t}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/profile/lock.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Profile lock — pre-creation lock and reopen/reload drift detection (2.0b).\n *\n * A Profile lock freezes the resolved `ProfileIdentityV1` at session creation.\n * Reopen/reload verifies the current resolved identity against the lock:\n * - same manifest digest + profile version → `locked`;\n * - same version but a different resolved set → `drift` with the differing\n * capability/plugin facts;\n * - different profile version → `migration_required` (explicit, never an\n * automatic replacement).\n * Replacing a lock requires explicit authorization and is observable — the\n * locked list can never be silently swapped.\n */\n\nimport { t } from \"../core/i18n/index.ts\";\nimport type { ProfileIdentityV1 } from \"./identity.ts\";\nimport { profileIdentitiesCompatible } from \"./identity.ts\";\n\nexport interface ProfileLockV1 {\n\treadonly identity: ProfileIdentityV1;\n\treadonly lockedAt: number;\n\treadonly lockedBy: string;\n\t/** Generation increments on each authorized replacement. */\n\treadonly generation: number;\n\treadonly supersededBy?: never;\n}\n\nexport type ProfileLockVerificationV1 =\n\t| { readonly status: \"locked\" }\n\t| {\n\t\t\treadonly status: \"drift\";\n\t\t\treadonly reason: string;\n\t\t\treadonly missingCapabilityVersions: readonly string[];\n\t\t\treadonly addedCapabilityVersions: readonly string[];\n\t }\n\t| {\n\t\t\treadonly status: \"migration_required\";\n\t\t\treadonly reason: string;\n\t\t\treadonly fromProfileVersion: string;\n\t\t\treadonly toProfileVersion: string;\n\t };\n\nexport interface ProfileLockReplaceResultV1 {\n\treadonly previousGeneration: number;\n\treadonly lock: ProfileLockV1;\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0)\n\t\tthrow new Error(t(\"{label} must be a non-empty string\", { label }));\n}\n\n/** Creates an immutable Profile lock over a resolved identity (2.0b). */\nexport function createProfileLock(\n\tidentity: ProfileIdentityV1,\n\toptions: { readonly lockedBy?: string; readonly now?: () => number } = {},\n): ProfileLockV1 {\n\tassertNonEmptyString(identity.profileId, \"Profile lock.profileId\");\n\treturn Object.freeze({\n\t\tidentity,\n\t\tlockedAt: options.now?.() ?? 0,\n\t\tlockedBy: options.lockedBy ?? \"host\",\n\t\tgeneration: 1,\n\t});\n}\n\n/**\n * Verifies a current resolved identity against the lock during reopen/reload.\n * The lock itself is never mutated; drift and migration are explicit results.\n */\nexport function verifyProfileLock(lock: ProfileLockV1, current: ProfileIdentityV1): ProfileLockVerificationV1 {\n\tif (current.profileVersion !== lock.identity.profileVersion) {\n\t\treturn Object.freeze({\n\t\t\tstatus: \"migration_required\",\n\t\t\treason: `Profile version changed from ${lock.identity.profileVersion} to ${current.profileVersion}; explicit migration is required`,\n\t\t\tfromProfileVersion: lock.identity.profileVersion,\n\t\t\ttoProfileVersion: current.profileVersion,\n\t\t});\n\t}\n\tif (!profileIdentitiesCompatible(lock.identity, current)) {\n\t\tconst lockedIds = new Set(lock.identity.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst currentIds = new Set(current.capabilityVersions.map((ref) => `${ref.id}@${ref.version}`));\n\t\tconst missing = [...lockedIds].filter((id) => !currentIds.has(id));\n\t\tconst added = [...currentIds].filter((id) => !lockedIds.has(id));\n\t\treturn Object.freeze({\n\t\t\tstatus: \"drift\",\n\t\t\treason: \"Resolved manifest digest differs from the locked profile identity\",\n\t\t\tmissingCapabilityVersions: Object.freeze(missing),\n\t\t\taddedCapabilityVersions: Object.freeze(added),\n\t\t});\n\t}\n\treturn { status: \"locked\" };\n}\n\n/**\n * Replaces a lock with explicit authorization. This is the ONLY way to change\n * a lock — silent replacement does not exist.\n */\nexport function replaceProfileLock(\n\tlock: ProfileLockV1,\n\tidentity: ProfileIdentityV1,\n\tauthorization: { readonly authorizedBy: string },\n\toptions: { readonly now?: () => number } = {},\n): ProfileLockReplaceResultV1 {\n\tassertNonEmptyString(authorization.authorizedBy, \"authorizedBy\");\n\treturn {\n\t\tpreviousGeneration: lock.generation,\n\t\tlock: Object.freeze({\n\t\t\tidentity,\n\t\t\tlockedAt: options.now?.() ?? 0,\n\t\t\tlockedBy: authorization.authorizedBy,\n\t\t\tgeneration: lock.generation + 1,\n\t\t}),\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"plan-plugin.d.ts","sourceRoot":"","sources":["../../src/profile/plan-plugin.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1E,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,CAAC,KAAK,EAAE;QACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;KACtC,GAAG,MAAM,CAAC;IACX,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC7F,6EAA6E;IAC7E,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACnH,QAAQ,CACP,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACvE,MAAM,CAAC;IACV,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AAiBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,GAAG,YAAY,CAsJnG","sourcesContent":["/**\n * Plan plugin (2.3c) — Plan semantics bound to a branch/revision over the\n * 2.3a transition store.\n *\n * Status graph: null → draft → active → completed, and draft/active →\n * discarded. A plan is created with its step list and bound to a code branch;\n * while active, `linkRevision` records plan progress against concrete\n * revisions, and `complete` requires evidence plus the final revision. The\n * plugin holds NO shadow state: status, revision, steps, branch, and the\n * latest linked revision are all derived from the transition history, so\n * recovery is exactly a store replay.\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport type { TransitionEvidenceRefV1, TransitionStoreV1 } from \"./state-store.ts\";\n\nexport type PlanStatusV1 = \"draft\" | \"active\" | \"completed\" | \"discarded\";\n\nexport interface PlanStepV1 {\n\treadonly stepId: string;\n\treadonly title: string;\n}\n\nexport interface PlanV1 {\n\treadonly planId: string;\n\treadonly owner: string;\n\treadonly title: string;\n\treadonly branch: string;\n\treadonly steps: readonly PlanStepV1[];\n\treadonly status: PlanStatusV1;\n\t/** The latest revision explicitly linked while active (undefined before the first link). */\n\treadonly revision?: number;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revisionNo: number;\n}\n\nexport interface PlanPluginV1 {\n\tcreatePlan(input: {\n\t\treadonly planId: string;\n\t\treadonly owner: string;\n\t\treadonly title: string;\n\t\treadonly branch: string;\n\t\treadonly steps: readonly PlanStepV1[];\n\t}): PlanV1;\n\tactivate(planId: string, owner: string, options?: { readonly operationId?: string }): PlanV1;\n\t/** Records plan progress against a concrete revision of the bound branch. */\n\tlinkRevision(planId: string, owner: string, revision: number, options?: { readonly operationId?: string }): PlanV1;\n\tcomplete(\n\t\tplanId: string,\n\t\towner: string,\n\t\tevidence: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly atRevision?: number; readonly operationId?: string },\n\t): PlanV1;\n\tdiscard(planId: string, owner: string): PlanV1;\n\tget(planId: string, owner: string): PlanV1 | undefined;\n}\n\ninterface PlanPayloadV1 {\n\treadonly title: string;\n\treadonly branch: string;\n\treadonly steps: readonly PlanStepV1[];\n\treadonly linkedRevision?: number;\n}\n\nfunction planPayloadOf(record: { readonly payload?: unknown }): Partial<PlanPayloadV1> | undefined {\n\tconst payload = record.payload;\n\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\treturn payload as Partial<PlanPayloadV1>;\n\t}\n\treturn undefined;\n}\n\nexport function createPlanPlugin(options: { readonly transitions: TransitionStoreV1 }): PlanPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst planHistory = (planId: string, owner: string) =>\n\t\ttransitions.history(planId).filter((record) => record.domain === \"plan\" && record.owner === owner);\n\n\tconst current = (planId: string, owner: string): { status: PlanStatusV1; revisionNo: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"plan\", planId);\n\t\tif (state === undefined || planHistory(planId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as PlanStatusV1, revisionNo: state.revision };\n\t};\n\n\tconst planGet = (planId: string, owner: string): PlanV1 | undefined => {\n\t\tconst state = current(planId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = planHistory(planId, owner);\n\t\t// v8 ignore next -- 因果校验保证每条 plan 历史都以 fromStatus=null 的创建记录开始\n\t\tconst createPayload = planPayloadOf(history.find((record) => record.fromStatus === null) ?? {});\n\t\tconst lastLinked = [...history]\n\t\t\t.reverse()\n\t\t\t.map((record) => planPayloadOf(record)?.linkedRevision)\n\t\t\t.find((revision): revision is number => typeof revision === \"number\");\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\tplanId,\n\t\t\towner,\n\t\t\ttitle: createPayload?.title ?? planId,\n\t\t\tbranch: createPayload?.branch ?? \"\",\n\t\t\tsteps: createPayload?.steps ?? [],\n\t\t\tstatus: state.status,\n\t\t\t...(lastLinked === undefined ? {} : { revision: lastLinked }),\n\t\t\tevidence: lastEvidence,\n\t\t\trevisionNo: state.revisionNo,\n\t\t};\n\t};\n\n\t/** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */\n\tconst replayedPlan = (\n\t\tplanId: string,\n\t\towner: string,\n\t\ttoStatus: PlanStatusV1,\n\t\toperationId?: string,\n\t): PlanV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = planHistory(planId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? planGet(planId, owner) : undefined;\n\t};\n\n\tconst transitionPlan = (\n\t\tplanId: string,\n\t\towner: string,\n\t\tfromStatus: PlanStatusV1 | null,\n\t\ttoStatus: PlanStatusV1,\n\t\tplanOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: PlanPayloadV1;\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): PlanV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"plan\",\n\t\t\tentityId: planId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: planOptions.operationId ?? `${toStatus}:${planId}`,\n\t\t\tevidence: planOptions.evidence,\n\t\t\tpayload: planOptions.payload as JsonValue | undefined,\n\t\t});\n\t\treturn planGet(planId, owner)!;\n\t};\n\n\treturn {\n\t\tcreatePlan({ planId, owner, title, branch, steps }) {\n\t\t\tif (title.trim().length === 0) throw new Error(\"Plan title must be a non-empty string\");\n\t\t\tif (branch.trim().length === 0) throw new Error(\"Plan branch must be a non-empty string\");\n\t\t\tif (steps.length === 0) throw new Error(\"Plan requires at least one step\");\n\t\t\tconst stepIds = new Set<string>();\n\t\t\tfor (const step of steps) {\n\t\t\t\tif (typeof step.stepId !== \"string\" || step.stepId.trim().length === 0) {\n\t\t\t\t\tthrow new Error(\"Plan stepId must be a non-empty string\");\n\t\t\t\t}\n\t\t\t\tif (typeof step.title !== \"string\" || step.title.trim().length === 0) {\n\t\t\t\t\tthrow new Error(\"Plan step title must be a non-empty string\");\n\t\t\t\t}\n\t\t\t\tif (stepIds.has(step.stepId)) throw new Error(`Duplicate plan stepId: ${step.stepId}`);\n\t\t\t\tstepIds.add(step.stepId);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, null, \"draft\", {\n\t\t\t\tpayload: { title, branch, steps: [...steps] },\n\t\t\t\toperationId: `create:${planId}`,\n\t\t\t});\n\t\t},\n\t\tactivate(planId, owner, activateOptions) {\n\t\t\tconst replayed = replayedPlan(planId, owner, \"active\", activateOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (plan?.status !== \"draft\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Plan ${planId} must be in draft status to activate (current: ${plan?.status ?? \"missing\"})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, \"draft\", \"active\", { operationId: activateOptions?.operationId });\n\t\t},\n\t\tlinkRevision(planId, owner, revision, linkOptions) {\n\t\t\tconst replayed = replayedPlan(planId, owner, \"active\", linkOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (plan?.status !== \"active\") {\n\t\t\t\tthrow new Error(`Plan ${planId} must be active to link a revision (current: ${plan?.status ?? \"missing\"})`);\n\t\t\t}\n\t\t\tif (!Number.isSafeInteger(revision) || revision < 1) {\n\t\t\t\tthrow new Error(`Plan revision must be a positive safe integer, got ${revision}`);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, \"active\", \"active\", {\n\t\t\t\tpayload: { title: plan.title, branch: plan.branch, steps: plan.steps, linkedRevision: revision },\n\t\t\t\toperationId: linkOptions?.operationId ?? `link:${planId}:${revision}`,\n\t\t\t});\n\t\t},\n\t\tcomplete(planId, owner, evidence, completeOptions) {\n\t\t\tconst replayed = replayedPlan(planId, owner, \"completed\", completeOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (plan?.status !== \"active\") {\n\t\t\t\tthrow new Error(`Plan ${planId} must be active to complete (current: ${plan?.status ?? \"missing\"})`);\n\t\t\t}\n\t\t\tif (evidence.length === 0) throw new Error(\"Completing a plan requires at least one evidence reference\");\n\t\t\tconst target = completeOptions?.atRevision ?? plan.revision;\n\t\t\tif (typeof target !== \"number\") {\n\t\t\t\tthrow new Error(`Plan ${planId} requires atRevision or a linked revision to complete`);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, \"active\", \"completed\", {\n\t\t\t\tevidence,\n\t\t\t\tpayload: { title: plan.title, branch: plan.branch, steps: plan.steps, linkedRevision: target },\n\t\t\t\toperationId: completeOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tdiscard(planId, owner) {\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (!plan || plan.status === \"completed\" || plan.status === \"discarded\") {\n\t\t\t\tthrow new Error(`Plan ${planId} cannot be discarded from status ${plan?.status ?? \"missing\"}`);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, plan.status, \"discarded\", {});\n\t\t},\n\t\tget(planId, owner) {\n\t\t\treturn planGet(planId, owner);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"plan-plugin.d.ts","sourceRoot":"","sources":["../../src/profile/plan-plugin.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1E,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,CAAC,KAAK,EAAE;QACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;KACtC,GAAG,MAAM,CAAC;IACX,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC7F,6EAA6E;IAC7E,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACnH,QAAQ,CACP,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACvE,MAAM,CAAC;IACV,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AAiBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,GAAG,YAAY,CAyKnG","sourcesContent":["/**\n * Plan plugin (2.3c) — Plan semantics bound to a branch/revision over the\n * 2.3a transition store.\n *\n * Status graph: null → draft → active → completed, and draft/active →\n * discarded. A plan is created with its step list and bound to a code branch;\n * while active, `linkRevision` records plan progress against concrete\n * revisions, and `complete` requires evidence plus the final revision. The\n * plugin holds NO shadow state: status, revision, steps, branch, and the\n * latest linked revision are all derived from the transition history, so\n * recovery is exactly a store replay.\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { t } from \"../core/i18n/index.ts\";\nimport type { TransitionEvidenceRefV1, TransitionStoreV1 } from \"./state-store.ts\";\n\nexport type PlanStatusV1 = \"draft\" | \"active\" | \"completed\" | \"discarded\";\n\nexport interface PlanStepV1 {\n\treadonly stepId: string;\n\treadonly title: string;\n}\n\nexport interface PlanV1 {\n\treadonly planId: string;\n\treadonly owner: string;\n\treadonly title: string;\n\treadonly branch: string;\n\treadonly steps: readonly PlanStepV1[];\n\treadonly status: PlanStatusV1;\n\t/** The latest revision explicitly linked while active (undefined before the first link). */\n\treadonly revision?: number;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revisionNo: number;\n}\n\nexport interface PlanPluginV1 {\n\tcreatePlan(input: {\n\t\treadonly planId: string;\n\t\treadonly owner: string;\n\t\treadonly title: string;\n\t\treadonly branch: string;\n\t\treadonly steps: readonly PlanStepV1[];\n\t}): PlanV1;\n\tactivate(planId: string, owner: string, options?: { readonly operationId?: string }): PlanV1;\n\t/** Records plan progress against a concrete revision of the bound branch. */\n\tlinkRevision(planId: string, owner: string, revision: number, options?: { readonly operationId?: string }): PlanV1;\n\tcomplete(\n\t\tplanId: string,\n\t\towner: string,\n\t\tevidence: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly atRevision?: number; readonly operationId?: string },\n\t): PlanV1;\n\tdiscard(planId: string, owner: string): PlanV1;\n\tget(planId: string, owner: string): PlanV1 | undefined;\n}\n\ninterface PlanPayloadV1 {\n\treadonly title: string;\n\treadonly branch: string;\n\treadonly steps: readonly PlanStepV1[];\n\treadonly linkedRevision?: number;\n}\n\nfunction planPayloadOf(record: { readonly payload?: unknown }): Partial<PlanPayloadV1> | undefined {\n\tconst payload = record.payload;\n\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\treturn payload as Partial<PlanPayloadV1>;\n\t}\n\treturn undefined;\n}\n\nexport function createPlanPlugin(options: { readonly transitions: TransitionStoreV1 }): PlanPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst planHistory = (planId: string, owner: string) =>\n\t\ttransitions.history(planId).filter((record) => record.domain === \"plan\" && record.owner === owner);\n\n\tconst current = (planId: string, owner: string): { status: PlanStatusV1; revisionNo: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"plan\", planId);\n\t\tif (state === undefined || planHistory(planId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as PlanStatusV1, revisionNo: state.revision };\n\t};\n\n\tconst planGet = (planId: string, owner: string): PlanV1 | undefined => {\n\t\tconst state = current(planId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = planHistory(planId, owner);\n\t\t// v8 ignore next -- 因果校验保证每条 plan 历史都以 fromStatus=null 的创建记录开始\n\t\tconst createPayload = planPayloadOf(history.find((record) => record.fromStatus === null) ?? {});\n\t\tconst lastLinked = [...history]\n\t\t\t.reverse()\n\t\t\t.map((record) => planPayloadOf(record)?.linkedRevision)\n\t\t\t.find((revision): revision is number => typeof revision === \"number\");\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\tplanId,\n\t\t\towner,\n\t\t\ttitle: createPayload?.title ?? planId,\n\t\t\tbranch: createPayload?.branch ?? \"\",\n\t\t\tsteps: createPayload?.steps ?? [],\n\t\t\tstatus: state.status,\n\t\t\t...(lastLinked === undefined ? {} : { revision: lastLinked }),\n\t\t\tevidence: lastEvidence,\n\t\t\trevisionNo: state.revisionNo,\n\t\t};\n\t};\n\n\t/** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */\n\tconst replayedPlan = (\n\t\tplanId: string,\n\t\towner: string,\n\t\ttoStatus: PlanStatusV1,\n\t\toperationId?: string,\n\t): PlanV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = planHistory(planId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? planGet(planId, owner) : undefined;\n\t};\n\n\tconst transitionPlan = (\n\t\tplanId: string,\n\t\towner: string,\n\t\tfromStatus: PlanStatusV1 | null,\n\t\ttoStatus: PlanStatusV1,\n\t\tplanOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: PlanPayloadV1;\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): PlanV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"plan\",\n\t\t\tentityId: planId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: planOptions.operationId ?? `${toStatus}:${planId}`,\n\t\t\tevidence: planOptions.evidence,\n\t\t\tpayload: planOptions.payload as JsonValue | undefined,\n\t\t});\n\t\treturn planGet(planId, owner)!;\n\t};\n\n\treturn {\n\t\tcreatePlan({ planId, owner, title, branch, steps }) {\n\t\t\tif (title.trim().length === 0) throw new Error(t(\"Plan title must be a non-empty string\"));\n\t\t\tif (branch.trim().length === 0) throw new Error(t(\"Plan branch must be a non-empty string\"));\n\t\t\tif (steps.length === 0) throw new Error(t(\"Plan requires at least one step\"));\n\t\t\tconst stepIds = new Set<string>();\n\t\t\tfor (const step of steps) {\n\t\t\t\tif (typeof step.stepId !== \"string\" || step.stepId.trim().length === 0) {\n\t\t\t\t\tthrow new Error(t(\"Plan stepId must be a non-empty string\"));\n\t\t\t\t}\n\t\t\t\tif (typeof step.title !== \"string\" || step.title.trim().length === 0) {\n\t\t\t\t\tthrow new Error(t(\"Plan step title must be a non-empty string\"));\n\t\t\t\t}\n\t\t\t\tif (stepIds.has(step.stepId))\n\t\t\t\t\tthrow new Error(t(\"Duplicate plan stepId: {stepId}\", { stepId: step.stepId }));\n\t\t\t\tstepIds.add(step.stepId);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, null, \"draft\", {\n\t\t\t\tpayload: { title, branch, steps: [...steps] },\n\t\t\t\toperationId: `create:${planId}`,\n\t\t\t});\n\t\t},\n\t\tactivate(planId, owner, activateOptions) {\n\t\t\tconst replayed = replayedPlan(planId, owner, \"active\", activateOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (plan?.status !== \"draft\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Plan {planId} must be in draft status to activate (current: {status})\", {\n\t\t\t\t\t\tplanId,\n\t\t\t\t\t\tstatus: plan?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, \"draft\", \"active\", { operationId: activateOptions?.operationId });\n\t\t},\n\t\tlinkRevision(planId, owner, revision, linkOptions) {\n\t\t\tconst replayed = replayedPlan(planId, owner, \"active\", linkOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (plan?.status !== \"active\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Plan {planId} must be active to link a revision (current: {status})\", {\n\t\t\t\t\t\tplanId,\n\t\t\t\t\t\tstatus: plan?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!Number.isSafeInteger(revision) || revision < 1) {\n\t\t\t\tthrow new Error(t(\"Plan revision must be a positive safe integer, got {revision}\", { revision }));\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, \"active\", \"active\", {\n\t\t\t\tpayload: { title: plan.title, branch: plan.branch, steps: plan.steps, linkedRevision: revision },\n\t\t\t\toperationId: linkOptions?.operationId ?? `link:${planId}:${revision}`,\n\t\t\t});\n\t\t},\n\t\tcomplete(planId, owner, evidence, completeOptions) {\n\t\t\tconst replayed = replayedPlan(planId, owner, \"completed\", completeOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (plan?.status !== \"active\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Plan {planId} must be active to complete (current: {status})\", {\n\t\t\t\t\t\tplanId,\n\t\t\t\t\t\tstatus: plan?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (evidence.length === 0) throw new Error(t(\"Completing a plan requires at least one evidence reference\"));\n\t\t\tconst target = completeOptions?.atRevision ?? plan.revision;\n\t\t\tif (typeof target !== \"number\") {\n\t\t\t\tthrow new Error(t(\"Plan {planId} requires atRevision or a linked revision to complete\", { planId }));\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, \"active\", \"completed\", {\n\t\t\t\tevidence,\n\t\t\t\tpayload: { title: plan.title, branch: plan.branch, steps: plan.steps, linkedRevision: target },\n\t\t\t\toperationId: completeOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tdiscard(planId, owner) {\n\t\t\tconst plan = planGet(planId, owner);\n\t\t\tif (!plan || plan.status === \"completed\" || plan.status === \"discarded\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Plan {planId} cannot be discarded from status {status}\", {\n\t\t\t\t\t\tplanId,\n\t\t\t\t\t\tstatus: plan?.status ?? \"missing\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn transitionPlan(planId, owner, plan.status, \"discarded\", {});\n\t\t},\n\t\tget(planId, owner) {\n\t\t\treturn planGet(planId, owner);\n\t\t},\n\t};\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { t } from "../core/i18n/index.js";
1
2
  function planPayloadOf(record) {
2
3
  const payload = record.payload;
3
4
  if (payload !== null && typeof payload === "object" && !Array.isArray(payload)) {
@@ -61,21 +62,21 @@ export function createPlanPlugin(options) {
61
62
  return {
62
63
  createPlan({ planId, owner, title, branch, steps }) {
63
64
  if (title.trim().length === 0)
64
- throw new Error("Plan title must be a non-empty string");
65
+ throw new Error(t("Plan title must be a non-empty string"));
65
66
  if (branch.trim().length === 0)
66
- throw new Error("Plan branch must be a non-empty string");
67
+ throw new Error(t("Plan branch must be a non-empty string"));
67
68
  if (steps.length === 0)
68
- throw new Error("Plan requires at least one step");
69
+ throw new Error(t("Plan requires at least one step"));
69
70
  const stepIds = new Set();
70
71
  for (const step of steps) {
71
72
  if (typeof step.stepId !== "string" || step.stepId.trim().length === 0) {
72
- throw new Error("Plan stepId must be a non-empty string");
73
+ throw new Error(t("Plan stepId must be a non-empty string"));
73
74
  }
74
75
  if (typeof step.title !== "string" || step.title.trim().length === 0) {
75
- throw new Error("Plan step title must be a non-empty string");
76
+ throw new Error(t("Plan step title must be a non-empty string"));
76
77
  }
77
78
  if (stepIds.has(step.stepId))
78
- throw new Error(`Duplicate plan stepId: ${step.stepId}`);
79
+ throw new Error(t("Duplicate plan stepId: {stepId}", { stepId: step.stepId }));
79
80
  stepIds.add(step.stepId);
80
81
  }
81
82
  return transitionPlan(planId, owner, null, "draft", {
@@ -89,7 +90,10 @@ export function createPlanPlugin(options) {
89
90
  return replayed;
90
91
  const plan = planGet(planId, owner);
91
92
  if (plan?.status !== "draft") {
92
- throw new Error(`Plan ${planId} must be in draft status to activate (current: ${plan?.status ?? "missing"})`);
93
+ throw new Error(t("Plan {planId} must be in draft status to activate (current: {status})", {
94
+ planId,
95
+ status: plan?.status ?? "missing",
96
+ }));
93
97
  }
94
98
  return transitionPlan(planId, owner, "draft", "active", { operationId: activateOptions?.operationId });
95
99
  },
@@ -99,10 +103,13 @@ export function createPlanPlugin(options) {
99
103
  return replayed;
100
104
  const plan = planGet(planId, owner);
101
105
  if (plan?.status !== "active") {
102
- throw new Error(`Plan ${planId} must be active to link a revision (current: ${plan?.status ?? "missing"})`);
106
+ throw new Error(t("Plan {planId} must be active to link a revision (current: {status})", {
107
+ planId,
108
+ status: plan?.status ?? "missing",
109
+ }));
103
110
  }
104
111
  if (!Number.isSafeInteger(revision) || revision < 1) {
105
- throw new Error(`Plan revision must be a positive safe integer, got ${revision}`);
112
+ throw new Error(t("Plan revision must be a positive safe integer, got {revision}", { revision }));
106
113
  }
107
114
  return transitionPlan(planId, owner, "active", "active", {
108
115
  payload: { title: plan.title, branch: plan.branch, steps: plan.steps, linkedRevision: revision },
@@ -115,13 +122,16 @@ export function createPlanPlugin(options) {
115
122
  return replayed;
116
123
  const plan = planGet(planId, owner);
117
124
  if (plan?.status !== "active") {
118
- throw new Error(`Plan ${planId} must be active to complete (current: ${plan?.status ?? "missing"})`);
125
+ throw new Error(t("Plan {planId} must be active to complete (current: {status})", {
126
+ planId,
127
+ status: plan?.status ?? "missing",
128
+ }));
119
129
  }
120
130
  if (evidence.length === 0)
121
- throw new Error("Completing a plan requires at least one evidence reference");
131
+ throw new Error(t("Completing a plan requires at least one evidence reference"));
122
132
  const target = completeOptions?.atRevision ?? plan.revision;
123
133
  if (typeof target !== "number") {
124
- throw new Error(`Plan ${planId} requires atRevision or a linked revision to complete`);
134
+ throw new Error(t("Plan {planId} requires atRevision or a linked revision to complete", { planId }));
125
135
  }
126
136
  return transitionPlan(planId, owner, "active", "completed", {
127
137
  evidence,
@@ -132,7 +142,10 @@ export function createPlanPlugin(options) {
132
142
  discard(planId, owner) {
133
143
  const plan = planGet(planId, owner);
134
144
  if (!plan || plan.status === "completed" || plan.status === "discarded") {
135
- throw new Error(`Plan ${planId} cannot be discarded from status ${plan?.status ?? "missing"}`);
145
+ throw new Error(t("Plan {planId} cannot be discarded from status {status}", {
146
+ planId,
147
+ status: plan?.status ?? "missing",
148
+ }));
136
149
  }
137
150
  return transitionPlan(planId, owner, plan.status, "discarded", {});
138
151
  },