@absol-labs/agent 0.3.2 → 0.5.0

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 (59) hide show
  1. package/README.md +378 -168
  2. package/dist/capability/invocation-capability.d.ts +184 -0
  3. package/dist/capability/invocation-capability.d.ts.map +1 -0
  4. package/dist/capability/invocation-capability.js +183 -0
  5. package/dist/capability/invocation-capability.js.map +1 -0
  6. package/dist/frameworks/agentkit.js +2 -2
  7. package/dist/frameworks/agentkit.js.map +1 -1
  8. package/dist/frameworks/eliza.js +2 -2
  9. package/dist/frameworks/eliza.js.map +1 -1
  10. package/dist/frameworks/langchain.js +2 -2
  11. package/dist/frameworks/langchain.js.map +1 -1
  12. package/dist/gateway/caller-auth-gateway.d.ts +106 -0
  13. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -0
  14. package/dist/gateway/caller-auth-gateway.js +189 -0
  15. package/dist/gateway/caller-auth-gateway.js.map +1 -0
  16. package/dist/gateway/http-server.d.ts +49 -0
  17. package/dist/gateway/http-server.d.ts.map +1 -0
  18. package/dist/gateway/http-server.js +227 -0
  19. package/dist/gateway/http-server.js.map +1 -0
  20. package/dist/gateway/server-entry.d.ts +2 -0
  21. package/dist/gateway/server-entry.d.ts.map +1 -0
  22. package/dist/gateway/server-entry.js +30 -0
  23. package/dist/gateway/server-entry.js.map +1 -0
  24. package/dist/index.d.ts +7 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +7 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/mcp/server.js +2 -2
  29. package/dist/mcp/server.js.map +1 -1
  30. package/dist/sdk/invoke.d.ts +122 -0
  31. package/dist/sdk/invoke.d.ts.map +1 -0
  32. package/dist/sdk/invoke.js +158 -0
  33. package/dist/sdk/invoke.js.map +1 -0
  34. package/dist/wallet/lifecycle.d.ts +101 -0
  35. package/dist/wallet/lifecycle.d.ts.map +1 -0
  36. package/dist/wallet/lifecycle.js +57 -0
  37. package/dist/wallet/lifecycle.js.map +1 -0
  38. package/dist/zktls/reclaim.d.ts +84 -5
  39. package/dist/zktls/reclaim.d.ts.map +1 -1
  40. package/dist/zktls/reclaim.js +47 -3
  41. package/dist/zktls/reclaim.js.map +1 -1
  42. package/dist/zktls/t2-delivery-proof.d.ts +296 -0
  43. package/dist/zktls/t2-delivery-proof.d.ts.map +1 -0
  44. package/dist/zktls/t2-delivery-proof.js +336 -0
  45. package/dist/zktls/t2-delivery-proof.js.map +1 -0
  46. package/package.json +6 -3
  47. package/src/capability/invocation-capability.ts +255 -0
  48. package/src/frameworks/agentkit.ts +2 -2
  49. package/src/frameworks/eliza.ts +2 -2
  50. package/src/frameworks/langchain.ts +2 -2
  51. package/src/gateway/caller-auth-gateway.ts +328 -0
  52. package/src/gateway/http-server.ts +325 -0
  53. package/src/gateway/server-entry.ts +38 -0
  54. package/src/index.ts +103 -0
  55. package/src/mcp/server.ts +2 -2
  56. package/src/sdk/invoke.ts +313 -0
  57. package/src/wallet/lifecycle.ts +158 -0
  58. package/src/zktls/reclaim.ts +88 -8
  59. package/src/zktls/t2-delivery-proof.ts +559 -0
package/README.md CHANGED
@@ -1,183 +1,386 @@
1
1
  # @absol-labs/agent
2
2
 
3
- The **Metrik agent layer** how AI agents hire and pay for verified services through
4
- Metrik, safely. This is the **go-to-market wedge** and the investor headline:
5
- _verified, per-second, conditional payments for autonomous agents, built on x402._
6
- The layer is operator/infra-agnostic by design: _"x402 proves the payment, Metrik
7
- proves the delivery."_
3
+ The Metrik agent layer: everything an autonomous agent needs to **hire and pay for
4
+ third-party services safely**, where money is released only for delivery that is
5
+ independently verified. Metrik is an infra-agnostic verified-service marketplace and
6
+ payment rail for AI agents, settling in USDC on Base. The tagline is the design
7
+ contract: **x402 proves the payment, Metrik proves the delivery** — and delivery,
8
+ honestly scoped, is what this package makes an agent able to enforce, never
9
+ "proven-correct output."
10
+
11
+ It builds on [`@absol-labs/sdk`](https://github.com/Absol-Labs/metrik-sdk), which speaks
12
+ to the on-chain metered escrow. This layer wraps that client in the guardrails and
13
+ integration surfaces an agent actually needs: owner-signed spend mandates that gate every
14
+ fund move, an x402 facilitator that turns a `402` into a verified stream, an MCP server
15
+ that exposes hire/monitor/reclaim as agent tools, adapters for the major agent frameworks,
16
+ Reclaim consumer zkTLS for buyer-side delivery proofs, and non-custodial Coinbase CDP
17
+ wallets so Metrik never holds a key.
18
+
19
+ Live on Base Sepolia (chainId `84532`), testnet only. App: [app.metrik.live](https://app.metrik.live).
20
+ Docs: [metrik.live](https://metrik.live). Frontdoor: [github.com/Absol-Labs](https://github.com/Absol-Labs).
21
+
22
+ ## Install
8
23
 
9
- Built on [`@absol-labs/sdk`](https://github.com/Absol-Labs/streamproof-sdk); it turns
10
- the protocol into something an agent can actually use.
11
-
12
- > **New here?** [`docs/quickstart.md`](./docs/quickstart.md) — hire a verified,
13
- > auto-refunding service in under 10 minutes (install → wallet → mandate → hire →
14
- > monitor → settle), with copy-pasteable examples for all four integration surfaces.
15
-
16
- > **Why agents are the wedge** + the full agent journey:
17
- > [`product-strategy.md`](https://github.com/Absol-Labs/streamproof-protocol/blob/main/docs/product-strategy.md)
18
- > and [`production-flow.md`](https://github.com/Absol-Labs/streamproof-protocol/blob/main/docs/production-flow.md)
19
- > (Journey 2 — Atlas). The agent's own zkTLS delivery proof is **L2** (consumer zkTLS) in
20
- > [`verification-model.md`](https://github.com/Absol-Labs/streamproof-protocol/blob/main/docs/verification-model.md).
21
- > The explicit agent-layer security review lives in
22
- > [docs/threat-model.md](./docs/threat-model.md).
24
+ ```bash
25
+ pnpm add @absol-labs/agent
26
+ ```
23
27
 
24
- ## What's here
28
+ The package is **ESM-only** (`"type": "module"`) and targets **Node `>=20 <21`**. It ships
29
+ `@absol-labs/sdk`, `@absol-labs/shared`, `viem`, `zod`, the Reclaim zkTLS SDKs, the
30
+ Coinbase CDP SDK, and the MCP SDK as dependencies. The agent-framework packages
31
+ (`@coinbase/agentkit`, `@elizaos/core`, `@langchain/core`) are **optional peer
32
+ dependencies** — install only the one you use.
33
+
34
+ ## What it does
35
+
36
+ **Spend mandates** are the "an agent can't run away with the wallet" guarantee. The wallet
37
+ owner signs an EIP-712 `SpendMandate` carrying per-stream, total, rate, and duration caps
38
+ plus an operator allowlist and expiry. Every buyer-side fund move checks the mandate first
39
+ and hard-stops on denial; `MandateDecision` returns a machine-readable reason. Buyer-recovery
40
+ actions (close, reclaim) verify only the signature — never expiry or caps — so a lapsed
41
+ mandate can never strand the buyer's own funds in escrow.
42
+
43
+ **The x402 facilitator** (`VerifiedStreamX402Facilitator`) turns an HTTP `402` challenge
44
+ into a real on-chain stream open instead of a one-shot payment. The agent receives a
45
+ verified-streaming requirement, signs a payload bound to its mandate, and the facilitator
46
+ opens the escrowed stream through the SDK.
47
+
48
+ **The MCP server** exposes `discover_services`, `hire_verified_service`,
49
+ `check_stream_status`, `reclaim_unspent`, `list_streams`, and — when Reclaim credentials are
50
+ configured — `prove_https_response`, so any MCP-native agent transacts directly. It reads
51
+ settlement config from the environment and never accepts a raw key as a tool argument.
52
+
53
+ **Framework adapters** put the same mandate-gated client behind AgentKit, ElizaOS,
54
+ LangChain, and CrewAI, each importable from its own subpath.
55
+
56
+ **Consumer zkTLS** (`ReclaimConsumerProofService`) lets the buyer prove the exact HTTPS
57
+ response it received, bound to its stream context. This is the L2 delivery signal in
58
+ Metrik's verification stack — dispute evidence that complements, and never replaces, the
59
+ oracle's L1 observations.
60
+
61
+ **Non-custodial wallets**: `resolveAgentWallet()` accepts either an injected viem signer or
62
+ a provisioned Coinbase CDP Server Wallet v2 account. CDP mode uses Coinbase-managed remote
63
+ signing, so no private key is ever read or exported.
64
+
65
+ ## Usage
66
+
67
+ ### Sign and check a spend mandate
68
+
69
+ ```ts
70
+ import { privateKeyToAccount } from "viem/accounts";
71
+ import {
72
+ spendMandateSchema,
73
+ signedSpendMandateSchema,
74
+ createSpendMandateTypedData,
75
+ checkMandate,
76
+ } from "@absol-labs/agent";
77
+
78
+ const owner = privateKeyToAccount(
79
+ process.env.METRIK_AGENT_PRIVATE_KEY as `0x${string}`,
80
+ );
81
+
82
+ const mandate = spendMandateSchema.parse({
83
+ maxPerStreamUsdc: 5_000_000n, // 5 USDC (6 decimals)
84
+ maxTotalUsdc: 50_000_000n,
85
+ maxRatePerSecondUsdc: 1_000n,
86
+ maxDurationSeconds: 86_400,
87
+ allowedOperators: ["0x28ea4eF61ac4cca3ed6a64dBb5b2D4be1aDC9814"],
88
+ expiresAt: 2_000_000_000,
89
+ });
90
+
91
+ const unsigned = {
92
+ mandateId: `0x${"77".repeat(32)}` as const,
93
+ owner: owner.address,
94
+ chainId: 84532,
95
+ issuedAt: Math.floor(Date.now() / 1000),
96
+ mandate,
97
+ };
98
+
99
+ const signature = await owner.signTypedData(
100
+ createSpendMandateTypedData(unsigned),
101
+ );
102
+ const signedMandate = signedSpendMandateSchema.parse({
103
+ ...unsigned,
104
+ signature,
105
+ });
106
+
107
+ // Every fund-moving action gates on this. Fail-closed on any denial.
108
+ const decision = await checkMandate(
109
+ signedMandate,
110
+ {
111
+ operator: "0x28ea4eF61ac4cca3ed6a64dBb5b2D4be1aDC9814",
112
+ budgetUsdc: 2_000_000n,
113
+ ratePerSecondUsdc: 500n,
114
+ maxDurationSeconds: 3_600,
115
+ },
116
+ 0n, // spentSoFarUsdc under this mandate
117
+ { nowSeconds: Math.floor(Date.now() / 1000) },
118
+ );
119
+
120
+ if (!decision.allowed) throw new Error(`mandate denied: ${decision.reason}`);
121
+ ```
25
122
 
123
+ ### Agent wallet lifecycle: create/restore → fund → sign
124
+
125
+ An agent gets a usable wallet entirely through this package — no out-of-band key. `resolveAgentWallet()`
126
+ accepts an injected viem private key OR a Coinbase CDP config; the CDP path is **create-or-restore**:
127
+ the same `ownerName` always resolves to the same CDP-managed address, so re-running with the same env
128
+ restores the identical wallet instead of minting a new one.
129
+
130
+ ```ts
131
+ import {
132
+ resolveAgentWallet,
133
+ parseAgentWalletEnv,
134
+ getWalletBalances,
135
+ faucetHint,
136
+ requestCdpFaucet,
137
+ } from "@absol-labs/agent";
138
+ import { MetrikClient } from "@absol-labs/sdk";
139
+ import { CdpClient } from "@coinbase/cdp-sdk";
140
+
141
+ // 1) Create or restore, from env (CDP_API_KEY_ID/CDP_API_KEY_SECRET/CDP_WALLET_SECRET +
142
+ // METRIK_AGENT_CDP_OWNER_NAME, or METRIK_AGENT_PRIVATE_KEY for an injected key).
143
+ const wallet = await resolveAgentWallet(parseAgentWalletEnv());
144
+ console.log("address:", wallet.account.address);
145
+
146
+ // 2) Read balance.
147
+ const metrik = MetrikClient.baseSepolia({ account: wallet.account });
148
+ const balances = await getWalletBalances({
149
+ publicClient: metrik.publicClient,
150
+ address: wallet.account.address,
151
+ usdc: metrik.config.usdc,
152
+ });
153
+
154
+ // 3) Fund (testnet only) — a faucet hint, or request funds programmatically for a CDP wallet.
155
+ console.log(faucetHint(84532)); // { cdpFaucetUrl, usdcFaucetUrl, ... }
156
+ if (wallet.source === "cdp" && balances.nativeWei === 0n) {
157
+ const cdp = new CdpClient({
158
+ /* same CDP_* creds */
159
+ });
160
+ await requestCdpFaucet({
161
+ cdp,
162
+ address: wallet.account.address,
163
+ token: "eth",
164
+ });
165
+ }
166
+
167
+ // 4) Sign — `wallet.account` is a plain viem `Account`; no extra wrapper needed.
168
+ const signature = await wallet.account.signTypedData?.(/* ... */);
26
169
  ```
27
- src/
28
- mandates/ spend mandates per-stream/total/rate/duration caps + operator
29
- allowlists. The "agents can't run away with the wallet" guarantee.
30
- sdk/ mandate-gated wrapper over @absol-labs/sdk for open/status/claim/reclaim.
31
- x402/ Metrik as an x402 verified-streaming payment scheme / facilitator.
32
- mcp/ an MCP server exposing hire/monitor/reclaim as agent tools.
33
- docs/
34
- quickstart.md zerohired verified service in <10 min (start here).
35
- agent-layer.md the product design + investor narrative.
36
- crewai.md CrewAI's official MCP integration path for Metrik tools.
37
- eliza.md ElizaOS plugin/actions for hire/status/reclaim.
38
- threat-model.md the explicit autonomous-spend / key-custody review.
39
- langchain.md the LangChain tool adapter and example loop.
170
+
171
+ `getWalletBalances` and `faucetHint`/`requestCdpFaucet` are deliberately thin: they don't introduce a
172
+ second wallet system, they just fill in the "read balance" and "get testnet funds" gaps around the
173
+ existing `resolveAgentWallet()` / `createWalletBackedAgentClient()` surface. `faucetHint` and
174
+ `requestCdpFaucet` only support Base Sepolia (`84532`) Metrik is testnet-only, single-chain.
175
+
176
+ See [`scripts/e2e-hire.ts`](./scripts/e2e-hire.ts) for the full journey (create/restore wallet → fund →
177
+ hireinvoke settle) run against real Base Sepolia infrastructure: `pnpm e2e:hire` (needs real CDP
178
+ creds see `.env.example`).
179
+
180
+ ### Hire via x402: `402` challenge → signed payload → open stream
181
+
182
+ ```ts
183
+ import {
184
+ VerifiedStreamX402Facilitator,
185
+ encodeX402PayloadHeader,
186
+ } from "@absol-labs/agent";
187
+
188
+ const facilitator = new VerifiedStreamX402Facilitator({
189
+ sdkConfig, // @absol-labs/sdk StreamProofClientConfig (chain, transport, account, escrow, usdc)
190
+ });
191
+
192
+ const challenge = facilitator.challenge({
193
+ operator: "0x28ea4eF61ac4cca3ed6a64dBb5b2D4be1aDC9814",
194
+ serviceRef: `0x${"ab".repeat(32)}`,
195
+ chainId: 84532,
196
+ escrow: sdkConfig.escrow,
197
+ ratePerSecondUsdc: 500n,
198
+ maxDurationSeconds: 3_600,
199
+ maxBudgetUsdc: 2_000_000n,
200
+ });
201
+
202
+ const header = encodeX402PayloadHeader({ challenge, signedMandate });
203
+ const { streamId } = await facilitator.open(header); // mandate-gated, opens on-chain
40
204
  ```
41
205
 
42
- ## The four pieces
43
-
44
- 1. **x402 integration** — an agent hits a `402`, gets back a _verified-streaming_
45
- payment requirement, and opens a stream instead of a one-shot payment. Reuses
46
- the `402` negotiation surface and opens the on-chain stream through the SDK.
47
- 2. **MCP server** exposes "hire verified compute / get status / reclaim" as MCP
48
- tools so any MCP agent (Claude, etc.) transacts natively. When Reclaim creds are
49
- configured, it also exposes `prove_https_response` for public-response zkTLS proofs
50
- tied to a tracked stream.
51
- 3. **Framework tools** — LangChain, ElizaOS, and CrewAI now sit on top of the same
52
- mandate-gated client. CrewAI consumes the existing MCP lane via its official MCP
53
- integration; ElizaOS gets a direct plugin/action surface.
54
- 4. **Spend mandates** — owner-signed EIP-712 budget/intent guardrails (AP2-style):
55
- caps and allowlists enforced before any fund-moving action, with expiry and
56
- local revocation checks.
57
- 5. **zkTLS delivery proofs (L2, consumer zkTLS)** — the agent can now produce a Reclaim-backed proof
58
- of the exact HTTPS response it consumed, bound to buyer/stream context and intended
59
- to complement, not replace, oracle L1 evidence.
60
-
61
- Current real chain surface:
62
-
63
- - `VerifiedStreamAgentClient` wraps `@absol-labs/sdk` for `open`, `status`, `claim`,
64
- `close`, and `reclaim`.
65
- - All autonomous fund-moving helpers (`openVerifiedStream()`, `claimStream()`,
66
- `closeStream()`, `reclaimStream()`) enforce signed-mandate verification first and
67
- hard-stop on denial.
68
- - `VerifiedStreamX402Facilitator` turns an HTTP `402` challenge into a real verified
69
- stream open on Base Sepolia.
70
- - `createVerifiedStreamMcpServer()` exposes real MCP tools for `hire_verified_service`,
71
- `check_stream_status`, `reclaim_unspent`, and `list_streams`.
72
- - `ReclaimConsumerProofService` generates a verifiable zkTLS proof of a consumed HTTPS
73
- response, hashing the exact request/match/redaction policy before local verification.
74
- - `VerifiedStreamX402Facilitator.proveConsumedHttpsResponse()` binds that proof to the
75
- same buyer/mandate context that opened the stream.
76
- - If Reclaim env vars are configured, `createVerifiedStreamMcpServer()` also exposes
77
- `prove_https_response` for tracked streams. The MCP tool intentionally excludes secret
78
- headers/cookies so credentials do not pass through model-visible tool arguments.
79
- - `createLangChainVerifiedStreamTools()` exposes the same hire/status/reclaim flow as
80
- LangChain tools, including a `shouldStop` status signal for paused streams.
81
- - `createMetrikElizaPlugin()` (alias `metrikElizaPlugin`) exposes the hire/status/reclaim/close
82
- flow as an ElizaOS plugin — canonical `HIRE_VERIFIED_SERVICE`, `CHECK_STREAM_STATUS`,
83
- `RECLAIM_UNSPENT`, and `CLOSE_STREAM` actions with structured results and fail-closed
84
- payload parsing. See [`examples/metrik-character.ts`](examples/metrik-character.ts).
85
- - `createCrewAiVerifiedStreamMcpConfig()` emits a CrewAI `MCPServerStdio` config for
86
- the existing mandate-gated MCP server, with a tool filter limited to verified-stream
87
- operations.
88
- - `resolveAgentWallet()` accepts either an injected viem signer or a provisioned
89
- Coinbase CDP Server Wallet v2 account, then feeds that signer into the existing
90
- agent/SDK path without ever exporting a private key.
91
- - Model A is infra-agnostic: a stream targets a seller payout wallet + `serviceRef`
92
- only (no service-network field). Settlement is single-chain on Base Sepolia.
93
-
94
- ## Wallet modes
95
-
96
- The agent layer now supports two wallet sources:
97
-
98
- - injected viem account via `METRIK_AGENT_PRIVATE_KEY`
99
- - Coinbase CDP Server Wallet v2 via `CDP_API_KEY_ID`, `CDP_API_KEY_SECRET`,
100
- `CDP_WALLET_SECRET`, and `METRIK_AGENT_CDP_OWNER_NAME`
101
-
102
- CDP mode uses Coinbase-managed remote signing. Metrik never reads or exports the
103
- wallet private key. If `METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT=true`, the agent also
104
- provisions a per-agent ERC-4337 smart account owned by that CDP wallet for the later
105
- gas-abstraction path.
106
-
107
- ## MCP server
108
-
109
- The MCP server runs over stdio and reads its settlement config from env. It never
206
+ The scheme name is exported as `X402_SCHEME`; `parseX402ChallengeJson`,
207
+ `parseX402PayloadHeader`, and `encodeX402PayloadHeader` handle the wire format on both sides.
208
+
209
+ ### Run the MCP server
210
+
211
+ The server runs over stdio and reads its settlement config from the environment; it never
110
212
  asks for a raw key in a tool argument.
111
213
 
112
214
  ```bash
113
- cp .env.example .env
114
- pnpm mcp:stdio
215
+ pnpm mcp:stdio # or: pnpm mcp:http
216
+ ```
217
+
218
+ Programmatically:
219
+
220
+ ```ts
221
+ import {
222
+ METRIK_MCP_TOOLS,
223
+ startVerifiedStreamMcpServerStdio,
224
+ createVerifiedStreamMcpServerFromEnv,
225
+ } from "@absol-labs/agent";
226
+
227
+ console.log(METRIK_MCP_TOOLS.map((t) => t.name));
228
+ // discover_services, hire_verified_service, check_stream_status,
229
+ // reclaim_unspent, list_streams, prove_https_response
230
+
231
+ await startVerifiedStreamMcpServerStdio(); // reads process.env, connects stdio
232
+ // or, for custom transport wiring:
233
+ // const runtime = await createVerifiedStreamMcpServerFromEnv();
234
+ ```
235
+
236
+ Core env surface: `METRIK_AGENT_RPC_URL`, `METRIK_AGENT_ESCROW`, `METRIK_AGENT_USDC`,
237
+ `METRIK_AGENT_CHAIN_ID` (`84532`), and one wallet source — either
238
+ `METRIK_AGENT_PRIVATE_KEY` (injected signer) or the CDP set (`CDP_API_KEY_ID`,
239
+ `CDP_API_KEY_SECRET`, `CDP_WALLET_SECRET`, `METRIK_AGENT_CDP_OWNER_NAME`). Set
240
+ `RECLAIM_APP_ID` / `RECLAIM_APP_SECRET` to enable the `prove_https_response` zkTLS tool.
241
+ See [`docs/quickstart.md`](./docs/quickstart.md) for the full list.
242
+
243
+ ## Closed loop: `hire → invoke → (prove) → settle`
244
+
245
+ Opening and funding a stream only pays for a service - it does not, on its own,
246
+ authorize the buyer to _call_ it. The caller-auth gateway (metrik-protocol#62/#63/#64)
247
+ closes that gap: a seller runs a small reverse-proxy gateway in front of its real
248
+ service, and a buyer's SDK calls it directly with a short-lived, single-use,
249
+ stream-bound `InvocationCapability` - no separate credential, no manual API key.
250
+
251
+ **Seller side** - front any HTTP service with the reference gateway:
252
+
253
+ ```ts
254
+ import {
255
+ CallerAuthGateway,
256
+ createCallerAuthGatewayServer,
257
+ } from "@absol-labs/agent";
258
+
259
+ const gateway = new CallerAuthGateway({
260
+ escrowAddress: "0x...", // the StreamEscrowV2 this service's streams settle on
261
+ rpcUrl: process.env.METRIK_GATEWAY_RPC_URL!,
262
+ serviceRef: "0x...", // this gateway's serviceRef - must match the listing
263
+ });
264
+
265
+ const server = createCallerAuthGatewayServer({
266
+ gateway,
267
+ upstreamUrl: "https://my-real-service.example.com",
268
+ });
269
+ await server.listen(8787);
115
270
  ```
116
271
 
117
- Env surface:
118
-
119
- - `METRIK_AGENT_RPC_URL`
120
- - `METRIK_AGENT_ESCROW`
121
- - `METRIK_AGENT_USDC`
122
- - `METRIK_AGENT_PRIVATE_KEY` (optional injected signer mode)
123
- - `CDP_API_KEY_ID`
124
- - `CDP_API_KEY_SECRET`
125
- - `CDP_WALLET_SECRET`
126
- - `METRIK_AGENT_CDP_OWNER_NAME`
127
- - `METRIK_AGENT_CDP_SMART_ACCOUNT_NAME`
128
- - `METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT`
129
- - `METRIK_AGENT_CHAIN_ID` (`84532` in Phase 2)
130
- - `METRIK_AGENT_CHAIN_NAME`
131
- - `METRIK_AGENT_MCP_STATE_FILE` (optional JSON registry for `list_streams`)
132
- - `RECLAIM_APP_ID` (optional, enables zkTLS proof generation)
133
- - `RECLAIM_APP_SECRET` (optional, enables zkTLS proof generation)
134
- - `METRIK_AGENT_RECLAIM_LOGS` (optional, defaults to `false`)
135
-
136
- ## zkTLS / Reclaim (L2, consumer zkTLS)
137
-
138
- Use the programmatic proof service or the x402 facilitator when the consumed request
139
- needs private headers/cookies. Use the MCP tool only for public request inputs; it
140
- looks up stream context locally and avoids sending secrets through the model/tool layer.
141
-
142
- Every proof is verified locally against the exact URL/method/body/match/redaction
143
- policy the agent declared. This is an **L2 delivery signal** (consumer zkTLS) only: the verifier should
144
- cross-check it with L1/oracle observations before any settlement decision.
145
-
146
- ## End-to-end journey (hire -> stream -> settle)
147
-
148
- The whole loop is proven end to end against a local node in
149
- [`test/e2e.int.test.ts`](test/e2e.int.test.ts) and scripted for integrators in
150
- [`examples/hire-stream-settle.ts`](examples/hire-stream-settle.ts):
151
-
152
- 1. **Wallet** — a non-custodial injected-key viem account (credential-free; swap
153
- in a Coinbase CDP wallet via `createWalletBackedAgentClient({ cdp: ... })`).
154
- 2. **Mandate** — the owner signs an EIP-712 spend mandate (caps + operator
155
- allowlist); every buyer-side fund move is mandate-gated and fail-closed.
156
- 3. **Hire** — the x402 facilitator turns a `402` challenge into an escrowed
157
- `openStream` on the hardened `StreamEscrow`.
158
- 4. **Verify** — an M-of-N oracle quorum submits real EIP-712 `Delivered`
159
- attestations; USDC accrues only for verified seconds (`checkedAt <=
160
- block.timestamp` is enforced on-chain).
161
- 5. **Settle** — the operator claims its earned USDC net of the protocol fee, the
162
- buyer closes and reclaims the unspent balance, a terminal `SlaReceiptIssued`
163
- event is emitted, and every cent conserves exactly.
164
-
165
- The test also covers the **failure branch**: two failed checks auto-pause the
166
- stream, the buyer reclaims, and the operator can still claim only what it earned
167
- before the outage — buyer-favouring by construction.
168
-
169
- Run the example (needs Foundry `anvil`):
272
+ Or run the ready-made standalone server from env (`METRIK_GATEWAY_ESCROW_ADDRESS`,
273
+ `METRIK_GATEWAY_RPC_URL`, `METRIK_GATEWAY_SERVICE_REF`, `METRIK_GATEWAY_UPSTREAM_URL`,
274
+ optional `METRIK_GATEWAY_CHAIN_ID` / `METRIK_GATEWAY_PORT` / `METRIK_GATEWAY_HOST`):
170
275
 
171
276
  ```bash
172
- anvil --silent &
173
- pnpm tsx examples/hire-stream-settle.ts
277
+ pnpm gateway
174
278
  ```
175
279
 
176
- > The SLA receipt carries the raw material for on-chain reputation
177
- > (`verifiedSeconds`, `accrued`, `finalStatus`, `consecutiveFailures`). Feeding it
178
- > into `@absol-labs/shared`'s `slaReceiptRecordFromEvent` + `aggregateReputation`
179
- > (protocol#38) is a one-liner once the installed `@absol-labs/shared` is bumped to
180
- > the version that exports them (the pinned `0.3.0` does not yet).
280
+ Every request must carry a valid capability; every failure mode (missing header, bad
281
+ signature, wrong buyer, closed/expired/underfunded stream, replayed nonce, wrong
282
+ method/path, wrong `serviceRef`) is rejected with a distinct machine-readable `reason`
283
+ and a `402`/`403` - the real upstream is never touched on a rejection. Access is revoked
284
+ automatically: once a stream is closed, expired, or reclaimed, the next on-chain read
285
+ fails closed with no extra bookkeeping.
286
+
287
+ **Buyer side** - after `hireVerifiedService`/`open()`, call the purchased service
288
+ directly:
289
+
290
+ ```ts
291
+ import { privateKeyToAccount } from "viem/accounts";
292
+ import { createSdkInvokeStreamReader, invoke } from "@absol-labs/agent";
293
+
294
+ const buyer = privateKeyToAccount(
295
+ process.env.METRIK_AGENT_PRIVATE_KEY as `0x${string}`,
296
+ );
297
+ const streamReader = createSdkInvokeStreamReader({
298
+ escrowAddress: "0x...",
299
+ rpcUrl: process.env.METRIK_AGENT_RPC_URL!,
300
+ });
301
+
302
+ const { response } = await invoke(
303
+ streamId, // from hireVerifiedService()
304
+ {
305
+ method: "POST",
306
+ path: "/v1/infer",
307
+ body: JSON.stringify({ prompt: "..." }),
308
+ },
309
+ {
310
+ streamReader,
311
+ buyer,
312
+ domain: { chainId: 84532, verifyingContract: "0x..." }, // same escrow as above
313
+ serviceBaseUrl: "https://gateway.example.com", // the seller's gateway, not the real upstream
314
+ },
315
+ );
316
+
317
+ console.log(await response.json());
318
+ ```
319
+
320
+ `invoke()` loads the stream, fails closed BEFORE any network call if it is not active,
321
+ is expired, or does not belong to the signing account, then builds and signs a
322
+ capability scoped to exactly that one `method`+`path` (short expiry, single-use nonce)
323
+ and attaches it as the `x-metrik-capability` header. Use `capabilityFor()` directly if
324
+ you only need the signed capability without the SDK also performing the `fetch`.
325
+
326
+ For a T2 (consumer-zkTLS) service, `invokeWithT2DeliveryProof()` attaches the SAME
327
+ capability to the exact request the buyer's Reclaim attestor proves, so the
328
+ capability-authorized call IS the delivery evidence - usage and proof become one action:
329
+
330
+ ```ts
331
+ import { invokeWithT2DeliveryProof } from "@absol-labs/agent";
332
+
333
+ const { t2 } = await invokeWithT2DeliveryProof(streamId, {
334
+ streamReader,
335
+ buyer,
336
+ domain: { chainId: 84532, verifyingContract: "0x..." },
337
+ request: {
338
+ url: "https://gateway.example.com/v1/infer",
339
+ method: "POST",
340
+ responseMatches: [{ type: "regex", value: '"result":"(?<result>.*)"' }],
341
+ nonceInjection: { in: "query", name: "nonce" },
342
+ },
343
+ intervalIndex: 0n,
344
+ nonce: deliveryNonce, // from GET /delivery/nonce
345
+ });
346
+
347
+ // t2.body is the exact POST /delivery/proof request payload.
348
+ ```
349
+
350
+ ## Consumer zkTLS (delivery proofs)
351
+
352
+ `ReclaimConsumerProofService` (and `createReclaimConsumerProofServiceFromEnv`) generate a
353
+ Reclaim-backed proof of the exact HTTPS response the agent consumed, verified locally
354
+ against the declared URL, method, body, match, and redaction policy before it is trusted.
355
+ `VerifiedStreamX402Facilitator.proveConsumedHttpsResponse()` binds that proof to the same
356
+ buyer/mandate context that opened the stream. When credentials require private headers or
357
+ cookies, use the programmatic service or the facilitator directly rather than the MCP tool,
358
+ so secrets never pass through model-visible tool arguments. Treat the result as an L2
359
+ signal: the verifier cross-checks it with L1/oracle evidence before any settlement decision.
360
+
361
+ ## Framework adapters
362
+
363
+ Framework adapters are intentionally not re-exported from the top-level barrel — some pull
364
+ heavy optional dependency graphs — so import each from its own subpath:
365
+
366
+ ```ts
367
+ import { metrikActionProvider } from "@absol-labs/agent/agentkit";
368
+ import { metrikElizaPlugin } from "@absol-labs/agent/eliza";
369
+ import { createLangChainVerifiedStreamTools } from "@absol-labs/agent/langchain";
370
+ import { createCrewAiVerifiedStreamMcpConfig } from "@absol-labs/agent/crewai";
371
+ ```
372
+
373
+ Per-framework guides: [`docs/langchain.md`](./docs/langchain.md),
374
+ [`docs/eliza.md`](./docs/eliza.md), [`docs/crewai.md`](./docs/crewai.md).
375
+
376
+ ## Requirements
377
+
378
+ - Node **`>=20 <21`**, package manager `pnpm@9.15.x`.
379
+ - **ESM only** — this package has no CommonJS build; consume it from an ESM context.
380
+ - Framework adapters need their matching optional peer dependency installed. Known caveat:
381
+ the AgentKit adapter pulls a heavy graph (`@coinbase/agentkit` / ZeroDev) that can break a
382
+ flat `npm install`; prefer `pnpm` and import the adapter only from its `@absol-labs/agent/agentkit`
383
+ subpath.
181
384
 
182
385
  ## Develop
183
386
 
@@ -189,8 +392,15 @@ pnpm test
189
392
  pnpm build
190
393
  ```
191
394
 
192
- > The x402 facilitator, zkTLS proof path, signed-mandate engine, MCP server, and
193
- > framework adapters are all real and tested in-repo. The `*.int.test.ts`
194
- > integration suites deploy the hardened `StreamEscrow` on a throwaway `anvil`
195
- > node and self-skip cleanly when `anvil` is not on `PATH`, so default CI stays
196
- > green.
395
+ The x402 facilitator, zkTLS proof path, signed-mandate engine, MCP server, and framework
396
+ adapters are all real and tested in-repo. The `*.int.test.ts` integration suites deploy the
397
+ hardened `StreamEscrow` on a throwaway `anvil` node and self-skip when `anvil` is not on
398
+ `PATH`, so default CI stays green. A full hire stream settle walkthrough lives in
399
+ [`examples/hire-stream-settle.ts`](examples/hire-stream-settle.ts).
400
+
401
+ ## Links
402
+
403
+ - Frontdoor: [github.com/Absol-Labs](https://github.com/Absol-Labs)
404
+ - Docs: [metrik.live](https://metrik.live) · App: [app.metrik.live](https://app.metrik.live)
405
+ - SDK: [`@absol-labs/sdk`](https://github.com/Absol-Labs/metrik-sdk)
406
+ - Quickstart: [`docs/quickstart.md`](./docs/quickstart.md) · Design + narrative: [`docs/agent-layer.md`](./docs/agent-layer.md) · Security review: [`docs/threat-model.md`](./docs/threat-model.md)