@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,533 @@
1
+ import { InvalidResponseError } from "./Errors.mjs";
2
+
3
+ const CLOUD_ACCOUNT_PROVIDERS = Object.freeze([
4
+ "github",
5
+ "gmail",
6
+ "gdrive",
7
+ "gcalendar",
8
+ "gtasks",
9
+ "gdocs",
10
+ "gsheets",
11
+ "gslides",
12
+ "gcontacts",
13
+ "slack",
14
+ "x",
15
+ "chatgpt",
16
+ ]);
17
+ const MCP_CONNECTION_ID = /^[A-Za-z0-9_-]{43}$/;
18
+ const AGENT_CONVERSATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
19
+ const CATALOG_DIGEST = /^0x[0-9a-f]{64}$/;
20
+ const HOST_PRINCIPAL_ID = /^[A-Za-z0-9_-]{43}$/;
21
+
22
+ export function connectionFromWire(value) {
23
+ const wire = object(value, "connection");
24
+ const grant = object(wire.grant, "connection.grant");
25
+ const accessKey = wire.access_key === undefined
26
+ ? undefined
27
+ : object(wire.access_key, "connection.access_key");
28
+ const authorization = authorizationMode(wire.authorization_mode ?? (accessKey ? "access_key" : "hosted"));
29
+ const mpp = wire.mpp === undefined ? undefined : object(wire.mpp, "connection.mpp");
30
+ const hasAccount = wire.account_address !== undefined;
31
+ const hasPrincipal = wire.principal !== undefined;
32
+ if (hasAccount === hasPrincipal) {
33
+ throw new InvalidResponseError("connection must contain exactly one wallet or host principal owner");
34
+ }
35
+ const owner = hasPrincipal
36
+ ? { principal: hostPrincipal(wire.principal, "connection.principal") }
37
+ : { accountAddress: hex(wire.account_address, "connection.account_address") };
38
+ if (hasPrincipal && authorization !== "hosted") {
39
+ throw new InvalidResponseError("host principal connections require hosted authorization");
40
+ }
41
+ if (authorization === "hosted" && (accessKey !== undefined || mpp !== undefined)) {
42
+ throw new InvalidResponseError("hosted connections cannot contain access-key or MPP authority");
43
+ }
44
+ if (authorization === "access_key" && (accessKey === undefined || mpp === undefined)) {
45
+ throw new InvalidResponseError("access-key connections require access-key and MPP authority");
46
+ }
47
+ const capabilities = strings(grant.capabilities, "connection.grant.capabilities");
48
+ const grantMcpConnections = mcpConnections(
49
+ grant.mcp_connections,
50
+ "connection.grant.mcp_connections",
51
+ );
52
+ requireExactMcpProjection(
53
+ capabilities,
54
+ grantMcpConnections,
55
+ "connection.grant",
56
+ );
57
+ const grantConnectors = connectors(capabilities, "connection.grant.capabilities");
58
+ const grantConnectorConnections = connectorConnections(
59
+ grant.connector_connections,
60
+ grantConnectors,
61
+ "connection.grant.connector_connections",
62
+ );
63
+ return Object.freeze({
64
+ ...owner,
65
+ agentId: string(wire.agent_id, "connection.agent_id"),
66
+ grant: Object.freeze({
67
+ id: hex(grant.id, "connection.grant.id"),
68
+ permission: string(grant.permission, "connection.grant.permission"),
69
+ status: status(grant.status),
70
+ expiresAt: integer(grant.expires_at, "connection.grant.expires_at"),
71
+ ...(grant.conversation_id === undefined ? {} : {
72
+ conversationId: agentConversationId(grant.conversation_id, "connection.grant.conversation_id"),
73
+ }),
74
+ capabilities,
75
+ connectors: grantConnectors,
76
+ ...(grantConnectorConnections === undefined ? {} : {
77
+ connectorConnections: grantConnectorConnections,
78
+ }),
79
+ mcpConnections: grantMcpConnections,
80
+ visibility: agentVisibility(capabilities),
81
+ ...(grant.app_tool_catalog_digest === undefined ? {} : {
82
+ appToolCatalogDigest: catalogDigest(
83
+ grant.app_tool_catalog_digest,
84
+ "connection.grant.app_tool_catalog_digest",
85
+ ),
86
+ }),
87
+ }),
88
+ authorization,
89
+ ...(accessKey === undefined ? {} : { accessKey: accessKeyFromWire(accessKey) }),
90
+ ...(mpp === undefined ? {} : { mpp: Object.freeze({
91
+ token: hex(mpp.token, "connection.mpp.token"),
92
+ symbol: string(mpp.symbol, "connection.mpp.symbol"),
93
+ balance: bigint(mpp.balance_atomics, "connection.mpp.balance_atomics"),
94
+ balanceStatus: mpp.balance_status === "ready" ? "ready" : "pending",
95
+ settlementToken: hex(mpp.settlement_token, "connection.mpp.settlement_token"),
96
+ settlementSymbol: string(mpp.settlement_symbol, "connection.mpp.settlement_symbol"),
97
+ settlementBalance: bigint(mpp.settlement_balance_atomics, "connection.mpp.settlement_balance_atomics"),
98
+ spent: bigint(mpp.spent_atomics, "connection.mpp.spent_atomics"),
99
+ limit: bigint(mpp.limit_atomics, "connection.mpp.limit_atomics"),
100
+ period: integer(mpp.period, "connection.mpp.period"),
101
+ maxPerRequest: bigint(mpp.max_per_request_atomics, "connection.mpp.max_per_request_atomics"),
102
+ }) }),
103
+ });
104
+ }
105
+
106
+ function hostPrincipal(value, label) {
107
+ const principal = object(value, label);
108
+ if (principal.kind !== "host" || typeof principal.id !== "string"
109
+ || !HOST_PRINCIPAL_ID.test(principal.id)
110
+ || Object.keys(principal).some((key) => key !== "kind" && key !== "id")) {
111
+ throw new InvalidResponseError(`${label} must contain an exact host principal`);
112
+ }
113
+ return Object.freeze({ kind: "host", id: principal.id });
114
+ }
115
+
116
+ export function connectionMatchesRequest(connection, options = {}) {
117
+ if (options.permission !== undefined && connection.grant.permission !== options.permission) {
118
+ return false;
119
+ }
120
+ if (options.authorization !== undefined && connection.authorization !== options.authorization) return false;
121
+ if (Object.hasOwn(options, "appToolCatalogDigest")) {
122
+ if (options.appToolCatalogDigest !== connection.grant.appToolCatalogDigest) return false;
123
+ }
124
+ if (Object.hasOwn(options, "conversationId")) {
125
+ const requested = options.conversationId;
126
+ if (requested === null) {
127
+ if (connection.grant.conversationId !== undefined) return false;
128
+ } else if (typeof requested !== "string" || !AGENT_CONVERSATION_ID.test(requested)
129
+ || connection.grant.conversationId !== requested) {
130
+ return false;
131
+ }
132
+ }
133
+ const requestedCloudAccounts = options.capabilities?.cloudAccounts;
134
+ if (requestedCloudAccounts !== undefined) {
135
+ const requested = CLOUD_ACCOUNT_PROVIDERS.filter(
136
+ (provider) => requestedCloudAccounts?.[provider] === true,
137
+ );
138
+ if (requested.length !== connection.grant.connectors.length
139
+ || requested.some((provider) => !connection.grant.connectors.includes(provider))) {
140
+ return false;
141
+ }
142
+ }
143
+ if (Object.hasOwn(options, "connectorConnections")
144
+ && !sameConnectorConnections(
145
+ options.connectorConnections,
146
+ connection.grant.connectorConnections,
147
+ )) return false;
148
+ const requestedAgent = options.capabilities?.agent;
149
+ if (requestedAgent !== undefined) {
150
+ const rawTraces = requestedAgent?.rawTraces === true;
151
+ const expected = {
152
+ finalMessages: rawTraces || requestedAgent?.finalMessages !== false,
153
+ actionSummaries: rawTraces || requestedAgent?.actionSummaries !== false,
154
+ conversationHistory: rawTraces || requestedAgent?.conversationHistory === true,
155
+ rawTraces,
156
+ };
157
+ for (const name of Object.keys(expected)) {
158
+ if (connection.grant.visibility[name] !== expected[name]) return false;
159
+ }
160
+ }
161
+ if (options.mcpConnectionIds !== undefined) {
162
+ if (!Array.isArray(options.mcpConnectionIds)
163
+ || options.mcpConnectionIds.some((id) => typeof id !== "string" || !MCP_CONNECTION_ID.test(id))
164
+ || new Set(options.mcpConnectionIds).size !== options.mcpConnectionIds.length) {
165
+ return false;
166
+ }
167
+ const actual = connection.grant.mcpConnections.map(({ id }) => id);
168
+ if (actual.length !== options.mcpConnectionIds.length
169
+ || actual.some((id) => !options.mcpConnectionIds.includes(id))) {
170
+ return false;
171
+ }
172
+ }
173
+ return true;
174
+ }
175
+
176
+ /** Builds the exact non-secret request projection retained by one minted grant. */
177
+ export function reconnectRequestFromConnection(connection) {
178
+ return connectionRequestFromGrant({
179
+ ...connection.grant,
180
+ authorization: connection.authorization,
181
+ });
182
+ }
183
+
184
+ /** Builds the exact non-secret request projection for normalized grant fields. */
185
+ export function connectionRequestFromGrant(grant) {
186
+ return Object.freeze({
187
+ capabilities: Object.freeze({
188
+ agent: grant.visibility,
189
+ cloudAccounts: Object.freeze(Object.fromEntries(
190
+ grant.connectors.map((provider) => [provider, true]),
191
+ )),
192
+ }),
193
+ mcpConnectionIds: Object.freeze(grant.mcpConnections.map(({ id }) => id)),
194
+ ...(grant.connectorConnections === undefined ? {} : {
195
+ connectorConnections: grant.connectorConnections,
196
+ }),
197
+ authorization: grant.authorization ?? "access_key",
198
+ appToolCatalogDigest: grant.appToolCatalogDigest,
199
+ permission: grant.permission,
200
+ conversationId: grant.conversationId ?? null,
201
+ });
202
+ }
203
+
204
+ export function preparedConnectionFromWire(value) {
205
+ const wire = object(value, "prepared connection");
206
+ const app = object(wire.app, "prepared connection.app");
207
+ const auth = object(wire.auth, "prepared connection.auth");
208
+ const permission = object(wire.permission, "prepared connection.permission");
209
+ const mpp = object(wire.mpp, "prepared connection.mpp");
210
+ const prepared = Object.freeze({
211
+ requestId: string(wire.request_id, "prepared connection.request_id"),
212
+ app: Object.freeze({
213
+ id: string(app.id, "prepared connection.app.id"),
214
+ name: string(app.name, "prepared connection.app.name"),
215
+ origin: string(app.origin, "prepared connection.app.origin"),
216
+ }),
217
+ accountAddress: hex(wire.account_address, "prepared connection.account_address"),
218
+ auth: Object.freeze({
219
+ message: string(auth.message, "prepared connection.auth.message"),
220
+ resources: strings(auth.resources, "prepared connection.auth.resources"),
221
+ }),
222
+ permission: Object.freeze({
223
+ id: string(permission.id, "prepared connection.permission.id"),
224
+ title: string(permission.title, "prepared connection.permission.title"),
225
+ description: string(permission.description, "prepared connection.permission.description"),
226
+ connectors: array(permission.connectors, "prepared connection.permission.connectors").map((item, index) => {
227
+ const connector = object(item, `prepared connection.permission.connectors[${index}]`);
228
+ return Object.freeze({
229
+ id: string(connector.id, `prepared connection.permission.connectors[${index}].id`),
230
+ name: string(connector.name, `prepared connection.permission.connectors[${index}].name`),
231
+ detail: string(connector.detail, `prepared connection.permission.connectors[${index}].detail`),
232
+ });
233
+ }),
234
+ }),
235
+ accessKey: accessKeyFromWire(object(wire.access_key, "prepared connection.access_key")),
236
+ mpp: Object.freeze({
237
+ token: hex(mpp.token, "prepared connection.mpp.token"),
238
+ symbol: string(mpp.symbol, "prepared connection.mpp.symbol"),
239
+ limit: bigint(mpp.limit_atomics, "prepared connection.mpp.limit_atomics"),
240
+ period: integer(mpp.period, "prepared connection.mpp.period"),
241
+ maxPerRequest: bigint(mpp.max_per_request_atomics, "prepared connection.mpp.max_per_request_atomics"),
242
+ }),
243
+ });
244
+ return { prepared, wire };
245
+ }
246
+
247
+ export function accessKeyFromWire(wire) {
248
+ return Object.freeze({
249
+ address: hex(wire.address, "access key.address"),
250
+ chainId: bigint(wire.chain_id, "access key.chain_id"),
251
+ keyId: hex(wire.key_id, "access key.key_id"),
252
+ ...(wire.public_key === undefined ? {} : { publicKey: hex(wire.public_key, "access key.public_key") }),
253
+ keyType: keyType(wire.key_type),
254
+ limits: Object.freeze(array(wire.limits, "access key.limits").map((item, index) => {
255
+ const limit = object(item, `access key.limits[${index}]`);
256
+ return Object.freeze({
257
+ token: hex(limit.token, `access key.limits[${index}].token`),
258
+ limit: bigint(limit.limit, `access key.limits[${index}].limit`),
259
+ ...(limit.period === undefined ? {} : { period: integer(limit.period, `access key.limits[${index}].period`) }),
260
+ });
261
+ })),
262
+ scopes: Object.freeze(array(wire.scopes, "access key.scopes").map((item, index) => {
263
+ const scope = object(item, `access key.scopes[${index}]`);
264
+ return Object.freeze({
265
+ address: hex(scope.address, `access key.scopes[${index}].address`),
266
+ ...(scope.selector === undefined ? {} : { selector: string(scope.selector, `access key.scopes[${index}].selector`) }),
267
+ ...(scope.recipients === undefined ? {} : { recipients: strings(scope.recipients, `access key.scopes[${index}].recipients`) }),
268
+ });
269
+ })),
270
+ witness: hex(wire.witness, "access key.witness"),
271
+ expiry: integer(wire.expiry, "access key.expiry"),
272
+ ...(wire.authorization === undefined ? {} : { authorization: hex(wire.authorization, "access key.authorization") }),
273
+ });
274
+ }
275
+
276
+ export function grantFromWire(value) {
277
+ const grant = object(value, "grant");
278
+ const capabilities = strings(grant.capabilities, "grant.capabilities");
279
+ const grantMcpConnections = mcpConnections(grant.mcp_connections, "grant.mcp_connections");
280
+ requireExactMcpProjection(capabilities, grantMcpConnections, "grant");
281
+ const grantConnectors = connectors(capabilities, "grant.capabilities");
282
+ const grantConnectorConnections = connectorConnections(
283
+ grant.connector_connections,
284
+ grantConnectors,
285
+ "grant.connector_connections",
286
+ );
287
+ return Object.freeze({
288
+ id: hex(grant.id, "grant.id"),
289
+ permission: string(grant.permission, "grant.permission"),
290
+ status: status(grant.status),
291
+ expiresAt: integer(grant.expires_at, "grant.expires_at"),
292
+ ...(grant.conversation_id === undefined ? {} : {
293
+ conversationId: agentConversationId(grant.conversation_id, "grant.conversation_id"),
294
+ }),
295
+ capabilities,
296
+ connectors: grantConnectors,
297
+ ...(grantConnectorConnections === undefined ? {} : {
298
+ connectorConnections: grantConnectorConnections,
299
+ }),
300
+ mcpConnections: grantMcpConnections,
301
+ visibility: agentVisibility(capabilities),
302
+ ...(grant.app_tool_catalog_digest === undefined ? {} : {
303
+ appToolCatalogDigest: catalogDigest(grant.app_tool_catalog_digest, "grant.app_tool_catalog_digest"),
304
+ }),
305
+ });
306
+ }
307
+
308
+ function authorizationMode(value) {
309
+ if (value === "access_key" || value === "hosted") return value;
310
+ throw new InvalidResponseError("connection.authorization_mode must be access_key or hosted");
311
+ }
312
+
313
+ function catalogDigest(value, name) {
314
+ if (typeof value !== "string" || !CATALOG_DIGEST.test(value)) {
315
+ throw new InvalidResponseError(`${name} must be a lowercase SHA-256 digest`);
316
+ }
317
+ return value;
318
+ }
319
+
320
+ const AGENT_VISIBILITY_CAPABILITIES = Object.freeze({
321
+ finalMessages: "agent.output.final",
322
+ actionSummaries: "agent.output.actions",
323
+ conversationHistory: "agent.history.read",
324
+ rawTraces: "agent.trace.read",
325
+ });
326
+
327
+ function agentVisibility(capabilities) {
328
+ const recognized = Object.values(AGENT_VISIBILITY_CAPABILITIES)
329
+ .some((capability) => capabilities.includes(capability));
330
+ const rawTraces = capabilities.includes(AGENT_VISIBILITY_CAPABILITIES.rawTraces);
331
+ return Object.freeze({
332
+ finalMessages: rawTraces || !recognized || capabilities.includes(AGENT_VISIBILITY_CAPABILITIES.finalMessages),
333
+ actionSummaries: rawTraces || !recognized || capabilities.includes(AGENT_VISIBILITY_CAPABILITIES.actionSummaries),
334
+ conversationHistory: rawTraces || capabilities.includes(AGENT_VISIBILITY_CAPABILITIES.conversationHistory),
335
+ rawTraces,
336
+ });
337
+ }
338
+
339
+ function agentConversationId(value, name) {
340
+ const id = string(value, name);
341
+ if (!AGENT_CONVERSATION_ID.test(id)) throw new InvalidResponseError(`${name} must be a lowercase UUIDv4`);
342
+ return id;
343
+ }
344
+
345
+ export function machineUsdConfigFromWire(value) {
346
+ const wire = object(value, "MACH config");
347
+ return Object.freeze({
348
+ chainId: integer(wire.chain_id, "MACH config.chain_id"),
349
+ minUsdAmountCents: integer(wire.min_usd_amount_cents, "MACH config.min_usd_amount_cents"),
350
+ maxUsdAmountCents: integer(wire.max_usd_amount_cents, "MACH config.max_usd_amount_cents"),
351
+ onrampEnabled: wire.onramp_enabled === undefined
352
+ ? true
353
+ : boolean(wire.onramp_enabled, "MACH config.onramp_enabled"),
354
+ stripePublishableKey: string(wire.stripe_publishable_key, "MACH config.stripe_publishable_key"),
355
+ tokenAddress: hex(wire.token_address, "MACH config.token_address"),
356
+ });
357
+ }
358
+
359
+ export function fundResultFromWire(value, client) {
360
+ const wire = object(value, "MACH funding result");
361
+ const order = object(wire.order, "MACH funding result.order");
362
+ return Object.freeze({
363
+ order: Object.freeze({
364
+ id: string(order.id, "MACH order.id"),
365
+ status: string(order.status, "MACH order.status"),
366
+ usdAmountCents: integer(order.usd_amount_cents, "MACH order.usd_amount_cents"),
367
+ machineUsdAmount: bigint(
368
+ order.mach_amount_atomics ?? order.machine_usd_amount_atomics,
369
+ "MACH order.mach_amount_atomics",
370
+ ),
371
+ issuanceTransactionHash: hex(order.issuance_transaction_hash, "MACH order.issuance_transaction_hash"),
372
+ }),
373
+ connection: connectionFromWire(wire.connection),
374
+ });
375
+ }
376
+
377
+ export function chargeResultFromWire(value, client) {
378
+ const wire = object(value, "MPP charge result");
379
+ const receipt = object(wire.receipt, "MPP charge result.receipt");
380
+ return Object.freeze({
381
+ receipt: Object.freeze({
382
+ id: string(receipt.id, "MPP receipt.id"),
383
+ amount: bigint(receipt.amount_atomics, "MPP receipt.amount_atomics"),
384
+ origin: string(receipt.origin, "MPP receipt.origin"),
385
+ transactionHash: hex(receipt.transaction_hash, "MPP receipt.transaction_hash"),
386
+ }),
387
+ connection: connectionFromWire(wire.connection),
388
+ });
389
+ }
390
+
391
+ function array(value, label) {
392
+ if (!Array.isArray(value)) throw new InvalidResponseError(`${label} must be an array`);
393
+ return value;
394
+ }
395
+
396
+ function object(value, label) {
397
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
398
+ throw new InvalidResponseError(`${label} must be an object`);
399
+ }
400
+ return value;
401
+ }
402
+
403
+ function string(value, label) {
404
+ if (typeof value !== "string" || value.length === 0) {
405
+ throw new InvalidResponseError(`${label} must be a non-empty string`);
406
+ }
407
+ return value;
408
+ }
409
+
410
+ function strings(value, label) {
411
+ return Object.freeze(array(value, label).map((item, index) => string(item, `${label}[${index}]`)));
412
+ }
413
+
414
+ function connectors(capabilities, label) {
415
+ const items = strings(capabilities, label);
416
+ return Object.freeze(CLOUD_ACCOUNT_PROVIDERS.filter((provider) =>
417
+ items.includes(provider) || items.includes(`urn:nanocodex:connector:${provider}`)
418
+ ));
419
+ }
420
+
421
+ function connectorConnections(value, grantConnectors, label) {
422
+ if (value === undefined) return undefined;
423
+ const selection = object(value, label);
424
+ const projected = {};
425
+ for (const [capability, rawIds] of Object.entries(selection)) {
426
+ if (capability === "chatgpt" || !grantConnectors.includes(capability)) {
427
+ throw new InvalidResponseError(`${label} contains an ungranted connector capability`);
428
+ }
429
+ const ids = array(rawIds, `${label}.${capability}`);
430
+ if (ids.length > 64) {
431
+ throw new InvalidResponseError(`${label}.${capability} contains too many connections`);
432
+ }
433
+ const normalized = ids.map((id, index) => {
434
+ const connectionId = string(id, `${label}.${capability}[${index}]`);
435
+ if (!MCP_CONNECTION_ID.test(connectionId)) {
436
+ throw new InvalidResponseError(`${label}.${capability}[${index}] is not an opaque connection ID`);
437
+ }
438
+ return connectionId;
439
+ });
440
+ if (new Set(normalized).size !== normalized.length) {
441
+ throw new InvalidResponseError(`${label}.${capability} contains duplicate connections`);
442
+ }
443
+ projected[capability] = Object.freeze(normalized);
444
+ }
445
+ return Object.freeze(projected);
446
+ }
447
+
448
+ function sameConnectorConnections(left, right) {
449
+ if (left === undefined || right === undefined) return left === right;
450
+ if (!left || typeof left !== "object" || Array.isArray(left)
451
+ || !right || typeof right !== "object" || Array.isArray(right)) return false;
452
+ const leftKeys = Object.keys(left).sort();
453
+ const rightKeys = Object.keys(right).sort();
454
+ if (leftKeys.length !== rightKeys.length
455
+ || leftKeys.some((key, index) => key !== rightKeys[index])) return false;
456
+ return leftKeys.every((key) => Array.isArray(left[key]) && Array.isArray(right[key])
457
+ && left[key].length === right[key].length
458
+ && left[key].every((id, index) => id === right[key][index]));
459
+ }
460
+
461
+ function mcpConnections(value, label) {
462
+ if (value === undefined) return Object.freeze([]);
463
+ const ids = new Set();
464
+ const connections = array(value, label);
465
+ if (connections.length > 16) throw new InvalidResponseError(`${label} must contain at most 16 connections`);
466
+ return Object.freeze(connections.map((item, index) => {
467
+ const connection = object(item, `${label}[${index}]`);
468
+ if (Object.keys(connection).some((key) => key !== "id" && key !== "name")) {
469
+ throw new InvalidResponseError(`${label}[${index}] contains private or unknown fields`);
470
+ }
471
+ const id = string(connection.id, `${label}[${index}].id`);
472
+ const name = string(connection.name, `${label}[${index}].name`);
473
+ if (!MCP_CONNECTION_ID.test(id) || ids.has(id)
474
+ || name.length < 1 || name.length > 256 || name.trim() !== name) {
475
+ throw new InvalidResponseError(`${label}[${index}] must contain an exact hosted MCP identity`);
476
+ }
477
+ ids.add(id);
478
+ return Object.freeze({ id, name });
479
+ }));
480
+ }
481
+
482
+ function requireExactMcpProjection(capabilities, connections, label) {
483
+ const ids = capabilities.flatMap((capability) => capability.startsWith("mcp:")
484
+ ? [capability.slice("mcp:".length)]
485
+ : []);
486
+ if (ids.length > 16
487
+ || ids.some((id) => !MCP_CONNECTION_ID.test(id))
488
+ || new Set(ids).size !== ids.length) {
489
+ throw new InvalidResponseError(`${label}.capabilities contains invalid hosted MCP identities`);
490
+ }
491
+ const metadataIds = connections.map(({ id }) => id);
492
+ if (ids.length !== metadataIds.length || ids.some((id) => !metadataIds.includes(id))) {
493
+ throw new InvalidResponseError(`${label} MCP capabilities and metadata must match exactly`);
494
+ }
495
+ }
496
+
497
+ function hex(value, label) {
498
+ const result = string(value, label);
499
+ if (!/^0x[0-9a-fA-F]+$/.test(result)) throw new InvalidResponseError(`${label} must be hex`);
500
+ return result;
501
+ }
502
+
503
+ function bigint(value, label) {
504
+ try {
505
+ return BigInt(value);
506
+ } catch {
507
+ throw new InvalidResponseError(`${label} must be an integer string`);
508
+ }
509
+ }
510
+
511
+ function integer(value, label) {
512
+ if (!Number.isSafeInteger(value)) throw new InvalidResponseError(`${label} must be a safe integer`);
513
+ return value;
514
+ }
515
+
516
+ function boolean(value, label) {
517
+ if (typeof value !== "boolean") throw new InvalidResponseError(`${label} must be a boolean`);
518
+ return value;
519
+ }
520
+
521
+ function status(value) {
522
+ if (value !== "active" && value !== "revoked" && value !== "expired") {
523
+ throw new InvalidResponseError("grant.status is invalid");
524
+ }
525
+ return value;
526
+ }
527
+
528
+ function keyType(value) {
529
+ if (value !== "secp256k1" && value !== "p256" && value !== "webAuthn") {
530
+ throw new InvalidResponseError("access key.key_type is invalid");
531
+ }
532
+ return value;
533
+ }
@@ -0,0 +1,108 @@
1
+ import { DEFAULT_MERCATOR_MCP_URL } from "../runtime/tempo-provider.mjs";
2
+
3
+ const MERCATOR_ORIGIN = new URL(DEFAULT_MERCATOR_MCP_URL).origin;
4
+ const JOBS_PATH = "/v1/jobs";
5
+ const MAX_BODY_BYTES = 64 * 1024;
6
+ const PAYMENT_DECIMALS = 6;
7
+
8
+ /** @internal Creates the bounded executor for Mercator's paid REST handoff. */
9
+ export function mercatorRestTool({ connection, fetch, calls, relay }) {
10
+ if (typeof fetch !== "function") {
11
+ throw new TypeError("Mercator REST execution requires an MPP-aware fetch function");
12
+ }
13
+ if (!connection.mpp) {
14
+ throw new TypeError("Mercator requires a Connect grant with explicit payment authority");
15
+ }
16
+ const relayUrl = new URL(relay);
17
+ return {
18
+ description: "Execute Mercator's run_rest_request handoff through MPP. Use only the exact rest and maxSpend fields returned by Mercator create_job, then poll the returned job with Mercator get_job.",
19
+ parameters: {
20
+ type: "object",
21
+ properties: {
22
+ url: { type: "string" },
23
+ method: { type: "string", enum: ["POST"] },
24
+ body: { type: "object", additionalProperties: true },
25
+ maxSpend: { type: "string", description: "Maximum quoted spend in decimal USDC, copied from the Mercator handoff." },
26
+ },
27
+ required: ["url", "method", "body", "maxSpend"],
28
+ additionalProperties: false,
29
+ },
30
+ async handler(input, context) {
31
+ calls.add("run_rest_request", context);
32
+ const url = mercatorJobUrl(input?.url);
33
+ if (input?.method !== "POST") {
34
+ throw new Error("Mercator REST handoffs must use POST.");
35
+ }
36
+ const maxAmount = decimalAtomics(input?.maxSpend, PAYMENT_DECIMALS);
37
+ if (maxAmount <= 0n) throw new Error("Mercator maxSpend must be greater than zero.");
38
+ if (maxAmount > connection.mpp.maxPerRequest) {
39
+ throw new Error(
40
+ `Mercator maxSpend exceeds this Connect grant's per-request limit (${connection.mpp.maxPerRequest} atomics).`,
41
+ );
42
+ }
43
+ if (!plainObject(input.body)) {
44
+ throw new TypeError("Mercator REST handoff body must be an object.");
45
+ }
46
+ const body = JSON.stringify(input.body);
47
+ if (new TextEncoder().encode(body).byteLength > MAX_BODY_BYTES) {
48
+ throw new Error(`Mercator REST handoff body exceeds ${MAX_BODY_BYTES} bytes.`);
49
+ }
50
+
51
+ const response = await fetch(relayUrl, {
52
+ method: "POST",
53
+ headers: { accept: "application/json", "content-type": "application/json" },
54
+ body,
55
+ }, { intent: "charge", maxAmount });
56
+ const responseText = await response.text();
57
+ if (!response.ok) {
58
+ throw new Error(
59
+ `Mercator job submission failed with HTTP ${response.status}: ${responseText.slice(0, 2_048)}`,
60
+ );
61
+ }
62
+ return {
63
+ status: response.status,
64
+ paymentReceipt: response.headers.get("payment-receipt") ?? undefined,
65
+ result: parseResponse(responseText),
66
+ };
67
+ },
68
+ };
69
+ }
70
+
71
+ function mercatorJobUrl(value) {
72
+ let url;
73
+ try {
74
+ url = new URL(value);
75
+ } catch (error) {
76
+ throw new TypeError("Mercator REST handoff URL is invalid.", { cause: error });
77
+ }
78
+ if (url.origin !== MERCATOR_ORIGIN || url.pathname !== JOBS_PATH
79
+ || url.username || url.password || url.search || url.hash) {
80
+ throw new Error(`Mercator REST handoff must target ${MERCATOR_ORIGIN}${JOBS_PATH}.`);
81
+ }
82
+ return url.href;
83
+ }
84
+
85
+ function decimalAtomics(value, decimals) {
86
+ if (typeof value !== "string") throw new TypeError("Mercator maxSpend must be a decimal string.");
87
+ const match = /^(\d+)(?:\.(\d+))?$/.exec(value);
88
+ if (!match || (match[2]?.length ?? 0) > decimals) {
89
+ throw new TypeError(`Mercator maxSpend must have at most ${decimals} decimal places.`);
90
+ }
91
+ return BigInt(match[1]) * (10n ** BigInt(decimals))
92
+ + BigInt((match[2] ?? "").padEnd(decimals, "0") || "0");
93
+ }
94
+
95
+ function plainObject(value) {
96
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
97
+ const prototype = Object.getPrototypeOf(value);
98
+ return prototype === Object.prototype || prototype === null;
99
+ }
100
+
101
+ function parseResponse(value) {
102
+ if (!value) return null;
103
+ try {
104
+ return JSON.parse(value);
105
+ } catch {
106
+ return value;
107
+ }
108
+ }
@@ -0,0 +1,34 @@
1
+ export type Claims = Readonly<{
2
+ /** Stable opaque identity-provider namespace. Limited to 512 non-control characters. */
3
+ issuer: string;
4
+ /** Stable opaque tenant namespace within the issuer. Limited to 512 non-control characters. */
5
+ tenant: string;
6
+ /** Stable opaque user subject; limited to 512 non-control characters. */
7
+ subject: string;
8
+ /** Opaque host-login session identifier; limited to 512 non-control characters. */
9
+ sessionId: string;
10
+ }>;
11
+
12
+ export type Exchange = Readonly<{ token: string; expiresAt: number }>;
13
+
14
+ export type Client = Readonly<{
15
+ create(options: Claims & Readonly<{
16
+ resources: readonly string[];
17
+ expiresIn?: number | undefined;
18
+ signal?: AbortSignal | undefined;
19
+ }>): Promise<Exchange>;
20
+ handler(options: Readonly<{
21
+ authenticate(request: Request): Promise<Claims | undefined> | Claims | undefined;
22
+ }>): (request: Request) => Promise<Response>;
23
+ revoke(options: Claims & Readonly<{
24
+ signal?: AbortSignal | undefined;
25
+ }>): Promise<void>;
26
+ }>;
27
+
28
+ export function create(parameters: Readonly<{
29
+ appId: string;
30
+ appOrigin: string;
31
+ secret: string;
32
+ baseUrl?: string | URL | undefined;
33
+ fetch?: typeof globalThis.fetch | undefined;
34
+ }>): Client;