@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
@@ -1,4 +1,9 @@
1
- import { SettlementWindowOpenError } from "@absol-labs/sdk";
1
+ import {
2
+ CheckpointNotReadyError,
3
+ ReclaimFailedError,
4
+ SettlementWindowOpenError,
5
+ StreamNotInCheckpointError,
6
+ } from "@absol-labs/sdk";
2
7
 
3
8
  import { booleanFlag, integerFlag } from "../args.js";
4
9
  import { formatUsdc } from "../amounts.js";
@@ -12,6 +17,12 @@ import {
12
17
  import { signRecoveryMandate } from "../mandate.js";
13
18
  import { assertCanPayGas } from "../preflight.js";
14
19
  import { writeClientOptions } from "../deps.js";
20
+ import {
21
+ describeAwaitingCheckpoint,
22
+ describeTerminalBlocker,
23
+ readReclaimBlocker,
24
+ type ReclaimBlocker,
25
+ } from "../reclaimability.js";
15
26
  import { isPolicyEnforced, resolveCliWallet } from "../wallet.js";
16
27
  import type { CommandContext, CommandSpec } from "../context.js";
17
28
  import type { CliReadClient, CliWriteClient } from "../deps.js";
@@ -20,6 +31,9 @@ import type {
20
31
  StreamProofTransactionResult,
21
32
  } from "../../sdk/client.js";
22
33
  import type { StreamV2 } from "@absol-labs/sdk";
34
+ import { streamEscrowV2Abi } from "@absol-labs/shared";
35
+ import { parseEventLogs, type Log } from "viem";
36
+ import { escrowGenerationFor } from "../../escrow-generation.js";
23
37
 
24
38
  /**
25
39
  * How long to keep retrying a reclaim that is blocked only by an open
@@ -38,6 +52,8 @@ export const reclaimCommand: CommandSpec = {
38
52
  signs: true,
39
53
  async run(context: CommandContext): Promise<void> {
40
54
  const { args, config, env, out, deps } = context;
55
+ const setActivity = (activity: string | null): void =>
56
+ context.progress?.setActivity(activity);
41
57
  const [streamIdArg] = args.positionals;
42
58
  if (streamIdArg === undefined) {
43
59
  throw usageError("metrik reclaim requires a <streamId>");
@@ -56,7 +72,24 @@ export const reclaimCommand: CommandSpec = {
56
72
  const { account } = wallet;
57
73
  const read = deps.createReadClient(config);
58
74
  await assertTestnetRpc(config, () => read.getChainId());
59
- await assertCanPayGas(read, account.address);
75
+
76
+ // Name the condition from chain state BEFORE anything is built or spent.
77
+ // The public endpoint's revert data is not dependable enough to tell
78
+ // "already reclaimed" from "still running" from "not checkpointed yet", and
79
+ // those three have nothing in common but the word "reverted". See
80
+ // `../reclaimability.ts`.
81
+ const blocker = await readReclaimBlocker(read, streamId, deps.nowSeconds());
82
+ // `--close-first` exists precisely to clear a running stream, so it is the
83
+ // one caller allowed past that refusal. Nothing gets past "already
84
+ // reclaimed": there is no money left to take, with or without a close.
85
+ if (blocker !== null && !(closeFirst && blocker.kind === "running")) {
86
+ throwIfTerminal(blocker, streamId);
87
+ if (blocker.kind === "awaiting-checkpoint") {
88
+ out.note(describeAwaitingCheckpoint(blocker));
89
+ }
90
+ }
91
+
92
+ await assertCanPayGas(read, account.address, config.network);
60
93
 
61
94
  const write = deps.createWriteClient(
62
95
  config,
@@ -105,7 +138,24 @@ export const reclaimCommand: CommandSpec = {
105
138
  out.note(
106
139
  `closed in ${closeResult.txHash} — waiting for it to land …`,
107
140
  );
141
+ setActivity(
142
+ `waiting for the RPC endpoint to show the close in ${closeResult.txHash}`,
143
+ );
108
144
  await awaitClosed(read, streamId, deps.sleep, closeResult.txHash);
145
+ setActivity(null);
146
+ // A stream closed before any checkpoint covers its stop time is the
147
+ // ORDINARY outcome of a short trial run, not an exotic one — and it
148
+ // is the state `--close-first` leaves behind by construction. Say
149
+ // what is being waited for, so the retries below read as the
150
+ // advertised wait rather than as a command that has stalled.
151
+ const afterClose = await readReclaimBlocker(
152
+ read,
153
+ streamId,
154
+ deps.nowSeconds(),
155
+ );
156
+ if (afterClose?.kind === "awaiting-checkpoint") {
157
+ out.note(describeAwaitingCheckpoint(afterClose));
158
+ }
109
159
  }
110
160
  }
111
161
  }
@@ -122,27 +172,56 @@ export const reclaimCommand: CommandSpec = {
122
172
  attempts,
123
173
  intervalMs: interval * 1_000,
124
174
  sleep: deps.sleep,
125
- onRetry: (attempt, reason) =>
175
+ isRetryable: async (error) =>
176
+ await isWaitingForCheckpoint(
177
+ error,
178
+ read,
179
+ streamId,
180
+ deps.nowSeconds(),
181
+ ),
182
+ explainFailure: async (error) =>
183
+ await explainReclaimFailure(error, read, streamId, deps.nowSeconds()),
184
+ onRetry: (attempt, reason) => {
126
185
  out.note(
127
186
  `reclaim blocked: no checkpoint covers the stream's stop time yet (attempt ${attempt}/${attempts}) — ${reason}`,
128
- ),
187
+ );
188
+ // The heartbeat must describe THIS wait, not guess at a slow RPC.
189
+ setActivity(
190
+ `waiting for the oracle's next checkpoint (attempt ${attempt}/${attempts} was blocked; retrying every ${interval}s)`,
191
+ );
192
+ },
129
193
  // "Retry later" is the wrong advice when the oracle is gone: every
130
194
  // attempt this command makes needs a proof FROM that oracle. So the
131
195
  // give-up message names the path that needs no proof, and says whether
132
196
  // it is open yet.
133
197
  exhaustedHint: async () =>
134
- await describeLastResort(read, streamId, deps.nowSeconds()),
198
+ await describeLastResort(
199
+ read,
200
+ streamId,
201
+ deps.nowSeconds(),
202
+ escrowGenerationFor(config.chainId, config.escrow),
203
+ ),
135
204
  },
136
205
  );
137
206
 
138
207
  // Read the settled figure back only once the node we are reading from has
139
- // actually applied the reclaim. `sepolia.base.org` is load-balanced, so an
208
+ // actually applied the reclaim. The public RPC is load-balanced, so an
140
209
  // immediate read can land on a lagged node and report `settledCumulative: 0`
141
- // for a stream that just settled a non-zero amount — printing "settled to
142
- // operator 0 USDC" when it was not zero. Poll for `reclaimed` (which the
210
+ // for a stream that just settled a non-zero amount — printing an operator
211
+ // entitlement of 0 USDC when it was not zero. Poll for `reclaimed` (which the
143
212
  // transaction we just landed sets) so the number beside it is from the same
144
213
  // post-reclaim state. Bounded, and falls back to the last value read.
214
+ setActivity("waiting for the RPC endpoint to show the reclaim");
145
215
  const stream = await readSettledStream(read, streamId, deps.sleep);
216
+ setActivity(null);
217
+ // The refund comes from the reclaim's own BuyerReclaimed event, never from
218
+ // a post-reclaim read: reclaim overwrites `deposit` with the operator's
219
+ // entitlement, so deposit − settled reads 0 after every reclaim.
220
+ const refunded = refundFromReceipt(
221
+ result.reclaimResult.receipt,
222
+ config.escrow,
223
+ streamId,
224
+ );
146
225
  out.result({
147
226
  streamId,
148
227
  txHash: result.reclaimResult.txHash,
@@ -152,6 +231,14 @@ export const reclaimCommand: CommandSpec = {
152
231
  // asked for".
153
232
  closeSkippedAlreadyClosed: closeFirst ? alreadyClosed : null,
154
233
  settledCumulative: stream?.settledCumulative ?? null,
234
+ // What the reclaim sent back: everything not settled to the operator.
235
+ // Only reported once the read reflects the reclaim, so a lagged node can
236
+ // never print a refund figure computed from pre-reclaim state.
237
+ refundedAmount: refunded,
238
+ // For an x402 stream the refund goes to the opener's payer, whoever sent
239
+ // the (permissionless) reclaim.
240
+ opener: stream?.opener ?? null,
241
+ refundedTo: stream === null ? null : (stream.payer ?? stream.buyer),
155
242
  });
156
243
  out.print("Reclaimed.");
157
244
  out.table([
@@ -165,7 +252,30 @@ export const reclaimCommand: CommandSpec = {
165
252
  ...(stream === null
166
253
  ? []
167
254
  : ([
168
- ["settled to operator", formatUsdc(stream.settledCumulative)],
255
+ // No USDC moves to the operator at reclaim: reclaim fixes the
256
+ // operator's entitlement, the operator claims it later, and the
257
+ // protocol fee comes out at that claim. "Settled to operator"
258
+ // read as a payment that never happened.
259
+ [
260
+ "operator entitled",
261
+ `${formatUsdc(stream.settledCumulative)} (claims separately; fee taken at claim)`,
262
+ ],
263
+ ...(refunded === null
264
+ ? []
265
+ : ([
266
+ [
267
+ (stream.opener ?? null) === null
268
+ ? "returned to buyer"
269
+ : "returned to payer",
270
+ formatUsdc(refunded),
271
+ ],
272
+ ] as const)),
273
+ ...((stream.opener ?? null) === null
274
+ ? []
275
+ : ([
276
+ ["via opener", stream.opener as string],
277
+ ["refunded to payer", stream.payer],
278
+ ] as const)),
169
279
  ] as const)),
170
280
  ]);
171
281
  },
@@ -207,7 +317,7 @@ function isAlreadyClosed(error: unknown): boolean {
207
317
  * Block until the node we read from reports the stream as closed.
208
318
  *
209
319
  * The close transaction's own receipt is awaited inside the SDK, but
210
- * `sepolia.base.org` is load-balanced: the receipt can come back from one node
320
+ * the public RPC is load-balanced: the receipt can come back from one node
211
321
  * while the very next `simulateContract` lands on another that has not imported
212
322
  * the block yet. That node simulates `reclaim` against PRE-close state and
213
323
  * reverts `StreamNotReclaimable()`. Confirming the close through the same read
@@ -243,8 +353,16 @@ export async function awaitClosed(
243
353
  * time. That is not an error condition — it is the normal ordering of a
244
354
  * checkpoint rail, and it happens on the FIRST attempt essentially every time.
245
355
  * A CLI that surfaced the raw revert would teach every user that reclaim is
246
- * broken, so the wait is built in and narrated. Any other failure is rethrown
247
- * immediately: only an open settlement window is retried.
356
+ * broken, so the wait is built in and narrated.
357
+ *
358
+ * What counts as "wait for the next checkpoint" is decided by `isRetryable`,
359
+ * NOT by this function. It used to be `instanceof SettlementWindowOpenError`
360
+ * alone, which missed the two ways the wait actually shows up in practice: the
361
+ * ORACLE refusing a proof for a stream it has not checkpointed yet (the SDK
362
+ * fetches the proof before it ever simulates, so the escrow's revert never
363
+ * happens), and the endpoint returning revert data the ABI cannot decode. Both
364
+ * killed the advertised retry on its first attempt — the second one right
365
+ * after `--close-first` had already spent the buyer's close.
248
366
  */
249
367
  export async function reclaimWithBoundedRetry(
250
368
  write: CliWriteClient,
@@ -253,6 +371,10 @@ export async function reclaimWithBoundedRetry(
253
371
  readonly attempts: number;
254
372
  readonly intervalMs: number;
255
373
  readonly sleep: (ms: number) => Promise<void>;
374
+ /** True when this failure is the checkpoint wait and will clear on its own. */
375
+ readonly isRetryable: (error: unknown) => Promise<boolean>;
376
+ /** Turn a failure that will NOT clear into something worth reading. */
377
+ readonly explainFailure: (error: unknown) => Promise<unknown>;
256
378
  readonly onRetry: (attempt: number, reason: string) => void;
257
379
  /** What to tell the buyer once the budget is spent. See `describeLastResort`. */
258
380
  readonly exhaustedHint?: () => Promise<string>;
@@ -263,7 +385,8 @@ export async function reclaimWithBoundedRetry(
263
385
  try {
264
386
  return await write.reclaimStream(await buildInput());
265
387
  } catch (error) {
266
- if (!isSettlementWindowOpen(error)) throw explainReclaimFailure(error);
388
+ if (!(await options.isRetryable(error)))
389
+ throw await options.explainFailure(error);
267
390
  lastError = error;
268
391
  if (attempt === options.attempts) break;
269
392
  options.onRetry(attempt, describeError(error));
@@ -299,25 +422,123 @@ async function describeLastResort(
299
422
  read: CliReadClient,
300
423
  streamId: `0x${string}`,
301
424
  fallbackNowSeconds: number,
425
+ generation: string,
302
426
  ): Promise<string> {
303
427
  const [stream, grace] = await Promise.all([
304
428
  read.getStreamV2(streamId),
305
429
  read.escapeGraceSeconds(),
306
430
  ]);
307
431
  const now = await readChainNow(read, fallbackNowSeconds);
308
- const window = escapeHatchWindow(stream, grace, now.seconds);
432
+ const window = escapeHatchWindow(stream, grace, now.seconds, generation);
309
433
  const escapeCommand = `metrik reclaim-unverified ${streamId} --forfeit-unclaimed`;
310
434
  return window.ready
311
435
  ? `the funds are not lost. The no-proof escape hatch is OPEN (${describeEscapeHatch(window)}): \`${escapeCommand}\` takes them back without the oracle — at the cost of forfeiting whatever the operator earned but has not yet claimed. Retrying this command only helps if the oracle comes back.`
312
436
  : `the funds are not lost, but retrying only helps if the oracle comes back — this path needs a proof from it. The no-proof escape hatch ${describeEscapeHatch(window)}; from then on \`${escapeCommand}\` takes them back without the oracle, forfeiting whatever the operator earned but has not yet claimed.`;
313
437
  }
314
438
 
439
+ /** Raise a blocker that will not clear on its own as a named CliError. */
440
+ function throwIfTerminal(
441
+ blocker: ReclaimBlocker,
442
+ streamId: `0x${string}`,
443
+ cause?: unknown,
444
+ ): void {
445
+ const terminal = describeTerminalBlocker(blocker, streamId);
446
+ if (terminal === null) return;
447
+ throw new CliError(terminal.message, {
448
+ exitCode: 4,
449
+ hint: terminal.hint,
450
+ ...(cause === undefined ? {} : { cause }),
451
+ });
452
+ }
453
+
454
+ /**
455
+ * Is this failure the checkpoint wait, i.e. will it clear on its own?
456
+ *
457
+ * Three shapes, in increasing order of how little the endpoint told us:
458
+ *
459
+ * 1. `SettlementWindowOpenError` — the escrow said so and the ABI decoded it.
460
+ * 2. `StreamNotInCheckpointError` / `CheckpointNotReadyError` — the ORACLE has
461
+ * no proof for this stream yet, so the SDK never reached the escrow at all.
462
+ * This is the state `--close-first` creates by construction on a stream
463
+ * closed before its first covering checkpoint, and treating it as fatal is
464
+ * what stranded a buyer's deposit in escrow for three minutes of manual
465
+ * retries while the CLI's own help promised it would wait.
466
+ * 3. A reclaim revert the ABI could not decode. Nothing in the error names the
467
+ * condition, so ask the chain instead — the state is authoritative and the
468
+ * answer is the same one the pre-flight computes.
469
+ *
470
+ * Case 3 is deliberately narrowed to `ReclaimFailedError`: an unrelated failure
471
+ * (no gas, a dead RPC) must still fail on its first attempt rather than burn
472
+ * twelve of them.
473
+ */
474
+ async function isWaitingForCheckpoint(
475
+ error: unknown,
476
+ read: Pick<
477
+ CliReadClient,
478
+ "getStreamV2" | "latestCheckpointCheckedAt" | "latestBlockTimestamp"
479
+ >,
480
+ streamId: `0x${string}`,
481
+ nowSeconds: number,
482
+ ): Promise<boolean> {
483
+ if (isSettlementWindowOpen(error)) return true;
484
+ if (hasNoCheckpointProofYet(error)) return true;
485
+ if (!isReclaimRevert(error)) return false;
486
+ const blocker = await readReclaimBlocker(read, streamId, nowSeconds).catch(
487
+ () => null,
488
+ );
489
+ return blocker?.kind === "awaiting-checkpoint";
490
+ }
491
+
492
+ /**
493
+ * The oracle could not supply a proof yet: it has no leaf for this stream in
494
+ * the latest checkpoint (404), or no internally consistent checkpoint is ready
495
+ * (503). Both resolve when the next checkpoint lands, ~15-22s apart.
496
+ */
497
+ function hasNoCheckpointProofYet(error: unknown): boolean {
498
+ for (let current: unknown = error, depth = 0; depth < 8; depth++) {
499
+ if (
500
+ current instanceof StreamNotInCheckpointError ||
501
+ current instanceof CheckpointNotReadyError
502
+ ) {
503
+ return true;
504
+ }
505
+ if (!(current instanceof Error) || current.cause === undefined)
506
+ return false;
507
+ current = current.cause;
508
+ }
509
+ return false;
510
+ }
511
+
512
+ /** True when the escrow rejected the reclaim, whatever the reason decoded to. */
513
+ function isReclaimRevert(error: unknown): boolean {
514
+ for (let current: unknown = error, depth = 0; depth < 8; depth++) {
515
+ if (current instanceof ReclaimFailedError) return true;
516
+ if (!(current instanceof Error) || current.cause === undefined)
517
+ return false;
518
+ current = current.cause;
519
+ }
520
+ return false;
521
+ }
522
+
315
523
  /**
316
- * `StreamNotReclaimable()` means exactly one thing: the stream is neither closed
317
- * nor past its expiry. Say that, and say which command fixes it, instead of
318
- * handing the reader a selector.
524
+ * Name a failure that will not clear.
525
+ *
526
+ * `StreamNotReclaimable()` means exactly one thing when the ABI decodes it: the
527
+ * stream is neither closed nor past its expiry. When it does NOT decode — the
528
+ * live failure mode on the public endpoint — the chain is asked the same
529
+ * question, so "already reclaimed" and "still running" are still told apart and
530
+ * still say which command fixes them. The raw error is kept as the `cause`
531
+ * either way.
319
532
  */
320
- function explainReclaimFailure(error: unknown): unknown {
533
+ async function explainReclaimFailure(
534
+ error: unknown,
535
+ read: Pick<
536
+ CliReadClient,
537
+ "getStreamV2" | "latestCheckpointCheckedAt" | "latestBlockTimestamp"
538
+ >,
539
+ streamId: `0x${string}`,
540
+ nowSeconds: number,
541
+ ): Promise<unknown> {
321
542
  if (!(error instanceof Error)) return error;
322
543
  for (let current: unknown = error, depth = 0; depth < 8; depth++) {
323
544
  if (!(current instanceof Error)) break;
@@ -326,7 +547,7 @@ function explainReclaimFailure(error: unknown): unknown {
326
547
  "the stream is still running, so there is nothing to reclaim yet",
327
548
  {
328
549
  exitCode: 4,
329
- hint: "stop the clock first — `metrik close <streamId>`, or re-run this command with --close-first — or wait for the stream to expire.",
550
+ hint: `stop the clock first — \`metrik close ${streamId}\`, or re-run this command with --close-first — or wait for the stream to expire.`,
330
551
  cause: error,
331
552
  },
332
553
  );
@@ -334,6 +555,18 @@ function explainReclaimFailure(error: unknown): unknown {
334
555
  if (current.cause === undefined) break;
335
556
  current = current.cause;
336
557
  }
558
+ if (isReclaimRevert(error)) {
559
+ const blocker = await readReclaimBlocker(read, streamId, nowSeconds).catch(
560
+ () => null,
561
+ );
562
+ if (blocker !== null) {
563
+ try {
564
+ throwIfTerminal(blocker, streamId, error);
565
+ } catch (named) {
566
+ return named;
567
+ }
568
+ }
569
+ }
337
570
  return error;
338
571
  }
339
572
 
@@ -359,6 +592,31 @@ const SETTLED_READ_ATTEMPTS = 5;
359
592
  // `--interval`), so the two waits are tellable apart in tests and in logs.
360
593
  const SETTLED_READ_INTERVAL_MS = 500;
361
594
 
595
+ /**
596
+ * The amount the reclaim returned, decoded from its BuyerReclaimed event (same
597
+ * signature on V2 and V3). Null when the receipt carries no matching log, so
598
+ * the CLI prints nothing rather than a guessed figure.
599
+ */
600
+ export function refundFromReceipt(
601
+ receipt: { logs?: readonly Log[] } | undefined,
602
+ escrow: string,
603
+ streamId: `0x${string}`,
604
+ ): bigint | null {
605
+ const logs = receipt?.logs;
606
+ if (!Array.isArray(logs)) return null;
607
+ const events = parseEventLogs({
608
+ abi: streamEscrowV2Abi,
609
+ eventName: "BuyerReclaimed",
610
+ logs: logs.filter(
611
+ (log) => log.address?.toLowerCase() === escrow.toLowerCase(),
612
+ ),
613
+ });
614
+ const match = events.find(
615
+ (event) => event.args.streamId.toLowerCase() === streamId.toLowerCase(),
616
+ );
617
+ return match === undefined ? null : match.args.amount;
618
+ }
619
+
362
620
  async function readSettledStream(
363
621
  read: { getStreamV2(streamId: `0x${string}`): Promise<StreamV2> },
364
622
  streamId: `0x${string}`,