@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
@@ -0,0 +1,172 @@
1
+ import { formatUnits } from "viem";
2
+
3
+ import {
4
+ rateOutsideBounds,
5
+ SignedRateBoundsError,
6
+ verifySignedRateBounds,
7
+ type SignedRateBounds,
8
+ } from "../gateway/signed-rate-bounds.js";
9
+
10
+ /**
11
+ * The buyer-side twin of the gateway's signed-rate check.
12
+ *
13
+ * `StreamEscrowV2.openStream` accepts any non-zero `ratePerSecond`; the band a
14
+ * seller will actually SERVE lives in its operator-signed descriptor
15
+ * (`commercial.minRatePerSecond` / `maxRatePerSecond`), and the seller's
16
+ * capability gateway answers every request on an out-of-band stream with
17
+ * `403 stream-rate-out-of-bounds`. Opening such a stream locks the buyer's
18
+ * deposit in a stream that can never be served, so every buyer surface that
19
+ * takes a caller-chosen rate checks it against the SAME band, with the SAME
20
+ * verification ({@link verifySignedRateBounds}), before anything is broadcast.
21
+ *
22
+ * Nothing here is a second signature implementation: the bounds come only out
23
+ * of `verifySignedRateBounds`, which re-derives the serviceRef from the signed
24
+ * content and recovers the operator's EIP-712 signature.
25
+ */
26
+
27
+ /** A listing as discovery hands it over: the serviceRef and its signed record. */
28
+ export interface RateBandListing {
29
+ readonly serviceRef: string;
30
+ readonly signed: unknown;
31
+ }
32
+
33
+ /**
34
+ * The operator-signed rate band of a verified listing.
35
+ *
36
+ * - `null`: the listing is a legacy service BINDING, which carries no
37
+ * commercial terms, so the operator signed no band and there is nothing to
38
+ * enforce (the gateway enforces none either).
39
+ * - a {@link SignedRateBounds}: the descriptor verified. Either bound may still
40
+ * be `null` when the operator signed only one side, or none.
41
+ *
42
+ * Throws {@link SignedRateBoundsError} when the record IS a descriptor but does
43
+ * not verify, or verifies for a different serviceRef than the listing claims.
44
+ * Callers must treat that as a refusal: a band that cannot be verified is not a
45
+ * band that can be assumed absent.
46
+ */
47
+ export async function listingRateBand(
48
+ listing: RateBandListing,
49
+ ): Promise<SignedRateBounds | null> {
50
+ const signed = listing.signed;
51
+ if (
52
+ typeof signed !== "object" ||
53
+ signed === null ||
54
+ !("descriptor" in signed)
55
+ ) {
56
+ return null;
57
+ }
58
+ const bounds = await verifySignedRateBounds(signed);
59
+ if (bounds.serviceRef !== listing.serviceRef.toLowerCase()) {
60
+ throw new SignedRateBoundsError(
61
+ `the signed descriptor verifies for serviceRef ${bounds.serviceRef}, not the listed ${listing.serviceRef}`,
62
+ );
63
+ }
64
+ return bounds;
65
+ }
66
+
67
+ /** `0.0001` for `100n` — USDC has 6 decimals. */
68
+ function usdc(atomic: bigint): string {
69
+ return formatUnits(atomic, 6);
70
+ }
71
+
72
+ /**
73
+ * The signed band in both units, e.g. `100..10000 atomic/s (0.0001..0.01
74
+ * USDC/s)`. A one-sided band reads `at least 100 atomic/s (…)` /
75
+ * `at most 10000 atomic/s (…)`.
76
+ */
77
+ export function describeRateBand(bounds: SignedRateBounds): string {
78
+ const { minRatePerSecond: min, maxRatePerSecond: max } = bounds;
79
+ if (min !== null && max !== null) {
80
+ return `${min}..${max} atomic/s (${usdc(min)}..${usdc(max)} USDC/s)`;
81
+ }
82
+ if (min !== null) {
83
+ return `at least ${min} atomic/s (${usdc(min)} USDC/s)`;
84
+ }
85
+ if (max !== null) {
86
+ return `at most ${max} atomic/s (${usdc(max)} USDC/s)`;
87
+ }
88
+ return "any rate (the operator signed no bounds)";
89
+ }
90
+
91
+ /**
92
+ * `undefined` when `rate` is inside the operator-signed band (bounds are
93
+ * inclusive, exactly as the gateway applies them), otherwise an actionable
94
+ * refusal naming the band in atomic units and USDC. `rateLabel` is how the
95
+ * caller supplied the rate (`--rate`, `ratePerSecondUsdc`).
96
+ */
97
+ export function rateBandRefusal(
98
+ rate: bigint,
99
+ bounds: SignedRateBounds,
100
+ rateLabel: string,
101
+ ): string | undefined {
102
+ const outside = rateOutsideBounds(rate, bounds);
103
+ if (outside === undefined) return undefined;
104
+ return `the operator signed rates ${describeRateBand(bounds)}; ${rateLabel} ${rate} (${usdc(rate)} USDC/s) would be refused by the service's gateway (${outside}), so the stream could never be served. Nothing was broadcast.`;
105
+ }
106
+
107
+ /**
108
+ * Outcome of {@link checkListedRateBand}: `ok: false` is a refusal, and its
109
+ * `message` is safe to hand straight back to the caller.
110
+ */
111
+ export type ListedRateBandCheck =
112
+ | {
113
+ readonly ok: true;
114
+ /** `unlisted`: no verified listing, so no operator-signed band exists. */
115
+ readonly band: "in-band" | "no-band-signed" | "unlisted";
116
+ }
117
+ | { readonly ok: false; readonly message: string };
118
+
119
+ /**
120
+ * Discover the serviceRef's verified listing(s) and check `rate` against every
121
+ * operator-signed band among them, for a surface that has no listing in hand.
122
+ *
123
+ * Fail closed: a registry that cannot be read means a signed band may exist and
124
+ * is unknown, so it is a refusal — never a pass. A serviceRef with no verified
125
+ * listing has no signed band to violate, and is allowed (these surfaces never
126
+ * required a listing to hire).
127
+ */
128
+ export async function checkListedRateBand(
129
+ discover: () => Promise<readonly RateBandListing[]>,
130
+ serviceRef: string,
131
+ rate: bigint,
132
+ rateLabel: string,
133
+ ): Promise<ListedRateBandCheck> {
134
+ let listings: readonly RateBandListing[];
135
+ try {
136
+ listings = await discover();
137
+ } catch (error) {
138
+ return {
139
+ ok: false,
140
+ message: `refusing to hire: the service registry could not be read (${error instanceof Error ? error.message : String(error)}), so the operator-signed rate band for ${serviceRef} cannot be checked and the stream might never be served. Nothing was spent — retry when the registry answers.`,
141
+ };
142
+ }
143
+ const wanted = serviceRef.toLowerCase();
144
+ const matches = listings.filter(
145
+ (listing) => listing.serviceRef.toLowerCase() === wanted,
146
+ );
147
+ if (matches.length === 0) return { ok: true, band: "unlisted" };
148
+ let signedAny = false;
149
+ for (const listing of matches) {
150
+ let bounds: SignedRateBounds | null;
151
+ try {
152
+ bounds = await listingRateBand(listing);
153
+ } catch (error) {
154
+ if (error instanceof SignedRateBoundsError) {
155
+ return {
156
+ ok: false,
157
+ message: `refusing to hire: the listing's operator-signed rate band could not be verified (${error.message}). Nothing was spent.`,
158
+ };
159
+ }
160
+ throw error;
161
+ }
162
+ if (bounds === null) continue;
163
+ signedAny = true;
164
+ const refusal = rateBandRefusal(rate, bounds, rateLabel);
165
+ if (refusal !== undefined) {
166
+ return { ok: false, message: `refusing to hire: ${refusal}` };
167
+ }
168
+ }
169
+ return { ok: true, band: signedAny ? "in-band" : "no-band-signed" };
170
+ }
171
+
172
+ export { SignedRateBoundsError };
@@ -0,0 +1,135 @@
1
+ import { findDeployment } from "@absol-labs/sdk";
2
+ import type { Address, Hex } from "viem";
3
+
4
+ /**
5
+ * Escrow-generation-specific rules, driven by the SDK network registry's
6
+ * `escrow.generation` (metrik-agent#121).
7
+ *
8
+ * Today the registry knows only `v2` (`StreamEscrowV2`). `StreamEscrowV3`
9
+ * changes one rule the agent reasons about: the buyer's no-proof escape hatch.
10
+ *
11
+ * - **V2**: `reclaimUnverified` opens at `expiresAt + escapeGraceSeconds`.
12
+ * Closing early does NOT bring it forward.
13
+ * - **V3**: it opens at `min(expiresAt, closedAt) + escapeGraceSeconds`, and the
14
+ * escrow exposes `escapeHatchReadyAt(bytes32)` to read it directly. Where a
15
+ * chain read is available it is preferred over re-deriving the formula.
16
+ *
17
+ * An unknown generation is refused, never guessed: a wrong readiness answer
18
+ * either sends a buyer a guaranteed revert or tells them to wait when they need
19
+ * not. (V3 also restricts `recordProven` to the stream's operator or an active
20
+ * oracle signer; no agent or relay path calls it on a buyer's behalf.)
21
+ */
22
+
23
+ /** Escrow generations whose escape-hatch rule this package implements. */
24
+ export type KnownEscrowGeneration = "v2" | "v3";
25
+
26
+ export class UnsupportedEscrowGenerationError extends Error {
27
+ readonly generation: string;
28
+
29
+ constructor(generation: string) {
30
+ super(
31
+ `escrow generation "${generation}" is not one this package knows the escape-hatch rule for (known: v2, v3)`,
32
+ );
33
+ this.name = "UnsupportedEscrowGenerationError";
34
+ this.generation = generation;
35
+ }
36
+ }
37
+
38
+ function known(generation: string): KnownEscrowGeneration {
39
+ if (generation === "v2" || generation === "v3") return generation;
40
+ throw new UnsupportedEscrowGenerationError(generation);
41
+ }
42
+
43
+ /**
44
+ * The generation of `escrow` on `chainId`, from the network registry. An escrow
45
+ * the registry does not record is treated as V2 — the generation every
46
+ * unregistered, explicitly configured Metrik deployment speaks today.
47
+ */
48
+ export function escrowGenerationFor(
49
+ chainId: number,
50
+ escrow: Address,
51
+ ): KnownEscrowGeneration {
52
+ return known(
53
+ // The generation of THIS deployment (current or legacy), not of the
54
+ // network's current escrow.
55
+ findDeployment(chainId, escrow)?.deployment.escrow.generation ?? "v2",
56
+ );
57
+ }
58
+
59
+ /**
60
+ * The second the no-proof escape hatch opens, by formula. `closedAt` is `0`
61
+ * while the stream is open.
62
+ */
63
+ export function escapeHatchReadyAtFormula(
64
+ generation: string,
65
+ stream: { readonly expiresAt: bigint; readonly closedAt: bigint },
66
+ graceSeconds: bigint,
67
+ ): bigint {
68
+ switch (known(generation)) {
69
+ case "v2":
70
+ return stream.expiresAt + graceSeconds;
71
+ case "v3": {
72
+ const stop =
73
+ stream.closedAt > 0n && stream.closedAt < stream.expiresAt
74
+ ? stream.closedAt
75
+ : stream.expiresAt;
76
+ return stop + graceSeconds;
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * `StreamEscrowV3.escapeHatchReadyAt(bytes32)`. A one-function fragment until
83
+ * the SDK ships the V3 ABI; replace it with the SDK's export then.
84
+ */
85
+ export const streamEscrowV3EscapeHatchAbi = [
86
+ {
87
+ type: "function",
88
+ name: "escapeHatchReadyAt",
89
+ stateMutability: "view",
90
+ inputs: [{ name: "streamId", type: "bytes32" }],
91
+ outputs: [{ name: "", type: "uint256" }],
92
+ },
93
+ ] as const;
94
+
95
+ /** The minimal read surface {@link readEscapeHatchReadyAt} needs. */
96
+ export interface EscapeHatchReader {
97
+ readContract(args: {
98
+ readonly address: Address;
99
+ readonly abi: typeof streamEscrowV3EscapeHatchAbi;
100
+ readonly functionName: "escapeHatchReadyAt";
101
+ readonly args: readonly [Hex];
102
+ }): Promise<bigint>;
103
+ }
104
+
105
+ /**
106
+ * The escape-hatch second for one stream: V3 reads the escrow's own view (and
107
+ * falls back to the V3 formula if that read fails); V2 uses its formula.
108
+ */
109
+ export async function readEscapeHatchReadyAt(input: {
110
+ readonly generation: string;
111
+ readonly reader: EscapeHatchReader;
112
+ readonly escrow: Address;
113
+ readonly streamId: Hex;
114
+ readonly stream: { readonly expiresAt: bigint; readonly closedAt: bigint };
115
+ readonly graceSeconds: bigint;
116
+ }): Promise<bigint> {
117
+ const generation = known(input.generation);
118
+ if (generation === "v3") {
119
+ try {
120
+ return await input.reader.readContract({
121
+ address: input.escrow,
122
+ abi: streamEscrowV3EscapeHatchAbi,
123
+ functionName: "escapeHatchReadyAt",
124
+ args: [input.streamId],
125
+ });
126
+ } catch {
127
+ // Fall through to the V3 formula: the same rule the view implements.
128
+ }
129
+ }
130
+ return escapeHatchReadyAtFormula(
131
+ generation,
132
+ input.stream,
133
+ input.graceSeconds,
134
+ );
135
+ }
@@ -2,6 +2,7 @@ import "reflect-metadata";
2
2
 
3
3
  import type {
4
4
  HireComputeResult,
5
+ MetrikNetwork,
5
6
  StreamProofClientConfig,
6
7
  } from "@absol-labs/sdk";
7
8
  import {
@@ -19,7 +20,13 @@ import {
19
20
  type DiscoverServicesOptions,
20
21
  type ServiceListing,
21
22
  } from "../discovery/registry.js";
23
+ import { checkListedRateBand } from "../discovery/rate-band.js";
22
24
  import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
25
+ import {
26
+ defaultNetwork,
27
+ describeAllowedNetworks,
28
+ requireNetworkForChainId,
29
+ } from "../network.js";
23
30
  import {
24
31
  isCheckpointStream,
25
32
  MandateDeniedError,
@@ -34,10 +41,17 @@ import {
34
41
  } from "../sdk/client.js";
35
42
 
36
43
  /**
37
- * Base Sepolia is the only settlement network Metrik supports today (Model A,
38
- * single-chain USDC on Base). Every action fails closed on any other network.
44
+ * The default settlement chain id: the registry's default network. A provider
45
+ * may be configured for any REGISTERED TESTNET network instead; every action
46
+ * fails closed on a network the registry does not know, on a mainnet, and on a
47
+ * wallet whose network differs from the configured one.
39
48
  */
40
- export const METRIK_BASE_SEPOLIA_CHAIN_ID = 84532;
49
+ export const METRIK_AGENTKIT_DEFAULT_CHAIN_ID: number =
50
+ defaultNetwork().chain.id;
51
+
52
+ /** @deprecated Use {@link METRIK_AGENTKIT_DEFAULT_CHAIN_ID}; this is the registry's default network, read from the SDK registry. */
53
+ export const METRIK_BASE_SEPOLIA_CHAIN_ID: number =
54
+ METRIK_AGENTKIT_DEFAULT_CHAIN_ID;
41
55
 
42
56
  const addressSchema = z
43
57
  .string()
@@ -159,7 +173,7 @@ export interface MetrikActionProviderConfig {
159
173
  * agent's construction — the LLM never supplies or edits it.
160
174
  */
161
175
  readonly signedMandate: SignedSpendMandate;
162
- /** Settlement chain id. Defaults to Base Sepolia (84532); any other value fails closed. */
176
+ /** Settlement chain id. Defaults to the registry's default network; a chain the registry does not know as a testnet fails closed. */
163
177
  readonly chainId?: number;
164
178
  /** Injected Metrik client (test path). Mutually exclusive with `sdkConfig`. */
165
179
  readonly agentClient?: MetrikAgentKitClient;
@@ -232,6 +246,7 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
232
246
  readonly #resolver: Partial<MetrikMandateStateResolver>;
233
247
  readonly #now: () => number;
234
248
  readonly #chainId: number;
249
+ readonly #network: MetrikNetwork;
235
250
  readonly #discover: (
236
251
  opts?: DiscoverServicesOptions,
237
252
  ) => Promise<ServiceListing[]>;
@@ -239,11 +254,17 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
239
254
  constructor(config: MetrikActionProviderConfig) {
240
255
  super("metrik", []);
241
256
 
242
- this.#chainId = config.chainId ?? METRIK_BASE_SEPOLIA_CHAIN_ID;
243
- if (this.#chainId !== METRIK_BASE_SEPOLIA_CHAIN_ID) {
244
- // Fail closed: Model A settles only on Base Sepolia today.
257
+ this.#chainId = config.chainId ?? METRIK_AGENTKIT_DEFAULT_CHAIN_ID;
258
+ try {
259
+ this.#network = requireNetworkForChainId(
260
+ this.#chainId,
261
+ { testnetOnly: true },
262
+ "chainId",
263
+ );
264
+ } catch (error) {
265
+ // Fail closed: only a registered testnet network settles.
245
266
  throw new MetrikActionProviderConfigError(
246
- `unsupported chainId ${this.#chainId}: Metrik settles only on Base Sepolia (${METRIK_BASE_SEPOLIA_CHAIN_ID})`,
267
+ `unsupported chainId ${this.#chainId}: Metrik settles only on registered testnet networks (${describeAllowedNetworks({ testnetOnly: true })}) — ${error instanceof Error ? error.message : String(error)}`,
247
268
  );
248
269
  }
249
270
 
@@ -361,6 +382,19 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
361
382
  throw new MandateDeniedError(decision.reason ?? "mandate-expired");
362
383
  }
363
384
 
385
+ // The rate must sit inside the band the operator SIGNED, or the seller's
386
+ // gateway refuses every request on the stream while the deposit stays
387
+ // escrowed. Fails closed when the registry cannot be read.
388
+ const band = await checkListedRateBand(
389
+ () => this.#discover(),
390
+ serviceRef,
391
+ ratePerSecondUsdc,
392
+ "ratePerSecondUsdc",
393
+ );
394
+ if (!band.ok) {
395
+ throw new Error(band.message);
396
+ }
397
+
364
398
  const result = await this.#client.openVerifiedStream({
365
399
  operator,
366
400
  serviceRef,
@@ -375,7 +409,7 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
375
409
 
376
410
  const urlLine = args.publicUrl ? ` Endpoint: ${args.publicUrl}.` : "";
377
411
  return (
378
- `Hired verified service via Metrik. Stream ${result.streamId} opened on Base Sepolia (escrow ${result.handle.escrow}). ` +
412
+ `Hired verified service via Metrik. Stream ${result.streamId} opened on ${this.#network.name} (escrow ${result.handle.escrow}). ` +
379
413
  `Escrowed ${formatUsdc(budgetUsdc)} USDC at ${formatUsdc(ratePerSecondUsdc)} USDC/sec to operator ${operator} for service ${serviceRef}, ` +
380
414
  `max ${maxDurationSeconds}s. Payment accrues only for oracle-verified delivery; unspent USDC is reclaimable.${urlLine} ` +
381
415
  `tx=${result.txHash}${result.approveTxHash ? ` approve=${result.approveTxHash}` : ""}`
@@ -457,7 +491,7 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
457
491
  }
458
492
 
459
493
  supportsNetwork(network: Network): boolean {
460
- // Fail closed: EVM Base Sepolia only.
494
+ // Fail closed: the configured EVM network only.
461
495
  return (
462
496
  network.protocolFamily === "evm" &&
463
497
  network.chainId === String(this.#chainId)
@@ -468,7 +502,7 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
468
502
  const network = walletProvider.getNetwork();
469
503
  if (!this.supportsNetwork(network)) {
470
504
  throw new MetrikActionProviderConfigError(
471
- `unsupported network ${network.protocolFamily}:${network.chainId ?? "unknown"}: Metrik requires Base Sepolia (evm:${this.#chainId})`,
505
+ `unsupported network ${network.protocolFamily}:${network.chainId ?? "unknown"}: Metrik requires ${this.#network.name} (evm:${this.#chainId})`,
472
506
  );
473
507
  }
474
508
  }
@@ -13,6 +13,7 @@ import {
13
13
  type DiscoverServicesOptions,
14
14
  type ServiceListing,
15
15
  } from "../discovery/registry.js";
16
+ import { checkListedRateBand } from "../discovery/rate-band.js";
16
17
  import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
17
18
  import {
18
19
  isCheckpointStream,
@@ -23,12 +24,14 @@ import {
23
24
  type StreamStatusView,
24
25
  type VerifiedStreamAgentOpener,
25
26
  } from "../sdk/client.js";
27
+ import { defaultNetwork } from "../network.js";
26
28
 
27
29
  /**
28
- * Base Sepolia is the only settlement network Metrik supports today (Model A,
29
- * single-chain USDC on Base).
30
+ * The settlement chain id the Eliza plugin's examples and defaults use: the
31
+ * registry's default network, read from the `@absol-labs/sdk` network registry.
30
32
  */
31
- export const METRIK_ELIZA_SETTLEMENT_CHAIN_ID = 84532;
33
+ export const METRIK_ELIZA_SETTLEMENT_CHAIN_ID: number =
34
+ defaultNetwork().chain.id;
32
35
 
33
36
  const addressSchema = z
34
37
  .string()
@@ -195,7 +198,12 @@ export function createMetrikElizaPlugin(
195
198
  "Mandate-gated Metrik actions for hiring, monitoring, reclaiming, and closing verified service streams on Base. x402 proves the payment, Metrik proves the delivery.",
196
199
  actions: [
197
200
  createDiscoverAction(options.discoverServices ?? defaultDiscoverServices),
198
- createHireAction(options.agentClient, resolver, now),
201
+ createHireAction(
202
+ options.agentClient,
203
+ resolver,
204
+ now,
205
+ options.discoverServices ?? defaultDiscoverServices,
206
+ ),
199
207
  createStatusAction(options.agentClient),
200
208
  createReclaimAction(options.agentClient, resolver, now),
201
209
  createCloseAction(options.agentClient, resolver, now),
@@ -277,6 +285,7 @@ function createHireAction(
277
285
  agentClient: MetrikElizaClient,
278
286
  resolver: MetrikElizaMandateStateResolver,
279
287
  now: () => number,
288
+ discover: (opts?: DiscoverServicesOptions) => Promise<ServiceListing[]>,
280
289
  ): Action {
281
290
  return {
282
291
  name: "HIRE_VERIFIED_SERVICE",
@@ -328,6 +337,19 @@ function createHireAction(
328
337
  );
329
338
  }
330
339
 
340
+ // The rate must sit inside the band the operator SIGNED, or the
341
+ // seller's gateway refuses every request on the stream while the
342
+ // deposit stays escrowed. Fails closed when the registry cannot be read.
343
+ const band = await checkListedRateBand(
344
+ () => discover(),
345
+ input.serviceRef,
346
+ BigInt(input.ratePerSecondUsdc),
347
+ "ratePerSecondUsdc",
348
+ );
349
+ if (!band.ok) {
350
+ return await failureResult(band.message, callback);
351
+ }
352
+
331
353
  const result = await agentClient.openVerifiedStream({
332
354
  operator: input.operator as `0x${string}`,
333
355
  serviceRef: input.serviceRef as `0x${string}`,
@@ -507,7 +529,7 @@ function createExamples(action: MetrikElizaActionName): ActionExample[][] {
507
529
  const exampleMandate = {
508
530
  mandateId: `0x${"55".repeat(32)}`,
509
531
  owner: "0x1111111111111111111111111111111111111111",
510
- chainId: 84532,
532
+ chainId: METRIK_ELIZA_SETTLEMENT_CHAIN_ID,
511
533
  issuedAt: 1_900_000_000,
512
534
  mandate: {
513
535
  maxPerStreamUsdc: "100000000",
@@ -7,6 +7,7 @@ import {
7
7
  type DiscoverServicesOptions,
8
8
  type ServiceListing,
9
9
  } from "../discovery/registry.js";
10
+ import { checkListedRateBand } from "../discovery/rate-band.js";
10
11
  import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
11
12
  import {
12
13
  isCheckpointStream,
@@ -206,6 +207,19 @@ export function createLangChainVerifiedStreamTools(
206
207
  throw new Error(`mandate denied: ${decision.reason ?? "unknown"}`);
207
208
  }
208
209
 
210
+ // The rate must sit inside the band the operator SIGNED, or the
211
+ // seller's gateway refuses every request on the stream while the
212
+ // deposit stays escrowed. Fails closed when the registry cannot be read.
213
+ const band = await checkListedRateBand(
214
+ () => discoverImpl(),
215
+ input.serviceRef,
216
+ BigInt(input.ratePerSecondUsdc),
217
+ "ratePerSecondUsdc",
218
+ );
219
+ if (!band.ok) {
220
+ throw new Error(band.message);
221
+ }
222
+
209
223
  const result = await options.agentClient.openVerifiedStream({
210
224
  operator: input.operator as `0x${string}`,
211
225
  serviceRef: input.serviceRef as `0x${string}`,