twzrd-x402-gate 0.1.1 → 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.
- package/README.md +279 -47
- package/dist/auto-gate.d.ts +48 -0
- package/dist/auto-gate.d.ts.map +1 -0
- package/dist/auto-gate.js +34 -0
- package/dist/auto-gate.js.map +1 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +32 -6
- package/dist/config.js.map +1 -1
- package/dist/evaluate.d.ts +77 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +118 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/gate.d.ts +13 -0
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +22 -0
- package/dist/gate.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-hook.d.ts.map +1 -1
- package/dist/mcp-hook.js +27 -18
- package/dist/mcp-hook.js.map +1 -1
- package/dist/merchant-card.d.ts +52 -0
- package/dist/merchant-card.d.ts.map +1 -0
- package/dist/merchant-card.js +69 -0
- package/dist/merchant-card.js.map +1 -0
- package/dist/payto.d.ts +6 -0
- package/dist/payto.d.ts.map +1 -1
- package/dist/payto.js +17 -0
- package/dist/payto.js.map +1 -1
- package/dist/policy.d.ts +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +89 -7
- package/dist/policy.js.map +1 -1
- package/dist/quick.d.ts +48 -0
- package/dist/quick.d.ts.map +1 -0
- package/dist/quick.js +49 -0
- package/dist/quick.js.map +1 -0
- package/dist/sponsored.d.ts +51 -0
- package/dist/sponsored.d.ts.map +1 -0
- package/dist/sponsored.js +55 -0
- package/dist/sponsored.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/with-guard.d.ts +23 -0
- package/dist/with-guard.d.ts.map +1 -0
- package/dist/with-guard.js +76 -0
- package/dist/with-guard.js.map +1 -0
- package/dist/wrap-fetch.d.ts.map +1 -1
- package/dist/wrap-fetch.js +5 -3
- package/dist/wrap-fetch.js.map +1 -1
- package/package.json +7 -11
package/README.md
CHANGED
|
@@ -1,17 +1,43 @@
|
|
|
1
1
|
# twzrd-x402-gate
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`onPaymentRequested` hook. Fail-open by default so an unreachable preflight never hard-blocks a payment.
|
|
3
|
+
Agent-side x402 **firewall**. TWZRD sits in the path before any 402 payment leaves your
|
|
4
|
+
agent's wallet — free preflight + free merchant_card wash refuse before USDC moves.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
```typescript
|
|
7
|
+
import { installTwzrdAutoGate } from "twzrd-x402-gate";
|
|
8
|
+
import { wrapFetchWithPayment } from "@x402/svm";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
|
|
11
|
+
// Every 402 gated BEFORE your client signs → free preflight + free merchant_card wash check → throws on block.
|
|
12
|
+
const response = await payingFetch("https://api.exa.ai/search");
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Buyer flow (trustless, fail-open) — locked sequence
|
|
16
|
+
|
|
17
|
+
Canonical path for every agent that spends USDC on Solana x402:
|
|
18
|
+
|
|
19
|
+
| Step | Call | Cost | What gates pay |
|
|
20
|
+
|------|------|------|----------------|
|
|
21
|
+
| 1 | `POST /v1/intel/preflight` | free | `decision=block` → refuse (`twzrd_decision_block`). Score floor / optional `can_spend` also deny. |
|
|
22
|
+
| 2 | `GET /v1/intel/merchant_card/{payTo}` | free | `wash_flagged: true` → refuse by default (`twzrd_wash_flagged`). **Only tightens** step 1. |
|
|
23
|
+
| 3 | Optional paid trust | $0.05 / $0.001 | On `warn` or high-value: `GET /v1/intel/trust/{payTo}` or `quickCheck`. Never required for the free refuse path. |
|
|
24
|
+
| 4 | Pay (or refuse) | resource price | Only if steps 1–2 approved (and any opt-in paid escalate did not block). |
|
|
25
|
+
|
|
26
|
+
**Fail-open (no invent):**
|
|
27
|
+
- Preflight HTTP/network error → default fail-closed in gate 0.2+ (`TWZRD_FAIL_OPEN=true` restores legacy allow-on-outage).
|
|
28
|
+
- Merchant card unreachable / non-2xx / missing `wash_flagged` → `washFlagged=null` → **do not refuse on wash** (preflight decision stands).
|
|
29
|
+
- Only a successful card with `wash_flagged: true` triggers wash refuse or soft cap.
|
|
30
|
+
|
|
31
|
+
**Wash policy (exact):**
|
|
32
|
+
- Prior preflight deny → unchanged (wash never loosens a block).
|
|
33
|
+
- `refuseWashFlagged=false` or wash not true → keep preflight approval.
|
|
34
|
+
- `wash_flagged=true` + no cap → `approved=false`, `reason=twzrd_wash_flagged`, `verdict=block`.
|
|
35
|
+
- `wash_flagged=true` + `washMaxUsdc` set + `priceUsdc <= cap` → allow, `washCapped=true`, reason `twzrd_wash_capped_{price}_le_{cap}`.
|
|
36
|
+
- `wash_flagged=true` + price above cap (or price unknown) → refuse with `twzrd_wash_flagged_above_cap_*`.
|
|
37
|
+
|
|
38
|
+
**Order note:** `onWarnUpsell` (points at paid `/trust`) fires on preflight `warn` **before** the merchant_card wash check. A wash-flagged seller that preflighted as `warn` may still get the upsell hook, then be refused on step 2.
|
|
39
|
+
|
|
40
|
+
Dogfood (live, free only): `npm run wash-dogfood` → [`examples/wash-refuse-dogfood.ts`](./examples/wash-refuse-dogfood.ts).
|
|
15
41
|
|
|
16
42
|
## Install
|
|
17
43
|
|
|
@@ -19,34 +45,206 @@ AgentCash's `fetch` (it handles 402 internally) — verify before assuming a wra
|
|
|
19
45
|
npm install twzrd-x402-gate
|
|
20
46
|
```
|
|
21
47
|
|
|
22
|
-
##
|
|
48
|
+
## Quickstart: `installTwzrdAutoGate` (default-on)
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
`installTwzrdAutoGate` is the one-liner form of "guard the raw fetch, then hand it to your
|
|
51
|
+
x402 client." It takes a `payWrap` function — whatever composes your paying client on top of
|
|
52
|
+
a fetch — and returns a fetch that's already gated: a blocked seller throws before your client
|
|
53
|
+
ever gets a chance to sign.
|
|
25
54
|
|
|
26
|
-
```
|
|
27
|
-
import {
|
|
55
|
+
```typescript
|
|
56
|
+
import { installTwzrdAutoGate } from "twzrd-x402-gate";
|
|
57
|
+
import { wrapFetchWithPayment } from "@x402/svm";
|
|
28
58
|
|
|
29
|
-
const
|
|
59
|
+
const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
|
|
30
60
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
// x402 client attaches payment and retries as usual.
|
|
34
|
-
const res = await gatedFetch("https://merchant.example/paid");
|
|
61
|
+
// Use payingFetch everywhere you'd call a paid resource:
|
|
62
|
+
const response = await payingFetch("https://api.exa.ai/search");
|
|
35
63
|
```
|
|
36
64
|
|
|
37
|
-
|
|
65
|
+
`payWrap` receives the **guarded** fetch (the guard has already run by the time your client
|
|
66
|
+
sees a 402) — this is the only correct composition order. Building it the other way round
|
|
67
|
+
(guarding an already-paying fetch) is a no-op; see [Compatibility note](#compatibility-note).
|
|
68
|
+
Any x402 client that composes over an underlying `fetch` works the same way — swap in
|
|
69
|
+
whatever `payWrap` your client's API expects (agentcash, ClawRouter, PayAI, a custom
|
|
70
|
+
`@x402/svm` scheme, etc.).
|
|
38
71
|
|
|
39
|
-
|
|
40
|
-
|
|
72
|
+
Default **ON**. Disable with `TWZRD_AUTO_GATE=0` (env, deploy-time kill switch) or
|
|
73
|
+
`{ disabled: true }` (per-call, e.g. in tests) — the raw fetch is handed straight to
|
|
74
|
+
`payWrap`, unguarded.
|
|
41
75
|
|
|
42
|
-
|
|
43
|
-
|
|
76
|
+
What happens on every HTTP 402 the raw fetch returns:
|
|
77
|
+
1. Reads the Solana-network entry from `accepts[]` (falls back to first entry) to get the seller wallet.
|
|
78
|
+
2. Calls `POST /v1/intel/preflight` — free, no auth. `decision=block` (or score floor) throws — `payWrap`'s client never signs.
|
|
79
|
+
3. Calls `GET /v1/intel/merchant_card/{payTo}` — free, no auth. `wash_flagged: true` refuses by default (only tightens step 2; fail-open if the card is unreachable — no invent).
|
|
80
|
+
4. Otherwise returns the 402 to `payWrap`'s client, which pays normally.
|
|
81
|
+
|
|
82
|
+
Non-402 responses pass through unchanged.
|
|
83
|
+
|
|
84
|
+
### Lower-level: `withTwzrdGuard`
|
|
85
|
+
|
|
86
|
+
`installTwzrdAutoGate` is built on `withTwzrdGuard` — the fetch wrapper itself, if you want to
|
|
87
|
+
manage the raw/paying composition yourself:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { withTwzrdGuard } from "twzrd-x402-gate";
|
|
91
|
+
import { wrapFetchWithPayment } from "@x402/svm";
|
|
92
|
+
|
|
93
|
+
const raw = globalThis.fetch; // MUST still surface HTTP 402
|
|
94
|
+
const guarded = withTwzrdGuard(raw); // guard sits upstream
|
|
95
|
+
const safeFetch = wrapFetchWithPayment(guarded, buyerWallet);
|
|
96
|
+
|
|
97
|
+
const response = await safeFetch("https://api.exa.ai/search");
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
What the guard does on HTTP 402:
|
|
101
|
+
1. Reads the Solana-network entry from `accepts[]` (falls back to first entry) to get the seller wallet.
|
|
102
|
+
2. Free `POST /v1/intel/preflight` — `decision=block` / score floor deny.
|
|
103
|
+
3. Free `GET /v1/intel/merchant_card/{payTo}` — `wash_flagged:true` **refuses by default**
|
|
104
|
+
(`reason: twzrd_wash_flagged`). Fail-open if the card is unreachable (no invent).
|
|
105
|
+
4. If approved: returns the original 402 for the x402 client to pay.
|
|
106
|
+
|
|
107
|
+
Opt out of wash refuse: `withTwzrdGuard(fetch, { refuseWashFlagged: false })` or
|
|
108
|
+
`TWZRD_REFUSE_WASH_FLAGGED=0`. Soft cap instead of hard refuse: `washMaxUsdc` /
|
|
109
|
+
`TWZRD_WASH_MAX_USDC`.
|
|
110
|
+
|
|
111
|
+
Non-402 responses pass through unchanged.
|
|
112
|
+
|
|
113
|
+
### Auto-receipt on warn (revenue path)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const safeFetch = withTwzrdGuard(x402Fetch, {
|
|
117
|
+
autoReceipt: true, // on warn or allow, auto-buy the $0.05 TWZRD trust receipt
|
|
118
|
+
x402Fetch, // the paying fetch — TWZRD earns the fee on-chain
|
|
119
|
+
onReceipt: (receipt, tx) => {
|
|
120
|
+
// receipt is a twzrd_receipt (V6 + ERC-8004 reputation_credential)
|
|
121
|
+
console.log("Trust receipt captured:", tx);
|
|
122
|
+
},
|
|
44
123
|
});
|
|
45
124
|
```
|
|
46
125
|
|
|
47
|
-
|
|
126
|
+
`autoReceipt` is **off by default** — it spends the **buyer's** USDC, so you opt in. When on,
|
|
127
|
+
every warn/allow verdict settles $0.05 USDC to TWZRD and returns a signed V6 trust credential
|
|
128
|
+
for the counterparty before you pay the resource.
|
|
129
|
+
|
|
130
|
+
**`x402Fetch` is yours to supply** (this package is dependency-free). Wire the proven
|
|
131
|
+
`@x402/svm` sponsored-feePayer client — the same one `twzrd-mcp-server` uses:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { wrapFetchWithPayment } from "@x402/svm";
|
|
135
|
+
const x402Fetch = wrapFetchWithPayment(fetch, buyerWallet); // settles 402 challenges
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Gate it behind your own ROI policy (e.g. only auto-buy the receipt for payments above a
|
|
139
|
+
threshold). Runnable, no-spend demo: [`examples/auto-receipt.ts`](./examples/auto-receipt.ts)
|
|
140
|
+
(`npm run autoreceipt-demo`). A bundled/sponsored `x402Fetch` (so integrators need no wallet)
|
|
141
|
+
is the next step.
|
|
142
|
+
|
|
143
|
+
### Quick tier ($0.001) — cheap paid qualify
|
|
144
|
+
|
|
145
|
+
The reputation ladder has three rungs: **free** preflight (`allow/warn/block`), **$0.001**
|
|
146
|
+
`quickCheck` (tier + score, no receipt), **$0.05** `autoReceipt` (full intel + signed V6
|
|
147
|
+
receipt). When the free preflight is inconclusive (`warn` / unknown seller) and you want a
|
|
148
|
+
cheap *paid* confirmation before committing — without paying 50× for the portable receipt —
|
|
149
|
+
use `quickCheck`:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { quickCheck } from "twzrd-x402-gate";
|
|
153
|
+
|
|
154
|
+
const q = await quickCheck(sellerWallet, { x402Fetch }); // settles $0.001 to /v1/intel/quick
|
|
155
|
+
if (q.available && (q.tier === "Gold" || q.tier === "Platinum")) {
|
|
156
|
+
// tier is high enough — proceed with the larger payment
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`quickCheck` is **fail-soft** — it never throws; any gap (no `x402Fetch`, unreachable, settle
|
|
161
|
+
failure) returns `available: false`, so a quick-tier hiccup can't break your flow. The hard
|
|
162
|
+
allow/warn/block decision stays the free preflight's job.
|
|
163
|
+
|
|
164
|
+
### Autonomous risk-escalation — `escalateOnWarn` (pay-to-confirm on warn)
|
|
165
|
+
|
|
166
|
+
The free preflight leaves an unknown/uncertain seller at `warn`, which **proceeds** by
|
|
167
|
+
default. `escalateOnWarn` closes the loop autonomously: on a proceeding `warn`, the guard
|
|
168
|
+
settles the cheap **$0.001** quick tier and **re-decides on the paid score** — below the
|
|
169
|
+
floor the payment is **blocked**, at/above it proceeds. The paid call fires from your
|
|
170
|
+
agent's own risk policy (no human), and the paid signal actually gates the spend (unlike
|
|
171
|
+
`autoReceipt`, which is upsell-only and never changes the decision).
|
|
48
172
|
|
|
49
173
|
```ts
|
|
174
|
+
const safeFetch = withTwzrdGuard(x402Fetch, {
|
|
175
|
+
escalateOnWarn: {
|
|
176
|
+
minSpendUsdc: 0.01, // don't pay $0.001 to vet a sub-cent buy
|
|
177
|
+
blockBelowScore: 40, // block when the paid quick score is below this (default: preflightMinScore)
|
|
178
|
+
},
|
|
179
|
+
x402Fetch, // settles the $0.001 quick charge
|
|
180
|
+
});
|
|
181
|
+
// warn + paid score < 40 -> throws "[twzrd-guard] payment blocked: twzrd_escalated_warn_block ..."
|
|
182
|
+
// warn + paid score >= 40 -> proceeds (result.escalated=true, result.escalatedScore set)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Opt-in, **fail-soft** (if the quick tier can't answer, the base `warn` is preserved), and it
|
|
186
|
+
**only tightens** — a `warn` may become a block, but an `allow` or `block` is never changed.
|
|
187
|
+
This is the autonomous demand loop: an uncertain counterparty is vetted with real paid intel,
|
|
188
|
+
automatically, before your agent commits.
|
|
189
|
+
|
|
190
|
+
### Sponsored payer — use the paid rungs with no wallet (prototype)
|
|
191
|
+
|
|
192
|
+
`createSponsoredX402Fetch` lets a **sponsor** settle the paid rungs on the agent's behalf, so
|
|
193
|
+
an integrator can call `quickCheck` / `autoReceipt` with **no wallet of their own**:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { createSponsoredX402Fetch, quickCheck } from "twzrd-x402-gate";
|
|
197
|
+
|
|
198
|
+
// `settle` = the funded backend (your @x402/svm fetch, or a TWZRD treasury sponsor endpoint).
|
|
199
|
+
const x402Fetch = createSponsoredX402Fetch({ settle });
|
|
200
|
+
const q = await quickCheck(seller, { x402Fetch }); // sponsor pays — caller holds no wallet
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Two backends plug into `settle`: **gas-sponsored** (live via `@x402/svm` — agent pays USDC, the
|
|
204
|
+
resource server's `feePayer` covers SOL gas, the model `twzrd-mcp-server` uses) and
|
|
205
|
+
**full-sponsor** (a TWZRD treasury endpoint pays on the agent's behalf — the true no-wallet
|
|
206
|
+
path). The full-sponsor endpoint + treasury is **founder-gated** (who funds it + per-agent
|
|
207
|
+
budget caps); this ships the client seam + a dry-run so the wiring is ready.
|
|
208
|
+
No-spend demo: [`examples/sponsored-payer.ts`](./examples/sponsored-payer.ts) (`npm run sponsored-demo`).
|
|
209
|
+
|
|
210
|
+
## `evaluate_x402_resource` — standalone preflight
|
|
211
|
+
|
|
212
|
+
Use when you already have the `paymentRequirements` object from a parsed 402 body:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { evaluate_x402_resource } from "twzrd-x402-gate";
|
|
216
|
+
|
|
217
|
+
const result = await evaluate_x402_resource(
|
|
218
|
+
"https://api.exa.ai/search",
|
|
219
|
+
paymentRequirements, // X402PaymentRequirements from the 402 body
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
console.log(result.decision); // "allow" | "warn" | "block"
|
|
223
|
+
console.log(result.trustScore); // number | null
|
|
224
|
+
console.log(result.approved); // boolean
|
|
225
|
+
console.log(result.receiptUrl); // "https://intel.twzrd.xyz/v1/intel/trust/<payTo>"
|
|
226
|
+
|
|
227
|
+
if (!result.approved) throw new Error(`Blocked: ${result.reason}`);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
With `autoReceipt`:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
const result = await evaluate_x402_resource(url, requirements, {
|
|
234
|
+
autoReceipt: true,
|
|
235
|
+
x402Fetch: myPayingFetch,
|
|
236
|
+
onReceipt: (receipt, tx) => storeCredential(receipt),
|
|
237
|
+
});
|
|
238
|
+
// result.receipt — twzrd_receipt (V6 + ERC-8004 reputation_credential)
|
|
239
|
+
// result.receiptTx — on-chain settlement tx
|
|
240
|
+
// result.receiptFeeCaptured — true when fee landed
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Lower-level APIs
|
|
244
|
+
|
|
245
|
+
### Direct approval call
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
50
248
|
import { createTwzrdGate } from "twzrd-x402-gate";
|
|
51
249
|
|
|
52
250
|
const gate = createTwzrdGate();
|
|
@@ -58,38 +256,72 @@ const { approved, reason, card } = await gate.approvePayment({
|
|
|
58
256
|
if (!approved) abort(reason);
|
|
59
257
|
```
|
|
60
258
|
|
|
259
|
+
### `@x402/mcp` payment hook
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { defaultGate } from "twzrd-x402-gate";
|
|
263
|
+
|
|
264
|
+
const client = createX402MCPClient({
|
|
265
|
+
onPaymentRequested: defaultGate.onPaymentRequested, // returns false to deny
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### `wrapFetchWithTwzrdGate`
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { wrapFetchWithTwzrdGate, resolveConfig } from "twzrd-x402-gate";
|
|
273
|
+
|
|
274
|
+
// Alternative fetch wrapper — same interception logic, no autoReceipt.
|
|
275
|
+
const gatedFetch = wrapFetchWithTwzrdGate(fetch, resolveConfig());
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`withTwzrdGuard` is preferred — it composes with `autoReceipt` and `onReceipt`.
|
|
279
|
+
`wrapFetchWithTwzrdGate` remains for codebases that can't migrate.
|
|
280
|
+
|
|
61
281
|
## Policy
|
|
62
282
|
|
|
63
|
-
A payment is **
|
|
283
|
+
A payment is **blocked** when:
|
|
284
|
+
1. `decision ∈ blockDecisions` (default: `["block"]`)
|
|
285
|
+
2. `trust_score < preflightMinScore` (default: `40`)
|
|
286
|
+
3. `can_spend === false` — **only** when `gateOnCanSpend: true` (default `false`, opt-in)
|
|
287
|
+
|
|
288
|
+
`warn` is allowed unless overridden. Preflight network failure **fails closed** by default (a preflight outage blocks the payment, so an intel hiccup never silently approves a spend); set `failOpen: true` / `TWZRD_FAIL_OPEN=true` to opt into legacy allow-on-outage.
|
|
64
289
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
290
|
+
> **`can_spend` note:** the free preflight returns `can_spend=false` for most sellers
|
|
291
|
+
> not yet in the TWZRD corpus, including legitimate ones. The default is decision-only
|
|
292
|
+
> gating so unknown sellers on platforms like Agentic.Market are not blocked by default.
|
|
293
|
+
> Set `gateOnCanSpend: true` for strict mode.
|
|
68
294
|
|
|
69
|
-
|
|
70
|
-
failure the gate **fails open** (approves) unless `failOpen` is disabled.
|
|
295
|
+
## Config
|
|
71
296
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
297
|
+
| Option | Env | Default | Description |
|
|
298
|
+
|---|---|---|---|
|
|
299
|
+
| `intelBase` | `TWZRD_INTEL_BASE` | `https://intel.twzrd.xyz` | Preflight API base |
|
|
300
|
+
| `preflightMinScore` | `TWZRD_PREFLIGHT_MIN_SCORE` | `40` | Block below this score |
|
|
301
|
+
| `blockDecisions` | `TWZRD_BLOCK_DECISIONS` | `block` | Decisions that throw |
|
|
302
|
+
| `failOpen` | `TWZRD_FAIL_OPEN` | `false` | `true` opts into legacy allow-on-outage; default blocks (fail-closed) |
|
|
303
|
+
| `gateOnCanSpend` | `TWZRD_GATE_ON_CAN_SPEND` | `false` | Also block when `can_spend=false` |
|
|
304
|
+
| `autoReceipt` | — | `false` | Auto-buy $0.05 TWZRD receipt on warn/allow |
|
|
305
|
+
| `x402Fetch` | — | — | x402-capable fetch for `autoReceipt` |
|
|
306
|
+
| `onReceipt` | — | — | Callback after receipt is captured |
|
|
307
|
+
| `disabled` (`installTwzrdAutoGate` only) | `TWZRD_AUTO_GATE=0`/`false` | `false` | Bypass the guard entirely — `payWrap` gets the raw, unguarded fetch |
|
|
76
308
|
|
|
77
|
-
##
|
|
309
|
+
## Compatibility note
|
|
78
310
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| `fetch` | — | global `fetch` |
|
|
311
|
+
**Proxied x402 clients** (AgentCash's `.fetch`, ClawRouter `:8402`): these clients handle
|
|
312
|
+
402 internally and return 200. The guard never sees a 402 if it wraps the client's *output* —
|
|
313
|
+
it must wrap the client's *input*. `installTwzrdAutoGate` enforces this composition order by
|
|
314
|
+
construction: it guards the raw fetch first, then hands the guarded fetch to your `payWrap`.
|
|
315
|
+
If you're composing `withTwzrdGuard` manually instead, pass the raw (non-paying) fetch to
|
|
316
|
+
`withTwzrdGuard`, then wrap its output in your x402 client — never the reverse. Or call
|
|
317
|
+
`evaluate_x402_resource` explicitly before routing through the proxy.
|
|
87
318
|
|
|
88
319
|
## Why pre-spend, not post-pay
|
|
89
320
|
|
|
90
|
-
`GET /v1/intel/trust/{wallet}` is the **paid** (0.05 USDC) deep-intel surface —
|
|
91
|
-
`POST /v1/intel/preflight` is the **free** `ReadinessCard` for the pre-spend decision.
|
|
92
|
-
only ever calls the free preflight; you decide whether to proceed before any
|
|
321
|
+
`GET /v1/intel/trust/{wallet}` is the **paid** ($0.05 USDC) deep-intel surface — not a gate.
|
|
322
|
+
`POST /v1/intel/preflight` is the **free** `ReadinessCard` for the pre-spend decision.
|
|
323
|
+
This package only ever calls the free preflight; you decide whether to proceed before any
|
|
324
|
+
USDC leaves your wallet.
|
|
93
325
|
|
|
94
326
|
## License
|
|
95
327
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type TwzrdGuardOptions } from "./with-guard.js";
|
|
2
|
+
/**
|
|
3
|
+
* Takes a guarded (pre-pay-checked) fetch and returns the fetch your agent actually
|
|
4
|
+
* calls — i.e. your x402 client composed on top of the guard (agentcash, ClawRouter,
|
|
5
|
+
* @x402/svm, PayAI, etc.).
|
|
6
|
+
*/
|
|
7
|
+
export type PayWrap = (guardedFetch: typeof fetch) => typeof fetch;
|
|
8
|
+
export type InstallAutoGateOptions = TwzrdGuardOptions & {
|
|
9
|
+
/**
|
|
10
|
+
* The RAW (non-paying) fetch to guard. This MUST be a fetch that still surfaces
|
|
11
|
+
* HTTP 402 — do not pass an already-paying client here (see compatibility note
|
|
12
|
+
* in README: proxied clients like AgentCash/.fetch resolve 402 internally and
|
|
13
|
+
* return 200, so the guard would never see the block condition).
|
|
14
|
+
* Default: globalThis.fetch.
|
|
15
|
+
*/
|
|
16
|
+
rawFetch?: typeof fetch;
|
|
17
|
+
/**
|
|
18
|
+
* Force-disable the gate (payWrap gets the raw fetch, unguarded). Mirrors
|
|
19
|
+
* TWZRD_AUTO_GATE=0 / "false". Prefer the env var for a deploy-time kill switch;
|
|
20
|
+
* use this for tests.
|
|
21
|
+
*/
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Default-on composition helper: guards the RAW fetch with the free TWZRD preflight,
|
|
26
|
+
* THEN hands the guarded fetch to your payment wrapper. This is the one-liner form of
|
|
27
|
+
* the two-step pattern documented in the README:
|
|
28
|
+
*
|
|
29
|
+
* const raw = globalThis.fetch;
|
|
30
|
+
* const gated = withTwzrdGuard(raw);
|
|
31
|
+
* const paying = payWrap(gated);
|
|
32
|
+
*
|
|
33
|
+
* Composing it this way by construction rules out the common mis-wiring of guarding
|
|
34
|
+
* an already-paying fetch (which returns 200 and never gives the guard a 402 to act on).
|
|
35
|
+
*
|
|
36
|
+
* Default ON — a blocked seller throws before payWrap's client ever signs. Opt out with
|
|
37
|
+
* TWZRD_AUTO_GATE=0 (env) or { disabled: true } (per-call), e.g. for a local dev harness
|
|
38
|
+
* that intentionally wants the unguarded fetch.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* import { installTwzrdAutoGate } from "twzrd-x402-gate";
|
|
42
|
+
* import { wrapFetchWithPayment } from "@x402/svm";
|
|
43
|
+
*
|
|
44
|
+
* const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
|
|
45
|
+
* await payingFetch("https://api.exa.ai/search"); // blocked sellers throw before signing
|
|
46
|
+
*/
|
|
47
|
+
export declare function installTwzrdAutoGate(payWrap: PayWrap, options?: InstallAutoGateOptions): typeof fetch;
|
|
48
|
+
//# sourceMappingURL=auto-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-gate.d.ts","sourceRoot":"","sources":["../src/auto-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,YAAY,EAAE,OAAO,KAAK,KAAK,OAAO,KAAK,CAAC;AAEnE,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG;IACvD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,KAAK,CAQd"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { withTwzrdGuard } from "./with-guard.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default-on composition helper: guards the RAW fetch with the free TWZRD preflight,
|
|
4
|
+
* THEN hands the guarded fetch to your payment wrapper. This is the one-liner form of
|
|
5
|
+
* the two-step pattern documented in the README:
|
|
6
|
+
*
|
|
7
|
+
* const raw = globalThis.fetch;
|
|
8
|
+
* const gated = withTwzrdGuard(raw);
|
|
9
|
+
* const paying = payWrap(gated);
|
|
10
|
+
*
|
|
11
|
+
* Composing it this way by construction rules out the common mis-wiring of guarding
|
|
12
|
+
* an already-paying fetch (which returns 200 and never gives the guard a 402 to act on).
|
|
13
|
+
*
|
|
14
|
+
* Default ON — a blocked seller throws before payWrap's client ever signs. Opt out with
|
|
15
|
+
* TWZRD_AUTO_GATE=0 (env) or { disabled: true } (per-call), e.g. for a local dev harness
|
|
16
|
+
* that intentionally wants the unguarded fetch.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* import { installTwzrdAutoGate } from "twzrd-x402-gate";
|
|
20
|
+
* import { wrapFetchWithPayment } from "@x402/svm";
|
|
21
|
+
*
|
|
22
|
+
* const payingFetch = installTwzrdAutoGate((guarded) => wrapFetchWithPayment(guarded, buyerWallet));
|
|
23
|
+
* await payingFetch("https://api.exa.ai/search"); // blocked sellers throw before signing
|
|
24
|
+
*/
|
|
25
|
+
export function installTwzrdAutoGate(payWrap, options) {
|
|
26
|
+
const raw = options?.rawFetch ?? globalThis.fetch;
|
|
27
|
+
const envDisabled = process.env.TWZRD_AUTO_GATE === "0" || process.env.TWZRD_AUTO_GATE === "false";
|
|
28
|
+
const disabled = options?.disabled ?? envDisabled;
|
|
29
|
+
if (disabled)
|
|
30
|
+
return payWrap(raw);
|
|
31
|
+
const guarded = withTwzrdGuard(raw, options);
|
|
32
|
+
return payWrap(guarded);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=auto-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-gate.js","sourceRoot":"","sources":["../src/auto-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA0B,MAAM,iBAAiB,CAAC;AA0BzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAgB,EAChB,OAAgC;IAEhC,MAAM,GAAG,GAAG,OAAO,EAAE,QAAQ,IAAI,UAAU,CAAC,KAAK,CAAC;IAClD,MAAM,WAAW,GACf,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO,CAAC;IACjF,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,WAAW,CAAC;IAClD,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import type { TwzrdGateConfig } from "./types.js";
|
|
1
|
+
import type { TwzrdGateConfig, TwzrdUpsellContext } from "./types.js";
|
|
2
2
|
export type ResolvedTwzrdGateConfig = {
|
|
3
3
|
intelBase: string;
|
|
4
4
|
preflightMinScore: number;
|
|
5
5
|
blockDecisions: Set<string>;
|
|
6
6
|
failOpen: boolean;
|
|
7
7
|
gateOnCanSpend: boolean;
|
|
8
|
+
/** Default true: refuse when free merchant_card.wash_flagged */
|
|
9
|
+
refuseWashFlagged: boolean;
|
|
10
|
+
/** Soft cap USDC when wash_flagged; null = hard refuse */
|
|
11
|
+
washMaxUsdc: number | null;
|
|
8
12
|
fetch: typeof fetch;
|
|
13
|
+
onWarnUpsell?: (ctx: TwzrdUpsellContext) => void | Promise<void>;
|
|
9
14
|
};
|
|
10
15
|
export declare function resolveConfig(overrides?: TwzrdGateConfig): ResolvedTwzrdGateConfig;
|
|
11
16
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE,CAAC;AAYF,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,eAAe,GAAG,uBAAuB,CAkElF"}
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function parseBlockDecisions(raw) {
|
|
2
|
-
const source = raw
|
|
2
|
+
const source = raw?.trim() || "block";
|
|
3
3
|
return new Set(source
|
|
4
4
|
.split(",")
|
|
5
5
|
.map((s) => s.trim())
|
|
@@ -9,17 +9,40 @@ export function resolveConfig(overrides) {
|
|
|
9
9
|
const intelBase = (overrides?.intelBase ??
|
|
10
10
|
process.env.TWZRD_INTEL_BASE ??
|
|
11
11
|
"https://intel.twzrd.xyz").replace(/\/+$/, "");
|
|
12
|
-
const
|
|
12
|
+
const rawMin = overrides?.preflightMinScore ??
|
|
13
13
|
Number(process.env.TWZRD_PREFLIGHT_MIN_SCORE ?? "40");
|
|
14
|
+
const preflightMinScore = (Number.isFinite(rawMin) && rawMin >= 0) ? rawMin : 40;
|
|
14
15
|
const blockDecisions = overrides?.blockDecisions != null
|
|
15
16
|
? new Set([...overrides.blockDecisions].map((s) => s.trim()).filter(Boolean))
|
|
16
17
|
: parseBlockDecisions(process.env.TWZRD_BLOCK_DECISIONS);
|
|
18
|
+
// Default false (fail-closed): block and log loudly on preflight outage.
|
|
19
|
+
// Opt in to legacy fail-open with TWZRD_FAIL_OPEN=true or TWZRD_FAIL_OPEN=1.
|
|
17
20
|
const failOpen = overrides?.failOpen ??
|
|
18
|
-
(process.env.TWZRD_FAIL_OPEN
|
|
19
|
-
process.env.TWZRD_FAIL_OPEN
|
|
21
|
+
(process.env.TWZRD_FAIL_OPEN === "true" ||
|
|
22
|
+
process.env.TWZRD_FAIL_OPEN === "1");
|
|
23
|
+
// Default false (decision-only): an unknown seller (warn / can_spend=false,
|
|
24
|
+
// which is EVERY not-yet-seen merchant at score 45) is NOT blocked by default —
|
|
25
|
+
// only an explicit decision=block (a real wash/sybil flag) blocks. This matches
|
|
26
|
+
// the sister package @wzrd_sol/plugin-trustgate and the preflight's own
|
|
27
|
+
// warn-not-block intent, and keeps the gate usable for discovery. Opt in to
|
|
28
|
+
// strict can_spend gating with TWZRD_GATE_ON_CAN_SPEND=true or =1.
|
|
20
29
|
const gateOnCanSpend = overrides?.gateOnCanSpend ??
|
|
21
|
-
(process.env.TWZRD_GATE_ON_CAN_SPEND
|
|
22
|
-
process.env.TWZRD_GATE_ON_CAN_SPEND
|
|
30
|
+
(process.env.TWZRD_GATE_ON_CAN_SPEND === "true" ||
|
|
31
|
+
process.env.TWZRD_GATE_ON_CAN_SPEND === "1");
|
|
32
|
+
// Default true: free merchant_card.wash_flagged → refuse pay (trustless step 3).
|
|
33
|
+
// Opt out: refuseWashFlagged:false or TWZRD_REFUSE_WASH_FLAGGED=0|false.
|
|
34
|
+
const refuseWashEnv = process.env.TWZRD_REFUSE_WASH_FLAGGED;
|
|
35
|
+
const refuseWashFlagged = overrides?.refuseWashFlagged ??
|
|
36
|
+
!(refuseWashEnv === "0" || refuseWashEnv === "false");
|
|
37
|
+
let washMaxUsdc = null;
|
|
38
|
+
if (overrides?.washMaxUsdc != null && Number.isFinite(overrides.washMaxUsdc)) {
|
|
39
|
+
washMaxUsdc = overrides.washMaxUsdc;
|
|
40
|
+
}
|
|
41
|
+
else if (process.env.TWZRD_WASH_MAX_USDC != null && process.env.TWZRD_WASH_MAX_USDC !== "") {
|
|
42
|
+
const n = Number(process.env.TWZRD_WASH_MAX_USDC);
|
|
43
|
+
if (Number.isFinite(n) && n >= 0)
|
|
44
|
+
washMaxUsdc = n;
|
|
45
|
+
}
|
|
23
46
|
const fetchFn = overrides?.fetch ?? globalThis.fetch;
|
|
24
47
|
if (typeof fetchFn !== "function") {
|
|
25
48
|
throw new Error("[twzrd-x402-gate] fetch is not available; pass config.fetch");
|
|
@@ -30,7 +53,10 @@ export function resolveConfig(overrides) {
|
|
|
30
53
|
blockDecisions,
|
|
31
54
|
failOpen,
|
|
32
55
|
gateOnCanSpend,
|
|
56
|
+
refuseWashFlagged,
|
|
57
|
+
washMaxUsdc,
|
|
33
58
|
fetch: fetchFn,
|
|
59
|
+
onWarnUpsell: overrides?.onWarnUpsell,
|
|
34
60
|
};
|
|
35
61
|
}
|
|
36
62
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type TwzrdTier } from "./quick.js";
|
|
2
|
+
import type { TwzrdDecision, TwzrdGateConfig, TwzrdReadinessCard, X402PaymentRequirements } from "./types.js";
|
|
3
|
+
export type EvaluateX402Options = TwzrdGateConfig & {
|
|
4
|
+
/**
|
|
5
|
+
* When true, automatically fetches the paid TWZRD trust receipt (via x402)
|
|
6
|
+
* after a warn/allow decision. Requires x402Fetch to be provided.
|
|
7
|
+
* Default: false.
|
|
8
|
+
*/
|
|
9
|
+
autoReceipt?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* x402-capable fetch that can settle USDC payments. Used by autoReceipt (the
|
|
12
|
+
* $0.05 receipt) and by escalateOnWarn (the $0.001 quick tier). The caller wires
|
|
13
|
+
* in a Solana wallet + x402 payer.
|
|
14
|
+
*/
|
|
15
|
+
x402Fetch?: typeof fetch;
|
|
16
|
+
/**
|
|
17
|
+
* Called immediately after a receipt is captured on-chain.
|
|
18
|
+
* Provides the raw twzrd_receipt object and the settlement tx hash (if present).
|
|
19
|
+
*/
|
|
20
|
+
onReceipt?: (receipt: unknown, tx: string | undefined) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Autonomous risk-escalation. When the free preflight is inconclusive
|
|
23
|
+
* (decision="warn" and otherwise proceeding), the gate autonomously settles the
|
|
24
|
+
* cheap $0.001 quick tier and RE-DECIDES on the paid score: below `blockBelowScore`
|
|
25
|
+
* (default: preflightMinScore) the payment is denied (approved=false); at/above it
|
|
26
|
+
* proceeds. The paid call fires from the agent's own risk policy - no human - and
|
|
27
|
+
* the paid signal actually gates the spend (unlike autoReceipt, which is upsell-only
|
|
28
|
+
* and never changes the decision). Opt-in; requires x402Fetch. Fail-soft: if the
|
|
29
|
+
* quick tier cannot answer, the base warn decision is preserved. Only tightens
|
|
30
|
+
* (warn -> maybe block); never loosens a block or allow. Short-circuits the
|
|
31
|
+
* autoReceipt path for the warn case (no double settle).
|
|
32
|
+
*/
|
|
33
|
+
escalateOnWarn?: {
|
|
34
|
+
/** Skip escalation when the resource price is below this - don't pay $0.001 to vet a sub-cent buy. Default 0. */
|
|
35
|
+
minSpendUsdc?: number;
|
|
36
|
+
/** Deny when the paid quick score is below this. Default: preflightMinScore (40). */
|
|
37
|
+
blockBelowScore?: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type EvaluateX402Result = {
|
|
41
|
+
decision: TwzrdDecision | "unknown";
|
|
42
|
+
trustScore: number | null;
|
|
43
|
+
approved: boolean;
|
|
44
|
+
reason: string;
|
|
45
|
+
card: TwzrdReadinessCard;
|
|
46
|
+
/** true when the preflight was unreachable and fail-open allowed the resource */
|
|
47
|
+
failOpen?: boolean;
|
|
48
|
+
/** URL of the paid TWZRD trust endpoint for this seller (for manual upsell) */
|
|
49
|
+
receiptUrl?: string;
|
|
50
|
+
/** Present when autoReceipt=true and the x402 trust call succeeded */
|
|
51
|
+
receipt?: unknown;
|
|
52
|
+
/** On-chain settlement tx from the receipt payment */
|
|
53
|
+
receiptTx?: string;
|
|
54
|
+
/** true when a fee was captured on-chain */
|
|
55
|
+
receiptFeeCaptured?: boolean;
|
|
56
|
+
/** true when a `warn` triggered an autonomous paid quick-tier re-decision (escalateOnWarn) */
|
|
57
|
+
escalated?: boolean;
|
|
58
|
+
/** the paid quick-tier score that drove the escalated decision; null when the quick tier could not answer */
|
|
59
|
+
escalatedScore?: number | null;
|
|
60
|
+
/** the paid quick-tier label (Bronze/Silver/Gold/Platinum) from the escalation */
|
|
61
|
+
escalatedTier?: TwzrdTier | null;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate an x402 resource before the buyer pays:
|
|
65
|
+
* 1. Run free TWZRD preflight on the seller (no auth, no cost).
|
|
66
|
+
* 2. Return decision + trust score.
|
|
67
|
+
* 3. If escalateOnWarn is set and decision=warn: autonomously settle the cheap
|
|
68
|
+
* $0.001 quick tier and re-decide on the paid score (the autonomous risk loop).
|
|
69
|
+
* 4. Else if autoReceipt=true and decision !== block: auto-fetch the paid TWZRD
|
|
70
|
+
* trust receipt via x402Fetch (TWZRD earns the receipt fee on-chain).
|
|
71
|
+
*
|
|
72
|
+
* Defaults to gateOnCanSpend=false (decision-only) — the free-tier preflight
|
|
73
|
+
* returns can_spend=false for most unknown sellers, which would block too eagerly
|
|
74
|
+
* on platforms like Agentic.Market where sellers are not yet in the corpus.
|
|
75
|
+
*/
|
|
76
|
+
export declare function evaluate_x402_resource(resourceUrl: string, paymentRequirements: X402PaymentRequirements, opts?: EvaluateX402Options): Promise<EvaluateX402Result>;
|
|
77
|
+
//# sourceMappingURL=evaluate.d.ts.map
|