@try-works/dsh-tool-mode 0.0.0-stage → 0.5.44

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 (425) hide show
  1. package/README.md +299 -2
  2. package/bin/dsh-tool-mode-credentials.mjs +20 -0
  3. package/bin/dsh-tool-mode-mcp-stdio.mjs +80 -0
  4. package/bin/dsh-tool-mode.mjs +211 -0
  5. package/builtins.lock.json +26 -0
  6. package/catalog/desert-ant-models.json +693 -0
  7. package/catalog/playbooks.json +531 -0
  8. package/client/client.model-tools.js +1910 -0
  9. package/client/index.js +4039 -0
  10. package/cordis.patch.yml +39 -0
  11. package/lib/adapters/index.d.ts +61 -0
  12. package/lib/adapters/index.d.ts.map +1 -0
  13. package/lib/adapters/index.js +82 -0
  14. package/lib/adapters/types.d.ts +106 -0
  15. package/lib/adapters/types.d.ts.map +1 -0
  16. package/lib/adapters/types.js +85 -0
  17. package/lib/approval.d.ts +189 -0
  18. package/lib/approval.d.ts.map +1 -0
  19. package/lib/approval.js +510 -0
  20. package/lib/asset-acquisition.d.ts +149 -0
  21. package/lib/asset-acquisition.d.ts.map +1 -0
  22. package/lib/asset-acquisition.js +175 -0
  23. package/lib/authority.d.ts +185 -0
  24. package/lib/authority.d.ts.map +1 -0
  25. package/lib/authority.js +223 -0
  26. package/lib/bindings.d.ts +144 -0
  27. package/lib/bindings.d.ts.map +1 -0
  28. package/lib/bindings.js +314 -0
  29. package/lib/builtins.d.ts +27 -0
  30. package/lib/builtins.d.ts.map +1 -0
  31. package/lib/builtins.js +37 -0
  32. package/lib/capability.d.ts +58 -0
  33. package/lib/capability.d.ts.map +1 -0
  34. package/lib/capability.js +192 -0
  35. package/lib/capture-path.d.ts +97 -0
  36. package/lib/capture-path.d.ts.map +1 -0
  37. package/lib/capture-path.js +152 -0
  38. package/lib/catalog.d.ts +37 -0
  39. package/lib/catalog.d.ts.map +1 -0
  40. package/lib/catalog.js +274 -0
  41. package/lib/contract-payload.d.ts +39 -0
  42. package/lib/contract-payload.d.ts.map +1 -0
  43. package/lib/contract-payload.js +56 -0
  44. package/lib/custom-tools/compile.d.ts +25 -0
  45. package/lib/custom-tools/compile.d.ts.map +1 -0
  46. package/lib/custom-tools/compile.js +61 -0
  47. package/lib/custom-tools/composite-executor.d.ts +42 -0
  48. package/lib/custom-tools/composite-executor.d.ts.map +1 -0
  49. package/lib/custom-tools/composite-executor.js +113 -0
  50. package/lib/custom-tools/dispatch.d.ts +28 -0
  51. package/lib/custom-tools/dispatch.d.ts.map +1 -0
  52. package/lib/custom-tools/dispatch.js +34 -0
  53. package/lib/custom-tools/http-executor.d.ts +48 -0
  54. package/lib/custom-tools/http-executor.d.ts.map +1 -0
  55. package/lib/custom-tools/http-executor.js +241 -0
  56. package/lib/custom-tools/index.d.ts +12 -0
  57. package/lib/custom-tools/index.d.ts.map +1 -0
  58. package/lib/custom-tools/index.js +12 -0
  59. package/lib/custom-tools/mcp-surface.d.ts +14 -0
  60. package/lib/custom-tools/mcp-surface.d.ts.map +1 -0
  61. package/lib/custom-tools/mcp-surface.js +41 -0
  62. package/lib/custom-tools/prompt-executor.d.ts +39 -0
  63. package/lib/custom-tools/prompt-executor.d.ts.map +1 -0
  64. package/lib/custom-tools/prompt-executor.js +59 -0
  65. package/lib/custom-tools/registry.d.ts +69 -0
  66. package/lib/custom-tools/registry.d.ts.map +1 -0
  67. package/lib/custom-tools/registry.js +130 -0
  68. package/lib/custom-tools/schema.d.ts +26 -0
  69. package/lib/custom-tools/schema.d.ts.map +1 -0
  70. package/lib/custom-tools/schema.js +375 -0
  71. package/lib/custom-tools/service.d.ts +90 -0
  72. package/lib/custom-tools/service.d.ts.map +1 -0
  73. package/lib/custom-tools/service.js +203 -0
  74. package/lib/custom-tools/store.d.ts +61 -0
  75. package/lib/custom-tools/store.d.ts.map +1 -0
  76. package/lib/custom-tools/store.js +240 -0
  77. package/lib/custom-tools/types.d.ts +131 -0
  78. package/lib/custom-tools/types.d.ts.map +1 -0
  79. package/lib/custom-tools/types.js +11 -0
  80. package/lib/decision-combine.d.ts +113 -0
  81. package/lib/decision-combine.d.ts.map +1 -0
  82. package/lib/decision-combine.js +156 -0
  83. package/lib/decision-evaluate.d.ts +185 -0
  84. package/lib/decision-evaluate.d.ts.map +1 -0
  85. package/lib/decision-evaluate.js +227 -0
  86. package/lib/deployment-coordinator.d.ts +213 -0
  87. package/lib/deployment-coordinator.d.ts.map +1 -0
  88. package/lib/deployment-coordinator.js +274 -0
  89. package/lib/diagnostics.d.ts +169 -0
  90. package/lib/diagnostics.d.ts.map +1 -0
  91. package/lib/diagnostics.js +136 -0
  92. package/lib/discovery-contracts.d.ts +14 -0
  93. package/lib/discovery-contracts.d.ts.map +1 -0
  94. package/lib/discovery-contracts.js +90 -0
  95. package/lib/discovery-describe.d.ts +35 -0
  96. package/lib/discovery-describe.d.ts.map +1 -0
  97. package/lib/discovery-describe.js +99 -0
  98. package/lib/embedding-index.d.ts +55 -0
  99. package/lib/embedding-index.d.ts.map +1 -0
  100. package/lib/embedding-index.js +126 -0
  101. package/lib/extension-dependencies.d.ts +355 -0
  102. package/lib/extension-dependencies.d.ts.map +1 -0
  103. package/lib/extension-dependencies.js +987 -0
  104. package/lib/extension-dispatch.d.ts +150 -0
  105. package/lib/extension-dispatch.d.ts.map +1 -0
  106. package/lib/extension-dispatch.js +178 -0
  107. package/lib/extension-page-state.d.ts +69 -0
  108. package/lib/extension-page-state.d.ts.map +1 -0
  109. package/lib/extension-page-state.js +82 -0
  110. package/lib/extension-registrar.d.ts +116 -0
  111. package/lib/extension-registrar.d.ts.map +1 -0
  112. package/lib/extension-registrar.js +144 -0
  113. package/lib/extensions-api.d.ts +346 -0
  114. package/lib/extensions-api.d.ts.map +1 -0
  115. package/lib/extensions-api.js +17 -0
  116. package/lib/extensions-remote.d.ts +57 -0
  117. package/lib/extensions-remote.d.ts.map +1 -0
  118. package/lib/extensions-remote.js +69 -0
  119. package/lib/extensions-service.d.ts +568 -0
  120. package/lib/extensions-service.d.ts.map +1 -0
  121. package/lib/extensions-service.js +2507 -0
  122. package/lib/harness-budgets.d.ts +158 -0
  123. package/lib/harness-budgets.d.ts.map +1 -0
  124. package/lib/harness-budgets.js +152 -0
  125. package/lib/harness-llm.d.ts +138 -0
  126. package/lib/harness-llm.d.ts.map +1 -0
  127. package/lib/harness-llm.js +218 -0
  128. package/lib/harness-serving.d.ts +115 -0
  129. package/lib/harness-serving.d.ts.map +1 -0
  130. package/lib/harness-serving.js +176 -0
  131. package/lib/headless.d.ts +97 -0
  132. package/lib/headless.d.ts.map +1 -0
  133. package/lib/headless.js +407 -0
  134. package/lib/http-acquisition.d.ts +27 -0
  135. package/lib/http-acquisition.d.ts.map +1 -0
  136. package/lib/http-acquisition.js +140 -0
  137. package/lib/index.d.ts +546 -0
  138. package/lib/index.d.ts.map +1 -0
  139. package/lib/index.js +5197 -0
  140. package/lib/invocation-accounting.d.ts +169 -0
  141. package/lib/invocation-accounting.d.ts.map +1 -0
  142. package/lib/invocation-accounting.js +148 -0
  143. package/lib/jev.d.ts +218 -0
  144. package/lib/jev.d.ts.map +1 -0
  145. package/lib/jev.js +366 -0
  146. package/lib/legacy-migration.d.ts +95 -0
  147. package/lib/legacy-migration.d.ts.map +1 -0
  148. package/lib/legacy-migration.js +429 -0
  149. package/lib/license-basis.d.ts +29 -0
  150. package/lib/license-basis.d.ts.map +1 -0
  151. package/lib/license-basis.js +58 -0
  152. package/lib/managed-workers.d.ts +158 -0
  153. package/lib/managed-workers.d.ts.map +1 -0
  154. package/lib/managed-workers.js +269 -0
  155. package/lib/mcp/auth-selection.d.ts +35 -0
  156. package/lib/mcp/auth-selection.d.ts.map +1 -0
  157. package/lib/mcp/auth-selection.js +64 -0
  158. package/lib/mcp/client-grant.d.ts +89 -0
  159. package/lib/mcp/client-grant.d.ts.map +1 -0
  160. package/lib/mcp/client-grant.js +63 -0
  161. package/lib/mcp/credential-cli.d.ts +38 -0
  162. package/lib/mcp/credential-cli.d.ts.map +1 -0
  163. package/lib/mcp/credential-cli.js +148 -0
  164. package/lib/mcp/credential-store.d.ts +143 -0
  165. package/lib/mcp/credential-store.d.ts.map +1 -0
  166. package/lib/mcp/credential-store.js +461 -0
  167. package/lib/mcp/export-policy.d.ts +25 -0
  168. package/lib/mcp/export-policy.d.ts.map +1 -0
  169. package/lib/mcp/export-policy.js +31 -0
  170. package/lib/mcp/host-binding.d.ts +144 -0
  171. package/lib/mcp/host-binding.d.ts.map +1 -0
  172. package/lib/mcp/host-binding.js +280 -0
  173. package/lib/mcp/http.d.ts +180 -0
  174. package/lib/mcp/http.d.ts.map +1 -0
  175. package/lib/mcp/http.js +602 -0
  176. package/lib/mcp/oauth.d.ts +149 -0
  177. package/lib/mcp/oauth.d.ts.map +1 -0
  178. package/lib/mcp/oauth.js +396 -0
  179. package/lib/mcp/protocol.d.ts +329 -0
  180. package/lib/mcp/protocol.d.ts.map +1 -0
  181. package/lib/mcp/protocol.js +459 -0
  182. package/lib/mcp/rate-limit.d.ts +68 -0
  183. package/lib/mcp/rate-limit.d.ts.map +1 -0
  184. package/lib/mcp/rate-limit.js +114 -0
  185. package/lib/mcp/sdk-v2.d.ts +17 -0
  186. package/lib/mcp/sdk-v2.d.ts.map +1 -0
  187. package/lib/mcp/sdk-v2.js +204 -0
  188. package/lib/mcp/server.d.ts +149 -0
  189. package/lib/mcp/server.d.ts.map +1 -0
  190. package/lib/mcp/server.js +302 -0
  191. package/lib/mcp/stdio.d.ts +80 -0
  192. package/lib/mcp/stdio.d.ts.map +1 -0
  193. package/lib/mcp/stdio.js +184 -0
  194. package/lib/mcp/subscriptions.d.ts +177 -0
  195. package/lib/mcp/subscriptions.d.ts.map +1 -0
  196. package/lib/mcp/subscriptions.js +288 -0
  197. package/lib/mcp-status.d.ts +96 -0
  198. package/lib/mcp-status.d.ts.map +1 -0
  199. package/lib/mcp-status.js +70 -0
  200. package/lib/mcp-usage.d.ts +110 -0
  201. package/lib/mcp-usage.d.ts.map +1 -0
  202. package/lib/mcp-usage.js +189 -0
  203. package/lib/model-qualification.d.ts +192 -0
  204. package/lib/model-qualification.d.ts.map +1 -0
  205. package/lib/model-qualification.js +178 -0
  206. package/lib/planning.d.ts +256 -0
  207. package/lib/planning.d.ts.map +1 -0
  208. package/lib/planning.js +471 -0
  209. package/lib/playbook.d.ts +42 -0
  210. package/lib/playbook.d.ts.map +1 -0
  211. package/lib/playbook.js +65 -0
  212. package/lib/prepared-assets.d.ts +269 -0
  213. package/lib/prepared-assets.d.ts.map +1 -0
  214. package/lib/prepared-assets.js +471 -0
  215. package/lib/provider-bridge.d.ts +107 -0
  216. package/lib/provider-bridge.d.ts.map +1 -0
  217. package/lib/provider-bridge.js +423 -0
  218. package/lib/ptc-authority.d.ts +154 -0
  219. package/lib/ptc-authority.d.ts.map +1 -0
  220. package/lib/ptc-authority.js +242 -0
  221. package/lib/ptc-confinement.d.ts +60 -0
  222. package/lib/ptc-confinement.d.ts.map +1 -0
  223. package/lib/ptc-confinement.js +212 -0
  224. package/lib/ptc-runtime.d.ts +166 -0
  225. package/lib/ptc-runtime.d.ts.map +1 -0
  226. package/lib/ptc-runtime.js +231 -0
  227. package/lib/publication.d.ts +161 -0
  228. package/lib/publication.d.ts.map +1 -0
  229. package/lib/publication.js +181 -0
  230. package/lib/receipt-store.d.ts +57 -0
  231. package/lib/receipt-store.d.ts.map +1 -0
  232. package/lib/receipt-store.js +143 -0
  233. package/lib/receipt.d.ts +231 -0
  234. package/lib/receipt.d.ts.map +1 -0
  235. package/lib/receipt.js +165 -0
  236. package/lib/registrar.d.ts +75 -0
  237. package/lib/registrar.d.ts.map +1 -0
  238. package/lib/registrar.js +152 -0
  239. package/lib/resource-governance.d.ts +204 -0
  240. package/lib/resource-governance.d.ts.map +1 -0
  241. package/lib/resource-governance.js +213 -0
  242. package/lib/server-lifecycle.d.ts +175 -0
  243. package/lib/server-lifecycle.d.ts.map +1 -0
  244. package/lib/server-lifecycle.js +269 -0
  245. package/lib/skills.d.ts +31 -0
  246. package/lib/skills.d.ts.map +1 -0
  247. package/lib/skills.js +170 -0
  248. package/lib/speech-bridge.d.ts +74 -0
  249. package/lib/speech-bridge.d.ts.map +1 -0
  250. package/lib/speech-bridge.js +115 -0
  251. package/lib/state.d.ts +100 -0
  252. package/lib/state.d.ts.map +1 -0
  253. package/lib/state.js +160 -0
  254. package/lib/title-proposal.d.ts +194 -0
  255. package/lib/title-proposal.d.ts.map +1 -0
  256. package/lib/title-proposal.js +244 -0
  257. package/lib/tool-inventory.d.ts +90 -0
  258. package/lib/tool-inventory.d.ts.map +1 -0
  259. package/lib/tool-inventory.js +102 -0
  260. package/lib/tool-mode-run.d.ts +310 -0
  261. package/lib/tool-mode-run.d.ts.map +1 -0
  262. package/lib/tool-mode-run.js +411 -0
  263. package/lib/tool-sources.d.ts +80 -0
  264. package/lib/tool-sources.d.ts.map +1 -0
  265. package/lib/tool-sources.js +123 -0
  266. package/lib/tool-usage-projection.d.ts +390 -0
  267. package/lib/tool-usage-projection.d.ts.map +1 -0
  268. package/lib/tool-usage-projection.js +1038 -0
  269. package/lib/tool-usage-report.d.ts +153 -0
  270. package/lib/tool-usage-report.d.ts.map +1 -0
  271. package/lib/tool-usage-report.js +274 -0
  272. package/lib/transport.d.ts +158 -0
  273. package/lib/transport.d.ts.map +1 -0
  274. package/lib/transport.js +332 -0
  275. package/lib/types.d.ts +134 -0
  276. package/lib/types.d.ts.map +1 -0
  277. package/lib/types.js +8 -0
  278. package/lib/update-surface.d.ts +79 -0
  279. package/lib/update-surface.d.ts.map +1 -0
  280. package/lib/update-surface.js +224 -0
  281. package/lib/version.d.ts +3 -0
  282. package/lib/version.d.ts.map +1 -0
  283. package/lib/version.js +27 -0
  284. package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts +13 -0
  285. package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts.map +1 -0
  286. package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.js +85 -0
  287. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
  288. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
  289. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +87 -0
  290. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts +38 -0
  291. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts.map +1 -0
  292. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.js +445 -0
  293. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
  294. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
  295. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
  296. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts +14 -0
  297. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts.map +1 -0
  298. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.js +63 -0
  299. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts +14 -0
  300. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts.map +1 -0
  301. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.js +76 -0
  302. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
  303. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
  304. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +274 -0
  305. package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +26 -0
  306. package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
  307. package/node_modules/@try-works/tool-mode-contracts/lib/index.js +53 -0
  308. package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +133 -0
  309. package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
  310. package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +64 -0
  311. package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +121 -0
  312. package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
  313. package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +246 -0
  314. package/node_modules/@try-works/tool-mode-contracts/package.json +35 -0
  315. package/node_modules/@try-works/tool-mode-extension-sdk/bin/dsh-tool-mode.mjs +20 -0
  316. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts +80 -0
  317. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts.map +1 -0
  318. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.js +533 -0
  319. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts +86 -0
  320. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts.map +1 -0
  321. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.js +145 -0
  322. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts +70 -0
  323. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts.map +1 -0
  324. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.js +201 -0
  325. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts +57 -0
  326. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts.map +1 -0
  327. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.js +83 -0
  328. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts +33 -0
  329. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts.map +1 -0
  330. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.js +33 -0
  331. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts +86 -0
  332. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts.map +1 -0
  333. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.js +191 -0
  334. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +137 -0
  335. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts.map +1 -0
  336. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.js +349 -0
  337. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +274 -0
  338. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts.map +1 -0
  339. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.js +932 -0
  340. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts +210 -0
  341. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts.map +1 -0
  342. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.js +368 -0
  343. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts +63 -0
  344. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts.map +1 -0
  345. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.js +149 -0
  346. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts +35 -0
  347. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts.map +1 -0
  348. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.js +179 -0
  349. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts +179 -0
  350. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts.map +1 -0
  351. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.js +299 -0
  352. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +395 -0
  353. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts.map +1 -0
  354. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.js +839 -0
  355. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts +44 -0
  356. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts.map +1 -0
  357. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.js +255 -0
  358. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts +185 -0
  359. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts.map +1 -0
  360. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.js +504 -0
  361. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts +17 -0
  362. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts.map +1 -0
  363. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.js +80 -0
  364. package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts +221 -0
  365. package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts.map +1 -0
  366. package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.js +188 -0
  367. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts +52 -0
  368. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts.map +1 -0
  369. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.js +102 -0
  370. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts +171 -0
  371. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts.map +1 -0
  372. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.js +271 -0
  373. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts +242 -0
  374. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts.map +1 -0
  375. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.js +58 -0
  376. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts +128 -0
  377. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts.map +1 -0
  378. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.js +113 -0
  379. package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts +41 -0
  380. package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts.map +1 -0
  381. package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.js +52 -0
  382. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts +2 -0
  383. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts.map +1 -0
  384. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.js +351 -0
  385. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts +157 -0
  386. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts.map +1 -0
  387. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.js +688 -0
  388. package/node_modules/@try-works/tool-mode-extension-sdk/package.json +54 -0
  389. package/node_modules/@try-works/tool-mode-extension-sdk/schema/extension-manifest-1.1.0.schema.json +253 -0
  390. package/node_modules/@try-works/tool-mode-recipes/bin/tool-mode-recipe.mjs +15 -0
  391. package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts +460 -0
  392. package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts.map +1 -0
  393. package/node_modules/@try-works/tool-mode-recipes/lib/broker.js +647 -0
  394. package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +61 -0
  395. package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts.map +1 -0
  396. package/node_modules/@try-works/tool-mode-recipes/lib/cli.js +1106 -0
  397. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts +237 -0
  398. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts.map +1 -0
  399. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.js +205 -0
  400. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts +85 -0
  401. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts.map +1 -0
  402. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.js +176 -0
  403. package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts +119 -0
  404. package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts.map +1 -0
  405. package/node_modules/@try-works/tool-mode-recipes/lib/images.js +201 -0
  406. package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts +25 -0
  407. package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts.map +1 -0
  408. package/node_modules/@try-works/tool-mode-recipes/lib/index.js +25 -0
  409. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts +345 -0
  410. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts.map +1 -0
  411. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.js +1465 -0
  412. package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts +51 -0
  413. package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts.map +1 -0
  414. package/node_modules/@try-works/tool-mode-recipes/lib/templates.js +310 -0
  415. package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts +231 -0
  416. package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts.map +1 -0
  417. package/node_modules/@try-works/tool-mode-recipes/lib/titles.js +435 -0
  418. package/node_modules/@try-works/tool-mode-recipes/package.json +56 -0
  419. package/node_modules/@try-works/tool-mode-recipes/recipes/describe-image.json +38 -0
  420. package/node_modules/@try-works/tool-mode-recipes/recipes/text-title.json +45 -0
  421. package/node_modules/@try-works/tool-mode-recipes/schema/harness-model-recipe.schema.json +273 -0
  422. package/package.json +127 -4
  423. package/remote/spec.mjs +993 -0
  424. package/remote/typert.host.mjs +14 -0
  425. package/remote/typert.remote-client.mjs +16 -0
@@ -0,0 +1,932 @@
1
+ /**
2
+ * Activation and deactivation (EXT-04, EXT-26, EXT-27, EXT-28).
3
+ *
4
+ * The two orderings this module exists to enforce:
5
+ *
6
+ * - **Activate commits last.** Contributions are published to a candidate set,
7
+ * and the catalog generation advances only once EVERY required contribution has
8
+ * been accepted. A half-activated package is worse than an inactive one,
9
+ * because tools would appear that nothing can serve.
10
+ * - **Deactivate persists intent first.** The inactive preference is made durable
11
+ * BEFORE anything is withdrawn. If withdrawal then fails or the process dies,
12
+ * the durable answer is still "inactive", so a restart cannot resurrect a
13
+ * package the operator switched off.
14
+ *
15
+ * Rollback is deliberately NARROW: a failed activation withdraws only the handles
16
+ * it published itself. Touching another extension's contributions would turn one
17
+ * bad package into an outage of every package.
18
+ *
19
+ * @module @try-works/tool-mode-extension-sdk/install/lifecycle
20
+ */
21
+ import { rmSync } from 'node:fs';
22
+ import { ManagementError } from "./../errors.js";
23
+ import { discoverExtension } from "./../discovery.js";
24
+ import { isProcessAlive } from "./store.js";
25
+ import { summarize } from "./../state.js";
26
+ const CALLABLE_READINESS = new Set([
27
+ 'prepared-cold',
28
+ 'loading',
29
+ 'ready',
30
+ 'busy',
31
+ ]);
32
+ /** Default grace period for a contribution to finish stopping, in milliseconds. */
33
+ export const DEFAULT_STOP_GRACE_MS = 5000;
34
+ /**
35
+ * Whether an extension's readiness should block activation.
36
+ *
37
+ * EXT-26 is explicit that a valid package with merely UNPREPARED model assets may
38
+ * be active with `needs-preparation` diagnostics: the tools exist and can be
39
+ * described, and preparation happens on first use. A FAILED model is different —
40
+ * it will not become ready by waiting.
41
+ * @param readiness - per-deployment readiness.
42
+ * @returns diagnostics, and whether activation may proceed.
43
+ */
44
+ export function assessReadiness(readiness) {
45
+ const diagnostics = [];
46
+ let proceed = true;
47
+ for (const [deployment, value] of Object.entries(readiness)) {
48
+ if (value === 'failed') {
49
+ proceed = false;
50
+ diagnostics.push({
51
+ code: 'not-registerable',
52
+ subject: deployment,
53
+ message: `the model deployment ${deployment} is in a failed state and will not become ready by waiting`,
54
+ });
55
+ }
56
+ else if (value === 'needs-preparation' || value === 'preparing') {
57
+ diagnostics.push({
58
+ code: 'needs-preparation',
59
+ subject: deployment,
60
+ message: `the model deployment ${deployment} is ${value}; working inference tools are withheld until preparation is complete`,
61
+ });
62
+ }
63
+ }
64
+ return { proceed, diagnostics };
65
+ }
66
+ /** The extension lifecycle for one profile. */
67
+ export class ExtensionLifecycle {
68
+ #store;
69
+ #profile;
70
+ #registrar;
71
+ #onWithdrawal;
72
+ #protectedBuiltins;
73
+ #networkPolicy;
74
+ #onAudit;
75
+ #generation = 1;
76
+ #operations = new Set();
77
+ #assertIdle(extensionId) {
78
+ if (this.#operations.has(extensionId)) {
79
+ throw new ManagementError('EXTENSION_IN_USE', 'activate', `${extensionId} has a lifecycle operation in progress`, 'Wait for the current operation to settle, then retry.');
80
+ }
81
+ }
82
+ async #exclusive(extensionId, work) {
83
+ this.#assertIdle(extensionId);
84
+ this.#operations.add(extensionId);
85
+ try {
86
+ return await work();
87
+ }
88
+ finally {
89
+ this.#operations.delete(extensionId);
90
+ }
91
+ }
92
+ /** Handles published by this profile, keyed by extension then contribution name. */
93
+ #published = new Map();
94
+ #callableTools = new Map();
95
+ /**
96
+ * Extensions whose published surface THIS instance owns.
97
+ *
98
+ * Distinct from `#published`, which is empty once every handle has been
99
+ * withdrawn. Reconciliation must not republish a surface another process owns:
100
+ * a CLI attached to no host holds no handles and cannot manage one.
101
+ */
102
+ #owned = new Set();
103
+ constructor(options) {
104
+ this.#store = options.store;
105
+ this.#profile = options.profile;
106
+ this.#registrar = options.registrar;
107
+ this.#onWithdrawal = options.onWithdrawal;
108
+ this.#protectedBuiltins = new Set(options.protectedBuiltins ?? []);
109
+ this.#networkPolicy = options.networkPolicy ?? 'any';
110
+ this.#onAudit = options.onAudit;
111
+ this.#generation = options.generation ?? 1;
112
+ }
113
+ /** Run a lifecycle operation, emitting its outcome as an EXT-35 audit event. */
114
+ async #audited(operation, extensionId, version, work) {
115
+ try {
116
+ const result = await work();
117
+ this.#onAudit?.({ operation, extensionId, version, outcome: 'succeeded' });
118
+ return result;
119
+ }
120
+ catch (error) {
121
+ this.#onAudit?.({
122
+ operation,
123
+ extensionId,
124
+ version,
125
+ outcome: 'failed',
126
+ reason: error instanceof Error ? error.message : String(error),
127
+ });
128
+ throw error;
129
+ }
130
+ }
131
+ /** The current catalog generation. */
132
+ get generation() {
133
+ return this.#generation;
134
+ }
135
+ /** Read the persisted states for this profile. */
136
+ states() {
137
+ return this.#store.readProfileState(this.#profile).value;
138
+ }
139
+ /** Read one extension's persisted state. */
140
+ stateOf(extensionId) {
141
+ return this.states().find(state => state.extensionId === extensionId);
142
+ }
143
+ /** Replace one extension's state in the profile, preserving the others. */
144
+ #writeState(state) {
145
+ const others = this.states().filter(candidate => candidate.extensionId !== state.extensionId);
146
+ // The EXT-30 update pointers (activeVersions/previousVersions) live in the
147
+ // SAME profile document and are PRESERVED on every lifecycle write: the
148
+ // commit flow writes the pointer, and the rebind's own activate/deactivate
149
+ // must not erase it (the host's quiesce/rebind hooks call exactly these
150
+ // lifecycle writes). A corrupt pointer read fails closed like any other
151
+ // unreadable state — the write then proceeds WITHOUT inventing pointers.
152
+ const pointers = this.#store.readProfilePointers(this.#profile);
153
+ if (pointers.corrupt) {
154
+ this.#store.writeProfileState(this.#profile, [...others, state]);
155
+ return;
156
+ }
157
+ const { activeVersions, previousVersions } = pointers.value;
158
+ this.#store.writeProfileState(this.#profile, [...others, state], undefined, activeVersions, previousVersions);
159
+ }
160
+ /**
161
+ * Activate an installed extension (EXT-26).
162
+ *
163
+ * Verifies the installed bytes and locks, checks readiness, publishes every
164
+ * required contribution, and advances the generation only if all of that
165
+ * succeeded. On any failure the contributions THIS call published are withdrawn
166
+ * and the generation is left where it was.
167
+ * @param extensionId - the extension to activate.
168
+ * @param version - the installed version to activate.
169
+ * @param options - failure-recording policy for probe activations.
170
+ * @returns the activation outcome.
171
+ * @throws ManagementError when the extension is not installed, its content changed, or a contribution cannot be registered.
172
+ */
173
+ async activate(extensionId, version, options = {}) {
174
+ // The store lock wraps the WHOLE operation: activation writes state, and an
175
+ // offline CLI editing the same store must not interleave its read-modify-write
176
+ // between this one's read and write (EXT-31).
177
+ return this.#audited('activate', extensionId, version, () => (this.#store.exclusive(() => this.#exclusive(extensionId, () => this.#activate(extensionId, version, options)))));
178
+ }
179
+ /**
180
+ * Retry owned deletion left pending by a previous uninstall (EXT-29).
181
+ *
182
+ * An uninstall that could not delete its private installation keeps the record and
183
+ * leaves the installation absent in the profile state. On restart this finds only
184
+ * those records, deletes exactly the recorded path, and removes the record only
185
+ * after the deletion succeeds.
186
+ * @returns the cleaned ids and the failures with reasons.
187
+ */
188
+ async retryCleanupPending() {
189
+ const cleaned = [];
190
+ const failed = [];
191
+ const absent = new Set(this.states().filter(state => state.installation === 'absent').map(state => state.extensionId));
192
+ for (const record of this.#store.listInstalled().value) {
193
+ if (!absent.has(record.extensionId))
194
+ continue;
195
+ try {
196
+ rmSync(record.installPath, { recursive: true, force: true });
197
+ this.#store.removeRecord(record.extensionId);
198
+ cleaned.push(record.extensionId);
199
+ }
200
+ catch (error) {
201
+ failed.push({
202
+ extensionId: record.extensionId,
203
+ reason: error instanceof Error ? error.message : String(error),
204
+ });
205
+ }
206
+ }
207
+ return { cleaned, failed };
208
+ }
209
+ async #activate(extensionId, version, options) {
210
+ const record = this.#findRecord(extensionId, version);
211
+ if (record === undefined) {
212
+ throw new ManagementError('EXTENSION_INACTIVE', 'activate', `${extensionId}@${version} is not installed`, 'Install it first; activation does not install.');
213
+ }
214
+ // Checked BEFORE anything is published: activating bytes that differ from the
215
+ // ones the operator approved would execute code nobody reviewed.
216
+ const integrity = this.#store.verifyUnchanged(record);
217
+ if (!integrity.unchanged) {
218
+ throw new ManagementError('INTEGRITY_MISMATCH', 'activate', `${extensionId}@${version} cannot be activated: ${integrity.diagnostic ?? 'the installed content changed'}`, 'Reinstall the package from its declared source, then activate it.');
219
+ }
220
+ /**
221
+ * LIC-04: no-network admission is decided from the DECLARED behaviour before
222
+ * anything is published. Suppressing required telemetry to fit the policy would
223
+ * breach the licence, so the refusal names the alternative instead.
224
+ */
225
+ if (this.#networkPolicy === 'no-network') {
226
+ const discovery = this.#discoverOf(record);
227
+ if (discovery === undefined) {
228
+ throw new ManagementError('EXTENSION_FAULT', 'activate', `${extensionId}@${version} cannot be read, so its network behaviour cannot be checked`, 'Reinstall the package or repair its manifest.');
229
+ }
230
+ if (!discovery.noNetwork.eligible) {
231
+ throw new ManagementError('PERMISSION_REQUIRED', 'activate', `${extensionId} is not eligible for a strict no-network deployment: `
232
+ + `${discovery.noNetwork.reason ?? 'its declared network behaviour does not allow it'}`, discovery.noNetwork.alternative ?? 'Choose an implementation whose declared behaviour is network-free.');
233
+ }
234
+ }
235
+ const previous = this.stateOf(extensionId);
236
+ /**
237
+ * An extension THIS instance already has active is not a competing publication:
238
+ * re-activating it is how a caller picks up newly recorded readiness. Refusing
239
+ * here made preparation a dead end — every model-backed package publishes
240
+ * `implementation` metadata handles, so the guard fired even when nothing was
241
+ * actually serving a tool. Reconcile the surface instead.
242
+ */
243
+ if (previous?.observedActivation === 'active' && this.#owned.has(extensionId)) {
244
+ const refresher = this.#store.listInstalled().value.find(candidate => candidate.extensionId === extensionId);
245
+ const refreshedTools = this.#toolsOf(record);
246
+ const refreshedReadiness = this.#readinessOf(record, previous.readiness);
247
+ const refreshedDiagnostics = [...assessReadiness(refreshedReadiness).diagnostics];
248
+ this.#writeState(this.#stateFor(record, {
249
+ desiredActivation: 'active',
250
+ observedActivation: 'active',
251
+ tools: refreshedTools,
252
+ readiness: refreshedReadiness,
253
+ }));
254
+ if (refresher !== undefined)
255
+ await this.#reconcileSurface(extensionId, refreshedDiagnostics);
256
+ return {
257
+ extensionId,
258
+ version,
259
+ state: this.stateOf(extensionId) ?? this.#stateFor(record, {
260
+ desiredActivation: 'active', observedActivation: 'active',
261
+ tools: refreshedTools, readiness: refreshedReadiness,
262
+ }),
263
+ diagnostics: refreshedDiagnostics,
264
+ generation: this.#generation,
265
+ };
266
+ }
267
+ /**
268
+ * ADOPTION IS NOT ACTIVATION. State saying `active` means SOMEBODY published this
269
+ * surface. If this instance holds no handles for it, the question is whether that
270
+ * owner is GONE (a restart: restoring is the whole point of RUN-05) or ALIVE
271
+ * (another host: republishing would create a second owner serving the same names).
272
+ * The recorded owner PID is the only durable way to tell them apart, because
273
+ * handles are in-memory.
274
+ *
275
+ * The cases that must REFUSE are: a live foreign PID, and OUR OWN PID without
276
+ * local ownership (another instance of this process published it). A dead or
277
+ * absent PID is a restart of a legacy owner and may be restored.
278
+ */
279
+ if (previous?.observedActivation === 'active' && !this.#owned.has(extensionId)) {
280
+ const pid = previous.ownerPid;
281
+ const foreignLive = pid !== undefined && pid !== process.pid && isProcessAlive(pid);
282
+ const sameProcessStranger = pid === process.pid;
283
+ if (foreignLive || sameProcessStranger) {
284
+ throw new ManagementError('EXTENSION_IN_USE', 'activate', `${extensionId} is already active under ${foreignLive ? `live process ${pid}` : 'another owner in this process'}`, 'Deactivate it through the owning host before activating it here.');
285
+ }
286
+ }
287
+ if ((this.#published.get(extensionId)?.size ?? 0) > 0
288
+ || previous?.observedActivation === 'quarantined'
289
+ || previous?.observedActivation === 'deactivating'
290
+ || previous?.observedActivation === 'activating') {
291
+ throw new ManagementError('EXTENSION_IN_USE', 'activate', `${extensionId} is already published or requires cleanup before activation`, 'Deactivate through the owning host and confirm cleanup before activating again.');
292
+ }
293
+ const tools = this.#toolsOf(record);
294
+ const readiness = this.#readinessOf(record, previous?.readiness);
295
+ const assessment = assessReadiness(readiness);
296
+ const diagnostics = [...assessment.diagnostics];
297
+ if (!assessment.proceed) {
298
+ const reason = diagnostics.map(entry => entry.message).join('; ');
299
+ const failed = {
300
+ desiredActivation: 'active',
301
+ observedActivation: 'failed',
302
+ tools,
303
+ readiness,
304
+ };
305
+ // A probe activation suppresses the cause: its refusal is a probe artifact,
306
+ // not an operator-facing reason. A real attempt records it durably.
307
+ this.#writeState(this.#stateFor(record, options.recordFailure === false
308
+ ? failed
309
+ : { ...failed, activationError: reason }));
310
+ throw new ManagementError('EXTENSION_FAULT', 'activate', `${extensionId}@${version} cannot be activated: ${reason}`, 'Prepare or repair the model deployment, then activate again.');
311
+ }
312
+ // Intended-active is persisted before publication so a crash mid-activation
313
+ // leaves a record that activation was attempted, not a silent no-op. The
314
+ // override set OMITS `activationError`, which is how a retry clears the previous
315
+ // failure's reason: #stateFor rebuilds the object rather than keeping stale fields.
316
+ this.#writeState(this.#stateFor(record, {
317
+ desiredActivation: 'active',
318
+ observedActivation: 'activating',
319
+ tools, readiness,
320
+ }));
321
+ const published = [];
322
+ const callableTools = new Set();
323
+ try {
324
+ for (const contribution of this.#contributionsOf(record, readiness, diagnostics)) {
325
+ if (contribution.kind === 'tool')
326
+ callableTools.add(contribution.name);
327
+ const handle = await this.#registrar.publish(contribution);
328
+ published.push({ kind: contribution.kind, name: contribution.name, handle });
329
+ }
330
+ }
331
+ catch (error) {
332
+ // Rollback is NARROW: only what this call published is withdrawn, so one bad
333
+ // package cannot disturb the others.
334
+ const remaining = new Map();
335
+ for (const entry of published.reverse()) {
336
+ try {
337
+ await this.#registrar.withdraw(entry.handle);
338
+ }
339
+ catch {
340
+ remaining.set(entry.kind ? `${entry.kind}:${entry.name}` : `unknown:${entry.name}`, entry.handle);
341
+ }
342
+ }
343
+ if (remaining.size > 0)
344
+ this.#published.set(extensionId, remaining);
345
+ const reason = error instanceof Error ? error.message : String(error);
346
+ const failed = {
347
+ desiredActivation: 'active',
348
+ observedActivation: remaining.size > 0 ? 'quarantined' : 'failed',
349
+ tools,
350
+ readiness,
351
+ };
352
+ // The published-contribution refusal is the cause the operator must see;
353
+ // the thrown ManagementError is not persisted anywhere else. A probe
354
+ // activation suppresses it (see ActivationOptions.recordFailure).
355
+ this.#writeState(this.#stateFor(record, options.recordFailure === false
356
+ ? failed
357
+ : { ...failed, activationError: reason }));
358
+ throw new ManagementError('EXTENSION_FAULT', 'activate', `${extensionId}@${version} was not activated: ${reason}`, 'Fix the reported contribution and activate again; other extensions were not disturbed.');
359
+ }
360
+ const handles = new Map(published.map(entry => [`${entry.kind}:${entry.name}`, entry.handle]));
361
+ this.#published.set(extensionId, handles);
362
+ this.#callableTools.set(extensionId, callableTools);
363
+ this.#owned.add(extensionId);
364
+ // Committed only now, once every required contribution was accepted.
365
+ this.#generation += 1;
366
+ // No `activationError` in the override set: a successful activation CARRIES NO
367
+ // stale failure reason forward (an active extension must not look faulted).
368
+ const state = this.#stateFor(record, {
369
+ desiredActivation: 'active',
370
+ observedActivation: 'active',
371
+ tools, readiness,
372
+ });
373
+ this.#writeState(state);
374
+ return { extensionId, version, state, diagnostics, generation: this.#generation };
375
+ }
376
+ /**
377
+ * Deactivate an extension (EXT-27).
378
+ *
379
+ * The inactive intent is made durable FIRST, so a failure or crash during
380
+ * withdrawal cannot resurrect the package on the next start.
381
+ * @param extensionId - the extension to deactivate.
382
+ * @returns the deactivation outcome, which reports honestly when it is not fully stopped.
383
+ */
384
+ async deactivate(extensionId) {
385
+ return this.#audited('deactivate', extensionId, null, () => (this.#store.exclusive(() => this.#exclusive(extensionId, () => this.#deactivate(extensionId)))));
386
+ }
387
+ async #deactivate(extensionId) {
388
+ const current = this.stateOf(extensionId);
389
+ if (current === undefined) {
390
+ throw new ManagementError('EXTENSION_INACTIVE', 'deactivate', `${extensionId} is not known in profile ${this.#profile}`, 'Nothing to deactivate.');
391
+ }
392
+ // ORDERING: persist inactive intent before withdrawing anything.
393
+ this.#writeState({ ...current, desiredActivation: 'inactive', observedActivation: 'deactivating' });
394
+ const handles = this.#published.get(extensionId) ?? new Map();
395
+ const hadHandles = handles.size > 0;
396
+ const failures = [];
397
+ for (const [name, handle] of handles) {
398
+ try {
399
+ await this.#registrar.withdraw(handle);
400
+ handles.delete(name);
401
+ }
402
+ catch (error) {
403
+ failures.push(`${name}: ${error instanceof Error ? error.message : String(error)}`);
404
+ }
405
+ }
406
+ if (handles.size === 0) {
407
+ this.#published.delete(extensionId);
408
+ this.#callableTools.delete(extensionId);
409
+ }
410
+ /**
411
+ * A store that says the extension was ACTIVE while this process holds no
412
+ * handles means somebody else published them — normally a host this CLI is not
413
+ * attached to, or a previous run that has since exited.
414
+ *
415
+ * Reporting a clean stop here would be a lie of exactly the kind EXT-27
416
+ * forbids: the contributions are still published somewhere, and this process
417
+ * has no way to withdraw them. Saying so is the only honest answer.
418
+ */
419
+ const deadOwner = current.ownerPid !== undefined
420
+ && current.ownerPid !== process.pid
421
+ && !isProcessAlive(current.ownerPid);
422
+ const orphaned = (current.observedActivation === 'active' || current.observedActivation === 'quarantined'
423
+ || current.observedActivation === 'deactivating' || current.observedActivation === 'activating') && !hadHandles
424
+ && !deadOwner;
425
+ if (orphaned) {
426
+ failures.push('the contributions were published by another process and cannot be withdrawn from here; '
427
+ + 'stop the owning host, or deactivate through it');
428
+ }
429
+ const stopped = failures.length === 0;
430
+ /**
431
+ * onWithdrawal — a TEARDOWN OBSERVER, exactly once per withdrawn extension.
432
+ *
433
+ * It runs AFTER every registrar handle withdrawal has settled (successes
434
+ * and failures alike: a host must dispose its worker in both cases) and
435
+ * BEFORE the final state write, inside the existing store + operation
436
+ * locks. Its failure is RECORDED, never thrown: the state write below must
437
+ * commit even when a disposal callback misbehaves — losing the durable
438
+ * "inactive" answer to a hook error would let a restart resurrect the
439
+ * package the operator switched off. The error surfaces on the result.
440
+ */
441
+ let hookFailure;
442
+ if (this.#onWithdrawal !== undefined) {
443
+ try {
444
+ await this.#onWithdrawal(extensionId);
445
+ }
446
+ catch (error) {
447
+ hookFailure = error instanceof Error ? error.message : String(error);
448
+ }
449
+ }
450
+ const { ownerPid: _previousOwner, ...withoutOwner } = current;
451
+ const inactive = {
452
+ ...(stopped ? withoutOwner : current),
453
+ desiredActivation: 'inactive',
454
+ observedActivation: stopped ? 'inactive' : 'quarantined',
455
+ };
456
+ this.#writeState(inactive);
457
+ // Ownership ends only with a CONFIRMED stop; a quarantined extension still has
458
+ // live handlers this instance must be able to retry withdrawing.
459
+ if (failures.length === 0) {
460
+ this.#generation += 1;
461
+ this.#owned.delete(extensionId);
462
+ }
463
+ return {
464
+ extensionId,
465
+ state: inactive,
466
+ // Never claim a full stop while something it owns is still running.
467
+ fullyStopped: failures.length === 0,
468
+ ...failures.length === 0 ? {} : { cleanupFailure: failures.join('; ') },
469
+ ...hookFailure === undefined ? {} : { hookFailure },
470
+ generation: this.#generation,
471
+ };
472
+ }
473
+ /**
474
+ * Enable or disable ONE tool inside an active extension (EXT-04).
475
+ *
476
+ * Independent of activation: the package stays active while a single tool is
477
+ * hidden from callers.
478
+ *
479
+ * ASYNC because the toggle also reconciles the PUBLISHED surface: a disabled
480
+ * tool is withdrawn from the registrar rather than merely remembered as off,
481
+ * and a re-enabled one is published again. When this instance does not own the
482
+ * surface — a CLI with no host attached — only the durable choice is recorded,
483
+ * because it cannot manage a registry another process owns.
484
+ * @param extensionId - the owning extension.
485
+ * @param toolName - the tool to toggle.
486
+ * @param enabled - the new visibility.
487
+ * @throws ManagementError when the extension or tool is unknown.
488
+ * @returns the state after the change.
489
+ */
490
+ async setToolEnabled(extensionId, toolName, enabled) {
491
+ return this.#store.exclusive(() => this.#exclusive(extensionId, () => this.#setToolEnabled(extensionId, toolName, enabled)));
492
+ }
493
+ async #setToolEnabled(extensionId, toolName, enabled) {
494
+ const current = this.stateOf(extensionId);
495
+ if (current === undefined) {
496
+ throw new ManagementError('EXTENSION_INACTIVE', 'deactivate', `${extensionId} is not known`, 'Nothing to toggle.');
497
+ }
498
+ const tool = current.tools.find(candidate => candidate.toolName === toolName);
499
+ if (tool === undefined) {
500
+ throw new ManagementError('INVALID_EXTENSION', 'deactivate', `${extensionId} contributes no tool named ${JSON.stringify(toolName)}`, 'List the extension to see the tools it contributes.');
501
+ }
502
+ const next = {
503
+ ...current,
504
+ tools: current.tools.map(candidate => candidate.toolName === toolName ? { ...candidate, enabled } : candidate),
505
+ };
506
+ /**
507
+ * INTENT IS PERSISTED BEFORE THE SURFACE MOVES. Disabling a tool that stays
508
+ * REGISTERED would keep answering callers, which is the defect this ordering
509
+ * exists to close; writing the choice first means a crash mid-reconcile
510
+ * leaves the durable answer as "off" rather than silently "on".
511
+ */
512
+ this.#writeState(next);
513
+ if (next.observedActivation === 'active' && this.#owned.has(extensionId)) {
514
+ await this.#reconcileSurface(extensionId);
515
+ }
516
+ return this.stateOf(extensionId) ?? next;
517
+ }
518
+ /**
519
+ * Bring the PUBLISHED surface back in line with the current state (EXT-04).
520
+ *
521
+ * A tool toggle is not only a state edit: the registrar still holds the handles
522
+ * from activation. This recomputes the desired contribution set — which already
523
+ * encodes disabled tools, readiness gates and dependent usage skills — and then
524
+ * withdraws what is no longer wanted and publishes what is newly wanted.
525
+ *
526
+ * Nothing is recomputed for an extension that is not active: a deactivated
527
+ * package has no visible surface to reconcile, so this is a no-op there.
528
+ * @param extensionId - the extension whose surface to reconcile.
529
+ */
530
+ async #reconcileSurface(extensionId, diagnostics = []) {
531
+ const record = this.#store.listInstalled().value.find(candidate => candidate.extensionId === extensionId);
532
+ if (record === undefined)
533
+ return;
534
+ /**
535
+ * THE SAME INTEGRITY GATE ACTIVATION USES. Activation refuses to publish bytes
536
+ * that changed after installation; reconcile publishes and withdraws through the
537
+ * SAME registry, so skipping the check here would make "edit the installed
538
+ * package, then toggle an unrelated tool" a way to run code nobody reviewed.
539
+ */
540
+ const integrity = this.#store.verifyUnchanged(record);
541
+ if (!integrity.unchanged) {
542
+ throw new ManagementError('INTEGRITY_MISMATCH', 'activate', `${extensionId} surface cannot be reconciled: ${integrity.diagnostic ?? 'the installed content changed'}`, 'Reinstall the package from its declared source, then toggle the tool.');
543
+ }
544
+ /**
545
+ * AN UNKNOWN DESIRED SET IS NOT AN EMPTY ONE. `#contributionsOf` returns [] when
546
+ * the manifest cannot be read, and treating that as "nothing is wanted" would
547
+ * withdraw the ENTIRE surface — a transient AV-scanner lock or concurrent writer
548
+ * would silently unregister every tool while the durable state still said active.
549
+ * Refuse instead, leaving the surface exactly as it was.
550
+ */
551
+ const discovery = this.#discoverOf(record);
552
+ if (discovery === undefined) {
553
+ throw new ManagementError('INVALID_EXTENSION', 'activate', `${extensionId} manifest could not be read, so the published surface was left untouched`, 'Ensure the installed package is readable, then retry.');
554
+ }
555
+ const state = this.stateOf(extensionId);
556
+ const previous = this.#published.get(extensionId) ?? new Map();
557
+ const desired = this.#contributionsOf(record, state?.readiness ?? {}, diagnostics);
558
+ /**
559
+ * Keyed by KIND AND NAME. A manifest may legally declare a tool named `shared`
560
+ * and an implementation whose id is also `shared` (uniqueness is per-label), so
561
+ * name-only keying would let one kind's survival mask the other's withdrawal.
562
+ */
563
+ const keyOf = (contribution) => `${contribution.kind}:${contribution.name}`;
564
+ const desiredKeys = new Set(desired.map(keyOf));
565
+ /**
566
+ * A REDUNDANT TOGGLE IS A NO-OP. When the published keys already equal the
567
+ * desired keys, there is nothing to withdraw or publish, and bumping the
568
+ * generation would invalidate consumer caches for a surface that did not move.
569
+ * This check ALSO makes a retry cheap: after a failed withdrawal the surviving
570
+ * key is still present, so a retry that now succeeds is a real change and does
571
+ * move the generation.
572
+ */
573
+ const existingKeys = new Set(previous.keys());
574
+ if (existingKeys.size === desiredKeys.size && [...existingKeys].every(key => desiredKeys.has(key))) {
575
+ return;
576
+ }
577
+ /**
578
+ * `live` and `visible` record what ACTUALLY exists after this pass, and they are
579
+ * committed in the `finally` below. Mutating the stored map in place and only then
580
+ * updating `#callableTools` would let a mid-pass failure leave admission asserting
581
+ * a surface the registry no longer has — the exact divergence this reconcile
582
+ * exists to remove.
583
+ */
584
+ const live = new Map();
585
+ const visible = new Set();
586
+ const failures = [];
587
+ for (const [key, handle] of [...previous]) {
588
+ // Keys are `kind:name`, so the KIND participates in the match rather than
589
+ // only the name: a tool and an implementation may legally share a name.
590
+ const name = key.slice(key.indexOf(':') + 1);
591
+ if (desiredKeys.has(key)) {
592
+ live.set(key, handle);
593
+ visible.add(name);
594
+ continue;
595
+ }
596
+ try {
597
+ await this.#registrar.withdraw(handle);
598
+ }
599
+ catch (error) {
600
+ // A handle that would not withdraw is STILL PUBLISHED, so it stays tracked
601
+ // and stays visible; forgetting it would be the lie.
602
+ failures.push(`${name}: ${error instanceof Error ? error.message : String(error)}`);
603
+ live.set(key, handle);
604
+ visible.add(name);
605
+ }
606
+ }
607
+ for (const contribution of desired) {
608
+ const key = keyOf(contribution);
609
+ if (live.has(key))
610
+ continue;
611
+ try {
612
+ const handle = await this.#registrar.publish(contribution);
613
+ live.set(key, handle);
614
+ visible.add(contribution.name);
615
+ }
616
+ catch (error) {
617
+ failures.push(`${contribution.name}: ${error instanceof Error ? error.message : String(error)}`);
618
+ }
619
+ }
620
+ if (live.size === 0)
621
+ this.#published.delete(extensionId);
622
+ else
623
+ this.#published.set(extensionId, live);
624
+ this.#callableTools.set(extensionId, new Set(desired.filter(contribution => contribution.kind === 'tool' && visible.has(contribution.name))
625
+ .map(contribution => contribution.name)));
626
+ /**
627
+ * The surface moved (fully or partly), so consumers must see a new generation.
628
+ * This advances BEFORE the failure is raised, because a partly-reconciled
629
+ * surface is still a different surface and must not be served under the old
630
+ * generation.
631
+ */
632
+ this.#generation += 1;
633
+ if (failures.length > 0) {
634
+ throw new ManagementError('EXTENSION_FAULT', 'deactivate', `${extensionId} surface was only partly reconciled: ${failures.join('; ')}`, 'Retry the toggle once the reported contribution can be managed; what remains published is still tracked.');
635
+ }
636
+ }
637
+ /**
638
+ * Whether a tool may be invoked right now.
639
+ * @param extensionId - the owning extension.
640
+ * @param toolName - the tool being invoked.
641
+ */
642
+ admits(extensionId, toolName) {
643
+ const current = this.stateOf(extensionId);
644
+ if (current === undefined)
645
+ return false;
646
+ /**
647
+ * ADMISSION MUST AGREE WITH PUBLICATION. The surface is exactly the tool
648
+ * contributions this instance committed, so a tool withheld for a missing,
649
+ * mismatched or unprepared binding is not admitted merely because its
650
+ * enablement flag survives in state. Readiness is already folded into that
651
+ * committed set, which also stops an UNRELATED deployment's preparation from
652
+ * rejecting a tool whose own binding is callable.
653
+ */
654
+ if (!(this.#callableTools.get(extensionId)?.has(toolName) ?? false))
655
+ return false;
656
+ /**
657
+ * Enablement and lifecycle are checked HERE rather than through
658
+ * `admitsInvocation`, because that helper also requires EVERY declared
659
+ * deployment to be callable. This tool's binding was already evaluated when its
660
+ * contribution was published, so an unrelated unprepared deployment must not
661
+ * make a callable tool inadmissible.
662
+ */
663
+ if (current.installation !== 'installed')
664
+ return false;
665
+ if (current.desiredActivation !== 'active' || current.observedActivation !== 'active')
666
+ return false;
667
+ return current.tools.find(tool => tool.toolName === toolName)?.enabled === true;
668
+ }
669
+ /**
670
+ * Uninstall an installed extension (EXT-04, EXT-28).
671
+ * @param extensionId - the extension to remove.
672
+ * @param options - `deactivate` must be set to remove an active package.
673
+ * @throws ManagementError `EXTENSION_IN_USE`, `BUILTIN_PROTECTED`, or `EXTENSION_INACTIVE`.
674
+ */
675
+ async uninstall(extensionId, options = {}) {
676
+ return this.#audited('uninstall', extensionId, null, () => (this.#store.exclusive(() => this.#exclusive(extensionId, () => this.#uninstall(extensionId, options)))));
677
+ }
678
+ async #uninstall(extensionId, options) {
679
+ if (this.#protectedBuiltins.has(extensionId)) {
680
+ throw new ManagementError('BUILTIN_PROTECTED', 'uninstall', `${extensionId} is a protected built-in of this host release`, 'Built-ins cannot be uninstalled; update the host release instead. A package manifest cannot change this list.');
681
+ }
682
+ const current = this.stateOf(extensionId);
683
+ const active = current?.observedActivation === 'active' || current?.desiredActivation === 'active'
684
+ || current?.observedActivation === 'quarantined' || current?.observedActivation === 'deactivating'
685
+ || (this.#published.get(extensionId)?.size ?? 0) > 0;
686
+ if (active && options.deactivate !== true) {
687
+ throw new ManagementError('EXTENSION_IN_USE', 'uninstall', `${extensionId} is active in profile ${this.#profile}`, 'Re-run with deactivation requested; the affected profiles and tools are listed in the preview.');
688
+ }
689
+ if (active) {
690
+ const stopped = await this.#deactivate(extensionId);
691
+ if (!stopped.fullyStopped) {
692
+ throw new ManagementError('EXTENSION_IN_USE', 'uninstall', `${extensionId} cleanup is incomplete: ${stopped.cleanupFailure ?? 'owned resources are not confirmed stopped'}`, 'Retry cleanup through the owning host; installed bytes and quarantine are retained.');
693
+ }
694
+ }
695
+ // The disabled tombstone is RETAINED rather than deleted, so a later automatic
696
+ // install cannot silently resurrect a package the operator removed (EXT-28).
697
+ const previous = this.stateOf(extensionId);
698
+ const { ownerPid: _staleOwner, ...withoutOwner } = previous ?? {
699
+ extensionId,
700
+ installation: 'absent',
701
+ desiredActivation: 'inactive',
702
+ observedActivation: 'inactive',
703
+ readiness: {},
704
+ tools: [],
705
+ export: 'not-exported',
706
+ };
707
+ this.#writeState({
708
+ ...withoutOwner,
709
+ installation: 'absent',
710
+ desiredActivation: 'inactive',
711
+ observedActivation: 'inactive',
712
+ tools: [],
713
+ });
714
+ this.#callableTools.delete(extensionId);
715
+ this.#owned.delete(extensionId);
716
+ /**
717
+ * EXT-28: delete the private installation (including its private resolved
718
+ * dependencies) BEFORE forgetting the record. If Windows still holds a file, the
719
+ * record remains and the caller gets CLEANUP_PENDING, so the uninstall can be
720
+ * retried; erasing the record first would orphan bytes with no way to find them.
721
+ */
722
+ for (const installedRecord of this.#store.listInstalled().value.filter(record => record.extensionId === extensionId)) {
723
+ try {
724
+ rmSync(installedRecord.installPath, { recursive: true, force: true });
725
+ }
726
+ catch (error) {
727
+ throw new ManagementError('CLEANUP_PENDING', 'uninstall', `${extensionId} could not delete its private installation at ${installedRecord.installPath}: `
728
+ + `${error instanceof Error ? error.message : String(error)}`, 'Retry uninstall after the owning process releases the files; it remains recorded and inactive.');
729
+ }
730
+ }
731
+ this.#store.removeRecord(extensionId);
732
+ }
733
+ #findRecord(extensionId, version) {
734
+ return this.#store.listInstalled().value.find(record => record.extensionId === extensionId && record.version === version);
735
+ }
736
+ /** The tools a record contributes, all enabled by default. */
737
+ #toolsOf(record) {
738
+ const discovery = this.#discoverOf(record);
739
+ const tools = discovery?.manifest.tools ?? [];
740
+ const previous = this.stateOf(record.extensionId)?.tools ?? [];
741
+ return tools.map(tool => {
742
+ const previousTool = previous.find(candidate => candidate.toolName === tool.name);
743
+ return {
744
+ toolName: tool.name,
745
+ enabled: previousTool?.enabled ?? true,
746
+ // A preserved provider pick must survive re-activation too: rebuilding
747
+ // the selection from the manifest drops it otherwise, which would undo
748
+ // the BASE-09 migration's provider preservation on the next refresh.
749
+ ...previousTool?.selectedImplementation === undefined ? {} : { selectedImplementation: previousTool.selectedImplementation },
750
+ };
751
+ });
752
+ }
753
+ /** Re-read the installed manifest, or undefined when it cannot be read. */
754
+ #discoverOf(record) {
755
+ try {
756
+ return discoverExtension(record.installPath);
757
+ }
758
+ catch {
759
+ // A package whose manifest no longer reads is reported by the caller as
760
+ // contributing nothing, rather than crashing the whole lifecycle.
761
+ return undefined;
762
+ }
763
+ }
764
+ /** Default declared model deployments to needs-preparation until a host records a stronger fact. */
765
+ #readinessOf(record, previous) {
766
+ const discovery = this.#discoverOf(record);
767
+ const readiness = {};
768
+ for (const model of discovery?.manifest.models ?? []) {
769
+ readiness[model.id] = previous?.[model.id] ?? 'needs-preparation';
770
+ }
771
+ return readiness;
772
+ }
773
+ /**
774
+ * Record host-inspected readiness for an extension's declared deployments.
775
+ *
776
+ * This is the production writer `#readinessOf` reads: without it, every declared
777
+ * model stays `needs-preparation` forever and a model-backed package can never
778
+ * publish a callable tool. Inspection is the host's read-only EXT-18 surface, so
779
+ * recording it here is how "prepared" becomes a durable fact rather than a guess.
780
+ *
781
+ * Only values for deployments the CURRENT manifest declares are kept, matching the
782
+ * projection `#readinessOf` already applies, and `failed` is recorded so a failed
783
+ * deployment refuses activation instead of staying silent.
784
+ *
785
+ * ASYNC because a readiness CHANGE also moves the surface: a deployment that just
786
+ * became `failed` must have its tool withdrawn, and one that just became callable
787
+ * must be published. Recording without reconciling would leave the published
788
+ * snapshot stale, which is exactly the divergence admission exists to prevent.
789
+ * @param extensionId - the extension whose deployments were inspected.
790
+ * @param inspected - readiness keyed by deployment id.
791
+ * @returns the state after recording.
792
+ * @throws ManagementError when the extension is not installed.
793
+ */
794
+ async recordReadiness(extensionId, inspected) {
795
+ return this.#store.exclusive(() => this.#exclusive(extensionId, () => this.#recordReadiness(extensionId, inspected)));
796
+ }
797
+ async #recordReadiness(extensionId, inspected) {
798
+ const current = this.stateOf(extensionId);
799
+ if (current === undefined) {
800
+ throw new ManagementError('EXTENSION_INACTIVE', 'activate', `${extensionId} is not known`, 'Nothing to record for it.');
801
+ }
802
+ const record = this.#store.listInstalled().value.find(candidate => candidate.extensionId === extensionId);
803
+ if (record === undefined) {
804
+ throw new ManagementError('EXTENSION_INACTIVE', 'activate', `${extensionId} is not installed`, 'Install it before recording readiness.');
805
+ }
806
+ const discovery = this.#discoverOf(record);
807
+ if (discovery === undefined) {
808
+ // An UNREADABLE manifest cannot say which deployments are declared, so recording
809
+ // against it would write junk into the durable state. Refuse rather than guess.
810
+ throw new ManagementError('INVALID_EXTENSION', 'activate', `${extensionId} manifest could not be read, so readiness was not recorded`, 'Ensure the installed package is readable, then retry.');
811
+ }
812
+ const declared = new Set(discovery.manifest.models.map(model => model.id));
813
+ const readiness = { ...current.readiness };
814
+ for (const [id, value] of Object.entries(inspected)) {
815
+ // Unknown deployments are DROPPED, mirroring the projection activation applies:
816
+ // a removed deployment's stale readiness must not poison the current manifest.
817
+ if (declared.has(id))
818
+ readiness[id] = value;
819
+ }
820
+ const changed = Object.entries(readiness).some(([id, value]) => current.readiness[id] !== value);
821
+ const next = { ...current, readiness };
822
+ this.#writeState(next);
823
+ // The surface must agree with the new facts: a tool whose deployment just failed
824
+ // is withdrawn, and one that just became callable is published.
825
+ if (changed && next.observedActivation === 'active' && this.#owned.has(extensionId)) {
826
+ await this.#reconcileSurface(extensionId);
827
+ }
828
+ return this.stateOf(extensionId) ?? next;
829
+ }
830
+ /** The contributions a record publishes. */
831
+ #contributionsOf(record, readiness, diagnostics) {
832
+ const discovery = this.#discoverOf(record);
833
+ if (discovery === undefined)
834
+ return [];
835
+ const contributions = [];
836
+ const declaredVisibleTools = new Set();
837
+ /**
838
+ * A declarative package declares tools for discovery/readiness, but its
839
+ * IMPLEMENTATION is the single contribution that publishes the standard
840
+ * contract tool. Publishing both makes the registrar see the same tool name
841
+ * twice ("already registered"), which aborts activation for a manifest that
842
+ * is correct per the declarative specification.
843
+ */
844
+ const declarative = discovery.manifest.execution.kind === 'declarative';
845
+ const disabled = new Set(this.stateOf(record.extensionId)?.tools
846
+ .filter(tool => !tool.enabled).map(tool => tool.toolName) ?? []);
847
+ for (const tool of discovery.manifest.tools) {
848
+ if (disabled.has(tool.name))
849
+ continue;
850
+ const candidates = discovery.manifest.implementations.filter(implementation => implementation.contractId === tool.contractId);
851
+ const implementations = candidates.filter(implementation => implementation.contractVersion === tool.contractVersion);
852
+ if (implementations.length === 0) {
853
+ diagnostics.push({
854
+ code: 'not-registerable',
855
+ subject: tool.name,
856
+ message: `${tool.name} is withheld because no implementation serves ${tool.contractId}@${tool.contractVersion}`,
857
+ });
858
+ continue;
859
+ }
860
+ // Dispatch currently selects the FIRST exact contract binding. Do not let
861
+ // a later ready alternative justify visibility for that unprepared target.
862
+ const selected = implementations[0];
863
+ const callable = selected?.modelId === undefined
864
+ || CALLABLE_READINESS.has(readiness[selected.modelId] ?? 'needs-preparation');
865
+ if (!callable) {
866
+ diagnostics.push({
867
+ code: 'needs-preparation',
868
+ subject: tool.name,
869
+ message: `${tool.name} is withheld because no implementation for ${tool.contractId} is prepared and callable`,
870
+ });
871
+ continue;
872
+ }
873
+ declaredVisibleTools.add(tool.name);
874
+ if (declarative)
875
+ continue;
876
+ contributions.push({
877
+ extensionId: record.extensionId, kind: 'tool', name: tool.name, contractId: tool.contractId,
878
+ });
879
+ }
880
+ for (const implementation of discovery.manifest.implementations) {
881
+ contributions.push({
882
+ extensionId: record.extensionId,
883
+ kind: 'implementation',
884
+ name: implementation.id,
885
+ contractId: implementation.contractId,
886
+ });
887
+ }
888
+ const visibleTools = new Set([
889
+ ...declaredVisibleTools,
890
+ ...contributions.filter(entry => entry.kind === 'tool').map(entry => entry.name),
891
+ ]);
892
+ for (const skill of discovery.manifest.skills) {
893
+ // Setup guidance has no required tools; usage guidance must not advertise
894
+ // leaves withheld by readiness. Keep both projections in the same decision.
895
+ if (skill.requiresTools?.some(id => {
896
+ const tool = discovery.manifest.tools.find(candidate => candidate.id === id);
897
+ return !visibleTools.has(tool?.name ?? id);
898
+ }))
899
+ continue;
900
+ contributions.push({ extensionId: record.extensionId, kind: 'skill', name: skill.name });
901
+ }
902
+ return contributions;
903
+ }
904
+ /**
905
+ * Build a state value for a record.
906
+ *
907
+ * The override set decides the WHOLE persisted state: a field it omits is absent
908
+ * from the new document, never inherited from the previous one. That is what makes
909
+ * a successful transition clear `activationError` without assigning `undefined`
910
+ * (which `exactOptionalPropertyTypes` rightly refuses).
911
+ */
912
+ #stateFor(record, overrides) {
913
+ const previous = this.stateOf(record.extensionId);
914
+ return {
915
+ extensionId: record.extensionId,
916
+ installation: 'installed',
917
+ export: previous?.export ?? 'not-exported',
918
+ /**
919
+ * The PID is stamped while THIS process publishes or is about to publish the
920
+ * surface, and dropped on any confirmed stop. That is what lets a restarted
921
+ * process restore an `active` record whose owner died, while still refusing a
922
+ * LIVE second owner.
923
+ */
924
+ ...overrides.observedActivation === 'active' || overrides.observedActivation === 'activating'
925
+ ? { ownerPid: process.pid }
926
+ : {},
927
+ ...overrides,
928
+ };
929
+ }
930
+ }
931
+ export { summarize };
932
+ //# sourceMappingURL=lifecycle.js.map