@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
package/lib/mcp/server.js CHANGED
@@ -53,15 +53,44 @@ export async function dispatch(request, host, options = {}) {
53
53
  negotiateVersion(metadata);
54
54
  // Capabilities the CLIENT requires. A requirement this server cannot meet is
55
55
  // -32021 rather than a silent partial service.
56
+ const wiring = { subscriptions: options.subscriptions !== undefined };
56
57
  const required = requiredCapabilities(metadata.clientCapabilities);
57
- const unsupported = unsupportedRequiredCapabilities(required);
58
+ const unsupported = unsupportedRequiredCapabilities(required, wiring);
58
59
  if (unsupported.length > 0) {
59
- throw new WireError(ERROR_CODES.unsupportedClientCapability, `the client requires unsupported capability: ${unsupported.join(', ')}`, { unsupported });
60
+ throw new WireError(ERROR_CODES.unsupportedClientCapability, `the client requires unsupported capability: ${unsupported.join(', ')}`,
61
+ /**
62
+ * THE PINNED SCHEMA'S OWN FIELD NAME, and this was WRONG.
63
+ *
64
+ * `MissingRequiredClientCapabilityError` REQUIRES `data.requiredCapabilities`. This
65
+ * sent `{ unsupported: [...] }`, which Ajv rejects against the pinned definition:
66
+ *
67
+ * INVALID -> /error/data must have required property 'requiredCapabilities'
68
+ *
69
+ * It went unnoticed because the GENERIC `JSONRPCErrorResponse` accepts ANY `data`,
70
+ * every fixture took that path, and no fixture covered `-32021` at all — so the
71
+ * conformance suite validated a shape the specific error definition forbids.
72
+ * P26-03 requires `-32021` "and the schema-defined data"; this is that shape.
73
+ */
74
+ /**
75
+ * A `ClientCapabilities` OBJECT, not an array of names — and this was STILL wrong.
76
+ *
77
+ * The previous correction fixed the FIELD NAME (`unsupported` -> `requiredCapabilities`)
78
+ * and left the VALUE as an array of strings. The pinned definition is precise:
79
+ *
80
+ * requiredCapabilities -> $ref ClientCapabilities (type: object)
81
+ *
82
+ * so the shape is `{ sampling: {} }`, a capabilities object keyed by capability name.
83
+ * Both defects are the same kind — a plausible shape that no fixture covered — and this
84
+ * one survived the first fix because only a LIVE response validated against the pinned
85
+ * definition could distinguish an array from an object. The fixture probe never saw it,
86
+ * and no test asserted the value's TYPE.
87
+ */
88
+ { requiredCapabilities: Object.fromEntries(unsupported.map(name => [name, {}])) });
60
89
  }
61
90
  switch (request.method) {
62
91
  case 'server/discover':
63
92
  return {
64
- response: { jsonrpc: '2.0', id, result: successResult(discoverResult(), host.serverInfo) },
93
+ response: { jsonrpc: '2.0', id, result: successResult(discoverResult(wiring), host.serverInfo) },
65
94
  httpStatus: 200,
66
95
  };
67
96
  case 'tools/list':
@@ -72,7 +101,15 @@ export async function dispatch(request, host, options = {}) {
72
101
  result: successResult({
73
102
  // Deterministic ordering (MCP-06): the caller sees a stable list, so
74
103
  // a client diffing two listings does not see spurious changes.
104
+ //
105
+ // SEC-04: an AUTHENTICATED external caller sees only its GRANTED
106
+ // tools — the list is the client's authorized set, not the operator's
107
+ // whole surface. The grant stays the AUTHORITY: calling a
108
+ // non-granted tool still yields the grant refusal, this only
109
+ // narrows what a client is shown. The caller-less path (stdio /
110
+ // native) sees the full surface.
75
111
  tools: [...host.listTools()]
112
+ .filter(tool => options.caller === undefined || options.caller.allowedTools.includes(tool.name))
76
113
  .sort((left, right) => left.name.localeCompare(right.name))
77
114
  .map(toMcpTool),
78
115
  // Required by the schema's CacheableResult; omitting them makes the
@@ -101,7 +138,7 @@ export async function dispatch(request, host, options = {}) {
101
138
  };
102
139
  }
103
140
  case 'tools/call':
104
- return await callTool(request, id, host, options.signal ?? new AbortController().signal);
141
+ return await callTool(request, id, host, options.signal ?? new AbortController().signal, options.caller);
105
142
  default:
106
143
  throw new WireError(ERROR_CODES.methodNotFound, `unknown method ${JSON.stringify(request.method)}`);
107
144
  }
@@ -114,13 +151,43 @@ export async function dispatch(request, host, options = {}) {
114
151
  // payload (MCP-10), so it leaves as a JSON-RPC error.
115
152
  return {
116
153
  response: errorResponse(id, wire),
117
- // Header problems are a transport-level rejection as well as a JSON-RPC error.
118
- httpStatus: wire.code === ERROR_CODES.headerMismatch || wire.code === ERROR_CODES.invalidParams ? 400 : 200,
154
+ /**
155
+ * THE PINNED SCHEMA'S OWN `MUST`, read from the schema rather than inferred.
156
+ *
157
+ * Three error definitions state it in as many words — "For HTTP, the response status
158
+ * code MUST be `400 Bad Request`":
159
+ *
160
+ * HeaderMismatchError -> -32020
161
+ * MissingRequiredClientCapabilityError -> -32021
162
+ * UnsupportedProtocolVersionError -> -32022
163
+ *
164
+ * Only the FIRST and `invalidParams` were mapped to 400, so a client requiring an
165
+ * unsupported capability — or presenting an unsupported protocol version — received an
166
+ * error body under a 200 status. The suite stayed green either way, so the status was
167
+ * untested in BOTH directions.
168
+ *
169
+ * Kept as a named SET so the next transport-level rejection is a one-line addition
170
+ * rather than another condition somebody can forget.
171
+ */
172
+ httpStatus: TRANSPORT_LEVEL_REJECTIONS.has(wire.code) ? 400 : 200,
119
173
  };
120
174
  }
121
175
  }
176
+ /**
177
+ * The JSON-RPC codes the pinned schema requires be answered with HTTP 400 as well.
178
+ *
179
+ * Every one of these definitions says, verbatim: "For HTTP, the response status code MUST
180
+ * be `400 Bad Request`." Kept as a named set so the rule lives in ONE place and the next
181
+ * transport-level rejection is an addition rather than another forgotten condition.
182
+ */
183
+ const TRANSPORT_LEVEL_REJECTIONS = new Set([
184
+ ERROR_CODES.headerMismatch, // -32020
185
+ ERROR_CODES.unsupportedClientCapability, // -32021
186
+ ERROR_CODES.unsupportedProtocolVersion, // -32022
187
+ ERROR_CODES.invalidParams, // -32602, a malformed request shape
188
+ ]);
122
189
  /** Handle `tools/call`. */
123
- async function callTool(request, id, host, signal) {
190
+ async function callTool(request, id, host, signal, caller) {
124
191
  const name = request.params?.name;
125
192
  if (typeof name !== 'string' || name.length === 0) {
126
193
  throw new WireError(ERROR_CODES.invalidParams, 'tools/call requires params.name');
@@ -140,7 +207,7 @@ async function callTool(request, id, host, signal) {
140
207
  if (definition.importedFromMcp) {
141
208
  throw new WireError(ERROR_CODES.invalidParams, `tool ${JSON.stringify(name)} was imported from MCP and is not re-exported`);
142
209
  }
143
- const outcome = await host.callTool(name, (args ?? {}), signal);
210
+ const outcome = await host.callTool(name, (args ?? {}), signal, caller);
144
211
  const result = outcome.isError === true
145
212
  // An application failure is a tool-error RESULT (P26-04), so the protocol
146
213
  // exchange still succeeded.
@@ -156,13 +223,13 @@ async function callTool(request, id, host, signal) {
156
223
  };
157
224
  }
158
225
  /** The `server/discover` result, in the shape the pinned schema requires. */
159
- function discoverResult() {
226
+ function discoverResult(wiring = {}) {
160
227
  return {
161
228
  // `supportedVersions` is the field the schema defines; there is no
162
229
  // `protocolVersion` on a DiscoverResult, which is the mistake pinning the schema
163
230
  // caught. A client learns the versions the server speaks, not one it picked.
164
231
  supportedVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
165
- capabilities: serverCapabilities(),
232
+ capabilities: serverCapabilities(wiring),
166
233
  ...cacheableResultFields(),
167
234
  };
168
235
  }
@@ -174,10 +241,10 @@ function discoverResult() {
174
241
  * advertised only when it is genuinely implemented.
175
242
  * @returns the `ServerCapabilities` object.
176
243
  */
177
- function serverCapabilities() {
178
- const implemented = new Set(implementedCapabilities());
244
+ function serverCapabilities(wiring = {}) {
245
+ const implemented = new Set(implementedCapabilities(wiring));
179
246
  return {
180
- ...implemented.has('tools') ? { tools: {} } : {},
247
+ ...implemented.has('tools') ? { tools: { listChanged: implemented.has('subscriptions/listen') } } : {},
181
248
  ...implemented.has('prompts') ? { prompts: {} } : {},
182
249
  ...implemented.has('resources/subscribe') ? { resources: {} } : {},
183
250
  ...implemented.has('logging') ? { logging: {} } : {},
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/mcp/stdio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAElE,gEAAgE;AAChE,MAAM,WAAW,QAAQ;IACvB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,2CAA2C;IAC3C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KACzF,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChF;AAED,wBAAwB;AACxB,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACrD,yDAAyD;IACzD,KAAK,IAAI,IAAI,CAAA;IACb,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBtF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,QAAQ,GAAG,WAAW,CAyGrF"}
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/mcp/stdio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAElE,gEAAgE;AAChE,MAAM,WAAW,QAAQ;IACvB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,2CAA2C;IAC3C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KACzF,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChF;AAED,wBAAwB;AACxB,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACrD,yDAAyD;IACzD,KAAK,IAAI,IAAI,CAAA;IACb,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBtF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,QAAQ,GAAG,WAAW,CAiHrF"}
package/lib/mcp/stdio.js CHANGED
@@ -73,9 +73,17 @@ export function createStdioBridge(endpoint, io) {
73
73
  catch {
74
74
  // A malformed frame is answered on the PROTOCOL channel, because the client
75
75
  // is waiting for a response and a diagnostic would not reach it.
76
+ /**
77
+ * The `id` is OMITTED rather than set to `null`, which is the rule `protocol.ts`
78
+ * documents three hundred lines away and this site contradicted.
79
+ *
80
+ * JSON-RPC 2.0 conventionally allows `id: null` for an undeterminable id, but the
81
+ * pinned revision types `RequestId` as `string | integer` and does not admit null,
82
+ * while `id` itself is OPTIONAL. A frame that failed to parse has no id to echo, so
83
+ * omitting it is the conformant way to say "unknown".
84
+ */
76
85
  return frame({
77
86
  jsonrpc: '2.0',
78
- id: null,
79
87
  error: { code: -32700, message: 'the frame is not valid JSON' },
80
88
  });
81
89
  }
@@ -27,7 +27,7 @@
27
27
  *
28
28
  * @module @try-works/dsh-model-as-tool/mcp/subscriptions
29
29
  */
30
- import type { WireRequest } from './protocol.ts';
30
+ import { type WireRequest } from './protocol.ts';
31
31
  /** The notification types the revision defines for catalog changes. */
32
32
  export type SubscriptionFilterKey = 'toolsListChanged' | 'promptsListChanged' | 'resourcesListChanged' | 'resourceSubscriptions';
33
33
  /** The filter a client requested, or the subset the server acknowledged. */
@@ -79,6 +79,7 @@ export declare function acknowledgeFilter(requested: SubscriptionFilter): Subscr
79
79
  /** Manages the active catalog subscriptions. */
80
80
  export declare class SubscriptionRegistry {
81
81
  #private;
82
+ /** Counter for ids this server issues, so an id is never caller-chosen. */
82
83
  /** Active subscriptions, for diagnostics. */
83
84
  get active(): readonly Subscription[];
84
85
  /**
@@ -132,4 +133,45 @@ export declare class SubscriptionRegistry {
132
133
  * @returns the notification.
133
134
  */
134
135
  export declare function cancelSubscription(subscriptionId: string): Record<string, unknown>;
136
+ /**
137
+ * The DELIVERY half of P26-09 (task-73): what a streaming transport
138
+ * implements. The registry owns the subscriptions and the filtering; the sink
139
+ * owns the per-client open streams. A production SSE transport provides its
140
+ * writer here; the tests inject a collecting sink that exercises the SAME
141
+ * consumer path end to end.
142
+ */
143
+ export interface SubscriptionDeliverySink {
144
+ /** Deliver one addressed notification to its client's open stream. */
145
+ deliver(message: SubscriptionMessage): void;
146
+ /** Close every stream belonging to one client (teardown-cancelled first). */
147
+ closeClient(clientId: string, reason: string): void;
148
+ }
149
+ /**
150
+ * Run one catalog-change publication through the delivery consumer: the
151
+ * registry's per-caller filtering decides WHO gets the event, and the sink
152
+ * carries it to each client's stream. Acknowledgement ordering is the
153
+ * registry's own guarantee — an acknowledgement is marked sent before any
154
+ * publish can run, so no event can precede it.
155
+ * @param registry - the subscription state.
156
+ * @param generation - the catalog generation that changed.
157
+ * @param sink - the delivery transport.
158
+ * @returns how many subscribers received the change.
159
+ */
160
+ export declare function deliverCatalogChange(registry: SubscriptionRegistry, generation: number, sink: SubscriptionDeliverySink): number;
161
+ /**
162
+ * The memory sink: one per-client queue, used by the tests to exercise the
163
+ * consumer and by the host as the transport seam until a streaming endpoint
164
+ * injects its writer. It honours the same contract a real transport must:
165
+ * delivery only to the addressed client, and closeClient stops that client's
166
+ * queue entirely.
167
+ */
168
+ export declare class MemoryDeliverySink implements SubscriptionDeliverySink {
169
+ #private;
170
+ /** The delivered messages for one client, in delivery order. */
171
+ forClient(clientId: string): readonly SubscriptionMessage[];
172
+ /** Whether the sink closed a client, and why. */
173
+ closeReason(clientId: string): string | undefined;
174
+ deliver(message: SubscriptionMessage): void;
175
+ closeClient(clientId: string, reason: string): void;
176
+ }
135
177
  //# sourceMappingURL=subscriptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/mcp/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB,oBAAoB,GACpB,sBAAsB,GACtB,uBAAuB,CAAA;AAE3B,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC,GAAG;IACjF,QAAQ,CAAC,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACnD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,qBAAqB,EAAyB,CAAA;AAEvF,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,2CAA2C,CAAA;AAE3E,+BAA+B;AAC/B,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;IACzC;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,4BAA4B;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gCAAgC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,GAAG,kBAAkB,CAQnF;AAED,gDAAgD;AAChD,qBAAa,oBAAoB;;IAK/B,6CAA6C;IAC7C,IAAI,MAAM,IAAI,SAAS,YAAY,EAAE,CAEpC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG;QAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACxC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;KAChC;IA+BD;;;;;;OAMG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,mBAAmB,EAAE;IA0B1E;;;;;;;OAOG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,mBAAmB,EAAE;IAyBxD;;;;;OAKG;IACH,KAAK,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IASxD,kDAAkD;IAClD,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,YAAY,EAAE;CAGrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMlF"}
1
+ {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/mcp/subscriptions.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@
27
27
  *
28
28
  * @module @try-works/dsh-model-as-tool/mcp/subscriptions
29
29
  */
30
+ import { ERROR_CODES, WireError } from "./protocol.js";
30
31
  /**
31
32
  * The notification types this server actually emits.
32
33
  *
@@ -59,7 +60,6 @@ export function acknowledgeFilter(requested) {
59
60
  export class SubscriptionRegistry {
60
61
  #subscriptions = new Map();
61
62
  /** Counter for ids this server issues, so an id is never caller-chosen. */
62
- #issued = 0;
63
63
  /** Active subscriptions, for diagnostics. */
64
64
  get active() {
65
65
  return [...this.#subscriptions.values()];
@@ -74,13 +74,47 @@ export class SubscriptionRegistry {
74
74
  * @returns the result envelope, the acknowledgement, and the subscription id.
75
75
  */
76
76
  listen(request, clientId) {
77
- const requested = (request.params?.notifications ?? {});
78
- const acknowledged = acknowledgeFilter(requested);
79
- // Server-issued. A caller-chosen id would let one client address another
80
- // client's subscription in a later cancellation.
81
- const subscriptionId = `sub_${Date.now().toString(36)}_${(++this.#issued).toString(36)}`;
82
- const subscription = { subscriptionId, clientId, acknowledged, announced: true };
83
- this.#subscriptions.set(subscriptionId, subscription);
77
+ /**
78
+ * `notifications` IS REQUIRED, and this used to default it.
79
+ *
80
+ * The pinned schema's `SubscriptionsListenRequestParams` declares
81
+ * `required: ["_meta","notifications"]`. Defaulting an absent filter to `{}` and
82
+ * returning SUCCESS contradicted that AND contradicted this module's own rule that a
83
+ * missing required field is `invalidParams` rather than a defaulted value — a client
84
+ * would receive an acknowledgement for a subscription it never described, and the
85
+ * filter it believed it had sent would be silently empty.
86
+ */
87
+ const requested = request.params?.notifications;
88
+ if (requested === undefined || requested === null || typeof requested !== 'object' || Array.isArray(requested)) {
89
+ throw new WireError(ERROR_CODES.invalidParams, 'subscriptions/listen requires params.notifications; a missing filter is invalid rather than defaulted');
90
+ }
91
+ const filter = requested;
92
+ const acknowledged = acknowledgeFilter(filter);
93
+ /**
94
+ * THE SUBSCRIPTION IS IDENTIFIED BY THE LISTEN REQUEST'S OWN ID.
95
+ *
96
+ * This used to invent `sub_<time>_<n>`, which was WRONG, and the pinned schema says so
97
+ * in as many words. `CancelledNotification` states that the server's stream-terminating
98
+ * cancellation "references the ID of the `subscriptions/listen` request that opened the
99
+ * stream", and `SubscriptionsListenResultMetaObject` types its required
100
+ * `io.modelcontextprotocol/subscriptionId` as a **`RequestId`** — the same type as a
101
+ * request id — "so the client can correlate it with the originating subscription".
102
+ *
103
+ * A server-invented id that the client never saw cannot be correlated with anything, so
104
+ * a client could neither match the acknowledgement to its request nor recognise the
105
+ * cancellation that ends the stream.
106
+ *
107
+ * The original comment claimed a caller-chosen id would let one client address
108
+ * another's subscription. That concern is real but it is NOT addressed by inventing an
109
+ * id: subscriptions are keyed by this id and BOUND to `clientId`, and the binding is
110
+ * what prevents cross-client addressing.
111
+ */
112
+ const subscriptionId = request.id;
113
+ if (typeof subscriptionId !== 'string' && typeof subscriptionId !== 'number') {
114
+ throw new WireError(ERROR_CODES.invalidParams, 'subscriptions/listen requires a string or integer request id, which IS the subscription id');
115
+ }
116
+ const subscription = { subscriptionId: String(subscriptionId), clientId, acknowledged, announced: true };
117
+ this.#subscriptions.set(String(subscriptionId), subscription);
84
118
  return {
85
119
  result: {
86
120
  resultType: 'complete',
@@ -98,7 +132,7 @@ export class SubscriptionRegistry {
98
132
  _meta: { [SUBSCRIPTION_ID_KEY]: subscriptionId },
99
133
  },
100
134
  },
101
- subscriptionId,
135
+ subscriptionId: String(subscriptionId),
102
136
  };
103
137
  }
104
138
  /**
@@ -204,4 +238,51 @@ export function cancelSubscription(subscriptionId) {
204
238
  params: { requestId: subscriptionId },
205
239
  };
206
240
  }
241
+ /**
242
+ * Run one catalog-change publication through the delivery consumer: the
243
+ * registry's per-caller filtering decides WHO gets the event, and the sink
244
+ * carries it to each client's stream. Acknowledgement ordering is the
245
+ * registry's own guarantee — an acknowledgement is marked sent before any
246
+ * publish can run, so no event can precede it.
247
+ * @param registry - the subscription state.
248
+ * @param generation - the catalog generation that changed.
249
+ * @param sink - the delivery transport.
250
+ * @returns how many subscribers received the change.
251
+ */
252
+ export function deliverCatalogChange(registry, generation, sink) {
253
+ const messages = registry.publishToolListChanged(generation);
254
+ for (const message of messages)
255
+ sink.deliver(message);
256
+ return messages.length;
257
+ }
258
+ /**
259
+ * The memory sink: one per-client queue, used by the tests to exercise the
260
+ * consumer and by the host as the transport seam until a streaming endpoint
261
+ * injects its writer. It honours the same contract a real transport must:
262
+ * delivery only to the addressed client, and closeClient stops that client's
263
+ * queue entirely.
264
+ */
265
+ export class MemoryDeliverySink {
266
+ #queues = new Map();
267
+ #closed = new Map();
268
+ /** The delivered messages for one client, in delivery order. */
269
+ forClient(clientId) {
270
+ return [...(this.#queues.get(clientId) ?? [])];
271
+ }
272
+ /** Whether the sink closed a client, and why. */
273
+ closeReason(clientId) {
274
+ return this.#closed.get(clientId);
275
+ }
276
+ deliver(message) {
277
+ if (this.#closed.has(message.clientId))
278
+ return;
279
+ const queue = this.#queues.get(message.clientId) ?? [];
280
+ queue.push(message);
281
+ this.#queues.set(message.clientId, queue);
282
+ }
283
+ closeClient(clientId, reason) {
284
+ this.#closed.set(clientId, reason);
285
+ this.#queues.delete(clientId);
286
+ }
287
+ }
207
288
  //# sourceMappingURL=subscriptions.js.map
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Model qualification and capability resolution (MODEL-01, MODEL-04, MODEL-05, MODEL-06).
3
+ *
4
+ * Four rules, each guarding a way of claiming more than is known:
5
+ *
6
+ * - **A platform table is not proof of correct inference** (MODEL-05). A record is
7
+ * qualified only when the exact revision, artifact hash, tokenizer, processor, engine
8
+ * version, ABI and execution provider are all recorded. A missing field is a GAP, and
9
+ * a gap is not a qualification.
10
+ * - **A model's modality list is not an adapter's** (MODEL-05). A model can support
11
+ * video while the selected adapter does not, so what is ADVERTISED is the INTERSECTION
12
+ * of the two, described as such.
13
+ * - **A model name is not a capability test** (MODEL-06). Capability comes from the
14
+ * resolved model, never from recognising a brand.
15
+ * - **Unknown is not unsupported** (MODEL-06). The requirement says to record them
16
+ * separately, because collapsing them lets an unevaluated capability be reported as a
17
+ * known absence — or, worse, the reverse.
18
+ *
19
+ * @module @try-works/dsh-model-as-tool/model-qualification
20
+ */
21
+ /**
22
+ * What is known about one capability.
23
+ *
24
+ * Three states rather than two, because MODEL-06 requires unknown and unsupported to be
25
+ * recorded separately.
26
+ */
27
+ export type CapabilityState =
28
+ /** Evaluated, and it works. */
29
+ 'supported'
30
+ /** Evaluated, and it does not. */
31
+ | 'unsupported'
32
+ /** NOT evaluated. Reporting this as either of the others is the defect. */
33
+ | 'unknown';
34
+ /** A capability of a model or an adapter. */
35
+ export type Modality = 'text' | 'code' | 'image' | 'audio' | 'video' | 'embedding';
36
+ /**
37
+ * The exact path a model is executed through (MODEL-05).
38
+ *
39
+ * Every field is required for qualification, because each one has been the difference
40
+ * between "the package imports" and "inference is correct".
41
+ */
42
+ export interface QualificationRecord {
43
+ /** The model revision, not a branch or floating tag. */
44
+ readonly modelRevision: string;
45
+ /** The SHA of the exported artifact that is actually loaded. */
46
+ readonly artifactSha: string;
47
+ /** The tokenizer identity. */
48
+ readonly tokenizer: string;
49
+ /** The processor or preprocessing recipe identity. */
50
+ readonly processor: string;
51
+ /** The engine or package version. */
52
+ readonly engineVersion: string;
53
+ /** The Node ABI. */
54
+ readonly nodeAbi: string;
55
+ /** The CPU architecture, e.g. `x64`. */
56
+ readonly architecture: string;
57
+ /** The execution provider, e.g. `cpu` or `wasm`. */
58
+ readonly executionProvider: string;
59
+ }
60
+ /** The fields a qualification record must carry. */
61
+ export declare const QUALIFICATION_FIELDS: readonly (keyof QualificationRecord)[];
62
+ /**
63
+ * Report which qualification fields are missing (MODEL-05).
64
+ *
65
+ * A platform table, a successful package import or a runnable base model is not proof of
66
+ * correct inference, so a record with gaps is not a qualification however well it runs.
67
+ * @param record - the partial record.
68
+ * @returns the missing fields.
69
+ */
70
+ export declare function qualificationGaps(record: Partial<QualificationRecord>): readonly (keyof QualificationRecord)[];
71
+ /**
72
+ * Whether a record qualifies a path (MODEL-05).
73
+ * @param record - the partial record.
74
+ * @returns whether every required field is present.
75
+ */
76
+ export declare function isQualified(record: Partial<QualificationRecord>): record is QualificationRecord;
77
+ /** What a model supports, and what an adapter supports. */
78
+ export interface ModalityClaim {
79
+ readonly modelSupports: readonly Modality[];
80
+ readonly adapterSupports: readonly Modality[];
81
+ }
82
+ /**
83
+ * The capabilities that may be ADVERTISED (MODEL-05).
84
+ *
85
+ * The intersection, because a model can support a modality while the selected adapter
86
+ * does not. Advertising the model's list would promise something the adapter cannot do.
87
+ * @param claim - what each side supports.
88
+ * @returns the advertisable modalities.
89
+ */
90
+ export declare function advertisableModalities(claim: ModalityClaim): readonly Modality[];
91
+ /**
92
+ * Describe a model's capability as the INTERSECTION (MODEL-05).
93
+ *
94
+ * The description names what is not advertisable and why, so a reader is not left to
95
+ * infer that the model lacks the modality when in fact the adapter does.
96
+ * @param claim - what each side supports.
97
+ * @returns a description that is true of the pairing.
98
+ */
99
+ export declare function describeCapability(claim: ModalityClaim): string;
100
+ /**
101
+ * Resolve one capability (MODEL-06).
102
+ *
103
+ * A capability is resolved from what was EVALUATED. A model name is never consulted,
104
+ * because a brand is not a capability test, and an unevaluated capability returns
105
+ * `unknown` rather than being inferred from a sibling.
106
+ * @param evidence - the observed result, or undefined when not evaluated.
107
+ * @returns the state.
108
+ */
109
+ export declare function resolveCapability(evidence: boolean | undefined): CapabilityState;
110
+ /**
111
+ * Whether a model name may be used to infer a capability (MODEL-06).
112
+ *
113
+ * It may not. Returns false so the prohibition is asserted rather than merely intended:
114
+ * a rule that lives only in a comment is one a future branch will not read.
115
+ * @returns always false.
116
+ */
117
+ export declare function modelNameImpliesCapability(): boolean;
118
+ /** How a candidate implementation executes (MODEL-04). */
119
+ export type ExecutionMode =
120
+ /** Pure JavaScript or WASM usable in plain Node. */
121
+ 'js-wasm-node'
122
+ /** Prebuilt ONNX Runtime for Node. */
123
+ | 'onnxruntime-node'
124
+ /** A managed native Windows executable with pinned assets. */
125
+ | 'managed-native-windows'
126
+ /** Requires Python on the end user's machine. */
127
+ | 'python-required'
128
+ /** Requires WSL. */
129
+ | 'wsl-required'
130
+ /** Requires Docker. */
131
+ | 'docker-required'
132
+ /** Requires a GPU. */
133
+ | 'gpu-required'
134
+ /** Only works in a browser. */
135
+ | 'browser-only'
136
+ /** Requires a consumer-side compile step. */
137
+ | 'consumer-compile';
138
+ /** Why a mode was refused. */
139
+ export interface ModeVerdict {
140
+ readonly acceptable: boolean;
141
+ readonly reason?: string;
142
+ }
143
+ /**
144
+ * Whether an execution mode is acceptable for the end user (MODEL-04).
145
+ *
146
+ * Python, WSL, Docker, a GPU and a consumer compile step are all refused: maintainers may
147
+ * use Python to export artifacts, but it MUST NOT be required on an end user's Windows
148
+ * machine. A browser-only API does not establish Node support.
149
+ * @param mode - the mode.
150
+ * @returns the verdict, with a reason when refused.
151
+ */
152
+ export declare function executionModeVerdict(mode: ExecutionMode): ModeVerdict;
153
+ /** An embedding space identity (MODEL-01, BASE-12). */
154
+ export interface EmbeddingSpace {
155
+ /** The model that produces it. */
156
+ readonly modelId: string;
157
+ /** The dimensionality of the vectors it produces. */
158
+ readonly dimensions: number;
159
+ }
160
+ /**
161
+ * Whether one embedding space may silently stand in for another (BASE-12).
162
+ *
163
+ * Only when the dimensions MATCH. EmbeddingGemma 2 is 768-dimensional and MiniLM is
164
+ * 384-dimensional, so substituting one for the other silently would compare vectors of
165
+ * different widths and produce meaningless similarity.
166
+ * @param from - the space in use.
167
+ * @param to - the proposed replacement.
168
+ * @returns whether the substitution is safe without an explicit migration.
169
+ */
170
+ export declare function maySubstituteSilently(from: EmbeddingSpace, to: EmbeddingSpace): boolean;
171
+ /**
172
+ * Describe an embedding substitution (BASE-12).
173
+ * @param from - the space in use.
174
+ * @param to - the proposed replacement.
175
+ * @returns a description naming the incompatibility when there is one.
176
+ */
177
+ export declare function describeSubstitution(from: EmbeddingSpace, to: EmbeddingSpace): string;
178
+ /**
179
+ * The reduced dimensionalities EmbeddingGemma 2 supports (MODEL-01).
180
+ *
181
+ * The model card describes 768 reducible to 512, 256 and 128. Recorded rather than
182
+ * assumed, because a reduction that is not applied consistently produces vectors that
183
+ * cannot be compared with each other.
184
+ */
185
+ export declare const EMBEDDINGGEMMA2_DIMENSIONS: readonly number[];
186
+ /**
187
+ * Whether a stated dimensionality is one EmbeddingGemma 2 can produce (MODEL-01).
188
+ * @param dimensions - the stated width.
189
+ * @returns whether it is a documented width.
190
+ */
191
+ export declare function isEmbeddingGemma2Width(dimensions: number): boolean;
192
+ //# sourceMappingURL=model-qualification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-qualification.d.ts","sourceRoot":"","sources":["../src/model-qualification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,eAAe;AACzB,+BAA+B;AAC7B,WAAW;AACb,kCAAkC;GAChC,aAAa;AACf,2EAA2E;GACzE,SAAS,CAAA;AAEb,6CAA6C;AAC7C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAA;AAElF;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,oBAAoB;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,wCAAwC;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;CACnC;AAED,oDAAoD;AACpD,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,MAAM,mBAAmB,CAAC,EAGtE,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,SAAS,CAAC,MAAM,mBAAmB,CAAC,EAAE,CAK9G;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,IAAI,mBAAmB,CAE/F;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,aAAa,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC3C,QAAQ,CAAC,eAAe,EAAE,SAAS,QAAQ,EAAE,CAAA;CAC9C;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,QAAQ,EAAE,CAGhF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAW/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,eAAe,CAGhF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED,0DAA0D;AAC1D,MAAM,MAAM,aAAa;AACvB,oDAAoD;AAClD,cAAc;AAChB,sCAAsC;GACpC,kBAAkB;AACpB,8DAA8D;GAC5D,wBAAwB;AAC1B,iDAAiD;GAC/C,iBAAiB;AACnB,oBAAoB;GAClB,cAAc;AAChB,uBAAuB;GACrB,iBAAiB;AACnB,sBAAsB;GACpB,cAAc;AAChB,+BAA+B;GAC7B,cAAc;AAChB,6CAA6C;GAC3C,kBAAkB,CAAA;AAEtB,8BAA8B;AAC9B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,WAAW,CAsBrE;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAEvF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,MAAM,CAMrF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAyB,CAAA;AAEjF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAElE"}