@lamplitisles/dsh-nanocodex 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/LICENSE +65 -0
  2. package/NOTICE +2 -0
  3. package/README.md +182 -0
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/cordis.patch.yml +52 -0
  6. package/dist/index.d.ts +435 -0
  7. package/dist/index.js +3405 -0
  8. package/dist/standard-preset.d.ts +7 -0
  9. package/dist/standard-preset.js +25 -0
  10. package/engine-release.json +14 -0
  11. package/package.json +119 -0
  12. package/presets/standard/agent.cordis.yml +6 -0
  13. package/presets/standard/preset.yml +2 -0
  14. package/vendor/nanocodex/README.md +1344 -0
  15. package/vendor/nanocodex/actions/events.d.mts +10 -0
  16. package/vendor/nanocodex/actions/events.mjs +163 -0
  17. package/vendor/nanocodex/actions/index.d.mts +13 -0
  18. package/vendor/nanocodex/actions/index.mjs +35 -0
  19. package/vendor/nanocodex/actions/session.d.mts +66 -0
  20. package/vendor/nanocodex/actions/session.mjs +59 -0
  21. package/vendor/nanocodex/actions/turn.d.mts +59 -0
  22. package/vendor/nanocodex/actions/turn.mjs +37 -0
  23. package/vendor/nanocodex/actions/voice.d.mts +22 -0
  24. package/vendor/nanocodex/actions/voice.mjs +50 -0
  25. package/vendor/nanocodex/browser/Agent.d.mts +55 -0
  26. package/vendor/nanocodex/browser/Agent.mjs +11 -0
  27. package/vendor/nanocodex/browser/ChatGptSubscription.d.mts +7 -0
  28. package/vendor/nanocodex/browser/ChatGptSubscription.mjs +19 -0
  29. package/vendor/nanocodex/browser/InlineAgent.mjs +266 -0
  30. package/vendor/nanocodex/browser/Transport.d.mts +74 -0
  31. package/vendor/nanocodex/browser/Transport.mjs +61 -0
  32. package/vendor/nanocodex/browser/Voice.d.mts +71 -0
  33. package/vendor/nanocodex/browser/Voice.mjs +291 -0
  34. package/vendor/nanocodex/browser/VoiceSession.mjs +688 -0
  35. package/vendor/nanocodex/browser/WorkerAgent.d.mts +65 -0
  36. package/vendor/nanocodex/browser/WorkerAgent.mjs +1472 -0
  37. package/vendor/nanocodex/browser/agent.worker.mjs +3 -0
  38. package/vendor/nanocodex/browser/config.d.mts +36 -0
  39. package/vendor/nanocodex/browser/config.mjs +439 -0
  40. package/vendor/nanocodex/browser/engine.mjs +13 -0
  41. package/vendor/nanocodex/browser/harness.mjs +55 -0
  42. package/vendor/nanocodex/browser/host.d.mts +83 -0
  43. package/vendor/nanocodex/browser/host.mjs +555 -0
  44. package/vendor/nanocodex/browser/hostManagedWebSocket.d.mts +14 -0
  45. package/vendor/nanocodex/browser/hostManagedWebSocket.mjs +110 -0
  46. package/vendor/nanocodex/browser/index.d.mts +92 -0
  47. package/vendor/nanocodex/browser/index.mjs +23 -0
  48. package/vendor/nanocodex/browser/indexeddb-durability-store.mjs +197 -0
  49. package/vendor/nanocodex/browser/workspace.d.mts +16 -0
  50. package/vendor/nanocodex/browser/workspace.mjs +107 -0
  51. package/vendor/nanocodex/cloud/Client.d.mts +83 -0
  52. package/vendor/nanocodex/cloud/Client.mjs +231 -0
  53. package/vendor/nanocodex/cloud/Decorator.d.mts +31 -0
  54. package/vendor/nanocodex/cloud/Decorator.mjs +37 -0
  55. package/vendor/nanocodex/cloud/Dialog.d.mts +113 -0
  56. package/vendor/nanocodex/cloud/Dialog.mjs +366 -0
  57. package/vendor/nanocodex/cloud/Errors.d.mts +22 -0
  58. package/vendor/nanocodex/cloud/Errors.mjs +39 -0
  59. package/vendor/nanocodex/cloud/Principal.d.mts +26 -0
  60. package/vendor/nanocodex/cloud/Principal.mjs +88 -0
  61. package/vendor/nanocodex/cloud/RemoteProvider.mjs +88 -0
  62. package/vendor/nanocodex/cloud/Transport.d.mts +47 -0
  63. package/vendor/nanocodex/cloud/Transport.mjs +454 -0
  64. package/vendor/nanocodex/cloud/actions/account.d.mts +9 -0
  65. package/vendor/nanocodex/cloud/actions/account.mjs +19 -0
  66. package/vendor/nanocodex/cloud/actions/agent.d.mts +18 -0
  67. package/vendor/nanocodex/cloud/actions/agent.mjs +428 -0
  68. package/vendor/nanocodex/cloud/actions/connection.d.mts +133 -0
  69. package/vendor/nanocodex/cloud/actions/connection.mjs +580 -0
  70. package/vendor/nanocodex/cloud/actions/grant.d.mts +10 -0
  71. package/vendor/nanocodex/cloud/actions/grant.mjs +12 -0
  72. package/vendor/nanocodex/cloud/actions/index.d.mts +7 -0
  73. package/vendor/nanocodex/cloud/actions/index.mjs +7 -0
  74. package/vendor/nanocodex/cloud/actions/machineUsd.d.mts +23 -0
  75. package/vendor/nanocodex/cloud/actions/machineUsd.mjs +37 -0
  76. package/vendor/nanocodex/cloud/actions/model.d.mts +12 -0
  77. package/vendor/nanocodex/cloud/actions/model.mjs +50 -0
  78. package/vendor/nanocodex/cloud/actions/mpp.d.mts +28 -0
  79. package/vendor/nanocodex/cloud/actions/mpp.mjs +39 -0
  80. package/vendor/nanocodex/cloud/index.d.mts +30 -0
  81. package/vendor/nanocodex/cloud/index.mjs +9 -0
  82. package/vendor/nanocodex/cloud/internal.mjs +533 -0
  83. package/vendor/nanocodex/cloud/mercator.mjs +108 -0
  84. package/vendor/nanocodex/cloud/server/HostPrincipal.d.mts +34 -0
  85. package/vendor/nanocodex/cloud/server/HostPrincipal.mjs +202 -0
  86. package/vendor/nanocodex/cloud/server/index.d.mts +1 -0
  87. package/vendor/nanocodex/cloud/server/index.mjs +1 -0
  88. package/vendor/nanocodex/cloud/types.d.mts +211 -0
  89. package/vendor/nanocodex/cloudflare/Agent.d.mts +133 -0
  90. package/vendor/nanocodex/cloudflare/Agent.mjs +811 -0
  91. package/vendor/nanocodex/cloudflare/egress-subject.mjs +19 -0
  92. package/vendor/nanocodex/cloudflare/egress.d.mts +41 -0
  93. package/vendor/nanocodex/cloudflare/egress.mjs +119 -0
  94. package/vendor/nanocodex/cloudflare/event-socket.mjs +316 -0
  95. package/vendor/nanocodex/cloudflare/index.d.mts +7 -0
  96. package/vendor/nanocodex/cloudflare/index.mjs +6 -0
  97. package/vendor/nanocodex/host/Agent.d.mts +55 -0
  98. package/vendor/nanocodex/host/Agent.mjs +1 -0
  99. package/vendor/nanocodex/host/index.d.mts +82 -0
  100. package/vendor/nanocodex/host/index.mjs +10 -0
  101. package/vendor/nanocodex/index.d.mts +81 -0
  102. package/vendor/nanocodex/index.mjs +12 -0
  103. package/vendor/nanocodex/internal.mjs +1275 -0
  104. package/vendor/nanocodex/managed/Agent.d.mts +380 -0
  105. package/vendor/nanocodex/managed/Agent.mjs +1694 -0
  106. package/vendor/nanocodex/managed/ManagedError.d.mts +9 -0
  107. package/vendor/nanocodex/managed/ManagedError.mjs +8 -0
  108. package/vendor/nanocodex/managed/README.md +241 -0
  109. package/vendor/nanocodex/managed/Voice.mjs +202 -0
  110. package/vendor/nanocodex/managed/index.d.mts +20 -0
  111. package/vendor/nanocodex/managed/index.mjs +2 -0
  112. package/vendor/nanocodex/managed/internal.mjs +119 -0
  113. package/vendor/nanocodex/node/Agent.d.mts +50 -0
  114. package/vendor/nanocodex/node/Agent.mjs +203 -0
  115. package/vendor/nanocodex/node/ChatGptSubscription.d.mts +9 -0
  116. package/vendor/nanocodex/node/ChatGptSubscription.mjs +17 -0
  117. package/vendor/nanocodex/node/Transport.d.mts +44 -0
  118. package/vendor/nanocodex/node/Transport.mjs +46 -0
  119. package/vendor/nanocodex/node/host.mjs +731 -0
  120. package/vendor/nanocodex/node/index.d.mts +73 -0
  121. package/vendor/nanocodex/node/index.mjs +17 -0
  122. package/vendor/nanocodex/node/workspace.d.mts +10 -0
  123. package/vendor/nanocodex/node/workspace.mjs +198 -0
  124. package/vendor/nanocodex/package.json +200 -0
  125. package/vendor/nanocodex/pkg-node/nanocodex.d.ts +675 -0
  126. package/vendor/nanocodex/pkg-node/nanocodex.js +2628 -0
  127. package/vendor/nanocodex/pkg-node/nanocodex_bg.wasm.d.ts +105 -0
  128. package/vendor/nanocodex/pkg-node/package.json +1 -0
  129. package/vendor/nanocodex/pkg-web/.nanocodex-bindgen-stamp +6 -0
  130. package/vendor/nanocodex/pkg-web/nanocodex-build.json +1 -0
  131. package/vendor/nanocodex/pkg-web/nanocodex.d.ts +805 -0
  132. package/vendor/nanocodex/pkg-web/nanocodex.js +2714 -0
  133. package/vendor/nanocodex/pkg-web/nanocodex_bg.js +2615 -0
  134. package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm +0 -0
  135. package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm.d.ts +105 -0
  136. package/vendor/nanocodex/pkg-web/nanocodex_worker.js +9 -0
  137. package/vendor/nanocodex/pkg-web/package.json +1 -0
  138. package/vendor/nanocodex/runtime/chatgpt-subscription.mjs +212 -0
  139. package/vendor/nanocodex/runtime/cloudflare-durability-store.d.mts +26 -0
  140. package/vendor/nanocodex/runtime/cloudflare-durability-store.mjs +168 -0
  141. package/vendor/nanocodex/runtime/code-evaluator.worker.mjs +137 -0
  142. package/vendor/nanocodex/runtime/code-runtime.mjs +1 -0
  143. package/vendor/nanocodex/runtime/durability-store.d.mts +85 -0
  144. package/vendor/nanocodex/runtime/durability-store.mjs +631 -0
  145. package/vendor/nanocodex/runtime/durability.mjs +153 -0
  146. package/vendor/nanocodex/runtime/managed-transport.mjs +460 -0
  147. package/vendor/nanocodex/runtime/mcp-runtime.mjs +534 -0
  148. package/vendor/nanocodex/runtime/postgres-durability-store.d.mts +64 -0
  149. package/vendor/nanocodex/runtime/postgres-durability-store.mjs +532 -0
  150. package/vendor/nanocodex/runtime/quickjs-evaluator.d.mts +17 -0
  151. package/vendor/nanocodex/runtime/quickjs-evaluator.mjs +236 -0
  152. package/vendor/nanocodex/runtime/responses-transport.mjs +22 -0
  153. package/vendor/nanocodex/runtime/subagents.d.mts +107 -0
  154. package/vendor/nanocodex/runtime/subagents.mjs +54 -0
  155. package/vendor/nanocodex/runtime/subscription-store.d.mts +8 -0
  156. package/vendor/nanocodex/runtime/subscription-store.mjs +42 -0
  157. package/vendor/nanocodex/runtime/tempo-provider.d.mts +125 -0
  158. package/vendor/nanocodex/runtime/tempo-provider.mjs +264 -0
  159. package/vendor/nanocodex/runtime/tool-configuration.mjs +1 -0
  160. package/vendor/nanocodex/runtime/tool-router.mjs +1 -0
  161. package/vendor/nanocodex/runtime/utf8.mjs +1 -0
  162. package/vendor/nanocodex/runtime/worker-evaluator.mjs +162 -0
  163. package/vendor/nanocodex/runtime/workspace.d.mts +1 -0
  164. package/vendor/nanocodex/runtime/workspace.mjs +1 -0
  165. package/vendor/nanocodex/tools/Tools.d.mts +56 -0
  166. package/vendor/nanocodex/tools/Tools.mjs +136 -0
  167. package/vendor/nanocodex/tools/artifact.d.mts +1 -0
  168. package/vendor/nanocodex/tools/artifact.mjs +1 -0
  169. package/vendor/nanocodex/tools/attachment.mjs +1 -0
  170. package/vendor/nanocodex/tools/bash.d.mts +1 -0
  171. package/vendor/nanocodex/tools/bash.mjs +1 -0
  172. package/vendor/nanocodex/tools/browser/accountInfo.mjs +795 -0
  173. package/vendor/nanocodex/tools/browser/browserBuffer.mjs +18 -0
  174. package/vendor/nanocodex/tools/browser/browserCompiler.mjs +155 -0
  175. package/vendor/nanocodex/tools/browser/browserEgress.mjs +161 -0
  176. package/vendor/nanocodex/tools/browser/browserPython.mjs +133 -0
  177. package/vendor/nanocodex/tools/browser/browserShell.mjs +1075 -0
  178. package/vendor/nanocodex/tools/browser/browserSprintf.mjs +4 -0
  179. package/vendor/nanocodex/tools/browser/browserSsh.mjs +201 -0
  180. package/vendor/nanocodex/tools/browser/browserZlib.mjs +136 -0
  181. package/vendor/nanocodex/tools/browser/compiler.worker.mjs +160 -0
  182. package/vendor/nanocodex/tools/browser/devTunnelsSshBrowser.mjs +13 -0
  183. package/vendor/nanocodex/tools/browser/index.d.mts +209 -0
  184. package/vendor/nanocodex/tools/browser/index.mjs +174 -0
  185. package/vendor/nanocodex/tools/browser/opfsGit.mjs +248 -0
  186. package/vendor/nanocodex/tools/browser/python.worker.mjs +114 -0
  187. package/vendor/nanocodex/tools/browser/threadGit.mjs +197 -0
  188. package/vendor/nanocodex/tools/browser/unsupportedNodeRsa.mjs +5 -0
  189. package/vendor/nanocodex/tools/browser/workspace.mjs +83 -0
  190. package/vendor/nanocodex/tools/dataset.d.mts +1 -0
  191. package/vendor/nanocodex/tools/dataset.mjs +1 -0
  192. package/vendor/nanocodex/tools/datasetContract.mjs +1 -0
  193. package/vendor/nanocodex/tools/datasetEngine.mjs +1 -0
  194. package/vendor/nanocodex/tools/hostedCatalog.d.mts +1 -0
  195. package/vendor/nanocodex/tools/hostedCatalog.mjs +1 -0
  196. package/vendor/nanocodex/tools/index.d.mts +49 -0
  197. package/vendor/nanocodex/tools/index.mjs +19 -0
  198. package/vendor/nanocodex/tools/namedTool.mjs +1 -0
  199. package/vendor/nanocodex/tools/repository-workspace.d.mts +1 -0
  200. package/vendor/nanocodex/tools/repository-workspace.mjs +1 -0
  201. package/vendor/nanocodex/tools/ssh.d.mts +1 -0
  202. package/vendor/nanocodex/tools/ssh.mjs +1 -0
  203. package/vendor/nanocodex/tools/standard.mjs +1 -0
  204. package/vendor/nanocodex/tools/standardDescriptions.mjs +1 -0
  205. package/vendor/nanocodex/types.d.mts +663 -0
  206. package/vendor/nanocodex/wasm.d.mts +4 -0
  207. package/vendor/nanocodex/worker/ChatGptSubscription.d.mts +9 -0
  208. package/vendor/nanocodex/worker/ChatGptSubscription.mjs +29 -0
  209. package/vendor/nanocodex/worker/index.d.mts +18 -0
  210. package/vendor/nanocodex/worker/index.mjs +5 -0
  211. package/vendor/nanocodex-tools/README.md +65 -0
  212. package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.d.ts +7 -0
  213. package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.js +14 -0
  214. package/vendor/nanocodex-tools/dist/hosted/broker-core.d.ts +172 -0
  215. package/vendor/nanocodex-tools/dist/hosted/broker-core.js +1278 -0
  216. package/vendor/nanocodex-tools/dist/hosted/index.d.ts +3 -0
  217. package/vendor/nanocodex-tools/dist/hosted/index.js +3 -0
  218. package/vendor/nanocodex-tools/dist/hosted/protocol.d.ts +120 -0
  219. package/vendor/nanocodex-tools/dist/hosted/protocol.js +415 -0
  220. package/vendor/nanocodex-tools/dist/index.d.ts +19 -0
  221. package/vendor/nanocodex-tools/dist/index.js +14 -0
  222. package/vendor/nanocodex-tools/dist/memory.d.ts +204 -0
  223. package/vendor/nanocodex-tools/dist/memory.js +411 -0
  224. package/vendor/nanocodex-tools/dist/namespace.d.ts +87 -0
  225. package/vendor/nanocodex-tools/dist/namespace.js +338 -0
  226. package/vendor/nanocodex-tools/dist/repository-archive.d.ts +4 -0
  227. package/vendor/nanocodex-tools/dist/repository-archive.js +104 -0
  228. package/vendor/nanocodex-tools/dist/runtime.d.ts +30 -0
  229. package/vendor/nanocodex-tools/dist/runtime.js +37 -0
  230. package/vendor/nanocodex-tools/dist/session.d.ts +167 -0
  231. package/vendor/nanocodex-tools/dist/session.js +286 -0
  232. package/vendor/nanocodex-tools/dist/shell.d.ts +51 -0
  233. package/vendor/nanocodex-tools/dist/shell.js +553 -0
  234. package/vendor/nanocodex-tools/dist/ssh.d.ts +29 -0
  235. package/vendor/nanocodex-tools/dist/ssh.js +66 -0
  236. package/vendor/nanocodex-tools/dist/workspace.d.ts +45 -0
  237. package/vendor/nanocodex-tools/dist/workspace.js +237 -0
  238. package/vendor/nanocodex-tools/package.json +122 -0
  239. package/vendor/nanocodex-tools/runtime/code-runtime.mjs +817 -0
  240. package/vendor/nanocodex-tools/runtime/tool-configuration.mjs +50 -0
  241. package/vendor/nanocodex-tools/runtime/tool-router.mjs +730 -0
  242. package/vendor/nanocodex-tools/runtime/utf8.d.mts +2 -0
  243. package/vendor/nanocodex-tools/runtime/utf8.mjs +25 -0
  244. package/vendor/nanocodex-tools/tools/artifact.d.mts +69 -0
  245. package/vendor/nanocodex-tools/tools/artifact.mjs +270 -0
  246. package/vendor/nanocodex-tools/tools/attachment.mjs +603 -0
  247. package/vendor/nanocodex-tools/tools/bash.d.mts +90 -0
  248. package/vendor/nanocodex-tools/tools/bash.mjs +742 -0
  249. package/vendor/nanocodex-tools/tools/dataset.d.mts +9 -0
  250. package/vendor/nanocodex-tools/tools/dataset.mjs +47 -0
  251. package/vendor/nanocodex-tools/tools/datasetContract.mjs +54 -0
  252. package/vendor/nanocodex-tools/tools/datasetEngine.mjs +1167 -0
  253. package/vendor/nanocodex-tools/tools/execution-contract.d.mts +5 -0
  254. package/vendor/nanocodex-tools/tools/execution-contract.mjs +63 -0
  255. package/vendor/nanocodex-tools/tools/hostedCatalog.d.mts +20 -0
  256. package/vendor/nanocodex-tools/tools/hostedCatalog.mjs +67 -0
  257. package/vendor/nanocodex-tools/tools/hostedMachine.d.mts +10 -0
  258. package/vendor/nanocodex-tools/tools/hostedMachine.mjs +56 -0
  259. package/vendor/nanocodex-tools/tools/just-bash-browser.d.mts +1 -0
  260. package/vendor/nanocodex-tools/tools/just-bash-browser.mjs +1 -0
  261. package/vendor/nanocodex-tools/tools/namedTool.d.mts +3 -0
  262. package/vendor/nanocodex-tools/tools/namedTool.mjs +3 -0
  263. package/vendor/nanocodex-tools/tools/nodeProcess.d.mts +10 -0
  264. package/vendor/nanocodex-tools/tools/nodeProcess.mjs +314 -0
  265. package/vendor/nanocodex-tools/tools/processOutput.mjs +56 -0
  266. package/vendor/nanocodex-tools/tools/repository-workspace.d.mts +55 -0
  267. package/vendor/nanocodex-tools/tools/repository-workspace.mjs +397 -0
  268. package/vendor/nanocodex-tools/tools/ssh.d.mts +56 -0
  269. package/vendor/nanocodex-tools/tools/ssh.mjs +503 -0
  270. package/vendor/nanocodex-tools/tools/standard.mjs +458 -0
  271. package/vendor/nanocodex-tools/tools/standardDescriptions.mjs +3 -0
  272. package/vendor/nanocodex-tools/tools/types.d.mts +83 -0
  273. package/vendor/nanocodex-tools/tools/workspace.d.mts +25 -0
  274. package/vendor/nanocodex-tools/tools/workspace.mjs +282 -0
  275. package/vendor/nanocodex-tools/tools/x.d.mts +30 -0
  276. package/vendor/nanocodex-tools/tools/x.mjs +91 -0
@@ -0,0 +1,730 @@
1
+ const MAX_CONCURRENT_CALLS = 128;
2
+ const CANCELLATION_MESSAGE = "tool execution was cancelled";
3
+ const TOOL_RESULT = Symbol.for("nanocodex.toolResult");
4
+ export const toolRouterBrand = Symbol.for("nanocodex.toolRouter");
5
+ export const toolRouterRuntime = Symbol("nanocodex.toolRouterRuntime");
6
+ export const toolRuntimeLifecycle = Symbol("nanocodex.toolRuntimeLifecycle");
7
+ export const preDispatchUnavailable = Symbol.for("nanocodex.tool.preDispatchUnavailable");
8
+
9
+ const TOOL_SEARCH_DEFINITION = deepFreeze({
10
+ type: "tool_search",
11
+ execution: "client",
12
+ description: "Searches all deferred tool sources and exposes matching tools for the next model call.",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ query: { type: "string", description: "Search query for deferred tools." },
17
+ limit: { type: "number", description: "Maximum number of tools to return. Defaults to 8." },
18
+ },
19
+ required: ["query"],
20
+ additionalProperties: false,
21
+ },
22
+ });
23
+
24
+ /** Deterministic routing and admission boundary shared by direct and Code Mode calls. */
25
+ export class ToolRouter {
26
+ #sources = new Map();
27
+ #admissions = new AdmissionGate();
28
+ #execution = new AsyncReadWriteGate();
29
+ #permits = new AsyncSemaphore(MAX_CONCURRENT_CALLS);
30
+ #reset;
31
+
32
+ constructor(sources = []) {
33
+ for (const source of sources) this.addSource(source);
34
+ }
35
+
36
+ get [toolRouterBrand]() { return true; }
37
+
38
+ addSource(source) {
39
+ const normalized = normalizeSource(source);
40
+ if (this.#sources.has(normalized.id)) {
41
+ throw new Error(`tool source is already configured: ${normalized.id}`);
42
+ }
43
+ this.#sources.set(normalized.id, normalized);
44
+ // Build now so collisions fail at configuration, independent of source order.
45
+ try { this.#buildSnapshot(); } catch (error) {
46
+ this.#sources.delete(normalized.id);
47
+ throw error;
48
+ }
49
+ return this;
50
+ }
51
+
52
+ /** Validates a candidate source against the complete route table without publishing it. */
53
+ validateSource(source) {
54
+ const normalized = normalizeSource(source);
55
+ const previous = this.#sources.get(normalized.id);
56
+ this.#sources.set(normalized.id, normalized);
57
+ try {
58
+ this.#buildSnapshot();
59
+ return true;
60
+ } finally {
61
+ if (previous) this.#sources.set(normalized.id, previous);
62
+ else this.#sources.delete(normalized.id);
63
+ }
64
+ }
65
+
66
+ async attachSource(source) {
67
+ const normalized = normalizeSource({ ...source, kind: "attached", mode: "attached-over-cloud" });
68
+ const release = await this.#admissions.write();
69
+ try {
70
+ if (this.#sources.has(normalized.id)) {
71
+ throw new Error(`tool source is already configured: ${normalized.id}`);
72
+ }
73
+ this.#sources.set(normalized.id, normalized);
74
+ try { this.#buildSnapshot(); } catch (error) {
75
+ this.#sources.delete(normalized.id);
76
+ throw error;
77
+ }
78
+ } finally {
79
+ release();
80
+ }
81
+ }
82
+
83
+ async detachSource(id) {
84
+ const release = await this.#admissions.write();
85
+ try { return this.#sources.delete(id); } finally { release(); }
86
+ }
87
+
88
+ async admit(signal) {
89
+ const release = await this.#admissions.read(signal);
90
+ try {
91
+ const built = this.#buildSnapshot();
92
+ let closed = false;
93
+ return Object.freeze({
94
+ definitions: built.definitions,
95
+ tools: built.tools,
96
+ catalog: (provider = "javascript") => catalogSnapshot(built, provider),
97
+ invoke: (name, input, context) => this.#invoke(built, name, input, context),
98
+ release: () => {
99
+ if (closed) return;
100
+ closed = true;
101
+ release();
102
+ },
103
+ });
104
+ } catch (error) {
105
+ release();
106
+ throw error;
107
+ }
108
+ }
109
+
110
+ snapshot() {
111
+ const built = this.#buildSnapshot();
112
+ return Object.freeze({
113
+ definitions: built.definitions,
114
+ tools: built.tools,
115
+ catalog: (provider = "javascript") => catalogSnapshot(built, provider),
116
+ invoke: (name, input, context) => this.#invoke(built, name, input, context),
117
+ release() {},
118
+ });
119
+ }
120
+
121
+ modelDefinitions() {
122
+ // Provider catalogs (notably MCP discovery) are live until an admission
123
+ // snapshots them. Rebuild here so newly discovered deferred definitions
124
+ // become available to Code Mode; `defer_loading` keeps them out of the
125
+ // direct model prefix at the Rust boundary.
126
+ return this.#buildSnapshot().modelDefinitions;
127
+ }
128
+
129
+ definitions() { return this.#buildSnapshot().definitions; }
130
+ resolve(name) { return this.#buildSnapshot().tools.get(name); }
131
+ hasSource(id) { return this.#sources.has(id); }
132
+ hasSourceKind(kind) {
133
+ return [...this.#sources.values()].some((source) => source.kind === kind);
134
+ }
135
+
136
+ async settled() {
137
+ const sources = [...this.#sources.values()];
138
+ await Promise.all(sources.map((source) => source.settled?.()));
139
+ }
140
+
141
+ catalog(provider = "javascript") {
142
+ return catalogSnapshot(this.#buildSnapshot(), provider);
143
+ }
144
+
145
+ async execute(name, input, context) {
146
+ const admission = await this.admit(context?.signal);
147
+ try { return await admission.invoke(name, input, context); } finally { admission.release(); }
148
+ }
149
+
150
+ releaseSession(sessionId) {
151
+ for (const tool of this.#allTools()) tool.releaseSession?.(sessionId);
152
+ }
153
+
154
+ reset(reentrantPromise) {
155
+ if (this.#reset) return this.#reset;
156
+ const sources = [...this.#sources.values()];
157
+ let tools = [];
158
+ let enumerationError;
159
+ try { tools = [...this.#allTools(sources.filter((source) => typeof source.close !== "function"))]; }
160
+ catch (error) { enumerationError = error; }
161
+ this.#sources.clear();
162
+ const actions = [
163
+ ...(enumerationError ? [() => { throw enumerationError; }] : []),
164
+ ...tools.map((tool) => () => tool.dispose?.()),
165
+ ...sources.filter((source) => typeof source.close === "function").map((source) => () => source.close()),
166
+ ];
167
+ this.#reset = Promise.resolve().then(() => settleCleanup(
168
+ actions,
169
+ "tool router cleanup failed",
170
+ reentrantPromise,
171
+ ));
172
+ return this.#reset;
173
+ }
174
+
175
+ #allTools(sources = this.#sources.values()) {
176
+ const tools = new Set();
177
+ for (const source of sources) {
178
+ for (const definition of source.definitions()) {
179
+ const name = definition.type === "tool_search" ? "tool_search" : definition.name;
180
+ const tool = source.resolve(name);
181
+ if (tool) tools.add(tool);
182
+ }
183
+ }
184
+ return tools;
185
+ }
186
+
187
+ #buildSnapshot() {
188
+ const entries = [];
189
+ const searches = [];
190
+ const sources = [...this.#sources.values()].sort((a, b) => a.id.localeCompare(b.id));
191
+ for (const source of sources) {
192
+ const definitions = jsonSnapshot(source.definitions(), `tool source ${source.id} definitions`);
193
+ if (!Array.isArray(definitions)) throw new TypeError(`tool source ${source.id} definitions() must return an array`);
194
+ for (const [sourceIndex, raw] of definitions.entries()) {
195
+ if (raw?.type === "tool_search") {
196
+ if (typeof source.search === "function") searches.push({ source, search: source.search });
197
+ continue;
198
+ }
199
+ const candidate = catalogCandidate(raw, source.id);
200
+ const exactDefinition = normalizeDefinition(candidate?.definition ?? raw, source.id);
201
+ const definition = source.kind === "attached"
202
+ ? deepFreeze({ ...exactDefinition, defer_loading: true })
203
+ : exactDefinition;
204
+ const tool = source.resolve(definition.name);
205
+ if (!tool) throw new Error(`tool source ${source.id} cannot resolve ${definition.name}`);
206
+ const resolved = normalizeResolvedTool(definition.name, tool, candidate);
207
+ entries.push({
208
+ definition,
209
+ fingerprint: callableContractFingerprint(exactDefinition),
210
+ normalizedName: normalizeToolName(definition.name),
211
+ source,
212
+ sourceIndex,
213
+ tool: resolved,
214
+ });
215
+ }
216
+ }
217
+ entries.sort(compareEntries);
218
+ const selected = new Map();
219
+ const normalized = new Map();
220
+ for (const entry of entries) {
221
+ const collision = normalized.get(entry.normalizedName);
222
+ if (!collision) {
223
+ selected.set(entry.definition.name, entry);
224
+ normalized.set(entry.normalizedName, entry);
225
+ continue;
226
+ }
227
+ const winner = selectCollision(collision, entry);
228
+ selected.delete(collision.definition.name);
229
+ selected.set(winner.definition.name, winner);
230
+ normalized.set(entry.normalizedName, winner);
231
+ }
232
+ const ordered = [...selected.values()].sort((a, b) =>
233
+ a.source.id.localeCompare(b.source.id) || a.sourceIndex - b.sourceIndex);
234
+ const tools = new Map(ordered.map((entry) => [
235
+ entry.definition.name,
236
+ entry.fallback ? overlayTool(entry.tool, entry.fallback.tool) : entry.tool,
237
+ ]));
238
+ const definitions = ordered.map((entry) => entry.definition);
239
+ const hasSearch = searches.length
240
+ || sources.some((source) => source.kind === "attached" || source.kind === "mcp" || source.deferred === true)
241
+ || ordered.some((entry) => entry.definition.defer_loading === true);
242
+ if (hasSearch) {
243
+ definitions.unshift(TOOL_SEARCH_DEFINITION);
244
+ tools.set("tool_search", Object.freeze({
245
+ name: "tool_search",
246
+ parallelSafe: true,
247
+ handler: (input, context) => searchSnapshot(input, context, ordered, searches),
248
+ }));
249
+ }
250
+ const modelDefinitions = [
251
+ ...(hasSearch ? [TOOL_SEARCH_DEFINITION] : []),
252
+ ...ordered.flatMap((entry) => entry.source.kind === "attached"
253
+ ? entry.fallback ? [entry.fallback.definition] : []
254
+ : [entry.definition]),
255
+ ];
256
+ return Object.freeze({
257
+ definitions: deepFreeze(definitions),
258
+ modelDefinitions: deepFreeze(modelDefinitions),
259
+ tools,
260
+ });
261
+ }
262
+
263
+ async #invoke(snapshot, name, input, context = {}) {
264
+ const tool = snapshot.tools.get(name);
265
+ if (!tool) throw new Error(`unknown application tool: ${name}`);
266
+ const signal = context.signal ?? new AbortController().signal;
267
+ signal.throwIfAborted?.();
268
+ const releasePermit = await this.#permits.acquire(signal);
269
+ let releaseExecution;
270
+ try {
271
+ releaseExecution = await this.#execution.acquire(tool.parallelSafe, signal);
272
+ return await tool.handler(input, context);
273
+ } finally {
274
+ releaseExecution?.();
275
+ releasePermit();
276
+ }
277
+ }
278
+ }
279
+
280
+ export function createToolRouter(sources) { return new ToolRouter(sources); }
281
+
282
+ /** Runs every cleanup action before reporting failures in their original order. */
283
+ export async function settleCleanup(actions, message, reentrantPromise) {
284
+ const pending = actions.map((action) => {
285
+ try {
286
+ const result = action();
287
+ return result === reentrantPromise ? Promise.resolve() : Promise.resolve(result);
288
+ }
289
+ catch (error) { return Promise.reject(error); }
290
+ });
291
+ const settled = await Promise.allSettled(pending);
292
+ const errors = settled
293
+ .filter((result) => result.status === "rejected")
294
+ .map((result) => result.reason);
295
+ if (errors.length === 1) throw errors[0];
296
+ if (errors.length > 1) throw new AggregateError(errors, message);
297
+ }
298
+
299
+ export function toolMapSource(id, configuration = {}, options = {}) {
300
+ const tools = new Map();
301
+ const definitions = [];
302
+ for (const [name, value] of Object.entries(configuration)) {
303
+ if (!value || typeof value.handler !== "function") {
304
+ throw new TypeError(`tool ${name} requires a handler function`);
305
+ }
306
+ const definition = value.definition
307
+ ? { ...value.definition, name }
308
+ : {
309
+ type: "function",
310
+ name,
311
+ description: value.description || "Application-defined tool.",
312
+ strict: value.strict ?? false,
313
+ parameters: value.parameters || { type: "object", additionalProperties: true },
314
+ ...(value.outputSchema !== undefined ? { output_schema: value.outputSchema } : {}),
315
+ };
316
+ definitions.push(deepFreeze(jsonSnapshot(definition, `tool ${name} definition`)));
317
+ tools.set(name, Object.freeze({
318
+ name,
319
+ handler: value.handler,
320
+ parallelSafe: value.supportsParallelToolCalls === true || value.parallelSafe === true,
321
+ provider: value.provider,
322
+ remoteName: value.remoteName,
323
+ summary: value.summary,
324
+ timeoutMs: value.timeoutMs,
325
+ dispose: typeof value.dispose === "function" ? value.dispose : undefined,
326
+ releaseSession: typeof value.releaseSession === "function" ? value.releaseSession : undefined,
327
+ }));
328
+ }
329
+ return Object.freeze({
330
+ id,
331
+ kind: options.kind ?? "cloud",
332
+ mode: options.mode ?? "union",
333
+ definitions: () => definitions,
334
+ resolve: (name) => tools.get(name),
335
+ });
336
+ }
337
+
338
+ export function providerSource(id, provider, options = {}) {
339
+ if (!provider || typeof provider.definitions !== "function" || typeof provider.resolve !== "function") {
340
+ throw new TypeError("a tool source requires definitions() and resolve(name)");
341
+ }
342
+ return Object.freeze({
343
+ id,
344
+ kind: options.kind ?? provider.kind ?? "union",
345
+ mode: options.mode ?? provider.mode ?? "union",
346
+ definitions: () => provider.definitions(),
347
+ resolve: (name) => provider.resolve(name),
348
+ search: typeof provider.search === "function" ? (input, context) => provider.search(input, context) : undefined,
349
+ deferred: options.deferred ?? provider.deferred,
350
+ settled: () => provider.settled?.(),
351
+ ...(typeof provider.close === "function" ? { close: () => provider.close() } : {}),
352
+ });
353
+ }
354
+
355
+ export function logicalContractFingerprint(definition) {
356
+ const logical = { ...normalizeDefinition(definition, "fingerprint") };
357
+ // Defer loading changes request presentation, not the callable contract.
358
+ delete logical.defer_loading;
359
+ return stableJson(logical);
360
+ }
361
+
362
+ export function normalizeToolName(name) {
363
+ if (typeof name !== "string" || !name.trim()) throw new TypeError("tool name must not be empty");
364
+ return [...name].map((character) => /[A-Za-z0-9_-]/.test(character) ? character : "_").join("");
365
+ }
366
+
367
+ async function searchSnapshot(input, context, entries, searches) {
368
+ const query = requiredQuery(input?.query);
369
+ const limit = normalizeLimit(input?.limit);
370
+ const admittedNames = new Set(entries.map((entry) => entry.definition.name));
371
+ const searchedSources = new Set(searches.map(({ source }) => source));
372
+ const words = tokenize(query);
373
+ const generic = entries
374
+ .filter((entry) => !searchedSources.has(entry.source)
375
+ && (entry.source.kind === "attached" || entry.definition.defer_loading === true))
376
+ .map((entry) => ({ entry, score: score(entry, words) }))
377
+ .filter(({ score }) => score > 0)
378
+ .sort((a, b) => b.score - a.score || a.entry.definition.name.localeCompare(b.entry.definition.name));
379
+ const tools = generic.map(({ entry }) => ({
380
+ name: entry.definition.name,
381
+ provider: entry.source.id,
382
+ remote_name: entry.source.remoteName?.(entry.definition.name) ?? entry.definition.name,
383
+ description: entry.definition.description ?? "",
384
+ supports_parallel_tool_calls: entry.tool.parallelSafe,
385
+ input_schema: entry.definition.parameters,
386
+ ...(entry.definition.output_schema !== undefined ? { output_schema: entry.definition.output_schema } : {}),
387
+ }));
388
+ const failures = {};
389
+ const providerResults = [];
390
+ let pending = 0;
391
+ for (const { search } of searches) {
392
+ const result = await search({ query, limit }, context);
393
+ const body = result?.[TOOL_RESULT] ? result.output : result;
394
+ if (result?.[TOOL_RESULT]) {
395
+ providerResults.push(...(Array.isArray(result.structuredResult)
396
+ ? result.structuredResult
397
+ : [result.structuredResult]));
398
+ }
399
+ const value = Array.isArray(body) ? body[0] : body;
400
+ if (Array.isArray(value?.tools)) tools.push(...value.tools);
401
+ pending += value?.pending_servers ?? value?.pending_sources ?? 0;
402
+ Object.assign(failures, value?.failed_servers ?? value?.failed_sources ?? {});
403
+ }
404
+ const deduplicated = [...new Map(tools
405
+ .filter((tool) => admittedNames.has(tool.name))
406
+ .map((tool) => [tool.name, tool])).values()].slice(0, limit);
407
+ const selectedNames = new Set(deduplicated.map((tool) => tool.name));
408
+ const structured = [];
409
+ const representedNames = new Set();
410
+ for (const value of providerResults) {
411
+ const admitted = admittedStructuredResult(value, selectedNames, representedNames);
412
+ if (!admitted) continue;
413
+ structured.push(admitted);
414
+ for (const name of structuredToolNames(admitted)) representedNames.add(name);
415
+ }
416
+ structured.push(...generic
417
+ .map(({ entry }) => entry)
418
+ .filter((entry) => selectedNames.has(entry.definition.name)
419
+ && !representedNames.has(entry.definition.name))
420
+ .map((entry) => providerToolDefinition(entry.definition)));
421
+ const result = {
422
+ tools: deduplicated,
423
+ pending_sources: pending,
424
+ failed_sources: failures,
425
+ // Preserve the established MCP result fields during the transition.
426
+ pending_servers: pending,
427
+ failed_servers: failures,
428
+ };
429
+ return Object.freeze({
430
+ [TOOL_RESULT]: true,
431
+ metadata: null,
432
+ output: result,
433
+ structuredResult: structured,
434
+ success: true,
435
+ value: result,
436
+ });
437
+ }
438
+
439
+ function admittedStructuredResult(value, admittedNames, representedNames) {
440
+ if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
441
+ if (value.type === "function" || value.type === "custom") {
442
+ return admittedNames.has(value.name) && !representedNames.has(value.name)
443
+ ? providerToolDefinition(value)
444
+ : undefined;
445
+ }
446
+ if (value.type !== "namespace" || typeof value.name !== "string" || !Array.isArray(value.tools)) {
447
+ return undefined;
448
+ }
449
+ const namespaceNames = new Set();
450
+ const tools = value.tools
451
+ .filter((tool) => {
452
+ if (typeof tool?.name !== "string") return false;
453
+ const name = `${value.name}${tool.name}`;
454
+ if (!admittedNames.has(name) || representedNames.has(name) || namespaceNames.has(name)) {
455
+ return false;
456
+ }
457
+ namespaceNames.add(name);
458
+ return true;
459
+ })
460
+ .map(providerToolDefinition);
461
+ return tools.length ? deepFreeze({
462
+ type: "namespace",
463
+ name: value.name,
464
+ description: typeof value.description === "string" ? value.description : "",
465
+ tools,
466
+ }) : undefined;
467
+ }
468
+
469
+ function structuredToolNames(value) {
470
+ if (value.type === "namespace") return value.tools.map((tool) => `${value.name}${tool.name}`);
471
+ return [value.name];
472
+ }
473
+
474
+ function providerToolDefinition(definition) {
475
+ if (definition.type === "custom") {
476
+ return deepFreeze({
477
+ type: "custom",
478
+ name: definition.name,
479
+ description: typeof definition.description === "string" ? definition.description : "",
480
+ defer_loading: true,
481
+ format: definition.format,
482
+ });
483
+ }
484
+ return deepFreeze({
485
+ type: "function",
486
+ name: definition.name,
487
+ description: typeof definition.description === "string" ? definition.description : "",
488
+ strict: definition.strict === true,
489
+ defer_loading: true,
490
+ parameters: definition.parameters,
491
+ });
492
+ }
493
+
494
+ function catalogSnapshot(snapshot, provider) {
495
+ return deepFreeze(snapshot.definitions
496
+ .filter((definition) => definition.type === "function" || definition.type === "custom")
497
+ .map((definition) => {
498
+ const tool = snapshot.tools.get(definition.name);
499
+ return {
500
+ provider: tool.provider ?? provider,
501
+ remote_name: tool.remoteName ?? definition.name,
502
+ definition,
503
+ parallel_safe: tool.parallelSafe,
504
+ ...(tool.summary !== undefined ? { summary: tool.summary } : {}),
505
+ ...(tool.timeoutMs !== undefined ? { timeout_ms: tool.timeoutMs } : {}),
506
+ };
507
+ })
508
+ .sort((left, right) => left.definition.name.localeCompare(right.definition.name)));
509
+ }
510
+
511
+ function selectCollision(left, right) {
512
+ const leftAttached = left.source.mode === "attached-over-cloud" || left.source.kind === "attached";
513
+ const rightAttached = right.source.mode === "attached-over-cloud" || right.source.kind === "attached";
514
+ if (leftAttached !== rightAttached) {
515
+ const attached = leftAttached ? left : right;
516
+ const cloud = leftAttached ? right : left;
517
+ if (cloud.source.kind !== "cloud") {
518
+ throw collisionError(left, right, "attached overlays may only replace cloud tools");
519
+ }
520
+ if (attached.definition.name !== cloud.definition.name || attached.fingerprint !== cloud.fingerprint) {
521
+ throw collisionError(left, right, "attached/cloud catalog parity mismatch");
522
+ }
523
+ return { ...attached, fallback: cloud };
524
+ }
525
+ throw collisionError(left, right, left.definition.name === right.definition.name
526
+ ? "duplicate tool name"
527
+ : "normalized tool name collision");
528
+ }
529
+
530
+ function overlayTool(attached, cloud) {
531
+ return Object.freeze({
532
+ ...attached,
533
+ parallelSafe: attached.parallelSafe === true && cloud.parallelSafe === true,
534
+ async handler(input, context) {
535
+ const result = await attached.handler(input, context);
536
+ return result?.[preDispatchUnavailable] === true
537
+ ? cloud.handler(input, context)
538
+ : result;
539
+ },
540
+ releaseSession(sessionId) {
541
+ attached.releaseSession?.(sessionId);
542
+ cloud.releaseSession?.(sessionId);
543
+ },
544
+ dispose() {
545
+ attached.dispose?.();
546
+ cloud.dispose?.();
547
+ },
548
+ });
549
+ }
550
+
551
+ function collisionError(left, right, reason) {
552
+ return new Error(`${reason}: ${left.source.id}/${left.definition.name} and ${right.source.id}/${right.definition.name}`);
553
+ }
554
+
555
+ function normalizeSource(source) {
556
+ if (!source || typeof source !== "object" || typeof source.id !== "string" || !source.id.trim()) {
557
+ throw new TypeError("tool source requires a non-empty id");
558
+ }
559
+ if (typeof source.definitions !== "function" || typeof source.resolve !== "function") {
560
+ throw new TypeError(`tool source ${source.id} requires definitions() and resolve(name)`);
561
+ }
562
+ const mode = source.mode ?? "union";
563
+ if (mode !== "union" && mode !== "attached-over-cloud") throw new TypeError(`unknown tool source mode: ${mode}`);
564
+ return Object.freeze({ ...source, id: source.id.trim(), kind: source.kind ?? "union", mode });
565
+ }
566
+
567
+ function normalizeDefinition(definition, source) {
568
+ if (!definition || typeof definition !== "object" || Array.isArray(definition)) {
569
+ throw new TypeError(`tool source ${source} returned a non-object definition`);
570
+ }
571
+ if ((definition.type !== "function" && definition.type !== "custom")
572
+ || typeof definition.name !== "string" || !definition.name.trim()) {
573
+ throw new TypeError(`tool source ${source} returned an invalid callable definition`);
574
+ }
575
+ return deepFreeze(jsonSnapshot(definition, `tool ${definition.name} definition`));
576
+ }
577
+
578
+ function catalogCandidate(value, source) {
579
+ if (value?.definition === undefined) return undefined;
580
+ if (!value || typeof value !== "object" || Array.isArray(value)
581
+ || typeof value.provider !== "string" || !value.provider.trim()
582
+ || typeof value.remote_name !== "string" || !value.remote_name.trim()
583
+ || typeof value.parallel_safe !== "boolean"
584
+ || !Number.isSafeInteger(value.timeout_ms) || value.timeout_ms < 1) {
585
+ throw new TypeError(`tool source ${source} returned an invalid catalog candidate`);
586
+ }
587
+ return value;
588
+ }
589
+
590
+ function normalizeResolvedTool(name, tool, candidate) {
591
+ if (!tool || typeof tool.handler !== "function") throw new TypeError(`resolved tool ${name} requires a handler`);
592
+ return Object.freeze({
593
+ ...tool,
594
+ name,
595
+ parallelSafe: candidate?.parallel_safe
596
+ ?? (tool.parallelSafe === true || tool.supportsParallelToolCalls === true),
597
+ provider: candidate?.provider ?? tool.provider,
598
+ remoteName: candidate?.remote_name ?? tool.remoteName,
599
+ summary: candidate?.summary ?? tool.summary,
600
+ timeoutMs: candidate?.timeout_ms ?? tool.timeoutMs,
601
+ });
602
+ }
603
+
604
+ function callableContractFingerprint(definition) {
605
+ const logical = { ...definition };
606
+ delete logical.defer_loading;
607
+ delete logical.description;
608
+ return stableJson(logical);
609
+ }
610
+
611
+ function compareEntries(a, b) {
612
+ return a.normalizedName.localeCompare(b.normalizedName)
613
+ || a.definition.name.localeCompare(b.definition.name)
614
+ || a.source.id.localeCompare(b.source.id);
615
+ }
616
+
617
+ function requiredQuery(value) {
618
+ if (typeof value !== "string" || !value.trim()) throw new TypeError("tool_search query must not be empty");
619
+ return value.trim();
620
+ }
621
+
622
+ function normalizeLimit(value = 8) {
623
+ if (!Number.isInteger(value) || value < 1) throw new TypeError("tool_search limit must be a positive integer");
624
+ return Math.min(value, 32);
625
+ }
626
+
627
+ function tokenize(value) { return value.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean); }
628
+ function score(entry, words) {
629
+ const text = `${entry.definition.name} ${entry.definition.description ?? ""} ${Object.keys(entry.definition.parameters?.properties ?? {}).join(" ")}`.toLowerCase();
630
+ return words.reduce((total, word) => total + (text.includes(word) ? 1 : 0), 0);
631
+ }
632
+
633
+ function stableJson(value) {
634
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
635
+ if (value && typeof value === "object") {
636
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
637
+ }
638
+ return JSON.stringify(value);
639
+ }
640
+
641
+ function jsonSnapshot(value, label) {
642
+ try { return JSON.parse(JSON.stringify(value)); }
643
+ catch (error) { throw new TypeError(`${label} must be JSON-serializable`, { cause: error }); }
644
+ }
645
+
646
+ function deepFreeze(value) {
647
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
648
+ for (const child of Object.values(value)) deepFreeze(child);
649
+ return Object.freeze(value);
650
+ }
651
+
652
+ class AdmissionGate {
653
+ readers = 0;
654
+ writer = false;
655
+ queue = [];
656
+ read(signal) { return this.#acquire("read", signal); }
657
+ write(signal = new AbortController().signal) { return this.#acquire("write", signal); }
658
+ #acquire(kind, signal) {
659
+ if (signal?.aborted) return Promise.reject(signal.reason ?? new Error(CANCELLATION_MESSAGE));
660
+ if (!this.writer && this.queue.length === 0 && (kind === "read" || this.readers === 0)) {
661
+ if (kind === "read") this.readers++; else this.writer = true;
662
+ return Promise.resolve(once(() => this.#release(kind)));
663
+ }
664
+ return new Promise((resolve, reject) => {
665
+ const waiter = { kind, resolve, reject, signal };
666
+ waiter.abort = () => {
667
+ const index = this.queue.indexOf(waiter);
668
+ if (index >= 0) this.queue.splice(index, 1);
669
+ reject(signal.reason ?? new Error(CANCELLATION_MESSAGE));
670
+ };
671
+ signal?.addEventListener("abort", waiter.abort, { once: true });
672
+ this.queue.push(waiter);
673
+ });
674
+ }
675
+ #release(kind) {
676
+ if (kind === "read") this.readers--; else this.writer = false;
677
+ this.#drain();
678
+ }
679
+ #drain() {
680
+ if (this.writer || this.readers) return;
681
+ const first = this.queue[0];
682
+ if (!first) return;
683
+ if (first.kind === "write") {
684
+ this.queue.shift(); first.signal?.removeEventListener("abort", first.abort); this.writer = true;
685
+ first.resolve(once(() => this.#release("write"))); return;
686
+ }
687
+ while (this.queue[0]?.kind === "read") {
688
+ const waiter = this.queue.shift(); waiter.signal?.removeEventListener("abort", waiter.abort); this.readers++;
689
+ waiter.resolve(once(() => this.#release("read")));
690
+ }
691
+ }
692
+ }
693
+
694
+ class AsyncSemaphore {
695
+ constructor(permits) { this.permits = permits; this.waiters = []; }
696
+ acquire(signal) {
697
+ if (signal.aborted) return Promise.reject(signal.reason);
698
+ if (this.permits > 0 && this.waiters.length === 0) { this.permits--; return Promise.resolve(once(() => this.release())); }
699
+ return new Promise((resolve, reject) => {
700
+ const waiter = { resolve, reject, signal };
701
+ waiter.abort = () => { const index = this.waiters.indexOf(waiter); if (index >= 0) this.waiters.splice(index, 1); reject(signal.reason); };
702
+ signal.addEventListener("abort", waiter.abort, { once: true }); this.waiters.push(waiter);
703
+ });
704
+ }
705
+ release() {
706
+ while (this.waiters.length) { const waiter = this.waiters.shift(); waiter.signal.removeEventListener("abort", waiter.abort); if (waiter.signal.aborted) continue; waiter.resolve(once(() => this.release())); return; }
707
+ this.permits++;
708
+ }
709
+ }
710
+
711
+ class AsyncReadWriteGate {
712
+ readers = 0; writer = false; waiters = [];
713
+ acquire(safe, signal) {
714
+ if (signal.aborted) return Promise.reject(signal.reason);
715
+ if (!this.writer && this.waiters.length === 0 && (safe || this.readers === 0)) return Promise.resolve(this.#grant(safe));
716
+ return new Promise((resolve, reject) => {
717
+ const waiter = { safe, signal, resolve, reject };
718
+ waiter.abort = () => { const index = this.waiters.indexOf(waiter); if (index >= 0) this.waiters.splice(index, 1); reject(signal.reason); };
719
+ signal.addEventListener("abort", waiter.abort, { once: true }); this.waiters.push(waiter);
720
+ });
721
+ }
722
+ #grant(safe) { if (safe) this.readers++; else this.writer = true; return once(() => { if (safe) this.readers--; else this.writer = false; this.#drain(); }); }
723
+ #drain() {
724
+ if (this.writer || this.readers || !this.waiters.length) return;
725
+ if (!this.waiters[0].safe) { const waiter = this.waiters.shift(); waiter.signal.removeEventListener("abort", waiter.abort); waiter.resolve(this.#grant(false)); return; }
726
+ while (this.waiters[0]?.safe && !this.writer) { const waiter = this.waiters.shift(); waiter.signal.removeEventListener("abort", waiter.abort); waiter.resolve(this.#grant(true)); }
727
+ }
728
+ }
729
+
730
+ function once(callback) { let called = false; return () => { if (called) return; called = true; callback(); }; }