@absol-labs/agent 0.11.0 → 0.12.0-rc.2

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 (366) hide show
  1. package/README.md +43 -51
  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.d.ts +32 -0
  12. package/dist/cli/commands/claim.d.ts.map +1 -1
  13. package/dist/cli/commands/claim.js +116 -28
  14. package/dist/cli/commands/claim.js.map +1 -1
  15. package/dist/cli/commands/close.d.ts.map +1 -1
  16. package/dist/cli/commands/close.js +35 -3
  17. package/dist/cli/commands/close.js.map +1 -1
  18. package/dist/cli/commands/invoke.d.ts +2 -0
  19. package/dist/cli/commands/invoke.d.ts.map +1 -1
  20. package/dist/cli/commands/invoke.js +2 -2
  21. package/dist/cli/commands/invoke.js.map +1 -1
  22. package/dist/cli/commands/mcp.d.ts.map +1 -1
  23. package/dist/cli/commands/mcp.js +15 -2
  24. package/dist/cli/commands/mcp.js.map +1 -1
  25. package/dist/cli/commands/open.d.ts.map +1 -1
  26. package/dist/cli/commands/open.js +58 -4
  27. package/dist/cli/commands/open.js.map +1 -1
  28. package/dist/cli/commands/pay.d.ts +34 -0
  29. package/dist/cli/commands/pay.d.ts.map +1 -0
  30. package/dist/cli/commands/pay.js +479 -0
  31. package/dist/cli/commands/pay.js.map +1 -0
  32. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  33. package/dist/cli/commands/reclaim-unverified.js +3 -2
  34. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  35. package/dist/cli/commands/reclaim.d.ts +23 -2
  36. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  37. package/dist/cli/commands/reclaim.js +204 -20
  38. package/dist/cli/commands/reclaim.js.map +1 -1
  39. package/dist/cli/commands/refund.d.ts +31 -0
  40. package/dist/cli/commands/refund.d.ts.map +1 -0
  41. package/dist/cli/commands/refund.js +397 -0
  42. package/dist/cli/commands/refund.js.map +1 -0
  43. package/dist/cli/commands/status.d.ts.map +1 -1
  44. package/dist/cli/commands/status.js +21 -2
  45. package/dist/cli/commands/status.js.map +1 -1
  46. package/dist/cli/commands/streams.js +8 -12
  47. package/dist/cli/commands/streams.js.map +1 -1
  48. package/dist/cli/commands/watch.d.ts.map +1 -1
  49. package/dist/cli/commands/watch.js +4 -1
  50. package/dist/cli/commands/watch.js.map +1 -1
  51. package/dist/cli/config.d.ts +44 -26
  52. package/dist/cli/config.d.ts.map +1 -1
  53. package/dist/cli/config.js +88 -38
  54. package/dist/cli/config.js.map +1 -1
  55. package/dist/cli/context.d.ts +19 -0
  56. package/dist/cli/context.d.ts.map +1 -1
  57. package/dist/cli/deps.d.ts +53 -0
  58. package/dist/cli/deps.d.ts.map +1 -1
  59. package/dist/cli/deps.js +51 -1
  60. package/dist/cli/deps.js.map +1 -1
  61. package/dist/cli/errors.d.ts.map +1 -1
  62. package/dist/cli/errors.js +21 -1
  63. package/dist/cli/errors.js.map +1 -1
  64. package/dist/cli/escape-hatch.d.ts +6 -4
  65. package/dist/cli/escape-hatch.d.ts.map +1 -1
  66. package/dist/cli/escape-hatch.js +6 -2
  67. package/dist/cli/escape-hatch.js.map +1 -1
  68. package/dist/cli/listings.d.ts +18 -0
  69. package/dist/cli/listings.d.ts.map +1 -1
  70. package/dist/cli/listings.js +22 -12
  71. package/dist/cli/listings.js.map +1 -1
  72. package/dist/cli/preflight.d.ts +20 -14
  73. package/dist/cli/preflight.d.ts.map +1 -1
  74. package/dist/cli/preflight.js +34 -20
  75. package/dist/cli/preflight.js.map +1 -1
  76. package/dist/cli/privy.d.ts.map +1 -1
  77. package/dist/cli/privy.js +4 -2
  78. package/dist/cli/privy.js.map +1 -1
  79. package/dist/cli/progress.d.ts +13 -1
  80. package/dist/cli/progress.d.ts.map +1 -1
  81. package/dist/cli/progress.js +7 -1
  82. package/dist/cli/progress.js.map +1 -1
  83. package/dist/cli/reclaimability.d.ts +82 -0
  84. package/dist/cli/reclaimability.d.ts.map +1 -0
  85. package/dist/cli/reclaimability.js +80 -0
  86. package/dist/cli/reclaimability.js.map +1 -0
  87. package/dist/cli/run.d.ts +2 -1
  88. package/dist/cli/run.d.ts.map +1 -1
  89. package/dist/cli/run.js +62 -21
  90. package/dist/cli/run.js.map +1 -1
  91. package/dist/cli/scan-range.d.ts +2 -2
  92. package/dist/cli/scan-range.js +2 -2
  93. package/dist/cli/status.d.ts +106 -1
  94. package/dist/cli/status.d.ts.map +1 -1
  95. package/dist/cli/status.js +325 -44
  96. package/dist/cli/status.js.map +1 -1
  97. package/dist/cli/wallet.d.ts +2 -2
  98. package/dist/cli/wallet.d.ts.map +1 -1
  99. package/dist/cli/wallet.js +29 -5
  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 +83 -31
  121. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  122. package/dist/gateway/caller-auth-gateway.js +180 -27
  123. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  124. package/dist/gateway/http-server.d.ts +128 -44
  125. package/dist/gateway/http-server.d.ts.map +1 -1
  126. package/dist/gateway/http-server.js +506 -155
  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 +252 -0
  137. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  138. package/dist/gateway/x402-opener-mode.js +772 -0
  139. package/dist/gateway/x402-opener-mode.js.map +1 -0
  140. package/dist/index.d.ts +6 -2
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +10 -5
  143. package/dist/index.js.map +1 -1
  144. package/dist/mcp/server.d.ts +64 -1
  145. package/dist/mcp/server.d.ts.map +1 -1
  146. package/dist/mcp/server.js +247 -37
  147. package/dist/mcp/server.js.map +1 -1
  148. package/dist/mcp/x402-tools.d.ts +109 -0
  149. package/dist/mcp/x402-tools.d.ts.map +1 -0
  150. package/dist/mcp/x402-tools.js +554 -0
  151. package/dist/mcp/x402-tools.js.map +1 -0
  152. package/dist/network.d.ts +119 -0
  153. package/dist/network.d.ts.map +1 -0
  154. package/dist/network.js +199 -0
  155. package/dist/network.js.map +1 -0
  156. package/dist/sdk/client.d.ts +13 -0
  157. package/dist/sdk/client.d.ts.map +1 -1
  158. package/dist/sdk/client.js +15 -0
  159. package/dist/sdk/client.js.map +1 -1
  160. package/dist/sdk/invoke.d.ts +4 -0
  161. package/dist/sdk/invoke.d.ts.map +1 -1
  162. package/dist/sdk/invoke.js +2 -5
  163. package/dist/sdk/invoke.js.map +1 -1
  164. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  165. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  166. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  167. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  168. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  169. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  170. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  171. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  172. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  173. package/dist/wallet/autonomous-wallet.js +3 -2
  174. package/dist/wallet/autonomous-wallet.js.map +1 -1
  175. package/dist/wallet/broker-escrows.d.ts +222 -0
  176. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  177. package/dist/wallet/broker-escrows.js +36 -0
  178. package/dist/wallet/broker-escrows.js.map +1 -0
  179. package/dist/wallet/lifecycle.d.ts +21 -5
  180. package/dist/wallet/lifecycle.d.ts.map +1 -1
  181. package/dist/wallet/lifecycle.js +32 -12
  182. package/dist/wallet/lifecycle.js.map +1 -1
  183. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  184. package/dist/wallet/privy-broker-server.js +31 -26
  185. package/dist/wallet/privy-broker-server.js.map +1 -1
  186. package/dist/wallet/privy-session-broker.d.ts +14 -5
  187. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  188. package/dist/wallet/privy-session-broker.js +29 -17
  189. package/dist/wallet/privy-session-broker.js.map +1 -1
  190. package/dist/wallet/privy-session-provider.d.ts +3 -1
  191. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  192. package/dist/wallet/privy-session-provider.js +5 -3
  193. package/dist/wallet/privy-session-provider.js.map +1 -1
  194. package/dist/wallet/provider.d.ts.map +1 -1
  195. package/dist/wallet/provider.js +15 -8
  196. package/dist/wallet/provider.js.map +1 -1
  197. package/dist/x402/facilitator.d.ts +3 -30
  198. package/dist/x402/facilitator.d.ts.map +1 -1
  199. package/dist/x402/facilitator.js +0 -27
  200. package/dist/x402/facilitator.js.map +1 -1
  201. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  202. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  203. package/dist/x402-buyer/guarded-fetch.js +147 -0
  204. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  205. package/dist/x402-buyer/index.d.ts +10 -0
  206. package/dist/x402-buyer/index.d.ts.map +1 -0
  207. package/dist/x402-buyer/index.js +10 -0
  208. package/dist/x402-buyer/index.js.map +1 -0
  209. package/dist/x402-buyer/pay.d.ts +157 -0
  210. package/dist/x402-buyer/pay.d.ts.map +1 -0
  211. package/dist/x402-buyer/pay.js +366 -0
  212. package/dist/x402-buyer/pay.js.map +1 -0
  213. package/dist/x402-opener/abi.d.ts +684 -0
  214. package/dist/x402-opener/abi.d.ts.map +1 -0
  215. package/dist/x402-opener/abi.js +880 -0
  216. package/dist/x402-opener/abi.js.map +1 -0
  217. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  218. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  219. package/dist/x402-opener/facilitator-entry.js +40 -0
  220. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  221. package/dist/x402-opener/facilitator-server.d.ts +209 -0
  222. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  223. package/dist/x402-opener/facilitator-server.js +762 -0
  224. package/dist/x402-opener/facilitator-server.js.map +1 -0
  225. package/dist/x402-opener/index.d.ts +14 -0
  226. package/dist/x402-opener/index.d.ts.map +1 -0
  227. package/dist/x402-opener/index.js +14 -0
  228. package/dist/x402-opener/index.js.map +1 -0
  229. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  230. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  231. package/dist/x402-opener/listing-resolver.js +125 -0
  232. package/dist/x402-opener/listing-resolver.js.map +1 -0
  233. package/dist/x402-opener/refund-relay.d.ts +266 -0
  234. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  235. package/dist/x402-opener/refund-relay.js +1341 -0
  236. package/dist/x402-opener/refund-relay.js.map +1 -0
  237. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  238. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  239. package/dist/x402-opener/relayer-queue.js +18 -0
  240. package/dist/x402-opener/relayer-queue.js.map +1 -0
  241. package/dist/x402-opener/scheme.d.ts +296 -0
  242. package/dist/x402-opener/scheme.d.ts.map +1 -0
  243. package/dist/x402-opener/scheme.js +1035 -0
  244. package/dist/x402-opener/scheme.js.map +1 -0
  245. package/dist/x402-opener/terms.d.ts +165 -0
  246. package/dist/x402-opener/terms.d.ts.map +1 -0
  247. package/dist/x402-opener/terms.js +304 -0
  248. package/dist/x402-opener/terms.js.map +1 -0
  249. package/dist/zktls/reclaim.d.ts +2 -2
  250. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  251. package/docs/agent-layer.md +3 -2
  252. package/docs/autonomous-privy-wallet.md +188 -0
  253. package/docs/cli.md +265 -34
  254. package/docs/langchain.md +5 -0
  255. package/docs/mcp-reference.md +91 -5
  256. package/docs/probe-capability.md +191 -0
  257. package/docs/quickstart.md +39 -4
  258. package/docs/threat-model.md +20 -0
  259. package/docs/x402.md +766 -593
  260. package/package.json +19 -17
  261. package/src/capability/probe-capability.ts +355 -0
  262. package/src/cli/args.ts +29 -0
  263. package/src/cli/commands/claim.ts +168 -33
  264. package/src/cli/commands/close.ts +36 -3
  265. package/src/cli/commands/invoke.ts +5 -2
  266. package/src/cli/commands/mcp.ts +18 -2
  267. package/src/cli/commands/open.ts +75 -4
  268. package/src/cli/commands/pay.ts +584 -0
  269. package/src/cli/commands/reclaim-unverified.ts +8 -2
  270. package/src/cli/commands/reclaim.ts +277 -19
  271. package/src/cli/commands/refund.ts +475 -0
  272. package/src/cli/commands/status.ts +28 -2
  273. package/src/cli/commands/streams.ts +10 -14
  274. package/src/cli/commands/watch.ts +11 -1
  275. package/src/cli/config.ts +139 -52
  276. package/src/cli/context.ts +16 -0
  277. package/src/cli/deps.ts +111 -1
  278. package/src/cli/errors.ts +24 -1
  279. package/src/cli/escape-hatch.ts +19 -5
  280. package/src/cli/listings.ts +43 -10
  281. package/src/cli/preflight.ts +36 -21
  282. package/src/cli/privy.ts +4 -2
  283. package/src/cli/progress.ts +21 -2
  284. package/src/cli/reclaimability.ts +154 -0
  285. package/src/cli/run.ts +77 -21
  286. package/src/cli/scan-range.ts +2 -2
  287. package/src/cli/status.ts +454 -44
  288. package/src/cli/wallet.ts +45 -8
  289. package/src/discovery/rate-band.ts +172 -0
  290. package/src/escrow-generation.ts +135 -0
  291. package/src/frameworks/agentkit.ts +45 -11
  292. package/src/frameworks/eliza.ts +27 -5
  293. package/src/frameworks/langchain.ts +14 -0
  294. package/src/gateway/caller-auth-gateway.ts +320 -68
  295. package/src/gateway/http-server.ts +717 -220
  296. package/src/gateway/probe-auth.ts +191 -0
  297. package/src/gateway/signed-rate-bounds.ts +164 -0
  298. package/src/gateway/x402-opener-mode.ts +1171 -0
  299. package/src/index.ts +82 -19
  300. package/src/mcp/server.ts +351 -42
  301. package/src/mcp/x402-tools.ts +785 -0
  302. package/src/network.ts +302 -0
  303. package/src/sdk/client.ts +25 -0
  304. package/src/sdk/invoke.ts +6 -5
  305. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  306. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  307. package/src/wallet/autonomous-wallet.ts +4 -2
  308. package/src/wallet/broker-escrows.ts +66 -0
  309. package/src/wallet/lifecycle.ts +53 -18
  310. package/src/wallet/privy-broker-server.ts +35 -25
  311. package/src/wallet/privy-session-broker.ts +58 -25
  312. package/src/wallet/privy-session-provider.ts +7 -3
  313. package/src/wallet/provider.ts +20 -15
  314. package/src/x402/facilitator.ts +3 -43
  315. package/src/x402-buyer/guarded-fetch.ts +213 -0
  316. package/src/x402-buyer/index.ts +9 -0
  317. package/src/x402-buyer/pay.ts +555 -0
  318. package/src/x402-opener/abi.ts +881 -0
  319. package/src/x402-opener/facilitator-entry.ts +48 -0
  320. package/src/x402-opener/facilitator-server.ts +1114 -0
  321. package/src/x402-opener/index.ts +13 -0
  322. package/src/x402-opener/listing-resolver.ts +202 -0
  323. package/src/x402-opener/refund-relay.ts +1873 -0
  324. package/src/x402-opener/relayer-queue.ts +18 -0
  325. package/src/x402-opener/scheme.ts +1551 -0
  326. package/src/x402-opener/terms.ts +453 -0
  327. package/dist/gateway/x402-mode.d.ts +0 -123
  328. package/dist/gateway/x402-mode.d.ts.map +0 -1
  329. package/dist/gateway/x402-mode.js +0 -212
  330. package/dist/gateway/x402-mode.js.map +0 -1
  331. package/dist/x402/delivery-oracle.d.ts +0 -108
  332. package/dist/x402/delivery-oracle.d.ts.map +0 -1
  333. package/dist/x402/delivery-oracle.js +0 -174
  334. package/dist/x402/delivery-oracle.js.map +0 -1
  335. package/dist/x402/facilitator-server-entry.d.ts +0 -3
  336. package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
  337. package/dist/x402/facilitator-server-entry.js +0 -27
  338. package/dist/x402/facilitator-server-entry.js.map +0 -1
  339. package/dist/x402/facilitator-server.d.ts +0 -73
  340. package/dist/x402/facilitator-server.d.ts.map +0 -1
  341. package/dist/x402/facilitator-server.js +0 -305
  342. package/dist/x402/facilitator-server.js.map +0 -1
  343. package/dist/x402/payment-requirements.d.ts +0 -106
  344. package/dist/x402/payment-requirements.d.ts.map +0 -1
  345. package/dist/x402/payment-requirements.js +0 -138
  346. package/dist/x402/payment-requirements.js.map +0 -1
  347. package/dist/x402/relayer.d.ts +0 -41
  348. package/dist/x402/relayer.d.ts.map +0 -1
  349. package/dist/x402/relayer.js +0 -64
  350. package/dist/x402/relayer.js.map +0 -1
  351. package/dist/x402/resource-index.d.ts +0 -75
  352. package/dist/x402/resource-index.d.ts.map +0 -1
  353. package/dist/x402/resource-index.js +0 -126
  354. package/dist/x402/resource-index.js.map +0 -1
  355. package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
  356. package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
  357. package/dist/x402/verified-delivery-facilitator.js +0 -423
  358. package/dist/x402/verified-delivery-facilitator.js.map +0 -1
  359. package/src/gateway/x402-mode.ts +0 -350
  360. package/src/x402/delivery-oracle.ts +0 -285
  361. package/src/x402/facilitator-server-entry.ts +0 -35
  362. package/src/x402/facilitator-server.ts +0 -445
  363. package/src/x402/payment-requirements.ts +0 -231
  364. package/src/x402/relayer.ts +0 -122
  365. package/src/x402/resource-index.ts +0 -228
  366. package/src/x402/verified-delivery-facilitator.ts +0 -744
@@ -25,8 +25,9 @@ challenge/response and reuses the protocol's existing settlement path. (`src/x40
25
25
 
26
26
  It is **not the x402 protocol**: its scheme string, challenge body, payload and
27
27
  `X-METRIK-PAYMENT` header are all Metrik's own, and it does not interoperate with
28
- `x402-fetch`, `x402-axios`, or `x402-express`. See [docs/x402.md](./x402.md) for the
29
- evidence and for what real x402 support would require.
28
+ `x402-fetch`, `x402-axios`, or `x402-express`. Standard x402 v2 `exact` payments are
29
+ handled by a separate, opt-in path — the opener facilitator and the gateway's x402 v2
30
+ mode — described in [docs/x402.md](./x402.md).
30
31
 
31
32
  ### 2. MCP server
32
33
 
@@ -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
 
@@ -311,8 +325,8 @@ claim cannot be true. Every rendering of it is labelled unverified, in human out
311
325
  ### `metrik open`
312
326
 
313
327
  ```bash
314
- metrik open --service <serviceRef> --rate 0.000001 --budget 0.27 --duration 1h
315
- metrik open --service <serviceRef> --rate 1 --budget 270000 --duration 3600 --units atomic
328
+ metrik open --service <serviceRef> --rate 0.0001 --budget 0.36 --duration 1h
329
+ metrik open --service <serviceRef> --rate 100 --budget 360000 --duration 3600 --units atomic
316
330
  metrik open ... --dry-run
317
331
  ```
318
332
 
@@ -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,
@@ -430,6 +459,21 @@ escrow that grace is 900 seconds and immutable. An operator who has earned but n
430
459
  loses it at that moment, and the only defence is to `metrik claim` before it — which
431
460
  requires knowing the deadline exists.
432
461
 
462
+ **A stream with nothing left for anyone reads as finished.** Once a stream has stopped
463
+ (closed, or past expiry) and the operator's claimed amount — or on V3 the proven
464
+ high-water — already covers the whole deposit, both buyer exits would revert
465
+ `NothingToReclaim`, so `reclaimed` stays `false` forever. `status` renders it the way it
466
+ renders a reclaimed stream: `buyer side finished`, the operator's settled / claimed /
467
+ unclaimed amounts, and none of the buyer-reclaimable, escape-hatch or deadline rows. The
468
+ JSON sets `buyerSideFinished: true` and `finalSettlement.finishedBy: "deposit-exhausted"`
469
+ (`"reclaimed"` for a reclaimed stream). An unreadable proven high-water never makes a
470
+ stream look finished.
471
+
472
+ **Proven value is stated from chain.** On V3, when the latest checkpoint no longer contains
473
+ the stream but `provenCumulativeOf` is non-zero, `verified accrual` states that proven
474
+ amount (read from the escrow) instead of "not checkpointed yet", and `operator claimable`
475
+ is what `claimProven` would pay (`claimableSource: "proven-on-chain"` in the JSON).
476
+
433
477
  StreamEscrowV2 has exactly two states, `active` and `closed`. There is no paused state
434
478
  and the CLI never renders one.
435
479
 
@@ -520,7 +564,10 @@ Configured mandate ceilings are carried through verbatim — they are policy, no
520
564
  and re-typing four caps from memory is where a wrong ceiling comes from. **Credentials are
521
565
  always placeholders.** This command never reads a key out of your environment and prints
522
566
  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).
567
+ variables if those are what you have set, `METRIK_AGENT_PRIVATE_KEY` otherwise). A Privy
568
+ wallet is **not** an option here — `parseAgentWalletEnv()` has no Privy branch — so when
569
+ the environment carries one the command says so on stderr and prints a key placeholder
570
+ instead of a config the server could not start from.
524
571
 
525
572
  ### `metrik invoke`
526
573
 
@@ -550,6 +597,136 @@ listings ("consumer-attested T2 invocation is not supported for gated listings")
550
597
  live listings are gated — so a `metrik prove` would be a command that fails for almost
551
598
  everyone who ran it. It is left out until that constraint lifts.
552
599
 
600
+ ### `metrik pay` (x402)
601
+
602
+ ```bash
603
+ metrik pay https://x402.example/transform --json '{"text":"# hi"}' --max-amount 0.1
604
+ metrik pay <url> [--json <body> | --data <body>] [--method POST] [--header "K: V"] \
605
+ --max-amount <usdc> [--max-rate <usdc/s>] [--max-upfront <usdc>] [--max-duration <s>] \
606
+ [--min-duration <s>] [--operator <address>] [--units atomic]
607
+ ```
608
+
609
+ Pays any **x402 v2 `exact`** endpoint through `@absol-labs/sdk/x402`'s `payWithX402`,
610
+ with every SDK protection intact (network and USDC from the registry, EIP-3009 only,
611
+ registered opener factory, `payTo == factory.predict(terms)` read on-chain, bounded
612
+ authorization lifetime, a plain transfer to the seller refused). A Metrik seller's
613
+ payment opens an escrow stream with `deposit = price`; the seller earns
614
+ `ratePerSecond x verified seconds`, capped at the deposit.
615
+
616
+ **Upfront ("minimum charge") quotes.** A seller may instead quote the upfront opener:
617
+ the price splits into a **non-refundable** per-call charge (`upfrontAmount`, paid to the
618
+ seller at settlement whatever is delivered) and a **refundable**, metered part (`price -
619
+ upfrontAmount`, escrowed as above — the most any refund can return). `metrik pay` prints
620
+ the split before it signs (`quote: … = … NON-REFUNDABLE per-call charge + … metered and
621
+ refundable`) and again in the result (`non-refundable` / `refundable` rows; JSON
622
+ `openerKind`, `price`, `upfrontAmount`, `refundableAmount`, `upfrontFee`, `upfrontOnly`).
623
+ `--max-upfront <usdc>` caps the non-refundable part (the SDK's `maxUpfrontAmount`;
624
+ default 0.10 USDC, `0` refuses any upfront charge, never above the price ceiling). When
625
+ `upfrontAmount == price` no stream opens: the payment is **final**, `streamId` is
626
+ `null`, and no refund or reclaim command is printed, because there is nothing to
627
+ refund.
628
+
629
+ The payer needs **USDC only, no gas** (the facilitator's relayer submits the
630
+ authorization). A Privy wallet cannot pay: its broker does not sign EIP-3009.
631
+
632
+ **How the spend mandate bounds an x402 payment.** No spend without a mandate: a
633
+ payment IS a stream open, so its opener terms are checked by `checkMandate()` exactly
634
+ like `metrik open`, inside the SDK's `onQuote` hook, after the SDK's own checks and
635
+ **before anything is signed**:
636
+
637
+ | Mandate cap | Checked against |
638
+ | ---------------------- | ---------------------------------------------- |
639
+ | `maxPerStreamUsdc` | the quote's `amount` (the whole price) |
640
+ | `maxTotalUsdc` | spent so far + `amount` |
641
+ | `maxRatePerSecondUsdc` | `ratePerSecond` (how fast the seller can earn) |
642
+ | `maxDurationSeconds` | `maxDuration` (the stream window) |
643
+ | `allowedOperators` | `operator` (who the stream pays) |
644
+
645
+ The caps are also handed to the SDK as `maxAmount` / `maxRatePerSecond`, so the price is
646
+ enforced twice. A quote with no opener terms is refused. The mandate comes from
647
+ `METRIK_AGENT_MANDATE_*` when set, otherwise it is sized to this command:
648
+ `--max-amount` (required then) is the per-stream and total cap, `--max-rate` the rate cap
649
+ (default: no tighter than the price), `--max-duration` the window cap (default 1 day),
650
+ `--operator` the only allowed operator. With a standing mandate, every flag
651
+ **intersects** it (the most restrictive value wins; an `--operator` outside
652
+ `METRIK_AGENT_MANDATE_ALLOWED_OPERATORS` is refused): a flag can tighten the
653
+ standing ceiling, never loosen or bypass it. Like every CLI mandate it covers one
654
+ invocation.
655
+
656
+ If anything fails after the authorization was handed to the seller, `metrik pay`
657
+ establishes the outcome on-chain (USDC `authorizationState`, the opener's
658
+ `StreamOpenedFor` log for that nonce), exits `4`, and says whether the money left
659
+ the payer — with the `streamId` and the recovery command when the stream is found.
660
+ It never reports "nothing moved" unless the authorization was unused and expired.
661
+ For an upfront-only payment it says the money left the payer as a final, non-refundable
662
+ charge with nothing to recover.
663
+
664
+ A quote naming a **legacy** deployment's factory (for example a seller still on
665
+ StreamEscrowV2 after the default moved to V3) is refused by the SDK unless you pass
666
+ `--allow-legacy-factory`; that stream is then closed and reclaimed on the legacy
667
+ escrow, and the printed recovery command carries `--escrow <legacy escrow>`.
668
+
669
+ `--json <body>` is the request body (curl-style, sets `content-type: application/json`);
670
+ `--json-body <body>` is accepted as an alias (give the body once, with either). A bare
671
+ `--json` still selects JSON output, and both can be combined. The output is the
672
+ response, the `streamId`, the opener, the deposit and rate, `minEarnSeconds` (after how
673
+ many verified seconds nothing is left to reclaim) and the **refund URL**. When the
674
+ facilitator's `PAYMENT-RESPONSE` header (relayed by the seller) carries the settlement
675
+ transaction hash, it is printed as `settlement tx` and returned as
676
+ `settlementTransaction` with `settlementTransactionSource: "PAYMENT-RESPONSE"`: the
677
+ facilitator's relayer broadcast it, not the payer, and the CLI reports it as given.
678
+ **Refunds are not automatic** — see `metrik refund`.
679
+
680
+ Exit codes: `2` usage (including no ceiling), `3` refused by the mandate or the SDK
681
+ (nothing signed, including an upfront part above `--max-upfront`), `4` the settlement failed after signing (the error says whether the
682
+ authorization was used and how to cancel it; see [x402.md](./x402.md)).
683
+
684
+ ### `metrik refund`
685
+
686
+ ```bash
687
+ metrik refund <streamId> --refund-url <url> [--close] [--reclaim] [--attempts 5] [--max-wait 60]
688
+ ```
689
+
690
+ Drives the seller's **gasless refund relay** through the SDK's `refundClient`, bound to
691
+ the stream and to the opener read **from chain** (`factory.isOpener`), never from the
692
+ relay. With no action flag it prints the status and the next step. `--close` signs the
693
+ opener's EIP-712 `Close` as the payer (the relay pays the gas; authorized on the mandate
694
+ owner's signature only, like `metrik close`). `--reclaim` sends the permissionless
695
+ `opener.reclaim` (no wallet needed; the refund always goes to `payerOf(streamId)`) and
696
+ retries a bounded number of times while the settlement window is open, honouring
697
+ `Retry-After`; a longer wait exits `3` with the time to retry. Every relay "success" is
698
+ checked against the transaction receipt on the configured chain. `--refund-url` accepts
699
+ the full URL `metrik pay` printed, a `{streamId}` template or the relay's origin; it can
700
+ also come from `METRIK_AGENT_REFUND_URL`.
701
+
702
+ `--close` needs the key that paid, and nothing else: it signs an off-chain EIP-712
703
+ `Close` message, so the key needs no ETH or USDC. It is skipped (nothing signed, no
704
+ wallet read) when the relay reports the stream already closed.
705
+
706
+ **An upfront-only payment has nothing to refund.** `metrik refund 0x000…000` (stream id
707
+ 0, what an upfront-only payment amounts to) prints "Nothing to refund" and exits `0`
708
+ without reading the chain, contacting a relay or signing anything; the JSON carries
709
+ `finished: true`, `finishedReason: "upfront-only"`, `nothingToRefund: true`. For a
710
+ partial-upfront payment the stream is refunded as usual, but only its deposit (`price -
711
+ upfrontAmount`) can come back.
712
+
713
+ **A finished recovery exits `0`.** When the stream is already closed and reclaimed, or
714
+ the relay reports nothing to reclaim (`409 nothing-to-reclaim`: the verified delivery
715
+ covers the whole deposit), `refund` says "Already finished … nothing to recover" and
716
+ exits `0`; the JSON carries `finished: true`, `finishedReason` (`reclaimed` or
717
+ `nothing-to-reclaim`), `nothingToRecover: true`, and `closeSkipped` / `reclaimSkipped`
718
+ naming why a requested step sent nothing. It never suggests a gas-spending recovery when
719
+ nothing is recoverable. Real failures (relay down, settlement window still open, wrong
720
+ payer) still exit `3`.
721
+
722
+ Nothing is refunded automatically, and the relay is a convenience: a payer can always
723
+ recover on-chain without it (`metrik close` as the payer, with gas; `metrik reclaim` by
724
+ anyone).
725
+
726
+ `status`, `close` and `reclaim` understand opener-bought streams: they show the opener and the
727
+ payer, `close` routes through the opener (payer only), and `reclaim` sends the
728
+ permissionless opener reclaim and reports the refund to the payer.
729
+
553
730
  ### `metrik close` and `metrik reclaim`
554
731
 
555
732
  ```bash
@@ -563,12 +740,37 @@ returns the unspent budget, and the buyer sends it. `close` now points at
563
740
  `--close-first` for next time, rather than leaving the one-shot form visible only in
564
741
  `metrik reclaim --help`.
565
742
 
743
+ **`reclaim` names the condition before it spends anything.** Three different on-chain
744
+ states stop a reclaim, and they have three completely different answers: the stream was
745
+ already reclaimed (you have the money), the stream is still running (close it first), or
746
+ its stop time is not checkpointed yet (wait ~20 seconds). The CLI reads `getStream`,
747
+ `latestCheckpointCheckedAt` and the chain clock up front and says which one it is, rather
748
+ than sending a simulation and repeating whatever the endpoint's revert data decoded to —
749
+ on the public Base Sepolia RPC all three came back as `Execution reverted for an unknown
750
+ reason`. The pre-flight **fails open**: a read that does not answer never blocks the
751
+ reclaim, because refusing to return a buyer's deposit over a dropped view call is worse
752
+ than a wasted simulation.
753
+
754
+ **What `reclaim` prints for the operator.** The `operator entitled` row is the amount
755
+ the reclaim fixed as the operator's. No USDC moves to the operator at reclaim: the
756
+ operator claims it separately with `metrik claim`, and the protocol fee is taken at that
757
+ claim. `--json` keeps the field name `settledCumulative`.
758
+
566
759
  **`reclaim` waits, by design.** `reclaimWithProof` reverts with `SettlementWindowOpen`
567
760
  until a checkpoint whose `checkedAt` is at or after the stream's stop time has posted.
568
761
  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.
762
+ attempt fails and one roughly 30 seconds later succeeds. The CLI retries on a bounded
763
+ schedule and narrates the wait on stderr; every other failure is surfaced immediately and
764
+ unretried.
765
+
766
+ The wait shows up in three shapes, and all three retry: the escrow's decoded
767
+ `SettlementWindowOpen`; the **oracle** refusing a proof for a stream it has not
768
+ checkpointed yet (the SDK fetches the proof before it simulates, so the escrow's revert
769
+ never happens and the error is `StreamNotInCheckpointError` / `CheckpointNotReadyError`);
770
+ and a reclaim revert the ABI could not decode, where the chain's state is read to decide.
771
+ Only recognising the first of those is what made `--close-first` die on its first attempt
772
+ against a stream closed before its first covering checkpoint — the ordinary result of a
773
+ short trial run.
572
774
 
573
775
  If it is still blocked when the budget runs out, the message does **not** just say "retry
574
776
  later" — every attempt this command makes needs a proof from the oracle, so when the
@@ -584,8 +786,10 @@ lands on another that has not imported the block.) If the stream is **already**
584
786
  the close is skipped and the command goes straight to reclaim — so re-running it is always
585
787
  safe and never dead-ends on `StreamClosedAlready()`.
586
788
 
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.
789
+ `--close-first` is also the one caller allowed past the still-running refusal, since
790
+ clearing that state is the whole point of the flag. Once the close has landed it reports
791
+ what the following retries are waiting for — the stop time, and how far the oracle has
792
+ published — so the wait reads as the advertised wait rather than as a stalled command.
589
793
 
590
794
  Close and reclaim are buyer-recovery actions: `VerifiedStreamAgentClient` authorizes them
591
795
  on the mandate owner's signature alone, never on caps or expiry, so a lapsed mandate can
@@ -608,6 +812,21 @@ learn a fact that was already free. Same for a missing checkpoint: accrual lives
608
812
  oracle's proof, never in the stream tuple, so no proof means no claim is possible yet and
609
813
  the command says that instead of paying for `NothingToClaim()`.
610
814
 
815
+ **On StreamEscrowV3 it falls back to `claimProven` automatically.** The escrow keeps a
816
+ proven high-water (`provenCumulativeOf`) for every stream, and `claimProven` pays
817
+ `provenCumulative − claimedCumulative` (net of the fee) with no proof. That is the only
818
+ way to collect proven value whose checkpoint root has rotated out of the retained set (or
819
+ that a later root regressed below). `claim` asks the escrow what each path would pay
820
+ (`claimableFor` at the current proof's cumulative, and at the proven high-water), sends the
821
+ larger, and prefers `claimWithProof` on a tie because it also raises the high-water. When
822
+ both pay zero it exits `4` without broadcasting — "nothing to claim" when everything proven
823
+ is already claimed. The JSON names the path in `method` (`claimWithProof` / `claimProven`)
824
+ and adds `provenCumulative`; `verifiedCumulative` is `null` when there was no current proof.
825
+ There is no flag: `claimProven` pays only an amount already proven against a signed root
826
+ and recorded on-chain, to the same operator, under the same mandate check, so it can never
827
+ pay more than a proof claim could have — and an opt-in would leave a seller reading "nothing
828
+ provable" while their proven earnings sat stranded.
829
+
611
830
  ### `metrik reclaim-unverified`
612
831
 
613
832
  ```bash
@@ -672,7 +891,10 @@ wallet, mandate denied), `4` the operation failed.
672
891
  and the oracle fetch — an HTTP call the RPC transport does not cover — has its own 20s
673
892
  budget that names the endpoint when it expires. Any command still working after 8 seconds
674
893
  starts a heartbeat on stderr with the elapsed time, repeating every 10s, because with no
675
- output at all a slow endpoint and a wedged process are indistinguishable. The heartbeat is
894
+ output at all a slow endpoint and a wedged process are indistinguishable. While a command
895
+ is in a deliberate, announced wait — `reclaim` retrying until the oracle's next checkpoint
896
+ (`attempt n/N`), `refund` letting the relay wait out the settlement window, `watch`
897
+ polling — the heartbeat names that wait instead of blaming a slow RPC. The heartbeat is
676
898
  stderr in both modes, so `--json` stdout stays a single parseable document.
677
899
 
678
900
  Read paths time out; **broadcasts do not.** A transaction that has been sent may already be
@@ -682,17 +904,26 @@ that wait legible.
682
904
 
683
905
  ## Settlement configuration
684
906
 
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 |
907
+ Everything defaults to the selected network's entry in the `@absol-labs/sdk` network
908
+ registry (the default network is Base Sepolia), and every value is overridable per
909
+ command or through the environment. Nothing chain-specific is hardcoded in the CLI.
910
+
911
+ | Flag | Env | Default |
912
+ | --------------------- | -------------------------------- | ---------------------------------------------------------------------- |
913
+ | `--network` | `METRIK_NETWORK` | the registry default (id, chain id or CAIP-2) |
914
+ | `--chain-id` | `METRIK_AGENT_CHAIN_ID` | the network's chain id (must agree if both set) |
915
+ | `--rpc-url` | `METRIK_AGENT_RPC_URL` | the network's first registered RPC |
916
+ | `--escrow` | `METRIK_AGENT_ESCROW` | the network's current escrow (V3); a `legacy` one (V2) for its streams |
917
+ | `--escrow-generation` | `METRIK_AGENT_ESCROW_GENERATION` | the registry's; REQUIRED only for an unregistered escrow |
918
+ | `--usdc` | `METRIK_AGENT_USDC` | the network's registered USDC |
919
+ | `--oracle-url` | `METRIK_AGENT_ORACLE_URL` | the network's oracle |
920
+ | `--registry-url` | `METRIK_AGENT_REGISTRY_URL` | the network's public listings endpoint |
921
+
922
+ The gas preflight names the network's gas token and faucet (from its gas-token
923
+ metadata) instead of assuming ETH; on a network whose gas IS the settlement USDC it
924
+ checks the one USDC balance and requires the deposit to leave room for gas. An
925
+ `--escrow` the registry does not record has no log-scan anchor, so `metrik streams`
926
+ needs `--from-block` for it.
696
927
 
697
928
  ## Embedding it
698
929
 
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