@try-works/dsh-model-as-tool 0.1.2 → 0.3.6

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 (267) hide show
  1. package/bin/dsh-tool-mode-credentials.mjs +20 -0
  2. package/bin/dsh-tool-mode.mjs +202 -0
  3. package/catalog/desert-ant-models.json +693 -693
  4. package/client/client.model-tools.js +592 -0
  5. package/client/index.js +1679 -0
  6. package/lib/adapters/index.d.ts.map +1 -1
  7. package/lib/adapters/index.js +4 -4
  8. package/lib/bindings.d.ts.map +1 -1
  9. package/lib/bindings.js +26 -25
  10. package/lib/capability.d.ts.map +1 -1
  11. package/lib/capability.js +17 -0
  12. package/lib/capture-path.d.ts +97 -0
  13. package/lib/capture-path.d.ts.map +1 -0
  14. package/lib/capture-path.js +152 -0
  15. package/lib/diagnostics.d.ts +166 -0
  16. package/lib/diagnostics.d.ts.map +1 -0
  17. package/lib/diagnostics.js +135 -0
  18. package/lib/extension-dependencies.d.ts +197 -0
  19. package/lib/extension-dependencies.d.ts.map +1 -0
  20. package/lib/extension-dependencies.js +369 -0
  21. package/lib/extension-dispatch.d.ts +117 -0
  22. package/lib/extension-dispatch.d.ts.map +1 -0
  23. package/lib/extension-dispatch.js +124 -0
  24. package/lib/extension-registrar.d.ts +100 -0
  25. package/lib/extension-registrar.d.ts.map +1 -0
  26. package/lib/extension-registrar.js +134 -0
  27. package/lib/extensions-api.d.ts +217 -0
  28. package/lib/extensions-api.d.ts.map +1 -0
  29. package/lib/extensions-api.js +17 -0
  30. package/lib/extensions-remote.d.ts +54 -0
  31. package/lib/extensions-remote.d.ts.map +1 -0
  32. package/lib/extensions-remote.js +66 -0
  33. package/lib/extensions-service.d.ts +334 -0
  34. package/lib/extensions-service.d.ts.map +1 -0
  35. package/lib/extensions-service.js +1351 -0
  36. package/lib/harness-llm.d.ts +138 -0
  37. package/lib/harness-llm.d.ts.map +1 -0
  38. package/lib/harness-llm.js +218 -0
  39. package/lib/harness-serving.d.ts +95 -0
  40. package/lib/harness-serving.d.ts.map +1 -0
  41. package/lib/harness-serving.js +157 -0
  42. package/lib/headless.d.ts +97 -0
  43. package/lib/headless.d.ts.map +1 -0
  44. package/lib/headless.js +349 -0
  45. package/lib/http-acquisition.d.ts +27 -0
  46. package/lib/http-acquisition.d.ts.map +1 -0
  47. package/lib/http-acquisition.js +136 -0
  48. package/lib/index.d.ts +250 -7
  49. package/lib/index.d.ts.map +1 -1
  50. package/lib/index.js +2625 -70
  51. package/lib/invocation-accounting.d.ts +169 -0
  52. package/lib/invocation-accounting.d.ts.map +1 -0
  53. package/lib/invocation-accounting.js +148 -0
  54. package/lib/legacy-migration.d.ts +95 -0
  55. package/lib/legacy-migration.d.ts.map +1 -0
  56. package/lib/legacy-migration.js +429 -0
  57. package/lib/managed-workers.d.ts +151 -0
  58. package/lib/managed-workers.d.ts.map +1 -0
  59. package/lib/managed-workers.js +249 -0
  60. package/lib/mcp/client-grant.d.ts +74 -0
  61. package/lib/mcp/client-grant.d.ts.map +1 -0
  62. package/lib/mcp/client-grant.js +60 -0
  63. package/lib/mcp/credential-cli.d.ts +38 -0
  64. package/lib/mcp/credential-cli.d.ts.map +1 -0
  65. package/lib/mcp/credential-cli.js +148 -0
  66. package/lib/mcp/credential-store.d.ts +143 -0
  67. package/lib/mcp/credential-store.d.ts.map +1 -0
  68. package/lib/mcp/credential-store.js +454 -0
  69. package/lib/mcp/host-binding.d.ts +25 -0
  70. package/lib/mcp/host-binding.d.ts.map +1 -1
  71. package/lib/mcp/host-binding.js +107 -14
  72. package/lib/mcp/http.d.ts +68 -18
  73. package/lib/mcp/http.d.ts.map +1 -1
  74. package/lib/mcp/http.js +390 -62
  75. package/lib/mcp/oauth.d.ts +149 -0
  76. package/lib/mcp/oauth.d.ts.map +1 -0
  77. package/lib/mcp/oauth.js +396 -0
  78. package/lib/mcp/protocol.d.ts +46 -7
  79. package/lib/mcp/protocol.d.ts.map +1 -1
  80. package/lib/mcp/protocol.js +63 -16
  81. package/lib/mcp/rate-limit.d.ts +68 -0
  82. package/lib/mcp/rate-limit.d.ts.map +1 -0
  83. package/lib/mcp/rate-limit.js +114 -0
  84. package/lib/mcp/sdk-v2.d.ts +17 -0
  85. package/lib/mcp/sdk-v2.d.ts.map +1 -0
  86. package/lib/mcp/sdk-v2.js +181 -0
  87. package/lib/mcp/server.d.ts +11 -1
  88. package/lib/mcp/server.d.ts.map +1 -1
  89. package/lib/mcp/server.js +80 -13
  90. package/lib/mcp/stdio.d.ts.map +1 -1
  91. package/lib/mcp/stdio.js +9 -1
  92. package/lib/mcp/subscriptions.d.ts +43 -1
  93. package/lib/mcp/subscriptions.d.ts.map +1 -1
  94. package/lib/mcp/subscriptions.js +90 -9
  95. package/lib/model-qualification.d.ts +192 -0
  96. package/lib/model-qualification.d.ts.map +1 -0
  97. package/lib/model-qualification.js +178 -0
  98. package/lib/planning.d.ts +246 -0
  99. package/lib/planning.d.ts.map +1 -0
  100. package/lib/planning.js +457 -0
  101. package/lib/prepared-assets.d.ts +178 -0
  102. package/lib/prepared-assets.d.ts.map +1 -0
  103. package/lib/prepared-assets.js +286 -0
  104. package/lib/provider-bridge.d.ts +37 -0
  105. package/lib/provider-bridge.d.ts.map +1 -1
  106. package/lib/provider-bridge.js +189 -12
  107. package/lib/ptc-authority.d.ts +140 -0
  108. package/lib/ptc-authority.d.ts.map +1 -0
  109. package/lib/ptc-authority.js +216 -0
  110. package/lib/ptc-runtime.d.ts +156 -0
  111. package/lib/ptc-runtime.d.ts.map +1 -0
  112. package/lib/ptc-runtime.js +191 -0
  113. package/lib/publication.d.ts +2 -1
  114. package/lib/publication.d.ts.map +1 -1
  115. package/lib/publication.js +13 -5
  116. package/lib/receipt.d.ts.map +1 -1
  117. package/lib/receipt.js +8 -0
  118. package/lib/title-proposal.d.ts +194 -0
  119. package/lib/title-proposal.d.ts.map +1 -0
  120. package/lib/title-proposal.js +244 -0
  121. package/lib/tool-mode-run.d.ts +235 -0
  122. package/lib/tool-mode-run.d.ts.map +1 -0
  123. package/lib/tool-mode-run.js +283 -0
  124. package/lib/types.d.ts +3 -1
  125. package/lib/types.d.ts.map +1 -1
  126. package/lib/update-surface.d.ts +79 -0
  127. package/lib/update-surface.d.ts.map +1 -0
  128. package/lib/update-surface.js +224 -0
  129. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
  130. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
  131. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +85 -0
  132. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts +38 -0
  133. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts.map +1 -0
  134. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.js +445 -0
  135. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
  136. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
  137. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
  138. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts +14 -0
  139. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts.map +1 -0
  140. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.js +63 -0
  141. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts +14 -0
  142. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts.map +1 -0
  143. package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.js +76 -0
  144. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
  145. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
  146. package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +274 -0
  147. package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +26 -0
  148. package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
  149. package/node_modules/@try-works/tool-mode-contracts/lib/index.js +48 -0
  150. package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +104 -0
  151. package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
  152. package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +55 -0
  153. package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +107 -0
  154. package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
  155. package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +246 -0
  156. package/node_modules/@try-works/tool-mode-contracts/package.json +34 -0
  157. package/node_modules/@try-works/tool-mode-extension-sdk/bin/dsh-tool-mode.mjs +20 -0
  158. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts +80 -0
  159. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts.map +1 -0
  160. package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.js +533 -0
  161. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts +83 -0
  162. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts.map +1 -0
  163. package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.js +125 -0
  164. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts +70 -0
  165. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts.map +1 -0
  166. package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.js +201 -0
  167. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts +57 -0
  168. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts.map +1 -0
  169. package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.js +83 -0
  170. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts +31 -0
  171. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts.map +1 -0
  172. package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.js +31 -0
  173. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts +86 -0
  174. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts.map +1 -0
  175. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.js +185 -0
  176. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +126 -0
  177. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts.map +1 -0
  178. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.js +297 -0
  179. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +216 -0
  180. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts.map +1 -0
  181. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.js +800 -0
  182. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts +210 -0
  183. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts.map +1 -0
  184. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.js +368 -0
  185. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts +63 -0
  186. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts.map +1 -0
  187. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.js +149 -0
  188. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts +35 -0
  189. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts.map +1 -0
  190. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.js +179 -0
  191. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts +179 -0
  192. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts.map +1 -0
  193. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.js +235 -0
  194. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +367 -0
  195. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts.map +1 -0
  196. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.js +807 -0
  197. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts +185 -0
  198. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts.map +1 -0
  199. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.js +504 -0
  200. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts +17 -0
  201. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts.map +1 -0
  202. package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.js +80 -0
  203. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts +52 -0
  204. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts.map +1 -0
  205. package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.js +102 -0
  206. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts +171 -0
  207. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts.map +1 -0
  208. package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.js +271 -0
  209. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts +242 -0
  210. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts.map +1 -0
  211. package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.js +58 -0
  212. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts +118 -0
  213. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts.map +1 -0
  214. package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.js +113 -0
  215. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts +2 -0
  216. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts.map +1 -0
  217. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.js +351 -0
  218. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts +150 -0
  219. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts.map +1 -0
  220. package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.js +670 -0
  221. package/node_modules/@try-works/tool-mode-extension-sdk/package.json +53 -0
  222. package/node_modules/@try-works/tool-mode-extension-sdk/schema/extension-manifest-1.1.0.schema.json +253 -0
  223. package/node_modules/@try-works/tool-mode-recipes/bin/tool-mode-recipe.mjs +15 -0
  224. package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts +427 -0
  225. package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts.map +1 -0
  226. package/node_modules/@try-works/tool-mode-recipes/lib/broker.js +592 -0
  227. package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +60 -0
  228. package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts.map +1 -0
  229. package/node_modules/@try-works/tool-mode-recipes/lib/cli.js +936 -0
  230. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts +237 -0
  231. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts.map +1 -0
  232. package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.js +200 -0
  233. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts +85 -0
  234. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts.map +1 -0
  235. package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.js +176 -0
  236. package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts +105 -0
  237. package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts.map +1 -0
  238. package/node_modules/@try-works/tool-mode-recipes/lib/images.js +129 -0
  239. package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts +25 -0
  240. package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts.map +1 -0
  241. package/node_modules/@try-works/tool-mode-recipes/lib/index.js +25 -0
  242. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts +325 -0
  243. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts.map +1 -0
  244. package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.js +1310 -0
  245. package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts +51 -0
  246. package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts.map +1 -0
  247. package/node_modules/@try-works/tool-mode-recipes/lib/templates.js +310 -0
  248. package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts +227 -0
  249. package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts.map +1 -0
  250. package/node_modules/@try-works/tool-mode-recipes/lib/titles.js +407 -0
  251. package/node_modules/@try-works/tool-mode-recipes/package.json +56 -0
  252. package/node_modules/@try-works/tool-mode-recipes/recipes/describe-image.json +38 -0
  253. package/node_modules/@try-works/tool-mode-recipes/recipes/text-title.json +45 -0
  254. package/node_modules/@try-works/tool-mode-recipes/schema/harness-model-recipe.schema.json +273 -0
  255. package/package.json +50 -17
  256. package/remote/spec.mjs +439 -0
  257. package/remote/typert.host.mjs +14 -0
  258. package/remote/typert.remote-client.mjs +16 -0
  259. package/lib/adapters/tongue.d.ts +0 -31
  260. package/lib/adapters/tongue.d.ts.map +0 -1
  261. package/lib/adapters/tongue.js +0 -93
  262. package/lib/adapters/voz.d.ts +0 -28
  263. package/lib/adapters/voz.d.ts.map +0 -1
  264. package/lib/adapters/voz.js +0 -102
  265. /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts +0 -0
  266. /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts.map +0 -0
  267. /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.js +0 -0
@@ -0,0 +1,369 @@
1
+ /**
2
+ * First-party dependency graphs for developer-mode extension installs (EXT-09).
3
+ *
4
+ * Installing a first-party package from its checkout must satisfy the SAME
5
+ * discipline the SDK applies to a registry install: every declared runtime
6
+ * dependency is resolved to an exact version, verified by content, and
7
+ * materialized INSIDE the extension's own immutable installation directory. The
8
+ * SDK owns that discipline (`installDependencyGraph`); this module supplies the
9
+ * host half it deliberately leaves injected:
10
+ *
11
+ * - the RESOLVER: the exact version of a declared dependency is the one already
12
+ * installed in the host's own tree, found by walking `node_modules` upward from
13
+ * the source checkout exactly the way Node would. No registry request is made,
14
+ * no version is guessed, and a dependency that is not installed refuses the
15
+ * install instead of being skipped.
16
+ * - the ARCHIVE: the resolved package directory is turned into a deterministic
17
+ * zip whose SHA-512 is the integrity the SDK verifies before extraction. The
18
+ * archive is an ordinary package archive — `node_modules` is excluded because
19
+ * the SDK resolves the transitive graph itself, and the package's declared
20
+ * install-time scripts are left in place so the SDK's own refusal (EXT-10)
21
+ * decides whether the dependency is admissible.
22
+ *
23
+ * Two properties matter for truthfulness:
24
+ *
25
+ * - **The host's node_modules is READ, never written.** The only write target is
26
+ * the SDK's staging directory, which becomes the extension's private
27
+ * `node_modules`; the resolver asserts this by construction (it has no write
28
+ * path outside the archive bytes).
29
+ * - **A declaration is never silently dropped.** Every name the extension
30
+ * declares is either materialized or the whole install fails with a named
31
+ * `DEPENDENCY_UNSATISFIED` reason.
32
+ *
33
+ * INTEGRITY NOTE. The SDK's dependency lock records the SHA-512 of the archive
34
+ * bytes it verified (`verifyIntegrity`), which is what this module computes. The
35
+ * directory's own identity is additionally checked with the SDK's
36
+ * `digestInstalledContent` before and after the archive is built, so a checkout
37
+ * that changes while it is being archived refuses rather than shipping bytes that
38
+ * no longer match what was resolved.
39
+ *
40
+ * @module @try-works/dsh-model-as-tool/extension-dependencies
41
+ */
42
+ import { createHash } from 'node:crypto';
43
+ import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
44
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
45
+ import { fileURLToPath } from 'node:url';
46
+ import AdmZip from 'adm-zip';
47
+ import { createZipArchiveSource, DEFAULT_ARCHIVE_LIMITS, digestInstalledContent, ManagementError, refuseInstallScripts, } from '@try-works/tool-mode-extension-sdk';
48
+ /** A package identity: one optional scope segment, then one name segment. */
49
+ const PACKAGE_ID = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
50
+ /**
51
+ * The dependency archive byte cap.
52
+ *
53
+ * The SDK's `installDependencyGraph` calls `fetch` with a 256 MiB bound and
54
+ * requires the source to enforce it, so this module refuses an archive it could
55
+ * not truthfully hand over under that contract.
56
+ */
57
+ export const MAX_DEPENDENCY_ARCHIVE_BYTES = 256 * 1024 * 1024;
58
+ /**
59
+ * Refuse an identity that cannot safely become a path component.
60
+ * @param name - the declared dependency name.
61
+ * @throws ManagementError `DEPENDENCY_UNSATISFIED` for an unusable name.
62
+ */
63
+ function assertUsableName(name) {
64
+ if (!PACKAGE_ID.test(name)) {
65
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `${JSON.stringify(name)} is not a package name; a dependency declaration must be a name like @scope/name`, 'Correct the dependency name in the extension package.json.');
66
+ }
67
+ }
68
+ /** Read one installed package's identity, or undefined when it is not a package. */
69
+ function readInstalledPackage(directory, expectedName) {
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'));
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ if (typeof parsed !== 'object' || parsed === null)
78
+ return undefined;
79
+ // SAFETY: the value came from JSON.parse and the object guard above; every field
80
+ // is re-checked (name equality, string version) before it reaches the caller, so
81
+ // an unexpected shape is treated as "not this package" rather than trusted.
82
+ const recorded = parsed;
83
+ const name = recorded.name;
84
+ const version = recorded.version;
85
+ if (name !== expectedName)
86
+ return undefined;
87
+ if (typeof version !== 'string' || version.length === 0)
88
+ return undefined;
89
+ return { name: expectedName, version, directory };
90
+ }
91
+ /**
92
+ * Look for a package in the `node_modules` directory of a starting point and every
93
+ * ancestor, exactly the order Node's own resolver applies.
94
+ * @param start - a directory inside the requesting tree.
95
+ * @param name - the dependency name.
96
+ * @returns the installed package, or undefined when no ancestor holds it.
97
+ */
98
+ function walkNodeModules(start, name) {
99
+ let current = resolve(start);
100
+ for (let depth = 0; depth < 64; depth += 1) {
101
+ const candidate = join(current, 'node_modules', ...name.split('/'));
102
+ if (existsSync(join(candidate, 'package.json'))) {
103
+ const found = readInstalledPackage(candidate, name);
104
+ if (found !== undefined)
105
+ return found;
106
+ }
107
+ const parent = dirname(current);
108
+ if (parent === current)
109
+ return undefined;
110
+ current = parent;
111
+ }
112
+ return undefined;
113
+ }
114
+ /** The host's own module directory: the fallback tree for host-installed dependencies. */
115
+ function hostModuleDirectory() {
116
+ return dirname(fileURLToPath(import.meta.url));
117
+ }
118
+ /**
119
+ * Resolve one declared dependency from the host's installed tree.
120
+ *
121
+ * The source checkout's own ancestor chain is searched first (the tree whose
122
+ * `package.json` declared the dependency), then the host's own module chain, then
123
+ * any explicitly configured roots. The first installed package whose own
124
+ * `package.json` name matches wins; nothing is downloaded and no version is
125
+ * inferred.
126
+ * @param search - where to look.
127
+ * @param name - the declared dependency name.
128
+ * @param range - the declared spec, used only in the refusal message.
129
+ * @returns the installed dependency.
130
+ * @throws ManagementError `DEPENDENCY_UNSATISFIED` when it is not installed.
131
+ */
132
+ export function resolveFirstPartyDependency(search, name, range) {
133
+ assertUsableName(name);
134
+ const origins = [resolve(search.sourcePackageDir), hostModuleDirectory()];
135
+ for (const origin of origins) {
136
+ const found = walkNodeModules(origin, name);
137
+ if (found !== undefined)
138
+ return found;
139
+ }
140
+ for (const root of search.extraSearchRoots ?? []) {
141
+ const candidate = join(resolve(root), 'node_modules', ...name.split('/'));
142
+ if (existsSync(join(candidate, 'package.json'))) {
143
+ const found = readInstalledPackage(candidate, name);
144
+ if (found !== undefined)
145
+ return found;
146
+ }
147
+ }
148
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `${name} (declared as ${JSON.stringify(range)}) is not installed anywhere in the host's own dependency tree, `
149
+ + 'so it cannot be materialized into the extension', 'Install the dependency for the host first (or bundle it with the extension); the installer never downloads '
150
+ + 'dependencies for a source-directory install and never silently drops a declared one.');
151
+ }
152
+ /**
153
+ * Collect a package directory's files as archive entries.
154
+ *
155
+ * `node_modules` is skipped (the SDK resolves the transitive graph through this
156
+ * same resolver), and any symbolic link or junction inside the tree is REFUSED
157
+ * rather than followed: a link is how a package reaches bytes outside the content
158
+ * that was resolved.
159
+ * @param root - the package directory.
160
+ * @param limits - the byte bounds.
161
+ * @returns the selected files, in sorted walk order.
162
+ */
163
+ function collectPackageFiles(root, limits) {
164
+ const files = [];
165
+ let totalBytes = 0;
166
+ const walk = (current) => {
167
+ for (const entry of readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
168
+ const absolute = join(current, entry.name);
169
+ if (entry.isSymbolicLink()) {
170
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the installed dependency contains a symbolic link at ${absolute}; a link makes the archived content differ from the content that was resolved`, 'Repair the installed dependency so the tree contains plain files, then retry.');
171
+ }
172
+ if (entry.isDirectory()) {
173
+ if (entry.name === 'node_modules')
174
+ continue;
175
+ walk(absolute);
176
+ continue;
177
+ }
178
+ if (!entry.isFile()) {
179
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the installed dependency contains ${absolute}, which is neither a file nor a directory`, 'Repair the installed dependency, then retry.');
180
+ }
181
+ const size = lstatSync(absolute).size;
182
+ if (size > limits.maxEntryBytes) {
183
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the dependency file ${absolute} is ${size} bytes, over the ${limits.maxEntryBytes}-byte per-file bound`, 'Use a dependency whose individual files fit the archive bounds, or install it outside the extension.');
184
+ }
185
+ totalBytes += size;
186
+ if (totalBytes > limits.maxSourceBytes) {
187
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the dependency tree at ${root} is over the ${limits.maxSourceBytes}-byte archive bound`, 'Use a dependency whose installed tree fits the bounds, or install it outside the extension.');
188
+ }
189
+ files.push({ relative: relativePath(root, absolute), absolute, size });
190
+ }
191
+ };
192
+ walk(root);
193
+ if (files.length === 0) {
194
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the installed dependency at ${root} contains no files`, 'Repair the installed dependency, then retry.');
195
+ }
196
+ return files;
197
+ }
198
+ /** A path relative to the package root, using `/` separators for the archive. */
199
+ function relativePath(root, absolute) {
200
+ return relative(resolve(root), absolute).split('\\').join('/');
201
+ }
202
+ /** Whether a directory is the root or lives underneath it. */
203
+ function isUnder(root, candidate) {
204
+ const rootPath = resolve(root);
205
+ const candidatePath = resolve(candidate);
206
+ if (candidatePath === rootPath)
207
+ return true;
208
+ const relativePathFromRoot = relative(rootPath, candidatePath);
209
+ return relativePathFromRoot.length > 0 && !relativePathFromRoot.startsWith('..') && !isAbsolute(relativePathFromRoot);
210
+ }
211
+ /**
212
+ * Build a deterministic zip of one installed dependency directory.
213
+ *
214
+ * The result is the archive the SDK's dependency graph fetches and verifies. Its
215
+ * SHA-512 is the integrity handed to the graph; the directory's own
216
+ * `digestInstalledContent` is checked before and after the build so a concurrent
217
+ * change refuses.
218
+ * @param dependency - the resolved installed dependency.
219
+ * @param limits - the byte bounds.
220
+ * @returns the archive bytes, their integrity, and the directory digest.
221
+ * @throws ManagementError `DEPENDENCY_UNSATISFIED` for any refusal.
222
+ */
223
+ export function buildDependencyArchive(dependency, limits) {
224
+ const files = collectPackageFiles(dependency.directory, limits);
225
+ // The same content-digest discipline the SDK applies to a directory install:
226
+ // the tree's identity is checked around the build, so the archive cannot be
227
+ // assembled from a tree that changed while it was being read.
228
+ let before;
229
+ try {
230
+ before = digestInstalledContent(dependency.directory);
231
+ }
232
+ catch (error) {
233
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the installed dependency ${dependency.name}@${dependency.version} could not be digested: ${error instanceof Error ? error.message : String(error)}`, 'Repair the installed dependency so every file is readable, then retry.');
234
+ }
235
+ const zip = new AdmZip();
236
+ for (const file of files)
237
+ zip.addFile(file.relative, readFileSync(file.absolute));
238
+ const bytes = new Uint8Array(zip.toBuffer());
239
+ if (bytes.byteLength > limits.maxArchiveBytes) {
240
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the dependency ${dependency.name}@${dependency.version} archives to ${bytes.byteLength} bytes, over the ${limits.maxArchiveBytes}-byte dependency bound`, 'Use a dependency whose archive fits the installer bound, or install it outside the extension.');
241
+ }
242
+ const after = digestInstalledContent(dependency.directory);
243
+ if (before !== after) {
244
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the installed dependency ${dependency.name}@${dependency.version} changed while it was being archived`, 'Make sure nothing is installing into the host tree, then retry.');
245
+ }
246
+ return {
247
+ bytes,
248
+ integrity: createHash('sha512').update(bytes).digest('hex'),
249
+ contentDigest: after,
250
+ };
251
+ }
252
+ /**
253
+ * Decide how this host supplies one dependency, applying the operator gate.
254
+ *
255
+ * With the preinstalled gate on, the resolved directory is returned as-is (the SDK
256
+ * copies it and records provenance; scripts are not run and are NOT stripped).
257
+ * With the gate off, the bytes arm applies: the SDK's EXT-10 refusal is consulted
258
+ * BEFORE any archive is built, so a script-declaring dependency refuses cheaply
259
+ * with the SDK's exact named error, then the archive is built and its SHA-512 is
260
+ * returned as the integrity the SDK will verify.
261
+ * @param options - the search tree and gate.
262
+ * @param limits - the archive bounds used by the bytes arm.
263
+ * @param name - the declared dependency name.
264
+ * @param range - the declared dependency spec, for refusals.
265
+ * @returns the supply decision.
266
+ * @throws ManagementError `DEPENDENCY_UNSATISFIED` for every refusal.
267
+ */
268
+ function resolveDependency(options, limits, name, range) {
269
+ const dependency = resolveFirstPartyDependency(options, name, range);
270
+ if (options.allowPreinstalled === true) {
271
+ const root = options.preinstalledRoot;
272
+ if (root === undefined || !isUnder(root, dependency.directory)) {
273
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `${dependency.name}@${dependency.version} is installed at ${dependency.directory}, outside the operator-configured preinstalled root ${root ?? '(none configured)'}; preinstalled content may only be copied from inside that root`, 'Configure the preinstalled-dependency root so it contains the host tree this dependency was installed into.');
274
+ }
275
+ return { kind: 'preinstalled', dependency, provenance: 'preinstalled' };
276
+ }
277
+ // The SDK's own EXT-10 gate runs before any bytes are built: a dependency that
278
+ // declares an install-time script refuses here with the SDK's exact named error
279
+ // instead of after a full archive/extract cycle. The SDK checks again after
280
+ // extraction; this copy is the cheap, identical refusal.
281
+ refuseInstallScripts(dependency.directory, dependency.name, dependency.version);
282
+ const built = buildDependencyArchive(dependency, limits);
283
+ return { kind: 'registry-bytes', dependency, integrity: built.integrity, bytes: built.bytes };
284
+ }
285
+ /**
286
+ * Create the first-party dependency graph supplied to the SDK's directory installer.
287
+ *
288
+ * Resolution and archiving happen lazily, one dependency at a time: `resolve`
289
+ * builds the exact bytes the SDK will verify and `fetch` hands back those same
290
+ * bytes, so the integrity the lock records is the integrity of the served archive
291
+ * rather than a value guessed from metadata. Only the most recently built archive
292
+ * is retained, which matches the SDK's resolve-then-fetch sequence and bounds
293
+ * memory to one dependency.
294
+ *
295
+ * The preinstalled-content gate is OPT-IN and OFF by default, matching the SDK:
296
+ * without it every dependency is served as verified bytes and EXT-10 applies. With
297
+ * it, the SDK requires `preinstalledRoot`, copies the resolved directory into the
298
+ * extension's own `node_modules`, and records `provenance: 'preinstalled'`.
299
+ * @param options - the search tree, bounds, gate and forwarded graph limits.
300
+ * @returns the install-dependency graph plus its resolver and archive halves.
301
+ * @throws ManagementError `DEPENDENCY_UNSATISFIED` when the gate has no root.
302
+ */
303
+ export function createFirstPartyDependencyGraph(options) {
304
+ const limits = {
305
+ maxSourceBytes: options.maxSourceBytes ?? DEFAULT_ARCHIVE_LIMITS.maxTotalBytes,
306
+ maxEntryBytes: options.maxEntryBytes ?? DEFAULT_ARCHIVE_LIMITS.maxEntryBytes,
307
+ maxArchiveBytes: options.maxArchiveBytes ?? MAX_DEPENDENCY_ARCHIVE_BYTES,
308
+ };
309
+ let preinstalledGate;
310
+ if (options.allowPreinstalled === true) {
311
+ const root = options.preinstalledRoot;
312
+ if (root === undefined || root.trim().length === 0) {
313
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', 'the preinstalled-dependency gate is enabled but no operator-configured preinstalled root was supplied', 'Configure the root directory that contains the host dependency tree, or leave the gate off.');
314
+ }
315
+ preinstalledGate = { allowPreinstalled: true, preinstalledRoot: resolve(root) };
316
+ }
317
+ /** The archive built by the most recent resolve, keyed by name@version. */
318
+ let latest;
319
+ const keyOf = (name, version) => `${name}@${version}`;
320
+ const resolver = {
321
+ async resolve(name, range) {
322
+ const supply = resolveDependency(options, limits, name, range);
323
+ const key = keyOf(supply.dependency.name, supply.dependency.version);
324
+ if (supply.kind === 'preinstalled') {
325
+ return {
326
+ name: supply.dependency.name,
327
+ version: supply.dependency.version,
328
+ dir: supply.dependency.directory,
329
+ provenance: 'preinstalled',
330
+ };
331
+ }
332
+ latest = { key, bytes: supply.bytes };
333
+ return {
334
+ name: supply.dependency.name,
335
+ version: supply.dependency.version,
336
+ tarball: { kind: 'local', requested: key },
337
+ integrity: supply.integrity,
338
+ };
339
+ },
340
+ };
341
+ const archive = createZipArchiveSource(async (source) => {
342
+ if (latest === undefined || latest.key !== source.requested) {
343
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the dependency archive ${source.requested} was not built by the resolver, so it cannot be served`, 'Retry the install; the graph builds each archive immediately before the SDK fetches it.');
344
+ }
345
+ if (latest.bytes.byteLength > MAX_DEPENDENCY_ARCHIVE_BYTES) {
346
+ throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `the dependency archive ${source.requested} is ${latest.bytes.byteLength} bytes, over the ${MAX_DEPENDENCY_ARCHIVE_BYTES}-byte fetch bound`, 'Use a dependency whose archive fits the installer bound.');
347
+ }
348
+ return latest.bytes;
349
+ });
350
+ /** The optional graph fields, added only when configured (no empty-object spreads). */
351
+ const optionalGraphFields = {};
352
+ if (preinstalledGate !== undefined) {
353
+ optionalGraphFields.allowPreinstalled = preinstalledGate.allowPreinstalled;
354
+ optionalGraphFields.preinstalledRoot = preinstalledGate.preinstalledRoot;
355
+ }
356
+ if (options.maxDepth !== undefined)
357
+ optionalGraphFields.maxDepth = options.maxDepth;
358
+ if (options.maxPackages !== undefined)
359
+ optionalGraphFields.maxPackages = options.maxPackages;
360
+ const graph = {
361
+ resolver,
362
+ archive,
363
+ ...optionalGraphFields,
364
+ };
365
+ return { graph, resolver, archive };
366
+ }
367
+ /** Re-export for callers that want the size bound wording. */
368
+ export { DEFAULT_ARCHIVE_LIMITS };
369
+ //# sourceMappingURL=extension-dependencies.js.map
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Provider-backed dispatch for extension-declared tools (EXT-19, EXT-21, EXT-22, ARCH-05).
3
+ *
4
+ * The TMX runtime API has **no tool-invocation surface**. `ExtensionInstanceV1` exposes
5
+ * exactly `createProvider` and `dispose` — nothing else. So an extension's tool is not
6
+ * something the host calls directly; the tool is a caller-facing NAME whose contract is
7
+ * bound to an IMPLEMENTATION, and the implementation is a model PROVIDER.
8
+ *
9
+ * manifest.tools[] -> the caller-facing name and its contract
10
+ * manifest.implementations[] -> which contract, served by which model
11
+ * instance.createProvider() -> the thing that actually runs
12
+ *
13
+ * That chain is what this module walks. An earlier version of the host registrar
14
+ * registered a tool whose `execute` simply REFUSED, which was honest about there being no
15
+ * implementation bound but left the bridge functionally incomplete: the name appeared in
16
+ * the registry and could never be called.
17
+ *
18
+ * The context is INJECTED so the chain is testable without a real extension on disk.
19
+ *
20
+ * @module @try-works/dsh-model-as-tool/extension-dispatch
21
+ */
22
+ import type { PreparedAssetMap } from './prepared-assets.ts';
23
+ /** What a manifest declares about one tool. */
24
+ export interface DeclaredTool {
25
+ readonly name: string;
26
+ readonly contractId: string;
27
+ readonly contractVersion: string;
28
+ }
29
+ /** What a manifest declares about one implementation. */
30
+ export interface DeclaredImplementation {
31
+ readonly id: string;
32
+ readonly contractId: string;
33
+ readonly contractVersion: string;
34
+ /** The model that serves the contract. */
35
+ readonly modelId: string;
36
+ }
37
+ /** The parts of a manifest this module needs. */
38
+ export interface DispatchManifest {
39
+ readonly tools: readonly DeclaredTool[];
40
+ readonly implementations: readonly DeclaredImplementation[];
41
+ }
42
+ /** A provider, narrowed to what dispatch needs. */
43
+ export interface DispatchProvider {
44
+ load(request: {
45
+ readonly assets: unknown;
46
+ readonly signal: AbortSignal;
47
+ /** The manifest model id; the managed worker's load keys by it. */
48
+ readonly modelId?: string;
49
+ }): Promise<void>;
50
+ invoke(request: {
51
+ readonly implementationId: string;
52
+ readonly input: Readonly<Record<string, unknown>>;
53
+ readonly context: unknown;
54
+ }): Promise<Record<string, unknown>>;
55
+ }
56
+ /** What dispatch needs from the host to reach an extension. */
57
+ export interface DispatchEnvironment {
58
+ /** The manifest of the activated extension that declared the tool. */
59
+ getManifest(): DispatchManifest;
60
+ /** Create (or return a cached) provider for a model deployment. */
61
+ providerFor(modelId: string): Promise<DispatchProvider>;
62
+ /** Build the invocation context, which carries host-issued identity. */
63
+ contextFor(implementationId: string, signal: AbortSignal): unknown;
64
+ /**
65
+ * The host's verified prepared-asset map for THIS extension, or undefined
66
+ * when nothing was prepared (the load then receives the EMPTY map — an
67
+ * extension needing assets refuses by name inside its own load, which is the
68
+ * honest MOD-02 behavior rather than an invented verification). Closes over
69
+ * the extension the environment was built for.
70
+ */
71
+ assetsFor?(): PreparedAssetMap | undefined;
72
+ }
73
+ /** Why a tool could not be dispatched. */
74
+ export interface DispatchFailure {
75
+ readonly code: 'not-declared' | 'no-implementation' | 'unsupported';
76
+ readonly message: string;
77
+ readonly remedy: string;
78
+ }
79
+ /**
80
+ * Resolve the implementation chain for a declared tool.
81
+ *
82
+ * @param manifest - the activated extension's manifest.
83
+ * @param toolName - the caller-facing name.
84
+ * @returns the tool, its implementation, or why the chain is incomplete.
85
+ */
86
+ export declare function resolveImplementation(manifest: DispatchManifest, toolName: string): {
87
+ readonly ok: true;
88
+ readonly tool: DeclaredTool;
89
+ readonly implementation: DeclaredImplementation;
90
+ } | {
91
+ readonly ok: false;
92
+ readonly failure: DispatchFailure;
93
+ };
94
+ /**
95
+ * Build an `execute` for one declared tool.
96
+ *
97
+ * The provider is created lazily on first call, so ACTIVATION stays lightweight as EXT-19
98
+ * requires: nothing is loaded until a caller actually invokes the tool.
99
+ *
100
+ * @param environment - how to reach the extension.
101
+ * @param toolName - the caller-facing name.
102
+ * @returns the execute function, or the failure that prevents one.
103
+ */
104
+ export declare function createDispatch(environment: DispatchEnvironment, toolName: string): Promise<{
105
+ readonly ok: true;
106
+ readonly execute: (args: Record<string, unknown>, signal: AbortSignal) => Promise<Record<string, unknown>>;
107
+ } | {
108
+ readonly ok: false;
109
+ readonly failure: DispatchFailure;
110
+ }>;
111
+ /**
112
+ * Describe a dispatch failure for a caller (EXT-30).
113
+ * @param failure - the failure.
114
+ * @returns a readable message naming what to do.
115
+ */
116
+ export declare function describeDispatchFailure(failure: DispatchFailure): string;
117
+ //# sourceMappingURL=extension-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-dispatch.d.ts","sourceRoot":"","sources":["../src/extension-dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Provider-backed dispatch for extension-declared tools (EXT-19, EXT-21, EXT-22, ARCH-05).
3
+ *
4
+ * The TMX runtime API has **no tool-invocation surface**. `ExtensionInstanceV1` exposes
5
+ * exactly `createProvider` and `dispose` — nothing else. So an extension's tool is not
6
+ * something the host calls directly; the tool is a caller-facing NAME whose contract is
7
+ * bound to an IMPLEMENTATION, and the implementation is a model PROVIDER.
8
+ *
9
+ * manifest.tools[] -> the caller-facing name and its contract
10
+ * manifest.implementations[] -> which contract, served by which model
11
+ * instance.createProvider() -> the thing that actually runs
12
+ *
13
+ * That chain is what this module walks. An earlier version of the host registrar
14
+ * registered a tool whose `execute` simply REFUSED, which was honest about there being no
15
+ * implementation bound but left the bridge functionally incomplete: the name appeared in
16
+ * the registry and could never be called.
17
+ *
18
+ * The context is INJECTED so the chain is testable without a real extension on disk.
19
+ *
20
+ * @module @try-works/dsh-model-as-tool/extension-dispatch
21
+ */
22
+ import { assertNoReservedMeta } from '@try-works/tool-mode-extension-sdk';
23
+ /**
24
+ * Resolve the implementation chain for a declared tool.
25
+ *
26
+ * @param manifest - the activated extension's manifest.
27
+ * @param toolName - the caller-facing name.
28
+ * @returns the tool, its implementation, or why the chain is incomplete.
29
+ */
30
+ export function resolveImplementation(manifest, toolName) {
31
+ const tool = manifest.tools.find(candidate => candidate.name === toolName);
32
+ if (tool === undefined) {
33
+ return {
34
+ ok: false,
35
+ failure: {
36
+ code: 'not-declared',
37
+ message: `${toolName} is not declared by this extension's manifest`,
38
+ remedy: 'A registered tool must be declared in the manifest it was published from.',
39
+ },
40
+ };
41
+ }
42
+ const implementation = manifest.implementations.find(candidate => candidate.contractId === tool.contractId && candidate.contractVersion === tool.contractVersion);
43
+ if (implementation === undefined) {
44
+ /**
45
+ * THE HONEST REFUSAL, and the case the earlier stub conflated with every other.
46
+ *
47
+ * An extension may DECLARE a tool it does not yet serve — EmbeddingGemma does exactly
48
+ * this, listing contracts whose ONNX path is not wired. The name is legitimately
49
+ * registered and legitimately uncallable, and the caller must be told WHICH of those
50
+ * two situations they are in.
51
+ */
52
+ return {
53
+ ok: false,
54
+ failure: {
55
+ code: 'no-implementation',
56
+ message: `${toolName} declares contract ${tool.contractId}, which no implementation serves`,
57
+ remedy: 'The extension declares this tool but binds no model to it yet; it is not callable in this build.',
58
+ },
59
+ };
60
+ }
61
+ return { ok: true, tool, implementation };
62
+ }
63
+ /**
64
+ * Build an `execute` for one declared tool.
65
+ *
66
+ * The provider is created lazily on first call, so ACTIVATION stays lightweight as EXT-19
67
+ * requires: nothing is loaded until a caller actually invokes the tool.
68
+ *
69
+ * @param environment - how to reach the extension.
70
+ * @param toolName - the caller-facing name.
71
+ * @returns the execute function, or the failure that prevents one.
72
+ */
73
+ export async function createDispatch(environment, toolName) {
74
+ const resolved = resolveImplementation(environment.getManifest(), toolName);
75
+ if (!resolved.ok)
76
+ return resolved;
77
+ const { implementation } = resolved;
78
+ return {
79
+ ok: true,
80
+ async execute(args, signal) {
81
+ // Resolved per call rather than captured, so a provider that was unloaded — or an
82
+ // extension that was deactivated and reactivated — is not served from a stale handle.
83
+ const provider = await environment.providerFor(implementation.modelId);
84
+ /**
85
+ * The HOST-VERIFIED prepared-asset map, when a preparation completed.
86
+ * An absent map means the empty map — the extension's own load refuses a
87
+ * missing group by name (MOD-02), which is the honest outcome rather than
88
+ * a host that invents a verification that never happened. The same map
89
+ * reaches BOTH provider routes here: the in-process module provider and
90
+ * the managed worker (its load forwards the assets to the child).
91
+ */
92
+ const prepared = environment.assetsFor?.();
93
+ await provider.load({ assets: prepared ?? { get: () => undefined, assetIds: [] }, signal, modelId: implementation.modelId });
94
+ const value = await provider.invoke({
95
+ implementationId: implementation.id,
96
+ input: args,
97
+ context: environment.contextFor(implementation.id, signal),
98
+ });
99
+ /**
100
+ * THE EXT-17 META RESERVATION IS ENFORCED HERE, AT THE DISPATCH BOUNDARY.
101
+ *
102
+ * The reserved root `meta` is HOST-authored provenance; an extension supplying one
103
+ * is asserting who ran it and under what receipt, which it does not get to do. The
104
+ * ProviderBridge asserts this on the CATALOG path, but the contribution path
105
+ * (module and declarative providers alike) went unasserted — so a provider that
106
+ * smuggled a `meta` key reached callers whenever its declared output schema did not
107
+ * require the envelope. Asserting at the boundary the two provider routes share
108
+ * closes the gap for both, and it runs BEFORE any host stamping, so a forged
109
+ * envelope is refused rather than overwritten.
110
+ */
111
+ assertNoReservedMeta(value, implementation.id);
112
+ return value;
113
+ },
114
+ };
115
+ }
116
+ /**
117
+ * Describe a dispatch failure for a caller (EXT-30).
118
+ * @param failure - the failure.
119
+ * @returns a readable message naming what to do.
120
+ */
121
+ export function describeDispatchFailure(failure) {
122
+ return `${failure.message}. ${failure.remedy}`;
123
+ }
124
+ //# sourceMappingURL=extension-dispatch.js.map