@absol-labs/agent 0.10.0 → 0.12.0-rc.1

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 (331) hide show
  1. package/README.md +106 -30
  2. package/dist/capability/invocation-capability.d.ts +20 -8
  3. package/dist/capability/invocation-capability.d.ts.map +1 -1
  4. package/dist/capability/invocation-capability.js.map +1 -1
  5. package/dist/capability/probe-capability.d.ts +225 -0
  6. package/dist/capability/probe-capability.d.ts.map +1 -0
  7. package/dist/capability/probe-capability.js +248 -0
  8. package/dist/capability/probe-capability.js.map +1 -0
  9. package/dist/cli/args.d.ts +11 -0
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/args.js +23 -0
  12. package/dist/cli/args.js.map +1 -1
  13. package/dist/cli/commands/claim.d.ts.map +1 -1
  14. package/dist/cli/commands/claim.js +21 -5
  15. package/dist/cli/commands/claim.js.map +1 -1
  16. package/dist/cli/commands/close.d.ts.map +1 -1
  17. package/dist/cli/commands/close.js +51 -7
  18. package/dist/cli/commands/close.js.map +1 -1
  19. package/dist/cli/commands/invoke.d.ts +2 -0
  20. package/dist/cli/commands/invoke.d.ts.map +1 -1
  21. package/dist/cli/commands/invoke.js +3 -2
  22. package/dist/cli/commands/invoke.js.map +1 -1
  23. package/dist/cli/commands/mcp.d.ts.map +1 -1
  24. package/dist/cli/commands/mcp.js +15 -2
  25. package/dist/cli/commands/mcp.js.map +1 -1
  26. package/dist/cli/commands/open.d.ts.map +1 -1
  27. package/dist/cli/commands/open.js +121 -15
  28. package/dist/cli/commands/open.js.map +1 -1
  29. package/dist/cli/commands/pay.d.ts +19 -0
  30. package/dist/cli/commands/pay.d.ts.map +1 -0
  31. package/dist/cli/commands/pay.js +355 -0
  32. package/dist/cli/commands/pay.js.map +1 -0
  33. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  34. package/dist/cli/commands/reclaim-unverified.js +15 -6
  35. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  36. package/dist/cli/commands/reclaim.d.ts +23 -2
  37. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  38. package/dist/cli/commands/reclaim.js +200 -21
  39. package/dist/cli/commands/reclaim.js.map +1 -1
  40. package/dist/cli/commands/refund.d.ts +13 -0
  41. package/dist/cli/commands/refund.d.ts.map +1 -0
  42. package/dist/cli/commands/refund.js +224 -0
  43. package/dist/cli/commands/refund.js.map +1 -0
  44. package/dist/cli/commands/status.d.ts.map +1 -1
  45. package/dist/cli/commands/status.js +10 -1
  46. package/dist/cli/commands/status.js.map +1 -1
  47. package/dist/cli/commands/streams.js +9 -13
  48. package/dist/cli/commands/streams.js.map +1 -1
  49. package/dist/cli/commands/watch.d.ts.map +1 -1
  50. package/dist/cli/commands/watch.js +2 -1
  51. package/dist/cli/commands/watch.js.map +1 -1
  52. package/dist/cli/config.d.ts +44 -26
  53. package/dist/cli/config.d.ts.map +1 -1
  54. package/dist/cli/config.js +88 -38
  55. package/dist/cli/config.js.map +1 -1
  56. package/dist/cli/deps.d.ts +86 -7
  57. package/dist/cli/deps.d.ts.map +1 -1
  58. package/dist/cli/deps.js +102 -16
  59. package/dist/cli/deps.js.map +1 -1
  60. package/dist/cli/errors.d.ts.map +1 -1
  61. package/dist/cli/errors.js +21 -1
  62. package/dist/cli/errors.js.map +1 -1
  63. package/dist/cli/escape-hatch.d.ts +6 -4
  64. package/dist/cli/escape-hatch.d.ts.map +1 -1
  65. package/dist/cli/escape-hatch.js +6 -2
  66. package/dist/cli/escape-hatch.js.map +1 -1
  67. package/dist/cli/listings.d.ts +18 -0
  68. package/dist/cli/listings.d.ts.map +1 -1
  69. package/dist/cli/listings.js +22 -12
  70. package/dist/cli/listings.js.map +1 -1
  71. package/dist/cli/preflight.d.ts +20 -14
  72. package/dist/cli/preflight.d.ts.map +1 -1
  73. package/dist/cli/preflight.js +34 -20
  74. package/dist/cli/preflight.js.map +1 -1
  75. package/dist/cli/privy.d.ts +83 -0
  76. package/dist/cli/privy.d.ts.map +1 -0
  77. package/dist/cli/privy.js +232 -0
  78. package/dist/cli/privy.js.map +1 -0
  79. package/dist/cli/reclaimability.d.ts +82 -0
  80. package/dist/cli/reclaimability.d.ts.map +1 -0
  81. package/dist/cli/reclaimability.js +80 -0
  82. package/dist/cli/reclaimability.js.map +1 -0
  83. package/dist/cli/run.d.ts +2 -1
  84. package/dist/cli/run.d.ts.map +1 -1
  85. package/dist/cli/run.js +40 -11
  86. package/dist/cli/run.js.map +1 -1
  87. package/dist/cli/scan-range.d.ts +2 -2
  88. package/dist/cli/scan-range.js +2 -2
  89. package/dist/cli/spend-policy.d.ts +60 -0
  90. package/dist/cli/spend-policy.d.ts.map +1 -0
  91. package/dist/cli/spend-policy.js +65 -0
  92. package/dist/cli/spend-policy.js.map +1 -0
  93. package/dist/cli/status.d.ts +3 -1
  94. package/dist/cli/status.d.ts.map +1 -1
  95. package/dist/cli/status.js +15 -3
  96. package/dist/cli/status.js.map +1 -1
  97. package/dist/cli/wallet.d.ts +109 -40
  98. package/dist/cli/wallet.d.ts.map +1 -1
  99. package/dist/cli/wallet.js +109 -44
  100. package/dist/cli/wallet.js.map +1 -1
  101. package/dist/discovery/rate-band.d.ts +74 -0
  102. package/dist/discovery/rate-band.d.ts.map +1 -0
  103. package/dist/discovery/rate-band.js +114 -0
  104. package/dist/discovery/rate-band.js.map +1 -0
  105. package/dist/escrow-generation.d.ts +81 -0
  106. package/dist/escrow-generation.d.ts.map +1 -0
  107. package/dist/escrow-generation.js +76 -0
  108. package/dist/escrow-generation.js.map +1 -0
  109. package/dist/frameworks/agentkit.d.ts +8 -4
  110. package/dist/frameworks/agentkit.d.ts.map +1 -1
  111. package/dist/frameworks/agentkit.js +27 -10
  112. package/dist/frameworks/agentkit.js.map +1 -1
  113. package/dist/frameworks/eliza.d.ts +3 -3
  114. package/dist/frameworks/eliza.d.ts.map +1 -1
  115. package/dist/frameworks/eliza.js +15 -6
  116. package/dist/frameworks/eliza.js.map +1 -1
  117. package/dist/frameworks/langchain.d.ts.map +1 -1
  118. package/dist/frameworks/langchain.js +8 -0
  119. package/dist/frameworks/langchain.js.map +1 -1
  120. package/dist/gateway/caller-auth-gateway.d.ts +91 -4
  121. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  122. package/dist/gateway/caller-auth-gateway.js +184 -7
  123. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  124. package/dist/gateway/http-server.d.ts +121 -1
  125. package/dist/gateway/http-server.d.ts.map +1 -1
  126. package/dist/gateway/http-server.js +474 -5
  127. package/dist/gateway/http-server.js.map +1 -1
  128. package/dist/gateway/probe-auth.d.ts +112 -0
  129. package/dist/gateway/probe-auth.d.ts.map +1 -0
  130. package/dist/gateway/probe-auth.js +127 -0
  131. package/dist/gateway/probe-auth.js.map +1 -0
  132. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  133. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  134. package/dist/gateway/signed-rate-bounds.js +94 -0
  135. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  136. package/dist/gateway/x402-opener-mode.d.ts +215 -0
  137. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  138. package/dist/gateway/x402-opener-mode.js +639 -0
  139. package/dist/gateway/x402-opener-mode.js.map +1 -0
  140. package/dist/index.d.ts +11 -5
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +15 -5
  143. package/dist/index.js.map +1 -1
  144. package/dist/mandates/env.d.ts +3 -2
  145. package/dist/mandates/env.d.ts.map +1 -1
  146. package/dist/mandates/env.js +3 -2
  147. package/dist/mandates/env.js.map +1 -1
  148. package/dist/mcp/server.d.ts +100 -0
  149. package/dist/mcp/server.d.ts.map +1 -1
  150. package/dist/mcp/server.js +719 -39
  151. package/dist/mcp/server.js.map +1 -1
  152. package/dist/mcp/service-invoker.d.ts +97 -0
  153. package/dist/mcp/service-invoker.d.ts.map +1 -0
  154. package/dist/mcp/service-invoker.js +167 -0
  155. package/dist/mcp/service-invoker.js.map +1 -0
  156. package/dist/mcp/x402-tools.d.ts +104 -0
  157. package/dist/mcp/x402-tools.d.ts.map +1 -0
  158. package/dist/mcp/x402-tools.js +434 -0
  159. package/dist/mcp/x402-tools.js.map +1 -0
  160. package/dist/network.d.ts +119 -0
  161. package/dist/network.d.ts.map +1 -0
  162. package/dist/network.js +199 -0
  163. package/dist/network.js.map +1 -0
  164. package/dist/sdk/invoke.d.ts +23 -4
  165. package/dist/sdk/invoke.d.ts.map +1 -1
  166. package/dist/sdk/invoke.js +71 -10
  167. package/dist/sdk/invoke.js.map +1 -1
  168. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  169. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  170. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  171. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  172. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  173. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  174. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  175. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  176. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  177. package/dist/wallet/autonomous-wallet.js +3 -2
  178. package/dist/wallet/autonomous-wallet.js.map +1 -1
  179. package/dist/wallet/broker-escrows.d.ts +222 -0
  180. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  181. package/dist/wallet/broker-escrows.js +36 -0
  182. package/dist/wallet/broker-escrows.js.map +1 -0
  183. package/dist/wallet/lifecycle.d.ts +21 -5
  184. package/dist/wallet/lifecycle.d.ts.map +1 -1
  185. package/dist/wallet/lifecycle.js +32 -12
  186. package/dist/wallet/lifecycle.js.map +1 -1
  187. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  188. package/dist/wallet/privy-broker-server.js +31 -26
  189. package/dist/wallet/privy-broker-server.js.map +1 -1
  190. package/dist/wallet/privy-session-broker.d.ts +14 -5
  191. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  192. package/dist/wallet/privy-session-broker.js +29 -17
  193. package/dist/wallet/privy-session-broker.js.map +1 -1
  194. package/dist/wallet/privy-session-provider.d.ts +3 -1
  195. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  196. package/dist/wallet/privy-session-provider.js +5 -3
  197. package/dist/wallet/privy-session-provider.js.map +1 -1
  198. package/dist/wallet/provider.d.ts.map +1 -1
  199. package/dist/wallet/provider.js +15 -8
  200. package/dist/wallet/provider.js.map +1 -1
  201. package/dist/x402/facilitator.d.ts +42 -17
  202. package/dist/x402/facilitator.d.ts.map +1 -1
  203. package/dist/x402/facilitator.js +49 -33
  204. package/dist/x402/facilitator.js.map +1 -1
  205. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  206. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  207. package/dist/x402-buyer/guarded-fetch.js +147 -0
  208. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  209. package/dist/x402-buyer/index.d.ts +10 -0
  210. package/dist/x402-buyer/index.d.ts.map +1 -0
  211. package/dist/x402-buyer/index.js +10 -0
  212. package/dist/x402-buyer/index.js.map +1 -0
  213. package/dist/x402-buyer/pay.d.ts +135 -0
  214. package/dist/x402-buyer/pay.d.ts.map +1 -0
  215. package/dist/x402-buyer/pay.js +336 -0
  216. package/dist/x402-buyer/pay.js.map +1 -0
  217. package/dist/x402-opener/abi.d.ts +684 -0
  218. package/dist/x402-opener/abi.d.ts.map +1 -0
  219. package/dist/x402-opener/abi.js +880 -0
  220. package/dist/x402-opener/abi.js.map +1 -0
  221. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  222. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  223. package/dist/x402-opener/facilitator-entry.js +37 -0
  224. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  225. package/dist/x402-opener/facilitator-server.d.ts +180 -0
  226. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  227. package/dist/x402-opener/facilitator-server.js +696 -0
  228. package/dist/x402-opener/facilitator-server.js.map +1 -0
  229. package/dist/x402-opener/index.d.ts +14 -0
  230. package/dist/x402-opener/index.d.ts.map +1 -0
  231. package/dist/x402-opener/index.js +14 -0
  232. package/dist/x402-opener/index.js.map +1 -0
  233. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  234. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  235. package/dist/x402-opener/listing-resolver.js +125 -0
  236. package/dist/x402-opener/listing-resolver.js.map +1 -0
  237. package/dist/x402-opener/refund-relay.d.ts +240 -0
  238. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  239. package/dist/x402-opener/refund-relay.js +1240 -0
  240. package/dist/x402-opener/refund-relay.js.map +1 -0
  241. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  242. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  243. package/dist/x402-opener/relayer-queue.js +18 -0
  244. package/dist/x402-opener/relayer-queue.js.map +1 -0
  245. package/dist/x402-opener/scheme.d.ts +217 -0
  246. package/dist/x402-opener/scheme.d.ts.map +1 -0
  247. package/dist/x402-opener/scheme.js +749 -0
  248. package/dist/x402-opener/scheme.js.map +1 -0
  249. package/dist/x402-opener/terms.d.ts +105 -0
  250. package/dist/x402-opener/terms.d.ts.map +1 -0
  251. package/dist/x402-opener/terms.js +212 -0
  252. package/dist/x402-opener/terms.js.map +1 -0
  253. package/dist/zktls/reclaim.d.ts +2 -2
  254. package/docs/agent-layer.md +13 -8
  255. package/docs/autonomous-privy-wallet.md +188 -0
  256. package/docs/cli.md +342 -53
  257. package/docs/langchain.md +5 -0
  258. package/docs/mcp-hosted.md +4 -3
  259. package/docs/mcp-reference.md +245 -0
  260. package/docs/probe-capability.md +191 -0
  261. package/docs/quickstart.md +54 -5
  262. package/docs/threat-model.md +24 -3
  263. package/docs/x402.md +681 -0
  264. package/package.json +19 -6
  265. package/src/capability/invocation-capability.ts +17 -1
  266. package/src/capability/probe-capability.ts +355 -0
  267. package/src/cli/args.ts +29 -0
  268. package/src/cli/commands/claim.ts +31 -5
  269. package/src/cli/commands/close.ts +56 -7
  270. package/src/cli/commands/invoke.ts +6 -2
  271. package/src/cli/commands/mcp.ts +18 -2
  272. package/src/cli/commands/open.ts +151 -14
  273. package/src/cli/commands/pay.ts +442 -0
  274. package/src/cli/commands/reclaim-unverified.ts +24 -6
  275. package/src/cli/commands/reclaim.ts +274 -19
  276. package/src/cli/commands/refund.ts +277 -0
  277. package/src/cli/commands/status.ts +17 -1
  278. package/src/cli/commands/streams.ts +11 -15
  279. package/src/cli/commands/watch.ts +7 -1
  280. package/src/cli/config.ts +139 -52
  281. package/src/cli/deps.ts +189 -25
  282. package/src/cli/errors.ts +24 -1
  283. package/src/cli/escape-hatch.ts +19 -5
  284. package/src/cli/listings.ts +43 -10
  285. package/src/cli/preflight.ts +36 -21
  286. package/src/cli/privy.ts +398 -0
  287. package/src/cli/reclaimability.ts +154 -0
  288. package/src/cli/run.ts +54 -11
  289. package/src/cli/scan-range.ts +2 -2
  290. package/src/cli/spend-policy.ts +139 -0
  291. package/src/cli/status.ts +14 -1
  292. package/src/cli/wallet.ts +189 -58
  293. package/src/discovery/rate-band.ts +172 -0
  294. package/src/escrow-generation.ts +135 -0
  295. package/src/frameworks/agentkit.ts +45 -11
  296. package/src/frameworks/eliza.ts +27 -5
  297. package/src/frameworks/langchain.ts +14 -0
  298. package/src/gateway/caller-auth-gateway.ts +336 -13
  299. package/src/gateway/http-server.ts +690 -7
  300. package/src/gateway/probe-auth.ts +191 -0
  301. package/src/gateway/signed-rate-bounds.ts +164 -0
  302. package/src/gateway/x402-opener-mode.ts +974 -0
  303. package/src/index.ts +112 -14
  304. package/src/mandates/env.ts +3 -2
  305. package/src/mcp/server.ts +973 -44
  306. package/src/mcp/service-invoker.ts +285 -0
  307. package/src/mcp/x402-tools.ts +621 -0
  308. package/src/network.ts +302 -0
  309. package/src/sdk/invoke.ts +96 -17
  310. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  311. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  312. package/src/wallet/autonomous-wallet.ts +4 -2
  313. package/src/wallet/broker-escrows.ts +66 -0
  314. package/src/wallet/lifecycle.ts +53 -18
  315. package/src/wallet/privy-broker-server.ts +35 -25
  316. package/src/wallet/privy-session-broker.ts +58 -25
  317. package/src/wallet/privy-session-provider.ts +7 -3
  318. package/src/wallet/provider.ts +20 -15
  319. package/src/x402/facilitator.ts +85 -46
  320. package/src/x402-buyer/guarded-fetch.ts +213 -0
  321. package/src/x402-buyer/index.ts +9 -0
  322. package/src/x402-buyer/pay.ts +517 -0
  323. package/src/x402-opener/abi.ts +881 -0
  324. package/src/x402-opener/facilitator-entry.ts +45 -0
  325. package/src/x402-opener/facilitator-server.ts +1014 -0
  326. package/src/x402-opener/index.ts +13 -0
  327. package/src/x402-opener/listing-resolver.ts +202 -0
  328. package/src/x402-opener/refund-relay.ts +1743 -0
  329. package/src/x402-opener/relayer-queue.ts +18 -0
  330. package/src/x402-opener/scheme.ts +1133 -0
  331. package/src/x402-opener/terms.ts +300 -0
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Metrik-native HTTP `402` challenge flow.
3
+ *
4
+ * This is NOT an implementation of the x402 protocol. It uses its own scheme
5
+ * string (`streamproof-verified-stream`), its own wire format, and its own
6
+ * request header (`x-metrik-payment`). It does not interoperate with
7
+ * `x402-fetch`, `x402-axios`, `x402-express`, or any other x402 client or
8
+ * facilitator. See `docs/x402.md` for the full explanation of why Metrik's
9
+ * metered-stream settlement does not fit any shipped x402 scheme.
10
+ *
11
+ * The directory name is historical. The deprecated `X402*` / `x402*` alias
12
+ * exports that used to live here were removed with the x402 v1 integration
13
+ * (metrik-agent#115); only the canonical names below are exported.
14
+ */
1
15
  import type { StreamProofClientConfig } from "@absol-labs/sdk";
2
16
  import { z } from "zod";
3
17
 
@@ -16,7 +30,19 @@ import {
16
30
  type VerifiedConsumedHttpsResponseProof,
17
31
  } from "../zktls/reclaim.js";
18
32
 
19
- export const X402_SCHEME = "streamproof-verified-stream" as const;
33
+ /**
34
+ * Immutable wire constant for the Metrik-native 402 scheme. The value is
35
+ * historical (`streamproof-*` predates the Metrik rename) and MUST NOT change:
36
+ * the live gateway and deployed callers depend on it.
37
+ */
38
+ export const METRIK_STREAM_SCHEME = "streamproof-verified-stream" as const;
39
+
40
+ /**
41
+ * Request header carrying the Metrik verified-stream payload. Deliberately NOT
42
+ * `x-payment`: that header belongs to the x402 protocol, which this flow does
43
+ * not implement.
44
+ */
45
+ export const METRIK_PAYMENT_HEADER = "x-metrik-payment" as const;
20
46
 
21
47
  const addressSchema = z.string().regex(/^0x[0-9a-fA-F]{40}$/);
22
48
  const bytes32Schema = z.string().regex(/^0x[0-9a-fA-F]{64}$/);
@@ -35,36 +61,36 @@ export interface VerifiedStreamRequirements extends z.infer<
35
61
  typeof verifiedStreamRequirementsSchema
36
62
  > {}
37
63
 
38
- export const x402StreamChallengeSchema = z.object({
39
- scheme: z.literal(X402_SCHEME),
64
+ export const streamChallengeSchema = z.object({
65
+ scheme: z.literal(METRIK_STREAM_SCHEME),
40
66
  requirements: verifiedStreamRequirementsSchema,
41
67
  });
42
68
 
43
- export interface X402StreamChallenge extends z.infer<
44
- typeof x402StreamChallengeSchema
69
+ export interface StreamChallenge extends z.infer<
70
+ typeof streamChallengeSchema
45
71
  > {}
46
72
 
47
- export const x402OpenPayloadSchema = z.object({
48
- challenge: x402StreamChallengeSchema,
73
+ export const streamOpenPayloadSchema = z.object({
74
+ challenge: streamChallengeSchema,
49
75
  signedMandate: signedSpendMandateSchema,
50
76
  });
51
77
 
52
- export interface X402OpenPayload extends z.infer<
53
- typeof x402OpenPayloadSchema
78
+ export interface StreamOpenPayload extends z.infer<
79
+ typeof streamOpenPayloadSchema
54
80
  > {}
55
81
 
56
- export interface X402Facilitator {
82
+ export interface StreamFacilitator {
57
83
  /** Build the 402 challenge a server returns to prompt a verified stream. */
58
- challenge(req: VerifiedStreamRequirements): X402StreamChallenge;
84
+ challenge(req: VerifiedStreamRequirements): StreamChallenge;
59
85
  /** Verify + open the stream from an agent's signed payment payload. */
60
86
  open(payloadHeader: string): Promise<{ streamId: `0x${string}` }>;
61
87
  /** Prove the HTTPS response consumed after opening a verified stream. */
62
88
  proveConsumedHttpsResponse(
63
- input: X402ConsumedHttpsResponseProofInput,
89
+ input: ConsumedHttpsResponseProofInput,
64
90
  ): Promise<VerifiedConsumedHttpsResponseProof>;
65
91
  }
66
92
 
67
- export interface X402ConsumedHttpsResponseProofInput {
93
+ export interface ConsumedHttpsResponseProofInput {
68
94
  readonly signedMandate: SignedSpendMandate;
69
95
  readonly streamId?: `0x${string}`;
70
96
  readonly operator?: `0x${string}`;
@@ -109,9 +135,11 @@ function warnFacilitatorOnce(key: string, message: string): void {
109
135
  console.error(message);
110
136
  }
111
137
 
112
- export class X402PayloadError extends Error {
138
+ export class StreamPayloadError extends Error {
113
139
  constructor(message: string) {
114
140
  super(message);
141
+ // Unchanged on purpose: `error.name` is an observable value that existing
142
+ // consumers may match on. The class was renamed; its `name` was not.
115
143
  this.name = "X402PayloadError";
116
144
  }
117
145
  }
@@ -137,14 +165,15 @@ export class DeliveryProofUnavailableError extends Error {
137
165
  * facilitator denies the open in this case (fail-closed, buyer-favoring) rather
138
166
  * than proceeding on an unverified assumption of "0 spent / none revoked".
139
167
  */
140
- export class X402SpendControlUnavailableError extends Error {
168
+ export class SpendControlUnavailableError extends Error {
141
169
  constructor(message: string) {
142
170
  super(message);
171
+ // Unchanged on purpose: see StreamPayloadError above.
143
172
  this.name = "X402SpendControlUnavailableError";
144
173
  }
145
174
  }
146
175
 
147
- export class VerifiedStreamX402Facilitator implements X402Facilitator {
176
+ export class VerifiedStreamFacilitator implements StreamFacilitator {
148
177
  private readonly agentClient: VerifiedStreamAgentOpener;
149
178
  private readonly deliveryProofs: ConsumerDeliveryProofService | undefined;
150
179
  private readonly sdkConfig: StreamProofClientConfig;
@@ -181,26 +210,26 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
181
210
  // both resolvers are wired; warn once so the reason is visible at deploy time.
182
211
  if (options.resolveSpentSoFarUsdc === undefined) {
183
212
  warnFacilitatorOnce(
184
- "x402-spent",
185
- "[metrik] x402 facilitator: no resolveSpentSoFarUsdc wired — the mandate cumulative cap (maxTotalUsdc) cannot be verified, so open() will DENY every spend (fail-closed) until a resolver is wired.",
213
+ "metrik-stream-spent",
214
+ "[metrik] Metrik verified-stream facilitator: no resolveSpentSoFarUsdc wired — the mandate cumulative cap (maxTotalUsdc) cannot be verified, so open() will DENY every spend (fail-closed) until a resolver is wired.",
186
215
  );
187
216
  }
188
217
  if (options.resolveRevokedMandateIds === undefined) {
189
218
  warnFacilitatorOnce(
190
- "x402-revocation",
191
- "[metrik] x402 facilitator: no resolveRevokedMandateIds wired — revocation state cannot be verified, so open() will DENY every spend (fail-closed) until a resolver is wired.",
219
+ "metrik-stream-revocation",
220
+ "[metrik] Metrik verified-stream facilitator: no resolveRevokedMandateIds wired — revocation state cannot be verified, so open() will DENY every spend (fail-closed) until a resolver is wired.",
192
221
  );
193
222
  }
194
223
  }
195
224
 
196
- challenge(req: VerifiedStreamRequirements): X402StreamChallenge {
225
+ challenge(req: VerifiedStreamRequirements): StreamChallenge {
197
226
  const requirements = verifiedStreamRequirementsSchema.parse(req);
198
227
  assertSettlementTarget(requirements, this.sdkConfig);
199
- return { scheme: X402_SCHEME, requirements };
228
+ return { scheme: METRIK_STREAM_SCHEME, requirements };
200
229
  }
201
230
 
202
231
  async open(payloadHeader: string): Promise<{ streamId: `0x${string}` }> {
203
- const payload = parseX402PayloadHeader(payloadHeader);
232
+ const payload = parseStreamPayloadHeader(payloadHeader);
204
233
  assertSettlementTarget(payload.challenge.requirements, this.sdkConfig);
205
234
 
206
235
  // FAIL-CLOSED fund safety (B6). Both the cumulative-cap accounting and the
@@ -209,12 +238,12 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
209
238
  // returns a non-bigint/negative spend is treated as "state cannot be
210
239
  // verified" and DENIES the open — never a silent fail-open to 0/none.
211
240
  if (this.resolveSpentSoFarUsdc === undefined) {
212
- throw new X402SpendControlUnavailableError(
241
+ throw new SpendControlUnavailableError(
213
242
  "cannot open verified stream: no resolveSpentSoFarUsdc wired — the mandate cumulative cap (maxTotalUsdc) cannot be verified (fail-closed)",
214
243
  );
215
244
  }
216
245
  if (this.resolveRevokedMandateIds === undefined) {
217
- throw new X402SpendControlUnavailableError(
246
+ throw new SpendControlUnavailableError(
218
247
  "cannot open verified stream: no resolveRevokedMandateIds wired — mandate revocation state cannot be verified (fail-closed)",
219
248
  );
220
249
  }
@@ -223,14 +252,14 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
223
252
  try {
224
253
  spentSoFarUsdc = await this.resolveSpentSoFarUsdc(payload.signedMandate);
225
254
  } catch (error) {
226
- throw new X402SpendControlUnavailableError(
255
+ throw new SpendControlUnavailableError(
227
256
  `cannot verify mandate cumulative cap (maxTotalUsdc): ${
228
257
  error instanceof Error ? error.message : String(error)
229
258
  } (fail-closed)`,
230
259
  );
231
260
  }
232
261
  if (typeof spentSoFarUsdc !== "bigint" || spentSoFarUsdc < 0n) {
233
- throw new X402SpendControlUnavailableError(
262
+ throw new SpendControlUnavailableError(
234
263
  "cannot verify mandate cumulative cap (maxTotalUsdc): resolver returned an invalid spent-so-far value (fail-closed)",
235
264
  );
236
265
  }
@@ -241,7 +270,7 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
241
270
  payload.signedMandate,
242
271
  );
243
272
  } catch (error) {
244
- throw new X402SpendControlUnavailableError(
273
+ throw new SpendControlUnavailableError(
245
274
  `cannot verify mandate revocation state: ${
246
275
  error instanceof Error ? error.message : String(error)
247
276
  } (fail-closed)`,
@@ -254,7 +283,7 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
254
283
  Symbol.iterator
255
284
  ] !== "function"
256
285
  ) {
257
- throw new X402SpendControlUnavailableError(
286
+ throw new SpendControlUnavailableError(
258
287
  "cannot verify mandate revocation state: resolver returned a non-iterable value (fail-closed)",
259
288
  );
260
289
  }
@@ -275,11 +304,11 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
275
304
  }
276
305
 
277
306
  async proveConsumedHttpsResponse(
278
- input: X402ConsumedHttpsResponseProofInput,
307
+ input: ConsumedHttpsResponseProofInput,
279
308
  ): Promise<VerifiedConsumedHttpsResponseProof> {
280
309
  if (this.deliveryProofs === undefined) {
281
310
  throw new DeliveryProofUnavailableError(
282
- "zkTLS delivery proofs are not configured on this x402 facilitator",
311
+ "zkTLS delivery proofs are not configured on this Metrik verified-stream facilitator",
283
312
  );
284
313
  }
285
314
 
@@ -317,42 +346,52 @@ export class VerifiedStreamX402Facilitator implements X402Facilitator {
317
346
  }
318
347
  }
319
348
 
320
- export function encodeX402PayloadHeader(payload: X402OpenPayload): string {
321
- const parsed = x402OpenPayloadSchema.parse(payload);
322
- const token = Buffer.from(stringifyX402Payload(parsed)).toString("base64url");
323
- return `${X402_SCHEME} ${token}`;
349
+ export function encodeStreamPayloadHeader(payload: StreamOpenPayload): string {
350
+ const parsed = streamOpenPayloadSchema.parse(payload);
351
+ const token = Buffer.from(stringifyStreamPayload(parsed)).toString(
352
+ "base64url",
353
+ );
354
+ return `${METRIK_STREAM_SCHEME} ${token}`;
324
355
  }
325
356
 
326
- export function parseX402ChallengeJson(value: unknown): X402StreamChallenge {
327
- const parsed = x402StreamChallengeSchema.safeParse(reviveBigInts(value));
357
+ export function parseStreamChallengeJson(value: unknown): StreamChallenge {
358
+ const parsed = streamChallengeSchema.safeParse(reviveBigInts(value));
328
359
  if (!parsed.success) {
329
- throw new X402PayloadError(
360
+ throw new StreamPayloadError(
330
361
  parsed.error.issues.map((issue) => issue.message).join("; "),
331
362
  );
332
363
  }
333
364
  return parsed.data;
334
365
  }
335
366
 
336
- export function parseX402PayloadHeader(payloadHeader: string): X402OpenPayload {
367
+ export function parseStreamPayloadHeader(
368
+ payloadHeader: string,
369
+ ): StreamOpenPayload {
337
370
  const [scheme, token] = payloadHeader.split(" ", 2);
338
- if (scheme !== X402_SCHEME || token === undefined || token.trim() === "") {
339
- throw new X402PayloadError("invalid x402 verified-stream payload header");
371
+ if (
372
+ scheme !== METRIK_STREAM_SCHEME ||
373
+ token === undefined ||
374
+ token.trim() === ""
375
+ ) {
376
+ throw new StreamPayloadError(
377
+ "invalid Metrik verified-stream payload header",
378
+ );
340
379
  }
341
380
 
342
381
  let decoded: unknown;
343
382
  try {
344
383
  decoded = JSON.parse(Buffer.from(token, "base64url").toString("utf8"));
345
384
  } catch (error) {
346
- throw new X402PayloadError(
347
- `invalid x402 verified-stream payload encoding: ${
385
+ throw new StreamPayloadError(
386
+ `invalid Metrik verified-stream payload encoding: ${
348
387
  error instanceof Error ? error.message : String(error)
349
388
  }`,
350
389
  );
351
390
  }
352
391
 
353
- const parsed = x402OpenPayloadSchema.safeParse(reviveBigInts(decoded));
392
+ const parsed = streamOpenPayloadSchema.safeParse(reviveBigInts(decoded));
354
393
  if (!parsed.success) {
355
- throw new X402PayloadError(
394
+ throw new StreamPayloadError(
356
395
  parsed.error.issues.map((issue) => issue.message).join("; "),
357
396
  );
358
397
  }
@@ -381,7 +420,7 @@ function assertSettlementTarget(
381
420
  }
382
421
  }
383
422
 
384
- function stringifyX402Payload(payload: X402OpenPayload): string {
423
+ function stringifyStreamPayload(payload: StreamOpenPayload): string {
385
424
  return JSON.stringify(payload, (_key, value) =>
386
425
  typeof value === "bigint" ? `${value.toString()}n` : value,
387
426
  );
@@ -0,0 +1,213 @@
1
+ import { lookup as dnsLookup } from "node:dns/promises";
2
+ import { isIP } from "node:net";
3
+
4
+ /**
5
+ * Outbound-request guard for x402 surfaces where the URL comes from a model
6
+ * (MCP `x402_pay`, `refund`, `refund_status`): without it, those tools are an
7
+ * SSRF probe of whatever network the server runs on.
8
+ *
9
+ * Every request is refused unless:
10
+ * - the URL is `https:` (or `http:` only when private networks are explicitly
11
+ * allowed, i.e. local testing);
12
+ * - its host is on the allowlist, when one is configured
13
+ * (`METRIK_AGENT_X402_ALLOWED_HOSTS`; `*.example.com` matches subdomains);
14
+ * - its host — an IP literal, or EVERY address the name resolves to — is not
15
+ * loopback, private, link-local, unique-local, CGNAT, multicast, reserved or
16
+ * unspecified.
17
+ *
18
+ * Redirects are never followed (`redirect: "manual"`; a 3xx is a refusal), so
19
+ * a public host cannot bounce the request — or a payment signature — onto an
20
+ * internal one.
21
+ *
22
+ * Limitation, stated: the address is checked at lookup time and the platform
23
+ * `fetch` resolves the name again when it connects, so a DNS-rebinding host
24
+ * with a near-zero TTL can still race the check. Use the allowlist where that
25
+ * matters.
26
+ */
27
+
28
+ export type LookupFn = (
29
+ hostname: string,
30
+ ) => Promise<readonly { readonly address: string; readonly family: number }[]>;
31
+
32
+ export interface GuardedFetchOptions {
33
+ /** Exact hostnames, or `*.domain` for subdomains. Unset = any public host. */
34
+ readonly allowedHosts?: readonly string[];
35
+ /** Tests / local development only: permit loopback and private ranges. */
36
+ readonly allowPrivateNetworks?: boolean;
37
+ /** The fetch to wrap. Default `globalThis.fetch`. */
38
+ readonly fetch?: typeof fetch;
39
+ /** DNS resolution. Default `dns.lookup(host, { all: true })`. */
40
+ readonly lookup?: LookupFn;
41
+ }
42
+
43
+ /** A request the guard refused; nothing was sent. */
44
+ export class OutboundRequestRefusedError extends Error {
45
+ constructor(reason: string) {
46
+ super(`outbound request refused: ${reason}`);
47
+ this.name = "OutboundRequestRefusedError";
48
+ }
49
+ }
50
+
51
+ const defaultLookup: LookupFn = async (hostname) =>
52
+ await dnsLookup(hostname, { all: true, verbatim: true });
53
+
54
+ function ipv4ToInt(address: string): number {
55
+ return address.split(".").reduce((acc, part) => acc * 256 + Number(part), 0);
56
+ }
57
+
58
+ const BLOCKED_V4: readonly (readonly [string, number])[] = [
59
+ ["0.0.0.0", 8],
60
+ ["10.0.0.0", 8],
61
+ ["100.64.0.0", 10],
62
+ ["127.0.0.0", 8],
63
+ ["169.254.0.0", 16],
64
+ ["172.16.0.0", 12],
65
+ ["192.0.0.0", 24],
66
+ ["192.0.2.0", 24],
67
+ ["192.168.0.0", 16],
68
+ ["198.18.0.0", 15],
69
+ ["198.51.100.0", 24],
70
+ ["203.0.113.0", 24],
71
+ ["224.0.0.0", 4],
72
+ ["240.0.0.0", 4],
73
+ ];
74
+
75
+ function blockedV4(address: string): boolean {
76
+ const value = ipv4ToInt(address);
77
+ return BLOCKED_V4.some(([base, bits]) => {
78
+ const mask = bits === 0 ? 0 : (~0 << (32 - bits)) >>> 0;
79
+ return (value & mask) >>> 0 === (ipv4ToInt(base) & mask) >>> 0;
80
+ });
81
+ }
82
+
83
+ function blockedV6(raw: string): boolean {
84
+ const address = raw.toLowerCase().replace(/^\[|\]$/g, "");
85
+ if (address === "::" || address === "::1") return true;
86
+ // IPv4-mapped / -compatible: judge the embedded IPv4 address.
87
+ const mapped = /^(?:::ffff:|::)(\d+\.\d+\.\d+\.\d+)$/.exec(address);
88
+ if (mapped?.[1] !== undefined) return blockedV4(mapped[1]);
89
+ const hexMapped = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(address);
90
+ if (hexMapped?.[1] !== undefined && hexMapped[2] !== undefined) {
91
+ const hi = parseInt(hexMapped[1], 16);
92
+ const lo = parseInt(hexMapped[2], 16);
93
+ return blockedV4(`${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`);
94
+ }
95
+ const first = parseInt(address.split(":")[0] || "0", 16);
96
+ return (
97
+ (first & 0xfe00) === 0xfc00 || // fc00::/7 unique local
98
+ (first & 0xffc0) === 0xfe80 || // fe80::/10 link-local
99
+ (first & 0xffc0) === 0xfec0 || // fec0::/10 site-local (deprecated)
100
+ (first & 0xff00) === 0xff00 || // ff00::/8 multicast
101
+ address.startsWith("64:ff9b:") || // NAT64 (embeds IPv4)
102
+ address.startsWith("2001:db8:") // documentation
103
+ );
104
+ }
105
+
106
+ /** Whether an IP literal is in a range an outbound x402 request must not reach. */
107
+ export function isBlockedAddress(address: string): boolean {
108
+ const bare = address.replace(/^\[|\]$/g, "");
109
+ const family = isIP(bare);
110
+ if (family === 4) return blockedV4(bare);
111
+ if (family === 6) return blockedV6(bare);
112
+ return true; // not an IP: never "safe" by default
113
+ }
114
+
115
+ function hostAllowed(hostname: string, allowed: readonly string[]): boolean {
116
+ const host = hostname.toLowerCase();
117
+ return allowed.some((entry) => {
118
+ const rule = entry.trim().toLowerCase();
119
+ if (rule.startsWith("*.")) return host.endsWith(rule.slice(1));
120
+ return host === rule;
121
+ });
122
+ }
123
+
124
+ /** Validate one URL against the guard; throws {@link OutboundRequestRefusedError}. */
125
+ export async function assertOutboundUrlAllowed(
126
+ raw: string | URL,
127
+ options: GuardedFetchOptions = {},
128
+ ): Promise<URL> {
129
+ let url: URL;
130
+ try {
131
+ url = new URL(String(raw));
132
+ } catch {
133
+ throw new OutboundRequestRefusedError(`"${String(raw)}" is not a URL`);
134
+ }
135
+ const allowPrivate = options.allowPrivateNetworks === true;
136
+ if (
137
+ url.protocol !== "https:" &&
138
+ !(url.protocol === "http:" && allowPrivate)
139
+ ) {
140
+ throw new OutboundRequestRefusedError(
141
+ `${url.protocol} is not allowed (https only)`,
142
+ );
143
+ }
144
+ if (url.username !== "" || url.password !== "") {
145
+ throw new OutboundRequestRefusedError("credentials in the URL");
146
+ }
147
+ const hostname = url.hostname.replace(/^\[|\]$/g, "");
148
+ if (
149
+ options.allowedHosts !== undefined &&
150
+ options.allowedHosts.length > 0 &&
151
+ !hostAllowed(hostname, options.allowedHosts)
152
+ ) {
153
+ throw new OutboundRequestRefusedError(
154
+ `${hostname} is not in METRIK_AGENT_X402_ALLOWED_HOSTS`,
155
+ );
156
+ }
157
+ if (allowPrivate) return url;
158
+ if (isIP(hostname) !== 0) {
159
+ if (isBlockedAddress(hostname)) {
160
+ throw new OutboundRequestRefusedError(
161
+ `${hostname} is a loopback, private, link-local or reserved address`,
162
+ );
163
+ }
164
+ return url;
165
+ }
166
+ if (hostname === "localhost" || hostname.endsWith(".localhost")) {
167
+ throw new OutboundRequestRefusedError(`${hostname} is loopback`);
168
+ }
169
+ let addresses: readonly { readonly address: string }[];
170
+ try {
171
+ addresses = await (options.lookup ?? defaultLookup)(hostname);
172
+ } catch {
173
+ throw new OutboundRequestRefusedError(`${hostname} did not resolve`);
174
+ }
175
+ if (addresses.length === 0) {
176
+ throw new OutboundRequestRefusedError(`${hostname} did not resolve`);
177
+ }
178
+ const blocked = addresses.find((entry) => isBlockedAddress(entry.address));
179
+ if (blocked !== undefined) {
180
+ throw new OutboundRequestRefusedError(
181
+ `${hostname} resolves to ${blocked.address}, a loopback, private, link-local or reserved address`,
182
+ );
183
+ }
184
+ return url;
185
+ }
186
+
187
+ /**
188
+ * A `fetch` that applies {@link assertOutboundUrlAllowed} to every request and
189
+ * never follows a redirect.
190
+ */
191
+ export function createGuardedFetch(
192
+ options: GuardedFetchOptions = {},
193
+ ): typeof fetch {
194
+ const base = options.fetch ?? globalThis.fetch.bind(globalThis);
195
+ const guarded = async (
196
+ input: string | URL | Request,
197
+ init?: RequestInit,
198
+ ): Promise<Response> => {
199
+ const target = input instanceof Request ? input.url : input;
200
+ await assertOutboundUrlAllowed(target, options);
201
+ const response = await base(input, { ...init, redirect: "manual" });
202
+ if (
203
+ response.type === "opaqueredirect" ||
204
+ (response.status >= 300 && response.status < 400)
205
+ ) {
206
+ throw new OutboundRequestRefusedError(
207
+ `${new URL(String(target)).host} answered with a redirect (${response.status}), which is never followed`,
208
+ );
209
+ }
210
+ return response;
211
+ };
212
+ return guarded as typeof fetch;
213
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@absol-labs/agent/x402-buyer` — the BUYER side of Metrik x402: pay an x402
3
+ * v2 endpoint through `@absol-labs/sdk/x402`'s `payWithX402` with every SDK
4
+ * protection intact, gated by a spend mandate (`checkMandate()`) before
5
+ * anything is signed. Kept out of the root barrel so consumers that never use
6
+ * x402 do not load `@x402/*`. See docs/x402.md.
7
+ */
8
+ export * from "./pay.js";
9
+ export * from "./guarded-fetch.js";