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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/README.md +106 -30
  2. package/dist/capability/invocation-capability.d.ts +20 -8
  3. package/dist/capability/invocation-capability.d.ts.map +1 -1
  4. package/dist/capability/invocation-capability.js.map +1 -1
  5. package/dist/capability/probe-capability.d.ts +225 -0
  6. package/dist/capability/probe-capability.d.ts.map +1 -0
  7. package/dist/capability/probe-capability.js +248 -0
  8. package/dist/capability/probe-capability.js.map +1 -0
  9. package/dist/cli/args.d.ts +11 -0
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/args.js +23 -0
  12. package/dist/cli/args.js.map +1 -1
  13. package/dist/cli/commands/claim.d.ts.map +1 -1
  14. package/dist/cli/commands/claim.js +21 -5
  15. package/dist/cli/commands/claim.js.map +1 -1
  16. package/dist/cli/commands/close.d.ts.map +1 -1
  17. package/dist/cli/commands/close.js +51 -7
  18. package/dist/cli/commands/close.js.map +1 -1
  19. package/dist/cli/commands/invoke.d.ts +2 -0
  20. package/dist/cli/commands/invoke.d.ts.map +1 -1
  21. package/dist/cli/commands/invoke.js +3 -2
  22. package/dist/cli/commands/invoke.js.map +1 -1
  23. package/dist/cli/commands/mcp.d.ts.map +1 -1
  24. package/dist/cli/commands/mcp.js +15 -2
  25. package/dist/cli/commands/mcp.js.map +1 -1
  26. package/dist/cli/commands/open.d.ts.map +1 -1
  27. package/dist/cli/commands/open.js +121 -15
  28. package/dist/cli/commands/open.js.map +1 -1
  29. package/dist/cli/commands/pay.d.ts +19 -0
  30. package/dist/cli/commands/pay.d.ts.map +1 -0
  31. package/dist/cli/commands/pay.js +355 -0
  32. package/dist/cli/commands/pay.js.map +1 -0
  33. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  34. package/dist/cli/commands/reclaim-unverified.js +15 -6
  35. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  36. package/dist/cli/commands/reclaim.d.ts +23 -2
  37. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  38. package/dist/cli/commands/reclaim.js +200 -21
  39. package/dist/cli/commands/reclaim.js.map +1 -1
  40. package/dist/cli/commands/refund.d.ts +13 -0
  41. package/dist/cli/commands/refund.d.ts.map +1 -0
  42. package/dist/cli/commands/refund.js +224 -0
  43. package/dist/cli/commands/refund.js.map +1 -0
  44. package/dist/cli/commands/status.d.ts.map +1 -1
  45. package/dist/cli/commands/status.js +10 -1
  46. package/dist/cli/commands/status.js.map +1 -1
  47. package/dist/cli/commands/streams.js +9 -13
  48. package/dist/cli/commands/streams.js.map +1 -1
  49. package/dist/cli/commands/watch.d.ts.map +1 -1
  50. package/dist/cli/commands/watch.js +2 -1
  51. package/dist/cli/commands/watch.js.map +1 -1
  52. package/dist/cli/config.d.ts +44 -26
  53. package/dist/cli/config.d.ts.map +1 -1
  54. package/dist/cli/config.js +88 -38
  55. package/dist/cli/config.js.map +1 -1
  56. package/dist/cli/deps.d.ts +86 -7
  57. package/dist/cli/deps.d.ts.map +1 -1
  58. package/dist/cli/deps.js +102 -16
  59. package/dist/cli/deps.js.map +1 -1
  60. package/dist/cli/errors.d.ts.map +1 -1
  61. package/dist/cli/errors.js +21 -1
  62. package/dist/cli/errors.js.map +1 -1
  63. package/dist/cli/escape-hatch.d.ts +6 -4
  64. package/dist/cli/escape-hatch.d.ts.map +1 -1
  65. package/dist/cli/escape-hatch.js +6 -2
  66. package/dist/cli/escape-hatch.js.map +1 -1
  67. package/dist/cli/listings.d.ts +18 -0
  68. package/dist/cli/listings.d.ts.map +1 -1
  69. package/dist/cli/listings.js +22 -12
  70. package/dist/cli/listings.js.map +1 -1
  71. package/dist/cli/preflight.d.ts +20 -14
  72. package/dist/cli/preflight.d.ts.map +1 -1
  73. package/dist/cli/preflight.js +34 -20
  74. package/dist/cli/preflight.js.map +1 -1
  75. package/dist/cli/privy.d.ts +83 -0
  76. package/dist/cli/privy.d.ts.map +1 -0
  77. package/dist/cli/privy.js +232 -0
  78. package/dist/cli/privy.js.map +1 -0
  79. package/dist/cli/reclaimability.d.ts +82 -0
  80. package/dist/cli/reclaimability.d.ts.map +1 -0
  81. package/dist/cli/reclaimability.js +80 -0
  82. package/dist/cli/reclaimability.js.map +1 -0
  83. package/dist/cli/run.d.ts +2 -1
  84. package/dist/cli/run.d.ts.map +1 -1
  85. package/dist/cli/run.js +40 -11
  86. package/dist/cli/run.js.map +1 -1
  87. package/dist/cli/scan-range.d.ts +2 -2
  88. package/dist/cli/scan-range.js +2 -2
  89. package/dist/cli/spend-policy.d.ts +60 -0
  90. package/dist/cli/spend-policy.d.ts.map +1 -0
  91. package/dist/cli/spend-policy.js +65 -0
  92. package/dist/cli/spend-policy.js.map +1 -0
  93. package/dist/cli/status.d.ts +3 -1
  94. package/dist/cli/status.d.ts.map +1 -1
  95. package/dist/cli/status.js +15 -3
  96. package/dist/cli/status.js.map +1 -1
  97. package/dist/cli/wallet.d.ts +109 -40
  98. package/dist/cli/wallet.d.ts.map +1 -1
  99. package/dist/cli/wallet.js +109 -44
  100. package/dist/cli/wallet.js.map +1 -1
  101. package/dist/discovery/rate-band.d.ts +74 -0
  102. package/dist/discovery/rate-band.d.ts.map +1 -0
  103. package/dist/discovery/rate-band.js +114 -0
  104. package/dist/discovery/rate-band.js.map +1 -0
  105. package/dist/escrow-generation.d.ts +81 -0
  106. package/dist/escrow-generation.d.ts.map +1 -0
  107. package/dist/escrow-generation.js +76 -0
  108. package/dist/escrow-generation.js.map +1 -0
  109. package/dist/frameworks/agentkit.d.ts +8 -4
  110. package/dist/frameworks/agentkit.d.ts.map +1 -1
  111. package/dist/frameworks/agentkit.js +27 -10
  112. package/dist/frameworks/agentkit.js.map +1 -1
  113. package/dist/frameworks/eliza.d.ts +3 -3
  114. package/dist/frameworks/eliza.d.ts.map +1 -1
  115. package/dist/frameworks/eliza.js +15 -6
  116. package/dist/frameworks/eliza.js.map +1 -1
  117. package/dist/frameworks/langchain.d.ts.map +1 -1
  118. package/dist/frameworks/langchain.js +8 -0
  119. package/dist/frameworks/langchain.js.map +1 -1
  120. package/dist/gateway/caller-auth-gateway.d.ts +91 -4
  121. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  122. package/dist/gateway/caller-auth-gateway.js +184 -7
  123. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  124. package/dist/gateway/http-server.d.ts +121 -1
  125. package/dist/gateway/http-server.d.ts.map +1 -1
  126. package/dist/gateway/http-server.js +474 -5
  127. package/dist/gateway/http-server.js.map +1 -1
  128. package/dist/gateway/probe-auth.d.ts +112 -0
  129. package/dist/gateway/probe-auth.d.ts.map +1 -0
  130. package/dist/gateway/probe-auth.js +127 -0
  131. package/dist/gateway/probe-auth.js.map +1 -0
  132. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  133. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  134. package/dist/gateway/signed-rate-bounds.js +94 -0
  135. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  136. package/dist/gateway/x402-opener-mode.d.ts +215 -0
  137. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  138. package/dist/gateway/x402-opener-mode.js +639 -0
  139. package/dist/gateway/x402-opener-mode.js.map +1 -0
  140. package/dist/index.d.ts +11 -5
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +15 -5
  143. package/dist/index.js.map +1 -1
  144. package/dist/mandates/env.d.ts +3 -2
  145. package/dist/mandates/env.d.ts.map +1 -1
  146. package/dist/mandates/env.js +3 -2
  147. package/dist/mandates/env.js.map +1 -1
  148. package/dist/mcp/server.d.ts +100 -0
  149. package/dist/mcp/server.d.ts.map +1 -1
  150. package/dist/mcp/server.js +719 -39
  151. package/dist/mcp/server.js.map +1 -1
  152. package/dist/mcp/service-invoker.d.ts +97 -0
  153. package/dist/mcp/service-invoker.d.ts.map +1 -0
  154. package/dist/mcp/service-invoker.js +167 -0
  155. package/dist/mcp/service-invoker.js.map +1 -0
  156. package/dist/mcp/x402-tools.d.ts +104 -0
  157. package/dist/mcp/x402-tools.d.ts.map +1 -0
  158. package/dist/mcp/x402-tools.js +434 -0
  159. package/dist/mcp/x402-tools.js.map +1 -0
  160. package/dist/network.d.ts +119 -0
  161. package/dist/network.d.ts.map +1 -0
  162. package/dist/network.js +199 -0
  163. package/dist/network.js.map +1 -0
  164. package/dist/sdk/invoke.d.ts +23 -4
  165. package/dist/sdk/invoke.d.ts.map +1 -1
  166. package/dist/sdk/invoke.js +71 -10
  167. package/dist/sdk/invoke.js.map +1 -1
  168. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  169. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  170. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  171. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  172. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  173. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  174. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  175. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  176. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  177. package/dist/wallet/autonomous-wallet.js +3 -2
  178. package/dist/wallet/autonomous-wallet.js.map +1 -1
  179. package/dist/wallet/broker-escrows.d.ts +222 -0
  180. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  181. package/dist/wallet/broker-escrows.js +36 -0
  182. package/dist/wallet/broker-escrows.js.map +1 -0
  183. package/dist/wallet/lifecycle.d.ts +21 -5
  184. package/dist/wallet/lifecycle.d.ts.map +1 -1
  185. package/dist/wallet/lifecycle.js +32 -12
  186. package/dist/wallet/lifecycle.js.map +1 -1
  187. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  188. package/dist/wallet/privy-broker-server.js +31 -26
  189. package/dist/wallet/privy-broker-server.js.map +1 -1
  190. package/dist/wallet/privy-session-broker.d.ts +14 -5
  191. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  192. package/dist/wallet/privy-session-broker.js +29 -17
  193. package/dist/wallet/privy-session-broker.js.map +1 -1
  194. package/dist/wallet/privy-session-provider.d.ts +3 -1
  195. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  196. package/dist/wallet/privy-session-provider.js +5 -3
  197. package/dist/wallet/privy-session-provider.js.map +1 -1
  198. package/dist/wallet/provider.d.ts.map +1 -1
  199. package/dist/wallet/provider.js +15 -8
  200. package/dist/wallet/provider.js.map +1 -1
  201. package/dist/x402/facilitator.d.ts +42 -17
  202. package/dist/x402/facilitator.d.ts.map +1 -1
  203. package/dist/x402/facilitator.js +49 -33
  204. package/dist/x402/facilitator.js.map +1 -1
  205. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  206. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  207. package/dist/x402-buyer/guarded-fetch.js +147 -0
  208. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  209. package/dist/x402-buyer/index.d.ts +10 -0
  210. package/dist/x402-buyer/index.d.ts.map +1 -0
  211. package/dist/x402-buyer/index.js +10 -0
  212. package/dist/x402-buyer/index.js.map +1 -0
  213. package/dist/x402-buyer/pay.d.ts +135 -0
  214. package/dist/x402-buyer/pay.d.ts.map +1 -0
  215. package/dist/x402-buyer/pay.js +336 -0
  216. package/dist/x402-buyer/pay.js.map +1 -0
  217. package/dist/x402-opener/abi.d.ts +684 -0
  218. package/dist/x402-opener/abi.d.ts.map +1 -0
  219. package/dist/x402-opener/abi.js +880 -0
  220. package/dist/x402-opener/abi.js.map +1 -0
  221. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  222. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  223. package/dist/x402-opener/facilitator-entry.js +37 -0
  224. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  225. package/dist/x402-opener/facilitator-server.d.ts +180 -0
  226. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  227. package/dist/x402-opener/facilitator-server.js +696 -0
  228. package/dist/x402-opener/facilitator-server.js.map +1 -0
  229. package/dist/x402-opener/index.d.ts +14 -0
  230. package/dist/x402-opener/index.d.ts.map +1 -0
  231. package/dist/x402-opener/index.js +14 -0
  232. package/dist/x402-opener/index.js.map +1 -0
  233. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  234. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  235. package/dist/x402-opener/listing-resolver.js +125 -0
  236. package/dist/x402-opener/listing-resolver.js.map +1 -0
  237. package/dist/x402-opener/refund-relay.d.ts +240 -0
  238. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  239. package/dist/x402-opener/refund-relay.js +1240 -0
  240. package/dist/x402-opener/refund-relay.js.map +1 -0
  241. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  242. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  243. package/dist/x402-opener/relayer-queue.js +18 -0
  244. package/dist/x402-opener/relayer-queue.js.map +1 -0
  245. package/dist/x402-opener/scheme.d.ts +217 -0
  246. package/dist/x402-opener/scheme.d.ts.map +1 -0
  247. package/dist/x402-opener/scheme.js +749 -0
  248. package/dist/x402-opener/scheme.js.map +1 -0
  249. package/dist/x402-opener/terms.d.ts +105 -0
  250. package/dist/x402-opener/terms.d.ts.map +1 -0
  251. package/dist/x402-opener/terms.js +212 -0
  252. package/dist/x402-opener/terms.js.map +1 -0
  253. package/dist/zktls/reclaim.d.ts +2 -2
  254. package/docs/agent-layer.md +13 -8
  255. package/docs/autonomous-privy-wallet.md +188 -0
  256. package/docs/cli.md +342 -53
  257. package/docs/langchain.md +5 -0
  258. package/docs/mcp-hosted.md +4 -3
  259. package/docs/mcp-reference.md +245 -0
  260. package/docs/probe-capability.md +191 -0
  261. package/docs/quickstart.md +54 -5
  262. package/docs/threat-model.md +24 -3
  263. package/docs/x402.md +681 -0
  264. package/package.json +19 -6
  265. package/src/capability/invocation-capability.ts +17 -1
  266. package/src/capability/probe-capability.ts +355 -0
  267. package/src/cli/args.ts +29 -0
  268. package/src/cli/commands/claim.ts +31 -5
  269. package/src/cli/commands/close.ts +56 -7
  270. package/src/cli/commands/invoke.ts +6 -2
  271. package/src/cli/commands/mcp.ts +18 -2
  272. package/src/cli/commands/open.ts +151 -14
  273. package/src/cli/commands/pay.ts +442 -0
  274. package/src/cli/commands/reclaim-unverified.ts +24 -6
  275. package/src/cli/commands/reclaim.ts +274 -19
  276. package/src/cli/commands/refund.ts +277 -0
  277. package/src/cli/commands/status.ts +17 -1
  278. package/src/cli/commands/streams.ts +11 -15
  279. package/src/cli/commands/watch.ts +7 -1
  280. package/src/cli/config.ts +139 -52
  281. package/src/cli/deps.ts +189 -25
  282. package/src/cli/errors.ts +24 -1
  283. package/src/cli/escape-hatch.ts +19 -5
  284. package/src/cli/listings.ts +43 -10
  285. package/src/cli/preflight.ts +36 -21
  286. package/src/cli/privy.ts +398 -0
  287. package/src/cli/reclaimability.ts +154 -0
  288. package/src/cli/run.ts +54 -11
  289. package/src/cli/scan-range.ts +2 -2
  290. package/src/cli/spend-policy.ts +139 -0
  291. package/src/cli/status.ts +14 -1
  292. package/src/cli/wallet.ts +189 -58
  293. package/src/discovery/rate-band.ts +172 -0
  294. package/src/escrow-generation.ts +135 -0
  295. package/src/frameworks/agentkit.ts +45 -11
  296. package/src/frameworks/eliza.ts +27 -5
  297. package/src/frameworks/langchain.ts +14 -0
  298. package/src/gateway/caller-auth-gateway.ts +336 -13
  299. package/src/gateway/http-server.ts +690 -7
  300. package/src/gateway/probe-auth.ts +191 -0
  301. package/src/gateway/signed-rate-bounds.ts +164 -0
  302. package/src/gateway/x402-opener-mode.ts +974 -0
  303. package/src/index.ts +112 -14
  304. package/src/mandates/env.ts +3 -2
  305. package/src/mcp/server.ts +973 -44
  306. package/src/mcp/service-invoker.ts +285 -0
  307. package/src/mcp/x402-tools.ts +621 -0
  308. package/src/network.ts +302 -0
  309. package/src/sdk/invoke.ts +96 -17
  310. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  311. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  312. package/src/wallet/autonomous-wallet.ts +4 -2
  313. package/src/wallet/broker-escrows.ts +66 -0
  314. package/src/wallet/lifecycle.ts +53 -18
  315. package/src/wallet/privy-broker-server.ts +35 -25
  316. package/src/wallet/privy-session-broker.ts +58 -25
  317. package/src/wallet/privy-session-provider.ts +7 -3
  318. package/src/wallet/provider.ts +20 -15
  319. package/src/x402/facilitator.ts +85 -46
  320. package/src/x402-buyer/guarded-fetch.ts +213 -0
  321. package/src/x402-buyer/index.ts +9 -0
  322. package/src/x402-buyer/pay.ts +517 -0
  323. package/src/x402-opener/abi.ts +881 -0
  324. package/src/x402-opener/facilitator-entry.ts +45 -0
  325. package/src/x402-opener/facilitator-server.ts +1014 -0
  326. package/src/x402-opener/index.ts +13 -0
  327. package/src/x402-opener/listing-resolver.ts +202 -0
  328. package/src/x402-opener/refund-relay.ts +1743 -0
  329. package/src/x402-opener/relayer-queue.ts +18 -0
  330. package/src/x402-opener/scheme.ts +1133 -0
  331. package/src/x402-opener/terms.ts +300 -0
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
 
@@ -60,47 +61,194 @@ The rule is enforced twice:
60
61
 
61
62
  ## Wallet
62
63
 
63
- Two wallet sources, resolved in a **fixed order** — first match wins:
64
+ Three wallet sources, resolved in a **fixed order** — first match wins:
64
65
 
65
66
  | Order | Variables | Wallet |
66
67
  | ----- | ------------------------------------------------------------------------------------------ | --------------------------------------- |
67
68
  | 1 | `METRIK_AGENT_PRIVATE_KEY` | a raw Base Sepolia test key |
68
69
  | 2 | `CDP_API_KEY_ID`, `CDP_API_KEY_SECRET`, `CDP_WALLET_SECRET`, `METRIK_AGENT_CDP_OWNER_NAME` | a Coinbase CDP Server Wallet v2 account |
70
+ | 3 | `METRIK_AGENT_PRIVY_*` (below) | a Privy wallet reached through a broker |
69
71
 
70
- This is the same order `parseAgentWalletEnv()` uses, so one environment configures the
71
- CLI, the MCP server and the framework adapters identically. The order is **fixed**
72
- rather than "whichever is configured": an environment carrying both is ambiguous about
73
- which address funds the stream, and that ambiguity is a fund-moving one. When both are
74
- present the private key wins and the CLI says so on stderr rather than choosing quietly.
72
+ The first two are the same order `parseAgentWalletEnv()` uses, so one environment
73
+ configures the CLI, the MCP server and the framework adapters identically. The order is
74
+ **fixed** rather than "whichever is configured": an environment carrying two is ambiguous
75
+ about which address funds the stream, and that ambiguity is a fund-moving one. When more
76
+ than one is present the earlier entry wins and the CLI says so on stderr rather than
77
+ choosing quietly.
75
78
 
76
- Three misconfigurations are refused up front, at exit `3`, instead of failing somewhere
77
- deeper:
79
+ Misconfigurations are refused up front, at exit `3`, instead of failing somewhere deeper:
78
80
 
79
- - **A half-configured CDP wallet** ("you set three of the four variables") is reported as
80
- exactly that. Rendering it as "no wallet is configured" would send the reader looking
81
- in the wrong place.
81
+ - **A half-configured wallet** ("you set three of the four CDP variables", "you set the
82
+ Privy session token but not the address") is reported as exactly that, naming the
83
+ missing variables. Rendering it as "no wallet is configured" would send the reader
84
+ looking in the wrong place.
82
85
  - **A CDP _smart_ account** (`METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT` /
83
86
  `METRIK_AGENT_CDP_SMART_ACCOUNT_NAME`) is refused. It is an ERC-4337 account at a
84
87
  different address from its owner, reached through a bundler; this CLI broadcasts
85
88
  through `--rpc-url`, so it would sign and spend from the **owner** while you funded the
86
89
  smart account.
87
- - **A non-local signer** is refused with the reason, rather than accepted and failed
88
- later inside viem.
90
+ - **An account that cannot sign** is refused with the reason, rather than accepted and
91
+ failed later inside viem. The check is about capability, not locality: every wallet must
92
+ be able to sign EIP-712 typed data, and only the paths that broadcast through
93
+ `--rpc-url` additionally need a local signer.
89
94
 
90
95
  There is no `--private-key` flag, and there will not be one — nor a flag for any other
91
96
  credential: a secret on the command line lands in shell history, in `ps` output, and in
92
97
  any CI log that echoes the command. Commands print the derived **address**; the
93
98
  credential is never echoed, including by `metrik mcp --print-config`.
94
99
 
95
- **Privy is not wired into the CLI, and this is not an oversight.** Both Privy adapters in
96
- `src/wallet/` produce a JSON-RPC account with no `signTransaction`: they sign and
97
- broadcast by sending `eth_sendTransaction` / `eth_signTypedData_v4` to a provider, so
98
- they need the wallet client's **transport** to be that provider rather than a plain RPC.
99
- That is not three more environment variables — it changes the transport for every
100
- fund-moving command, and `metrik invoke` additionally requires a local signer
101
- (`InvokeOptions.buyer: LocalAccount`). CDP has neither problem (its account signs
102
- locally) and is the only wallet path in this package with live CI evidence, which is why
103
- it is the one that is wired.
100
+ ### Privy
101
+
102
+ A Privy wallet differs from the other two in one structural way: its account has no local
103
+ `signTransaction`. It signs and broadcasts by sending `eth_sendTransaction` /
104
+ `eth_signTypedData_v4` to a provider, so for a Privy wallet the **provider is the wallet
105
+ client's transport** instead of the plain `http(--rpc-url)` a local signer uses. That is
106
+ the whole of the wiring. Private-key and CDP runs are unchanged, byte for byte.
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
+
119
+ There are two shapes. Set one; setting both uses the agent-owned wallet and says so.
120
+
121
+ **Agent-owned (`privy-autonomous`)** — headless, no browser, no user login:
122
+
123
+ | Variable | Required | What it is |
124
+ | ------------------------------- | -------- | ------------------------------------------------------------------------ |
125
+ | `METRIK_AGENT_PRIVY_WALLET_KEY` | yes | the credential-store key of a wallet you already provisioned |
126
+ | `METRIK_AGENT_PRIVY_BROKER_URL` | no | overrides the broker the wallet was provisioned against |
127
+ | `METRIK_WALLET_ENCRYPTION_KEY` | no | selects the encrypted-file store (the durable option on a headless host) |
128
+
129
+ **The CLI never provisions a wallet for you.** A fund-moving command is the wrong place to
130
+ mint one, so if nothing is stored under that key the CLI says so and stops. Provision once,
131
+ deliberately — see [autonomous-privy-wallet.md](./autonomous-privy-wallet.md):
132
+
133
+ ```ts
134
+ import { provisionMetrikAutonomousWallet } from "@absol-labs/agent";
135
+
136
+ const wallet = await provisionMetrikAutonomousWallet({
137
+ storeKey: "my-agent/base-sepolia",
138
+ });
139
+ console.log(wallet.address); // fund this with test USDC and Base Sepolia ETH
140
+ ```
141
+
142
+ ```bash
143
+ export METRIK_AGENT_PRIVY_WALLET_KEY=my-agent/base-sepolia
144
+ metrik invoke 0x… --path /health
145
+ ```
146
+
147
+ Read the credential-store durability section of that document before funding anything: on
148
+ a headless Linux host the OS store is **not** durable and provisioning is refused, which
149
+ is why `METRIK_WALLET_ENCRYPTION_KEY` exists. The CLI selects the same store the same way
150
+ — set that variable and it reads the encrypted file, unset and it reads the OS store — so
151
+ a mismatch between provisioning and use simply finds no wallet and says so.
152
+
153
+ **User-owned broker session (`privy-session`)** — a scoped session minted from a Privy
154
+ login (today that means the dApp, which holds the user's Privy access token):
155
+
156
+ | Variable | Required | What it is |
157
+ | ---------------------------------- | -------- | ------------------------------------------------------------------ |
158
+ | `METRIK_AGENT_PRIVY_BROKER_URL` | yes | the broker that minted the token; a token is valid nowhere else |
159
+ | `METRIK_AGENT_PRIVY_SESSION_TOKEN` | yes | the token from the broker's `POST /v1/sessions` |
160
+ | `METRIK_AGENT_PRIVY_ADDRESS` | yes | the embedded wallet address the session is bound to |
161
+ | `METRIK_AGENT_PRIVY_APP_ID` | yes | the **public** Privy app id — the broker serves it at `/v1/config` |
162
+
163
+ #### Which broker the CLI talks to
164
+
165
+ The agent-owned path defaults to the broker URL the wallet was provisioned against, which
166
+ for `provisionMetrikAutonomousWallet()` is the Metrik broker at
167
+ `https://wallet.137.23.50.249.sslip.io`. Its `GET /health` and `GET /v1/config` answer
168
+ publicly; `/v1/config` carries only the public app id, the public policy id and the fixed
169
+ policy ceilings, and nothing there is a secret. The Privy **authorization key** stays on
170
+ the broker and is never served.
171
+
172
+ If you would rather not trust that deployment, run your own: it is
173
+ `metrik-privy-broker` from this package (`src/wallet/privy-broker-server.ts`), it needs
174
+ your own `PRIVY_APP_ID` / `PRIVY_APP_SECRET` / authorization key / policy id, and you then
175
+ point `METRIK_AGENT_PRIVY_BROKER_URL` at it. A wallet provisioned against one broker
176
+ cannot be authorized by another — only the broker holding that wallet's Privy
177
+ authorization key can sign for it — so switching brokers means provisioning a new wallet.
178
+
179
+ #### The spend mandate, and why a Privy wallet does not sign one
180
+
181
+ Every fund-moving command in this package normally self-signs an EIP-712
182
+ `SpendMandate` first. Read why, from `src/cli/mandate.ts`: _"the person typing
183
+ the command IS the mandate owner: they hold the key and they just stated the
184
+ exact spend on the command line."_ The mandate exists because, for a key-holding
185
+ wallet, the client is the last line of defence — so the agent is made to state
186
+ its own ceiling and is held to it.
187
+
188
+ **For a Privy wallet that premise is false.** The user does not hold the key: it
189
+ lives at Privy and is exercised by the broker, which caps every `openStream`
190
+ against a policy the client cannot reach and cannot raise. A self-signed mandate
191
+ on top of that is an agent attesting to its own limits when a stronger limit is
192
+ already enforced somewhere the agent cannot touch.
193
+
194
+ So for a policy-enforced wallet the CLI **does not sign a mandate**, and
195
+ **substitutes the real ceiling instead of dropping the check**: it reads the
196
+ broker's public `GET /v1/config` and refuses an over-ceiling hire locally, in the
197
+ same shape as the gas and USDC preflights — fail early with an actionable
198
+ message rather than paying gas to learn a knowable fact. `open --dry-run`
199
+ reports the ceiling and whether the hire fits; a real `open` refuses.
200
+
201
+ ```
202
+ mandate not signed — this wallet's ceiling is enforced by its broker, which is stronger
203
+ spend policy 1 USDC (1000000 atomic) total, 1000 atomic/second, 1h max — enforced by https://wallet.137.23.50.249.sslip.io
204
+ fits policy yes
205
+ ```
206
+
207
+ Two things this deliberately does NOT do:
208
+
209
+ - **It does not widen the broker.** Both brokers restrict `eth_signTypedData_v4`
210
+ to `primaryType: "InvocationCapability"`. Teaching them to sign a
211
+ `SpendMandate` would weaken the exact property that makes the wallet
212
+ non-custodial, to reinstate a check that is the weaker of the two.
213
+ - **It does not retire `METRIK_AGENT_MANDATE_*`.** Those caps are a standing
214
+ ceiling the operator deliberately chose, and can be TIGHTER than the broker
215
+ policy. They still apply, enforced locally, because no mandate is signed to
216
+ carry them.
217
+
218
+ Do not "restore" the mandate for consistency. It would both weaken the guarantee
219
+ and break every fund-moving command against the broker as deployed.
220
+
221
+ #### What works, and what does not
222
+
223
+ `open`, `close`, `reclaim`, `reclaim-unverified` and `invoke` all work with a
224
+ Privy wallet.
225
+
226
+ `metrik claim` does not, and that is not a Privy defect: `claim` is the
227
+ **operator's** side, and a broker policy allowlists the **buyer** actions only
228
+ (`openStream`, `close`, `reclaim`, `reclaimUnverified`, and a bounded USDC
229
+ approval to the escrow). An operator claims with the operator's own key. The CLI
230
+ says this before it runs rather than letting you discover it at the broker.
231
+
232
+ Privy support is Base Sepolia (84532) only, EOA only. No ERC-4337, no EIP-1271,
233
+ no gas sponsorship claim.
234
+
235
+ #### Trust model, honestly
236
+
237
+ A raw `METRIK_AGENT_PRIVATE_KEY` is the simplest thing that can possibly work and the CLI
238
+ holds the whole key: anything that reads your environment can move every token in that
239
+ wallet, forever.
240
+
241
+ Privy is not "no custody" — it is **different** custody. The signing key lives at Privy and
242
+ is exercised by the broker, so the parties who can move funds are Privy and whoever runs
243
+ the broker, together. What the CLI holds is a P-256 authorization key (agent-owned) or a
244
+ short-lived session token (session) — credentials that authorize a **policy-bounded**
245
+ request, not credentials that can drain the wallet. Leaking one is bounded by the broker's
246
+ policy: Base Sepolia only, the escrow and USDC contracts only, zero native value, and the
247
+ installed ceilings (`/v1/config`). Losing the agent-owned P-256 key is the mirror risk: the
248
+ wallet can never be authorized again and anything it holds is unspendable.
249
+
250
+ Pick a raw key if you are the only party and you want no third party in the path. Pick
251
+ Privy if you want a policy boundary between the machine running the agent and the money.
104
252
 
105
253
  `invoke` needs the key too, even though it broadcasts nothing: it signs an **off-chain
106
254
  EIP-712 invocation capability** as the stream's buyer. `open --dry-run` is the one path
@@ -109,19 +257,21 @@ that needs no key at all — it builds and prints the intended call and says whi
109
257
 
110
258
  ## Commands
111
259
 
112
- | Command | Needs a wallet? | Broadcasts a tx? | What it does |
113
- | -------------------- | ------------------------------ | ---------------- | ------------------------------------------------ |
114
- | `discover` | no | no | Lists verified registry listings |
115
- | `open` | **yes** (not with `--dry-run`) | **yes** | Escrows USDC into a metered stream |
116
- | `status` | no | no | One stream's on-chain state + verified accrual |
117
- | `streams` | no (with `--address`) | no | Lists a wallet's streams from the escrow's logs |
118
- | `watch` | no | no | Polls that until the stream closes |
119
- | `invoke` | **yes** (off-chain capability) | no | Calls the paid service, authorized by the stream |
120
- | `close` | **yes** | **yes** | Stops the clock |
121
- | `reclaim` | **yes** | **yes** | Takes back what was never earned |
122
- | `reclaim-unverified` | **yes** | **yes** | Buyer escape hatch: reclaim with no proof |
123
- | `claim` | **yes** (the operator's) | **yes** | Operator: redeem what verified |
124
- | `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` |
125
275
 
126
276
  ### `metrik discover`
127
277
 
@@ -222,6 +372,21 @@ as `simulationPendingApproval: true`, with no `simulationError` — a script bra
222
372
  that field no longer sees the expected path as a failure. An allowance-shaped revert when
223
373
  the allowance IS already sufficient, and every other revert, are still surfaced as errors.
224
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
+
225
390
  **Amounts.** `--budget` and `--rate` are decimal USDC by default; `--units atomic`
226
391
  switches both to raw 6-decimal base units (what the escrow, the oracle and every
227
392
  `METRIK_E2E_*` fixture use). The unit is explicit rather than inferred from magnitude,
@@ -384,7 +549,10 @@ Configured mandate ceilings are carried through verbatim — they are policy, no
384
549
  and re-typing four caps from memory is where a wrong ceiling comes from. **Credentials are
385
550
  always placeholders.** This command never reads a key out of your environment and prints
386
551
  it back; the wallet block is simply shaped like the environment it ran in (the CDP
387
- 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.
388
556
 
389
557
  ### `metrik invoke`
390
558
 
@@ -414,6 +582,96 @@ listings ("consumer-attested T2 invocation is not supported for gated listings")
414
582
  live listings are gated — so a `metrik prove` would be a command that fails for almost
415
583
  everyone who ran it. It is left out until that constraint lifts.
416
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
+
417
675
  ### `metrik close` and `metrik reclaim`
418
676
 
419
677
  ```bash
@@ -427,12 +685,32 @@ returns the unspent budget, and the buyer sends it. `close` now points at
427
685
  `--close-first` for next time, rather than leaving the one-shot form visible only in
428
686
  `metrik reclaim --help`.
429
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
+
430
699
  **`reclaim` waits, by design.** `reclaimWithProof` reverts with `SettlementWindowOpen`
431
700
  until a checkpoint whose `checkedAt` is at or after the stream's stop time has posted.
432
701
  That is the normal ordering of a checkpoint rail, not a fault — in practice the first
433
- attempt fails and one roughly 30 seconds later succeeds. The CLI retries **that one
434
- condition** on a bounded schedule and narrates the wait on stderr; every other failure is
435
- 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.
436
714
 
437
715
  If it is still blocked when the budget runs out, the message does **not** just say "retry
438
716
  later" — every attempt this command makes needs a proof from the oracle, so when the
@@ -448,8 +726,10 @@ lands on another that has not imported the block.) If the stream is **already**
448
726
  the close is skipped and the command goes straight to reclaim — so re-running it is always
449
727
  safe and never dead-ends on `StreamClosedAlready()`.
450
728
 
451
- `reclaim` on a stream that is still running reports that in words, with the command that
452
- 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.
453
733
 
454
734
  Close and reclaim are buyer-recovery actions: `VerifiedStreamAgentClient` authorizes them
455
735
  on the mandate owner's signature alone, never on caps or expiry, so a lapsed mandate can
@@ -546,17 +826,26 @@ that wait legible.
546
826
 
547
827
  ## Settlement configuration
548
828
 
549
- Everything defaults to Metrik's live Base Sepolia deployment, and every value is
550
- overridable per command or through the environment:
551
-
552
- | Flag | Env | Default |
553
- | ---------------- | --------------------------- | ------------------------------------- |
554
- | `--chain-id` | `METRIK_AGENT_CHAIN_ID` | `84532` |
555
- | `--rpc-url` | `METRIK_AGENT_RPC_URL` | Base Sepolia public RPC |
556
- | `--escrow` | `METRIK_AGENT_ESCROW` | `BASE_SEPOLIA_ESCROW_V2` |
557
- | `--usdc` | `METRIK_AGENT_USDC` | `BASE_SEPOLIA_USDC` |
558
- | `--oracle-url` | `METRIK_AGENT_ORACLE_URL` | `https://oracle.metrik.live` |
559
- | `--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.
560
849
 
561
850
  ## Embedding it
562
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
@@ -1,8 +1,9 @@
1
1
  # Hosted MCP over Streamable HTTP
2
2
 
3
3
  The agent lane ships two MCP transports over the **same** tool set
4
- (`hire_verified_service`, `check_stream_status`, `reclaim_unspent`, `list_streams`,
5
- and the optional `prove_https_response`):
4
+ (`discover_services`, `hire_verified_service`, `invoke_service`,
5
+ `check_stream_status`, `reclaim_unspent`, `list_streams`, and the optional
6
+ `prove_https_response` — see [`mcp-reference.md`](./mcp-reference.md)):
6
7
 
7
8
  - **stdio** (`src/mcp/stdio.ts`) — local dev, one process per caller. Started with
8
9
  the published `metrik-mcp` executable (`npx --package @absol-labs/agent metrik-mcp`),
@@ -79,7 +80,7 @@ network but need distinct wallets override only the wallet vars in their `env`.
79
80
 
80
81
  ```bash
81
82
  export METRIK_AGENT_RPC_URL="https://base-sepolia.example/rpc"
82
- export METRIK_AGENT_ESCROW="0x21948a5E6AE8d9A3D1050791AB6138657Fb54286"
83
+ export METRIK_AGENT_ESCROW="0x0f09f36Ccc05A7c9882F438721C08De314dFd46C" # StreamEscrowV2 — NOT the retired V1 escrow
83
84
  export METRIK_AGENT_USDC="0x036CbD53842c5426634e7929541eC2318f3dCF7e"
84
85
  # ... wallet vars (private key or CDP) ...
85
86
  export METRIK_MCP_TENANTS='[{"id":"acme","token":"REPLACE_WITH_SECRET"}]'