@ai-agent-forge/agent-forge 0.88.0 → 0.88.2

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 (445) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/bundled-plugins/agent-loop-policy/agent-forge.json +19 -0
  3. package/bundled-plugins/agent-loop-policy/dist/entry.d.ts +28 -0
  4. package/bundled-plugins/agent-loop-policy/dist/entry.d.ts.map +1 -0
  5. package/bundled-plugins/agent-loop-policy/dist/entry.js +69 -0
  6. package/bundled-plugins/agent-loop-policy/dist/entry.js.map +1 -0
  7. package/bundled-plugins/agent-loop-policy/dist/loop-guard.d.ts +199 -0
  8. package/bundled-plugins/agent-loop-policy/dist/loop-guard.d.ts.map +1 -0
  9. package/bundled-plugins/agent-loop-policy/dist/loop-guard.js +208 -0
  10. package/bundled-plugins/agent-loop-policy/dist/loop-guard.js.map +1 -0
  11. package/bundled-plugins/agent-loop-policy/dist/run-loop-strategy.d.ts +17 -0
  12. package/bundled-plugins/agent-loop-policy/dist/run-loop-strategy.d.ts.map +1 -0
  13. package/bundled-plugins/agent-loop-policy/dist/run-loop-strategy.js +18 -0
  14. package/bundled-plugins/agent-loop-policy/dist/run-loop-strategy.js.map +1 -0
  15. package/bundled-plugins/agent-loop-policy/plugin.json +7 -0
  16. package/bundled-plugins/coding-tools/agent-forge.json +19 -0
  17. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/bash.d.ts +5 -44
  18. package/bundled-plugins/coding-tools/dist/bash.d.ts.map +1 -0
  19. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/bash.js +15 -119
  20. package/bundled-plugins/coding-tools/dist/bash.js.map +1 -0
  21. package/bundled-plugins/coding-tools/dist/capability.d.ts +25 -0
  22. package/bundled-plugins/coding-tools/dist/capability.d.ts.map +1 -0
  23. package/bundled-plugins/coding-tools/dist/capability.js +98 -0
  24. package/bundled-plugins/coding-tools/dist/capability.js.map +1 -0
  25. package/bundled-plugins/coding-tools/dist/config.d.ts +8 -0
  26. package/bundled-plugins/coding-tools/dist/config.d.ts.map +1 -0
  27. package/bundled-plugins/coding-tools/dist/config.js +8 -0
  28. package/bundled-plugins/coding-tools/dist/config.js.map +1 -0
  29. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/edit.d.ts +3 -3
  30. package/bundled-plugins/coding-tools/dist/edit.d.ts.map +1 -0
  31. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/edit.js +12 -10
  32. package/bundled-plugins/coding-tools/dist/edit.js.map +1 -0
  33. package/bundled-plugins/coding-tools/dist/entry.d.ts +14 -0
  34. package/bundled-plugins/coding-tools/dist/entry.d.ts.map +1 -0
  35. package/bundled-plugins/coding-tools/dist/entry.js +23 -0
  36. package/bundled-plugins/coding-tools/dist/entry.js.map +1 -0
  37. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/find.d.ts +4 -4
  38. package/bundled-plugins/coding-tools/dist/find.d.ts.map +1 -0
  39. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/find.js +10 -8
  40. package/bundled-plugins/coding-tools/dist/find.js.map +1 -0
  41. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/grep.d.ts +3 -3
  42. package/bundled-plugins/coding-tools/dist/grep.d.ts.map +1 -0
  43. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/grep.js +10 -8
  44. package/bundled-plugins/coding-tools/dist/grep.js.map +1 -0
  45. package/bundled-plugins/coding-tools/dist/index.d.ts +17 -0
  46. package/bundled-plugins/coding-tools/dist/index.d.ts.map +1 -0
  47. package/bundled-plugins/coding-tools/dist/index.js +17 -0
  48. package/bundled-plugins/coding-tools/dist/index.js.map +1 -0
  49. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/ls.d.ts +3 -3
  50. package/bundled-plugins/coding-tools/dist/ls.d.ts.map +1 -0
  51. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/ls.js +8 -6
  52. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/ls.js.map +1 -1
  53. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/powershell.d.ts +2 -1
  54. package/bundled-plugins/coding-tools/dist/powershell.d.ts.map +1 -0
  55. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/powershell.js +6 -3
  56. package/bundled-plugins/coding-tools/dist/powershell.js.map +1 -0
  57. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/read.d.ts +3 -3
  58. package/bundled-plugins/coding-tools/dist/read.d.ts.map +1 -0
  59. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/read.js +15 -13
  60. package/bundled-plugins/coding-tools/dist/read.js.map +1 -0
  61. package/bundled-plugins/coding-tools/dist/types.d.ts +74 -0
  62. package/bundled-plugins/coding-tools/dist/types.d.ts.map +1 -0
  63. package/bundled-plugins/coding-tools/dist/types.js +28 -0
  64. package/bundled-plugins/coding-tools/dist/types.js.map +1 -0
  65. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/write.d.ts +2 -2
  66. package/bundled-plugins/coding-tools/dist/write.d.ts.map +1 -0
  67. package/{dist/core/tools → bundled-plugins/coding-tools/dist}/write.js +11 -9
  68. package/bundled-plugins/coding-tools/dist/write.js.map +1 -0
  69. package/bundled-plugins/coding-tools/plugin.json +10 -0
  70. package/bundled-plugins/compaction-policy/agent-forge.json +19 -0
  71. package/bundled-plugins/compaction-policy/dist/branch-summary-policy.d.ts +6 -0
  72. package/bundled-plugins/compaction-policy/dist/branch-summary-policy.d.ts.map +1 -0
  73. package/bundled-plugins/compaction-policy/dist/branch-summary-policy.js +19 -0
  74. package/bundled-plugins/compaction-policy/dist/branch-summary-policy.js.map +1 -0
  75. package/bundled-plugins/compaction-policy/dist/compaction-policy.d.ts +6 -0
  76. package/bundled-plugins/compaction-policy/dist/compaction-policy.d.ts.map +1 -0
  77. package/bundled-plugins/compaction-policy/dist/compaction-policy.js +38 -0
  78. package/bundled-plugins/compaction-policy/dist/compaction-policy.js.map +1 -0
  79. package/bundled-plugins/compaction-policy/dist/entry.d.ts +13 -0
  80. package/bundled-plugins/compaction-policy/dist/entry.d.ts.map +1 -0
  81. package/bundled-plugins/compaction-policy/dist/entry.js +23 -0
  82. package/bundled-plugins/compaction-policy/dist/entry.js.map +1 -0
  83. package/bundled-plugins/compaction-policy/plugin.json +10 -0
  84. package/bundled-plugins/mcp-http/dist/index.d.ts +8 -0
  85. package/bundled-plugins/mcp-http/dist/index.d.ts.map +1 -0
  86. package/bundled-plugins/mcp-http/dist/index.js +8 -0
  87. package/bundled-plugins/mcp-http/dist/index.js.map +1 -0
  88. package/bundled-plugins/mcp-http/dist/mcp-http.d.ts.map +1 -0
  89. package/{dist/plugins → bundled-plugins/mcp-http/dist}/mcp-http.js +1 -1
  90. package/bundled-plugins/mcp-http/dist/mcp-http.js.map +1 -0
  91. package/bundled-plugins/overflow-policy/agent-forge.json +19 -0
  92. package/bundled-plugins/overflow-policy/dist/entry.d.ts +16 -0
  93. package/bundled-plugins/overflow-policy/dist/entry.d.ts.map +1 -0
  94. package/bundled-plugins/overflow-policy/dist/entry.js +35 -0
  95. package/bundled-plugins/overflow-policy/dist/entry.js.map +1 -0
  96. package/bundled-plugins/overflow-policy/plugin.json +10 -0
  97. package/bundled-plugins/provider-failover/agent-forge.json +19 -0
  98. package/bundled-plugins/provider-failover/dist/entry.d.ts +8 -0
  99. package/bundled-plugins/provider-failover/dist/entry.d.ts.map +1 -0
  100. package/bundled-plugins/provider-failover/dist/entry.js +8 -0
  101. package/bundled-plugins/provider-failover/dist/entry.js.map +1 -0
  102. package/bundled-plugins/provider-failover/dist/provider-failover.d.ts +122 -0
  103. package/bundled-plugins/provider-failover/dist/provider-failover.d.ts.map +1 -0
  104. package/{dist/core/extensions → bundled-plugins/provider-failover/dist}/provider-failover.js +143 -56
  105. package/bundled-plugins/provider-failover/dist/provider-failover.js.map +1 -0
  106. package/bundled-plugins/provider-failover/plugin.json +7 -0
  107. package/bundled-plugins/retry-policy/agent-forge.json +19 -0
  108. package/bundled-plugins/retry-policy/dist/entry.d.ts +16 -0
  109. package/bundled-plugins/retry-policy/dist/entry.d.ts.map +1 -0
  110. package/bundled-plugins/retry-policy/dist/entry.js +34 -0
  111. package/bundled-plugins/retry-policy/dist/entry.js.map +1 -0
  112. package/bundled-plugins/retry-policy/plugin.json +10 -0
  113. package/dist/bundle/.build-manifest.json +109 -155
  114. package/dist/bundle/chunks/{anthropic-messages-ALDZBUKI.js → anthropic-messages-PLAKXR73.js} +1 -1
  115. package/dist/bundle/chunks/{azure-openai-responses-A2WJWZFB.js → azure-openai-responses-OUWQ2AZD.js} +1 -1
  116. package/dist/bundle/chunks/chunk-44I47ACN.js +13 -0
  117. package/dist/bundle/chunks/chunk-6F5JWGKE.js +4 -0
  118. package/dist/bundle/chunks/{chunk-CIX3FSBK.js → chunk-A3JYRWB6.js} +1 -1
  119. package/dist/bundle/chunks/{chunk-SQFG22WN.js → chunk-AC2FIS4G.js} +1 -1
  120. package/dist/bundle/chunks/chunk-HTC5K5SM.js +866 -0
  121. package/dist/bundle/chunks/{chunk-YJCQVXW5.js → chunk-HTEVUY7F.js} +1 -1
  122. package/dist/bundle/chunks/{chunk-Y7HXTASS.js → chunk-HV64ASMT.js} +1 -1
  123. package/dist/bundle/chunks/chunk-LSP24WNH.js +665 -0
  124. package/dist/bundle/chunks/{chunk-7ZY7IXZD.js → chunk-TRDNDS6A.js} +1 -1
  125. package/dist/bundle/chunks/{google-generative-ai-NIB4WONV.js → google-generative-ai-DLII6DAT.js} +1 -1
  126. package/dist/bundle/chunks/{google-vertex-DCPYUEUO.js → google-vertex-G7BZL7GM.js} +1 -1
  127. package/dist/bundle/chunks/image-resize-worker.js +4 -4
  128. package/dist/bundle/chunks/{mistral-conversations-OC6LA5XL.js → mistral-conversations-JXUK24ID.js} +1 -1
  129. package/dist/bundle/chunks/{openai-codex-responses-IO2X4Y7L.js → openai-codex-responses-5GSGFIPG.js} +1 -1
  130. package/dist/bundle/chunks/{openai-completions-XYQQHETU.js → openai-completions-EI2I6DTG.js} +1 -1
  131. package/dist/bundle/chunks/{openai-responses-DLIRN3CI.js → openai-responses-AUJQ3NWT.js} +1 -1
  132. package/dist/bundle/chunks/{pi-messages-E6RRYBO7.js → pi-messages-PWU2VJMR.js} +1 -1
  133. package/dist/bundle/chunks/virtual-modules-MRXD3RWG.js +5 -0
  134. package/dist/bundle/cli.js +1 -1
  135. package/dist/bundle/client.js +1 -1
  136. package/dist/bundle/index.js +3 -3
  137. package/dist/bundle/rpc-entry.js +1 -1
  138. package/dist/capabilities/builtin.d.ts +1 -1
  139. package/dist/capabilities/builtin.d.ts.map +1 -1
  140. package/dist/capabilities/builtin.js +5 -0
  141. package/dist/capabilities/builtin.js.map +1 -1
  142. package/dist/cli/plugin-command.d.ts.map +1 -1
  143. package/dist/cli/plugin-command.js +25 -3
  144. package/dist/cli/plugin-command.js.map +1 -1
  145. package/dist/config.d.ts +2 -3
  146. package/dist/config.d.ts.map +1 -1
  147. package/dist/config.js +2 -4
  148. package/dist/config.js.map +1 -1
  149. package/dist/core/agent-session-bash.d.ts +1 -1
  150. package/dist/core/agent-session-bash.d.ts.map +1 -1
  151. package/dist/core/agent-session-bash.js +1 -1
  152. package/dist/core/agent-session-bash.js.map +1 -1
  153. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  154. package/dist/core/agent-session-compaction.js +14 -1
  155. package/dist/core/agent-session-compaction.js.map +1 -1
  156. package/dist/core/agent-session-internal.d.ts +3 -1
  157. package/dist/core/agent-session-internal.d.ts.map +1 -1
  158. package/dist/core/agent-session-internal.js.map +1 -1
  159. package/dist/core/agent-session-loop.d.ts +4 -3
  160. package/dist/core/agent-session-loop.d.ts.map +1 -1
  161. package/dist/core/agent-session-loop.js +9 -6
  162. package/dist/core/agent-session-loop.js.map +1 -1
  163. package/dist/core/agent-session-retry.d.ts +1 -1
  164. package/dist/core/agent-session-retry.d.ts.map +1 -1
  165. package/dist/core/agent-session-retry.js +32 -3
  166. package/dist/core/agent-session-retry.js.map +1 -1
  167. package/dist/core/agent-session-services.d.ts +2 -0
  168. package/dist/core/agent-session-services.d.ts.map +1 -1
  169. package/dist/core/agent-session-services.js +31 -9
  170. package/dist/core/agent-session-services.js.map +1 -1
  171. package/dist/core/agent-session-store.d.ts.map +1 -1
  172. package/dist/core/agent-session-store.js +6 -1
  173. package/dist/core/agent-session-store.js.map +1 -1
  174. package/dist/core/agent-session-tools.d.ts.map +1 -1
  175. package/dist/core/agent-session-tools.js +18 -1
  176. package/dist/core/agent-session-tools.js.map +1 -1
  177. package/dist/core/agent-session.d.ts +34 -11
  178. package/dist/core/agent-session.d.ts.map +1 -1
  179. package/dist/core/agent-session.js +114 -38
  180. package/dist/core/agent-session.js.map +1 -1
  181. package/dist/core/bash-executor.d.ts +1 -1
  182. package/dist/core/bash-executor.d.ts.map +1 -1
  183. package/dist/core/bash-executor.js.map +1 -1
  184. package/dist/core/extensions/bootstrap-agent-loop.d.ts +23 -151
  185. package/dist/core/extensions/bootstrap-agent-loop.d.ts.map +1 -1
  186. package/dist/core/extensions/bootstrap-agent-loop.js +28 -290
  187. package/dist/core/extensions/bootstrap-agent-loop.js.map +1 -1
  188. package/dist/core/extensions/capability-runtime-types.d.ts +5 -3
  189. package/dist/core/extensions/capability-runtime-types.d.ts.map +1 -1
  190. package/dist/core/extensions/capability-runtime-types.js.map +1 -1
  191. package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
  192. package/dist/core/extensions/capability-runtime.js +42 -6
  193. package/dist/core/extensions/capability-runtime.js.map +1 -1
  194. package/dist/core/extensions/index.d.ts +12 -5
  195. package/dist/core/extensions/index.d.ts.map +1 -1
  196. package/dist/core/extensions/index.js +12 -5
  197. package/dist/core/extensions/index.js.map +1 -1
  198. package/dist/core/extensions/virtual-modules.d.ts.map +1 -1
  199. package/dist/core/extensions/virtual-modules.js +65 -0
  200. package/dist/core/extensions/virtual-modules.js.map +1 -1
  201. package/dist/core/facade/coding-agent-facade.d.ts +1 -1
  202. package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
  203. package/dist/core/facade/coding-agent-facade.js.map +1 -1
  204. package/dist/core/i18n/locales/zh-cn/cli.d.ts.map +1 -1
  205. package/dist/core/i18n/locales/zh-cn/cli.js +2 -0
  206. package/dist/core/i18n/locales/zh-cn/cli.js.map +1 -1
  207. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts +1 -1
  208. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts.map +1 -1
  209. package/dist/core/i18n/locales/zh-cn/core-contract-core.js +1 -1
  210. package/dist/core/i18n/locales/zh-cn/core-contract-core.js.map +1 -1
  211. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts.map +1 -1
  212. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js +2 -4
  213. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js.map +1 -1
  214. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -1
  215. package/dist/core/i18n/locales/zh-cn/core-errors.js +3 -1
  216. package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -1
  217. package/dist/core/i18n/locales/zh-cn/index.d.ts.map +1 -1
  218. package/dist/core/i18n/locales/zh-cn/index.js +2 -0
  219. package/dist/core/i18n/locales/zh-cn/index.js.map +1 -1
  220. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -1
  221. package/dist/core/i18n/locales/zh-cn/interactive-mode.js +5 -0
  222. package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -1
  223. package/dist/core/i18n/locales/zh-cn/session-cleanup.d.ts +7 -0
  224. package/dist/core/i18n/locales/zh-cn/session-cleanup.d.ts.map +1 -0
  225. package/dist/core/i18n/locales/zh-cn/session-cleanup.js +15 -0
  226. package/dist/core/i18n/locales/zh-cn/session-cleanup.js.map +1 -0
  227. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts.map +1 -1
  228. package/dist/core/i18n/locales/zh-cn/slash-commands.js +1 -0
  229. package/dist/core/i18n/locales/zh-cn/slash-commands.js.map +1 -1
  230. package/dist/core/sdk.d.ts +10 -3
  231. package/dist/core/sdk.d.ts.map +1 -1
  232. package/dist/core/sdk.js +287 -78
  233. package/dist/core/sdk.js.map +1 -1
  234. package/dist/core/session-cleanup.d.ts +49 -0
  235. package/dist/core/session-cleanup.d.ts.map +1 -0
  236. package/dist/core/session-cleanup.js +159 -0
  237. package/dist/core/session-cleanup.js.map +1 -0
  238. package/dist/core/session-index.d.ts +75 -0
  239. package/dist/core/session-index.d.ts.map +1 -0
  240. package/dist/core/session-index.js +455 -0
  241. package/dist/core/session-index.js.map +1 -0
  242. package/dist/core/session-manager.d.ts +9 -0
  243. package/dist/core/session-manager.d.ts.map +1 -1
  244. package/dist/core/session-manager.js +202 -47
  245. package/dist/core/session-manager.js.map +1 -1
  246. package/dist/core/slash-commands.d.ts.map +1 -1
  247. package/dist/core/slash-commands.js +5 -0
  248. package/dist/core/slash-commands.js.map +1 -1
  249. package/dist/core/system-prompt.d.ts.map +1 -1
  250. package/dist/core/system-prompt.js +12 -1
  251. package/dist/core/system-prompt.js.map +1 -1
  252. package/dist/core/tools/bash-operations.d.ts +41 -0
  253. package/dist/core/tools/bash-operations.d.ts.map +1 -0
  254. package/dist/core/tools/bash-operations.js +113 -0
  255. package/dist/core/tools/bash-operations.js.map +1 -0
  256. package/dist/core/tools/capability-mount.d.ts +40 -0
  257. package/dist/core/tools/capability-mount.d.ts.map +1 -0
  258. package/dist/core/tools/capability-mount.js +112 -0
  259. package/dist/core/tools/capability-mount.js.map +1 -0
  260. package/dist/core/tools/index.d.ts +12 -36
  261. package/dist/core/tools/index.d.ts.map +1 -1
  262. package/dist/core/tools/index.js +1 -117
  263. package/dist/core/tools/index.js.map +1 -1
  264. package/dist/core/tools/readme-path.d.ts +3 -0
  265. package/dist/core/tools/readme-path.d.ts.map +1 -0
  266. package/dist/core/tools/readme-path.js +15 -0
  267. package/dist/core/tools/readme-path.js.map +1 -0
  268. package/dist/index.d.ts +3 -19
  269. package/dist/index.d.ts.map +1 -1
  270. package/dist/index.js +6 -39
  271. package/dist/index.js.map +1 -1
  272. package/dist/main.d.ts.map +1 -1
  273. package/dist/main.js +1 -0
  274. package/dist/main.js.map +1 -1
  275. package/dist/modes/interactive/components/assistant-message.d.ts +3 -0
  276. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/assistant-message.js +27 -5
  278. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  279. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/session-selector-search.js +5 -1
  281. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  282. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/session-selector.js +9 -0
  284. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  285. package/dist/modes/interactive/components/tool-execution.d.ts +17 -1
  286. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  287. package/dist/modes/interactive/components/tool-execution.js +52 -25
  288. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  289. package/dist/modes/interactive/components/tool-run-group.d.ts +44 -0
  290. package/dist/modes/interactive/components/tool-run-group.d.ts.map +1 -0
  291. package/dist/modes/interactive/components/tool-run-group.js +121 -0
  292. package/dist/modes/interactive/components/tool-run-group.js.map +1 -0
  293. package/dist/modes/interactive/interactive-commands.d.ts +2 -0
  294. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  295. package/dist/modes/interactive/interactive-commands.js +7 -0
  296. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  297. package/dist/modes/interactive/interactive-mode.d.ts +18 -0
  298. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  299. package/dist/modes/interactive/interactive-mode.js +85 -3
  300. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  301. package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
  302. package/dist/plugins/ecosystem/cli.js +20 -5
  303. package/dist/plugins/ecosystem/cli.js.map +1 -1
  304. package/dist/plugins/ecosystem/node-npm-registry-source.d.ts +7 -2
  305. package/dist/plugins/ecosystem/node-npm-registry-source.d.ts.map +1 -1
  306. package/dist/plugins/ecosystem/node-npm-registry-source.js +64 -9
  307. package/dist/plugins/ecosystem/node-npm-registry-source.js.map +1 -1
  308. package/dist/plugins/ecosystem/node-plugin-acquisition-host.d.ts.map +1 -1
  309. package/dist/plugins/ecosystem/node-plugin-acquisition-host.js +6 -0
  310. package/dist/plugins/ecosystem/node-plugin-acquisition-host.js.map +1 -1
  311. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts +7 -1
  312. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  313. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +50 -3
  314. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  315. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts +28 -16
  316. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts.map +1 -1
  317. package/dist/plugins/ecosystem/plugin-bootstrap.js +35 -33
  318. package/dist/plugins/ecosystem/plugin-bootstrap.js.map +1 -1
  319. package/dist/profile/plugin-profiles.d.ts +1 -1
  320. package/dist/profile/plugin-profiles.d.ts.map +1 -1
  321. package/dist/profile/plugin-profiles.js +18 -0
  322. package/dist/profile/plugin-profiles.js.map +1 -1
  323. package/dist/profiles/builtin-suites.d.ts +19 -5
  324. package/dist/profiles/builtin-suites.d.ts.map +1 -1
  325. package/dist/profiles/builtin-suites.js +26 -6
  326. package/dist/profiles/builtin-suites.js.map +1 -1
  327. package/docs/settings.md +354 -354
  328. package/docs/usage.md +292 -292
  329. package/examples/extensions/bash-spawn-hook/index.ts +7 -2
  330. package/examples/extensions/built-in-tool-renderer/index.ts +4 -2
  331. package/examples/extensions/minimal-mode/index.ts +2 -2
  332. package/examples/extensions/truncated-tool/index.ts +1 -1
  333. package/examples/extensions/with-deps/package-lock.json +2 -2
  334. package/examples/extensions/with-deps/package.json +1 -1
  335. package/examples/extensions/with-deps/plugin.json +1 -1
  336. package/package.json +3 -2
  337. package/dist/bundle/chunks/chunk-6ZKDNZHA.js +0 -145
  338. package/dist/bundle/chunks/chunk-HLJEVRWP.js +0 -13
  339. package/dist/bundle/chunks/chunk-YGHQME4V.js +0 -1456
  340. package/dist/bundle/chunks/virtual-modules-KZ5DRH7Q.js +0 -2
  341. package/dist/core/extensions/bootstrap-branch-summary-policy.d.ts +0 -7
  342. package/dist/core/extensions/bootstrap-branch-summary-policy.d.ts.map +0 -1
  343. package/dist/core/extensions/bootstrap-branch-summary-policy.js +0 -28
  344. package/dist/core/extensions/bootstrap-branch-summary-policy.js.map +0 -1
  345. package/dist/core/extensions/bootstrap-compaction-policy.d.ts +0 -7
  346. package/dist/core/extensions/bootstrap-compaction-policy.d.ts.map +0 -1
  347. package/dist/core/extensions/bootstrap-compaction-policy.js +0 -48
  348. package/dist/core/extensions/bootstrap-compaction-policy.js.map +0 -1
  349. package/dist/core/extensions/bootstrap-overflow-policy.d.ts +0 -7
  350. package/dist/core/extensions/bootstrap-overflow-policy.d.ts.map +0 -1
  351. package/dist/core/extensions/bootstrap-overflow-policy.js +0 -30
  352. package/dist/core/extensions/bootstrap-overflow-policy.js.map +0 -1
  353. package/dist/core/extensions/bootstrap-retry-policy.d.ts +0 -7
  354. package/dist/core/extensions/bootstrap-retry-policy.d.ts.map +0 -1
  355. package/dist/core/extensions/bootstrap-retry-policy.js +0 -29
  356. package/dist/core/extensions/bootstrap-retry-policy.js.map +0 -1
  357. package/dist/core/extensions/bootstrap-tools.d.ts +0 -20
  358. package/dist/core/extensions/bootstrap-tools.d.ts.map +0 -1
  359. package/dist/core/extensions/bootstrap-tools.js +0 -36
  360. package/dist/core/extensions/bootstrap-tools.js.map +0 -1
  361. package/dist/core/extensions/provider-failover.d.ts +0 -110
  362. package/dist/core/extensions/provider-failover.d.ts.map +0 -1
  363. package/dist/core/extensions/provider-failover.js.map +0 -1
  364. package/dist/core/tools/bash.d.ts.map +0 -1
  365. package/dist/core/tools/bash.js.map +0 -1
  366. package/dist/core/tools/edit.d.ts.map +0 -1
  367. package/dist/core/tools/edit.js.map +0 -1
  368. package/dist/core/tools/find.d.ts.map +0 -1
  369. package/dist/core/tools/find.js.map +0 -1
  370. package/dist/core/tools/grep.d.ts.map +0 -1
  371. package/dist/core/tools/grep.js.map +0 -1
  372. package/dist/core/tools/ls.d.ts.map +0 -1
  373. package/dist/core/tools/powershell.d.ts.map +0 -1
  374. package/dist/core/tools/powershell.js.map +0 -1
  375. package/dist/core/tools/read.d.ts.map +0 -1
  376. package/dist/core/tools/read.js.map +0 -1
  377. package/dist/core/tools/write.d.ts.map +0 -1
  378. package/dist/core/tools/write.js.map +0 -1
  379. package/dist/plugins/mcp-http.d.ts.map +0 -1
  380. package/dist/plugins/mcp-http.js.map +0 -1
  381. package/dist/profile/child-facade.d.ts +0 -44
  382. package/dist/profile/child-facade.d.ts.map +0 -1
  383. package/dist/profile/child-facade.js +0 -56
  384. package/dist/profile/child-facade.js.map +0 -1
  385. package/dist/profile/dependency-wiring.d.ts +0 -48
  386. package/dist/profile/dependency-wiring.d.ts.map +0 -1
  387. package/dist/profile/dependency-wiring.js +0 -56
  388. package/dist/profile/dependency-wiring.js.map +0 -1
  389. package/dist/profile/domain-recovery.d.ts +0 -60
  390. package/dist/profile/domain-recovery.d.ts.map +0 -1
  391. package/dist/profile/domain-recovery.js +0 -94
  392. package/dist/profile/domain-recovery.js.map +0 -1
  393. package/dist/profile/first-roles.d.ts +0 -30
  394. package/dist/profile/first-roles.d.ts.map +0 -1
  395. package/dist/profile/first-roles.js +0 -83
  396. package/dist/profile/first-roles.js.map +0 -1
  397. package/dist/profile/goal-plugin.d.ts +0 -38
  398. package/dist/profile/goal-plugin.d.ts.map +0 -1
  399. package/dist/profile/goal-plugin.js +0 -132
  400. package/dist/profile/goal-plugin.js.map +0 -1
  401. package/dist/profile/plan-plugin.d.ts +0 -44
  402. package/dist/profile/plan-plugin.d.ts.map +0 -1
  403. package/dist/profile/plan-plugin.js +0 -157
  404. package/dist/profile/plan-plugin.js.map +0 -1
  405. package/dist/profile/policy-manifest.d.ts +0 -53
  406. package/dist/profile/policy-manifest.d.ts.map +0 -1
  407. package/dist/profile/policy-manifest.js +0 -53
  408. package/dist/profile/policy-manifest.js.map +0 -1
  409. package/dist/profile/role-assembly.d.ts +0 -47
  410. package/dist/profile/role-assembly.d.ts.map +0 -1
  411. package/dist/profile/role-assembly.js +0 -31
  412. package/dist/profile/role-assembly.js.map +0 -1
  413. package/dist/profile/role-catalog.d.ts +0 -43
  414. package/dist/profile/role-catalog.d.ts.map +0 -1
  415. package/dist/profile/role-catalog.js +0 -132
  416. package/dist/profile/role-catalog.js.map +0 -1
  417. package/dist/profile/role-orchestrator.d.ts +0 -123
  418. package/dist/profile/role-orchestrator.d.ts.map +0 -1
  419. package/dist/profile/role-orchestrator.js +0 -195
  420. package/dist/profile/role-orchestrator.js.map +0 -1
  421. package/dist/profile/role-runner.d.ts +0 -99
  422. package/dist/profile/role-runner.d.ts.map +0 -1
  423. package/dist/profile/role-runner.js +0 -79
  424. package/dist/profile/role-runner.js.map +0 -1
  425. package/dist/profile/root-facade.d.ts +0 -36
  426. package/dist/profile/root-facade.d.ts.map +0 -1
  427. package/dist/profile/root-facade.js +0 -49
  428. package/dist/profile/root-facade.js.map +0 -1
  429. package/dist/profile/skill-discovery-wiring.d.ts +0 -58
  430. package/dist/profile/skill-discovery-wiring.d.ts.map +0 -1
  431. package/dist/profile/skill-discovery-wiring.js +0 -80
  432. package/dist/profile/skill-discovery-wiring.js.map +0 -1
  433. package/dist/profile/state-store.d.ts +0 -62
  434. package/dist/profile/state-store.d.ts.map +0 -1
  435. package/dist/profile/state-store.js +0 -144
  436. package/dist/profile/state-store.js.map +0 -1
  437. package/dist/profile/todo-plugin.d.ts +0 -37
  438. package/dist/profile/todo-plugin.d.ts.map +0 -1
  439. package/dist/profile/todo-plugin.js +0 -137
  440. package/dist/profile/todo-plugin.js.map +0 -1
  441. package/examples/extensions/mcp-stdio/index.d.ts +0 -64
  442. package/examples/extensions/mcp-stdio/index.d.ts.map +0 -1
  443. package/examples/extensions/mcp-stdio/index.js +0 -292
  444. package/examples/extensions/mcp-stdio/index.js.map +0 -1
  445. /package/{dist/plugins → bundled-plugins/mcp-http/dist}/mcp-http.d.ts +0 -0
@@ -1,62 +0,0 @@
1
- /**
2
- * Generic transition/evidence/revision store (2.3a) — the domain-agnostic
3
- * contract base for Goal/Plan/Todo plugins.
4
- *
5
- * The store knows NOTHING about goal/plan/todo semantics. It only enforces:
6
- * - causality: a transition's `fromStatus` must equal the entity's current
7
- * status (null = initial transition), otherwise an explicit conflict;
8
- * - idempotency: replaying the same (domain, entityId, operationId, toStatus)
9
- * returns the existing record and never advances the revision;
10
- * - evidence: every transition carries structured evidence references;
11
- * - payload: transitions may carry an optional domain payload (e.g. a goal
12
- * title) that is validated JSON and part of the replayable record;
13
- * - replay: the full history rebuilds the identical current state.
14
- */
15
- import type { JsonValue } from "@agent-forge/ai";
16
- export interface TransitionEvidenceRefV1 {
17
- readonly kind: string;
18
- readonly ref: string;
19
- readonly digest?: string;
20
- }
21
- export interface TransitionRecordV1 {
22
- readonly domain: string;
23
- readonly entityId: string;
24
- readonly owner: string;
25
- readonly fromStatus: string | null;
26
- readonly toStatus: string;
27
- readonly operationId: string;
28
- readonly evidence: readonly TransitionEvidenceRefV1[];
29
- readonly payload?: JsonValue;
30
- readonly revision: number;
31
- readonly at: number;
32
- }
33
- export interface TransitionInputV1 {
34
- readonly domain: string;
35
- readonly entityId: string;
36
- readonly owner: string;
37
- readonly fromStatus: string | null;
38
- readonly toStatus: string;
39
- readonly operationId: string;
40
- readonly evidence?: readonly TransitionEvidenceRefV1[];
41
- readonly payload?: JsonValue;
42
- readonly at?: number;
43
- }
44
- export interface TransitionResultV1 {
45
- readonly record: TransitionRecordV1;
46
- readonly deduped: boolean;
47
- }
48
- export interface TransitionStoreV1 {
49
- transition(input: TransitionInputV1): TransitionResultV1;
50
- currentStatus(domain: string, entityId: string): {
51
- readonly status: string;
52
- readonly revision: number;
53
- } | undefined;
54
- history(entityId: string): readonly TransitionRecordV1[];
55
- /** Replays a history into a fresh store; the rebuilt state must be identical. */
56
- replay(history: readonly TransitionRecordV1[]): void;
57
- entities(): readonly string[];
58
- }
59
- export declare function createTransitionStore(options?: {
60
- readonly now?: () => number;
61
- }): TransitionStoreV1;
62
- //# sourceMappingURL=state-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/profile/state-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B;AA+DD,MAAM,WAAW,iBAAiB;IACjC,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAAC;IACzD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACpH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,kBAAkB,EAAE,CAAC;IACzD,iFAAiF;IACjF,MAAM,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAAC;IACrD,QAAQ,IAAI,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAAG,iBAAiB,CAoFtG","sourcesContent":["/**\n * Generic transition/evidence/revision store (2.3a) — the domain-agnostic\n * contract base for Goal/Plan/Todo plugins.\n *\n * The store knows NOTHING about goal/plan/todo semantics. It only enforces:\n * - causality: a transition's `fromStatus` must equal the entity's current\n * status (null = initial transition), otherwise an explicit conflict;\n * - idempotency: replaying the same (domain, entityId, operationId, toStatus)\n * returns the existing record and never advances the revision;\n * - evidence: every transition carries structured evidence references;\n * - payload: transitions may carry an optional domain payload (e.g. a goal\n * title) that is validated JSON and part of the replayable record;\n * - replay: the full history rebuilds the identical current state.\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { t } from \"../core/i18n/index.ts\";\n\nexport interface TransitionEvidenceRefV1 {\n\treadonly kind: string;\n\treadonly ref: string;\n\treadonly digest?: string;\n}\n\nexport interface TransitionRecordV1 {\n\treadonly domain: string;\n\treadonly entityId: string;\n\treadonly owner: string;\n\treadonly fromStatus: string | null;\n\treadonly toStatus: string;\n\treadonly operationId: string;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly payload?: JsonValue;\n\treadonly revision: number;\n\treadonly at: number;\n}\n\nexport interface TransitionInputV1 {\n\treadonly domain: string;\n\treadonly entityId: string;\n\treadonly owner: string;\n\treadonly fromStatus: string | null;\n\treadonly toStatus: string;\n\treadonly operationId: string;\n\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\treadonly payload?: JsonValue;\n\treadonly at?: number;\n}\n\nexport interface TransitionResultV1 {\n\treadonly record: TransitionRecordV1;\n\treadonly deduped: boolean;\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 validateEvidence(value: unknown, label: string): TransitionEvidenceRefV1[] {\n\tif (value === undefined) return [];\n\tif (!Array.isArray(value)) throw new Error(t(\"{label} must be an array\", { label }));\n\treturn value.map((item, index) => {\n\t\tconst depLabel = `${label}[${index}]`;\n\t\tif (item === null || typeof item !== \"object\" || Array.isArray(item)) {\n\t\t\tthrow new Error(t(\"{label} must be a plain object\", { label: depLabel }));\n\t\t}\n\t\tconst record = item as Record<string, unknown>;\n\t\tassertNonEmptyString(record.kind, `${depLabel}.kind`);\n\t\tassertNonEmptyString(record.ref, `${depLabel}.ref`);\n\t\tif (record.digest !== undefined) assertNonEmptyString(record.digest, `${depLabel}.digest`);\n\t\treturn Object.freeze({\n\t\t\tkind: record.kind,\n\t\t\tref: record.ref,\n\t\t\t...(record.digest === undefined ? {} : { digest: record.digest }),\n\t\t});\n\t});\n}\n\nfunction validatePayload(value: unknown, label: string): JsonValue | undefined {\n\tif (value === undefined) return undefined;\n\tconst seen = new Set<object>();\n\tconst check = (node: unknown): void => {\n\t\tif (node === null || typeof node === \"string\" || typeof node === \"boolean\") return;\n\t\tif (typeof node === \"number\") {\n\t\t\tif (!Number.isFinite(node))\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"{label} must be JSON-safe: {node} is not a finite number\", { label, node: String(node) }),\n\t\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (typeof node === \"object\") {\n\t\t\tif (seen.has(node)) throw new Error(t(\"{label} must be JSON-safe: circular reference\", { label }));\n\t\t\tseen.add(node);\n\t\t\tif (Array.isArray(node)) {\n\t\t\t\tfor (const item of node) check(item);\n\t\t\t} else {\n\t\t\t\tconst proto = Object.getPrototypeOf(node) as unknown;\n\t\t\t\tif (proto !== Object.prototype && proto !== null)\n\t\t\t\t\tthrow new Error(t(\"{label} must be a plain object\", { label }));\n\t\t\t\tfor (const entry of Object.entries(node)) {\n\t\t\t\t\tif (entry[1] === undefined)\n\t\t\t\t\t\tthrow new Error(t(\"{label} must be JSON-safe: {key} is undefined\", { label, key: entry[0] }));\n\t\t\t\t\tcheck(entry[1]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tseen.delete(node);\n\t\t\treturn;\n\t\t}\n\t\tthrow new Error(t(\"{label} must be JSON-safe: unsupported value of type {type}\", { label, type: typeof node }));\n\t};\n\tcheck(value);\n\treturn JSON.parse(JSON.stringify(value)) as JsonValue;\n}\n\nexport interface TransitionStoreV1 {\n\ttransition(input: TransitionInputV1): TransitionResultV1;\n\tcurrentStatus(domain: string, entityId: string): { readonly status: string; readonly revision: number } | undefined;\n\thistory(entityId: string): readonly TransitionRecordV1[];\n\t/** Replays a history into a fresh store; the rebuilt state must be identical. */\n\treplay(history: readonly TransitionRecordV1[]): void;\n\tentities(): readonly string[];\n}\n\nexport function createTransitionStore(options: { readonly now?: () => number } = {}): TransitionStoreV1 {\n\tconst now = options.now ?? (() => Date.now());\n\tconst recordsByEntity = new Map<string, TransitionRecordV1[]>();\n\tconst idempotency = new Map<string, TransitionRecordV1>();\n\tconst currentStatus = new Map<string, { status: string; revision: number }>();\n\tconst revisions = new Map<string, number>();\n\n\tconst entityKey = (domain: string, entityId: string): string => `${domain}\\u0000${entityId}`;\n\n\treturn {\n\t\ttransition(input) {\n\t\t\tassertNonEmptyString(input.domain, \"Transition domain\");\n\t\t\tassertNonEmptyString(input.entityId, \"Transition entityId\");\n\t\t\tassertNonEmptyString(input.owner, \"Transition owner\");\n\t\t\tassertNonEmptyString(input.operationId, \"Transition operationId\");\n\t\t\tassertNonEmptyString(input.toStatus, \"Transition toStatus\");\n\t\t\tconst evidence = validateEvidence(input.evidence, \"Transition evidence\");\n\t\t\tconst payload = validatePayload(input.payload, \"Transition payload\");\n\t\t\tconst key = `${entityKey(input.domain, input.entityId)}\\u0000${input.operationId}\\u0000${input.toStatus}`;\n\n\t\t\tconst existing = idempotency.get(key);\n\t\t\tif (existing) return { record: existing, deduped: true };\n\n\t\t\tconst entityKeyId = entityKey(input.domain, input.entityId);\n\t\t\tconst current = currentStatus.get(entityKeyId);\n\t\t\tconst fromStatus = input.fromStatus;\n\t\t\tif ((current?.status ?? null) !== fromStatus) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Transition conflict: expected fromStatus {from}, got {to}\", {\n\t\t\t\t\t\tfrom: JSON.stringify(current?.status ?? null),\n\t\t\t\t\t\tto: JSON.stringify(fromStatus),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst revision = (revisions.get(entityKeyId) ?? 0) + 1;\n\t\t\trevisions.set(entityKeyId, revision);\n\t\t\tconst record: TransitionRecordV1 = Object.freeze({\n\t\t\t\tdomain: input.domain,\n\t\t\t\tentityId: input.entityId,\n\t\t\t\towner: input.owner,\n\t\t\t\tfromStatus,\n\t\t\t\ttoStatus: input.toStatus,\n\t\t\t\toperationId: input.operationId,\n\t\t\t\tevidence: Object.freeze(evidence),\n\t\t\t\t...(payload === undefined ? {} : { payload }),\n\t\t\t\trevision,\n\t\t\t\tat: input.at ?? now(),\n\t\t\t});\n\t\t\tconst log = recordsByEntity.get(entityKeyId) ?? [];\n\t\t\tlog.push(record);\n\t\t\trecordsByEntity.set(entityKeyId, log);\n\t\t\tcurrentStatus.set(entityKeyId, { status: input.toStatus, revision });\n\t\t\tidempotency.set(key, record);\n\t\t\treturn { record, deduped: false };\n\t\t},\n\t\tcurrentStatus(domain: string, entityId: string) {\n\t\t\treturn currentStatus.get(entityKey(domain, entityId));\n\t\t},\n\t\thistory(entityId: string) {\n\t\t\treturn Object.freeze(\n\t\t\t\t[...recordsByEntity.entries()]\n\t\t\t\t\t.filter(([entityKeyId]) => entityKeyId.endsWith(`\\u0000${entityId}`))\n\t\t\t\t\t.flatMap(([, log]) => log),\n\t\t\t);\n\t\t},\n\t\treplay(history) {\n\t\t\tfor (const record of history) {\n\t\t\t\tthis.transition({\n\t\t\t\t\tdomain: record.domain,\n\t\t\t\t\tentityId: record.entityId,\n\t\t\t\t\towner: record.owner,\n\t\t\t\t\tfromStatus: record.fromStatus,\n\t\t\t\t\ttoStatus: record.toStatus,\n\t\t\t\t\toperationId: record.operationId,\n\t\t\t\t\tevidence: record.evidence,\n\t\t\t\t\tpayload: record.payload,\n\t\t\t\t\tat: record.at,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tentities() {\n\t\t\treturn Object.freeze([...new Set([...currentStatus.keys()].map((key) => key.split(\"\\u0000\")[1]!))]);\n\t\t},\n\t};\n}\n"]}
@@ -1,144 +0,0 @@
1
- import { t } from "../core/i18n/index.js";
2
- function assertNonEmptyString(value, label) {
3
- if (typeof value !== "string" || value.trim().length === 0)
4
- throw new Error(t("{label} must be a non-empty string", { label }));
5
- }
6
- function validateEvidence(value, label) {
7
- if (value === undefined)
8
- return [];
9
- if (!Array.isArray(value))
10
- throw new Error(t("{label} must be an array", { label }));
11
- return value.map((item, index) => {
12
- const depLabel = `${label}[${index}]`;
13
- if (item === null || typeof item !== "object" || Array.isArray(item)) {
14
- throw new Error(t("{label} must be a plain object", { label: depLabel }));
15
- }
16
- const record = item;
17
- assertNonEmptyString(record.kind, `${depLabel}.kind`);
18
- assertNonEmptyString(record.ref, `${depLabel}.ref`);
19
- if (record.digest !== undefined)
20
- assertNonEmptyString(record.digest, `${depLabel}.digest`);
21
- return Object.freeze({
22
- kind: record.kind,
23
- ref: record.ref,
24
- ...(record.digest === undefined ? {} : { digest: record.digest }),
25
- });
26
- });
27
- }
28
- function validatePayload(value, label) {
29
- if (value === undefined)
30
- return undefined;
31
- const seen = new Set();
32
- const check = (node) => {
33
- if (node === null || typeof node === "string" || typeof node === "boolean")
34
- return;
35
- if (typeof node === "number") {
36
- if (!Number.isFinite(node))
37
- throw new Error(t("{label} must be JSON-safe: {node} is not a finite number", { label, node: String(node) }));
38
- return;
39
- }
40
- if (typeof node === "object") {
41
- if (seen.has(node))
42
- throw new Error(t("{label} must be JSON-safe: circular reference", { label }));
43
- seen.add(node);
44
- if (Array.isArray(node)) {
45
- for (const item of node)
46
- check(item);
47
- }
48
- else {
49
- const proto = Object.getPrototypeOf(node);
50
- if (proto !== Object.prototype && proto !== null)
51
- throw new Error(t("{label} must be a plain object", { label }));
52
- for (const entry of Object.entries(node)) {
53
- if (entry[1] === undefined)
54
- throw new Error(t("{label} must be JSON-safe: {key} is undefined", { label, key: entry[0] }));
55
- check(entry[1]);
56
- }
57
- }
58
- seen.delete(node);
59
- return;
60
- }
61
- throw new Error(t("{label} must be JSON-safe: unsupported value of type {type}", { label, type: typeof node }));
62
- };
63
- check(value);
64
- return JSON.parse(JSON.stringify(value));
65
- }
66
- export function createTransitionStore(options = {}) {
67
- const now = options.now ?? (() => Date.now());
68
- const recordsByEntity = new Map();
69
- const idempotency = new Map();
70
- const currentStatus = new Map();
71
- const revisions = new Map();
72
- const entityKey = (domain, entityId) => `${domain}\u0000${entityId}`;
73
- return {
74
- transition(input) {
75
- assertNonEmptyString(input.domain, "Transition domain");
76
- assertNonEmptyString(input.entityId, "Transition entityId");
77
- assertNonEmptyString(input.owner, "Transition owner");
78
- assertNonEmptyString(input.operationId, "Transition operationId");
79
- assertNonEmptyString(input.toStatus, "Transition toStatus");
80
- const evidence = validateEvidence(input.evidence, "Transition evidence");
81
- const payload = validatePayload(input.payload, "Transition payload");
82
- const key = `${entityKey(input.domain, input.entityId)}\u0000${input.operationId}\u0000${input.toStatus}`;
83
- const existing = idempotency.get(key);
84
- if (existing)
85
- return { record: existing, deduped: true };
86
- const entityKeyId = entityKey(input.domain, input.entityId);
87
- const current = currentStatus.get(entityKeyId);
88
- const fromStatus = input.fromStatus;
89
- if ((current?.status ?? null) !== fromStatus) {
90
- throw new Error(t("Transition conflict: expected fromStatus {from}, got {to}", {
91
- from: JSON.stringify(current?.status ?? null),
92
- to: JSON.stringify(fromStatus),
93
- }));
94
- }
95
- const revision = (revisions.get(entityKeyId) ?? 0) + 1;
96
- revisions.set(entityKeyId, revision);
97
- const record = Object.freeze({
98
- domain: input.domain,
99
- entityId: input.entityId,
100
- owner: input.owner,
101
- fromStatus,
102
- toStatus: input.toStatus,
103
- operationId: input.operationId,
104
- evidence: Object.freeze(evidence),
105
- ...(payload === undefined ? {} : { payload }),
106
- revision,
107
- at: input.at ?? now(),
108
- });
109
- const log = recordsByEntity.get(entityKeyId) ?? [];
110
- log.push(record);
111
- recordsByEntity.set(entityKeyId, log);
112
- currentStatus.set(entityKeyId, { status: input.toStatus, revision });
113
- idempotency.set(key, record);
114
- return { record, deduped: false };
115
- },
116
- currentStatus(domain, entityId) {
117
- return currentStatus.get(entityKey(domain, entityId));
118
- },
119
- history(entityId) {
120
- return Object.freeze([...recordsByEntity.entries()]
121
- .filter(([entityKeyId]) => entityKeyId.endsWith(`\u0000${entityId}`))
122
- .flatMap(([, log]) => log));
123
- },
124
- replay(history) {
125
- for (const record of history) {
126
- this.transition({
127
- domain: record.domain,
128
- entityId: record.entityId,
129
- owner: record.owner,
130
- fromStatus: record.fromStatus,
131
- toStatus: record.toStatus,
132
- operationId: record.operationId,
133
- evidence: record.evidence,
134
- payload: record.payload,
135
- at: record.at,
136
- });
137
- }
138
- },
139
- entities() {
140
- return Object.freeze([...new Set([...currentStatus.keys()].map((key) => key.split("\u0000")[1]))]);
141
- },
142
- };
143
- }
144
- //# sourceMappingURL=state-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-store.js","sourceRoot":"","sources":["../../src/profile/state-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic transition/evidence/revision store (2.3a) — the domain-agnostic\n * contract base for Goal/Plan/Todo plugins.\n *\n * The store knows NOTHING about goal/plan/todo semantics. It only enforces:\n * - causality: a transition's `fromStatus` must equal the entity's current\n * status (null = initial transition), otherwise an explicit conflict;\n * - idempotency: replaying the same (domain, entityId, operationId, toStatus)\n * returns the existing record and never advances the revision;\n * - evidence: every transition carries structured evidence references;\n * - payload: transitions may carry an optional domain payload (e.g. a goal\n * title) that is validated JSON and part of the replayable record;\n * - replay: the full history rebuilds the identical current state.\n */\nimport type { JsonValue } from \"@agent-forge/ai\";\nimport { t } from \"../core/i18n/index.ts\";\n\nexport interface TransitionEvidenceRefV1 {\n\treadonly kind: string;\n\treadonly ref: string;\n\treadonly digest?: string;\n}\n\nexport interface TransitionRecordV1 {\n\treadonly domain: string;\n\treadonly entityId: string;\n\treadonly owner: string;\n\treadonly fromStatus: string | null;\n\treadonly toStatus: string;\n\treadonly operationId: string;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly payload?: JsonValue;\n\treadonly revision: number;\n\treadonly at: number;\n}\n\nexport interface TransitionInputV1 {\n\treadonly domain: string;\n\treadonly entityId: string;\n\treadonly owner: string;\n\treadonly fromStatus: string | null;\n\treadonly toStatus: string;\n\treadonly operationId: string;\n\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\treadonly payload?: JsonValue;\n\treadonly at?: number;\n}\n\nexport interface TransitionResultV1 {\n\treadonly record: TransitionRecordV1;\n\treadonly deduped: boolean;\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 validateEvidence(value: unknown, label: string): TransitionEvidenceRefV1[] {\n\tif (value === undefined) return [];\n\tif (!Array.isArray(value)) throw new Error(t(\"{label} must be an array\", { label }));\n\treturn value.map((item, index) => {\n\t\tconst depLabel = `${label}[${index}]`;\n\t\tif (item === null || typeof item !== \"object\" || Array.isArray(item)) {\n\t\t\tthrow new Error(t(\"{label} must be a plain object\", { label: depLabel }));\n\t\t}\n\t\tconst record = item as Record<string, unknown>;\n\t\tassertNonEmptyString(record.kind, `${depLabel}.kind`);\n\t\tassertNonEmptyString(record.ref, `${depLabel}.ref`);\n\t\tif (record.digest !== undefined) assertNonEmptyString(record.digest, `${depLabel}.digest`);\n\t\treturn Object.freeze({\n\t\t\tkind: record.kind,\n\t\t\tref: record.ref,\n\t\t\t...(record.digest === undefined ? {} : { digest: record.digest }),\n\t\t});\n\t});\n}\n\nfunction validatePayload(value: unknown, label: string): JsonValue | undefined {\n\tif (value === undefined) return undefined;\n\tconst seen = new Set<object>();\n\tconst check = (node: unknown): void => {\n\t\tif (node === null || typeof node === \"string\" || typeof node === \"boolean\") return;\n\t\tif (typeof node === \"number\") {\n\t\t\tif (!Number.isFinite(node))\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"{label} must be JSON-safe: {node} is not a finite number\", { label, node: String(node) }),\n\t\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (typeof node === \"object\") {\n\t\t\tif (seen.has(node)) throw new Error(t(\"{label} must be JSON-safe: circular reference\", { label }));\n\t\t\tseen.add(node);\n\t\t\tif (Array.isArray(node)) {\n\t\t\t\tfor (const item of node) check(item);\n\t\t\t} else {\n\t\t\t\tconst proto = Object.getPrototypeOf(node) as unknown;\n\t\t\t\tif (proto !== Object.prototype && proto !== null)\n\t\t\t\t\tthrow new Error(t(\"{label} must be a plain object\", { label }));\n\t\t\t\tfor (const entry of Object.entries(node)) {\n\t\t\t\t\tif (entry[1] === undefined)\n\t\t\t\t\t\tthrow new Error(t(\"{label} must be JSON-safe: {key} is undefined\", { label, key: entry[0] }));\n\t\t\t\t\tcheck(entry[1]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tseen.delete(node);\n\t\t\treturn;\n\t\t}\n\t\tthrow new Error(t(\"{label} must be JSON-safe: unsupported value of type {type}\", { label, type: typeof node }));\n\t};\n\tcheck(value);\n\treturn JSON.parse(JSON.stringify(value)) as JsonValue;\n}\n\nexport interface TransitionStoreV1 {\n\ttransition(input: TransitionInputV1): TransitionResultV1;\n\tcurrentStatus(domain: string, entityId: string): { readonly status: string; readonly revision: number } | undefined;\n\thistory(entityId: string): readonly TransitionRecordV1[];\n\t/** Replays a history into a fresh store; the rebuilt state must be identical. */\n\treplay(history: readonly TransitionRecordV1[]): void;\n\tentities(): readonly string[];\n}\n\nexport function createTransitionStore(options: { readonly now?: () => number } = {}): TransitionStoreV1 {\n\tconst now = options.now ?? (() => Date.now());\n\tconst recordsByEntity = new Map<string, TransitionRecordV1[]>();\n\tconst idempotency = new Map<string, TransitionRecordV1>();\n\tconst currentStatus = new Map<string, { status: string; revision: number }>();\n\tconst revisions = new Map<string, number>();\n\n\tconst entityKey = (domain: string, entityId: string): string => `${domain}\\u0000${entityId}`;\n\n\treturn {\n\t\ttransition(input) {\n\t\t\tassertNonEmptyString(input.domain, \"Transition domain\");\n\t\t\tassertNonEmptyString(input.entityId, \"Transition entityId\");\n\t\t\tassertNonEmptyString(input.owner, \"Transition owner\");\n\t\t\tassertNonEmptyString(input.operationId, \"Transition operationId\");\n\t\t\tassertNonEmptyString(input.toStatus, \"Transition toStatus\");\n\t\t\tconst evidence = validateEvidence(input.evidence, \"Transition evidence\");\n\t\t\tconst payload = validatePayload(input.payload, \"Transition payload\");\n\t\t\tconst key = `${entityKey(input.domain, input.entityId)}\\u0000${input.operationId}\\u0000${input.toStatus}`;\n\n\t\t\tconst existing = idempotency.get(key);\n\t\t\tif (existing) return { record: existing, deduped: true };\n\n\t\t\tconst entityKeyId = entityKey(input.domain, input.entityId);\n\t\t\tconst current = currentStatus.get(entityKeyId);\n\t\t\tconst fromStatus = input.fromStatus;\n\t\t\tif ((current?.status ?? null) !== fromStatus) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"Transition conflict: expected fromStatus {from}, got {to}\", {\n\t\t\t\t\t\tfrom: JSON.stringify(current?.status ?? null),\n\t\t\t\t\t\tto: JSON.stringify(fromStatus),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst revision = (revisions.get(entityKeyId) ?? 0) + 1;\n\t\t\trevisions.set(entityKeyId, revision);\n\t\t\tconst record: TransitionRecordV1 = Object.freeze({\n\t\t\t\tdomain: input.domain,\n\t\t\t\tentityId: input.entityId,\n\t\t\t\towner: input.owner,\n\t\t\t\tfromStatus,\n\t\t\t\ttoStatus: input.toStatus,\n\t\t\t\toperationId: input.operationId,\n\t\t\t\tevidence: Object.freeze(evidence),\n\t\t\t\t...(payload === undefined ? {} : { payload }),\n\t\t\t\trevision,\n\t\t\t\tat: input.at ?? now(),\n\t\t\t});\n\t\t\tconst log = recordsByEntity.get(entityKeyId) ?? [];\n\t\t\tlog.push(record);\n\t\t\trecordsByEntity.set(entityKeyId, log);\n\t\t\tcurrentStatus.set(entityKeyId, { status: input.toStatus, revision });\n\t\t\tidempotency.set(key, record);\n\t\t\treturn { record, deduped: false };\n\t\t},\n\t\tcurrentStatus(domain: string, entityId: string) {\n\t\t\treturn currentStatus.get(entityKey(domain, entityId));\n\t\t},\n\t\thistory(entityId: string) {\n\t\t\treturn Object.freeze(\n\t\t\t\t[...recordsByEntity.entries()]\n\t\t\t\t\t.filter(([entityKeyId]) => entityKeyId.endsWith(`\\u0000${entityId}`))\n\t\t\t\t\t.flatMap(([, log]) => log),\n\t\t\t);\n\t\t},\n\t\treplay(history) {\n\t\t\tfor (const record of history) {\n\t\t\t\tthis.transition({\n\t\t\t\t\tdomain: record.domain,\n\t\t\t\t\tentityId: record.entityId,\n\t\t\t\t\towner: record.owner,\n\t\t\t\t\tfromStatus: record.fromStatus,\n\t\t\t\t\ttoStatus: record.toStatus,\n\t\t\t\t\toperationId: record.operationId,\n\t\t\t\t\tevidence: record.evidence,\n\t\t\t\t\tpayload: record.payload,\n\t\t\t\t\tat: record.at,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tentities() {\n\t\t\treturn Object.freeze([...new Set([...currentStatus.keys()].map((key) => key.split(\"\\u0000\")[1]!))]);\n\t\t},\n\t};\n}\n"]}
@@ -1,37 +0,0 @@
1
- import type { TransitionEvidenceRefV1, TransitionStoreV1 } from "./state-store.ts";
2
- export type TodoStatusV1 = "open" | "in_progress" | "completed" | "cancelled";
3
- export interface TodoV1 {
4
- readonly todoId: string;
5
- readonly owner: string;
6
- readonly content: string;
7
- readonly status: TodoStatusV1;
8
- readonly evidence: readonly TransitionEvidenceRefV1[];
9
- readonly revision: number;
10
- }
11
- export interface TodoPluginV1 {
12
- createTodo(input: {
13
- readonly todoId: string;
14
- readonly owner: string;
15
- readonly content: string;
16
- }): TodoV1;
17
- start(todoId: string, owner: string, options?: {
18
- readonly operationId?: string;
19
- }): TodoV1;
20
- /** Rewrites the content while open/in_progress; the ledger keeps every version replayable. */
21
- update(todoId: string, owner: string, content: string, options?: {
22
- readonly operationId?: string;
23
- }): TodoV1;
24
- complete(todoId: string, owner: string, evidence?: readonly TransitionEvidenceRefV1[], options?: {
25
- readonly operationId?: string;
26
- }): TodoV1;
27
- cancel(todoId: string, owner: string, reason?: string): TodoV1;
28
- /** Restarts a cancelled todo back into open. */
29
- reopen(todoId: string, owner: string, options?: {
30
- readonly operationId?: string;
31
- }): TodoV1;
32
- get(todoId: string, owner: string): TodoV1 | undefined;
33
- }
34
- export declare function createTodoPlugin(options: {
35
- readonly transitions: TransitionStoreV1;
36
- }): TodoPluginV1;
37
- //# sourceMappingURL=todo-plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"todo-plugin.d.ts","sourceRoot":"","sources":["../../src/profile/todo-plugin.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,CAAC;AAE9E,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,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,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACzG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC1F,8FAA8F;IAC9F,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC5G,QAAQ,CACP,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,SAAS,uBAAuB,EAAE,EAC7C,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,MAAM,CAAC;IACV,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/D,gDAAgD;IAChD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IAC3F,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACvD;AAyBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,GAAG,YAAY,CA6InG","sourcesContent":["/**\n * Todo plugin (2.3d) — Todo content lifecycle with cancellation and restart\n * over the 2.3a transition store.\n *\n * Status graph: null → open → in_progress → completed; open/in_progress →\n * cancelled; cancelled → open (restart). Content updates rewrite the payload\n * in place while the todo is open/in_progress, and every terminal transition\n * stays replayable from the ledger. The plugin holds NO shadow state —\n * content and status are always derived from the transition history.\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 TodoStatusV1 = \"open\" | \"in_progress\" | \"completed\" | \"cancelled\";\n\nexport interface TodoV1 {\n\treadonly todoId: string;\n\treadonly owner: string;\n\treadonly content: string;\n\treadonly status: TodoStatusV1;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revision: number;\n}\n\nexport interface TodoPluginV1 {\n\tcreateTodo(input: { readonly todoId: string; readonly owner: string; readonly content: string }): TodoV1;\n\tstart(todoId: string, owner: string, options?: { readonly operationId?: string }): TodoV1;\n\t/** Rewrites the content while open/in_progress; the ledger keeps every version replayable. */\n\tupdate(todoId: string, owner: string, content: string, options?: { readonly operationId?: string }): TodoV1;\n\tcomplete(\n\t\ttodoId: string,\n\t\towner: string,\n\t\tevidence?: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly operationId?: string },\n\t): TodoV1;\n\tcancel(todoId: string, owner: string, reason?: string): TodoV1;\n\t/** Restarts a cancelled todo back into open. */\n\treopen(todoId: string, owner: string, options?: { readonly operationId?: string }): TodoV1;\n\tget(todoId: string, owner: string): TodoV1 | undefined;\n}\n\ninterface TodoPayloadV1 {\n\treadonly content: string;\n\treadonly cancelReason?: string;\n}\n\n/** FNV-1a 32-bit — a short deterministic operationId suffix for content updates. */\nfunction contentHash(content: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let index = 0; index < content.length; index += 1) {\n\t\thash ^= content.charCodeAt(index);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn (hash >>> 0).toString(36);\n}\n\nfunction todoPayloadOf(record: { readonly payload?: unknown }): Partial<TodoPayloadV1> | undefined {\n\tconst payload = record.payload;\n\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\treturn payload as Partial<TodoPayloadV1>;\n\t}\n\treturn undefined;\n}\n\nexport function createTodoPlugin(options: { readonly transitions: TransitionStoreV1 }): TodoPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst todoHistory = (todoId: string, owner: string) =>\n\t\ttransitions.history(todoId).filter((record) => record.domain === \"todo\" && record.owner === owner);\n\n\tconst current = (todoId: string, owner: string): { status: TodoStatusV1; revision: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"todo\", todoId);\n\t\tif (state === undefined || todoHistory(todoId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as TodoStatusV1, revision: state.revision };\n\t};\n\n\tconst todoGet = (todoId: string, owner: string): TodoV1 | undefined => {\n\t\tconst state = current(todoId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = todoHistory(todoId, owner);\n\t\tconst content = [...history]\n\t\t\t.reverse()\n\t\t\t.map((record) => todoPayloadOf(record)?.content)\n\t\t\t.find((value): value is string => typeof value === \"string\");\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\ttodoId,\n\t\t\towner,\n\t\t\t// v8 ignore next -- createTodo 恒写入 content payload,标题扫描必命中\n\t\t\tcontent: content ?? \"\",\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 replayedTodo = (\n\t\ttodoId: string,\n\t\towner: string,\n\t\ttoStatus: TodoStatusV1,\n\t\toperationId?: string,\n\t): TodoV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = todoHistory(todoId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? todoGet(todoId, owner) : undefined;\n\t};\n\n\tconst transitionTodo = (\n\t\ttodoId: string,\n\t\towner: string,\n\t\tfromStatus: TodoStatusV1 | null,\n\t\ttoStatus: TodoStatusV1,\n\t\ttodoOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: TodoPayloadV1;\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): TodoV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"todo\",\n\t\t\tentityId: todoId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: todoOptions.operationId ?? `${toStatus}:${todoId}`,\n\t\t\tevidence: todoOptions.evidence,\n\t\t\tpayload: todoOptions.payload as JsonValue | undefined,\n\t\t});\n\t\treturn todoGet(todoId, owner)!;\n\t};\n\n\tconst requireStatus = (todoId: string, owner: string, allowed: readonly TodoStatusV1[]): TodoV1 => {\n\t\tconst todo = todoGet(todoId, owner);\n\t\tif (!todo || !allowed.includes(todo.status)) {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Todo {todoId} must be {allowed} (current: {status})\", {\n\t\t\t\t\ttodoId,\n\t\t\t\t\tallowed: allowed.join(\"/\"),\n\t\t\t\t\tstatus: todo?.status ?? \"missing\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn todo;\n\t};\n\n\treturn {\n\t\tcreateTodo({ todoId, owner, content }) {\n\t\t\tif (content.trim().length === 0) throw new Error(t(\"Todo content must be a non-empty string\"));\n\t\t\treturn transitionTodo(todoId, owner, null, \"open\", {\n\t\t\t\tpayload: { content },\n\t\t\t\toperationId: `create:${todoId}`,\n\t\t\t});\n\t\t},\n\t\tstart(todoId, owner, startOptions) {\n\t\t\tconst replayed = replayedTodo(todoId, owner, \"in_progress\", startOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\trequireStatus(todoId, owner, [\"open\"]);\n\t\t\treturn transitionTodo(todoId, owner, \"open\", \"in_progress\", { operationId: startOptions?.operationId });\n\t\t},\n\t\tupdate(todoId, owner, content, updateOptions) {\n\t\t\tif (content.trim().length === 0) throw new Error(t(\"Todo content must be a non-empty string\"));\n\t\t\tconst replayed = replayedTodo(\n\t\t\t\ttodoId,\n\t\t\t\towner,\n\t\t\t\ttodoGet(todoId, owner)?.status ?? \"open\",\n\t\t\t\tupdateOptions?.operationId,\n\t\t\t);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst todo = requireStatus(todoId, owner, [\"open\", \"in_progress\"]);\n\t\t\treturn transitionTodo(todoId, owner, todo.status, todo.status, {\n\t\t\t\tpayload: { content },\n\t\t\t\toperationId: updateOptions?.operationId ?? `update:${todoId}:${contentHash(content)}`,\n\t\t\t});\n\t\t},\n\t\tcomplete(todoId, owner, evidence, completeOptions) {\n\t\t\tconst replayed = replayedTodo(todoId, owner, \"completed\", completeOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\trequireStatus(todoId, owner, [\"in_progress\"]);\n\t\t\treturn transitionTodo(todoId, owner, \"in_progress\", \"completed\", {\n\t\t\t\tevidence: evidence ?? [],\n\t\t\t\toperationId: completeOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tcancel(todoId, owner, reason) {\n\t\t\tconst todo = requireStatus(todoId, owner, [\"open\", \"in_progress\"]);\n\t\t\tif (reason !== undefined && reason.trim().length === 0) {\n\t\t\t\tthrow new Error(t(\"Todo cancel reason must be a non-empty string when provided\"));\n\t\t\t}\n\t\t\treturn transitionTodo(todoId, owner, todo.status, \"cancelled\", {\n\t\t\t\t...(reason === undefined ? {} : { payload: { content: todo.content, cancelReason: reason } }),\n\t\t\t});\n\t\t},\n\t\treopen(todoId, owner, reopenOptions) {\n\t\t\tconst replayed = replayedTodo(todoId, owner, \"open\", reopenOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\trequireStatus(todoId, owner, [\"cancelled\"]);\n\t\t\treturn transitionTodo(todoId, owner, \"cancelled\", \"open\", { operationId: reopenOptions?.operationId });\n\t\t},\n\t\tget(todoId, owner) {\n\t\t\treturn todoGet(todoId, owner);\n\t\t},\n\t};\n}\n"]}
@@ -1,137 +0,0 @@
1
- import { t } from "../core/i18n/index.js";
2
- /** FNV-1a 32-bit — a short deterministic operationId suffix for content updates. */
3
- function contentHash(content) {
4
- let hash = 0x811c9dc5;
5
- for (let index = 0; index < content.length; index += 1) {
6
- hash ^= content.charCodeAt(index);
7
- hash = Math.imul(hash, 0x01000193);
8
- }
9
- return (hash >>> 0).toString(36);
10
- }
11
- function todoPayloadOf(record) {
12
- const payload = record.payload;
13
- if (payload !== null && typeof payload === "object" && !Array.isArray(payload)) {
14
- return payload;
15
- }
16
- return undefined;
17
- }
18
- export function createTodoPlugin(options) {
19
- const transitions = options.transitions;
20
- const todoHistory = (todoId, owner) => transitions.history(todoId).filter((record) => record.domain === "todo" && record.owner === owner);
21
- const current = (todoId, owner) => {
22
- const state = transitions.currentStatus("todo", todoId);
23
- if (state === undefined || todoHistory(todoId, owner).length === 0)
24
- return undefined;
25
- return { status: state.status, revision: state.revision };
26
- };
27
- const todoGet = (todoId, owner) => {
28
- const state = current(todoId, owner);
29
- if (!state)
30
- return undefined;
31
- const history = todoHistory(todoId, owner);
32
- const content = [...history]
33
- .reverse()
34
- .map((record) => todoPayloadOf(record)?.content)
35
- .find((value) => typeof value === "string");
36
- const lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];
37
- return {
38
- todoId,
39
- owner,
40
- // v8 ignore next -- createTodo 恒写入 content payload,标题扫描必命中
41
- content: content ?? "",
42
- status: state.status,
43
- evidence: lastEvidence,
44
- revision: state.revision,
45
- };
46
- };
47
- /** A retried transition with an already-applied operationId returns the existing state instead of conflicting. */
48
- const replayedTodo = (todoId, owner, toStatus, operationId) => {
49
- if (!operationId)
50
- return undefined;
51
- const applied = todoHistory(todoId, owner).some((record) => record.toStatus === toStatus && record.operationId === operationId);
52
- return applied ? todoGet(todoId, owner) : undefined;
53
- };
54
- const transitionTodo = (todoId, owner, fromStatus, toStatus, todoOptions) => {
55
- transitions.transition({
56
- domain: "todo",
57
- entityId: todoId,
58
- owner,
59
- fromStatus,
60
- toStatus,
61
- operationId: todoOptions.operationId ?? `${toStatus}:${todoId}`,
62
- evidence: todoOptions.evidence,
63
- payload: todoOptions.payload,
64
- });
65
- return todoGet(todoId, owner);
66
- };
67
- const requireStatus = (todoId, owner, allowed) => {
68
- const todo = todoGet(todoId, owner);
69
- if (!todo || !allowed.includes(todo.status)) {
70
- throw new Error(t("Todo {todoId} must be {allowed} (current: {status})", {
71
- todoId,
72
- allowed: allowed.join("/"),
73
- status: todo?.status ?? "missing",
74
- }));
75
- }
76
- return todo;
77
- };
78
- return {
79
- createTodo({ todoId, owner, content }) {
80
- if (content.trim().length === 0)
81
- throw new Error(t("Todo content must be a non-empty string"));
82
- return transitionTodo(todoId, owner, null, "open", {
83
- payload: { content },
84
- operationId: `create:${todoId}`,
85
- });
86
- },
87
- start(todoId, owner, startOptions) {
88
- const replayed = replayedTodo(todoId, owner, "in_progress", startOptions?.operationId);
89
- if (replayed)
90
- return replayed;
91
- requireStatus(todoId, owner, ["open"]);
92
- return transitionTodo(todoId, owner, "open", "in_progress", { operationId: startOptions?.operationId });
93
- },
94
- update(todoId, owner, content, updateOptions) {
95
- if (content.trim().length === 0)
96
- throw new Error(t("Todo content must be a non-empty string"));
97
- const replayed = replayedTodo(todoId, owner, todoGet(todoId, owner)?.status ?? "open", updateOptions?.operationId);
98
- if (replayed)
99
- return replayed;
100
- const todo = requireStatus(todoId, owner, ["open", "in_progress"]);
101
- return transitionTodo(todoId, owner, todo.status, todo.status, {
102
- payload: { content },
103
- operationId: updateOptions?.operationId ?? `update:${todoId}:${contentHash(content)}`,
104
- });
105
- },
106
- complete(todoId, owner, evidence, completeOptions) {
107
- const replayed = replayedTodo(todoId, owner, "completed", completeOptions?.operationId);
108
- if (replayed)
109
- return replayed;
110
- requireStatus(todoId, owner, ["in_progress"]);
111
- return transitionTodo(todoId, owner, "in_progress", "completed", {
112
- evidence: evidence ?? [],
113
- operationId: completeOptions?.operationId,
114
- });
115
- },
116
- cancel(todoId, owner, reason) {
117
- const todo = requireStatus(todoId, owner, ["open", "in_progress"]);
118
- if (reason !== undefined && reason.trim().length === 0) {
119
- throw new Error(t("Todo cancel reason must be a non-empty string when provided"));
120
- }
121
- return transitionTodo(todoId, owner, todo.status, "cancelled", {
122
- ...(reason === undefined ? {} : { payload: { content: todo.content, cancelReason: reason } }),
123
- });
124
- },
125
- reopen(todoId, owner, reopenOptions) {
126
- const replayed = replayedTodo(todoId, owner, "open", reopenOptions?.operationId);
127
- if (replayed)
128
- return replayed;
129
- requireStatus(todoId, owner, ["cancelled"]);
130
- return transitionTodo(todoId, owner, "cancelled", "open", { operationId: reopenOptions?.operationId });
131
- },
132
- get(todoId, owner) {
133
- return todoGet(todoId, owner);
134
- },
135
- };
136
- }
137
- //# sourceMappingURL=todo-plugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"todo-plugin.js","sourceRoot":"","sources":["../../src/profile/todo-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Todo plugin (2.3d) — Todo content lifecycle with cancellation and restart\n * over the 2.3a transition store.\n *\n * Status graph: null → open → in_progress → completed; open/in_progress →\n * cancelled; cancelled → open (restart). Content updates rewrite the payload\n * in place while the todo is open/in_progress, and every terminal transition\n * stays replayable from the ledger. The plugin holds NO shadow state —\n * content and status are always derived from the transition history.\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 TodoStatusV1 = \"open\" | \"in_progress\" | \"completed\" | \"cancelled\";\n\nexport interface TodoV1 {\n\treadonly todoId: string;\n\treadonly owner: string;\n\treadonly content: string;\n\treadonly status: TodoStatusV1;\n\treadonly evidence: readonly TransitionEvidenceRefV1[];\n\treadonly revision: number;\n}\n\nexport interface TodoPluginV1 {\n\tcreateTodo(input: { readonly todoId: string; readonly owner: string; readonly content: string }): TodoV1;\n\tstart(todoId: string, owner: string, options?: { readonly operationId?: string }): TodoV1;\n\t/** Rewrites the content while open/in_progress; the ledger keeps every version replayable. */\n\tupdate(todoId: string, owner: string, content: string, options?: { readonly operationId?: string }): TodoV1;\n\tcomplete(\n\t\ttodoId: string,\n\t\towner: string,\n\t\tevidence?: readonly TransitionEvidenceRefV1[],\n\t\toptions?: { readonly operationId?: string },\n\t): TodoV1;\n\tcancel(todoId: string, owner: string, reason?: string): TodoV1;\n\t/** Restarts a cancelled todo back into open. */\n\treopen(todoId: string, owner: string, options?: { readonly operationId?: string }): TodoV1;\n\tget(todoId: string, owner: string): TodoV1 | undefined;\n}\n\ninterface TodoPayloadV1 {\n\treadonly content: string;\n\treadonly cancelReason?: string;\n}\n\n/** FNV-1a 32-bit — a short deterministic operationId suffix for content updates. */\nfunction contentHash(content: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let index = 0; index < content.length; index += 1) {\n\t\thash ^= content.charCodeAt(index);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn (hash >>> 0).toString(36);\n}\n\nfunction todoPayloadOf(record: { readonly payload?: unknown }): Partial<TodoPayloadV1> | undefined {\n\tconst payload = record.payload;\n\tif (payload !== null && typeof payload === \"object\" && !Array.isArray(payload)) {\n\t\treturn payload as Partial<TodoPayloadV1>;\n\t}\n\treturn undefined;\n}\n\nexport function createTodoPlugin(options: { readonly transitions: TransitionStoreV1 }): TodoPluginV1 {\n\tconst transitions = options.transitions;\n\n\tconst todoHistory = (todoId: string, owner: string) =>\n\t\ttransitions.history(todoId).filter((record) => record.domain === \"todo\" && record.owner === owner);\n\n\tconst current = (todoId: string, owner: string): { status: TodoStatusV1; revision: number } | undefined => {\n\t\tconst state = transitions.currentStatus(\"todo\", todoId);\n\t\tif (state === undefined || todoHistory(todoId, owner).length === 0) return undefined;\n\t\treturn { status: state.status as TodoStatusV1, revision: state.revision };\n\t};\n\n\tconst todoGet = (todoId: string, owner: string): TodoV1 | undefined => {\n\t\tconst state = current(todoId, owner);\n\t\tif (!state) return undefined;\n\t\tconst history = todoHistory(todoId, owner);\n\t\tconst content = [...history]\n\t\t\t.reverse()\n\t\t\t.map((record) => todoPayloadOf(record)?.content)\n\t\t\t.find((value): value is string => typeof value === \"string\");\n\t\tconst lastEvidence = [...history].reverse().find((record) => record.evidence.length > 0)?.evidence ?? [];\n\t\treturn {\n\t\t\ttodoId,\n\t\t\towner,\n\t\t\t// v8 ignore next -- createTodo 恒写入 content payload,标题扫描必命中\n\t\t\tcontent: content ?? \"\",\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 replayedTodo = (\n\t\ttodoId: string,\n\t\towner: string,\n\t\ttoStatus: TodoStatusV1,\n\t\toperationId?: string,\n\t): TodoV1 | undefined => {\n\t\tif (!operationId) return undefined;\n\t\tconst applied = todoHistory(todoId, owner).some(\n\t\t\t(record) => record.toStatus === toStatus && record.operationId === operationId,\n\t\t);\n\t\treturn applied ? todoGet(todoId, owner) : undefined;\n\t};\n\n\tconst transitionTodo = (\n\t\ttodoId: string,\n\t\towner: string,\n\t\tfromStatus: TodoStatusV1 | null,\n\t\ttoStatus: TodoStatusV1,\n\t\ttodoOptions: {\n\t\t\treadonly evidence?: readonly TransitionEvidenceRefV1[];\n\t\t\treadonly payload?: TodoPayloadV1;\n\t\t\treadonly operationId?: string;\n\t\t},\n\t): TodoV1 => {\n\t\ttransitions.transition({\n\t\t\tdomain: \"todo\",\n\t\t\tentityId: todoId,\n\t\t\towner,\n\t\t\tfromStatus,\n\t\t\ttoStatus,\n\t\t\toperationId: todoOptions.operationId ?? `${toStatus}:${todoId}`,\n\t\t\tevidence: todoOptions.evidence,\n\t\t\tpayload: todoOptions.payload as JsonValue | undefined,\n\t\t});\n\t\treturn todoGet(todoId, owner)!;\n\t};\n\n\tconst requireStatus = (todoId: string, owner: string, allowed: readonly TodoStatusV1[]): TodoV1 => {\n\t\tconst todo = todoGet(todoId, owner);\n\t\tif (!todo || !allowed.includes(todo.status)) {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"Todo {todoId} must be {allowed} (current: {status})\", {\n\t\t\t\t\ttodoId,\n\t\t\t\t\tallowed: allowed.join(\"/\"),\n\t\t\t\t\tstatus: todo?.status ?? \"missing\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn todo;\n\t};\n\n\treturn {\n\t\tcreateTodo({ todoId, owner, content }) {\n\t\t\tif (content.trim().length === 0) throw new Error(t(\"Todo content must be a non-empty string\"));\n\t\t\treturn transitionTodo(todoId, owner, null, \"open\", {\n\t\t\t\tpayload: { content },\n\t\t\t\toperationId: `create:${todoId}`,\n\t\t\t});\n\t\t},\n\t\tstart(todoId, owner, startOptions) {\n\t\t\tconst replayed = replayedTodo(todoId, owner, \"in_progress\", startOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\trequireStatus(todoId, owner, [\"open\"]);\n\t\t\treturn transitionTodo(todoId, owner, \"open\", \"in_progress\", { operationId: startOptions?.operationId });\n\t\t},\n\t\tupdate(todoId, owner, content, updateOptions) {\n\t\t\tif (content.trim().length === 0) throw new Error(t(\"Todo content must be a non-empty string\"));\n\t\t\tconst replayed = replayedTodo(\n\t\t\t\ttodoId,\n\t\t\t\towner,\n\t\t\t\ttodoGet(todoId, owner)?.status ?? \"open\",\n\t\t\t\tupdateOptions?.operationId,\n\t\t\t);\n\t\t\tif (replayed) return replayed;\n\t\t\tconst todo = requireStatus(todoId, owner, [\"open\", \"in_progress\"]);\n\t\t\treturn transitionTodo(todoId, owner, todo.status, todo.status, {\n\t\t\t\tpayload: { content },\n\t\t\t\toperationId: updateOptions?.operationId ?? `update:${todoId}:${contentHash(content)}`,\n\t\t\t});\n\t\t},\n\t\tcomplete(todoId, owner, evidence, completeOptions) {\n\t\t\tconst replayed = replayedTodo(todoId, owner, \"completed\", completeOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\trequireStatus(todoId, owner, [\"in_progress\"]);\n\t\t\treturn transitionTodo(todoId, owner, \"in_progress\", \"completed\", {\n\t\t\t\tevidence: evidence ?? [],\n\t\t\t\toperationId: completeOptions?.operationId,\n\t\t\t});\n\t\t},\n\t\tcancel(todoId, owner, reason) {\n\t\t\tconst todo = requireStatus(todoId, owner, [\"open\", \"in_progress\"]);\n\t\t\tif (reason !== undefined && reason.trim().length === 0) {\n\t\t\t\tthrow new Error(t(\"Todo cancel reason must be a non-empty string when provided\"));\n\t\t\t}\n\t\t\treturn transitionTodo(todoId, owner, todo.status, \"cancelled\", {\n\t\t\t\t...(reason === undefined ? {} : { payload: { content: todo.content, cancelReason: reason } }),\n\t\t\t});\n\t\t},\n\t\treopen(todoId, owner, reopenOptions) {\n\t\t\tconst replayed = replayedTodo(todoId, owner, \"open\", reopenOptions?.operationId);\n\t\t\tif (replayed) return replayed;\n\t\t\trequireStatus(todoId, owner, [\"cancelled\"]);\n\t\t\treturn transitionTodo(todoId, owner, \"cancelled\", \"open\", { operationId: reopenOptions?.operationId });\n\t\t},\n\t\tget(todoId, owner) {\n\t\t\treturn todoGet(todoId, owner);\n\t\t},\n\t};\n}\n"]}
@@ -1,64 +0,0 @@
1
- /**
2
- * MCP-like JSON-RPC 2.0 stdio bridge.
3
- *
4
- * This is intentionally an external capability plugin. It discovers tools
5
- * from one explicitly configured process and exposes them through the public
6
- * CapabilityAPI; policy, approval, and server selection remain host-owned.
7
- */
8
- import type { CapabilityAPI, CapabilityPluginFactory } from "@agent-forge/coding-agent";
9
- export type McpErrorReason = "spawn" | "exit" | "malformed" | "protocol" | "remote" | "aborted" | "timed_out";
10
- export interface McpErrorDetails {
11
- method?: string;
12
- requestId?: number;
13
- stdout?: string;
14
- stderr?: string;
15
- exitCode?: number | null;
16
- data?: unknown;
17
- }
18
- export declare class McpStdioError extends Error {
19
- readonly reason: McpErrorReason;
20
- readonly details: McpErrorDetails;
21
- constructor(message: string, reason: McpErrorReason, details?: McpErrorDetails);
22
- }
23
- export interface McpRequestOptions {
24
- signal?: AbortSignal;
25
- timeoutMs?: number;
26
- }
27
- export interface McpStdioTransport {
28
- request(method: string, params?: unknown, options?: McpRequestOptions): Promise<unknown>;
29
- notify(method: string, params?: unknown): Promise<void> | void;
30
- close(): Promise<void> | void;
31
- readonly stderr?: string;
32
- }
33
- export interface McpStdioTransportConfig {
34
- command: string;
35
- args: readonly string[];
36
- cwd?: string;
37
- env?: NodeJS.ProcessEnv;
38
- onStderr?: (text: string) => void;
39
- }
40
- export type McpStdioTransportFactory = (config: McpStdioTransportConfig) => McpStdioTransport | Promise<McpStdioTransport>;
41
- export interface McpStdioPluginOptions {
42
- command: string;
43
- args?: readonly string[];
44
- cwd?: string;
45
- env?: NodeJS.ProcessEnv;
46
- timeoutMs?: number;
47
- transportFactory?: McpStdioTransportFactory;
48
- onStderr?: (text: string) => void;
49
- protocolVersion?: string;
50
- clientInfo?: {
51
- name: string;
52
- version: string;
53
- };
54
- }
55
- /** Default line-delimited stdio transport. The command is never passed to a shell. */
56
- export declare function spawnMcpTransport(config: McpStdioTransportConfig): McpStdioTransport;
57
- /** Create an asynchronously discovering MCP stdio capability plugin. */
58
- export declare function createMcpStdioPlugin(options: McpStdioPluginOptions): CapabilityPluginFactory;
59
- /**
60
- * Template entry only. A working MCP transport needs a user-selected command,
61
- * arguments, and timeout; never launch an unconfigured child process.
62
- */
63
- export default function mcpStdioExample(_api: CapabilityAPI): never;
64
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EACX,aAAa,EACb,uBAAuB,EAGvB,MAAM,2BAA2B,CAAC;AAGnC,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAE9G,MAAM,WAAW,eAAe;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,qBAAa,aAAc,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAElC,YAAY,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,eAAoB,EAKjF;CACD;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzF,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,MAAM,wBAAwB,GAAG,CACtC,MAAM,EAAE,uBAAuB,KAC3B,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEpD,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AA+CD,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,iBAAiB,CAQpF;AAmLD,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,uBAAuB,CA0D5F;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,KAAK,CAIlE","sourcesContent":["/**\n * MCP-like JSON-RPC 2.0 stdio bridge.\n *\n * This is intentionally an external capability plugin. It discovers tools\n * from one explicitly configured process and exposes them through the public\n * CapabilityAPI; policy, approval, and server selection remain host-owned.\n */\n\nimport type { ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { spawn } from \"node:child_process\";\nimport type { AgentToolResult } from \"@agent-forge/agent-core\";\nimport type {\n\tCapabilityAPI,\n\tCapabilityPluginFactory,\n\tExtensionContext,\n\tToolDefinition,\n} from \"@agent-forge/coding-agent\";\nimport { Type } from \"typebox\";\n\nexport type McpErrorReason = \"spawn\" | \"exit\" | \"malformed\" | \"protocol\" | \"remote\" | \"aborted\" | \"timed_out\";\n\nexport interface McpErrorDetails {\n\tmethod?: string;\n\trequestId?: number;\n\tstdout?: string;\n\tstderr?: string;\n\texitCode?: number | null;\n\tdata?: unknown;\n}\n\nexport class McpStdioError extends Error {\n\treadonly reason: McpErrorReason;\n\treadonly details: McpErrorDetails;\n\n\tconstructor(message: string, reason: McpErrorReason, details: McpErrorDetails = {}) {\n\t\tsuper(message);\n\t\tthis.name = \"McpStdioError\";\n\t\tthis.reason = reason;\n\t\tthis.details = details;\n\t}\n}\n\nexport interface McpRequestOptions {\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n}\n\nexport interface McpStdioTransport {\n\trequest(method: string, params?: unknown, options?: McpRequestOptions): Promise<unknown>;\n\tnotify(method: string, params?: unknown): Promise<void> | void;\n\tclose(): Promise<void> | void;\n\treadonly stderr?: string;\n}\n\nexport interface McpStdioTransportConfig {\n\tcommand: string;\n\targs: readonly string[];\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\tonStderr?: (text: string) => void;\n}\n\nexport type McpStdioTransportFactory = (\n\tconfig: McpStdioTransportConfig,\n) => McpStdioTransport | Promise<McpStdioTransport>;\n\nexport interface McpStdioPluginOptions {\n\tcommand: string;\n\targs?: readonly string[];\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\ttimeoutMs?: number;\n\ttransportFactory?: McpStdioTransportFactory;\n\tonStderr?: (text: string) => void;\n\tprotocolVersion?: string;\n\tclientInfo?: { name: string; version: string };\n}\n\ninterface JsonRpcResponse {\n\tjsonrpc?: unknown;\n\tid?: unknown;\n\tmethod?: unknown;\n\tresult?: unknown;\n\terror?: { code?: unknown; message?: unknown; data?: unknown };\n}\n\nconst noResult = (): AgentToolResult<unknown> => ({ content: [{ type: \"text\", text: \"\" }], details: undefined });\n\nfunction asRecord(value: unknown, label: string): Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new McpStdioError(`${label} must be an object`, \"malformed\", { data: value });\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction validateToolList(value: unknown): readonly Record<string, unknown>[] {\n\tconst object = asRecord(value, \"MCP tools/list result\");\n\tif (!Array.isArray(object.tools)) {\n\t\tthrow new McpStdioError(\"MCP tools/list result must contain tools[]\", \"malformed\", { data: value });\n\t}\n\treturn object.tools.map((tool, index) => {\n\t\tconst record = asRecord(tool, `MCP tool at index ${index}`);\n\t\tif (typeof record.name !== \"string\" || !record.name.trim()) {\n\t\t\tthrow new McpStdioError(`MCP tool at index ${index} requires a non-empty name`, \"malformed\", { data: tool });\n\t\t}\n\t\tif (record.inputSchema !== undefined && (typeof record.inputSchema !== \"object\" || record.inputSchema === null)) {\n\t\t\tthrow new McpStdioError(`MCP tool has an invalid inputSchema: ${record.name}`, \"malformed\", { data: tool });\n\t\t}\n\t\treturn record;\n\t});\n}\n\nfunction normalizeToolResult(value: unknown): AgentToolResult<unknown> {\n\tif (value && typeof value === \"object\" && Array.isArray((value as { content?: unknown }).content)) {\n\t\treturn value as AgentToolResult<unknown>;\n\t}\n\tif (value === undefined) return noResult();\n\treturn {\n\t\tcontent: [{ type: \"text\", text: typeof value === \"string\" ? value : JSON.stringify(value) }],\n\t\tdetails: undefined,\n\t};\n}\n\n/** Default line-delimited stdio transport. The command is never passed to a shell. */\nexport function spawnMcpTransport(config: McpStdioTransportConfig): McpStdioTransport {\n\tconst child = spawn(config.command, [...config.args], {\n\t\tcwd: config.cwd,\n\t\tenv: config.env,\n\t\tshell: false,\n\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t});\n\treturn createJsonRpcTransport(child, config.onStderr);\n}\n\nfunction createJsonRpcTransport(\n\tchild: ChildProcessWithoutNullStreams,\n\tonStderr?: (text: string) => void,\n): McpStdioTransport {\n\tlet nextId = 1;\n\tlet closed = false;\n\tlet stdout = \"\";\n\tlet stderr = \"\";\n\tlet closeRequested = false;\n\tconst pending = new Map<\n\t\tnumber,\n\t\t{ method: string; resolve: (value: unknown) => void; reject: (error: unknown) => void }\n\t>();\n\tconst ignored = new Set<number>();\n\tlet fatalError: McpStdioError | undefined;\n\n\tconst failAll = (error: McpStdioError): void => {\n\t\tif (fatalError) return;\n\t\tfatalError = error;\n\t\tclosed = true;\n\t\tfor (const request of pending.values()) request.reject(error);\n\t\tpending.clear();\n\t};\n\n\tchild.stdout.setEncoding(\"utf8\");\n\tchild.stdout.on(\"data\", (chunk: string) => {\n\t\tstdout += chunk;\n\t\tconst lines = stdout.split(/\\r?\\n/);\n\t\tstdout = lines.pop() ?? \"\";\n\t\tfor (const line of lines) {\n\t\t\tif (!line.trim()) continue;\n\t\t\tlet parsed: JsonRpcResponse;\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(line) as JsonRpcResponse;\n\t\t\t} catch {\n\t\t\t\tfailAll(new McpStdioError(\"Malformed JSON-RPC response\", \"malformed\", { stdout: line, stderr }));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (parsed.jsonrpc !== \"2.0\") {\n\t\t\t\tfailAll(new McpStdioError(\"Invalid JSON-RPC response envelope\", \"protocol\", { data: parsed, stderr }));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (parsed.id === undefined) {\n\t\t\t\tif (typeof parsed.method === \"string\") continue;\n\t\t\t\tfailAll(new McpStdioError(\"JSON-RPC response is missing an id\", \"protocol\", { data: parsed, stderr }));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (typeof parsed.id !== \"number\" && parsed.id !== null) {\n\t\t\t\tfailAll(new McpStdioError(\"Invalid JSON-RPC response id\", \"protocol\", { data: parsed, stderr }));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (parsed.id === null) continue;\n\t\t\tconst responseId = parsed.id as number;\n\t\t\tif (ignored.delete(responseId)) continue;\n\t\t\tconst request = pending.get(responseId);\n\t\t\tif (!request) {\n\t\t\t\tfailAll(\n\t\t\t\t\tnew McpStdioError(`Unknown JSON-RPC response id: ${responseId}`, \"protocol\", { data: parsed, stderr }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpending.delete(responseId);\n\t\t\tif (parsed.error) {\n\t\t\t\tconst message = typeof parsed.error.message === \"string\" ? parsed.error.message : \"MCP request failed\";\n\t\t\t\trequest.reject(\n\t\t\t\t\tnew McpStdioError(message, \"remote\", {\n\t\t\t\t\t\tmethod: request.method,\n\t\t\t\t\t\trequestId: responseId,\n\t\t\t\t\t\tdata: parsed.error,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (!(\"result\" in parsed)) {\n\t\t\t\trequest.reject(\n\t\t\t\t\tnew McpStdioError(\"JSON-RPC response has neither result nor error\", \"protocol\", {\n\t\t\t\t\t\tmethod: request.method,\n\t\t\t\t\t\trequestId: responseId,\n\t\t\t\t\t\tdata: parsed,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\trequest.resolve(parsed.result);\n\t\t\t}\n\t\t}\n\t});\n\tchild.stderr.setEncoding(\"utf8\");\n\tchild.stderr.on(\"data\", (chunk: string) => {\n\t\tstderr += chunk;\n\t\tonStderr?.(chunk);\n\t});\n\tchild.once(\"error\", (error) => {\n\t\tfailAll(new McpStdioError(`Failed to spawn MCP process: ${error.message}`, \"spawn\", { stdout, stderr }));\n\t});\n\tchild.once(\"close\", (exitCode) => {\n\t\tif (closed) return;\n\t\tfailAll(\n\t\t\tnew McpStdioError(`MCP process exited with code ${exitCode ?? \"unknown\"}`, \"exit\", {\n\t\t\t\tstdout,\n\t\t\t\tstderr,\n\t\t\t\texitCode,\n\t\t\t}),\n\t\t);\n\t});\n\n\tconst request = (method: string, params?: unknown, options: McpRequestOptions = {}): Promise<unknown> => {\n\t\tif (fatalError || closed)\n\t\t\treturn Promise.reject(fatalError ?? new McpStdioError(\"MCP transport is closed\", \"exit\", { stderr }));\n\t\tif (options.signal?.aborted)\n\t\t\treturn Promise.reject(new McpStdioError(\"MCP request aborted\", \"aborted\", { method, stderr }));\n\t\tconst id = nextId++;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet timeout: NodeJS.Timeout | undefined;\n\t\t\tconst abort = (): void => {\n\t\t\t\tif (!pending.delete(id)) return;\n\t\t\t\tignored.add(id);\n\t\t\t\tif (timeout) clearTimeout(timeout);\n\t\t\t\treject(new McpStdioError(\"MCP request aborted\", \"aborted\", { method, requestId: id, stderr }));\n\t\t\t};\n\t\t\tpending.set(id, {\n\t\t\t\tmethod,\n\t\t\t\tresolve: (result) => {\n\t\t\t\t\tif (timeout) clearTimeout(timeout);\n\t\t\t\t\toptions.signal?.removeEventListener(\"abort\", abort);\n\t\t\t\t\tresolve(result);\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tif (timeout) clearTimeout(timeout);\n\t\t\t\t\toptions.signal?.removeEventListener(\"abort\", abort);\n\t\t\t\t\treject(error);\n\t\t\t\t},\n\t\t\t});\n\t\t\toptions.signal?.addEventListener(\"abort\", abort, { once: true });\n\t\t\tif (options.timeoutMs !== undefined) {\n\t\t\t\ttimeout = setTimeout(() => {\n\t\t\t\t\tif (!pending.delete(id)) return;\n\t\t\t\t\tignored.add(id);\n\t\t\t\t\toptions.signal?.removeEventListener(\"abort\", abort);\n\t\t\t\t\treject(new McpStdioError(\"MCP request timed out\", \"timed_out\", { method, requestId: id, stderr }));\n\t\t\t\t}, options.timeoutMs);\n\t\t\t}\n\t\t\tchild.stdin.write(\n\t\t\t\t`${JSON.stringify({ jsonrpc: \"2.0\", id, method, ...(params === undefined ? {} : { params }) })}\\n`,\n\t\t\t);\n\t\t});\n\t};\n\treturn {\n\t\trequest,\n\t\tnotify(method, params) {\n\t\t\tif (fatalError || closed) throw fatalError ?? new McpStdioError(\"MCP transport is closed\", \"exit\", { stderr });\n\t\t\tchild.stdin.write(\n\t\t\t\t`${JSON.stringify({ jsonrpc: \"2.0\", method, ...(params === undefined ? {} : { params }) })}\\n`,\n\t\t\t);\n\t\t},\n\t\tclose() {\n\t\t\tif (closeRequested) return;\n\t\t\tcloseRequested = true;\n\t\t\tclosed = true;\n\t\t\tfor (const request of pending.values())\n\t\t\t\trequest.reject(new McpStdioError(\"MCP transport closed\", \"exit\", { stdout, stderr }));\n\t\t\tpending.clear();\n\t\t\tchild.stdin.end();\n\t\t\tif (!child.killed) child.kill();\n\t\t},\n\t\tget stderr() {\n\t\t\treturn stderr;\n\t\t},\n\t};\n}\n\nfunction validateOptions(options: McpStdioPluginOptions): void {\n\tif (!options.command.trim()) throw new Error(\"MCP stdio command requires a non-empty command\");\n\tif (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {\n\t\tthrow new Error(\"MCP stdio timeoutMs must be a positive number\");\n\t}\n}\n\n/** Create an asynchronously discovering MCP stdio capability plugin. */\nexport function createMcpStdioPlugin(options: McpStdioPluginOptions): CapabilityPluginFactory {\n\tvalidateOptions(options);\n\tconst factory = options.transportFactory ?? spawnMcpTransport;\n\treturn async (api: CapabilityAPI) => {\n\t\tconst transport = await factory({\n\t\t\tcommand: options.command,\n\t\t\targs: options.args ?? [],\n\t\t\tcwd: options.cwd,\n\t\t\tenv: options.env,\n\t\t\tonStderr: options.onStderr,\n\t\t});\n\t\ttry {\n\t\t\tasRecord(\n\t\t\t\tawait transport.request(\n\t\t\t\t\t\"initialize\",\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocolVersion: options.protocolVersion ?? \"2024-11-05\",\n\t\t\t\t\t\tcapabilities: {},\n\t\t\t\t\t\tclientInfo: options.clientInfo ?? { name: \"agent-forge-mcp-stdio\", version: \"0.1.0\" },\n\t\t\t\t\t},\n\t\t\t\t\t{ timeoutMs: options.timeoutMs },\n\t\t\t\t),\n\t\t\t\t\"MCP initialize result\",\n\t\t\t);\n\t\t\tawait transport.notify(\"notifications/initialized\");\n\t\t\tconst discovered = validateToolList(\n\t\t\t\tawait transport.request(\"tools/list\", undefined, { timeoutMs: options.timeoutMs }),\n\t\t\t);\n\t\t\tfor (const remote of discovered) {\n\t\t\t\tconst name = remote.name as string;\n\t\t\t\tconst schema = (remote.inputSchema ?? Type.Object({})) as ToolDefinition[\"parameters\"];\n\t\t\t\tconst definition: ToolDefinition = {\n\t\t\t\t\tname,\n\t\t\t\t\tlabel: name,\n\t\t\t\t\tdescription: typeof remote.description === \"string\" ? remote.description : `MCP tool: ${name}`,\n\t\t\t\t\tparameters: schema,\n\t\t\t\t\tasync execute(toolCallId, params, signal, _onUpdate, _ctx: ExtensionContext) {\n\t\t\t\t\t\tconst input = params === undefined && typeof toolCallId !== \"string\" ? toolCallId : params;\n\t\t\t\t\t\tconst result = await transport.request(\n\t\t\t\t\t\t\t\"tools/call\",\n\t\t\t\t\t\t\t{ name, arguments: input ?? {} },\n\t\t\t\t\t\t\t{ signal, timeoutMs: options.timeoutMs },\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn normalizeToolResult(result);\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tapi.registerTool(definition);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tdispose: async () => {\n\t\t\t\t\tawait transport.close();\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tawait transport.close();\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n\n/**\n * Template entry only. A working MCP transport needs a user-selected command,\n * arguments, and timeout; never launch an unconfigured child process.\n */\nexport default function mcpStdioExample(_api: CapabilityAPI): never {\n\tthrow new Error(\n\t\t\"Configure createMcpStdioPlugin({ command, args, timeoutMs }) before installing this MCP stdio example.\",\n\t);\n}\n"]}