@absol-labs/agent 0.11.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 (350) hide show
  1. package/README.md +42 -50
  2. package/dist/capability/invocation-capability.d.ts +2 -2
  3. package/dist/capability/probe-capability.d.ts +225 -0
  4. package/dist/capability/probe-capability.d.ts.map +1 -0
  5. package/dist/capability/probe-capability.js +248 -0
  6. package/dist/capability/probe-capability.js.map +1 -0
  7. package/dist/cli/args.d.ts +11 -0
  8. package/dist/cli/args.d.ts.map +1 -1
  9. package/dist/cli/args.js +23 -0
  10. package/dist/cli/args.js.map +1 -1
  11. package/dist/cli/commands/claim.js +1 -1
  12. package/dist/cli/commands/claim.js.map +1 -1
  13. package/dist/cli/commands/close.d.ts.map +1 -1
  14. package/dist/cli/commands/close.js +35 -3
  15. package/dist/cli/commands/close.js.map +1 -1
  16. package/dist/cli/commands/invoke.d.ts +2 -0
  17. package/dist/cli/commands/invoke.d.ts.map +1 -1
  18. package/dist/cli/commands/invoke.js +2 -2
  19. package/dist/cli/commands/invoke.js.map +1 -1
  20. package/dist/cli/commands/mcp.d.ts.map +1 -1
  21. package/dist/cli/commands/mcp.js +15 -2
  22. package/dist/cli/commands/mcp.js.map +1 -1
  23. package/dist/cli/commands/open.d.ts.map +1 -1
  24. package/dist/cli/commands/open.js +58 -4
  25. package/dist/cli/commands/open.js.map +1 -1
  26. package/dist/cli/commands/pay.d.ts +19 -0
  27. package/dist/cli/commands/pay.d.ts.map +1 -0
  28. package/dist/cli/commands/pay.js +355 -0
  29. package/dist/cli/commands/pay.js.map +1 -0
  30. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  31. package/dist/cli/commands/reclaim-unverified.js +3 -2
  32. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  33. package/dist/cli/commands/reclaim.d.ts +23 -2
  34. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  35. package/dist/cli/commands/reclaim.js +184 -16
  36. package/dist/cli/commands/reclaim.js.map +1 -1
  37. package/dist/cli/commands/refund.d.ts +13 -0
  38. package/dist/cli/commands/refund.d.ts.map +1 -0
  39. package/dist/cli/commands/refund.js +224 -0
  40. package/dist/cli/commands/refund.js.map +1 -0
  41. package/dist/cli/commands/status.d.ts.map +1 -1
  42. package/dist/cli/commands/status.js +10 -1
  43. package/dist/cli/commands/status.js.map +1 -1
  44. package/dist/cli/commands/streams.js +8 -12
  45. package/dist/cli/commands/streams.js.map +1 -1
  46. package/dist/cli/commands/watch.d.ts.map +1 -1
  47. package/dist/cli/commands/watch.js +2 -1
  48. package/dist/cli/commands/watch.js.map +1 -1
  49. package/dist/cli/config.d.ts +44 -26
  50. package/dist/cli/config.d.ts.map +1 -1
  51. package/dist/cli/config.js +88 -38
  52. package/dist/cli/config.js.map +1 -1
  53. package/dist/cli/deps.d.ts +33 -0
  54. package/dist/cli/deps.d.ts.map +1 -1
  55. package/dist/cli/deps.js +37 -1
  56. package/dist/cli/deps.js.map +1 -1
  57. package/dist/cli/errors.d.ts.map +1 -1
  58. package/dist/cli/errors.js +21 -1
  59. package/dist/cli/errors.js.map +1 -1
  60. package/dist/cli/escape-hatch.d.ts +6 -4
  61. package/dist/cli/escape-hatch.d.ts.map +1 -1
  62. package/dist/cli/escape-hatch.js +6 -2
  63. package/dist/cli/escape-hatch.js.map +1 -1
  64. package/dist/cli/listings.d.ts +18 -0
  65. package/dist/cli/listings.d.ts.map +1 -1
  66. package/dist/cli/listings.js +22 -12
  67. package/dist/cli/listings.js.map +1 -1
  68. package/dist/cli/preflight.d.ts +20 -14
  69. package/dist/cli/preflight.d.ts.map +1 -1
  70. package/dist/cli/preflight.js +34 -20
  71. package/dist/cli/preflight.js.map +1 -1
  72. package/dist/cli/privy.d.ts.map +1 -1
  73. package/dist/cli/privy.js +4 -2
  74. package/dist/cli/privy.js.map +1 -1
  75. package/dist/cli/reclaimability.d.ts +82 -0
  76. package/dist/cli/reclaimability.d.ts.map +1 -0
  77. package/dist/cli/reclaimability.js +80 -0
  78. package/dist/cli/reclaimability.js.map +1 -0
  79. package/dist/cli/run.d.ts +2 -1
  80. package/dist/cli/run.d.ts.map +1 -1
  81. package/dist/cli/run.js +40 -11
  82. package/dist/cli/run.js.map +1 -1
  83. package/dist/cli/scan-range.d.ts +2 -2
  84. package/dist/cli/scan-range.js +2 -2
  85. package/dist/cli/status.d.ts +3 -1
  86. package/dist/cli/status.d.ts.map +1 -1
  87. package/dist/cli/status.js +15 -3
  88. package/dist/cli/status.js.map +1 -1
  89. package/dist/cli/wallet.d.ts +1 -1
  90. package/dist/cli/wallet.js +2 -2
  91. package/dist/cli/wallet.js.map +1 -1
  92. package/dist/discovery/rate-band.d.ts +74 -0
  93. package/dist/discovery/rate-band.d.ts.map +1 -0
  94. package/dist/discovery/rate-band.js +114 -0
  95. package/dist/discovery/rate-band.js.map +1 -0
  96. package/dist/escrow-generation.d.ts +81 -0
  97. package/dist/escrow-generation.d.ts.map +1 -0
  98. package/dist/escrow-generation.js +76 -0
  99. package/dist/escrow-generation.js.map +1 -0
  100. package/dist/frameworks/agentkit.d.ts +8 -4
  101. package/dist/frameworks/agentkit.d.ts.map +1 -1
  102. package/dist/frameworks/agentkit.js +27 -10
  103. package/dist/frameworks/agentkit.js.map +1 -1
  104. package/dist/frameworks/eliza.d.ts +3 -3
  105. package/dist/frameworks/eliza.d.ts.map +1 -1
  106. package/dist/frameworks/eliza.js +15 -6
  107. package/dist/frameworks/eliza.js.map +1 -1
  108. package/dist/frameworks/langchain.d.ts.map +1 -1
  109. package/dist/frameworks/langchain.js +8 -0
  110. package/dist/frameworks/langchain.js.map +1 -1
  111. package/dist/gateway/caller-auth-gateway.d.ts +83 -31
  112. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  113. package/dist/gateway/caller-auth-gateway.js +180 -27
  114. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  115. package/dist/gateway/http-server.d.ts +111 -44
  116. package/dist/gateway/http-server.d.ts.map +1 -1
  117. package/dist/gateway/http-server.js +439 -155
  118. package/dist/gateway/http-server.js.map +1 -1
  119. package/dist/gateway/probe-auth.d.ts +112 -0
  120. package/dist/gateway/probe-auth.d.ts.map +1 -0
  121. package/dist/gateway/probe-auth.js +127 -0
  122. package/dist/gateway/probe-auth.js.map +1 -0
  123. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  124. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  125. package/dist/gateway/signed-rate-bounds.js +94 -0
  126. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  127. package/dist/gateway/x402-opener-mode.d.ts +215 -0
  128. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  129. package/dist/gateway/x402-opener-mode.js +639 -0
  130. package/dist/gateway/x402-opener-mode.js.map +1 -0
  131. package/dist/index.d.ts +6 -2
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +10 -5
  134. package/dist/index.js.map +1 -1
  135. package/dist/mcp/server.d.ts +52 -1
  136. package/dist/mcp/server.d.ts.map +1 -1
  137. package/dist/mcp/server.js +228 -37
  138. package/dist/mcp/server.js.map +1 -1
  139. package/dist/mcp/x402-tools.d.ts +104 -0
  140. package/dist/mcp/x402-tools.d.ts.map +1 -0
  141. package/dist/mcp/x402-tools.js +434 -0
  142. package/dist/mcp/x402-tools.js.map +1 -0
  143. package/dist/network.d.ts +119 -0
  144. package/dist/network.d.ts.map +1 -0
  145. package/dist/network.js +199 -0
  146. package/dist/network.js.map +1 -0
  147. package/dist/sdk/invoke.d.ts +4 -0
  148. package/dist/sdk/invoke.d.ts.map +1 -1
  149. package/dist/sdk/invoke.js +2 -5
  150. package/dist/sdk/invoke.js.map +1 -1
  151. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  152. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  153. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  154. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  155. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  156. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  157. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  158. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  159. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  160. package/dist/wallet/autonomous-wallet.js +3 -2
  161. package/dist/wallet/autonomous-wallet.js.map +1 -1
  162. package/dist/wallet/broker-escrows.d.ts +222 -0
  163. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  164. package/dist/wallet/broker-escrows.js +36 -0
  165. package/dist/wallet/broker-escrows.js.map +1 -0
  166. package/dist/wallet/lifecycle.d.ts +21 -5
  167. package/dist/wallet/lifecycle.d.ts.map +1 -1
  168. package/dist/wallet/lifecycle.js +32 -12
  169. package/dist/wallet/lifecycle.js.map +1 -1
  170. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  171. package/dist/wallet/privy-broker-server.js +31 -26
  172. package/dist/wallet/privy-broker-server.js.map +1 -1
  173. package/dist/wallet/privy-session-broker.d.ts +14 -5
  174. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  175. package/dist/wallet/privy-session-broker.js +29 -17
  176. package/dist/wallet/privy-session-broker.js.map +1 -1
  177. package/dist/wallet/privy-session-provider.d.ts +3 -1
  178. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  179. package/dist/wallet/privy-session-provider.js +5 -3
  180. package/dist/wallet/privy-session-provider.js.map +1 -1
  181. package/dist/wallet/provider.d.ts.map +1 -1
  182. package/dist/wallet/provider.js +15 -8
  183. package/dist/wallet/provider.js.map +1 -1
  184. package/dist/x402/facilitator.d.ts +3 -30
  185. package/dist/x402/facilitator.d.ts.map +1 -1
  186. package/dist/x402/facilitator.js +0 -27
  187. package/dist/x402/facilitator.js.map +1 -1
  188. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  189. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  190. package/dist/x402-buyer/guarded-fetch.js +147 -0
  191. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  192. package/dist/x402-buyer/index.d.ts +10 -0
  193. package/dist/x402-buyer/index.d.ts.map +1 -0
  194. package/dist/x402-buyer/index.js +10 -0
  195. package/dist/x402-buyer/index.js.map +1 -0
  196. package/dist/x402-buyer/pay.d.ts +135 -0
  197. package/dist/x402-buyer/pay.d.ts.map +1 -0
  198. package/dist/x402-buyer/pay.js +336 -0
  199. package/dist/x402-buyer/pay.js.map +1 -0
  200. package/dist/x402-opener/abi.d.ts +684 -0
  201. package/dist/x402-opener/abi.d.ts.map +1 -0
  202. package/dist/x402-opener/abi.js +880 -0
  203. package/dist/x402-opener/abi.js.map +1 -0
  204. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  205. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  206. package/dist/x402-opener/facilitator-entry.js +37 -0
  207. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  208. package/dist/x402-opener/facilitator-server.d.ts +180 -0
  209. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  210. package/dist/x402-opener/facilitator-server.js +696 -0
  211. package/dist/x402-opener/facilitator-server.js.map +1 -0
  212. package/dist/x402-opener/index.d.ts +14 -0
  213. package/dist/x402-opener/index.d.ts.map +1 -0
  214. package/dist/x402-opener/index.js +14 -0
  215. package/dist/x402-opener/index.js.map +1 -0
  216. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  217. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  218. package/dist/x402-opener/listing-resolver.js +125 -0
  219. package/dist/x402-opener/listing-resolver.js.map +1 -0
  220. package/dist/x402-opener/refund-relay.d.ts +240 -0
  221. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  222. package/dist/x402-opener/refund-relay.js +1240 -0
  223. package/dist/x402-opener/refund-relay.js.map +1 -0
  224. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  225. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  226. package/dist/x402-opener/relayer-queue.js +18 -0
  227. package/dist/x402-opener/relayer-queue.js.map +1 -0
  228. package/dist/x402-opener/scheme.d.ts +217 -0
  229. package/dist/x402-opener/scheme.d.ts.map +1 -0
  230. package/dist/x402-opener/scheme.js +749 -0
  231. package/dist/x402-opener/scheme.js.map +1 -0
  232. package/dist/x402-opener/terms.d.ts +105 -0
  233. package/dist/x402-opener/terms.d.ts.map +1 -0
  234. package/dist/x402-opener/terms.js +212 -0
  235. package/dist/x402-opener/terms.js.map +1 -0
  236. package/dist/zktls/reclaim.d.ts +2 -2
  237. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  238. package/docs/agent-layer.md +3 -2
  239. package/docs/autonomous-privy-wallet.md +188 -0
  240. package/docs/cli.md +184 -31
  241. package/docs/langchain.md +5 -0
  242. package/docs/mcp-reference.md +75 -5
  243. package/docs/probe-capability.md +191 -0
  244. package/docs/quickstart.md +36 -1
  245. package/docs/threat-model.md +20 -0
  246. package/docs/x402.md +628 -595
  247. package/package.json +19 -17
  248. package/src/capability/probe-capability.ts +355 -0
  249. package/src/cli/args.ts +29 -0
  250. package/src/cli/commands/claim.ts +1 -1
  251. package/src/cli/commands/close.ts +36 -3
  252. package/src/cli/commands/invoke.ts +5 -2
  253. package/src/cli/commands/mcp.ts +18 -2
  254. package/src/cli/commands/open.ts +75 -4
  255. package/src/cli/commands/pay.ts +442 -0
  256. package/src/cli/commands/reclaim-unverified.ts +8 -2
  257. package/src/cli/commands/reclaim.ts +252 -14
  258. package/src/cli/commands/refund.ts +277 -0
  259. package/src/cli/commands/status.ts +17 -1
  260. package/src/cli/commands/streams.ts +10 -14
  261. package/src/cli/commands/watch.ts +7 -1
  262. package/src/cli/config.ts +139 -52
  263. package/src/cli/deps.ts +74 -1
  264. package/src/cli/errors.ts +24 -1
  265. package/src/cli/escape-hatch.ts +19 -5
  266. package/src/cli/listings.ts +43 -10
  267. package/src/cli/preflight.ts +36 -21
  268. package/src/cli/privy.ts +4 -2
  269. package/src/cli/reclaimability.ts +154 -0
  270. package/src/cli/run.ts +54 -11
  271. package/src/cli/scan-range.ts +2 -2
  272. package/src/cli/status.ts +14 -1
  273. package/src/cli/wallet.ts +2 -2
  274. package/src/discovery/rate-band.ts +172 -0
  275. package/src/escrow-generation.ts +135 -0
  276. package/src/frameworks/agentkit.ts +45 -11
  277. package/src/frameworks/eliza.ts +27 -5
  278. package/src/frameworks/langchain.ts +14 -0
  279. package/src/gateway/caller-auth-gateway.ts +320 -68
  280. package/src/gateway/http-server.ts +629 -220
  281. package/src/gateway/probe-auth.ts +191 -0
  282. package/src/gateway/signed-rate-bounds.ts +164 -0
  283. package/src/gateway/x402-opener-mode.ts +974 -0
  284. package/src/index.ts +82 -19
  285. package/src/mcp/server.ts +319 -42
  286. package/src/mcp/x402-tools.ts +621 -0
  287. package/src/network.ts +302 -0
  288. package/src/sdk/invoke.ts +6 -5
  289. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  290. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  291. package/src/wallet/autonomous-wallet.ts +4 -2
  292. package/src/wallet/broker-escrows.ts +66 -0
  293. package/src/wallet/lifecycle.ts +53 -18
  294. package/src/wallet/privy-broker-server.ts +35 -25
  295. package/src/wallet/privy-session-broker.ts +58 -25
  296. package/src/wallet/privy-session-provider.ts +7 -3
  297. package/src/wallet/provider.ts +20 -15
  298. package/src/x402/facilitator.ts +3 -43
  299. package/src/x402-buyer/guarded-fetch.ts +213 -0
  300. package/src/x402-buyer/index.ts +9 -0
  301. package/src/x402-buyer/pay.ts +517 -0
  302. package/src/x402-opener/abi.ts +881 -0
  303. package/src/x402-opener/facilitator-entry.ts +45 -0
  304. package/src/x402-opener/facilitator-server.ts +1014 -0
  305. package/src/x402-opener/index.ts +13 -0
  306. package/src/x402-opener/listing-resolver.ts +202 -0
  307. package/src/x402-opener/refund-relay.ts +1743 -0
  308. package/src/x402-opener/relayer-queue.ts +18 -0
  309. package/src/x402-opener/scheme.ts +1133 -0
  310. package/src/x402-opener/terms.ts +300 -0
  311. package/dist/gateway/x402-mode.d.ts +0 -123
  312. package/dist/gateway/x402-mode.d.ts.map +0 -1
  313. package/dist/gateway/x402-mode.js +0 -212
  314. package/dist/gateway/x402-mode.js.map +0 -1
  315. package/dist/x402/delivery-oracle.d.ts +0 -108
  316. package/dist/x402/delivery-oracle.d.ts.map +0 -1
  317. package/dist/x402/delivery-oracle.js +0 -174
  318. package/dist/x402/delivery-oracle.js.map +0 -1
  319. package/dist/x402/facilitator-server-entry.d.ts +0 -3
  320. package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
  321. package/dist/x402/facilitator-server-entry.js +0 -27
  322. package/dist/x402/facilitator-server-entry.js.map +0 -1
  323. package/dist/x402/facilitator-server.d.ts +0 -73
  324. package/dist/x402/facilitator-server.d.ts.map +0 -1
  325. package/dist/x402/facilitator-server.js +0 -305
  326. package/dist/x402/facilitator-server.js.map +0 -1
  327. package/dist/x402/payment-requirements.d.ts +0 -106
  328. package/dist/x402/payment-requirements.d.ts.map +0 -1
  329. package/dist/x402/payment-requirements.js +0 -138
  330. package/dist/x402/payment-requirements.js.map +0 -1
  331. package/dist/x402/relayer.d.ts +0 -41
  332. package/dist/x402/relayer.d.ts.map +0 -1
  333. package/dist/x402/relayer.js +0 -64
  334. package/dist/x402/relayer.js.map +0 -1
  335. package/dist/x402/resource-index.d.ts +0 -75
  336. package/dist/x402/resource-index.d.ts.map +0 -1
  337. package/dist/x402/resource-index.js +0 -126
  338. package/dist/x402/resource-index.js.map +0 -1
  339. package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
  340. package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
  341. package/dist/x402/verified-delivery-facilitator.js +0 -423
  342. package/dist/x402/verified-delivery-facilitator.js.map +0 -1
  343. package/src/gateway/x402-mode.ts +0 -350
  344. package/src/x402/delivery-oracle.ts +0 -285
  345. package/src/x402/facilitator-server-entry.ts +0 -35
  346. package/src/x402/facilitator-server.ts +0 -445
  347. package/src/x402/payment-requirements.ts +0 -231
  348. package/src/x402/relayer.ts +0 -122
  349. package/src/x402/resource-index.ts +0 -228
  350. package/src/x402/verified-delivery-facilitator.ts +0 -744
@@ -131,3 +131,191 @@ capability typed-data requests only. It does not claim ERC-4337, EIP-1271,
131
131
  smart-account, or gas-sponsorship support. The broker deployment and its real
132
132
  Privy policy remain integration dependencies and must be health-checked before
133
133
  use.
134
+
135
+ ## Escrow cutover (V2 → V3): what the broker allows, and the Privy policy change it needs
136
+
137
+ The broker (both the agent-owned and the session paths) sends transactions to
138
+ exactly two kinds of escrow, read from the registries rather than hardcoded
139
+ (`src/wallet/broker-escrows.ts`):
140
+
141
+ | Target | Allowed calls |
142
+ | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
143
+ | **Current escrow** (the SDK network registry's escrow for the broker's network) | `openStream`, `close`, `reclaim`, `reclaimUnverified` |
144
+ | **Legacy escrow(s)** (`@absol-labs/shared` deployments with `status: "legacy"`, other than the current one) | `close`, `reclaim`, `reclaimUnverified` **only** — no `openStream` |
145
+ | **USDC** | `approve(spender, amount)` with `spender` = the **current** escrow only (never a legacy escrow), `amount` within the existing cap |
146
+
147
+ Everything else is unchanged: chain id, zero `value`, `from` = the wallet, the
148
+ mandate/spend-cap checks on `openStream`, `stream.buyer == wallet` on every exit
149
+ (read from the escrow the exit targets), and invocation-capability typed data
150
+ bound to the current escrow. V2 and V3 share the selectors of every allowed
151
+ method, so one decoder (the union of the V2/V3 ABIs) serves both.
152
+
153
+ While the SDK default is still `StreamEscrowV2` (`@absol-labs/sdk@0.10.0-rc.1`),
154
+ V2 is current and nothing is legacy: behaviour is identical to before. With
155
+ `0.10.0-rc.2` the default becomes `StreamEscrowV3` and V2 becomes exit-only, so
156
+ agents holding open V2 streams can still close and reclaim them.
157
+
158
+ ### Privy policy rules to apply at cutover
159
+
160
+ The broker's checks are a second line; the Privy policy is what Privy itself
161
+ enforces. At the V3 cutover, the policy (`PRIVY_POLICY_ID`) must change as
162
+ follows. Base Sepolia addresses (from `@absol-labs/shared` `escrowDeployments`
163
+ and the SDK registry):
164
+
165
+ - current escrow (V3): `0x0643ba482C6A7aF31a1f8225e7e7e79Cfc467b13`
166
+ - legacy escrow (V2): `0x0f09f36Ccc05A7c9882F438721C08De314dFd46C`
167
+ - USDC: `0x036CbD53842c5426634e7929541eC2318f3dCF7e`
168
+
169
+ Keep every existing numeric bound exactly as it is (`openStream`
170
+ `deposit <= 1000000`, `ratePerSecond <= 1000`, `maxDuration <= 3600`; USDC
171
+ `approve` `amount <= 1000000`; chain `eip155:84532`; `value == 0`).
172
+
173
+ 1. **Contract allowlist, escrow — `openStream`**: change the target from the V2
174
+ escrow to the **V3** escrow only. The legacy V2 escrow must NOT be allowed
175
+ `openStream`.
176
+ 2. **Contract allowlist, escrow — exits** (`close(bytes32)` `0x39c79e0c`,
177
+ `reclaim(bytes32,uint256,bytes32[])` `0x2c307c9c`,
178
+ `reclaimUnverified(bytes32)` `0x5fcdce22`): allow them on **both** the V3
179
+ escrow and the V2 escrow (`to` IN [V3, V2]).
180
+ 3. **USDC `approve`** (`0x095ea7b3`): change the allowed `spender` from the V2
181
+ escrow to the **V3** escrow only. No approval to V2.
182
+ 4. Remove any rule that allows the V2 escrow for a method other than the three
183
+ exits above.
184
+
185
+ The same, as Privy policy rules (ALLOW rules, `eth_sendTransaction`,
186
+ `chain_type: "ethereum"`; DENY is the default):
187
+
188
+ ```json
189
+ [
190
+ {
191
+ "name": "metrik-open-stream-current-escrow",
192
+ "method": "eth_sendTransaction",
193
+ "action": "ALLOW",
194
+ "conditions": [
195
+ {
196
+ "field_source": "ethereum_transaction",
197
+ "field": "chain_id",
198
+ "operator": "eq",
199
+ "value": "84532"
200
+ },
201
+ {
202
+ "field_source": "ethereum_transaction",
203
+ "field": "to",
204
+ "operator": "eq",
205
+ "value": "0x0643ba482C6A7aF31a1f8225e7e7e79Cfc467b13"
206
+ },
207
+ {
208
+ "field_source": "ethereum_transaction",
209
+ "field": "value",
210
+ "operator": "eq",
211
+ "value": "0"
212
+ },
213
+ {
214
+ "field_source": "ethereum_calldata",
215
+ "field": "openStream.deposit",
216
+ "operator": "lte",
217
+ "value": "1000000",
218
+ "abi": "<StreamEscrowV3 openStream ABI>"
219
+ },
220
+ {
221
+ "field_source": "ethereum_calldata",
222
+ "field": "openStream.ratePerSecond",
223
+ "operator": "lte",
224
+ "value": "1000",
225
+ "abi": "<StreamEscrowV3 openStream ABI>"
226
+ },
227
+ {
228
+ "field_source": "ethereum_calldata",
229
+ "field": "openStream.maxDuration",
230
+ "operator": "lte",
231
+ "value": "3600",
232
+ "abi": "<StreamEscrowV3 openStream ABI>"
233
+ }
234
+ ]
235
+ },
236
+ {
237
+ "name": "metrik-exits-current-and-legacy-escrow",
238
+ "method": "eth_sendTransaction",
239
+ "action": "ALLOW",
240
+ "conditions": [
241
+ {
242
+ "field_source": "ethereum_transaction",
243
+ "field": "chain_id",
244
+ "operator": "eq",
245
+ "value": "84532"
246
+ },
247
+ {
248
+ "field_source": "ethereum_transaction",
249
+ "field": "to",
250
+ "operator": "in",
251
+ "value": [
252
+ "0x0643ba482C6A7aF31a1f8225e7e7e79Cfc467b13",
253
+ "0x0f09f36Ccc05A7c9882F438721C08De314dFd46C"
254
+ ]
255
+ },
256
+ {
257
+ "field_source": "ethereum_transaction",
258
+ "field": "value",
259
+ "operator": "eq",
260
+ "value": "0"
261
+ },
262
+ {
263
+ "field_source": "ethereum_calldata",
264
+ "field": "function_selector",
265
+ "operator": "in",
266
+ "value": ["0x39c79e0c", "0x2c307c9c", "0x5fcdce22"]
267
+ }
268
+ ]
269
+ },
270
+ {
271
+ "name": "metrik-usdc-approve-current-escrow",
272
+ "method": "eth_sendTransaction",
273
+ "action": "ALLOW",
274
+ "conditions": [
275
+ {
276
+ "field_source": "ethereum_transaction",
277
+ "field": "chain_id",
278
+ "operator": "eq",
279
+ "value": "84532"
280
+ },
281
+ {
282
+ "field_source": "ethereum_transaction",
283
+ "field": "to",
284
+ "operator": "eq",
285
+ "value": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
286
+ },
287
+ {
288
+ "field_source": "ethereum_transaction",
289
+ "field": "value",
290
+ "operator": "eq",
291
+ "value": "0"
292
+ },
293
+ {
294
+ "field_source": "ethereum_calldata",
295
+ "field": "approve.spender",
296
+ "operator": "eq",
297
+ "value": "0x0643ba482C6A7aF31a1f8225e7e7e79Cfc467b13",
298
+ "abi": "<ERC-20 approve ABI>"
299
+ },
300
+ {
301
+ "field_source": "ethereum_calldata",
302
+ "field": "approve.amount",
303
+ "operator": "lte",
304
+ "value": "1000000",
305
+ "abi": "<ERC-20 approve ABI>"
306
+ }
307
+ ]
308
+ }
309
+ ]
310
+ ```
311
+
312
+ Express the selector restriction in whatever form the live policy already uses
313
+ (a `function_selector` condition, or one calldata-field rule per exit method
314
+ with its ABI). What matters is the set: `openStream` → V3 only; the three exits
315
+ → V3 and V2; `approve.spender` → V3 only. The existing invocation-capability
316
+ typed-data rule must change its `verifyingContract` to the V3 escrow at the
317
+ same time (the broker already binds it to the current escrow).
318
+
319
+ Apply the policy change and the `@absol-labs/sdk@0.10.0-rc.2` bump together: the
320
+ broker refuses a V2 `openStream` as soon as the registry default is V3, and the
321
+ policy should too.
package/docs/cli.md CHANGED
@@ -46,7 +46,8 @@ consumer who installed the package could not run at all. `metrik mcp
46
46
  ## Testnet only
47
47
 
48
48
  The CLI signs with a raw private key and moves USDC, so the chain is an **allowlist**,
49
- not a default: only Base Sepolia (`84532`) is accepted. There is deliberately **no
49
+ not a default: only networks the `@absol-labs/sdk` network registry knows **and marks as
50
+ a testnet** are accepted (today: Base Sepolia, `84532`). There is deliberately **no
50
51
  override flag** — an escape hatch that re-enabled real funds would defeat the guarantee,
51
52
  and there is no mainnet deployment for it to reach anyway.
52
53
 
@@ -104,6 +105,17 @@ A Privy wallet differs from the other two in one structural way: its account has
104
105
  client's transport** instead of the plain `http(--rpc-url)` a local signer uses. That is
105
106
  the whole of the wiring. Private-key and CDP runs are unchanged, byte for byte.
106
107
 
108
+ **`metrik claim` does not work on a Privy wallet.** The broker's transaction allowlist
109
+ carries the BUYER actions only (`openStream`, `close`, `reclaim`, `reclaimUnverified`), so
110
+ an operator claim is refused server-side and the client cannot raise that ceiling. Claim
111
+ with the operator's own `METRIK_AGENT_PRIVATE_KEY`. Every other command works. The CLI
112
+ warns when you try it, and `metrik --help` / `metrik claim --help` say so up front.
113
+
114
+ **`metrik mcp` does not carry a Privy wallet either.** The MCP server resolves its wallet
115
+ with `parseAgentWalletEnv()`, which reads `METRIK_AGENT_PRIVATE_KEY` or the CDP variables
116
+ and has no Privy branch — so `metrik mcp --print-config` prints a key placeholder and says
117
+ why on stderr rather than emitting Privy variables the server would ignore.
118
+
107
119
  There are two shapes. Set one; setting both uses the agent-owned wallet and says so.
108
120
 
109
121
  **Agent-owned (`privy-autonomous`)** — headless, no browser, no user login:
@@ -245,19 +257,21 @@ that needs no key at all — it builds and prints the intended call and says whi
245
257
 
246
258
  ## Commands
247
259
 
248
- | Command | Needs a wallet? | Broadcasts a tx? | What it does |
249
- | -------------------- | ------------------------------ | ---------------- | ------------------------------------------------ |
250
- | `discover` | no | no | Lists verified registry listings |
251
- | `open` | **yes** (not with `--dry-run`) | **yes** | Escrows USDC into a metered stream |
252
- | `status` | no | no | One stream's on-chain state + verified accrual |
253
- | `streams` | no (with `--address`) | no | Lists a wallet's streams from the escrow's logs |
254
- | `watch` | no | no | Polls that until the stream closes |
255
- | `invoke` | **yes** (off-chain capability) | no | Calls the paid service, authorized by the stream |
256
- | `close` | **yes** | **yes** | Stops the clock |
257
- | `reclaim` | **yes** | **yes** | Takes back what was never earned |
258
- | `reclaim-unverified` | **yes** | **yes** | Buyer escape hatch: reclaim with no proof |
259
- | `claim` | **yes** (the operator's) | **yes** | Operator: redeem what verified |
260
- | `mcp` | no | no | Prints the MCP client config for `metrik-mcp` |
260
+ | Command | Needs a wallet? | Broadcasts a tx? | What it does |
261
+ | -------------------- | ------------------------------ | ----------------- | ------------------------------------------------ |
262
+ | `discover` | no | no | Lists verified registry listings |
263
+ | `open` | **yes** (not with `--dry-run`) | **yes** | Escrows USDC into a metered stream |
264
+ | `status` | no | no | One stream's on-chain state + verified accrual |
265
+ | `streams` | no (with `--address`) | no | Lists a wallet's streams from the escrow's logs |
266
+ | `watch` | no | no | Polls that until the stream closes |
267
+ | `invoke` | **yes** (off-chain capability) | no | Calls the paid service, authorized by the stream |
268
+ | `pay` | **yes** (EIP-3009 signature) | no (relayer does) | Pays an x402 endpoint into an escrow stream |
269
+ | `refund` | only for `--close` | no (relay does) | Gasless close/reclaim of an x402 stream |
270
+ | `close` | **yes** | **yes** | Stops the clock |
271
+ | `reclaim` | **yes** | **yes** | Takes back what was never earned |
272
+ | `reclaim-unverified` | **yes** | **yes** | Buyer escape hatch: reclaim with no proof |
273
+ | `claim` | **yes** (the operator's) | **yes** | Operator: redeem what verified |
274
+ | `mcp` | no | no | Prints the MCP client config for `metrik-mcp` |
261
275
 
262
276
  ### `metrik discover`
263
277
 
@@ -358,6 +372,21 @@ as `simulationPendingApproval: true`, with no `simulationError` — a script bra
358
372
  that field no longer sees the expected path as a failure. An allowance-shaped revert when
359
373
  the allowance IS already sufficient, and every other revert, are still surfaced as errors.
360
374
 
375
+ **The operator-signed rate band.** Before anything is signed or broadcast — `--dry-run`
376
+ included — `open` re-verifies the listing's operator-signed descriptor (the same check the
377
+ seller's gateway runs) and refuses at exit `2` when `--rate` is outside its
378
+ `minRatePerSecond`..`maxRatePerSecond` band, e.g. `the operator signed rates 100..10000
379
+ atomic/s (0.0001..0.01 USDC/s); --rate 20000 (0.02 USDC/s) would be refused by the
380
+ service's gateway`. The escrow accepts any non-zero rate, but the gateway answers every
381
+ request on an out-of-band stream with `403 stream-rate-out-of-bounds`, so the deposit
382
+ would sit in a stream that can never be served. Bounds are inclusive; a bound the operator
383
+ did not sign is not enforced, and a legacy binding signs none. A descriptor that does not
384
+ verify is a refusal (exit `4`), never "no band". Without `--operator`, an unreachable
385
+ registry already fails `open` (exit `4`). With `--operator` — the existing escape hatch for
386
+ a service the registry cannot vouch for — the band cannot be read, so the open proceeds
387
+ with a warning saying the rate was **not** checked. There is no flag to override a band
388
+ the CLI can read.
389
+
361
390
  **Amounts.** `--budget` and `--rate` are decimal USDC by default; `--units atomic`
362
391
  switches both to raw 6-decimal base units (what the escrow, the oracle and every
363
392
  `METRIK_E2E_*` fixture use). The unit is explicit rather than inferred from magnitude,
@@ -520,7 +549,10 @@ Configured mandate ceilings are carried through verbatim — they are policy, no
520
549
  and re-typing four caps from memory is where a wrong ceiling comes from. **Credentials are
521
550
  always placeholders.** This command never reads a key out of your environment and prints
522
551
  it back; the wallet block is simply shaped like the environment it ran in (the CDP
523
- variables if those are what you have set, `METRIK_AGENT_PRIVATE_KEY` otherwise).
552
+ variables if those are what you have set, `METRIK_AGENT_PRIVATE_KEY` otherwise). A Privy
553
+ wallet is **not** an option here — `parseAgentWalletEnv()` has no Privy branch — so when
554
+ the environment carries one the command says so on stderr and prints a key placeholder
555
+ instead of a config the server could not start from.
524
556
 
525
557
  ### `metrik invoke`
526
558
 
@@ -550,6 +582,96 @@ listings ("consumer-attested T2 invocation is not supported for gated listings")
550
582
  live listings are gated — so a `metrik prove` would be a command that fails for almost
551
583
  everyone who ran it. It is left out until that constraint lifts.
552
584
 
585
+ ### `metrik pay` (x402)
586
+
587
+ ```bash
588
+ metrik pay https://x402.example/transform --json '{"text":"# hi"}' --max-amount 0.1
589
+ metrik pay <url> [--json <body> | --data <body>] [--method POST] [--header "K: V"] \
590
+ --max-amount <usdc> [--max-rate <usdc/s>] [--max-duration <s>] [--min-duration <s>] \
591
+ [--operator <address>] [--units atomic]
592
+ ```
593
+
594
+ Pays any **x402 v2 `exact`** endpoint through `@absol-labs/sdk/x402`'s `payWithX402`,
595
+ with every SDK protection intact (network and USDC from the registry, EIP-3009 only,
596
+ registered opener factory, `payTo == factory.predict(terms)` read on-chain, bounded
597
+ authorization lifetime, a plain transfer to the seller refused). A Metrik seller's
598
+ payment opens an escrow stream with `deposit = price`; the seller earns
599
+ `ratePerSecond x verified seconds`, capped at the deposit.
600
+
601
+ The payer needs **USDC only, no gas** (the facilitator's relayer submits the
602
+ authorization). A Privy wallet cannot pay: its broker does not sign EIP-3009.
603
+
604
+ **How the spend mandate bounds an x402 payment.** No spend without a mandate: a
605
+ payment IS a stream open, so its opener terms are checked by `checkMandate()` exactly
606
+ like `metrik open`, inside the SDK's `onQuote` hook, after the SDK's own checks and
607
+ **before anything is signed**:
608
+
609
+ | Mandate cap | Checked against |
610
+ | ---------------------- | ---------------------------------------------- |
611
+ | `maxPerStreamUsdc` | the quote's `amount` (= the deposit) |
612
+ | `maxTotalUsdc` | spent so far + `amount` |
613
+ | `maxRatePerSecondUsdc` | `ratePerSecond` (how fast the seller can earn) |
614
+ | `maxDurationSeconds` | `maxDuration` (the stream window) |
615
+ | `allowedOperators` | `operator` (who the stream pays) |
616
+
617
+ The caps are also handed to the SDK as `maxAmount` / `maxRatePerSecond`, so the price is
618
+ enforced twice. A quote with no opener terms is refused. The mandate comes from
619
+ `METRIK_AGENT_MANDATE_*` when set, otherwise it is sized to this command:
620
+ `--max-amount` (required then) is the per-stream and total cap, `--max-rate` the rate cap
621
+ (default: no tighter than the price), `--max-duration` the window cap (default 1 day),
622
+ `--operator` the only allowed operator. With a standing mandate, every flag
623
+ **intersects** it (the most restrictive value wins; an `--operator` outside
624
+ `METRIK_AGENT_MANDATE_ALLOWED_OPERATORS` is refused): a flag can tighten the
625
+ standing ceiling, never loosen or bypass it. Like every CLI mandate it covers one
626
+ invocation.
627
+
628
+ If anything fails after the authorization was handed to the seller, `metrik pay`
629
+ establishes the outcome on-chain (USDC `authorizationState`, the opener's
630
+ `StreamOpenedFor` log for that nonce), exits `4`, and says whether the money left
631
+ the payer — with the `streamId` and the recovery command when the stream is found.
632
+ It never reports "nothing moved" unless the authorization was unused and expired.
633
+
634
+ A quote naming a **legacy** deployment's factory (for example a seller still on
635
+ StreamEscrowV2 after the default moved to V3) is refused by the SDK unless you pass
636
+ `--allow-legacy-factory`; that stream is then closed and reclaimed on the legacy
637
+ escrow, and the printed recovery command carries `--escrow <legacy escrow>`.
638
+
639
+ `--json <body>` is the request body (curl-style, sets `content-type: application/json`);
640
+ a bare `--json` still selects JSON output, and both can be combined. The output is the
641
+ response, the `streamId`, the opener, the deposit and rate, `minEarnSeconds` (after how
642
+ many verified seconds nothing is left to reclaim) and the **refund URL**.
643
+ **Refunds are not automatic** — see `metrik refund`.
644
+
645
+ Exit codes: `2` usage (including no ceiling), `3` refused by the mandate or the SDK
646
+ (nothing signed), `4` the settlement failed after signing (the error says whether the
647
+ authorization was used and how to cancel it; see [x402.md](./x402.md)).
648
+
649
+ ### `metrik refund`
650
+
651
+ ```bash
652
+ metrik refund <streamId> --refund-url <url> [--close] [--reclaim] [--attempts 5] [--max-wait 60]
653
+ ```
654
+
655
+ Drives the seller's **gasless refund relay** through the SDK's `refundClient`, bound to
656
+ the stream and to the opener read **from chain** (`factory.isOpener`), never from the
657
+ relay. With no action flag it prints the status and the next step. `--close` signs the
658
+ opener's EIP-712 `Close` as the payer (the relay pays the gas; authorized on the mandate
659
+ owner's signature only, like `metrik close`). `--reclaim` sends the permissionless
660
+ `opener.reclaim` (no wallet needed; the refund always goes to `payerOf(streamId)`) and
661
+ retries a bounded number of times while the settlement window is open, honouring
662
+ `Retry-After`; a longer wait exits `3` with the time to retry. Every relay "success" is
663
+ checked against the transaction receipt on the configured chain. `--refund-url` accepts
664
+ the full URL `metrik pay` printed, a `{streamId}` template or the relay's origin; it can
665
+ also come from `METRIK_AGENT_REFUND_URL`.
666
+
667
+ Nothing is refunded automatically, and the relay is a convenience: a payer can always
668
+ recover on-chain without it (`metrik close` as the payer, with gas; `metrik reclaim` by
669
+ anyone).
670
+
671
+ `status`, `close` and `reclaim` understand opener-bought streams: they show the opener and the
672
+ payer, `close` routes through the opener (payer only), and `reclaim` sends the
673
+ permissionless opener reclaim and reports the refund to the payer.
674
+
553
675
  ### `metrik close` and `metrik reclaim`
554
676
 
555
677
  ```bash
@@ -563,12 +685,32 @@ returns the unspent budget, and the buyer sends it. `close` now points at
563
685
  `--close-first` for next time, rather than leaving the one-shot form visible only in
564
686
  `metrik reclaim --help`.
565
687
 
688
+ **`reclaim` names the condition before it spends anything.** Three different on-chain
689
+ states stop a reclaim, and they have three completely different answers: the stream was
690
+ already reclaimed (you have the money), the stream is still running (close it first), or
691
+ its stop time is not checkpointed yet (wait ~20 seconds). The CLI reads `getStream`,
692
+ `latestCheckpointCheckedAt` and the chain clock up front and says which one it is, rather
693
+ than sending a simulation and repeating whatever the endpoint's revert data decoded to —
694
+ on the public Base Sepolia RPC all three came back as `Execution reverted for an unknown
695
+ reason`. The pre-flight **fails open**: a read that does not answer never blocks the
696
+ reclaim, because refusing to return a buyer's deposit over a dropped view call is worse
697
+ than a wasted simulation.
698
+
566
699
  **`reclaim` waits, by design.** `reclaimWithProof` reverts with `SettlementWindowOpen`
567
700
  until a checkpoint whose `checkedAt` is at or after the stream's stop time has posted.
568
701
  That is the normal ordering of a checkpoint rail, not a fault — in practice the first
569
- attempt fails and one roughly 30 seconds later succeeds. The CLI retries **that one
570
- condition** on a bounded schedule and narrates the wait on stderr; every other failure is
571
- surfaced immediately and unretried.
702
+ attempt fails and one roughly 30 seconds later succeeds. The CLI retries on a bounded
703
+ schedule and narrates the wait on stderr; every other failure is surfaced immediately and
704
+ unretried.
705
+
706
+ The wait shows up in three shapes, and all three retry: the escrow's decoded
707
+ `SettlementWindowOpen`; the **oracle** refusing a proof for a stream it has not
708
+ checkpointed yet (the SDK fetches the proof before it simulates, so the escrow's revert
709
+ never happens and the error is `StreamNotInCheckpointError` / `CheckpointNotReadyError`);
710
+ and a reclaim revert the ABI could not decode, where the chain's state is read to decide.
711
+ Only recognising the first of those is what made `--close-first` die on its first attempt
712
+ against a stream closed before its first covering checkpoint — the ordinary result of a
713
+ short trial run.
572
714
 
573
715
  If it is still blocked when the budget runs out, the message does **not** just say "retry
574
716
  later" — every attempt this command makes needs a proof from the oracle, so when the
@@ -584,8 +726,10 @@ lands on another that has not imported the block.) If the stream is **already**
584
726
  the close is skipped and the command goes straight to reclaim — so re-running it is always
585
727
  safe and never dead-ends on `StreamClosedAlready()`.
586
728
 
587
- `reclaim` on a stream that is still running reports that in words, with the command that
588
- fixes it, rather than surfacing the `StreamNotReclaimable()` selector.
729
+ `--close-first` is also the one caller allowed past the still-running refusal, since
730
+ clearing that state is the whole point of the flag. Once the close has landed it reports
731
+ what the following retries are waiting for — the stop time, and how far the oracle has
732
+ published — so the wait reads as the advertised wait rather than as a stalled command.
589
733
 
590
734
  Close and reclaim are buyer-recovery actions: `VerifiedStreamAgentClient` authorizes them
591
735
  on the mandate owner's signature alone, never on caps or expiry, so a lapsed mandate can
@@ -682,17 +826,26 @@ that wait legible.
682
826
 
683
827
  ## Settlement configuration
684
828
 
685
- Everything defaults to Metrik's live Base Sepolia deployment, and every value is
686
- overridable per command or through the environment:
687
-
688
- | Flag | Env | Default |
689
- | ---------------- | --------------------------- | ------------------------------------- |
690
- | `--chain-id` | `METRIK_AGENT_CHAIN_ID` | `84532` |
691
- | `--rpc-url` | `METRIK_AGENT_RPC_URL` | Base Sepolia public RPC |
692
- | `--escrow` | `METRIK_AGENT_ESCROW` | `BASE_SEPOLIA_ESCROW_V2` |
693
- | `--usdc` | `METRIK_AGENT_USDC` | `BASE_SEPOLIA_USDC` |
694
- | `--oracle-url` | `METRIK_AGENT_ORACLE_URL` | `https://oracle.metrik.live` |
695
- | `--registry-url` | `METRIK_AGENT_REGISTRY_URL` | the oracle's public listings endpoint |
829
+ Everything defaults to the selected network's entry in the `@absol-labs/sdk` network
830
+ registry (the default network is Base Sepolia), and every value is overridable per
831
+ command or through the environment. Nothing chain-specific is hardcoded in the CLI.
832
+
833
+ | Flag | Env | Default |
834
+ | --------------------- | -------------------------------- | ---------------------------------------------------------------------- |
835
+ | `--network` | `METRIK_NETWORK` | the registry default (id, chain id or CAIP-2) |
836
+ | `--chain-id` | `METRIK_AGENT_CHAIN_ID` | the network's chain id (must agree if both set) |
837
+ | `--rpc-url` | `METRIK_AGENT_RPC_URL` | the network's first registered RPC |
838
+ | `--escrow` | `METRIK_AGENT_ESCROW` | the network's current escrow (V3); a `legacy` one (V2) for its streams |
839
+ | `--escrow-generation` | `METRIK_AGENT_ESCROW_GENERATION` | the registry's; REQUIRED only for an unregistered escrow |
840
+ | `--usdc` | `METRIK_AGENT_USDC` | the network's registered USDC |
841
+ | `--oracle-url` | `METRIK_AGENT_ORACLE_URL` | the network's oracle |
842
+ | `--registry-url` | `METRIK_AGENT_REGISTRY_URL` | the network's public listings endpoint |
843
+
844
+ The gas preflight names the network's gas token and faucet (from its gas-token
845
+ metadata) instead of assuming ETH; on a network whose gas IS the settlement USDC it
846
+ checks the one USDC balance and requires the deposit to leave room for gas. An
847
+ `--escrow` the registry does not record has no log-scan anchor, so `metrik streams`
848
+ needs `--from-block` for it.
696
849
 
697
850
  ## Embedding it
698
851
 
package/docs/langchain.md CHANGED
@@ -12,6 +12,11 @@ Create them with [`createLangChainVerifiedStreamTools()`](../src/frameworks/lang
12
12
  ## Safety model
13
13
 
14
14
  - `hire_verified_service` checks the signed mandate before any stream open.
15
+ - It then refuses a `ratePerSecondUsdc` outside the listing's operator-signed rate
16
+ band (the seller's gateway would refuse every request on that stream), and fails
17
+ closed when the registry cannot be read. An unlisted `serviceRef` has no signed
18
+ band and is not constrained. The same check runs in the ElizaOS and AgentKit
19
+ adapters.
15
20
  - `reclaim_unspent` passes the same signed-mandate context into the agent client,
16
21
  which re-checks authorization before the SDK can write.
17
22
  - `check_stream_status` is read-only and also returns `shouldStop` for a closed
@@ -36,11 +36,16 @@ an economic question, not a cryptographic one.
36
36
  | `reclaim_unspent` | **yes** | Closes the stream (**stops the meter**) and returns unspent USDC to the buyer. |
37
37
  | `list_streams` | no | Lists the streams this server opened. |
38
38
  | `prove_https_response` | no | Buyer-side zkTLS proof of a response. Registered only with Reclaim credentials. |
39
+ | `x402_pay` | **yes** | Pays an x402 endpoint (opener path, docs/x402.md) under the spend mandate. |
40
+ | `refund_status` | no | Refund state of an x402 stream, from the seller's gasless refund relay. |
41
+ | `refund` | **yes** | Gasless close (payer-signed) and reclaim of an opener stream via the relay. |
39
42
 
40
43
  ### `discover_services`
41
44
 
42
45
  `category?`, `limit?`. Returns `serviceRef`, `operator`, `publicUrl`, `access`,
43
- `accessUrl` per listing.
46
+ `accessUrl` per listing, plus `ratePerSecondBand` (`minRatePerSecondUsdc` /
47
+ `maxRatePerSecondUsdc`, atomic units, `null` for an unsigned side) when the
48
+ operator's signed descriptor carries one.
44
49
 
45
50
  The registry is an **untrusted transport**: every `serviceRef` is re-derived
46
51
  from the signed record and the operator's signature recovered, so a compromised
@@ -80,6 +85,16 @@ warning on the result. It is operator-set input only — never a tool argument,
80
85
  because the caller being asked to relax a fund-safety check is the LLM the check
81
86
  exists to constrain — and it never overrides a positive contradiction.
82
87
 
88
+ `ratePerSecondUsdc` must sit inside the listing's operator-signed rate band
89
+ (inclusive). The descriptor is re-verified with the gateway's own check, and an
90
+ out-of-band rate is refused before any approval or open is sent: the escrow
91
+ accepts any non-zero rate, but the seller's gateway answers every request on
92
+ such a stream with `403 stream-rate-out-of-bounds`. A descriptor that does not
93
+ verify is a refusal. Under `METRIK_AGENT_ALLOW_UNVERIFIED_OPERATOR=true` an
94
+ unverifiable pairing has no listing to read a band from, and the result's
95
+ warning says the rate was not checked; the override never widens a band the
96
+ server can read.
97
+
83
98
  The result carries a `metering` block: the rate, the budget, the maximum this
84
99
  stream can ever spend, and what starts and stops the clock.
85
100
 
@@ -151,6 +166,56 @@ evidence of a delivery that did not happen here.
151
166
  This is an L2 signal to be cross-checked with oracle/L1 evidence — not a
152
167
  substitute for it.
153
168
 
169
+ ### `x402_pay`
170
+
171
+ Arguments: `url` (https; http only for localhost), optional `method`, `headers`,
172
+ `body`, `maxAmountUsdc`, `maxRatePerSecondUsdc` (base units; these can only TIGHTEN
173
+ the server mandate) and `minDurationSeconds`. Pays through `@absol-labs/sdk/x402`'s
174
+ `payWithX402` with every SDK protection intact, and is gated exactly like
175
+ `hire_verified_service`: the same server mandate, the same cumulative spend, the
176
+ same per-owner serialization. The payment's opener terms are checked by
177
+ `checkMandate()` before anything is signed — `amount` (the deposit) against the
178
+ per-stream and total caps, `ratePerSecond`, `maxDuration` and `operator` against
179
+ theirs — and the stream it opens is recorded, so it counts against `maxTotalUsdc`.
180
+ A caller-supplied `PAYMENT-SIGNATURE` / `X-PAYMENT` header is refused. The result
181
+ carries the response (bounded), `streamId`, `opener`, `refundUrl`, the deposit and
182
+ rate, and `refundIsAutomatic: false`.
183
+
184
+ ### `refund_status` / `refund`
185
+
186
+ Arguments: `streamId`, optional `refundUrl` (not needed for a stream this server
187
+ paid for — it was recorded); `refund` also takes `close` (stop the stream first;
188
+ this wallet signs the opener's `Close`, authorized on the mandate owner's signature
189
+ alone like `reclaim_unspent`) and `reclaim` (default `true`; the permissionless
190
+ reclaim, the refund always goes to the payer). Every relay success is verified
191
+ against the transaction receipt. A settlement window that is still open is reported
192
+ with when to retry. Nothing is refunded automatically.
193
+
194
+ The three x402 tools are **opt-in**: set `METRIK_AGENT_X402=1` to register them
195
+ (any other value, or none, leaves them off).
196
+
197
+ **Spend is reserved before anything is signed.** When the mandate approves a
198
+ quote, `x402_pay` records a reservation for its amount in the stream registry
199
+ — inside the same per-owner queue as `hire_verified_service` — before the SDK
200
+ signs the EIP-3009 authorization. If anything fails after the signature has
201
+ been handed out (a malformed or missing `PAYMENT-RESPONSE`, an unconfirmed
202
+ settlement, a dropped connection), the outcome is established from the chain,
203
+ never from the seller: USDC `authorizationState(from, nonce)` and the opener's
204
+ `StreamOpenedFor(streamId, payer, authorizationNonce, …)` log. The reservation
205
+ is released only if the authorization was never used AND has expired; a stream
206
+ found on-chain replaces it under its real `streamId`; otherwise it stays
207
+ counted, and the tool says the money may have moved — never "not settled".
208
+
209
+ **Outbound requests are guarded.** The URLs the payment tool, `refund` and
210
+ `refund_status` contact come from the model, so every request must be `https:`
211
+ to a host whose every resolved address is public (loopback, private,
212
+ link-local, unique-local, CGNAT, multicast and reserved ranges are refused),
213
+ redirects are never followed, and `METRIK_AGENT_X402_ALLOWED_HOSTS`
214
+ (comma-separated, exact or `*.domain`) restricts it further — list the seller
215
+ AND its refund relay. A response that did not ask for payment is not returned
216
+ (status only). The address is checked at lookup time; use the allowlist where
217
+ DNS rebinding matters.
218
+
154
219
  ## Configuration
155
220
 
156
221
  `metrik mcp --print-config` prints the `mcpServers` block to paste into an MCP
@@ -159,11 +224,15 @@ in it are placeholders by design.
159
224
 
160
225
  | Variable | Required | Meaning |
161
226
  | ---------------------------------------- | -------- | ----------------------------------------------------------------------------------------------- |
162
- | `METRIK_AGENT_RPC_URL` | **yes** | Base Sepolia RPC. |
163
- | `METRIK_AGENT_ESCROW` | **yes** | The **StreamEscrowV2** address. The retired V1 escrow is refused. |
227
+ | `METRIK_AGENT_RPC_URL` | **yes** | RPC of the selected network. |
228
+ | `METRIK_AGENT_ESCROW` | **yes** | The network's registered **StreamEscrowV2**. Anything else is refused (see below). |
164
229
  | `METRIK_AGENT_USDC` | **yes** | Settlement token. |
165
230
  | `METRIK_AGENT_ESCROW_GENERATION` | no | `v2` (default, and the only supported value). Declared, not probed. |
166
- | `METRIK_AGENT_CHAIN_ID` | no | Defaults to `84532`; nothing else is accepted. |
231
+ | `METRIK_NETWORK` | no | Registry id, chain id or CAIP-2 id; default the registry's default network. Testnets only. |
232
+ | `METRIK_AGENT_CHAIN_ID` | no | Alternative selector; must agree with `METRIK_NETWORK` when both are set. |
233
+ | `METRIK_AGENT_ALLOW_UNREGISTERED_ESCROW` | no | `true` accepts an escrow the registry does not record (a deliberate private deployment). |
234
+ | `METRIK_AGENT_X402` | no | `1` registers `x402_pay`, `refund_status`, `refund`. **Off by default.** |
235
+ | `METRIK_AGENT_X402_ALLOWED_HOSTS` | no | Comma-separated hosts (`*.domain` allowed) the x402 tools may contact. Unset = any public host. |
167
236
  | `METRIK_AGENT_REGISTRY_URL` | no | Registry the invocation origin is verified against. |
168
237
  | `METRIK_AGENT_ALLOW_UNVERIFIED_OPERATOR` | no | `true` downgrades an unverifiable operator pairing from a refusal to a warning. Off by default. |
169
238
  | `METRIK_AGENT_MCP_STATE_FILE` | no | Persists the stream registry across restarts. |
@@ -171,5 +240,6 @@ in it are placeholders by design.
171
240
  | wallet vars | **yes** | `METRIK_AGENT_PRIVATE_KEY`, or the CDP/Privy set. |
172
241
 
173
242
  The escrow generation is a **declared fact, not an inference**: the server
174
- refuses the retired V1 address by name, because a wrong-generation read does not
243
+ refuses any escrow that is not the network's registered `StreamEscrowV2` (the
244
+ retired V1 escrow included) unless `METRIK_AGENT_ALLOW_UNREGISTERED_ESCROW=true`, because a wrong-generation read does not
175
245
  revert — it returns confident nonsense about someone's money.