@twzrd1111/clawrouter-mpp 0.0.0-stage → 0.2.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/LICENSE +22 -0
- package/README.md +171 -2
- package/dist/index.d.ts +210 -0
- package/dist/index.js +1055 -0
- package/package.json +57 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BlockRunAI
|
|
4
|
+
Copyright (c) 2026 TWZRD (MPP rails)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,172 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @twzrd1111/clawrouter-mpp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MPP (Machine Payments Protocol) payment rails for [ClawRouter](https://github.com/BlockRunAI/ClawRouter):
|
|
4
|
+
pay an MPP seller over **Tempo** or **Solana** instead of x402, under ClawRouter's own spend limits.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { startProxy, SpendControl, FileSpendControlStorage } from "@twzrd1111/clawrouter";
|
|
8
|
+
import { createMppFetch, createSolanaMppFetch } from "@twzrd1111/clawrouter-mpp";
|
|
9
|
+
import { createTwzrdMppIntegration, createLocalDecisionSigner } from "twzrd-x402-gate";
|
|
10
|
+
|
|
11
|
+
const spend = new SpendControl({ storage: new FileSpendControlStorage() });
|
|
12
|
+
const twzrdMpp = createTwzrdMppIntegration({
|
|
13
|
+
signer: createLocalDecisionSigner(),
|
|
14
|
+
policy: { maxAmountUsd: "1.00" },
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Tempo (needs `mppx`); alternatively use createSolanaMppFetch with the same hooks.
|
|
18
|
+
const payFetch = createMppFetch(account, fetch, { spend, integration: twzrdMpp });
|
|
19
|
+
const proxy = await startProxy({ spendControl: spend, payFetch /* ... */ });
|
|
20
|
+
|
|
21
|
+
// proxy.close() closes the listener, then drains the rail's pending ledger writes.
|
|
22
|
+
await proxy.close();
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The supported setup uses the integration helper returned by `createTwzrdMppIntegration`.
|
|
26
|
+
The `twzrdMppIntegration` marker is a wiring check only; it is forgeable and does not authenticate
|
|
27
|
+
the origin of the hooks. Its gate receives the full MPP charge challenge before credential
|
|
28
|
+
creation and returns
|
|
29
|
+
`{ disposition: "allow" | "warn" | "block" }`; `block` and callback errors refuse payment before
|
|
30
|
+
signing, while `warn` proceeds. The MPP rail rejects integrations without the marker or required
|
|
31
|
+
hooks before loading payment SDKs.
|
|
32
|
+
|
|
33
|
+
The integration writes the pre-sign decision durably to `.twzrd/decisions.jsonl` before allowing
|
|
34
|
+
credential creation, then appends the projected MPP receipt to that same file. Fulfillment writes
|
|
35
|
+
run asynchronously so they do not hold the response stream. `payFetch.flush?.()` waits for pending
|
|
36
|
+
records and fsyncs the local ledger during orderly shutdown. `startProxy` calls the rail's flush hook
|
|
37
|
+
after closing its listener, and direct rail users must call `payFetch.flush()` themselves. It waits for
|
|
38
|
+
active SSE responses too, so drain or cancel them before flushing; an open stream can keep the flush
|
|
39
|
+
pending. Network contribution remains off unless separately enabled. It blocks unscored
|
|
40
|
+
networks by default, including Tempo mainnet and Moderato, until TWZRD has a reputation corpus for
|
|
41
|
+
them. The default provider also fails closed on outages; the ledger sink itself makes no network
|
|
42
|
+
call. An explicit `allowUnscoredNetwork: true` requires `policy.maxAmountUsd` and returns
|
|
43
|
+
`reputation_unchecked` as a warning; warnings proceed unless `treatWarnAsBlock` is set. The
|
|
44
|
+
fulfillment row omits prompts, responses, credentials, and raw receipt headers; its seller model
|
|
45
|
+
field is explicitly a seller claim. Network contribution remains a separate opt-in.
|
|
46
|
+
|
|
47
|
+
This integration requires `twzrd-x402-gate@0.17.0`, which is not published yet. npm's current
|
|
48
|
+
`0.16.1` does not include Tempo charge evaluation or this shared-ledger adapter.
|
|
49
|
+
|
|
50
|
+
Receipt-safe payment metadata is available through `readPaidCallEvidence(response)` for a paid
|
|
51
|
+
response from this rail. The
|
|
52
|
+
fulfillment hook records the decoded receipt reference, never the raw base64 receipt or payment
|
|
53
|
+
credential. Tempo session receipts emitted in an SSE `payment-receipt` event are captured when the
|
|
54
|
+
response stream completes; the stream bytes are forwarded unchanged, including the seller-reported
|
|
55
|
+
model label from its first JSON data event. A `valid_format` receipt means its structure, method, and
|
|
56
|
+
intent match the challenge; it is not an on-chain verification. `seller_reported_model_matches_request`
|
|
57
|
+
compares labels only and does not verify model identity. A failed local write does not change an
|
|
58
|
+
already-paid response; `payFetch.flush()` surfaces persistence failures to the shutdown caller.
|
|
59
|
+
|
|
60
|
+
This candidate requires `@twzrd1111/clawrouter >=0.12.309`, including deferred streaming headers.
|
|
61
|
+
The fork is the release target. Upstream PRs #545 and #546 are independent of this release.
|
|
62
|
+
|
|
63
|
+
For Tempo install the optional peer `mppx@~0.17.0`. For Solana install `@solana/mpp@~0.7.0`,
|
|
64
|
+
`mppx@~0.17.0`, and `@solana/kit@~6.10.0`. Solana now uses the official charge method
|
|
65
|
+
directly, replacing the pay-kit client wrapper. That wrapper's progress events omit the
|
|
66
|
+
challenge ID; the direct method lets policy checks and credits use the full challenge
|
|
67
|
+
before signing. `@solana/pay-kit` is used only by development tests as a fixture seller.
|
|
68
|
+
|
|
69
|
+
## What it guarantees
|
|
70
|
+
|
|
71
|
+
- **Spend limits apply.** The amount, network, asset and payee are checked, and budget reserved, _before_
|
|
72
|
+
anything is signed. Solana checks the complete challenge before transaction construction
|
|
73
|
+
and pins the configured network even when no spend control is supplied.
|
|
74
|
+
The proxy refuses to start a rail that does not set `enforcesSpend` while amount limits are configured.
|
|
75
|
+
- **A signed payment is never paid twice.** If a call fails after a credential or transaction was signed, it
|
|
76
|
+
ends in `MppPaidNoCompletionError`: the proxy answers 502 `payment_settled_no_completion` and does not
|
|
77
|
+
retry or fall back. The spend ledger keeps the spend with a note, and the error carries a payment
|
|
78
|
+
reference (`mpp-<16 hex>`, a hash of the signed payload) for disputes.
|
|
79
|
+
- **Streaming receipts.** Both factories set `deferStreamingHeaders`. The proxy holds final
|
|
80
|
+
headers while paying, sends informational HTTP 102 responses, and forwards the seller's
|
|
81
|
+
exact `Payment-Receipt` on the final streaming response. It does not fabricate a missing receipt.
|
|
82
|
+
Both also set `supportsNativeStreaming`: streaming chat requests retain `stream: true`
|
|
83
|
+
upstream and request usage chunks. Native SSE flows incrementally, including structured
|
|
84
|
+
tool calls, with reasoning filtered and token/cost accounting retained.
|
|
85
|
+
Ambiguous text-encoded calls wait for the choice to finish so recovery matches the existing
|
|
86
|
+
whole-message parser; structured tool deltas and ordinary text flow incrementally.
|
|
87
|
+
JSON-only replies use the existing simulated stream. In-flight coalesced callers receive live chunks from
|
|
88
|
+
one paid attempt; completed cache replays do not claim a new receipt.
|
|
89
|
+
HTTP 102 does not extend OpenClaw's deadline. Real content/tool deltas reset client activity
|
|
90
|
+
timers as the model streams. Payment/header wait and stalled output still have deadlines.
|
|
91
|
+
- **Optional peers.** Importing this package does not load payment SDKs. Tempo loads `mppx` on
|
|
92
|
+
first use; the asynchronous Solana factory loads `@solana/mpp`. Missing peers produce an
|
|
93
|
+
install instruction.
|
|
94
|
+
|
|
95
|
+
"Signed" is not "settled": on Solana the seller submits the transaction. Seller-side refunds
|
|
96
|
+
remain outside this package.
|
|
97
|
+
|
|
98
|
+
## Replay protection at a seller
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { SpentProofLedger, fulfillPaidProof, ReplayRejectedError } from "@twzrd1111/clawrouter-mpp";
|
|
102
|
+
|
|
103
|
+
const proofs = new SpentProofLedger(); // share across requests in this process
|
|
104
|
+
|
|
105
|
+
// First authenticate the challenge and independently verify its payment on chain.
|
|
106
|
+
// Pass a canonical network ID plus verified transaction and challenge IDs,
|
|
107
|
+
// never untrusted body fields. Example IDs: "solana:mainnet-beta", "eip155:4217".
|
|
108
|
+
const completion = await fulfillPaidProof({
|
|
109
|
+
ledger: proofs,
|
|
110
|
+
networkId: verifiedNetworkId,
|
|
111
|
+
txSignature: verifiedSignature,
|
|
112
|
+
challengeId: verifiedChallengeId,
|
|
113
|
+
upstream: () => runModel(),
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A reused transaction or challenge on the same network throws `ReplayRejectedError` with
|
|
118
|
+
`status: 409` and `code: "proof_replayed"` before invoking `upstream`. Network scope allows
|
|
119
|
+
the same signature text or challenge ID to exist independently on another chain. The
|
|
120
|
+
synchronous claim gives concurrent duplicates one winner. The claim remains spent if the
|
|
121
|
+
model fails. This helper does not verify a payment itself,
|
|
122
|
+
and installing a buyer rail cannot add replay protection to a remote seller.
|
|
123
|
+
|
|
124
|
+
## Credits for signed calls with no completion
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { MppPaidNoCompletionError, readCredits } from "@twzrd1111/clawrouter-mpp";
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
await payFetch(url, init);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (error instanceof MppPaidNoCompletionError) {
|
|
133
|
+
console.log(readCredits()); // immutable snapshots; do not log credentials
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Both rails record `{ payer, amount, challengeId, reason }` on a signed call's HTTP error or
|
|
140
|
+
transport failure. Payer comes from the supplied account/signer, never the seller's recipient.
|
|
141
|
+
Amount is the original integer base-unit string. Repeating a payer/challenge records one row.
|
|
142
|
+
|
|
143
|
+
When the proxy detects a provider error hidden inside HTTP 200, it calls the rail's
|
|
144
|
+
`onNoCompletion(response, reason)` hook. For a signed response this records the credit and
|
|
145
|
+
throws the same terminal error before any fallback model is paid. Unsigned responses retain
|
|
146
|
+
the ordinary provider fallback behavior. A custom host using these rails can invoke that
|
|
147
|
+
hook after its own completion validation; the response must be the one returned by the rail.
|
|
148
|
+
|
|
149
|
+
For a broken or unfinished native body the proxy invokes that same hook, records one credit,
|
|
150
|
+
and ends the existing SSE response with `payment_settled_no_completion`. Its HTTP 200 and
|
|
151
|
+
receipt have already been sent, so this is an in-stream error rather than a new HTTP 502.
|
|
152
|
+
The proxy never retries a model or pays a fallback after committing that stream. Disconnects
|
|
153
|
+
and request deadlines cancel the upstream reader and clean up coalesced callers.
|
|
154
|
+
|
|
155
|
+
`CreditLedger` and `creditPaidNoCompletion(ledger, error, fields)` are also public for callers
|
|
156
|
+
with their own ledger; `readCredits(ledger)` reads that ledger. Retrying the credit operation
|
|
157
|
+
returns the same immutable row and invokes no signer. It does not retry a model request.
|
|
158
|
+
|
|
159
|
+
Both ledgers are in memory, scoped to one process, and reset on restart. Credits record
|
|
160
|
+
signed attempts, not confirmed settlement or redeemable balances. There is no refund, chain
|
|
161
|
+
write, cross-process exclusion, persistence, or automatic reconciliation in these helpers.
|
|
162
|
+
|
|
163
|
+
## Develop
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
npm install # from the ClawRouter repo root
|
|
167
|
+
# Before the candidate core is published, install rails development dependencies:
|
|
168
|
+
npm install --legacy-peer-deps # in packages/clawrouter-mpp
|
|
169
|
+
npm run typecheck && npm test && npm run build # in packages/clawrouter-mpp
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Tests resolve `@twzrd1111/clawrouter` to the repo source until the seam is released.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Account } from 'viem';
|
|
2
|
+
import { SpendControl, PayFetch, SpendRecord } from '@twzrd1111/clawrouter';
|
|
3
|
+
export { PayFetch } from '@twzrd1111/clawrouter';
|
|
4
|
+
import { KeyPairSigner } from '@solana/kit';
|
|
5
|
+
|
|
6
|
+
declare const MPP_FULFILLMENT_RECORD_SCHEMA = "twzrd.mpp.fulfillment.v1";
|
|
7
|
+
declare const MPP_FULFILLMENT_MAX_BODY_BYTES: number;
|
|
8
|
+
type MppPaymentFacts = {
|
|
9
|
+
challengeId: string;
|
|
10
|
+
method: string;
|
|
11
|
+
intent: string;
|
|
12
|
+
payTo: string | null;
|
|
13
|
+
network: string | null;
|
|
14
|
+
asset: string | null;
|
|
15
|
+
amountAtomic: string | null;
|
|
16
|
+
};
|
|
17
|
+
type MppFulfillmentRecord = {
|
|
18
|
+
schema: typeof MPP_FULFILLMENT_RECORD_SCHEMA;
|
|
19
|
+
at_unix_ms: number;
|
|
20
|
+
resource_origin: string | null;
|
|
21
|
+
http_status: number;
|
|
22
|
+
payment_method: string;
|
|
23
|
+
payment_intent: string;
|
|
24
|
+
challenge_id: string;
|
|
25
|
+
paid_to: string | null;
|
|
26
|
+
network: string | null;
|
|
27
|
+
asset: string | null;
|
|
28
|
+
amount_atomic: string | null;
|
|
29
|
+
receipt_state: "valid_format" | "missing" | "invalid_format" | "unavailable";
|
|
30
|
+
receipt_status: string | null;
|
|
31
|
+
receipt_method: string | null;
|
|
32
|
+
/** Seller-supplied MPP receipt reference (EVM hash or Solana signature); not chain-verified here. */
|
|
33
|
+
receipt_reference: string | null;
|
|
34
|
+
receipt_timestamp: string | null;
|
|
35
|
+
requested_model: string | null;
|
|
36
|
+
/** Model label reported by the seller. This is not an attestation of model identity. */
|
|
37
|
+
seller_reported_model: string | null;
|
|
38
|
+
/** Equality of seller-reported and requested labels only; this does not verify the served model. */
|
|
39
|
+
seller_reported_model_matches_request: boolean | null;
|
|
40
|
+
gate: MppGateDecision | null;
|
|
41
|
+
};
|
|
42
|
+
type MppFulfillmentSink = (record: MppFulfillmentRecord) => void | Promise<void>;
|
|
43
|
+
|
|
44
|
+
/** Optional buyer-side policy hook evaluated on the exact MPP challenge before signing. */
|
|
45
|
+
type MppGateChallenge = {
|
|
46
|
+
id: string;
|
|
47
|
+
realm?: string;
|
|
48
|
+
method: string;
|
|
49
|
+
intent: string;
|
|
50
|
+
request: Record<string, unknown>;
|
|
51
|
+
expires?: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
};
|
|
54
|
+
/** Safe decision metadata retained with the paid response for receipt verification. */
|
|
55
|
+
type MppGateDecision = {
|
|
56
|
+
disposition: "allow" | "warn" | "block";
|
|
57
|
+
decisionId?: string;
|
|
58
|
+
intentHash?: string;
|
|
59
|
+
reasonCodes?: readonly string[];
|
|
60
|
+
};
|
|
61
|
+
/** Return the TWZRD gate's result. Throwing or returning an invalid result fails closed. */
|
|
62
|
+
type MppGateEvaluator = (challenge: MppGateChallenge) => MppGateDecision | Promise<MppGateDecision>;
|
|
63
|
+
/** Integration contract accepted by the MPP factories. The marker is not an authenticity proof. */
|
|
64
|
+
type MppIntegration = Readonly<{
|
|
65
|
+
twzrdMppIntegration: true;
|
|
66
|
+
gate: MppGateEvaluator;
|
|
67
|
+
onFulfillment: MppFulfillmentSink;
|
|
68
|
+
flush: () => Promise<void>;
|
|
69
|
+
}>;
|
|
70
|
+
declare class MppGateError extends Error {
|
|
71
|
+
readonly code: "MPP_GATE_BLOCKED" | "MPP_GATE_INVALID_RESULT";
|
|
72
|
+
readonly challengeId: string;
|
|
73
|
+
readonly decisionId?: string;
|
|
74
|
+
readonly reasonCodes?: readonly string[];
|
|
75
|
+
constructor(code: MppGateError["code"], challengeId: string, message: string, decision?: MppGateDecision);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Tempo TIP-20 USD stablecoins priced here, lower-case, all 6 decimals (the challenge carries no
|
|
80
|
+
* `decimals`). Any other asset cannot be priced, so it is refused while amount limits are set.
|
|
81
|
+
* - 0x20c0…0000 pathUSD (Moderato testnet 42431, also on mainnet 4217)
|
|
82
|
+
* - 0x20c0…b9537d11c60e8b50 fee token a Tempo-run MPP proxy quoted on mainnet 4217 (2026-11-03)
|
|
83
|
+
* - 0x20c0…f37de3740adec032 charge currency on that same OpenRouter challenge (6 decimals, 750 = 0.00075 USD)
|
|
84
|
+
*/
|
|
85
|
+
declare const DEFAULT_TEMPO_USD_ASSETS: ReadonlySet<string>;
|
|
86
|
+
type MppFetchOptions = {
|
|
87
|
+
/** When set, every challenge is checked against these limits before a credential is created. */
|
|
88
|
+
spend?: SpendControl;
|
|
89
|
+
/** Override the assets treated as 6-decimal USD (lower-case addresses). */
|
|
90
|
+
usdAssets?: ReadonlySet<string>;
|
|
91
|
+
/** Required branded object from createTwzrdMppIntegration. */
|
|
92
|
+
integration: MppIntegration;
|
|
93
|
+
};
|
|
94
|
+
type ChallengeLike = {
|
|
95
|
+
id: string;
|
|
96
|
+
realm?: string;
|
|
97
|
+
method: string;
|
|
98
|
+
intent: string;
|
|
99
|
+
request: Record<string, unknown>;
|
|
100
|
+
expires?: string;
|
|
101
|
+
description?: string;
|
|
102
|
+
};
|
|
103
|
+
/** The quote as the spend-control layer understands it. `amount` stays undefined if unpriceable. */
|
|
104
|
+
declare function quoteFromChallenge(challenge: ChallengeLike, usdAssets?: ReadonlySet<string>): {
|
|
105
|
+
payTo: string | undefined;
|
|
106
|
+
network: string | undefined;
|
|
107
|
+
asset: string | undefined;
|
|
108
|
+
amount: string | undefined;
|
|
109
|
+
priceable: boolean;
|
|
110
|
+
};
|
|
111
|
+
declare function createMppFetch(account: Account, base: typeof fetch | undefined, opts: MppFetchOptions): PayFetch;
|
|
112
|
+
|
|
113
|
+
declare const DEFAULT_SOLANA_USD_MINTS: ReadonlySet<string>;
|
|
114
|
+
type SolanaNetwork = "mainnet" | "devnet" | "localnet";
|
|
115
|
+
type SolanaMppFetchOptions = {
|
|
116
|
+
signer: KeyPairSigner;
|
|
117
|
+
rpcUrl: string;
|
|
118
|
+
network: SolanaNetwork;
|
|
119
|
+
spend?: SpendControl;
|
|
120
|
+
usdMints?: Iterable<string>;
|
|
121
|
+
/** Required branded object from createTwzrdMppIntegration. */
|
|
122
|
+
integration: MppIntegration;
|
|
123
|
+
/** Injectable transport; defaults to fetch. The SDK's RPC transport is separate. */
|
|
124
|
+
fetch?: typeof fetch;
|
|
125
|
+
};
|
|
126
|
+
declare function createSolanaMppFetch(opts: SolanaMppFetchOptions): Promise<PayFetch>;
|
|
127
|
+
|
|
128
|
+
/** In-memory credit row. No chain write and no signer. */
|
|
129
|
+
type CreditEntry = Readonly<{
|
|
130
|
+
payer: string;
|
|
131
|
+
amount: string;
|
|
132
|
+
challengeId: string;
|
|
133
|
+
reason: string;
|
|
134
|
+
}>;
|
|
135
|
+
declare class CreditLedger {
|
|
136
|
+
private readonly rows;
|
|
137
|
+
/** One row per payer and challenge. A retry returns the first row. */
|
|
138
|
+
recordOnce(entry: CreditEntry): CreditEntry;
|
|
139
|
+
read(): readonly CreditEntry[];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
declare class MppPaidNoCompletionError extends Error {
|
|
143
|
+
readonly reference: string;
|
|
144
|
+
readonly status?: number;
|
|
145
|
+
constructor(message: string, reference: string, status?: number, options?: {
|
|
146
|
+
cause?: unknown;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Record one credit for a paid call that returned no completion.
|
|
151
|
+
* A retry of the same payer and challenge returns the first row.
|
|
152
|
+
* `sign`, when passed, is never called.
|
|
153
|
+
*/
|
|
154
|
+
declare function creditPaidNoCompletion(ledger: CreditLedger, err: MppPaidNoCompletionError, fields: CreditEntry, hooks?: {
|
|
155
|
+
sign?: () => Promise<unknown>;
|
|
156
|
+
}): CreditEntry;
|
|
157
|
+
type PaidCall = {
|
|
158
|
+
/** The paying account supplied to the rail, never the challenge recipient. */
|
|
159
|
+
payer: string;
|
|
160
|
+
/** A credential or transaction was produced for this call. */
|
|
161
|
+
signed: boolean;
|
|
162
|
+
/** Stable, non-secret handle for the payment (hash of what was sent), for disputes. */
|
|
163
|
+
reference?: string;
|
|
164
|
+
/** The ledger entry written when the payment was signed, if limits made one. */
|
|
165
|
+
record?: SpendRecord;
|
|
166
|
+
/** Payer, amount, and challenge known before the model runs. Reason is filled on failure. */
|
|
167
|
+
credit?: Pick<CreditEntry, "payer" | "amount" | "challengeId">;
|
|
168
|
+
/** Optional pre-sign policy decision; no raw challenge or signed token is retained. */
|
|
169
|
+
gate?: MppGateDecision;
|
|
170
|
+
payment?: MppPaymentFacts;
|
|
171
|
+
};
|
|
172
|
+
/** Rows written by `fail` when a signed call returns no completion. */
|
|
173
|
+
declare function readCredits(ledger?: CreditLedger): readonly CreditEntry[];
|
|
174
|
+
/** Safe payment facts associated with this exact response, if it was paid by this rail. */
|
|
175
|
+
declare function readPaidCallEvidence(response: Response): Pick<PaidCall, "payer" | "reference" | "credit" | "gate" | "payment"> | undefined;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Seller-side spent-proof ledger. In memory only.
|
|
179
|
+
*
|
|
180
|
+
* A proof is one payment transaction signature plus one challenge id.
|
|
181
|
+
* The buyer's 402 followed by the authorized retry is one proof: the 402
|
|
182
|
+
* carries no transaction signature, so it never claims a slot.
|
|
183
|
+
*/
|
|
184
|
+
declare class ReplayRejectedError extends Error {
|
|
185
|
+
readonly networkId: string;
|
|
186
|
+
readonly txSignature: string;
|
|
187
|
+
readonly challengeId: string;
|
|
188
|
+
readonly status = 409;
|
|
189
|
+
readonly code = "proof_replayed";
|
|
190
|
+
constructor(networkId: string, txSignature: string, challengeId: string);
|
|
191
|
+
}
|
|
192
|
+
declare class SpentProofLedger {
|
|
193
|
+
private readonly transactions;
|
|
194
|
+
private readonly challenges;
|
|
195
|
+
claim(networkId: string, txSignature: string, challengeId: string): boolean;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Accept one paid proof and call the model at most once.
|
|
199
|
+
* The claim is synchronous, so two overlapping calls yield one winner.
|
|
200
|
+
*/
|
|
201
|
+
declare function fulfillPaidProof<T>(opts: {
|
|
202
|
+
ledger: SpentProofLedger;
|
|
203
|
+
networkId: string;
|
|
204
|
+
txSignature: string;
|
|
205
|
+
challengeId: string;
|
|
206
|
+
upstream: () => Promise<T>;
|
|
207
|
+
}): Promise<T>;
|
|
208
|
+
|
|
209
|
+
export { CreditLedger, DEFAULT_SOLANA_USD_MINTS, DEFAULT_TEMPO_USD_ASSETS, MPP_FULFILLMENT_MAX_BODY_BYTES, MPP_FULFILLMENT_RECORD_SCHEMA, MppGateError, MppPaidNoCompletionError, ReplayRejectedError, SpentProofLedger, createMppFetch, createSolanaMppFetch, creditPaidNoCompletion, fulfillPaidProof, quoteFromChallenge, readCredits, readPaidCallEvidence };
|
|
210
|
+
export type { CreditEntry, MppFetchOptions, MppFulfillmentRecord, MppFulfillmentSink, MppGateChallenge, MppGateDecision, MppGateEvaluator, MppIntegration, MppPaymentFacts, SolanaMppFetchOptions };
|