@haven_ai/signer 0.4.0-alpha.0 → 0.6.0-alpha.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/dist/index.d.ts CHANGED
@@ -1,21 +1,23 @@
1
- import { X402ExpectedAuth, X402PaymentRequired, X402PaymentOption, SweepAuthorization, SweepExpectedAuth } from '@haven_ai/sdk';
1
+ import { X402ExpectedAuth, X402PaymentRequired, X402PaymentOption, SweepAuthorization, SweepExpectedAuth, HavenClientUpdate } from '@haven_ai/sdk/edge';
2
+ export { deriveDelegateAccountAddress } from '@haven_ai/sdk/edge';
2
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { z } from 'zod/v3';
4
- import { z as z$1 } from 'zod';
4
+ import { z as z$1 } from 'zod/v3';
5
+ import { Stats } from 'node:fs';
6
+ import { z } from 'zod';
5
7
 
6
8
  /**
7
9
  * The edge signer core.
8
10
  *
9
- * Holds the delegate key in this process and exposes the two signing
10
- * operations a hosted-MCP flow needs. It performs no network I/O and never
11
+ * Holds the delegate key in this process and exposes the signing operations a
12
+ * hosted-MCP flow needs — five, none of them a raw-hash primitive (#3169): each
13
+ * takes a payload something can check — a Haven binding verified here, typed
14
+ * data the account validates on-chain, or a sweep authorization. It performs no network I/O and never
11
15
  * returns the key — only signatures and the standard x402 header. See
12
16
  * docs/architecture/07-edge-signer.md.
13
17
  */
14
18
  interface EdgeSigner {
15
19
  /** Address derived from the delegate key. */
16
20
  readonly delegateAddress: string;
17
- /** Sign an AllowanceModule funding/transfer hash (raw ECDSA, 65 bytes). */
18
- signPaymentHash(hash: string): string;
19
21
  /**
20
22
  * Sign a DIRECT delegation-rail payment's EIP-712 typed data (#1254) — the
21
23
  * non-x402 counterpart of `signX402FundingTypedData`. The Hybrid account
@@ -26,24 +28,27 @@ interface EdgeSigner {
26
28
  * visible to this process's audit log.
27
29
  */
28
30
  signDelegationTypedData(typedData: Record<string, unknown>): Promise<string>;
29
- /** Sign an x402 funding hash and remember the funded merchant-header context. */
30
- signX402FundingHash(hash: string, expected: X402ExpectedPayment): X402FundingSignatureResult;
31
31
  /**
32
32
  * Sign a delegation-rail x402 funding intent's EIP-712 typed data (#1138) and
33
- * remember the funded merchant-header context, exactly as the hash path does.
33
+ * remember the funded merchant-header context — every x402 funding intent
34
+ * now takes this path (#3272 criterion 8: the bare-hash v1 rail is
35
+ * retired).
34
36
  *
35
- * The account validates this typed data, NOT the bare ERC-4337 hash, so the
36
- * expected context must be v2 and commit to its digest — see
37
- * `assertExpectedBinding`.
37
+ * The account validates this typed data, NOT a bare hash, so the expected
38
+ * context must be v2/v3 and commit to its digest — see
39
+ * `assertExpectedBinding`. `typedData` is `undefined` when the caller had
40
+ * none to supply; that is itself refused (a v1 context that needed no typed
41
+ * data is refused earlier, by the version check).
38
42
  */
39
- signX402FundingTypedData(typedData: X402FundingTypedData, expected: X402ExpectedPayment): Promise<X402FundingSignatureResult>;
43
+ signX402FundingTypedData(typedData: X402FundingTypedData | undefined, expected: X402ExpectedPayment): Promise<X402FundingSignatureResult>;
40
44
  /** Build + sign the EIP-3009 X-PAYMENT header for the merchant leg of x402. */
41
45
  buildX402PaymentHeader(paymentRequired: X402PaymentRequired, x402Binding: string): Promise<X402HeaderResult>;
42
46
  /**
43
47
  * Sign a Haven-prepared EIP-3009 sweep authorization (gasless USDC recovery
44
- * delegate → Safe). Verifies the authorization came from Haven and pays out to
45
- * the delegate's own Safe before signing; the relayer broadcasts it and pays
46
- * gas. Never broadcasts — pure signing.
48
+ * delegate → the agent's account (Haven wallet)). Verifies the authorization
49
+ * came from Haven and pays out to the agent's account (Haven wallet) — the
50
+ * account belongs to the agent, not the delegate — before signing; the
51
+ * relayer broadcasts it and pays gas. Never broadcasts — pure signing.
47
52
  */
48
53
  signSweepAuthorization(input: SweepSignatureInput): Promise<SweepSignatureResult>;
49
54
  }
@@ -52,7 +57,7 @@ interface SweepSignatureInput {
52
57
  authorization: SweepAuthorization;
53
58
  /** Haven's signature over the authorization context (binding). */
54
59
  expectedAuth: SweepExpectedAuth;
55
- /** Optional Safe address from the local credential, cross-checked against `to`. */
60
+ /** Optional account (Haven wallet) address from the local credential, cross-checked against `to` when present. */
56
61
  expectedSafe?: string;
57
62
  }
58
63
  interface SweepSignatureResult {
@@ -121,7 +126,7 @@ interface X402HeaderResult {
121
126
  accepted: X402PaymentOption;
122
127
  }
123
128
  interface X402FundingSignatureResult {
124
- /** Raw ECDSA signature over the Haven funding hash. */
129
+ /** EIP-712 signature over the Haven-committed funding typed data. */
125
130
  signature: string;
126
131
  /** Opaque process-local binding for the later merchant header signing step. */
127
132
  x402Binding: string;
@@ -149,11 +154,12 @@ declare function assertX402MatchesExpected(paymentRequired: X402PaymentRequired,
149
154
  * version arrives *here* instead of dying at the schema boundary with a raw
150
155
  * validation string.
151
156
  *
152
- * **Adding a version here is not sufficient to support it.** The mode rules in
157
+ * **Adding a version here is not sufficient to support it.** The rules in
153
158
  * `assertExpectedBinding` derive the expected version from the context's
154
- * *contents* (`typedDataHash` present ⇒ 2), not from `auth.version`, so a v3
155
- * that carries anything new needs that derivation extended in the same change.
156
- * Widening this array alone would admit a v3 context to the v1/v2 rule set:
159
+ * *contents* (`payerDelegate` present ⇒ 3, else 2 — `typedDataHash` is
160
+ * required unconditionally, #3272), not from `auth.version`, so a v4 that
161
+ * carries anything new needs that derivation extended in the same change.
162
+ * Widening this array alone would admit a v4 context to the v2/v3 rule set:
157
163
  * the array announces what this signer can evaluate, it does not define it.
158
164
  */
159
165
  declare const SUPPORTED_X402_EXPECTED_VERSIONS: readonly number[];
@@ -182,7 +188,7 @@ declare const SUPPORTED_SWEEP_BINDING_VERSIONS: readonly number[];
182
188
  *
183
189
  * #2347: that word was "authorised" until this change, and the reading was
184
190
  * always the correct one — Haven does sign this message. It is now "declared",
185
- * the word `settlement-child.ts` already uses for this exact binding ("proves
191
+ * the word the settlement-child verifier (`@haven_ai/sdk`'s `settlement-child.ts`) already uses for this exact binding ("proves
186
192
  * Haven *declared* a payload … it says nothing about what the payload MEANS"),
187
193
  * because on an agent-facing refusal inside a payment flow the broader word
188
194
  * invites the #2334 misreading that Haven is what authorises the spend. It is
@@ -207,9 +213,10 @@ declare function assertSupportedBindingVersion(received: number, supported: read
207
213
  *
208
214
  * **The check is necessarily agent-mediated.** The signer and the hosted Haven
209
215
  * MCP are two separate servers connected to the same client; neither can
210
- * introspect the other. The signer's single Haven call (#1263, the read-only
211
- * `GET /x402/:payment_id/sign-context` in `sign-context.ts`) does not help
212
- * here: it fetches one payment's signing bytes, not the hosted server's
216
+ * introspect the other. The signer's Haven reads (#1263's read-only
217
+ * `GET /x402/:payment_id/sign-context` and #3271's direct-payment
218
+ * `GET /payments/:payment_id/sign-context`, both in `sign-context.ts`) do not help
219
+ * here: each fetches one payment's signing bytes, not the hosted server's
213
220
  * handshake, and it happens at signing time — after the quote this module
214
221
  * exists to get ahead of. So only the agent sees both handshakes, and what
215
222
  * ships here is the *information* plus the prompt to compare it — never a
@@ -237,6 +244,20 @@ interface SignerCompatibility {
237
244
  x402_expected_context_versions: number[];
238
245
  /** Sweep-binding versions this signer will verify (`SUPPORTED_SWEEP_BINDING_VERSIONS`). */
239
246
  sweep_binding_versions: number[];
247
+ /**
248
+ * #3271: `direct_sign_context_version`s this signer will fetch and verify
249
+ * from `GET /payments/:id/sign-context` — derived from the SDK's
250
+ * `DIRECT_SIGN_CONTEXT_VERSION` via `SUPPORTED_DIRECT_SIGN_CONTEXT_VERSIONS`,
251
+ * never a second literal.
252
+ */
253
+ direct_sign_context_versions: number[];
254
+ /**
255
+ * #3329: `task_sign_context_version`s this signer will fetch and verify
256
+ * from `GET /task-budgets/:id/sign-context` — derived from
257
+ * `SUPPORTED_TASK_SIGN_CONTEXT_VERSIONS`, never a second literal, same
258
+ * discipline as `direct_sign_context_versions`.
259
+ */
260
+ task_sign_context_versions: number[];
240
261
  }
241
262
  /** The supported sets this signer enforces, as a plain serialisable object. */
242
263
  declare function signerCompatibility(): SignerCompatibility;
@@ -270,9 +291,11 @@ declare function signerInstructions(): string;
270
291
  *
271
292
  * That is a claim about this credential, not about the process, and the
272
293
  * difference matters: the sentence that used to stand here ("it does not call
273
- * the Haven API") was retired by #1263. The MCP server layer does make one
274
- * authenticated call — a read-only `GET /x402/:payment_id/sign-context`, see
275
- * `sign-context.ts` — and it reads the `api_url` / `api_key` for it from a
294
+ * the Haven API") was retired by #1263. The MCP server layer does make at
295
+ * most two authenticated reads per signing call — a read-only
296
+ * `GET /x402/:payment_id/sign-context` and, for a direct payment via
297
+ * `haven_sign` since #3271, then `GET /payments/:payment_id/sign-context`, see
298
+ * `sign-context.ts` — and it reads the `api_url` / `api_key` for them from a
276
299
  * SEPARATE `identity.json` in the same directory as the credential file
277
300
  * resolved here. That is why `sourcePath` below is load-bearing rather than
278
301
  * diagnostic, and why a key supplied through `HAVEN_DELEGATE_KEY` alone (no
@@ -353,6 +376,65 @@ declare function readAccountAddressEnv(env: NodeJS.ProcessEnv): string | undefin
353
376
  */
354
377
  declare function warnIfCredentialFilePermissive(path: string, log?: (message: string) => void, platform?: NodeJS.Platform): Promise<void>;
355
378
 
379
+ /** Owner-only: the mode every file this package writes beside a credential gets. */
380
+ declare const OWNER_ONLY_MODE = 384;
381
+ type PermissionLog = (message: string) => void;
382
+ interface PermissiveFile {
383
+ /** The offending mode, e.g. `0644`. */
384
+ octal: string;
385
+ /** False for a symlink, directory or device at that path — never chmod those. */
386
+ regular: boolean;
387
+ }
388
+ /**
389
+ * Is this path readable, writable or executable by anyone but its owner?
390
+ * Returns null when the path cannot be stat'ed or is owner-only, and on
391
+ * Windows where POSIX mode bits do not map cleanly (the same carve-out
392
+ * `@haven_ai/mcp` makes). Pass `stats` to reuse a stat the caller took
393
+ * IMMEDIATELY before — with `stats` the path is not touched, so a cached
394
+ * `Stats` would be trusted over the disk. `follow` decides what a symlink at the path means: a file the signer
395
+ * only READS (the credential) is judged by its target, which is what the
396
+ * content protection is about and what the pre-#3172 `stat` did; a file the
397
+ * signer is about to CHMOD (the audit sidecar) is judged as the link, so a
398
+ * planted symlink is never chmod-ed through (#3172 review).
399
+ */
400
+ declare function permissiveMode(path: string, platform?: NodeJS.Platform, stats?: Stats, follow?: 'follow' | 'nofollow'): Promise<PermissiveFile | null>;
401
+ /**
402
+ * Warn (best-effort, POSIX only) when a file this signer READS is readable
403
+ * beyond its owner. The signer does not own the credential, so it tells the
404
+ * operator what to run rather than changing a file it was handed. Follows a
405
+ * symlink, as the pre-#3172 `stat` did: a link to a 0600 credential is fine.
406
+ */
407
+ declare function warnIfFilePermissive(kind: string, path: string, log?: PermissionLog, platform?: NodeJS.Platform): Promise<void>;
408
+ type TightenOutcome = 'owner-only' | 'tightened' | 'warned' | 'skipped';
409
+ /**
410
+ * Tighten a file this signer WRITES to owner-only when it finds it
411
+ * permissive (#3172): the audit sidecar was created with the default mode
412
+ * (0644 under the usual umask 022) by every release before this one, so an
413
+ * existing sidecar is fixed in place rather than warned about. Judged with
414
+ * `lstat`. Falls back to the warning when the path is not a regular file (a
415
+ * symlink would make chmod hit whatever it points at) or when chmod is
416
+ * refused (a file owned by another user). Outcomes: `tightened` / `warned`
417
+ * as above; `owner-only` when there is nothing to tighten (owner-only file,
418
+ * or no file at that path); `skipped` on Windows only.
419
+ */
420
+ declare function tightenIfFilePermissive(kind: string, path: string, log?: PermissionLog, platform?: NodeJS.Platform, stats?: Stats): Promise<TightenOutcome>;
421
+
422
+ /**
423
+ * #3172: the sidecar is bounded. When it reaches this size the current file is
424
+ * renamed to `<path>.1` (replacing the previous `.1`) and a fresh file starts,
425
+ * so at most two generations — the live file and one predecessor — ever exist.
426
+ * 8 MiB is roughly 30 000 entries at the ~270-byte row size; nothing in the
427
+ * signer reads the file back, so the bound protects the disk and the reader's
428
+ * patience, never a signing decision.
429
+ */
430
+ declare const AUDIT_ROTATE_BYTES: number;
431
+ interface AppendAuditOptions {
432
+ /** Where permission notices go; defaults to stderr. */
433
+ log?: PermissionLog;
434
+ /** Rotation threshold in bytes; exported default `AUDIT_ROTATE_BYTES`. */
435
+ rotateAtBytes?: number;
436
+ platform?: NodeJS.Platform;
437
+ }
356
438
  interface SigningAuditEntry {
357
439
  version: 1;
358
440
  timestamp: string;
@@ -377,7 +459,7 @@ interface SigningAuditContext {
377
459
  auditPath?: string;
378
460
  }
379
461
  declare function defaultSigningAuditPath(credentialsPath?: string): string;
380
- declare function appendSigningAuditEntry(entry: SigningAuditEntry, path: string): Promise<void>;
462
+ declare function appendSigningAuditEntry(entry: SigningAuditEntry, path: string, options?: AppendAuditOptions): Promise<void>;
381
463
  declare function createSigningAuditEntry(tool: SignerToolName, payloadHash: string, context: SigningAuditContext, now?: Date): SigningAuditEntry;
382
464
  declare function hashPayloadForAudit(payload: unknown): string;
383
465
 
@@ -386,6 +468,26 @@ interface HavenIdentity {
386
468
  apiKey: string;
387
469
  }
388
470
 
471
+ /**
472
+ * #3103 (epic #3105, slice 4/5): the signer's typed next step.
473
+ *
474
+ * The signer decides a `next_action` on its refusals and, until this module,
475
+ * never said which tool to call. It cannot know the client's server names
476
+ * (#2550), so the handoff is expressed through the role fields the SDK
477
+ * builder renders — `next_tool_server_role: hosted`, `next_tool_name` — from
478
+ * a bare tool name. The signer must not import the hosted server (it is an
479
+ * npm package installed on user machines; the hosted server is a Docker
480
+ * deployment), so the hosted tools it hands off to are DECLARED here as the
481
+ * argument shape the signer actually emits, and `next-step-signer-parity`
482
+ * in the hosted server's own suite pins them to the hosted schemas at test
483
+ * time — the mirror of how the hosted server declares the signer's shapes.
484
+ */
485
+ declare const SIGNER_HOSTED_HANDOFF_SHAPES: {
486
+ readonly haven_get_payment_status: {
487
+ readonly payment_id: z.ZodString;
488
+ };
489
+ };
490
+
389
491
  /**
390
492
  * Local signer tool set. These run on the agent's machine, next to the key,
391
493
  * and pair with the hosted server's construct/relay tools (#183). They sign;
@@ -394,16 +496,16 @@ interface HavenIdentity {
394
496
  type SignerToolName = 'haven_sign' | 'haven_x402_sign_header' | 'haven_sign_x402' | 'haven_sign_sweep_delegate';
395
497
  declare const toolSchemas: {
396
498
  readonly haven_sign_sweep_delegate: {
397
- readonly authorization: z.ZodObject<{
398
- from: z.ZodString;
399
- to: z.ZodString;
400
- value: z.ZodString;
401
- validAfter: z.ZodString;
402
- validBefore: z.ZodString;
403
- nonce: z.ZodString;
404
- token: z.ZodString;
405
- chainId: z.ZodNumber;
406
- }, "strip", z.ZodTypeAny, {
499
+ readonly authorization: z$1.ZodObject<{
500
+ from: z$1.ZodString;
501
+ to: z$1.ZodString;
502
+ value: z$1.ZodString;
503
+ validAfter: z$1.ZodString;
504
+ validBefore: z$1.ZodString;
505
+ nonce: z$1.ZodString;
506
+ token: z$1.ZodString;
507
+ chainId: z$1.ZodNumber;
508
+ }, "strip", z$1.ZodTypeAny, {
407
509
  chainId: number;
408
510
  to: string;
409
511
  from: string;
@@ -422,12 +524,12 @@ declare const toolSchemas: {
422
524
  validAfter: string;
423
525
  validBefore: string;
424
526
  }>;
425
- readonly expected_auth: z.ZodObject<{
426
- version: z.ZodNumber;
427
- message: z.ZodString;
428
- signature: z.ZodString;
429
- signer: z.ZodString;
430
- }, "strip", z.ZodTypeAny, {
527
+ readonly expected_auth: z$1.ZodObject<{
528
+ version: z$1.ZodNumber;
529
+ message: z$1.ZodString;
530
+ signature: z$1.ZodString;
531
+ signer: z$1.ZodString;
532
+ }, "strip", z$1.ZodTypeAny, {
431
533
  message: string;
432
534
  version: number;
433
535
  signature: string;
@@ -440,26 +542,26 @@ declare const toolSchemas: {
440
542
  }>;
441
543
  };
442
544
  readonly haven_sign: {
443
- readonly payment_id: z.ZodOptional<z.ZodString>;
444
- readonly payload_hash: z.ZodOptional<z.ZodString>;
445
- readonly x402_expected: z.ZodOptional<z.ZodObject<{
446
- payment_id: z.ZodString;
447
- payload_hash: z.ZodString;
448
- resource_url: z.ZodString;
449
- merchant_to: z.ZodString;
450
- amount: z.ZodString;
451
- asset: z.ZodString;
452
- network: z.ZodString;
453
- expires_at: z.ZodString;
454
- typed_data_hash: z.ZodOptional<z.ZodString>;
455
- payer_delegate: z.ZodOptional<z.ZodString>;
456
- payer_agent_id: z.ZodOptional<z.ZodString>;
457
- auth: z.ZodObject<{
458
- version: z.ZodNumber;
459
- message: z.ZodString;
460
- signature: z.ZodString;
461
- signer: z.ZodString;
462
- }, "strip", z.ZodTypeAny, {
545
+ readonly payment_id: z$1.ZodOptional<z$1.ZodString>;
546
+ readonly payload_hash: z$1.ZodOptional<z$1.ZodString>;
547
+ readonly x402_expected: z$1.ZodOptional<z$1.ZodObject<{
548
+ payment_id: z$1.ZodString;
549
+ payload_hash: z$1.ZodString;
550
+ resource_url: z$1.ZodString;
551
+ merchant_to: z$1.ZodString;
552
+ amount: z$1.ZodString;
553
+ asset: z$1.ZodString;
554
+ network: z$1.ZodString;
555
+ expires_at: z$1.ZodString;
556
+ typed_data_hash: z$1.ZodOptional<z$1.ZodString>;
557
+ payer_delegate: z$1.ZodOptional<z$1.ZodString>;
558
+ payer_agent_id: z$1.ZodOptional<z$1.ZodString>;
559
+ auth: z$1.ZodObject<{
560
+ version: z$1.ZodNumber;
561
+ message: z$1.ZodString;
562
+ signature: z$1.ZodString;
563
+ signer: z$1.ZodString;
564
+ }, "strip", z$1.ZodTypeAny, {
463
565
  message: string;
464
566
  version: number;
465
567
  signature: string;
@@ -470,15 +572,15 @@ declare const toolSchemas: {
470
572
  signature: string;
471
573
  signer: string;
472
574
  }>;
473
- }, "strip", z.ZodTypeAny, {
474
- network: string;
575
+ }, "strip", z$1.ZodTypeAny, {
475
576
  payment_id: string;
577
+ expires_at: string;
578
+ network: string;
476
579
  payload_hash: string;
477
580
  resource_url: string;
478
581
  merchant_to: string;
479
582
  amount: string;
480
583
  asset: string;
481
- expires_at: string;
482
584
  auth: {
483
585
  message: string;
484
586
  version: number;
@@ -489,14 +591,14 @@ declare const toolSchemas: {
489
591
  payer_delegate?: string | undefined;
490
592
  payer_agent_id?: string | undefined;
491
593
  }, {
492
- network: string;
493
594
  payment_id: string;
595
+ expires_at: string;
596
+ network: string;
494
597
  payload_hash: string;
495
598
  resource_url: string;
496
599
  merchant_to: string;
497
600
  amount: string;
498
601
  asset: string;
499
- expires_at: string;
500
602
  auth: {
501
603
  message: string;
502
604
  version: number;
@@ -507,34 +609,35 @@ declare const toolSchemas: {
507
609
  payer_delegate?: string | undefined;
508
610
  payer_agent_id?: string | undefined;
509
611
  }>>;
510
- readonly typed_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
511
- readonly typed_data_b64: z.ZodOptional<z.ZodString>;
612
+ readonly typed_data: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodUnknown>>;
613
+ readonly typed_data_b64: z$1.ZodOptional<z$1.ZodString>;
614
+ readonly task_budget_id: z$1.ZodOptional<z$1.ZodString>;
512
615
  };
513
616
  readonly haven_x402_sign_header: {
514
- readonly payment_required: z.ZodRecord<z.ZodString, z.ZodUnknown>;
515
- readonly x402_binding: z.ZodString;
617
+ readonly payment_required: z$1.ZodRecord<z$1.ZodString, z$1.ZodUnknown>;
618
+ readonly x402_binding: z$1.ZodString;
516
619
  };
517
620
  readonly haven_sign_x402: {
518
- readonly payment_id: z.ZodOptional<z.ZodString>;
519
- readonly payload_hash: z.ZodOptional<z.ZodString>;
520
- readonly x402_expected: z.ZodOptional<z.ZodObject<{
521
- payment_id: z.ZodString;
522
- payload_hash: z.ZodString;
523
- resource_url: z.ZodString;
524
- merchant_to: z.ZodString;
525
- amount: z.ZodString;
526
- asset: z.ZodString;
527
- network: z.ZodString;
528
- expires_at: z.ZodString;
529
- typed_data_hash: z.ZodOptional<z.ZodString>;
530
- payer_delegate: z.ZodOptional<z.ZodString>;
531
- payer_agent_id: z.ZodOptional<z.ZodString>;
532
- auth: z.ZodObject<{
533
- version: z.ZodNumber;
534
- message: z.ZodString;
535
- signature: z.ZodString;
536
- signer: z.ZodString;
537
- }, "strip", z.ZodTypeAny, {
621
+ readonly payment_id: z$1.ZodOptional<z$1.ZodString>;
622
+ readonly payload_hash: z$1.ZodOptional<z$1.ZodString>;
623
+ readonly x402_expected: z$1.ZodOptional<z$1.ZodObject<{
624
+ payment_id: z$1.ZodString;
625
+ payload_hash: z$1.ZodString;
626
+ resource_url: z$1.ZodString;
627
+ merchant_to: z$1.ZodString;
628
+ amount: z$1.ZodString;
629
+ asset: z$1.ZodString;
630
+ network: z$1.ZodString;
631
+ expires_at: z$1.ZodString;
632
+ typed_data_hash: z$1.ZodOptional<z$1.ZodString>;
633
+ payer_delegate: z$1.ZodOptional<z$1.ZodString>;
634
+ payer_agent_id: z$1.ZodOptional<z$1.ZodString>;
635
+ auth: z$1.ZodObject<{
636
+ version: z$1.ZodNumber;
637
+ message: z$1.ZodString;
638
+ signature: z$1.ZodString;
639
+ signer: z$1.ZodString;
640
+ }, "strip", z$1.ZodTypeAny, {
538
641
  message: string;
539
642
  version: number;
540
643
  signature: string;
@@ -545,15 +648,15 @@ declare const toolSchemas: {
545
648
  signature: string;
546
649
  signer: string;
547
650
  }>;
548
- }, "strip", z.ZodTypeAny, {
549
- network: string;
651
+ }, "strip", z$1.ZodTypeAny, {
550
652
  payment_id: string;
653
+ expires_at: string;
654
+ network: string;
551
655
  payload_hash: string;
552
656
  resource_url: string;
553
657
  merchant_to: string;
554
658
  amount: string;
555
659
  asset: string;
556
- expires_at: string;
557
660
  auth: {
558
661
  message: string;
559
662
  version: number;
@@ -564,14 +667,14 @@ declare const toolSchemas: {
564
667
  payer_delegate?: string | undefined;
565
668
  payer_agent_id?: string | undefined;
566
669
  }, {
567
- network: string;
568
670
  payment_id: string;
671
+ expires_at: string;
672
+ network: string;
569
673
  payload_hash: string;
570
674
  resource_url: string;
571
675
  merchant_to: string;
572
676
  amount: string;
573
677
  asset: string;
574
- expires_at: string;
575
678
  auth: {
576
679
  message: string;
577
680
  version: number;
@@ -582,9 +685,9 @@ declare const toolSchemas: {
582
685
  payer_delegate?: string | undefined;
583
686
  payer_agent_id?: string | undefined;
584
687
  }>>;
585
- readonly payment_required: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
586
- readonly typed_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
587
- readonly typed_data_b64: z.ZodOptional<z.ZodString>;
688
+ readonly payment_required: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodUnknown>>;
689
+ readonly typed_data: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodUnknown>>;
690
+ readonly typed_data_b64: z$1.ZodOptional<z$1.ZodString>;
588
691
  };
589
692
  };
590
693
  declare const toolDescriptions: Record<SignerToolName, string>;
@@ -626,11 +729,14 @@ interface ToolFailure {
626
729
  fallback?: string;
627
730
  /**
628
731
  * #3001: present on `SIGN_CONTEXT_REFUSED` — the HTTP status the backend
629
- * answered the `/x402/:id/sign-context` fetch with (404, 410, …).
732
+ * answered the sign-context fetch with (404, 410, …) — the x402 fetch, or
733
+ * the direct `/payments/:id/sign-context` fetch since #3271.
630
734
  */
631
735
  http_status?: number;
632
- /** #3001: the backend's own `error_code` on `SIGN_CONTEXT_REFUSED` (`expired`, `already_executed`, `not_signable`, `sign_context_unavailable`). */
736
+ /** #3001: the backend's own `error_code` on `SIGN_CONTEXT_REFUSED` (`expired`, `already_executed`, `not_signable`, `sign_context_unavailable`, and since #3303 `client_outdated`). */
633
737
  backend_error_code?: string;
738
+ /** #3303: on `client_outdated`, the backend's update hint — `upgrade_command` is what updates this signer. */
739
+ client_update?: HavenClientUpdate;
634
740
  }
635
741
  type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
636
742
  interface ToolHandlerOptions {
@@ -645,6 +751,8 @@ interface ToolHandlerOptions {
645
751
  signContext?: {
646
752
  loadIdentity: () => Promise<HavenIdentity | null>;
647
753
  fetchImpl?: typeof fetch;
754
+ /** #3303: `@haven_ai/signer/<version>`, sent as `X-Haven-Client` on every sign-context read. */
755
+ clientIdentity?: string;
648
756
  };
649
757
  }
650
758
  declare function createToolHandlers(signer: EdgeSigner, options?: ToolHandlerOptions): Record<SignerToolName, (input: unknown) => Promise<ToolPayload>>;
@@ -676,7 +784,7 @@ declare function renderSignerConsentBlock(input: SignerConsentInput, hash: strin
676
784
  declare function ensureSignerConsent(input: SignerConsentInput, options?: SignerConsentOptions): Promise<SignerConsentDecision>;
677
785
 
678
786
  declare const SIGNER_NAME = "@haven_ai/signer";
679
- declare const SIGNER_VERSION = "0.4.0-alpha.0";
787
+ declare const SIGNER_VERSION = "0.6.0-alpha.0";
680
788
  interface SignerOptions {
681
789
  /** Path to a Haven credential JSON file (delegate_key is read from it). */
682
790
  credentialsPath?: string;
@@ -741,24 +849,4 @@ declare function assertSupportedNodeVersion(nodeVersion?: string): void;
741
849
  declare function runSignerStdioServer(options?: SignerOptions): Promise<void>;
742
850
  declare function runSignerConsentGate(signer: EdgeSigner, credentials: SignerCredentials | undefined, options: SignerOptions): Promise<SignerConsentDecision>;
743
851
 
744
- /**
745
- * #3103 (epic #3105, slice 4/5): the signer's typed next step.
746
- *
747
- * The signer decides a `next_action` on its refusals and, until this module,
748
- * never said which tool to call. It cannot know the client's server names
749
- * (#2550), so the handoff is expressed through the role fields the SDK
750
- * builder renders — `next_tool_server_role: hosted`, `next_tool_name` — from
751
- * a bare tool name. The signer must not import the hosted server (it is an
752
- * npm package installed on user machines; the hosted server is a Docker
753
- * deployment), so the hosted tools it hands off to are DECLARED here as the
754
- * argument shape the signer actually emits, and `next-step-signer-parity`
755
- * in the hosted server's own suite pins them to the hosted schemas at test
756
- * time — the mirror of how the hosted server declares the signer's shapes.
757
- */
758
- declare const SIGNER_HOSTED_HANDOFF_SHAPES: {
759
- readonly haven_get_payment_status: {
760
- readonly payment_id: z$1.ZodString;
761
- };
762
- };
763
-
764
- export { type EdgeSigner, type ResolvedSignerRuntime, SIGNER_ACK_ENV, SIGNER_CAPABILITY_KEY, SIGNER_HOSTED_HANDOFF_SHAPES, 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, readAccountAddressEnv, readAccountAddressField, renderSignerConsentBlock, resolveEdgeSigner, resolveSignerRuntime, runSignerConsentGate, runSignerStdioServer, signerCapabilityAdvertisement, signerCompatibility, signerInstructions, toolDescriptions, toolSchemas, warnIfCredentialFilePermissive };
852
+ export { AUDIT_ROTATE_BYTES, type AppendAuditOptions, type EdgeSigner, OWNER_ONLY_MODE, type PermissionLog, type PermissiveFile, type ResolvedSignerRuntime, SIGNER_ACK_ENV, SIGNER_CAPABILITY_KEY, SIGNER_HOSTED_HANDOFF_SHAPES, 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 TightenOutcome, 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, permissiveMode, readAccountAddressEnv, readAccountAddressField, renderSignerConsentBlock, resolveEdgeSigner, resolveSignerRuntime, runSignerConsentGate, runSignerStdioServer, signerCapabilityAdvertisement, signerCompatibility, signerInstructions, tightenIfFilePermissive, toolDescriptions, toolSchemas, warnIfCredentialFilePermissive, warnIfFilePermissive };