@haven_ai/signer 0.0.0-dev.202609031523.fd49e1a
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 +240 -0
- package/dist/cli.cjs +1397 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1395 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1377 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +482 -0
- package/dist/index.d.ts +482 -0
- package/dist/index.js +1346 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { X402ExpectedAuth, X402PaymentRequired, X402PaymentOption, SweepAuthorization, SweepExpectedAuth } from '@haven_ai/sdk';
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { z } from 'zod/v3';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The edge signer core.
|
|
7
|
+
*
|
|
8
|
+
* Holds the delegate key in this process and exposes the two signing
|
|
9
|
+
* operations a hosted-MCP flow needs. It performs no network I/O and never
|
|
10
|
+
* returns the key — only signatures and the standard x402 header. See
|
|
11
|
+
* docs/architecture/07-edge-signer.md.
|
|
12
|
+
*/
|
|
13
|
+
interface EdgeSigner {
|
|
14
|
+
/** Address derived from the delegate key. */
|
|
15
|
+
readonly delegateAddress: string;
|
|
16
|
+
/** Sign an AllowanceModule funding/transfer hash (raw ECDSA, 65 bytes). */
|
|
17
|
+
signPaymentHash(hash: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Sign a DIRECT delegation-rail payment's EIP-712 typed data (#1254) — the
|
|
20
|
+
* non-x402 counterpart of `signX402FundingTypedData`. The Hybrid account
|
|
21
|
+
* validates the typed data, not the bare ERC-4337 hash; raw-signing the
|
|
22
|
+
* hash produced AA24 on-chain (found live, #908 mainnet canary). Signed
|
|
23
|
+
* VERBATIM (#829): the exact structure the hosted result carried — and,
|
|
24
|
+
* unlike a blind hash, a structure whose recipient/amount/account are
|
|
25
|
+
* visible to this process's audit log.
|
|
26
|
+
*/
|
|
27
|
+
signDelegationTypedData(typedData: Record<string, unknown>): Promise<string>;
|
|
28
|
+
/** Sign an x402 funding hash and remember the funded merchant-header context. */
|
|
29
|
+
signX402FundingHash(hash: string, expected: X402ExpectedPayment): X402FundingSignatureResult;
|
|
30
|
+
/**
|
|
31
|
+
* Sign a delegation-rail x402 funding intent's EIP-712 typed data (#1138) and
|
|
32
|
+
* remember the funded merchant-header context, exactly as the hash path does.
|
|
33
|
+
*
|
|
34
|
+
* The account validates this typed data, NOT the bare ERC-4337 hash, so the
|
|
35
|
+
* expected context must be v2 and commit to its digest — see
|
|
36
|
+
* `assertExpectedBinding`.
|
|
37
|
+
*/
|
|
38
|
+
signX402FundingTypedData(typedData: X402FundingTypedData, expected: X402ExpectedPayment): Promise<X402FundingSignatureResult>;
|
|
39
|
+
/** Build + sign the EIP-3009 X-PAYMENT header for the merchant leg of x402. */
|
|
40
|
+
buildX402PaymentHeader(paymentRequired: X402PaymentRequired, x402Binding: string): Promise<X402HeaderResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Sign a Haven-prepared EIP-3009 sweep authorization (gasless USDC recovery
|
|
43
|
+
* delegate → Safe). Verifies the authorization came from Haven and pays out to
|
|
44
|
+
* the delegate's own Safe before signing; the relayer broadcasts it and pays
|
|
45
|
+
* gas. Never broadcasts — pure signing.
|
|
46
|
+
*/
|
|
47
|
+
signSweepAuthorization(input: SweepSignatureInput): Promise<SweepSignatureResult>;
|
|
48
|
+
}
|
|
49
|
+
interface SweepSignatureInput {
|
|
50
|
+
/** The authorization fields prepared by the backend. */
|
|
51
|
+
authorization: SweepAuthorization;
|
|
52
|
+
/** Haven's signature over the authorization context (binding). */
|
|
53
|
+
expectedAuth: SweepExpectedAuth;
|
|
54
|
+
/** Optional Safe address from the local credential, cross-checked against `to`. */
|
|
55
|
+
expectedSafe?: string;
|
|
56
|
+
}
|
|
57
|
+
interface SweepSignatureResult {
|
|
58
|
+
/** EIP-712 signature over the TransferWithAuthorization, by the delegate key. */
|
|
59
|
+
signature: string;
|
|
60
|
+
}
|
|
61
|
+
/** EIP-712 payload the delegation-rail account validates (#1138). */
|
|
62
|
+
interface X402FundingTypedData {
|
|
63
|
+
domain: Record<string, unknown>;
|
|
64
|
+
types: Record<string, unknown>;
|
|
65
|
+
primaryType: string;
|
|
66
|
+
message: Record<string, unknown>;
|
|
67
|
+
}
|
|
68
|
+
interface X402ExpectedPayment {
|
|
69
|
+
/** Haven payment id for the funding transfer. */
|
|
70
|
+
paymentId: string;
|
|
71
|
+
/** Funding hash this expected context authenticates. */
|
|
72
|
+
payloadHash: string;
|
|
73
|
+
/**
|
|
74
|
+
* EIP-712 digest of the typed data the account validates (#1138). Present ⇒
|
|
75
|
+
* the binding is v2 and the delegation-rail typed-data path is the ONLY
|
|
76
|
+
* signing path allowed for this intent.
|
|
77
|
+
*/
|
|
78
|
+
typedDataHash?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Resource URL of the x402 payment Haven prepared. On the hosted surface
|
|
81
|
+
* that is `haven_pay_x402_quote` — `buildX402SigningContext` in
|
|
82
|
+
* `mcp-server/src/tools.ts` relays `intent.resourceUrl` here, and
|
|
83
|
+
* `haven_resume_x402_payment` re-emits the same context. Checked against
|
|
84
|
+
* the merchant header's resource in `assertX402MatchesExpected`.
|
|
85
|
+
*/
|
|
86
|
+
resourceUrl: string;
|
|
87
|
+
/**
|
|
88
|
+
* Merchant recipient of that same prepared payment (`intent.merchantTo`,
|
|
89
|
+
* same source). Checked against the header's `payTo` in
|
|
90
|
+
* `assertX402MatchesExpected` and, for an erc7710 settlement child,
|
|
91
|
+
* against the child's payee caveat in `verifySettlementChild`.
|
|
92
|
+
*/
|
|
93
|
+
merchantTo: string;
|
|
94
|
+
/** Atomic amount funded for the merchant header. */
|
|
95
|
+
amount: string;
|
|
96
|
+
/** Token contract funded for the merchant header. */
|
|
97
|
+
asset: string;
|
|
98
|
+
/** x402 network funded for the merchant header. */
|
|
99
|
+
network: string;
|
|
100
|
+
/** ISO expiry for the funding/quote window. When present, the signer refuses stale merchant headers. */
|
|
101
|
+
expiresAt?: string;
|
|
102
|
+
/**
|
|
103
|
+
* The delegate address this quote was created FOR (#1690). Present ⇒ the
|
|
104
|
+
* context is **version 3**, and the signer refuses to sign when it is not
|
|
105
|
+
* its own delegate. Inside the Haven-signed message, so it cannot be
|
|
106
|
+
* stripped or forged without breaking the binding signature.
|
|
107
|
+
*/
|
|
108
|
+
payerDelegate?: string;
|
|
109
|
+
/** The paying agent's id — carried for the refusal's diagnosis (#1690). */
|
|
110
|
+
payerAgentId?: string;
|
|
111
|
+
/** Haven signature over the expected funding context. */
|
|
112
|
+
auth: X402ExpectedAuth;
|
|
113
|
+
}
|
|
114
|
+
interface X402HeaderResult {
|
|
115
|
+
/** The merchant-verifiable X-PAYMENT header value. */
|
|
116
|
+
paymentHeader: string;
|
|
117
|
+
/** The x402 option this header pays. */
|
|
118
|
+
accepted: X402PaymentOption;
|
|
119
|
+
}
|
|
120
|
+
interface X402FundingSignatureResult {
|
|
121
|
+
/** Raw ECDSA signature over the Haven funding hash. */
|
|
122
|
+
signature: string;
|
|
123
|
+
/** Opaque process-local binding for the later merchant header signing step. */
|
|
124
|
+
x402Binding: string;
|
|
125
|
+
}
|
|
126
|
+
interface EdgeSignerOptions {
|
|
127
|
+
/** Address allowed to authenticate x402 expected-context messages from Haven. */
|
|
128
|
+
x402BindingSigner?: string;
|
|
129
|
+
/**
|
|
130
|
+
* This signer's OWN agent id, from the local credential (#1690). Used only
|
|
131
|
+
* to make the payer-mismatch refusal name both sides; the guard itself
|
|
132
|
+
* compares delegate addresses and works without it.
|
|
133
|
+
*/
|
|
134
|
+
agentId?: string;
|
|
135
|
+
}
|
|
136
|
+
declare function createEdgeSigner(delegateKey: string, options?: EdgeSignerOptions): EdgeSigner;
|
|
137
|
+
declare function assertX402MatchesExpected(paymentRequired: X402PaymentRequired, option: X402PaymentOption, expected: X402ExpectedPayment): void;
|
|
138
|
+
/**
|
|
139
|
+
* Expected-context binding versions THIS signer understands (#1143).
|
|
140
|
+
*
|
|
141
|
+
* The backend deploys continuously from `dev`; a signer reaches users only on a
|
|
142
|
+
* merge to `main` (the publish workflow). So a signer that is one release behind
|
|
143
|
+
* a context bump is a structural state, not an accident, and it needs to report
|
|
144
|
+
* itself as one. These sets are the signer's authority on what it will sign —
|
|
145
|
+
* the tool schemas deliberately accept any positive integer so an unknown
|
|
146
|
+
* version arrives *here* instead of dying at the schema boundary with a raw
|
|
147
|
+
* validation string.
|
|
148
|
+
*
|
|
149
|
+
* **Adding a version here is not sufficient to support it.** The mode rules in
|
|
150
|
+
* `assertExpectedBinding` derive the expected version from the context's
|
|
151
|
+
* *contents* (`typedDataHash` present ⇒ 2), not from `auth.version`, so a v3
|
|
152
|
+
* that carries anything new needs that derivation extended in the same change.
|
|
153
|
+
* Widening this array alone would admit a v3 context to the v1/v2 rule set:
|
|
154
|
+
* the array announces what this signer can evaluate, it does not define it.
|
|
155
|
+
*/
|
|
156
|
+
declare const SUPPORTED_X402_EXPECTED_VERSIONS: readonly number[];
|
|
157
|
+
declare const SUPPORTED_SWEEP_BINDING_VERSIONS: readonly number[];
|
|
158
|
+
/**
|
|
159
|
+
* Fail closed on a binding version this signer does not understand, with an
|
|
160
|
+
* error that names the received version, the ceiling this signer supports, and
|
|
161
|
+
* the fix — both as PROSE (`message`, unchanged since #1143) and, since #1309,
|
|
162
|
+
* as MACHINE-READABLE fields on the thrown error itself
|
|
163
|
+
* (`HavenUnsupportedSignerVersionError`): `code`, `supportedVersions`,
|
|
164
|
+
* `receivedVersion`, `fallback`. The tool boundary (`normalizeError` in
|
|
165
|
+
* `tools.ts`) relays those fields verbatim instead of leaving an agent to
|
|
166
|
+
* regex-parse this prose, which is the diagnosability gap
|
|
167
|
+
* `docs/operations/mcp-runtime-compatibility.md` documents.
|
|
168
|
+
*
|
|
169
|
+
* `supportedVersions`/`receivedVersion` are DERIVED from this call's own
|
|
170
|
+
* arguments — never a second literal — so they cannot drift from
|
|
171
|
+
* `SUPPORTED_X402_EXPECTED_VERSIONS` / `SUPPORTED_SWEEP_BINDING_VERSIONS`,
|
|
172
|
+
* which is what the signing path always passes in.
|
|
173
|
+
*
|
|
174
|
+
* The version travels inside the Haven-signed binding message, so the message
|
|
175
|
+
* also tells the caller not to "fix" it by rewriting the field — an agent that
|
|
176
|
+
* does would invalidate the signature and misrepresent what Haven declared.
|
|
177
|
+
* That instruction is NOT weakened by structuring the refusal: this function
|
|
178
|
+
* still throws before any content check runs, and nothing is ever signed.
|
|
179
|
+
*
|
|
180
|
+
* #2347: that word was "authorised" until this change, and the reading was
|
|
181
|
+
* always the correct one — Haven does sign this message. It is now "declared",
|
|
182
|
+
* the word `settlement-child.ts` already uses for this exact binding ("proves
|
|
183
|
+
* Haven *declared* a payload … it says nothing about what the payload MEANS"),
|
|
184
|
+
* because on an agent-facing refusal inside a payment flow the broader word
|
|
185
|
+
* invites the #2334 misreading that Haven is what authorises the spend. It is
|
|
186
|
+
* not; the owner-signed delegation and its on-chain caveat are. The property
|
|
187
|
+
* claimed is unchanged — only the verb naming it. Full argument in
|
|
188
|
+
* `docs/regulatory/casp-changelog/2026-09-01-2347.md`.
|
|
189
|
+
*
|
|
190
|
+
* Exported so a test can pin the historical case (a v2 context against a signer
|
|
191
|
+
* whose set was `{1}`) that this signer can no longer produce on its own. The
|
|
192
|
+
* signing path always passes the module constants above.
|
|
193
|
+
*/
|
|
194
|
+
declare function assertSupportedBindingVersion(received: number, supported: readonly number[], context: 'x402 expected context' | 'sweep authorization binding'): void;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Pre-payment skew detection (#1155).
|
|
198
|
+
*
|
|
199
|
+
* #1143 made a stale signer say so — but only at the signing step, after the
|
|
200
|
+
* agent had already quoted and was one call away from funding. This module
|
|
201
|
+
* moves the same information to the `initialize` handshake, so the set of
|
|
202
|
+
* expected-context versions this signer understands is observable *before* a
|
|
203
|
+
* payment is attempted.
|
|
204
|
+
*
|
|
205
|
+
* **The check is necessarily agent-mediated.** The signer and the hosted Haven
|
|
206
|
+
* MCP are two separate servers connected to the same client; neither can
|
|
207
|
+
* introspect the other. The signer's single Haven call (#1263, the read-only
|
|
208
|
+
* `GET /x402/:payment_id/sign-context` in `sign-context.ts`) does not help
|
|
209
|
+
* here: it fetches one payment's signing bytes, not the hosted server's
|
|
210
|
+
* handshake, and it happens at signing time — after the quote this module
|
|
211
|
+
* exists to get ahead of. So only the agent sees both handshakes, and what
|
|
212
|
+
* ships here is the *information* plus the prompt to compare it — never a
|
|
213
|
+
* server-side gate. A mismatch is advisory: the enforcement point remains the
|
|
214
|
+
* #1143 signing-time guard, which is unchanged.
|
|
215
|
+
*
|
|
216
|
+
* Everything below is DERIVED from the two constants in `core.ts` that the
|
|
217
|
+
* signing path actually enforces. A second hand-maintained literal is the one
|
|
218
|
+
* way this feature could become a lie, so there isn't one — including inside
|
|
219
|
+
* the human-readable `instructions` string, which renders the same arrays.
|
|
220
|
+
*/
|
|
221
|
+
/**
|
|
222
|
+
* Vendor-prefixed key under MCP's `capabilities.experimental`.
|
|
223
|
+
*
|
|
224
|
+
* `experimental` rather than the newer `extensions` field deliberately: both are
|
|
225
|
+
* `Record<string, object>` in `@modelcontextprotocol/sdk@1.29`, but a client on
|
|
226
|
+
* an older SDK parses the `initialize` result with a `ServerCapabilities` schema
|
|
227
|
+
* that has no `extensions` key, and Zod's default object behaviour would strip
|
|
228
|
+
* it. `experimental` has been in the schema since the beginning, so it survives
|
|
229
|
+
* an old client — which is precisely the population this feature exists for.
|
|
230
|
+
*/
|
|
231
|
+
declare const SIGNER_CAPABILITY_KEY = "haven/signer-compatibility";
|
|
232
|
+
interface SignerCompatibility {
|
|
233
|
+
/** Expected-context versions this signer will verify (`SUPPORTED_X402_EXPECTED_VERSIONS`). */
|
|
234
|
+
x402_expected_context_versions: number[];
|
|
235
|
+
/** Sweep-binding versions this signer will verify (`SUPPORTED_SWEEP_BINDING_VERSIONS`). */
|
|
236
|
+
sweep_binding_versions: number[];
|
|
237
|
+
}
|
|
238
|
+
/** The supported sets this signer enforces, as a plain serialisable object. */
|
|
239
|
+
declare function signerCompatibility(): SignerCompatibility;
|
|
240
|
+
/**
|
|
241
|
+
* The machine-readable half: `capabilities.experimental['haven/signer-compatibility']`,
|
|
242
|
+
* returned verbatim in the `initialize` result.
|
|
243
|
+
*
|
|
244
|
+
* `ServerCapabilities.experimental` is typed `z.record(z.string(), <any object>)`
|
|
245
|
+
* and the SDK's `mergeCapabilities` deep-merges per top-level key, so declaring
|
|
246
|
+
* this at construction survives the `tools` capability `McpServer` registers
|
|
247
|
+
* when the first tool is added.
|
|
248
|
+
*/
|
|
249
|
+
declare function signerCapabilityAdvertisement(): {
|
|
250
|
+
experimental: Record<string, SignerCompatibility>;
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* The agent-readable half: MCP `instructions`, which clients surface to the
|
|
254
|
+
* model. The machine-readable capability above is the precise statement, but
|
|
255
|
+
* most agent runtimes never expose `capabilities.experimental` to the model —
|
|
256
|
+
* this string is what actually reaches the reader who has to make the call.
|
|
257
|
+
*
|
|
258
|
+
* It names the same fix as #1143 so an agent that hits either surface — the
|
|
259
|
+
* handshake here or the signing-time error there — tells the user the same
|
|
260
|
+
* thing.
|
|
261
|
+
*/
|
|
262
|
+
declare function signerInstructions(): string;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The edge signer's own credential requires *only* `delegate_key` — unlike
|
|
266
|
+
* `@haven_ai/mcp`, no `api_key` belongs in this file.
|
|
267
|
+
*
|
|
268
|
+
* That is a claim about this credential, not about the process, and the
|
|
269
|
+
* difference matters: the sentence that used to stand here ("it does not call
|
|
270
|
+
* the Haven API") was retired by #1263. The MCP server layer does make one
|
|
271
|
+
* authenticated call — a read-only `GET /x402/:payment_id/sign-context`, see
|
|
272
|
+
* `sign-context.ts` — and it reads the `api_url` / `api_key` for it from a
|
|
273
|
+
* SEPARATE `identity.json` in the same directory as the credential file
|
|
274
|
+
* resolved here. That is why `sourcePath` below is load-bearing rather than
|
|
275
|
+
* diagnostic, and why a key supplied through `HAVEN_DELEGATE_KEY` alone (no
|
|
276
|
+
* file, so no directory) leaves that path with no identity to load and the
|
|
277
|
+
* process making no network calls at all. The delegate key read here is never
|
|
278
|
+
* part of any request or response either way.
|
|
279
|
+
*/
|
|
280
|
+
interface SignerCredentials {
|
|
281
|
+
delegateKey: string;
|
|
282
|
+
agentId?: string;
|
|
283
|
+
safeAddress?: string;
|
|
284
|
+
chainId?: number;
|
|
285
|
+
network?: string;
|
|
286
|
+
x402BindingSigner?: string;
|
|
287
|
+
/** Absolute path the key was loaded from, if a file was used. */
|
|
288
|
+
sourcePath?: string;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Resolve the delegate key for the edge signer.
|
|
292
|
+
*
|
|
293
|
+
* Order — earlier wins:
|
|
294
|
+
* 1. Explicit `path` (e.g. `--credentials <path>`).
|
|
295
|
+
* 2. `HAVEN_CREDENTIALS` env var pointing at a credential JSON file.
|
|
296
|
+
* 3. `HAVEN_DELEGATE_KEY` env var.
|
|
297
|
+
*
|
|
298
|
+
* The same credential JSON the dashboard emits works here — we just read its
|
|
299
|
+
* `delegate_key` and ignore the rest.
|
|
300
|
+
*/
|
|
301
|
+
declare function loadSignerCredentials(path?: string | undefined): Promise<SignerCredentials>;
|
|
302
|
+
/**
|
|
303
|
+
* Warn (best-effort, POSIX only) when the credential file is readable beyond
|
|
304
|
+
* its owner. Mirrors `@haven_ai/mcp`'s check — the delegate key is the most
|
|
305
|
+
* sensitive thing on the machine.
|
|
306
|
+
*
|
|
307
|
+
* Exported for testing.
|
|
308
|
+
*/
|
|
309
|
+
declare function warnIfCredentialFilePermissive(path: string, log?: (message: string) => void, platform?: NodeJS.Platform): Promise<void>;
|
|
310
|
+
|
|
311
|
+
interface SigningAuditEntry {
|
|
312
|
+
version: 1;
|
|
313
|
+
timestamp: string;
|
|
314
|
+
tool: SignerToolName;
|
|
315
|
+
payload_hash: string;
|
|
316
|
+
delegate_address: string;
|
|
317
|
+
safe_address?: string;
|
|
318
|
+
chain_id?: number;
|
|
319
|
+
}
|
|
320
|
+
interface SigningAuditContext {
|
|
321
|
+
delegateAddress: string;
|
|
322
|
+
safeAddress?: string;
|
|
323
|
+
chainId?: number;
|
|
324
|
+
auditPath?: string;
|
|
325
|
+
}
|
|
326
|
+
declare function defaultSigningAuditPath(credentialsPath?: string): string;
|
|
327
|
+
declare function appendSigningAuditEntry(entry: SigningAuditEntry, path: string): Promise<void>;
|
|
328
|
+
declare function createSigningAuditEntry(tool: SignerToolName, payloadHash: string, context: SigningAuditContext, now?: Date): SigningAuditEntry;
|
|
329
|
+
declare function hashPayloadForAudit(payload: unknown): string;
|
|
330
|
+
|
|
331
|
+
interface HavenIdentity {
|
|
332
|
+
apiUrl: string;
|
|
333
|
+
apiKey: string;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Local signer tool set. These run on the agent's machine, next to the key,
|
|
338
|
+
* and pair with the hosted server's construct/relay tools (#183). They sign;
|
|
339
|
+
* they never call the Haven API and never emit the key.
|
|
340
|
+
*/
|
|
341
|
+
type SignerToolName = 'haven_sign' | 'haven_x402_sign_header' | 'haven_sign_x402' | 'haven_sign_sweep_delegate';
|
|
342
|
+
declare const toolSchemas: Record<SignerToolName, z.ZodRawShape>;
|
|
343
|
+
declare const toolDescriptions: Record<SignerToolName, string>;
|
|
344
|
+
interface ToolSuccess<T> {
|
|
345
|
+
success: true;
|
|
346
|
+
data: T;
|
|
347
|
+
}
|
|
348
|
+
interface ToolFailure {
|
|
349
|
+
success: false;
|
|
350
|
+
code: string;
|
|
351
|
+
message: string;
|
|
352
|
+
statusCode?: number;
|
|
353
|
+
paymentId?: string;
|
|
354
|
+
next_action?: string;
|
|
355
|
+
retry_with_new_quote?: boolean;
|
|
356
|
+
suggested_tool?: string;
|
|
357
|
+
/**
|
|
358
|
+
* #1309: present on `UNSUPPORTED_EXPECTED_CONTEXT_VERSION` /
|
|
359
|
+
* `UNSUPPORTED_SWEEP_BINDING_VERSION` refusals — the exact version set this
|
|
360
|
+
* signer install enforces, derived from `SUPPORTED_X402_EXPECTED_VERSIONS` /
|
|
361
|
+
* `SUPPORTED_SWEEP_BINDING_VERSIONS` at the throw site.
|
|
362
|
+
*/
|
|
363
|
+
supported_versions?: number[];
|
|
364
|
+
/** #1309: the version Haven sent that triggered the refusal above. */
|
|
365
|
+
received_version?: number;
|
|
366
|
+
/**
|
|
367
|
+
* #1309: precise recovery guidance as DATA, not just prose inside `message`
|
|
368
|
+
* — the same text the hosted quote's advisory `signer_compatibility.fallback`
|
|
369
|
+
* carries when the refusal is an out-of-date signer.
|
|
370
|
+
*/
|
|
371
|
+
fallback?: string;
|
|
372
|
+
}
|
|
373
|
+
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
374
|
+
interface ToolHandlerOptions {
|
|
375
|
+
audit?: SigningAuditContext & {
|
|
376
|
+
auditPath: string;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* #1263: how the payment_id signing path reaches Haven. Lives at the MCP
|
|
380
|
+
* server layer (the core stays network-free); absent → payment_id calls
|
|
381
|
+
* refuse with a message naming the typed_data_b64 fallback.
|
|
382
|
+
*/
|
|
383
|
+
signContext?: {
|
|
384
|
+
loadIdentity: () => Promise<HavenIdentity | null>;
|
|
385
|
+
fetchImpl?: typeof fetch;
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
declare function createToolHandlers(signer: EdgeSigner, options?: ToolHandlerOptions): Record<SignerToolName, (input: unknown) => Promise<ToolPayload>>;
|
|
389
|
+
|
|
390
|
+
interface SignerConsentInput {
|
|
391
|
+
delegateAddress: string;
|
|
392
|
+
safeAddress?: string;
|
|
393
|
+
agentId?: string;
|
|
394
|
+
chainId?: number;
|
|
395
|
+
network?: string;
|
|
396
|
+
toolNames: readonly SignerToolName[];
|
|
397
|
+
}
|
|
398
|
+
interface SignerConsentDecision {
|
|
399
|
+
ok: boolean;
|
|
400
|
+
hash: string;
|
|
401
|
+
reason: 'env_var_match' | 'ack_file_match' | 'wrote_ack_file' | 'env_var_mismatch' | 'no_acknowledgement';
|
|
402
|
+
}
|
|
403
|
+
interface SignerConsentOptions {
|
|
404
|
+
credentialsPath?: string;
|
|
405
|
+
writeAck?: boolean;
|
|
406
|
+
env?: Record<string, string | undefined>;
|
|
407
|
+
out?: {
|
|
408
|
+
write: (chunk: string) => unknown;
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
declare const SIGNER_ACK_ENV = "HAVEN_SIGNER_ACK";
|
|
412
|
+
declare function computeSignerConsentHash(input: SignerConsentInput): string;
|
|
413
|
+
declare function renderSignerConsentBlock(input: SignerConsentInput, hash: string): string;
|
|
414
|
+
declare function ensureSignerConsent(input: SignerConsentInput, options?: SignerConsentOptions): Promise<SignerConsentDecision>;
|
|
415
|
+
|
|
416
|
+
declare const SIGNER_NAME = "@haven_ai/signer";
|
|
417
|
+
declare const SIGNER_VERSION = "0.0.0-dev.202609031523.fd49e1a";
|
|
418
|
+
interface SignerOptions {
|
|
419
|
+
/** Path to a Haven credential JSON file (delegate_key is read from it). */
|
|
420
|
+
credentialsPath?: string;
|
|
421
|
+
/** Pre-resolved delegate key (skips credential loading). */
|
|
422
|
+
delegateKey?: string;
|
|
423
|
+
/** Append local signing audit entries here. Defaults to a credential sidecar or ~/.haven. */
|
|
424
|
+
auditPath?: string;
|
|
425
|
+
/** Overridable so the Node-floor refusal is testable without spawning a Node. */
|
|
426
|
+
nodeVersion?: string;
|
|
427
|
+
/**
|
|
428
|
+
* When true, write the consent sidecar file (`<credentials>.signer-ack.json`)
|
|
429
|
+
* with the current consent hash and proceed. Surfaced via the `--ack` CLI flag.
|
|
430
|
+
*/
|
|
431
|
+
writeAck?: boolean;
|
|
432
|
+
/**
|
|
433
|
+
* When true, skip the consent gate entirely. Reserved for tests and controlled
|
|
434
|
+
* embedding — production CLIs should not set this.
|
|
435
|
+
*/
|
|
436
|
+
skipConsent?: boolean;
|
|
437
|
+
/** Override consent environment lookup. Reserved for tests and controlled embedding. */
|
|
438
|
+
consentEnv?: Record<string, string | undefined>;
|
|
439
|
+
/** Override the stream the consent block is printed to. Reserved for tests. */
|
|
440
|
+
consentOut?: {
|
|
441
|
+
write: (chunk: string) => unknown;
|
|
442
|
+
};
|
|
443
|
+
/** Trusted Haven signer address for x402 expected-context bindings. */
|
|
444
|
+
x402BindingSigner?: string;
|
|
445
|
+
}
|
|
446
|
+
declare function resolveEdgeSigner(options?: SignerOptions): Promise<EdgeSigner>;
|
|
447
|
+
interface ResolvedSignerRuntime {
|
|
448
|
+
signer: EdgeSigner;
|
|
449
|
+
credentials?: SignerCredentials;
|
|
450
|
+
}
|
|
451
|
+
declare function resolveSignerRuntime(options?: SignerOptions): Promise<ResolvedSignerRuntime>;
|
|
452
|
+
/**
|
|
453
|
+
* Build a local stdio MCP server exposing the sign-only tools, bound to an
|
|
454
|
+
* edge signer that holds the delegate key. It exposes no construct/relay
|
|
455
|
+
* tools — it only signs. Its ONE network capability (#1263) is an
|
|
456
|
+
* authenticated READ: fetching a payment's exact signing context from Haven
|
|
457
|
+
* by payment_id, so agents never have to relay multi-KB signing payloads
|
|
458
|
+
* through a model context. The signer CORE below it stays network-free, and
|
|
459
|
+
* fetched bytes pass the same verification as tool-argument bytes.
|
|
460
|
+
*/
|
|
461
|
+
declare function buildSignerMcpServer(signer: EdgeSigner, options?: Pick<SignerOptions, 'auditPath'> & {
|
|
462
|
+
credentials?: SignerCredentials;
|
|
463
|
+
}): McpServer;
|
|
464
|
+
/**
|
|
465
|
+
* Refuse to start on an unsupported Node (#1161).
|
|
466
|
+
*
|
|
467
|
+
* Asserted at STARTUP, not only at install, because the two can diverge: a user
|
|
468
|
+
* upgrades Node, connects successfully, then downgrades — or a version manager
|
|
469
|
+
* hands the agent runtime a different Node than the shell that ran setup. Only a
|
|
470
|
+
* startup check sees the version this process is actually running on.
|
|
471
|
+
*
|
|
472
|
+
* This is the signer, so refusing is the conservative answer rather than the
|
|
473
|
+
* aggressive one. It holds the delegate key and produces every payment
|
|
474
|
+
* signature; on an unsupported runtime the plausible failure is a wrong or
|
|
475
|
+
* absent signature, which is worse than not starting. The consent gate below
|
|
476
|
+
* takes the same posture for the same reason.
|
|
477
|
+
*/
|
|
478
|
+
declare function assertSupportedNodeVersion(nodeVersion?: string): void;
|
|
479
|
+
declare function runSignerStdioServer(options?: SignerOptions): Promise<void>;
|
|
480
|
+
declare function runSignerConsentGate(signer: EdgeSigner, credentials: SignerCredentials | undefined, options: SignerOptions): Promise<SignerConsentDecision>;
|
|
481
|
+
|
|
482
|
+
export { type EdgeSigner, type ResolvedSignerRuntime, SIGNER_ACK_ENV, SIGNER_CAPABILITY_KEY, SIGNER_NAME, SIGNER_VERSION, SUPPORTED_SWEEP_BINDING_VERSIONS, SUPPORTED_X402_EXPECTED_VERSIONS, type SignerCompatibility, type SignerConsentDecision, type SignerConsentInput, type SignerConsentOptions, type SignerCredentials, type SignerOptions, type SignerToolName, type SigningAuditContext, type SigningAuditEntry, type ToolFailure, type ToolPayload, type ToolSuccess, type X402ExpectedPayment, type X402FundingSignatureResult, type X402HeaderResult, appendSigningAuditEntry, assertSupportedBindingVersion, assertSupportedNodeVersion, assertX402MatchesExpected, buildSignerMcpServer, computeSignerConsentHash, createEdgeSigner, createSigningAuditEntry, createToolHandlers, defaultSigningAuditPath, ensureSignerConsent, hashPayloadForAudit, loadSignerCredentials, renderSignerConsentBlock, resolveEdgeSigner, resolveSignerRuntime, runSignerConsentGate, runSignerStdioServer, signerCapabilityAdvertisement, signerCompatibility, signerInstructions, toolDescriptions, toolSchemas, warnIfCredentialFilePermissive };
|