@terminal3/t3n-sdk 4.9.0 → 4.10.1

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
@@ -383,1408 +383,1235 @@ declare function createEmailOtpAuthInput(credentials: EmailOtpCredentials): Emai
383
383
  declare function createWorkloadAuthInput(provider: string, idToken: string): WorkloadAuthInput;
384
384
 
385
385
  /**
386
- * Error classes for T3n SDK
387
- */
388
- /**
389
- * Base error class for T3n SDK errors
386
+ * Public types export for T3n SDK
390
387
  */
391
- declare class T3nError extends Error {
392
- readonly code?: string | undefined;
393
- constructor(message: string, code?: string | undefined);
394
- }
395
388
  /**
396
- * Error thrown when session is in invalid state for operation
389
+ * Guest-to-Host request handler function type
390
+ *
391
+ * Handles requests from WASM guest that need host (SDK) to perform side
392
+ * effects. The exact shape of `requestData` depends on the specific
393
+ * handler — see `GuestToHostHandlers` below for the per-handler shapes.
394
+ * The wrapper layer in `T3nClient.handleGuestToHost` parses the JSON
395
+ * envelope and calls the matching handler with the parsed data, so
396
+ * each handler's implementation should narrow `requestData` to its
397
+ * own expected shape.
397
398
  */
398
- declare class SessionStateError extends T3nError {
399
- readonly currentState: string;
400
- constructor(message: string, currentState: string);
401
- }
399
+ type GuestToHostHandler = (requestData: Record<string, unknown>) => Promise<Uint8Array>;
402
400
  /**
403
- * Error thrown during authentication process
401
+ * Map of guest-to-host request handlers
402
+ * Keys match the guest_to_host tag values from the WASM
404
403
  */
405
- declare class AuthenticationError extends T3nError {
406
- readonly authMethod?: string | undefined;
407
- constructor(message: string, authMethod?: string | undefined);
404
+ interface GuestToHostHandlers {
405
+ /**
406
+ * Handle Ethereum signature requests
407
+ * requestData: { guest_to_host: "EthSign", challenge: string (base64) }
408
+ * Returns: JSON bytes of { host_to_guest: "EthSign", challenge: string, signature: string }
409
+ */
410
+ EthSign?: GuestToHostHandler;
411
+ /**
412
+ * Handle MlKem public key requests
413
+ * requestData: { guest_to_host: "MlKemPublicKey" }
414
+ * Returns: JSON bytes of { host_to_guest: "MlKemPublicKey", key: string }
415
+ */
416
+ MlKemPublicKey?: GuestToHostHandler;
417
+ /**
418
+ * Handle random bytes requests
419
+ * requestData: { guest_to_host: "Random", len?: number }
420
+ * Returns: JSON bytes of { host_to_guest: "Random", bytes: string (base64) }
421
+ */
422
+ Random?: GuestToHostHandler;
423
+ [key: string]: GuestToHostHandler | undefined;
408
424
  }
425
+
409
426
  /**
410
- * Error thrown during handshake process
427
+ * Transport layer for T3n SDK
428
+ *
429
+ * Abstracts the communication with T3n nodes, allowing for different
430
+ * implementations (HTTP, WebSocket, Mock) and easy testing.
411
431
  */
412
- declare class HandshakeError extends T3nError {
413
- constructor(message: string);
414
- }
415
432
  /**
416
- * Error thrown when a session-authenticated SDK client detects that the
417
- * caller-owned session is no longer usable and the caller must
418
- * re-authenticate before the call can succeed.
419
- *
420
- * Unlike {@link OrgDataClient} (which owns its ETH-secret-driven session
421
- * and silently rebuilds it on expiry), {@link SessionOrgDataClient} is
422
- * handed a caller-owned `T3nClient` whose session is bound to an
423
- * interactive flow (typically SIWE). The SDK can't re-authenticate
424
- * non-interactively, so on the same wire patterns that the
425
- * self-owned client recovers from, the session-bound client translates
426
- * the underlying error into this class and rethrows. The original error
427
- * is preserved on the native ES2022 `cause` property.
428
- *
429
- * Callers branch on `instanceof SessionExpiredError` to route the user
430
- * to a re-auth UX (e.g. SIWE modal / login redirect) rather than
431
- * parsing the message of a raw {@link RpcError}.
433
+ * JSON-RPC request structure
432
434
  */
433
- declare class SessionExpiredError extends T3nError {
434
- constructor(cause: unknown);
435
+ interface JsonRpcRequest {
436
+ jsonrpc: "2.0";
437
+ method: string;
438
+ params: unknown;
439
+ id: string | number;
435
440
  }
436
441
  /**
437
- * Error thrown during RPC communication.
438
- *
439
- * `message` is the human-readable error (preferring the server's
440
- * `error.data.detail` when present, with the request id appended in
441
- * `[<id>]` form so a UI that only surfaces `.message` still gives an
442
- * operator something to grep). The structured fields below preserve
443
- * the same info for callers that want to render or log them
444
- * separately — e.g. a toast that shows `detail` but surfaces
445
- * `requestId` in a "copy for support" affordance.
442
+ * JSON-RPC response structure
446
443
  */
447
- declare class RpcError extends T3nError {
448
- readonly rpcMethod?: string | undefined;
449
- readonly httpStatus?: number | undefined;
450
- /** Server-attached detail (JSON-RPC `error.data.detail`). User-facing kinds
451
- * carry the specific reason here; internal kinds omit it. */
452
- readonly detail?: string | undefined;
453
- /** Per-request correlation id (JSON-RPC `error.data.request_id`). */
454
- readonly requestId?: string | undefined;
455
- constructor(message: string, rpcMethod?: string | undefined, httpStatus?: number | undefined,
456
- /** Server-attached detail (JSON-RPC `error.data.detail`). User-facing kinds
457
- * carry the specific reason here; internal kinds omit it. */
458
- detail?: string | undefined,
459
- /** Per-request correlation id (JSON-RPC `error.data.request_id`). */
460
- requestId?: string | undefined);
444
+ interface JsonRpcResponse {
445
+ jsonrpc: "2.0";
446
+ result?: unknown;
447
+ error?: {
448
+ code: number;
449
+ message: string;
450
+ data?: unknown;
451
+ };
452
+ id: string | number;
461
453
  }
462
454
  /**
463
- * Error thrown when an OTP send is throttled by the node.
464
- *
465
- * The email-OTP login flow's `InitEmailOtp` step refuses a send when a
466
- * still-valid code was issued within the resend cooldown
467
- * (`reason: "cooldown_active"`) or the rolling per-recipient send cap is
468
- * exhausted (`reason: "send_cap_exceeded"`). Previously a cooldown-
469
- * suppressed resend returned a silent success, leaving a user who never
470
- * received the first code at a dead end; this typed error lets the app
471
- * render a "you just requested a code — retry in {retryAfterSecs}s"
472
- * affordance instead.
473
- *
474
- * Recognised from the node's stable
475
- * `OtpRateLimited (reason=…, retry_after_secs=…)` wire token (pinned by a
476
- * server-side test). Subclass of {@link RpcError} so callers that catch
477
- * the parent still see `httpStatus`/`requestId`; callers that want the
478
- * typed surface `instanceof OtpRateLimitedError` to read `reason` and
479
- * `retryAfterSecs`.
480
- *
481
- * Retryable after `retryAfterSecs` (caller-side back-off).
455
+ * Transport interface for sending requests to T3n nodes
482
456
  */
483
- declare class OtpRateLimitedError extends RpcError {
484
- /** Stable reason code: `"cooldown_active"` or `"send_cap_exceeded"`. */
485
- readonly reason: string;
486
- /** Seconds to wait before retrying the send (parsed from `retry_after_secs=`). */
487
- readonly retryAfterSecs: number;
488
- constructor(message: string,
489
- /** Stable reason code: `"cooldown_active"` or `"send_cap_exceeded"`. */
490
- reason: string,
491
- /** Seconds to wait before retrying the send (parsed from `retry_after_secs=`). */
492
- retryAfterSecs: number, rpcMethod?: string, requestId?: string);
457
+ interface Transport {
458
+ /**
459
+ * Send a JSON-RPC request to the T3n node
460
+ * @param request - The JSON-RPC request to send
461
+ * @param headers - Additional headers to include
462
+ * @returns Promise that resolves to the JSON-RPC response
463
+ */
464
+ send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
465
+ /**
466
+ * Send a JSON-RPC request with an attached binary blob (multipart/form-data).
467
+ * Part 1 (name=jsonrpc): JSON-RPC envelope.
468
+ * Part 2 (name=blob): raw binary bytes (e.g. WASM bytecode).
469
+ */
470
+ sendMultipart?(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
471
+ /**
472
+ * Optional accessor for the latest Set-Cookie header value.
473
+ * (Useful in Node.js demos/tests; browsers block HttpOnly cookies.)
474
+ */
475
+ getLastSetCookie?(): string | null;
476
+ /**
477
+ * Optional accessor for the last response headers (debugging).
478
+ */
479
+ getLastResponseHeaders?(): Record<string, string>;
493
480
  }
494
481
  /**
495
- * Error thrown when WASM operations fail
482
+ * HTTP transport implementation using fetch
496
483
  */
497
- declare class WasmError extends T3nError {
498
- readonly operation?: string | undefined;
499
- readonly payload?: unknown | undefined;
500
- constructor(message: string, operation?: string | undefined, payload?: unknown | undefined);
484
+ declare class HttpTransport implements Transport {
485
+ private baseUrl;
486
+ private timeout;
487
+ private lastSetCookie;
488
+ private lastResponseHeaders;
489
+ /**
490
+ * Cookie jar (`name -> value`) for non-browser callers. The session is
491
+ * node-local in-memory state, so once the load balancer pins a client
492
+ * to a node via its `GCLB` session-affinity cookie, every later request
493
+ * in the session must carry that cookie back or it gets re-balanced to
494
+ * a node that has never seen the handshake → `401 session not found`.
495
+ * Browsers replay the cookie automatically from their own jar (via
496
+ * `credentials: "include"`); plain `fetch` callers (Node demos, tests,
497
+ * server-side SDK usage) have no jar, so we keep one here. Empty in
498
+ * browsers — there `getSetCookie()` is unavailable and a script-set
499
+ * `Cookie` header is forbidden, so this stays inert.
500
+ */
501
+ private cookieJar;
502
+ constructor(baseUrl: string, timeout?: number);
503
+ getLastSetCookie(): string | null;
504
+ getLastResponseHeaders(): Record<string, string>;
505
+ /**
506
+ * Record response headers and merge any `Set-Cookie` into the jar.
507
+ *
508
+ * undici's `Headers` exposes `getSetCookie()` (one entry per cookie);
509
+ * the DOM `Headers` type doesn't declare it, so cast structurally. In
510
+ * browsers neither `getSetCookie()` nor reading `set-cookie` is
511
+ * permitted, so `setCookies` is empty and the jar is left untouched.
512
+ */
513
+ private captureResponseMetadata;
514
+ /**
515
+ * Build a `Cookie` request header from the jar so non-browser callers
516
+ * stay pinned to the node holding their session. Returns `{}` when the
517
+ * jar is empty — always the case in browsers, leaving their native
518
+ * cookie handling untouched.
519
+ *
520
+ * `callerHeaders` is checked case-insensitively: if the caller already
521
+ * supplied a `Cookie`/`cookie` header, the jar yields to it entirely.
522
+ * (Object spread keys are case-sensitive, but `fetch`'s `Headers` are
523
+ * not — emitting `Cookie` here while the caller passed `cookie` would
524
+ * make the two coexist and get comma-combined instead of overridden.)
525
+ */
526
+ private cookieHeader;
527
+ send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
528
+ sendMultipart(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
501
529
  }
502
530
  /**
503
- * Decode WASM error message from comma-separated byte array format
504
- * WASM errors often come as "83,101,114,100,101..." which represents ASCII bytes
531
+ * Mock transport for testing
505
532
  *
506
- * @param errorMessage - The error message string that may contain comma-separated bytes
507
- * @returns Decoded error message if it was encoded, otherwise original message
508
- */
509
- declare function decodeWasmErrorMessage(errorMessage: string): string;
510
- /**
511
- * Extract and decode error from WASM ComponentError
533
+ * @example
534
+ * ```typescript
535
+ * const mockTransport = new MockTransport();
536
+ * mockTransport.mockResponse("auth.handshake", { result: "..." });
512
537
  *
513
- * @param error - The error object from WASM
514
- * @returns Decoded error message
515
- */
516
- declare function extractWasmError(error: unknown): string;
517
-
518
- /**
519
- * Wire types for the explicit `otp-request` /
520
- * `otp-verify` / slim `user-upsert` exports on `tee:user/contracts`
521
- * (`tee:user@2.0.0`).
522
- *
523
- * Pre-2.0.0 the user contract dispatched OTP request and OTP verify
524
- * implicitly from the input shape of a single `user-upsert` call.
525
- * 2.0.0 splits that surface into three explicit functions; the
526
- * shapes here mirror the OTP / user-upsert wire contract.
527
- *
528
- * Bytes flow directly from the contract
529
- * through the JSON-RPC envelope into the SDK wrappers
530
- * (`T3nClient.otpRequest`, `T3nClient.otpVerify`,
531
- * `T3nClient.submitUserInput`).
538
+ * const client = new T3nClient({
539
+ * trustAnchor: { unsafe_trust_server: true },
540
+ * transport: mockTransport,
541
+ * wasmComponent: mockWasm,
542
+ * });
543
+ * ```
532
544
  */
545
+ declare class MockTransport implements Transport {
546
+ private responses;
547
+ private responseHeaders;
548
+ private lastResponseHeaders;
549
+ private requests;
550
+ private multipartRequests;
551
+ /**
552
+ * Mock a response for a specific method
553
+ */
554
+ mockResponse(method: string, response: Partial<JsonRpcResponse>): void;
555
+ /**
556
+ * Mock response headers for a specific method. Used by tests to
557
+ * simulate the server-minted `Session-Id` header the SDK picks up
558
+ * from the `auth.handshake` response. Unset methods
559
+ * default to no headers.
560
+ */
561
+ mockResponseHeaders(method: string, headers: Record<string, string>): void;
562
+ /**
563
+ * Mock an error response for a specific method
564
+ */
565
+ mockError(method: string, code: number, message: string, data?: unknown): void;
566
+ getLastResponseHeaders(): Record<string, string>;
567
+ /**
568
+ * Get all requests that were sent
569
+ */
570
+ getRequests(): Array<{
571
+ request: JsonRpcRequest;
572
+ headers: Record<string, string>;
573
+ }>;
574
+ /**
575
+ * Get requests for a specific method
576
+ */
577
+ getRequestsForMethod(method: string): Array<{
578
+ request: JsonRpcRequest;
579
+ headers: Record<string, string>;
580
+ }>;
581
+ getMultipartRequests(): Array<{
582
+ request: JsonRpcRequest;
583
+ headers: Record<string, string>;
584
+ blob: Blob;
585
+ }>;
586
+ /**
587
+ * Clear all recorded requests
588
+ */
589
+ clearRequests(): void;
590
+ sendMultipart(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
591
+ send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
592
+ }
533
593
 
534
594
  /**
535
- * OTP delivery channel. Matches the `OtpContactChannel` Rust enum
536
- * with `serde(rename_all = "snake_case")`.
537
- */
538
- type OtpChannel = "email" | "sms";
539
- /**
540
- * Discriminated OTP contact target. Mirrors the Rust `OtpRequest` enum
541
- * (`email_channel` / `sms_channel` on the wire). Exactly one branch is
542
- * set — no parallel optional email + phone with a separate channel tag.
543
- *
544
- * The legacy `keys.generic_api.otp_channel` body shadow was removed
545
- * in 2.0.0 — the contract rejects calls carrying it with a
546
- * `legacy_field` error.
547
- */
548
- type OtpRequestInput = {
549
- emailChannel: {
550
- emailAddress: string;
551
- };
552
- } | {
553
- smsChannel: {
554
- phoneNumber: string;
555
- };
556
- };
557
- /**
558
- * Response returned by `otp-request`. Mirrors the OTP wire response.
595
+ * TDX quote verifier backed by the Rust `signature` crate compiled to
596
+ * WASM. Full cryptographic verification: ECDSA P-256 attestation-key
597
+ * signature, PCK certificate chain walk to Intel's root CA, and
598
+ * report_data + RTMR comparison.
559
599
  *
560
- * - `contact` echoes the OTP destination so clients that race
561
- * multiple channels can correlate replies.
562
- * - `expiresAtSec` is the Unix-second TTL the host minted for the
563
- * pending OTP. Absent in `skip_otp` test environments.
564
- * - `status` is `"otp_pending"` on the happy path. `"otp_failed"`
565
- * only appears on retry without a fresh request — usually you
566
- * shouldn't see it on `otp-request`.
567
- * - `txHash` is the host-enriched ledger ref for the OTP-pending
568
- * write (the contract leaves it `null`; the host injects).
569
- * - `isNewProfile` is `true` on a fresh DID's first OTP request —
570
- * read this to detect "did the user just register".
571
- */
572
- interface OtpRequestResult {
573
- contact: string;
574
- channel: OtpChannel;
575
- expiresAtSec?: number;
576
- status?: string;
577
- txHash?: string;
578
- isNewProfile?: boolean;
579
- }
580
- /**
581
- * Input to `T3nClient.otpVerify`. `otpCode` is mandatory; `request`
582
- * repeats the same discriminated contact shape as {@link OtpRequestInput}.
600
+ * The WASM bytes are inlined as base64 (see quote_verifier_bytes.ts)
601
+ * so the SDK works without bundler WASM plugins and without runtime
602
+ * asset URL resolution.
583
603
  */
584
- interface OtpVerifyInput {
585
- otpCode: string;
586
- request: OtpRequestInput;
604
+ interface QuoteVerifyResult {
605
+ valid: boolean;
606
+ error?: string;
607
+ rtmr3?: string;
608
+ report_data?: string;
587
609
  }
588
- /**
589
- * Suggestion surfaced when the bind-target contact already belongs
590
- * to another DID. The contract refuses to silently steal the
591
- * authenticator; the caller must resolve the merge (typically via
592
- * `merge-profiles`) before re-attempting verify.
593
- */
594
- interface OtpMergeSuggestion {
595
- existingDid: string;
596
- currentDid: string;
597
- email?: string;
598
- phone?: string;
599
- channel: OtpChannel;
610
+ interface PeerQuoteResult {
611
+ peer_id: string;
612
+ valid: boolean;
613
+ error?: string;
614
+ rtmr3?: string;
600
615
  }
601
- /**
602
- * Response returned by `otp-verify`. Mirrors the OTP-verify wire response.
603
- *
604
- * - `email` is populated on `channel = "email"` success;
605
- * `phone` on `channel = "sms"` success. Mutually exclusive on
606
- * the happy path.
607
- * - `status` carries `"otp_failed"` / `"otp_expired"` on retry; the
608
- * happy path leaves it `undefined`.
609
- * - `mergeSuggestion` is set when the contact-to-bind is already
610
- * owned by another DID. Wire it into your merge UX.
611
- */
612
- interface OtpVerifyResult {
613
- txHash?: string;
614
- did: string;
615
- channel: OtpChannel;
616
- email?: string;
617
- phone?: string;
618
- status?: string;
619
- mergeSuggestion?: OtpMergeSuggestion;
620
- isNewProfile?: boolean;
616
+ interface DkgVerifyResult {
617
+ valid: boolean;
618
+ results: PeerQuoteResult[];
619
+ valid_count: number;
620
+ expected_count: number;
621
+ error?: string;
621
622
  }
622
623
  /**
623
- * Level-1 user-input fields accepted by the slim `user-upsert`. Field names
624
- * are the on-the-wire names — `submitUserInput` sends this object verbatim,
625
- * so keys must match the contract exactly (snake_case). The canonical Level-1
626
- * set is the six fields below; the shape is intentionally open so callers can
627
- * add provider-specific fields the contract knows how to merge.
624
+ * Client-pinned anchor the DKG attestation bundle is verified against.
628
625
  *
629
- * `email_address` / `phone_number` are normally bound via the OTP roundtrip
630
- * before `submitUserInput`, but the contract accepts them directly here too.
631
- */
632
- interface UserInputProfile {
633
- first_name?: string;
634
- last_name?: string;
635
- country_of_residence?: string;
636
- document_issuance_country?: string;
637
- ssn?: string;
638
- address?: string;
639
- email_address?: string;
640
- phone_number?: string;
641
- campaign_code?: string;
642
- role?: string;
643
- [key: string]: unknown;
644
- }
645
- /**
646
- * Input to `T3nClient.submitUserInput`. Maps onto the slim
647
- * `user-upsert` request shape: `{ profile, organisation_did?,
648
- * attestations?, keys? }`.
626
+ * Without it, "the server proved it runs in a TEE" only means "*some*
627
+ * TDX box somewhere" — any attacker with their own TDX cloud VM can
628
+ * forge a complete, cryptographically-valid bundle for their own key
629
+ * (SP-003). The anchor is the independent ground truth that closes it:
630
+ * the bundle must describe *our* cluster (`expected_peer_ids`) running
631
+ * *our* image (`rtmr3_allowlist`).
649
632
  */
650
- interface SubmitUserInputArgs {
651
- profile: UserInputProfile;
652
- organisationDid?: string;
653
- attestations?: unknown;
654
- keys?: Record<string, unknown>;
633
+ interface TrustAnchor {
655
634
  /**
656
- * KYC webhook orphan-attestation flow. When set, the contract
657
- * skips the email-not-verified gate and only records the
658
- * attestation if the DID has no profile yet.
659
- * Most callers should leave this `undefined`.
635
+ * Base58 peer IDs the client expects in the cluster. The bundle's
636
+ * `peer_ids` must equal this set exactly (order-insensitive) — a MITM
637
+ * cannot substitute its own single-node participant set.
660
638
  */
661
- requireExistingUser?: boolean;
639
+ expected_peer_ids: string[];
662
640
  /**
663
- * Testnet self-admit. When `true`, the contract additionally
664
- * runs the self-admit side-effect after the profile commit: writes
665
- * the caller's DID into `idx:_tenants` + `idx:_tenant_quotas` and
666
- * mints any operator-configured welcome credits. Inspect the
667
- * `tenantAdmit` field on the response for the outcome.
668
- *
669
- * Testnet-only. Production clusters return
670
- * `tenantAdmit.status = "refused"` with
671
- * `reason = "not_testnet" | "self_admit_disabled" | "not_eth_derived"`.
672
- * The profile commit always succeeds regardless of the inner outcome.
673
- *
674
- * Most callers should leave this `undefined`.
641
+ * Base64-encoded 48-byte RTMR3 measurements of the expected node
642
+ * image. Every quote's RTMR3 must be a member. **Must be non-empty.**
643
+ * An array (not a singleton) so a rolling deploy's two in-flight
644
+ * image measurements both verify.
675
645
  */
676
- becomeDevTenant?: boolean;
646
+ rtmr3_allowlist: string[];
677
647
  }
678
648
  /**
679
- * Testnet self-admit projection from `user-upsert`. Present only
680
- * when the caller set `becomeDevTenant: true` on the request.
649
+ * Escape hatch for local dev / tests against a node whose attestation
650
+ * can't be anchored (e.g. a mock signer that publishes no bundle).
681
651
  *
682
- * - `status: "admitted"` — first call for this DID; tenant record
683
- * committed and (if configured) welcome credits minted into
684
- * `grantedCredits`.
685
- * - `status: "already-admitted"` — replay; idempotent no-op.
686
- * - `status: "refused"` — the cluster declined the self-admit;
687
- * `reason` is the machine-readable cause (`not_testnet`,
688
- * `self_admit_disabled`, `not_eth_derived`), `detail` is a
689
- * human-readable string.
690
- */
691
- type TenantAdmitStatus = "admitted" | "already-admitted" | "refused";
692
- interface TenantAdmitProjection {
693
- status: TenantAdmitStatus;
694
- grantedCredits?: string;
695
- reason?: string;
696
- detail?: string;
697
- }
698
- /**
699
- * Response shape returned by the slim `user-upsert`. Carries the
700
- * post-merge tx hash plus the Level-1 merge diagnostics
701
- * (`refusedFields`, `mergeSuggestion`) the pre-2.0.0 omnibus
702
- * `UpsertResponse` already surfaced.
652
+ * The name is deliberately alarming so it stands out in code review and
653
+ * grep — it is the *only* way to skip DKG attestation verification on
654
+ * the handshake path.
703
655
  */
704
- interface SubmitUserInputResult {
705
- txHash?: string;
706
- refusedFields?: string[];
707
- mergeSuggestion?: OtpMergeSuggestion;
708
- userFound?: boolean;
709
- /**
710
- * Testnet self-admit projection. Present only when the caller
711
- * set `becomeDevTenant: true` on the request.
712
- */
713
- tenantAdmit?: TenantAdmitProjection;
656
+ interface UnsafeTrustServer {
657
+ unsafe_trust_server: true;
714
658
  }
659
+ type TrustAnchorOrUnsafe = TrustAnchor | UnsafeTrustServer;
660
+ /** Narrow a {@link TrustAnchorOrUnsafe} to the explicit opt-out arm. */
661
+ declare function isUnsafeTrustServer(anchor: TrustAnchorOrUnsafe): anchor is UnsafeTrustServer;
715
662
  /**
716
- * Discriminator for {@link UserUpsertError}. Mirrors the
717
- * `UserUpsertError` wire enum and its `<code>:<detail>` wire
718
- * format. Branch with a `switch` over `kind`.
663
+ * Verify a single TDX attestation quote with full cryptographic verification.
719
664
  *
720
- * - `EmailNotVerified` — the slim `user-upsert` was called against
721
- * a DID that has no verified email and no proving authenticator.
722
- * Run `otpRequest` + `otpVerify` first (or accept an
723
- * OIDC/Email-authed session).
724
- * - `LegacyField` — caller passed a pre-2.0.0 dispatch field
725
- * (`otp_code` to a non-verify export, or
726
- * `keys.generic_api.otp_channel` anywhere). Migrate the call
727
- * site to the new explicit functions.
728
- * - `UserNotFound` — `requireExistingUser` was set but no profile
729
- * exists for the DID. The attestation is recorded for audit;
730
- * no profile created.
665
+ * @param quoteB64 - Base64-encoded raw TDX v4 quote
666
+ * @param attestationMsgB64 - Base64-encoded attestation message
667
+ * (for DKG: encaps_key || sorted_peer_id_bytes)
668
+ * @param expectedRtmr3B64 - Optional base64-encoded 48-byte RTMR3
669
+ * @returns Verification result with extracted measurements
731
670
  */
732
- type UserUpsertErrorKind = "EmailNotVerified" | "LegacyField" | "UserNotFound";
671
+ declare function verifyTdxQuote(quoteB64: string, attestationMsgB64: string, expectedRtmr3B64?: string): Promise<QuoteVerifyResult>;
733
672
  /**
734
- * Typed wrapper for the `<code>:<detail>` errors the slim
735
- * `user-upsert` and the OTP entry points emit. `kind` is the
736
- * structured discriminator the SDK derives from the error code
737
- * prefix; `code` and `detail` retain the wire components for
738
- * fall-through logging.
673
+ * Verify a full DKG attestation bundle against a client-pinned
674
+ * {@link TrustAnchor}: multiple TDX quotes from all participating
675
+ * nodes, plus the binding between the quotes and the ML-KEM
676
+ * encapsulation key. Checks:
677
+ * 1. `expected_peer_ids` / `rtmr3_allowlist` are both non-empty
678
+ * 2. the bundle's `peer_ids` equal the pinned set exactly
679
+ * 3. attestationMsg starts with encapsKey (server can't swap the key)
680
+ * 4. every quote's ECDSA signature chains to Intel's SGX root CA
681
+ * 5. every quote's report_data == keccak512(attestationMsg)
682
+ * 6. every quote's RTMR3 is a member of `rtmr3_allowlist`
739
683
  *
740
- * Throw site: `T3nClient.submitUserInput` (and friends) when the
741
- * contract returns a string error matching the
742
- * `<code>:<detail>` shape.
684
+ * The anchor is **required**: there is no unauthenticated mode. Callers
685
+ * that genuinely want to trust the server must not call this at all —
686
+ * see `unsafe_trust_server` on the client config, which branches around
687
+ * the whole verify path.
688
+ *
689
+ * @param encapsKeyB64 - Base64-encoded ML-KEM encapsulation key
690
+ * (from `/status.encaps_key`)
691
+ * @param attestationMsgB64 - Base64-encoded raw attestation message
692
+ * (from `/status.dkg_attestation.attestation_msg`)
693
+ * @param peerIds - Base58 peer IDs as reported by the bundle
694
+ * @param quotes - Map of peer_id → base64-encoded TDX quote
695
+ * @param anchor - Client-pinned expected peer IDs + RTMR3 allowlist
696
+ * @returns Per-peer verification results and overall validity
743
697
  */
744
- declare class UserUpsertError extends T3nError {
745
- readonly kind: UserUpsertErrorKind | "Unknown";
746
- readonly detail: string;
747
- constructor(code: string, detail: string);
748
- /**
749
- * Try to parse a contract error string into a `UserUpsertError`.
750
- * Returns `null` if `raw` doesn't match the `<code>:<detail>`
751
- * shape — caller falls back to a generic error.
752
- */
753
- static fromWire(raw: string): UserUpsertError | null;
754
- }
698
+ declare function verifyDkgAttestation(encapsKeyB64: string, attestationMsgB64: string, peerIds: string[], quotes: Record<string, string>, anchor: TrustAnchor): Promise<DkgVerifyResult>;
755
699
 
756
700
  /**
757
- * Org-data wire types mirroring the org-data contract wire shapes.
758
- *
759
- * Plain TypeScript interfaces (no zod) — the SDK does not use a
760
- * validation library for domain types; see the existing `types/` files.
761
- *
762
- * Each response type below is paired with a shallow runtime predicate
763
- * (`isMutationResponse`, `isOrgPolicyMeta`, etc.) so the org-data client
764
- * can `assertShape` the decoded payload before returning to callers.
765
- * Predicates check the top-level structure only; nested elements
766
- * (e.g. each `UserGrant` inside `OrgContractGrants.grants`) are not
767
- * deeply validated — see `utils/shape.ts` for the rationale.
768
- *
769
- * Reference: the org-data wire contract.
701
+ * Configuration types for T3n Client
770
702
  */
703
+
771
704
  /**
772
- * Capability grant stored under `ORG_CONTRACT_GRANTS_MAP`.
773
- *
774
- * Mirrors the org-data `UserGrant` wire shape.
705
+ * Configuration interface for T3n Client
775
706
  */
776
- interface UserGrant {
777
- /** The user this grant applies to (`did:t3n:<40-hex>`). */
778
- user_did: string;
779
- /** WIT function names the user may invoke (e.g. `"set-records"`). */
780
- functions: string[];
781
- /** Data scope paths the user may access (e.g. `"example/records"`). */
782
- scopes: string[];
707
+ interface T3nClientConfig {
708
+ /** Base URL of the T3n node (used if transport not provided) */
709
+ baseUrl?: string;
710
+ /** WASM component instance for cryptographic operations */
711
+ wasmComponent: WasmComponent;
712
+ /** Optional transport layer - if not provided, uses HttpTransport with baseUrl */
713
+ transport?: Transport;
714
+ /** Optional request timeout in milliseconds (default: 30000) - used for HttpTransport */
715
+ timeout?: number;
716
+ /** Optional custom headers to include in requests */
717
+ headers?: Record<string, string>;
783
718
  /**
784
- * Optional key-value constraints that must match the request metadata
785
- * exactly for every key present in this map.
719
+ * Log level for this client instance.
720
+ * Defaults to global log level (LogLevel.ERROR) if not specified.
721
+ * Use LogLevel.DEBUG for verbose logging, LogLevel.INFO for informational messages,
722
+ * LogLevel.WARN for warnings, or LogLevel.ERROR for errors only.
786
723
  */
787
- constraints: Record<string, string>;
788
- /** Unix timestamp (secs) after which this grant is expired. `null` means never expires. */
789
- expires_at_secs: number | null;
790
- }
791
- /**
792
- * Policy record for an organisation's data tier.
793
- *
794
- * Mirrors the org-data policy-meta wire shape.
795
- */
796
- interface OrgPolicyMeta {
797
- /** DIDs (`did:t3n:<40-hex>`) of users authorised to manage policy and read data. */
798
- admins: string[];
799
- /** Maximum number of admins allowed for this org. */
800
- max_admins: number;
801
- /** Unix timestamp (secs) when the policy was first created. */
802
- created_at_secs: number;
803
- /** Unix timestamp (secs) of the most recent policy update. */
804
- updated_at_secs: number;
724
+ logLevel?: LogLevel;
725
+ /** Optional custom logger - if provided, overrides logLevel */
726
+ logger?: Logger;
727
+ /** Optional guest-to-host request handlers - provides custom behavior for WASM requests */
728
+ handlers?: GuestToHostHandlers;
805
729
  /**
806
- * Per-org ceiling on delegation-credential validity (seconds).
730
+ * **Required.** Client-pinned trust anchor the node's DKG attestation
731
+ * is verified against before the handshake trusts its ML-KEM key
732
+ * (SP-003). Either a {@link TrustAnchor} (`expected_peer_ids` +
733
+ * `rtmr3_allowlist`) or the explicit `{ unsafe_trust_server: true }`
734
+ * opt-out for local dev / mock-signer nodes.
807
735
  *
808
- * Absent when no org-level cap is set — the contract omits the field
809
- * (never serialises null) and the cluster-wide
810
- * `delegation.max_credential_validity_secs` governs instead. When set,
811
- * the effective cap is `min(max_credential_validity_secs, cluster_ceiling)`.
736
+ * Without this, a network attacker with their own TDX VM can hand the
737
+ * SDK a forged-but-valid attestation for a key it controls and read
738
+ * every session. It is a required field precisely so no caller can
739
+ * omit it by accident — bypassing verification must be a visible,
740
+ * grep-able choice.
812
741
  */
813
- max_credential_validity_secs?: number;
742
+ trustAnchor: TrustAnchorOrUnsafe;
814
743
  }
815
- /** Shallow runtime guard for {@link OrgPolicyMeta}. */
816
- declare function isOrgPolicyMeta(value: unknown): value is OrgPolicyMeta;
744
+
817
745
  /**
818
- * Effective org policy view returned by `org-policy-view`.
819
- *
820
- * Readable by any authenticated session (non-sensitive metadata).
821
- * Returns the **effective** credential validity cap:
822
- * `min(org.max_credential_validity_secs, cluster_ceiling)`, equal to the
823
- * cluster ceiling when the org has not set a per-org cap. The contract
824
- * always returns a value (or errors) — the field is never null.
746
+ * Error classes for T3n SDK
825
747
  */
826
- interface OrgPolicyView {
827
- /**
828
- * Effective delegation-credential validity cap in seconds:
829
- * `min(org value, cluster ceiling)`, equal to the cluster ceiling when the
830
- * org has not set a per-org cap. Always present — mirrors the contract's
831
- * `PolicyViewResponse.effective_validity_secs` (org-data `org-policy-view`).
832
- */
833
- effective_validity_secs: number;
834
- }
835
- /** Shallow runtime guard for {@link OrgPolicyView}. */
836
- declare function isOrgPolicyView(value: unknown): value is OrgPolicyView;
837
748
  /**
838
- * Standard response returned by all policy write and data mutation operations.
839
- *
840
- * Mirrors the org-data `MutationResponse` wire shape.
749
+ * Base error class for T3n SDK errors
841
750
  */
842
- interface MutationResponse {
843
- /** `"created"`, `"updated"`, or `"deleted"`. */
844
- status: string;
845
- /** Hex-encoded entry ID; present on data write/delete operations. */
846
- entry_id?: string;
847
- /** Whether the target key existed before deletion; present on single-entry deletes. */
848
- deleted?: boolean;
849
- /** Number of entries removed; present on `org-data-delete-scope`. */
850
- deleted_entries?: number;
851
- tx_hash: string | null;
751
+ declare class T3nError extends Error {
752
+ readonly code?: string | undefined;
753
+ constructor(message: string, code?: string | undefined);
852
754
  }
853
755
  /**
854
- * Shallow runtime guard for {@link MutationResponse}.
855
- *
856
- * Only the always-present fields are checked — `status` is mandatory on
857
- * every mutation; `tx_hash` is non-optional but nullable. The optional
858
- * fields (`entry_id`, `deleted`, `deleted_entries`) are not validated
859
- * because their presence depends on which mutation ran.
756
+ * Error thrown when session is in invalid state for operation
860
757
  */
861
- declare function isMutationResponse(value: unknown): value is MutationResponse;
758
+ declare class SessionStateError extends T3nError {
759
+ readonly currentState: string;
760
+ constructor(message: string, currentState: string);
761
+ }
862
762
  /**
863
- * Response type alias for org-writers-get.
864
- *
865
- * The wire body is `{ writers: string[] }` where each entry is
866
- * `did:t3n:<40-hex>`.
763
+ * Error thrown during authentication process
867
764
  */
868
- interface OrgWriters {
869
- writers: string[];
765
+ declare class AuthenticationError extends T3nError {
766
+ readonly authMethod?: string | undefined;
767
+ constructor(message: string, authMethod?: string | undefined);
870
768
  }
871
- /** Shallow runtime guard for {@link OrgWriters}. */
872
- declare function isOrgWriters(value: unknown): value is OrgWriters;
873
769
  /**
874
- * Response type alias for org-grants-get.
875
- *
876
- * The wire body echoes the `contract_id` alongside the grant list.
770
+ * Error thrown during handshake process
877
771
  */
878
- interface OrgContractGrants {
879
- contract_id: string;
880
- grants: UserGrant[];
772
+ declare class HandshakeError extends T3nError {
773
+ constructor(message: string);
881
774
  }
882
775
  /**
883
- * Shallow runtime guard for {@link OrgContractGrants}.
776
+ * Error thrown when a session-authenticated SDK client detects that the
777
+ * caller-owned session is no longer usable and the caller must
778
+ * re-authenticate before the call can succeed.
884
779
  *
885
- * Validates the immediate envelope (`contract_id: string`, `grants:
886
- * array`) without recursing into each `UserGrant`. The Rust contract
887
- * is the source of truth for grant element shape; widening the predicate
888
- * here would create maintenance churn against benign field additions.
780
+ * Unlike {@link OrgDataClient} (which owns its ETH-secret-driven session
781
+ * and silently rebuilds it on expiry), {@link SessionOrgDataClient} is
782
+ * handed a caller-owned `T3nClient` whose session is bound to an
783
+ * interactive flow (typically SIWE). The SDK can't re-authenticate
784
+ * non-interactively, so on the same wire patterns that the
785
+ * self-owned client recovers from, the session-bound client translates
786
+ * the underlying error into this class and rethrows. The original error
787
+ * is preserved on the native ES2022 `cause` property.
788
+ *
789
+ * Callers branch on `instanceof SessionExpiredError` to route the user
790
+ * to a re-auth UX (e.g. SIWE modal / login redirect) rather than
791
+ * parsing the message of a raw {@link RpcError}.
889
792
  */
890
- declare function isOrgContractGrants(value: unknown): value is OrgContractGrants;
891
- /** Response for `org-data-list`. */
892
- interface DataListResponse {
893
- /** Hex-encoded entry IDs for this page. */
894
- entry_ids: string[];
895
- /** Offset to pass for the next page. `null` when this is the last page. */
896
- next_offset: number | null;
897
- /** Total number of entries in the scope (across all pages). */
898
- total: number;
899
- }
900
- /** Shallow runtime guard for {@link DataListResponse}. */
901
- declare function isDataListResponse(value: unknown): value is DataListResponse;
902
- /** Response for `org-data-get`. */
903
- interface DataGetResponse {
904
- entry_id: string;
905
- /** Hex-encoded raw payload bytes. */
906
- payload_hex: string;
793
+ declare class SessionExpiredError extends T3nError {
794
+ constructor(cause: unknown);
907
795
  }
908
- /** Shallow runtime guard for {@link DataGetResponse}. */
909
- declare function isDataGetResponse(value: unknown): value is DataGetResponse;
910
796
  /**
911
- * Legacy direct-route org-data envelope shape retained for compatibility.
797
+ * Error thrown during RPC communication.
912
798
  *
913
- * This mirrors the removed `/api/user-contract/execute` body format from
914
- * the transitional transport. New callers should use `OrgDataClient`,
915
- * which now dispatches through authenticated `/api/rpc` +
916
- * `action.execute` instead.
799
+ * `message` is the human-readable error (preferring the server's
800
+ * `error.data.detail` when present, with the request id appended in
801
+ * `[<id>]` form so a UI that only surfaces `.message` still gives an
802
+ * operator something to grep). The structured fields below preserve
803
+ * the same info for callers that want to render or log them
804
+ * separately — e.g. a toast that shows `detail` but surfaces
805
+ * `requestId` in a "copy for support" affordance.
917
806
  */
918
- interface OrgDataActionWire {
919
- nonce: string;
920
- user_did: string;
921
- authenticator_id: string;
922
- contract_id: string;
923
- function: string;
924
- args_hash: string;
925
- expires_at_secs: number;
926
- signature: string;
807
+ declare class RpcError extends T3nError {
808
+ readonly rpcMethod?: string | undefined;
809
+ readonly httpStatus?: number | undefined;
810
+ /** Server-attached detail (JSON-RPC `error.data.detail`). User-facing kinds
811
+ * carry the specific reason here; internal kinds omit it. */
812
+ readonly detail?: string | undefined;
813
+ /** Per-request correlation id (JSON-RPC `error.data.request_id`). */
814
+ readonly requestId?: string | undefined;
815
+ constructor(message: string, rpcMethod?: string | undefined, httpStatus?: number | undefined,
816
+ /** Server-attached detail (JSON-RPC `error.data.detail`). User-facing kinds
817
+ * carry the specific reason here; internal kinds omit it. */
818
+ detail?: string | undefined,
819
+ /** Per-request correlation id (JSON-RPC `error.data.request_id`). */
820
+ requestId?: string | undefined);
927
821
  }
928
-
929
822
  /**
930
- * Token-metering wire shapes.
823
+ * Error thrown when an OTP send is throttled by the node.
931
824
  *
932
- * Mirrors the token-metering wire contract. `u128` fields land on the
933
- * wire as JSON numbers (same convention as the existing
934
- * `token.get-balance` response) — JS clients with histories that
935
- * exceed 2⁵³ tokens should switch to a streaming JSON parser; the
936
- * common case fits in `number` losslessly.
937
- */
938
- /**
939
- * The token `BalanceRow` wire shape — caller's credit row.
825
+ * The email-OTP login flow's `InitEmailOtp` step refuses a send when a
826
+ * still-valid code was issued within the resend cooldown
827
+ * (`reason: "cooldown_active"`) or the rolling per-recipient send cap is
828
+ * exhausted (`reason: "send_cap_exceeded"`). Previously a cooldown-
829
+ * suppressed resend returned a silent success, leaving a user who never
830
+ * received the first code at a dead end; this typed error lets the app
831
+ * render a "you just requested a code — retry in {retryAfterSecs}s"
832
+ * affordance instead.
940
833
  *
941
- * `available` and `reserved` are `u128` on the server; the wire
942
- * format is a JSON number. Callers should not assume bigint until
943
- * the SDK migrates to a streaming parser (tracked separately).
834
+ * Recognised from the node's stable
835
+ * `OtpRateLimited (reason=…, retry_after_secs=…)` wire token (pinned by a
836
+ * server-side test). Subclass of {@link RpcError} so callers that catch
837
+ * the parent still see `httpStatus`/`requestId`; callers that want the
838
+ * typed surface `instanceof OtpRateLimitedError` to read `reason` and
839
+ * `retryAfterSecs`.
944
840
  *
945
- * ⚠️ Amounts are in **base units** (`1 token = `
946
- * {@link BASE_UNITS_PER_TOKEN}), not whole tokens. Use
947
- * {@link formatTokens} to display them.
841
+ * Retryable after `retryAfterSecs` (caller-side back-off).
948
842
  */
949
- interface BalanceRow {
950
- /** Spendable balance, in base units (see {@link formatTokens}). */
951
- available: number;
952
- /** Reserved balance, in base units. */
953
- reserved: number;
954
- last_settled_seq_no: number;
955
- version: number;
956
- credit_exhausted: boolean;
843
+ declare class OtpRateLimitedError extends RpcError {
844
+ /** Stable reason code: `"cooldown_active"` or `"send_cap_exceeded"`. */
845
+ readonly reason: string;
846
+ /** Seconds to wait before retrying the send (parsed from `retry_after_secs=`). */
847
+ readonly retryAfterSecs: number;
848
+ constructor(message: string,
849
+ /** Stable reason code: `"cooldown_active"` or `"send_cap_exceeded"`. */
850
+ reason: string,
851
+ /** Seconds to wait before retrying the send (parsed from `retry_after_secs=`). */
852
+ retryAfterSecs: number, rpcMethod?: string, requestId?: string);
957
853
  }
958
854
  /**
959
- * The token-tx-kind wire alphabet (snake_case). Every
960
- * value listed here is accepted by the server-side
961
- * `token.get-usage` `kinds` filter.
962
- */
963
- type TokenTxKind = "mint" | "burn" | "charge" | "transfer" | "bridge_mint_attest" | "bridge_burn_attest";
964
- /**
965
- * Per-caller view direction. `"in"` = caller's balance went up;
966
- * `"out"` = caller's balance went down. Derived host-side from
967
- * `(caller_did, tx.src, tx.dst)`; the same `seq_no` appears in both
968
- * parties' usage feeds with opposite directions on a transfer.
969
- */
970
- type Direction = "in" | "out";
971
- /**
972
- * Discriminated union mirroring the charge-reason wire union.
973
- * Present only when the row's `kind === "charge"`.
974
- */
975
- type ChargeReason = {
976
- kind: "contract_register";
977
- script_name: string;
978
- version: string;
979
- } | {
980
- kind: "invocation";
981
- script_name: string;
982
- function: string;
983
- fuel_consumed: number;
984
- fuel_tokens: number;
985
- host_call_count: number;
986
- host_call_tokens: number;
987
- } | {
988
- kind: "kv_bytes";
989
- map: string;
990
- } | {
991
- kind: "cas_bytes";
992
- backend: string;
993
- } | {
994
- kind: "outbox_egress";
995
- upstream: string;
996
- };
997
- /**
998
- * One row in the caller's usage feed — a per-caller projection of
999
- * the underlying `TokenTx`. `counterparty` is the other party from
1000
- * the caller's perspective: `tx.dst` on outbound entries, `tx.src`
1001
- * on inbound. `null` when the underlying tx has no counterparty
1002
- * (mints have no `src`; burns and charges have no `dst`).
855
+ * Error thrown when WASM operations fail
1003
856
  */
1004
- interface UsageEntry {
1005
- seq_no: number;
1006
- kind: TokenTxKind;
1007
- /** Amount moved, in base units (`1 token = `{@link BASE_UNITS_PER_TOKEN}); see {@link formatTokens}. */
1008
- amount: number;
1009
- timestamp_ms: number;
1010
- direction: Direction;
1011
- counterparty?: string | null;
1012
- reason?: ChargeReason | null;
1013
- note?: string | null;
857
+ declare class WasmError extends T3nError {
858
+ readonly operation?: string | undefined;
859
+ readonly payload?: unknown | undefined;
860
+ constructor(message: string, operation?: string | undefined, payload?: unknown | undefined);
1014
861
  }
1015
862
  /**
1016
- * Response shape of `token.get-usage` — caller's balance plus a
1017
- * paginated slice of their most-recent `token:tx_log` entries.
863
+ * Decode WASM error message from comma-separated byte array format
864
+ * WASM errors often come as "83,101,114,100,101..." which represents ASCII bytes
1018
865
  *
1019
- * `next_cursor` is the inclusive upper-bound `seq_no` to pass back
1020
- * as `after_seq` to fetch the next page. `null` (or absent) means
1021
- * the caller's history is fully drained.
866
+ * @param errorMessage - The error message string that may contain comma-separated bytes
867
+ * @returns Decoded error message if it was encoded, otherwise original message
1022
868
  */
1023
- interface UsagePage {
1024
- balance: BalanceRow;
1025
- entries: UsageEntry[];
1026
- next_cursor?: number | null;
1027
- }
869
+ declare function decodeWasmErrorMessage(errorMessage: string): string;
1028
870
  /**
1029
- * Request shape — all fields optional. `limit` clamps to
1030
- * `1..=200` server-side; out-of-range values snap silently to the
1031
- * bounds. `kinds: []` is treated as "no filter".
871
+ * Extract and decode error from WASM ComponentError
872
+ *
873
+ * @param error - The error object from WASM
874
+ * @returns Decoded error message
1032
875
  */
1033
- interface GetUsageOptions {
1034
- limit?: number;
1035
- after_seq?: number;
1036
- kinds?: TokenTxKind[];
1037
- }
876
+ declare function extractWasmError(error: unknown): string;
1038
877
 
1039
878
  /**
1040
- * Tenant audit-event wire shapes.
879
+ * Contract response decoding for {@link T3nClient.execute}.
1041
880
  *
1042
- * Mirrors `tenant_types::AuditEvent` and the `audit.get-mine` response
1043
- * (`service::tenant_logs::{AuditBatch, AuditPage}`). A tenant contract
1044
- * emits an audit event via the `logging::audit` host call; the host
1045
- * stamps the identity fields (`subject` / `actor` / `vc_id`) from the
1046
- * verified dispatch context, so a contract can never forge who acted or
1047
- * on whom. Events are permanent (encrypted, append-only) and scoped to a
1048
- * user — distinct from the evicting debug ring read via `getContractLogs`.
881
+ * ## On-wire shape
882
+ *
883
+ * Trinity contracts are declared as `result<list<u8>, string>` in WIT and
884
+ * return JSON-encoded bytes. The server-side pipeline is:
885
+ *
886
+ * 1. The contract's returned bytes are deserialized into a JSON `Value`
887
+ * (the contract's decoded return value).
888
+ * 2. The host wraps that value in an internal `ContractResponse` struct
889
+ * for bookkeeping — it carries an extracted `otp_state` alongside the
890
+ * response.
891
+ * 3. The host then unwraps the struct, discarding the `otp_state` field
892
+ * and returning only the inner `Value` across the executor boundary.
893
+ * 4. That plain `Value` is serialised through the session channel.
894
+ *
895
+ * The client therefore receives the contract's **decoded JSON value
896
+ * directly** — no `{response, otp_state}` envelope, despite what the
897
+ * struct name suggests. `parseContractResponse` reflects that reality:
898
+ * it does a typed `JSON.parse` with optional schema validation, nothing
899
+ * more. OTP flows read their `otp_status` / OTP fields directly off the
900
+ * contract's response body; there is no separate envelope field for them
901
+ * on this path.
1049
902
  */
903
+
1050
904
  /**
1051
- * One host-stamped audit event. `subject` / `actor` are canonical DID
1052
- * strings. On a self-call `actor === subject` and `vc_id` is `null`; on a
1053
- * delegated call `actor` is the agent and `vc_id` is the delegation
1054
- * credential. The remaining fields are supplied verbatim by the contract.
905
+ * Minimal validator interface. Any library that exposes `.parse(value) -> T`
906
+ * (zod, valibot, superstruct, custom) is compatible.
1055
907
  */
1056
- interface AuditEvent {
1057
- /** Cluster-deterministic flush timestamp (ms). */
1058
- ts_ms: number;
1059
- /** The user whose data the call touched (host-stamped `pii_did`). */
1060
- subject: string;
1061
- /** The agent (delegated) or user (self-call) who acted (host-stamped). */
1062
- actor: string;
1063
- /** Delegation credential id on a delegated call; `null` on a self-call. */
1064
- vc_id?: string | null;
1065
- /** Contract-supplied: what happened, e.g. `"kyc.approve"`. */
1066
- action: string;
1067
- /** Contract-supplied: what it acted on, e.g. `"user-profile"`. */
1068
- target: string;
1069
- /** Contract-supplied: the result, e.g. `"success"` / `"denied"`. */
1070
- outcome: string;
1071
- /** Contract-supplied: optional free-text / JSON detail. */
1072
- details?: string | null;
908
+ interface ContractResponseSchema<T> {
909
+ parse(value: unknown): T;
1073
910
  }
1074
911
  /**
1075
- * One archived batch — the events committed in a single tenant dispatch.
1076
- * Every event in a batch shares the same `subject` / `actor` / `vc_id`.
912
+ * Thrown when the response string from {@link T3nClient.execute} cannot
913
+ * be parsed as JSON.
914
+ *
915
+ * Schema-validation failures are NOT wrapped — they surface as whatever
916
+ * error the caller's validator throws, preserving the rich diagnostics
917
+ * those libraries already provide.
1077
918
  */
1078
- interface AuditBatch {
1079
- /** Hex of the archive key — usable directly as `cursor` for the next page. */
1080
- key: string;
1081
- /**
1082
- * Host-stamped: did the emitting dispatch's contract transaction
1083
- * commit? `false` means the call rolled back or trapped, so an event's
1084
- * `outcome: "success"` in this batch is the contract's *claim*, not a
1085
- * committed fact — filter on this when you need only durable events.
1086
- */
1087
- committed: boolean;
1088
- events: AuditEvent[];
919
+ declare class ContractResponseError extends T3nError {
920
+ readonly raw?: string | undefined;
921
+ constructor(message: string, raw?: string | undefined);
1089
922
  }
1090
923
  /**
1091
- * `audit.get-mine` response — a page of audit batches, newest scan order.
1092
- * `next_cursor` is present (a key hex) when more batches remain; `null`
1093
- * once the scan reaches the end.
924
+ * Parse the JSON-encoded response returned by {@link T3nClient.execute}
925
+ * into a typed value.
926
+ *
927
+ * @param raw - the string returned by {@link T3nClient.execute}
928
+ * @param schema - optional validator applied to the parsed value
929
+ * @returns the decoded response, typed as `T`
930
+ * @throws {ContractResponseError} when `raw` is not valid JSON
931
+ * @throws whatever the schema throws on validation failure (e.g. `ZodError`)
1094
932
  */
1095
- interface AuditPage {
1096
- batches: AuditBatch[];
1097
- next_cursor?: string | null;
1098
- }
933
+ declare function parseContractResponse<T = unknown>(raw: string, schema?: ContractResponseSchema<T>): T;
934
+
1099
935
  /**
1100
- * Request shape for {@link T3nClient.getAuditEvents} — all fields
1101
- * optional. Omit `pii_did` to read your own trail (all actors); set it to
1102
- * another user's DID to read, as a delegated agent, the events you
1103
- * performed for them — admitted only while that user's agent-auth grant
1104
- * to you is live. `cursor` is the `next_cursor` from a prior page.
936
+ * Agent-auth grants — the standing on-chain authority a user delegates to
937
+ * an agent (envelope-free authorisation).
938
+ *
939
+ * `AGENT_AUTH_MAP` is keyed by the *user* DID — one policy document per
940
+ * user, global across orgs, with at most one entry per agent DID. Reads
941
+ * (`agent-auth-get`) and writes (`agent-auth-update`) on
942
+ * `tee:user/contracts` are **SelfOnly**: only the user, authenticated as
943
+ * themselves, may read or write their own grants. This is the user-approval
944
+ * proof the per-call delegation credential's `user_sig` used to carry.
945
+ *
946
+ * The write shape gained `scopes` + a validity window: the
947
+ * agent's delegated org-data scopes, and an optional `[valid_from_secs,
948
+ * valid_until_secs]` window. The ≤ cap *length* check is enforced contract-
949
+ * side at write time; the expiry gate is host-side at read time.
950
+ *
951
+ * The merge below is a script-level union (R59): writing a grant for one
952
+ * `(agent_did, script_name)` preserves every other script row on that
953
+ * agent's entry, and every other agent's entry, unchanged.
1105
954
  */
1106
- interface GetAuditEventsOptions {
1107
- pii_did?: string;
1108
- limit?: number;
1109
- cursor?: string;
955
+ /** A single script grant in the camelCase write-request shape. */
956
+ interface AgentAuthScriptGrant {
957
+ /** Contract name, or `"*"`. */
958
+ scriptName: string;
959
+ /** Optional semver requirement; `null` to match any version. */
960
+ versionReq?: string | null;
961
+ /** Functions the agent may invoke on this contract. */
962
+ functions: string[];
963
+ /** Org-data scopes delegated to the agent for the contract functions to
964
+ * read on its behalf. */
965
+ scopes?: string[];
966
+ /** Direct-read scopes the agent may enumerate itself, via whichever read
967
+ * function the contract exposes for direct enumeration. A strict subset of
968
+ * `scopes` (enforced contract-side). */
969
+ readScopes?: string[];
970
+ /** Optional validity-window start (unix seconds). */
971
+ validFromSecs?: number | null;
972
+ /** NEW: optional validity-window end (unix seconds). */
973
+ validUntilSecs?: number | null;
974
+ /** Egress hosts the agent may reach for this contract. */
975
+ allowedHosts: string[];
976
+ }
977
+ /** One agent's entry in the write-request shape. */
978
+ interface AgentAuthEntry {
979
+ agentDid: string;
980
+ scripts: AgentAuthScriptGrant[];
1110
981
  }
1111
-
1112
- /**
1113
- * Public types export for T3n SDK
1114
- */
1115
982
  /**
1116
- * Guest-to-Host request handler function type
983
+ * The full agent-auth policy document in the camelCase write shape:
984
+ * the per-agent grants plus the document-level discovery grants.
1117
985
  *
1118
- * Handles requests from WASM guest that need host (SDK) to perform side
1119
- * effects. The exact shape of `requestData` depends on the specific
1120
- * handler — see `GuestToHostHandlers` below for the per-handler shapes.
1121
- * The wrapper layer in `T3nClient.handleGuestToHost` parses the JSON
1122
- * envelope and calls the matching handler with the parsed data, so
1123
- * each handler's implementation should narrow `requestData` to its
1124
- * own expected shape.
986
+ * `discoverDids` (contract wire: `discover_dids`) is the
987
+ * list of DIDs the user lets their registered agents *enumerate the
988
+ * contracts of* via `listContracts`: an org DID grants org-mode discovery,
989
+ * the user's own DID grants self-mode discovery. It lives at document
990
+ * level — not on a script grant — because discovery must surface contracts
991
+ * the agent cannot yet call. The node's discovery gate additionally
992
+ * requires the calling agent to be named in `agents` (subject binding), so
993
+ * an unregistered agent discovers nothing regardless of this list.
1125
994
  */
1126
- type GuestToHostHandler = (requestData: Record<string, unknown>) => Promise<Uint8Array>;
995
+ interface AgentAuthPolicy {
996
+ agents: AgentAuthEntry[];
997
+ discoverDids: string[];
998
+ }
1127
999
  /**
1128
- * Map of guest-to-host request handlers
1129
- * Keys match the guest_to_host tag values from the WASM
1000
+ * Parse a full `agent-auth-get` response (the stored snake_case policy
1001
+ * document) into the camelCase write shape, including the document-level
1002
+ * `discover_dids`. A `null` / absent response is the empty policy.
1130
1003
  */
1131
- interface GuestToHostHandlers {
1132
- /**
1133
- * Handle Ethereum signature requests
1134
- * requestData: { guest_to_host: "EthSign", challenge: string (base64) }
1135
- * Returns: JSON bytes of { host_to_guest: "EthSign", challenge: string, signature: string }
1136
- */
1137
- EthSign?: GuestToHostHandler;
1138
- /**
1139
- * Handle MlKem public key requests
1140
- * requestData: { guest_to_host: "MlKemPublicKey" }
1141
- * Returns: JSON bytes of { host_to_guest: "MlKemPublicKey", key: string }
1142
- */
1143
- MlKemPublicKey?: GuestToHostHandler;
1144
- /**
1145
- * Handle random bytes requests
1146
- * requestData: { guest_to_host: "Random", len?: number }
1147
- * Returns: JSON bytes of { host_to_guest: "Random", bytes: string (base64) }
1148
- */
1149
- Random?: GuestToHostHandler;
1150
- [key: string]: GuestToHostHandler | undefined;
1004
+ declare function parseAgentAuthPolicy(response: unknown): AgentAuthPolicy;
1005
+ /** Extract the `agents` array from an `agent-auth-get` response. */
1006
+ declare function parseAgentAuthGet(response: unknown): AgentAuthEntry[];
1007
+ interface AgentAuthMergeResult {
1008
+ /** Full merged `agents` list, ready for `agent-auth-update`. */
1009
+ agents: AgentAuthEntry[];
1010
+ /** `"<agentDid> → <scriptName>"` rows preserved from the existing policy. */
1011
+ preservedRows: string[];
1012
+ }
1013
+ /** Input to the full-document write {@link T3nClient.agentAuthUpdate}. The
1014
+ * document IS the new state: an empty `agents` list revokes all agent
1015
+ * access, and an omitted / empty `discoverDids` persists an empty
1016
+ * discovery-grant list (the contract has no per-field merge). Mirrors the
1017
+ * contract `AgentAuthPolicy`. */
1018
+ interface AgentAuthUpdateInput {
1019
+ agents: AgentAuthEntry[];
1020
+ /** Document-level discovery grants — see {@link AgentAuthPolicy}. */
1021
+ discoverDids?: string[];
1151
1022
  }
1152
-
1153
1023
  /**
1154
- * Transport layer for T3n SDK
1155
- *
1156
- * Abstracts the communication with T3n nodes, allowing for different
1157
- * implementations (HTTP, WebSocket, Mock) and easy testing.
1024
+ * Serialise a camelCase policy document to the contract's canonical
1025
+ * snake_case `agent-auth-update` wire (`agent_did` / `script_name` /
1026
+ * `version_req` / `read_scopes` / `valid_from_secs` / `valid_until_secs` /
1027
+ * `allowed_hosts` / `discover_dids`), so neither write path relies on the
1028
+ * contract's compatibility aliases. Absent optionals are omitted (never
1029
+ * sent as `null`); empty `scopes` / `read_scopes` are omitted (the
1030
+ * contract defaults both to empty). `functions` and `allowed_hosts` are
1031
+ * always emitted — the contract requires explicit functions (`["*"]` =
1032
+ * all) and reads an absent `allowed_hosts` as deny-all egress. The
1033
+ * document-level `discover_dids` (strictly snake_case — the contract
1034
+ * defines no camelCase alias for it) is emitted only when non-empty; the
1035
+ * contract defaults an absent list to empty.
1158
1036
  */
1037
+ declare function toAgentAuthUpdateWire(agents: AgentAuthEntry[], discoverDids?: string[]): {
1038
+ agents: Array<{
1039
+ agent_did: string;
1040
+ scripts: Array<Record<string, unknown>>;
1041
+ }>;
1042
+ discover_dids?: string[];
1043
+ };
1159
1044
  /**
1160
- * JSON-RPC request structure
1045
+ * Order-preserving deduplicating union of discovery-grant DID lists —
1046
+ * the read-merge-write counterpart of {@link mergeAgentAuthEntries} for
1047
+ * the document-level `discoverDids` (the contract rejects duplicate
1048
+ * entries, so the union must dedupe).
1161
1049
  */
1162
- interface JsonRpcRequest {
1163
- jsonrpc: "2.0";
1164
- method: string;
1165
- params: unknown;
1166
- id: string | number;
1167
- }
1050
+ declare function mergeDiscoverDids(existing: string[], additions: string[]): string[];
1168
1051
  /**
1169
- * JSON-RPC response structure
1052
+ * Merge new agent entries into the existing policy with script-level union
1053
+ * semantics: for an agent DID present on both sides, script rows whose
1054
+ * `scriptName` is not being written are preserved; rows with a matching
1055
+ * `scriptName` are replaced by the new grant. Entries for other agent DIDs
1056
+ * pass through unchanged.
1170
1057
  */
1171
- interface JsonRpcResponse {
1172
- jsonrpc: "2.0";
1173
- result?: unknown;
1174
- error?: {
1175
- code: number;
1176
- message: string;
1177
- data?: unknown;
1178
- };
1179
- id: string | number;
1180
- }
1058
+ declare function mergeAgentAuthEntries(existing: AgentAuthEntry[], newEntries: AgentAuthEntry[]): AgentAuthMergeResult;
1059
+
1181
1060
  /**
1182
- * Transport interface for sending requests to T3n nodes
1061
+ * KYC types for the `tee:user/contracts::kyc-status` short-poll
1062
+ * function.
1063
+ *
1064
+ * The shape mirrors the `kyc-status` wire response.
1065
+ * The bytes go straight from the contract
1066
+ * through the JSON-RPC envelope into [[T3nClient.kycStatus]].
1183
1067
  */
1184
- interface Transport {
1185
- /**
1186
- * Send a JSON-RPC request to the T3n node
1187
- * @param request - The JSON-RPC request to send
1188
- * @param headers - Additional headers to include
1189
- * @returns Promise that resolves to the JSON-RPC response
1190
- */
1191
- send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
1192
- /**
1193
- * Send a JSON-RPC request with an attached binary blob (multipart/form-data).
1194
- * Part 1 (name=jsonrpc): JSON-RPC envelope.
1195
- * Part 2 (name=blob): raw binary bytes (e.g. WASM bytecode).
1196
- */
1197
- sendMultipart?(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
1198
- /**
1199
- * Optional accessor for the latest Set-Cookie header value.
1200
- * (Useful in Node.js demos/tests; browsers block HttpOnly cookies.)
1201
- */
1202
- getLastSetCookie?(): string | null;
1203
- /**
1204
- * Optional accessor for the last response headers (debugging).
1205
- */
1206
- getLastResponseHeaders?(): Record<string, string>;
1207
- }
1068
+
1208
1069
  /**
1209
- * HTTP transport implementation using fetch
1070
+ * Terminal status for a Level 2 KYC verification, plus `pending`.
1071
+ *
1072
+ * - `pending` — provider has not delivered a verdict yet, or the
1073
+ * webhook arrived but the post-action VC issuance hasn't completed.
1074
+ * - `verified` — provider approved AND a VC has been issued. `vcIds`
1075
+ * carries the issued credential ids.
1076
+ * - `rejected` — provider declined / required resubmission /
1077
+ * expired / the user abandoned the flow. `error` may carry a
1078
+ * provider-supplied reason.
1079
+ * - `orphan` — a Veriff webhook arrived with a `vendorData` that
1080
+ * couldn't be matched to any user. Should not
1081
+ * happen in the MetaMask flow but the contract surfaces it for
1082
+ * completeness.
1210
1083
  */
1211
- declare class HttpTransport implements Transport {
1212
- private baseUrl;
1213
- private timeout;
1214
- private lastSetCookie;
1215
- private lastResponseHeaders;
1216
- /**
1217
- * Cookie jar (`name -> value`) for non-browser callers. The session is
1218
- * node-local in-memory state, so once the load balancer pins a client
1219
- * to a node via its `GCLB` session-affinity cookie, every later request
1220
- * in the session must carry that cookie back or it gets re-balanced to
1221
- * a node that has never seen the handshake → `401 session not found`.
1222
- * Browsers replay the cookie automatically from their own jar (via
1223
- * `credentials: "include"`); plain `fetch` callers (Node demos, tests,
1224
- * server-side SDK usage) have no jar, so we keep one here. Empty in
1225
- * browsers — there `getSetCookie()` is unavailable and a script-set
1226
- * `Cookie` header is forbidden, so this stays inert.
1227
- */
1228
- private cookieJar;
1229
- constructor(baseUrl: string, timeout?: number);
1230
- getLastSetCookie(): string | null;
1231
- getLastResponseHeaders(): Record<string, string>;
1232
- /**
1233
- * Record response headers and merge any `Set-Cookie` into the jar.
1234
- *
1235
- * undici's `Headers` exposes `getSetCookie()` (one entry per cookie);
1236
- * the DOM `Headers` type doesn't declare it, so cast structurally. In
1237
- * browsers neither `getSetCookie()` nor reading `set-cookie` is
1238
- * permitted, so `setCookies` is empty and the jar is left untouched.
1239
- */
1240
- private captureResponseMetadata;
1241
- /**
1242
- * Build a `Cookie` request header from the jar so non-browser callers
1243
- * stay pinned to the node holding their session. Returns `{}` when the
1244
- * jar is empty — always the case in browsers, leaving their native
1245
- * cookie handling untouched.
1246
- *
1247
- * `callerHeaders` is checked case-insensitively: if the caller already
1248
- * supplied a `Cookie`/`cookie` header, the jar yields to it entirely.
1249
- * (Object spread keys are case-sensitive, but `fetch`'s `Headers` are
1250
- * not — emitting `Cookie` here while the caller passed `cookie` would
1251
- * make the two coexist and get comma-combined instead of overridden.)
1252
- */
1253
- private cookieHeader;
1254
- send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
1255
- sendMultipart(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
1256
- }
1084
+ type KycStatusKind = "pending" | "verified" | "rejected" | "orphan";
1257
1085
  /**
1258
- * Mock transport for testing
1259
- *
1260
- * @example
1261
- * ```typescript
1262
- * const mockTransport = new MockTransport();
1263
- * mockTransport.mockResponse("auth.handshake", { result: "..." });
1086
+ * Snapshot returned by `tee:user/contracts::kyc-status`.
1264
1087
  *
1265
- * const client = new T3nClient({
1266
- * trustAnchor: { unsafe_trust_server: true },
1267
- * transport: mockTransport,
1268
- * wasmComponent: mockWasm,
1269
- * });
1270
- * ```
1088
+ * Field names use camelCase on the SDK boundary even though the
1089
+ * wire is snake_case — the wrapper rewrites keys at the client edge
1090
+ * so callers don't see the JSON shape leaking through.
1271
1091
  */
1272
- declare class MockTransport implements Transport {
1273
- private responses;
1274
- private responseHeaders;
1275
- private lastResponseHeaders;
1276
- private requests;
1277
- private multipartRequests;
1278
- /**
1279
- * Mock a response for a specific method
1280
- */
1281
- mockResponse(method: string, response: Partial<JsonRpcResponse>): void;
1282
- /**
1283
- * Mock response headers for a specific method. Used by tests to
1284
- * simulate the server-minted `Session-Id` header the SDK picks up
1285
- * from the `auth.handshake` response. Unset methods
1286
- * default to no headers.
1287
- */
1288
- mockResponseHeaders(method: string, headers: Record<string, string>): void;
1092
+ interface KycStatus {
1093
+ /** Terminal status if reached, otherwise `pending`. */
1094
+ status: KycStatusKind;
1289
1095
  /**
1290
- * Mock an error response for a specific method
1096
+ * Provider being polled. Echoed back so callers that didn't
1097
+ * supply one see the contract's default (`"veriff"` in phase one).
1291
1098
  */
1292
- mockError(method: string, code: number, message: string, data?: unknown): void;
1293
- getLastResponseHeaders(): Record<string, string>;
1099
+ provider: string;
1294
1100
  /**
1295
- * Get all requests that were sent
1101
+ * Unix-millis of the latest contract-visible event:
1102
+ * VC issuance time, attestation arrival time, session-row
1103
+ * `started_at_ms`, or orphan-record arrival time. Best-effort —
1104
+ * `undefined` when no source carries a usable timestamp.
1296
1105
  */
1297
- getRequests(): Array<{
1298
- request: JsonRpcRequest;
1299
- headers: Record<string, string>;
1300
- }>;
1106
+ updatedAt?: number;
1301
1107
  /**
1302
- * Get requests for a specific method
1108
+ * VC ids appended for this provider, in append order. Empty for
1109
+ * `pending` / `rejected` / `orphan`.
1303
1110
  */
1304
- getRequestsForMethod(method: string): Array<{
1305
- request: JsonRpcRequest;
1306
- headers: Record<string, string>;
1307
- }>;
1308
- getMultipartRequests(): Array<{
1309
- request: JsonRpcRequest;
1310
- headers: Record<string, string>;
1311
- blob: Blob;
1312
- }>;
1111
+ vcIds: string[];
1313
1112
  /**
1314
- * Clear all recorded requests
1113
+ * Provider-supplied reason for `rejected`, when available.
1315
1114
  */
1316
- clearRequests(): void;
1317
- sendMultipart(request: JsonRpcRequest, headers: Record<string, string>, blob: Blob): Promise<JsonRpcResponse>;
1318
- send(request: JsonRpcRequest, headers: Record<string, string>): Promise<JsonRpcResponse>;
1115
+ error?: string;
1319
1116
  }
1320
-
1321
1117
  /**
1322
- * TDX quote verifier backed by the Rust `signature` crate compiled to
1323
- * WASM. Full cryptographic verification: ECDSA P-256 attestation-key
1324
- * signature, PCK certificate chain walk to Intel's root CA, and
1325
- * report_data + RTMR comparison.
1326
- *
1327
- * The WASM bytes are inlined as base64 (see quote_verifier_bytes.ts)
1328
- * so the SDK works without bundler WASM plugins and without runtime
1329
- * asset URL resolution.
1118
+ * Polling cadence for [[T3nClient.kycStatusPoll]]. Defaults:
1119
+ * poll fast for the first 30 seconds, then back
1120
+ * off, and bail after 5 minutes.
1330
1121
  */
1331
- interface QuoteVerifyResult {
1332
- valid: boolean;
1333
- error?: string;
1334
- rtmr3?: string;
1335
- report_data?: string;
1336
- }
1337
- interface PeerQuoteResult {
1338
- peer_id: string;
1339
- valid: boolean;
1340
- error?: string;
1341
- rtmr3?: string;
1342
- }
1343
- interface DkgVerifyResult {
1344
- valid: boolean;
1345
- results: PeerQuoteResult[];
1346
- valid_count: number;
1347
- expected_count: number;
1348
- error?: string;
1122
+ interface KycPollCadence {
1123
+ /** Poll interval in ms while `elapsed < switchAtMs`. Default: 2000. */
1124
+ fastMs: number;
1125
+ /** Poll interval in ms once `elapsed >= switchAtMs`. Default: 5000. */
1126
+ slowMs: number;
1127
+ /** Elapsed-ms threshold to switch from fast to slow cadence. Default: 30_000. */
1128
+ switchAtMs: number;
1129
+ /**
1130
+ * Maximum total time to spend polling before rejecting with
1131
+ * [[KycStatusTimeoutError]]. Default: 300_000 (5 minutes).
1132
+ */
1133
+ timeoutMs: number;
1349
1134
  }
1350
1135
  /**
1351
- * Client-pinned anchor the DKG attestation bundle is verified against.
1352
- *
1353
- * Without it, "the server proved it runs in a TEE" only means "*some*
1354
- * TDX box somewhere" — any attacker with their own TDX cloud VM can
1355
- * forge a complete, cryptographically-valid bundle for their own key
1356
- * (SP-003). The anchor is the independent ground truth that closes it:
1357
- * the bundle must describe *our* cluster (`expected_peer_ids`) running
1358
- * *our* image (`rtmr3_allowlist`).
1136
+ * Optional knobs for [[T3nClient.kycStatusPoll]]. Most callers won't
1137
+ * touch any of these — the cadence defaults are baked in.
1359
1138
  */
1360
- interface TrustAnchor {
1139
+ interface KycPollOptions {
1361
1140
  /**
1362
- * Base58 peer IDs the client expects in the cluster. The bundle's
1363
- * `peer_ids` must equal this set exactly (order-insensitive) — a MITM
1364
- * cannot substitute its own single-node participant set.
1141
+ * Cancellation signal. When aborted, the helper rejects with
1142
+ * `signal.reason` (or a generic `AbortError` if the consumer
1143
+ * didn't supply a reason). The currently-in-flight `kycStatus()`
1144
+ * call also receives the signal so it can short-circuit.
1365
1145
  */
1366
- expected_peer_ids: string[];
1146
+ signal?: AbortSignal;
1367
1147
  /**
1368
- * Base64-encoded 48-byte RTMR3 measurements of the expected node
1369
- * image. Every quote's RTMR3 must be a member. **Must be non-empty.**
1370
- * An array (not a singleton) so a rolling deploy's two in-flight
1371
- * image measurements both verify.
1148
+ * Called with every snapshot the helper receives, including
1149
+ * non-terminal `pending` ones. Useful for surfacing intermediate
1150
+ * UI states (e.g. "still waiting on provider…"). Errors thrown
1151
+ * from this callback are caught and ignored — they must not
1152
+ * sink the poll loop.
1372
1153
  */
1373
- rtmr3_allowlist: string[];
1154
+ onUpdate?: (status: KycStatus) => void;
1155
+ /**
1156
+ * Override one or more cadence fields. Anything not specified
1157
+ * uses the defaults from [[DEFAULT_KYC_POLL_CADENCE]].
1158
+ */
1159
+ cadence?: Partial<KycPollCadence>;
1160
+ /**
1161
+ * Provider id to poll. Defaults to the contract default
1162
+ * (`"veriff"` in phase one). Mirrored on the wire as
1163
+ * `input.provider_id`.
1164
+ */
1165
+ providerId?: string;
1374
1166
  }
1375
1167
  /**
1376
- * Escape hatch for local dev / tests against a node whose attestation
1377
- * can't be anchored (e.g. a mock signer that publishes no bundle).
1378
- *
1379
- * The name is deliberately alarming so it stands out in code review and
1380
- * grep — it is the *only* way to skip DKG attestation verification on
1381
- * the handshake path.
1168
+ * Default cadence used by [[T3nClient.kycStatusPoll]] when the caller
1169
+ * doesn't override `cadence.*`.
1382
1170
  */
1383
- interface UnsafeTrustServer {
1384
- unsafe_trust_server: true;
1171
+ declare const DEFAULT_KYC_POLL_CADENCE: KycPollCadence;
1172
+ /**
1173
+ * Subset of [[KycStatusKind]] that ends a poll. `pending` is the
1174
+ * only non-terminal value.
1175
+ */
1176
+ declare const TERMINAL_KYC_STATUSES: ReadonlySet<KycStatusKind>;
1177
+ /**
1178
+ * Thrown by [[T3nClient.kycStatusPoll]] when the cadence's
1179
+ * `timeoutMs` elapses without a terminal status arriving. The
1180
+ * `lastStatus` field is the most recent (necessarily `pending`)
1181
+ * snapshot the helper saw, useful for surfacing "we tried for N
1182
+ * minutes but Veriff is still working" UX.
1183
+ */
1184
+ declare class KycStatusTimeoutError extends T3nError {
1185
+ /** The timeout (ms) the helper exhausted before a terminal status arrived. */
1186
+ readonly timeoutMs: number;
1187
+ /** The last `pending` snapshot before timeout, if any. */
1188
+ readonly lastStatus?: KycStatus | undefined;
1189
+ constructor(
1190
+ /** The timeout (ms) the helper exhausted before a terminal status arrived. */
1191
+ timeoutMs: number,
1192
+ /** The last `pending` snapshot before timeout, if any. */
1193
+ lastStatus?: KycStatus | undefined);
1385
1194
  }
1386
- type TrustAnchorOrUnsafe = TrustAnchor | UnsafeTrustServer;
1387
- /** Narrow a {@link TrustAnchorOrUnsafe} to the explicit opt-out arm. */
1388
- declare function isUnsafeTrustServer(anchor: TrustAnchorOrUnsafe): anchor is UnsafeTrustServer;
1195
+
1389
1196
  /**
1390
- * Verify a single TDX attestation quote with full cryptographic verification.
1197
+ * Wire types for the explicit `otp-request` /
1198
+ * `otp-verify` / slim `user-upsert` exports on `tee:user/contracts`
1199
+ * (`tee:user@2.0.0`).
1391
1200
  *
1392
- * @param quoteB64 - Base64-encoded raw TDX v4 quote
1393
- * @param attestationMsgB64 - Base64-encoded attestation message
1394
- * (for DKG: encaps_key || sorted_peer_id_bytes)
1395
- * @param expectedRtmr3B64 - Optional base64-encoded 48-byte RTMR3
1396
- * @returns Verification result with extracted measurements
1201
+ * Pre-2.0.0 the user contract dispatched OTP request and OTP verify
1202
+ * implicitly from the input shape of a single `user-upsert` call.
1203
+ * 2.0.0 splits that surface into three explicit functions; the
1204
+ * shapes here mirror the OTP / user-upsert wire contract.
1205
+ *
1206
+ * Bytes flow directly from the contract
1207
+ * through the JSON-RPC envelope into the SDK wrappers
1208
+ * (`T3nClient.otpRequest`, `T3nClient.otpVerify`,
1209
+ * `T3nClient.submitUserInput`).
1397
1210
  */
1398
- declare function verifyTdxQuote(quoteB64: string, attestationMsgB64: string, expectedRtmr3B64?: string): Promise<QuoteVerifyResult>;
1211
+
1399
1212
  /**
1400
- * Verify a full DKG attestation bundle against a client-pinned
1401
- * {@link TrustAnchor}: multiple TDX quotes from all participating
1402
- * nodes, plus the binding between the quotes and the ML-KEM
1403
- * encapsulation key. Checks:
1404
- * 1. `expected_peer_ids` / `rtmr3_allowlist` are both non-empty
1405
- * 2. the bundle's `peer_ids` equal the pinned set exactly
1406
- * 3. attestationMsg starts with encapsKey (server can't swap the key)
1407
- * 4. every quote's ECDSA signature chains to Intel's SGX root CA
1408
- * 5. every quote's report_data == keccak512(attestationMsg)
1409
- * 6. every quote's RTMR3 is a member of `rtmr3_allowlist`
1213
+ * OTP delivery channel. Matches the `OtpContactChannel` Rust enum
1214
+ * with `serde(rename_all = "snake_case")`.
1215
+ */
1216
+ type OtpChannel = "email" | "sms";
1217
+ /**
1218
+ * Discriminated OTP contact target. Mirrors the Rust `OtpRequest` enum
1219
+ * (`email_channel` / `sms_channel` on the wire). Exactly one branch is
1220
+ * set — no parallel optional email + phone with a separate channel tag.
1410
1221
  *
1411
- * The anchor is **required**: there is no unauthenticated mode. Callers
1412
- * that genuinely want to trust the server must not call this at all —
1413
- * see `unsafe_trust_server` on the client config, which branches around
1414
- * the whole verify path.
1222
+ * The legacy `keys.generic_api.otp_channel` body shadow was removed
1223
+ * in 2.0.0 — the contract rejects calls carrying it with a
1224
+ * `legacy_field` error.
1225
+ */
1226
+ type OtpRequestInput = {
1227
+ emailChannel: {
1228
+ emailAddress: string;
1229
+ };
1230
+ } | {
1231
+ smsChannel: {
1232
+ phoneNumber: string;
1233
+ };
1234
+ };
1235
+ /**
1236
+ * Response returned by `otp-request`. Mirrors the OTP wire response.
1237
+ *
1238
+ * - `contact` echoes the OTP destination so clients that race
1239
+ * multiple channels can correlate replies.
1240
+ * - `expiresAtSec` is the Unix-second TTL the host minted for the
1241
+ * pending OTP. Absent in `skip_otp` test environments.
1242
+ * - `status` is `"otp_pending"` on the happy path. `"otp_failed"`
1243
+ * only appears on retry without a fresh request — usually you
1244
+ * shouldn't see it on `otp-request`.
1245
+ * - `txHash` is the host-enriched ledger ref for the OTP-pending
1246
+ * write (the contract leaves it `null`; the host injects).
1247
+ * - `isNewProfile` is `true` on a fresh DID's first OTP request —
1248
+ * read this to detect "did the user just register".
1249
+ */
1250
+ interface OtpRequestResult {
1251
+ contact: string;
1252
+ channel: OtpChannel;
1253
+ expiresAtSec?: number;
1254
+ status?: string;
1255
+ txHash?: string;
1256
+ isNewProfile?: boolean;
1257
+ }
1258
+ /**
1259
+ * Input to `T3nClient.otpVerify`. `otpCode` is mandatory; `request`
1260
+ * repeats the same discriminated contact shape as {@link OtpRequestInput}.
1261
+ */
1262
+ interface OtpVerifyInput {
1263
+ otpCode: string;
1264
+ request: OtpRequestInput;
1265
+ }
1266
+ /**
1267
+ * Suggestion surfaced when the bind-target contact already belongs
1268
+ * to another DID. The contract refuses to silently steal the
1269
+ * authenticator; the caller must resolve the merge (typically via
1270
+ * `merge-profiles`) before re-attempting verify.
1271
+ */
1272
+ interface OtpMergeSuggestion {
1273
+ existingDid: string;
1274
+ currentDid: string;
1275
+ email?: string;
1276
+ phone?: string;
1277
+ channel: OtpChannel;
1278
+ }
1279
+ /**
1280
+ * Response returned by `otp-verify`. Mirrors the OTP-verify wire response.
1415
1281
  *
1416
- * @param encapsKeyB64 - Base64-encoded ML-KEM encapsulation key
1417
- * (from `/status.encaps_key`)
1418
- * @param attestationMsgB64 - Base64-encoded raw attestation message
1419
- * (from `/status.dkg_attestation.attestation_msg`)
1420
- * @param peerIds - Base58 peer IDs as reported by the bundle
1421
- * @param quotes - Map of peer_id → base64-encoded TDX quote
1422
- * @param anchor - Client-pinned expected peer IDs + RTMR3 allowlist
1423
- * @returns Per-peer verification results and overall validity
1282
+ * - `email` is populated on `channel = "email"` success;
1283
+ * `phone` on `channel = "sms"` success. Mutually exclusive on
1284
+ * the happy path.
1285
+ * - `status` carries `"otp_failed"` / `"otp_expired"` on retry; the
1286
+ * happy path leaves it `undefined`.
1287
+ * - `mergeSuggestion` is set when the contact-to-bind is already
1288
+ * owned by another DID. Wire it into your merge UX.
1424
1289
  */
1425
- declare function verifyDkgAttestation(encapsKeyB64: string, attestationMsgB64: string, peerIds: string[], quotes: Record<string, string>, anchor: TrustAnchor): Promise<DkgVerifyResult>;
1426
-
1290
+ interface OtpVerifyResult {
1291
+ txHash?: string;
1292
+ did: string;
1293
+ channel: OtpChannel;
1294
+ email?: string;
1295
+ phone?: string;
1296
+ status?: string;
1297
+ mergeSuggestion?: OtpMergeSuggestion;
1298
+ isNewProfile?: boolean;
1299
+ }
1427
1300
  /**
1428
- * Configuration types for T3n Client
1301
+ * Level-1 user-input fields accepted by the slim `user-upsert`. Field names
1302
+ * are the on-the-wire names — `submitUserInput` sends this object verbatim,
1303
+ * so keys must match the contract exactly (snake_case). The canonical Level-1
1304
+ * set is the six fields below; the shape is intentionally open so callers can
1305
+ * add provider-specific fields the contract knows how to merge.
1306
+ *
1307
+ * `email_address` / `phone_number` are normally bound via the OTP roundtrip
1308
+ * before `submitUserInput`, but the contract accepts them directly here too.
1429
1309
  */
1430
-
1310
+ interface UserInputProfile {
1311
+ first_name?: string;
1312
+ last_name?: string;
1313
+ country_of_residence?: string;
1314
+ document_issuance_country?: string;
1315
+ ssn?: string;
1316
+ address?: string;
1317
+ email_address?: string;
1318
+ phone_number?: string;
1319
+ campaign_code?: string;
1320
+ role?: string;
1321
+ [key: string]: unknown;
1322
+ }
1431
1323
  /**
1432
- * Configuration interface for T3n Client
1324
+ * Input to `T3nClient.submitUserInput`. Maps onto the slim
1325
+ * `user-upsert` request shape: `{ profile, organisation_did?,
1326
+ * attestations?, keys? }`.
1433
1327
  */
1434
- interface T3nClientConfig {
1435
- /** Base URL of the T3n node (used if transport not provided) */
1436
- baseUrl?: string;
1437
- /** WASM component instance for cryptographic operations */
1438
- wasmComponent: WasmComponent;
1439
- /** Optional transport layer - if not provided, uses HttpTransport with baseUrl */
1440
- transport?: Transport;
1441
- /** Optional request timeout in milliseconds (default: 30000) - used for HttpTransport */
1442
- timeout?: number;
1443
- /** Optional custom headers to include in requests */
1444
- headers?: Record<string, string>;
1328
+ interface SubmitUserInputArgs {
1329
+ profile: UserInputProfile;
1330
+ organisationDid?: string;
1331
+ attestations?: unknown;
1332
+ keys?: Record<string, unknown>;
1445
1333
  /**
1446
- * Log level for this client instance.
1447
- * Defaults to global log level (LogLevel.ERROR) if not specified.
1448
- * Use LogLevel.DEBUG for verbose logging, LogLevel.INFO for informational messages,
1449
- * LogLevel.WARN for warnings, or LogLevel.ERROR for errors only.
1334
+ * KYC webhook orphan-attestation flow. When set, the contract
1335
+ * skips the email-not-verified gate and only records the
1336
+ * attestation if the DID has no profile yet.
1337
+ * Most callers should leave this `undefined`.
1450
1338
  */
1451
- logLevel?: LogLevel;
1452
- /** Optional custom logger - if provided, overrides logLevel */
1453
- logger?: Logger;
1454
- /** Optional guest-to-host request handlers - provides custom behavior for WASM requests */
1455
- handlers?: GuestToHostHandlers;
1339
+ requireExistingUser?: boolean;
1456
1340
  /**
1457
- * **Required.** Client-pinned trust anchor the node's DKG attestation
1458
- * is verified against before the handshake trusts its ML-KEM key
1459
- * (SP-003). Either a {@link TrustAnchor} (`expected_peer_ids` +
1460
- * `rtmr3_allowlist`) or the explicit `{ unsafe_trust_server: true }`
1461
- * opt-out for local dev / mock-signer nodes.
1341
+ * Testnet self-admit. When `true`, the contract additionally
1342
+ * runs the self-admit side-effect after the profile commit: writes
1343
+ * the caller's DID into `idx:_tenants` + `idx:_tenant_quotas` and
1344
+ * mints any operator-configured welcome credits. Inspect the
1345
+ * `tenantAdmit` field on the response for the outcome.
1462
1346
  *
1463
- * Without this, a network attacker with their own TDX VM can hand the
1464
- * SDK a forged-but-valid attestation for a key it controls and read
1465
- * every session. It is a required field precisely so no caller can
1466
- * omit it by accident — bypassing verification must be a visible,
1467
- * grep-able choice.
1347
+ * Testnet-only. Production clusters return
1348
+ * `tenantAdmit.status = "refused"` with
1349
+ * `reason = "not_testnet" | "self_admit_disabled" | "not_eth_derived"`.
1350
+ * The profile commit always succeeds regardless of the inner outcome.
1351
+ *
1352
+ * Most callers should leave this `undefined`.
1468
1353
  */
1469
- trustAnchor: TrustAnchorOrUnsafe;
1354
+ becomeDevTenant?: boolean;
1470
1355
  }
1471
-
1472
1356
  /**
1473
- * Contract response decoding for {@link T3nClient.execute}.
1474
- *
1475
- * ## On-wire shape
1476
- *
1477
- * Trinity contracts are declared as `result<list<u8>, string>` in WIT and
1478
- * return JSON-encoded bytes. The server-side pipeline is:
1479
- *
1480
- * 1. The contract's returned bytes are deserialized into a JSON `Value`
1481
- * (the contract's decoded return value).
1482
- * 2. The host wraps that value in an internal `ContractResponse` struct
1483
- * for bookkeeping — it carries an extracted `otp_state` alongside the
1484
- * response.
1485
- * 3. The host then unwraps the struct, discarding the `otp_state` field
1486
- * and returning only the inner `Value` across the executor boundary.
1487
- * 4. That plain `Value` is serialised through the session channel.
1357
+ * Testnet self-admit projection from `user-upsert`. Present only
1358
+ * when the caller set `becomeDevTenant: true` on the request.
1488
1359
  *
1489
- * The client therefore receives the contract's **decoded JSON value
1490
- * directly** — no `{response, otp_state}` envelope, despite what the
1491
- * struct name suggests. `parseContractResponse` reflects that reality:
1492
- * it does a typed `JSON.parse` with optional schema validation, nothing
1493
- * more. OTP flows read their `otp_status` / OTP fields directly off the
1494
- * contract's response body; there is no separate envelope field for them
1495
- * on this path.
1496
- */
1497
-
1498
- /**
1499
- * Minimal validator interface. Any library that exposes `.parse(value) -> T`
1500
- * (zod, valibot, superstruct, custom) is compatible.
1360
+ * - `status: "admitted"` — first call for this DID; tenant record
1361
+ * committed and (if configured) welcome credits minted into
1362
+ * `grantedCredits`.
1363
+ * - `status: "already-admitted"` — replay; idempotent no-op.
1364
+ * - `status: "refused"` — the cluster declined the self-admit;
1365
+ * `reason` is the machine-readable cause (`not_testnet`,
1366
+ * `self_admit_disabled`, `not_eth_derived`), `detail` is a
1367
+ * human-readable string.
1501
1368
  */
1502
- interface ContractResponseSchema<T> {
1503
- parse(value: unknown): T;
1369
+ type TenantAdmitStatus = "admitted" | "already-admitted" | "refused";
1370
+ interface TenantAdmitProjection {
1371
+ status: TenantAdmitStatus;
1372
+ grantedCredits?: string;
1373
+ reason?: string;
1374
+ detail?: string;
1504
1375
  }
1505
1376
  /**
1506
- * Thrown when the response string from {@link T3nClient.execute} cannot
1507
- * be parsed as JSON.
1508
- *
1509
- * Schema-validation failures are NOT wrapped — they surface as whatever
1510
- * error the caller's validator throws, preserving the rich diagnostics
1511
- * those libraries already provide.
1377
+ * Response shape returned by the slim `user-upsert`. Carries the
1378
+ * post-merge tx hash plus the Level-1 merge diagnostics
1379
+ * (`refusedFields`, `mergeSuggestion`) the pre-2.0.0 omnibus
1380
+ * `UpsertResponse` already surfaced.
1512
1381
  */
1513
- declare class ContractResponseError extends T3nError {
1514
- readonly raw?: string | undefined;
1515
- constructor(message: string, raw?: string | undefined);
1382
+ interface SubmitUserInputResult {
1383
+ txHash?: string;
1384
+ refusedFields?: string[];
1385
+ mergeSuggestion?: OtpMergeSuggestion;
1386
+ userFound?: boolean;
1387
+ /**
1388
+ * Testnet self-admit projection. Present only when the caller
1389
+ * set `becomeDevTenant: true` on the request.
1390
+ */
1391
+ tenantAdmit?: TenantAdmitProjection;
1516
1392
  }
1517
1393
  /**
1518
- * Parse the JSON-encoded response returned by {@link T3nClient.execute}
1519
- * into a typed value.
1394
+ * Discriminator for {@link UserUpsertError}. Mirrors the
1395
+ * `UserUpsertError` wire enum and its `<code>:<detail>` wire
1396
+ * format. Branch with a `switch` over `kind`.
1520
1397
  *
1521
- * @param raw - the string returned by {@link T3nClient.execute}
1522
- * @param schema - optional validator applied to the parsed value
1523
- * @returns the decoded response, typed as `T`
1524
- * @throws {ContractResponseError} when `raw` is not valid JSON
1525
- * @throws whatever the schema throws on validation failure (e.g. `ZodError`)
1398
+ * - `EmailNotVerified` — the slim `user-upsert` was called against
1399
+ * a DID that has no verified email and no proving authenticator.
1400
+ * Run `otpRequest` + `otpVerify` first (or accept an
1401
+ * OIDC/Email-authed session).
1402
+ * - `LegacyField` — caller passed a pre-2.0.0 dispatch field
1403
+ * (`otp_code` to a non-verify export, or
1404
+ * `keys.generic_api.otp_channel` anywhere). Migrate the call
1405
+ * site to the new explicit functions.
1406
+ * - `UserNotFound` — `requireExistingUser` was set but no profile
1407
+ * exists for the DID. The attestation is recorded for audit;
1408
+ * no profile created.
1526
1409
  */
1527
- declare function parseContractResponse<T = unknown>(raw: string, schema?: ContractResponseSchema<T>): T;
1528
-
1410
+ type UserUpsertErrorKind = "EmailNotVerified" | "LegacyField" | "UserNotFound";
1529
1411
  /**
1530
- * Agent-auth grants — the standing on-chain authority a user delegates to
1531
- * an agent (envelope-free authorisation).
1532
- *
1533
- * `AGENT_AUTH_MAP` is keyed by the *user* DID — one policy document per
1534
- * user, global across orgs, with at most one entry per agent DID. Reads
1535
- * (`agent-auth-get`) and writes (`agent-auth-update`) on
1536
- * `tee:user/contracts` are **SelfOnly**: only the user, authenticated as
1537
- * themselves, may read or write their own grants. This is the user-approval
1538
- * proof the per-call delegation credential's `user_sig` used to carry.
1539
- *
1540
- * The write shape gained `scopes` + a validity window: the
1541
- * agent's delegated org-data scopes, and an optional `[valid_from_secs,
1542
- * valid_until_secs]` window. The ≤ cap *length* check is enforced contract-
1543
- * side at write time; the expiry gate is host-side at read time.
1412
+ * Typed wrapper for the `<code>:<detail>` errors the slim
1413
+ * `user-upsert` and the OTP entry points emit. `kind` is the
1414
+ * structured discriminator the SDK derives from the error code
1415
+ * prefix; `code` and `detail` retain the wire components for
1416
+ * fall-through logging.
1544
1417
  *
1545
- * The merge below is a script-level union (R59): writing a grant for one
1546
- * `(agent_did, script_name)` preserves every other script row on that
1547
- * agent's entry, and every other agent's entry, unchanged.
1418
+ * Throw site: `T3nClient.submitUserInput` (and friends) when the
1419
+ * contract returns a string error matching the
1420
+ * `<code>:<detail>` shape.
1548
1421
  */
1549
- /** A single script grant in the camelCase write-request shape. */
1550
- interface AgentAuthScriptGrant {
1551
- /** Contract name, or `"*"`. */
1552
- scriptName: string;
1553
- /** Optional semver requirement; `null` to match any version. */
1554
- versionReq?: string | null;
1555
- /** Functions the agent may invoke on this contract. */
1556
- functions: string[];
1557
- /** Org-data scopes delegated to the agent for the contract functions to
1558
- * read on its behalf. */
1559
- scopes?: string[];
1560
- /** Direct-read scopes the agent may enumerate itself, via whichever read
1561
- * function the contract exposes for direct enumeration. A strict subset of
1562
- * `scopes` (enforced contract-side). */
1563
- readScopes?: string[];
1564
- /** Optional validity-window start (unix seconds). */
1565
- validFromSecs?: number | null;
1566
- /** NEW: optional validity-window end (unix seconds). */
1567
- validUntilSecs?: number | null;
1568
- /** Egress hosts the agent may reach for this contract. */
1569
- allowedHosts: string[];
1570
- }
1571
- /** One agent's entry in the write-request shape. */
1572
- interface AgentAuthEntry {
1573
- agentDid: string;
1574
- scripts: AgentAuthScriptGrant[];
1422
+ declare class UserUpsertError extends T3nError {
1423
+ readonly kind: UserUpsertErrorKind | "Unknown";
1424
+ readonly detail: string;
1425
+ constructor(code: string, detail: string);
1426
+ /**
1427
+ * Try to parse a contract error string into a `UserUpsertError`.
1428
+ * Returns `null` if `raw` doesn't match the `<code>:<detail>`
1429
+ * shape — caller falls back to a generic error.
1430
+ */
1431
+ static fromWire(raw: string): UserUpsertError | null;
1575
1432
  }
1433
+
1576
1434
  /**
1577
- * The full agent-auth policy document in the camelCase write shape:
1578
- * the per-agent grants plus the document-level discovery grants.
1435
+ * Token-metering wire shapes.
1579
1436
  *
1580
- * `discoverDids` (contract wire: `discover_dids`) is the
1581
- * list of DIDs the user lets their registered agents *enumerate the
1582
- * contracts of* via `listContracts`: an org DID grants org-mode discovery,
1583
- * the user's own DID grants self-mode discovery. It lives at document
1584
- * level — not on a script grant — because discovery must surface contracts
1585
- * the agent cannot yet call. The node's discovery gate additionally
1586
- * requires the calling agent to be named in `agents` (subject binding), so
1587
- * an unregistered agent discovers nothing regardless of this list.
1437
+ * Mirrors the token-metering wire contract. `u128` fields land on the
1438
+ * wire as JSON numbers (same convention as the existing
1439
+ * `token.get-balance` response) — JS clients with histories that
1440
+ * exceed 2⁵³ tokens should switch to a streaming JSON parser; the
1441
+ * common case fits in `number` losslessly.
1588
1442
  */
1589
- interface AgentAuthPolicy {
1590
- agents: AgentAuthEntry[];
1591
- discoverDids: string[];
1592
- }
1593
1443
  /**
1594
- * Parse a full `agent-auth-get` response (the stored snake_case policy
1595
- * document) into the camelCase write shape, including the document-level
1596
- * `discover_dids`. A `null` / absent response is the empty policy.
1444
+ * The token `BalanceRow` wire shape — caller's credit row.
1445
+ *
1446
+ * `available` and `reserved` are `u128` on the server; the wire
1447
+ * format is a JSON number. Callers should not assume bigint until
1448
+ * the SDK migrates to a streaming parser (tracked separately).
1449
+ *
1450
+ * ⚠️ Amounts are in **base units** (`1 token = `
1451
+ * {@link BASE_UNITS_PER_TOKEN}), not whole tokens. Use
1452
+ * {@link formatTokens} to display them.
1597
1453
  */
1598
- declare function parseAgentAuthPolicy(response: unknown): AgentAuthPolicy;
1599
- /** Extract the `agents` array from an `agent-auth-get` response. */
1600
- declare function parseAgentAuthGet(response: unknown): AgentAuthEntry[];
1601
- interface AgentAuthMergeResult {
1602
- /** Full merged `agents` list, ready for `agent-auth-update`. */
1603
- agents: AgentAuthEntry[];
1604
- /** `"<agentDid> → <scriptName>"` rows preserved from the existing policy. */
1605
- preservedRows: string[];
1606
- }
1607
- /** Input to the full-document write {@link T3nClient.agentAuthUpdate}. The
1608
- * document IS the new state: an empty `agents` list revokes all agent
1609
- * access, and an omitted / empty `discoverDids` persists an empty
1610
- * discovery-grant list (the contract has no per-field merge). Mirrors the
1611
- * contract `AgentAuthPolicy`. */
1612
- interface AgentAuthUpdateInput {
1613
- agents: AgentAuthEntry[];
1614
- /** Document-level discovery grants — see {@link AgentAuthPolicy}. */
1615
- discoverDids?: string[];
1454
+ interface BalanceRow {
1455
+ /** Spendable balance, in base units (see {@link formatTokens}). */
1456
+ available: number;
1457
+ /** Reserved balance, in base units. */
1458
+ reserved: number;
1459
+ last_settled_seq_no: number;
1460
+ version: number;
1461
+ credit_exhausted: boolean;
1616
1462
  }
1617
1463
  /**
1618
- * Serialise a camelCase policy document to the contract's canonical
1619
- * snake_case `agent-auth-update` wire (`agent_did` / `script_name` /
1620
- * `version_req` / `read_scopes` / `valid_from_secs` / `valid_until_secs` /
1621
- * `allowed_hosts` / `discover_dids`), so neither write path relies on the
1622
- * contract's compatibility aliases. Absent optionals are omitted (never
1623
- * sent as `null`); empty `scopes` / `read_scopes` are omitted (the
1624
- * contract defaults both to empty). `functions` and `allowed_hosts` are
1625
- * always emitted — the contract requires explicit functions (`["*"]` =
1626
- * all) and reads an absent `allowed_hosts` as deny-all egress. The
1627
- * document-level `discover_dids` (strictly snake_case — the contract
1628
- * defines no camelCase alias for it) is emitted only when non-empty; the
1629
- * contract defaults an absent list to empty.
1464
+ * The token-tx-kind wire alphabet (snake_case). Every
1465
+ * value listed here is accepted by the server-side
1466
+ * `token.get-usage` `kinds` filter.
1630
1467
  */
1631
- declare function toAgentAuthUpdateWire(agents: AgentAuthEntry[], discoverDids?: string[]): {
1632
- agents: Array<{
1633
- agent_did: string;
1634
- scripts: Array<Record<string, unknown>>;
1635
- }>;
1636
- discover_dids?: string[];
1637
- };
1468
+ type TokenTxKind = "mint" | "burn" | "charge" | "transfer" | "bridge_mint_attest" | "bridge_burn_attest";
1638
1469
  /**
1639
- * Order-preserving deduplicating union of discovery-grant DID lists —
1640
- * the read-merge-write counterpart of {@link mergeAgentAuthEntries} for
1641
- * the document-level `discoverDids` (the contract rejects duplicate
1642
- * entries, so the union must dedupe).
1470
+ * Per-caller view direction. `"in"` = caller's balance went up;
1471
+ * `"out"` = caller's balance went down. Derived host-side from
1472
+ * `(caller_did, tx.src, tx.dst)`; the same `seq_no` appears in both
1473
+ * parties' usage feeds with opposite directions on a transfer.
1643
1474
  */
1644
- declare function mergeDiscoverDids(existing: string[], additions: string[]): string[];
1475
+ type Direction = "in" | "out";
1645
1476
  /**
1646
- * Merge new agent entries into the existing policy with script-level union
1647
- * semantics: for an agent DID present on both sides, script rows whose
1648
- * `scriptName` is not being written are preserved; rows with a matching
1649
- * `scriptName` are replaced by the new grant. Entries for other agent DIDs
1650
- * pass through unchanged.
1477
+ * Discriminated union mirroring the charge-reason wire union.
1478
+ * Present only when the row's `kind === "charge"`.
1651
1479
  */
1652
- declare function mergeAgentAuthEntries(existing: AgentAuthEntry[], newEntries: AgentAuthEntry[]): AgentAuthMergeResult;
1653
-
1480
+ type ChargeReason = {
1481
+ kind: "contract_register";
1482
+ script_name: string;
1483
+ version: string;
1484
+ } | {
1485
+ kind: "invocation";
1486
+ script_name: string;
1487
+ function: string;
1488
+ fuel_consumed: number;
1489
+ fuel_tokens: number;
1490
+ host_call_count: number;
1491
+ host_call_tokens: number;
1492
+ } | {
1493
+ kind: "kv_bytes";
1494
+ map: string;
1495
+ } | {
1496
+ kind: "cas_bytes";
1497
+ backend: string;
1498
+ } | {
1499
+ kind: "outbox_egress";
1500
+ upstream: string;
1501
+ };
1654
1502
  /**
1655
- * KYC types for the `tee:user/contracts::kyc-status` short-poll
1656
- * function.
1657
- *
1658
- * The shape mirrors the `kyc-status` wire response.
1659
- * The bytes go straight from the contract
1660
- * through the JSON-RPC envelope into [[T3nClient.kycStatus]].
1503
+ * One row in the caller's usage feed — a per-caller projection of
1504
+ * the underlying `TokenTx`. `counterparty` is the other party from
1505
+ * the caller's perspective: `tx.dst` on outbound entries, `tx.src`
1506
+ * on inbound. `null` when the underlying tx has no counterparty
1507
+ * (mints have no `src`; burns and charges have no `dst`).
1661
1508
  */
1662
-
1509
+ interface UsageEntry {
1510
+ seq_no: number;
1511
+ kind: TokenTxKind;
1512
+ /** Amount moved, in base units (`1 token = `{@link BASE_UNITS_PER_TOKEN}); see {@link formatTokens}. */
1513
+ amount: number;
1514
+ timestamp_ms: number;
1515
+ direction: Direction;
1516
+ counterparty?: string | null;
1517
+ reason?: ChargeReason | null;
1518
+ note?: string | null;
1519
+ }
1663
1520
  /**
1664
- * Terminal status for a Level 2 KYC verification, plus `pending`.
1521
+ * Response shape of `token.get-usage` — caller's balance plus a
1522
+ * paginated slice of their most-recent `token:tx_log` entries.
1665
1523
  *
1666
- * - `pending` — provider has not delivered a verdict yet, or the
1667
- * webhook arrived but the post-action VC issuance hasn't completed.
1668
- * - `verified` — provider approved AND a VC has been issued. `vcIds`
1669
- * carries the issued credential ids.
1670
- * - `rejected` — provider declined / required resubmission /
1671
- * expired / the user abandoned the flow. `error` may carry a
1672
- * provider-supplied reason.
1673
- * - `orphan` — a Veriff webhook arrived with a `vendorData` that
1674
- * couldn't be matched to any user. Should not
1675
- * happen in the MetaMask flow but the contract surfaces it for
1676
- * completeness.
1524
+ * `next_cursor` is the inclusive upper-bound `seq_no` to pass back
1525
+ * as `after_seq` to fetch the next page. `null` (or absent) means
1526
+ * the caller's history is fully drained.
1677
1527
  */
1678
- type KycStatusKind = "pending" | "verified" | "rejected" | "orphan";
1528
+ interface UsagePage {
1529
+ balance: BalanceRow;
1530
+ entries: UsageEntry[];
1531
+ next_cursor?: number | null;
1532
+ }
1679
1533
  /**
1680
- * Snapshot returned by `tee:user/contracts::kyc-status`.
1681
- *
1682
- * Field names use camelCase on the SDK boundary even though the
1683
- * wire is snake_case — the wrapper rewrites keys at the client edge
1684
- * so callers don't see the JSON shape leaking through.
1534
+ * Request shape — all fields optional. `limit` clamps to
1535
+ * `1..=200` server-side; out-of-range values snap silently to the
1536
+ * bounds. `kinds: []` is treated as "no filter".
1685
1537
  */
1686
- interface KycStatus {
1687
- /** Terminal status if reached, otherwise `pending`. */
1688
- status: KycStatusKind;
1689
- /**
1690
- * Provider being polled. Echoed back so callers that didn't
1691
- * supply one see the contract's default (`"veriff"` in phase one).
1692
- */
1693
- provider: string;
1694
- /**
1695
- * Unix-millis of the latest contract-visible event:
1696
- * VC issuance time, attestation arrival time, session-row
1697
- * `started_at_ms`, or orphan-record arrival time. Best-effort —
1698
- * `undefined` when no source carries a usable timestamp.
1699
- */
1700
- updatedAt?: number;
1701
- /**
1702
- * VC ids appended for this provider, in append order. Empty for
1703
- * `pending` / `rejected` / `orphan`.
1704
- */
1705
- vcIds: string[];
1706
- /**
1707
- * Provider-supplied reason for `rejected`, when available.
1708
- */
1709
- error?: string;
1538
+ interface GetUsageOptions {
1539
+ limit?: number;
1540
+ after_seq?: number;
1541
+ kinds?: TokenTxKind[];
1710
1542
  }
1543
+
1711
1544
  /**
1712
- * Polling cadence for [[T3nClient.kycStatusPoll]]. Defaults:
1713
- * poll fast for the first 30 seconds, then back
1714
- * off, and bail after 5 minutes.
1545
+ * Tenant audit-event wire shapes.
1546
+ *
1547
+ * Mirrors `tenant_types::AuditEvent` and the `audit.get-mine` response
1548
+ * (`service::tenant_logs::{AuditBatch, AuditPage}`). A tenant contract
1549
+ * emits an audit event via the `logging::audit` host call; the host
1550
+ * stamps the identity fields (`subject` / `actor` / `vc_id`) from the
1551
+ * verified dispatch context, so a contract can never forge who acted or
1552
+ * on whom. Events are permanent (encrypted, append-only) and scoped to a
1553
+ * user — distinct from the evicting debug ring read via `getContractLogs`.
1715
1554
  */
1716
- interface KycPollCadence {
1717
- /** Poll interval in ms while `elapsed < switchAtMs`. Default: 2000. */
1718
- fastMs: number;
1719
- /** Poll interval in ms once `elapsed >= switchAtMs`. Default: 5000. */
1720
- slowMs: number;
1721
- /** Elapsed-ms threshold to switch from fast to slow cadence. Default: 30_000. */
1722
- switchAtMs: number;
1723
- /**
1724
- * Maximum total time to spend polling before rejecting with
1725
- * [[KycStatusTimeoutError]]. Default: 300_000 (5 minutes).
1726
- */
1727
- timeoutMs: number;
1555
+ /**
1556
+ * One host-stamped audit event. `subject` / `actor` are canonical DID
1557
+ * strings. On a self-call `actor === subject` and `vc_id` is `null`; on a
1558
+ * delegated call `actor` is the agent and `vc_id` is the delegation
1559
+ * credential. The remaining fields are supplied verbatim by the contract.
1560
+ */
1561
+ interface AuditEvent {
1562
+ /** Cluster-deterministic flush timestamp (ms). */
1563
+ ts_ms: number;
1564
+ /** The user whose data the call touched (host-stamped `pii_did`). */
1565
+ subject: string;
1566
+ /** The agent (delegated) or user (self-call) who acted (host-stamped). */
1567
+ actor: string;
1568
+ /** Delegation credential id on a delegated call; `null` on a self-call. */
1569
+ vc_id?: string | null;
1570
+ /** Contract-supplied: what happened, e.g. `"kyc.approve"`. */
1571
+ action: string;
1572
+ /** Contract-supplied: what it acted on, e.g. `"user-profile"`. */
1573
+ target: string;
1574
+ /** Contract-supplied: the result, e.g. `"success"` / `"denied"`. */
1575
+ outcome: string;
1576
+ /** Contract-supplied: optional free-text / JSON detail. */
1577
+ details?: string | null;
1728
1578
  }
1729
1579
  /**
1730
- * Optional knobs for [[T3nClient.kycStatusPoll]]. Most callers won't
1731
- * touch any of these — the cadence defaults are baked in.
1580
+ * One archived batch — the events committed in a single tenant dispatch.
1581
+ * Every event in a batch shares the same `subject` / `actor` / `vc_id`.
1732
1582
  */
1733
- interface KycPollOptions {
1734
- /**
1735
- * Cancellation signal. When aborted, the helper rejects with
1736
- * `signal.reason` (or a generic `AbortError` if the consumer
1737
- * didn't supply a reason). The currently-in-flight `kycStatus()`
1738
- * call also receives the signal so it can short-circuit.
1739
- */
1740
- signal?: AbortSignal;
1741
- /**
1742
- * Called with every snapshot the helper receives, including
1743
- * non-terminal `pending` ones. Useful for surfacing intermediate
1744
- * UI states (e.g. "still waiting on provider…"). Errors thrown
1745
- * from this callback are caught and ignored — they must not
1746
- * sink the poll loop.
1747
- */
1748
- onUpdate?: (status: KycStatus) => void;
1749
- /**
1750
- * Override one or more cadence fields. Anything not specified
1751
- * uses the defaults from [[DEFAULT_KYC_POLL_CADENCE]].
1752
- */
1753
- cadence?: Partial<KycPollCadence>;
1583
+ interface AuditBatch {
1584
+ /** Hex of the archive key — usable directly as `cursor` for the next page. */
1585
+ key: string;
1754
1586
  /**
1755
- * Provider id to poll. Defaults to the contract default
1756
- * (`"veriff"` in phase one). Mirrored on the wire as
1757
- * `input.provider_id`.
1587
+ * Host-stamped: did the emitting dispatch's contract transaction
1588
+ * commit? `false` means the call rolled back or trapped, so an event's
1589
+ * `outcome: "success"` in this batch is the contract's *claim*, not a
1590
+ * committed fact — filter on this when you need only durable events.
1758
1591
  */
1759
- providerId?: string;
1592
+ committed: boolean;
1593
+ events: AuditEvent[];
1760
1594
  }
1761
1595
  /**
1762
- * Default cadence used by [[T3nClient.kycStatusPoll]] when the caller
1763
- * doesn't override `cadence.*`.
1764
- */
1765
- declare const DEFAULT_KYC_POLL_CADENCE: KycPollCadence;
1766
- /**
1767
- * Subset of [[KycStatusKind]] that ends a poll. `pending` is the
1768
- * only non-terminal value.
1596
+ * `audit.get-mine` response — a page of audit batches, newest scan order.
1597
+ * `next_cursor` is present (a key hex) when more batches remain; `null`
1598
+ * once the scan reaches the end.
1769
1599
  */
1770
- declare const TERMINAL_KYC_STATUSES: ReadonlySet<KycStatusKind>;
1600
+ interface AuditPage {
1601
+ batches: AuditBatch[];
1602
+ next_cursor?: string | null;
1603
+ }
1771
1604
  /**
1772
- * Thrown by [[T3nClient.kycStatusPoll]] when the cadence's
1773
- * `timeoutMs` elapses without a terminal status arriving. The
1774
- * `lastStatus` field is the most recent (necessarily `pending`)
1775
- * snapshot the helper saw, useful for surfacing "we tried for N
1776
- * minutes but Veriff is still working" UX.
1605
+ * Request shape for {@link T3nClient.getAuditEvents} — all fields
1606
+ * optional. Omit `pii_did` to read your own trail (all actors); set it to
1607
+ * another user's DID to read, as a delegated agent, the events you
1608
+ * performed for them — admitted only while that user's agent-auth grant
1609
+ * to you is live. `cursor` is the `next_cursor` from a prior page.
1777
1610
  */
1778
- declare class KycStatusTimeoutError extends T3nError {
1779
- /** The timeout (ms) the helper exhausted before a terminal status arrived. */
1780
- readonly timeoutMs: number;
1781
- /** The last `pending` snapshot before timeout, if any. */
1782
- readonly lastStatus?: KycStatus | undefined;
1783
- constructor(
1784
- /** The timeout (ms) the helper exhausted before a terminal status arrived. */
1785
- timeoutMs: number,
1786
- /** The last `pending` snapshot before timeout, if any. */
1787
- lastStatus?: KycStatus | undefined);
1611
+ interface GetAuditEventsOptions {
1612
+ pii_did?: string;
1613
+ limit?: number;
1614
+ cursor?: string;
1788
1615
  }
1789
1616
 
1790
1617
  /**
@@ -2755,70 +2582,216 @@ declare function createRandomHandler(): GuestToHostHandler;
2755
2582
  declare function createDefaultHandlers(baseUrl: string, trustAnchor: TrustAnchorOrUnsafe): GuestToHostHandlers;
2756
2583
 
2757
2584
  /**
2758
- * Payroll request shapes + shared encoding utilities.
2585
+ * Token fixed-point units (USDT-style 6 decimals).
2759
2586
  *
2760
- * Envelope-free authorisation removed the per-call delegation
2761
- * credential and envelope entirely: authority is now the standing on-chain
2762
- * `agent_auth` ∩ `UserGrant` intersection the host re-reads every call, and
2763
- * every payroll call emits the plain Direct wire variant (`{ request }`).
2764
- * What remains here is the request body shape, its canonical defaults, and
2765
- * the base64url / DID helpers callers still need.
2587
+ * As of `tee:*` metering v2, every token amount on the wire —
2588
+ * {@link BalanceRow}'s `available`/`reserved`, {@link UsageEntry.amount},
2589
+ * and `total_supply` — is denominated in **base units**, where
2590
+ * `1 T3N token = `{@link BASE_UNITS_PER_TOKEN}` base units`. The node
2591
+ * meters the *actual* fractional cost of each operation (pro-rata by
2592
+ * bytes / byte-seconds / fuel) down to `1e-6` token, so amounts are
2593
+ * raw integers in base units, NOT whole tokens.
2766
2594
  *
2767
- * Wire conventions retained from the contract:
2768
- * - `batch_cap_cents` is emitted as a **JSON string** so JS Number
2769
- * precision never enters the wire surface.
2770
- * - binary fields are base64url-no-pad strings.
2771
- * - `org_id` is `did:t3n:<40-hex>` (the `CompactDid` `Display` form).
2595
+ * The wire fields are left as raw base units (no silent rescaling — that
2596
+ * would lose precision for large balances and surprise callers doing
2597
+ * arithmetic). Use {@link formatTokens} to render a human "tokens"
2598
+ * string and {@link toBaseUnits} to convert a token figure back.
2599
+ *
2600
+ * ⚠️ Breaking vs. the pre-decimals SDK: a balance that used to read `5`
2601
+ * (whole tokens) now reads `5_000_000` (base units). Multiply existing
2602
+ * display code through {@link formatTokens}.
2772
2603
  */
2604
+ /** Decimal places in a T3N token (fixed; mirrors the node constant). */
2605
+ declare const TOKEN_DECIMALS = 6;
2606
+ /** Base units per whole T3N token — `10 ** TOKEN_DECIMALS`. */
2607
+ declare const BASE_UNITS_PER_TOKEN = 1000000;
2773
2608
  /**
2774
- * Canonical sorted run-pipeline function surface for the z-payroll contract
2775
- * (`z:<org-tid>:payroll`). Sorted ascending. Mirrors `Z_PAYROLL_RUN_FUNCTIONS`
2776
- * in the Trinity harness; used as the `functions` set for agent-auth grants
2777
- * and `check-delegation` queries.
2609
+ * Render a base-unit amount as a human-readable token string, e.g.
2610
+ * `25_920_000 → "25.92"`, `1_000_000 → "1"`, `4768 → "0.004768"`.
2611
+ *
2612
+ * Trailing-zero fraction digits are trimmed; an integer amount renders
2613
+ * with no decimal point. Accepts `number` or `bigint`; for balances
2614
+ * above `Number.MAX_SAFE_INTEGER` prefer passing a `bigint`.
2778
2615
  */
2779
- declare const Z_PAYROLL_RUN_FUNCTIONS: readonly ["compute-payroll", "execute-disbursement", "finalize-audit", "submit-escalations", "validate-credentials"];
2616
+ declare function formatTokens(baseUnits: number | bigint): string;
2780
2617
  /**
2781
- * Canonical sorted audit-read function surface for the z-payroll contract.
2782
- * These functions authorise history reads via `list-audit-cycles` /
2783
- * `get-audit-entry`.
2618
+ * Convert a whole-or-fractional token figure to base units (rounded to
2619
+ * the nearest base unit). E.g. `25.92 → 25_920_000`, `1 → 1_000_000`.
2620
+ */
2621
+ declare function toBaseUnits(tokens: number): number;
2622
+
2623
+ /**
2624
+ * Org-data wire types mirroring the org-data contract wire shapes.
2625
+ *
2626
+ * Plain TypeScript interfaces (no zod) — the SDK does not use a
2627
+ * validation library for domain types; see the existing `types/` files.
2628
+ *
2629
+ * Each response type below is paired with a shallow runtime predicate
2630
+ * (`isMutationResponse`, `isOrgPolicyMeta`, etc.) so the org-data client
2631
+ * can `assertShape` the decoded payload before returning to callers.
2632
+ * Predicates check the top-level structure only; nested elements
2633
+ * (e.g. each `UserGrant` inside `OrgContractGrants.grants`) are not
2634
+ * deeply validated — see `utils/shape.ts` for the rationale.
2635
+ *
2636
+ * Reference: the org-data wire contract.
2637
+ */
2638
+ /**
2639
+ * Capability grant stored under `ORG_CONTRACT_GRANTS_MAP`.
2640
+ *
2641
+ * Mirrors the org-data `UserGrant` wire shape.
2642
+ */
2643
+ interface UserGrant {
2644
+ /** The user this grant applies to (`did:t3n:<40-hex>`). */
2645
+ user_did: string;
2646
+ /** WIT function names the user may invoke (e.g. `"set-records"`). */
2647
+ functions: string[];
2648
+ /** Data scope paths the user may access (e.g. `"example/records"`). */
2649
+ scopes: string[];
2650
+ /**
2651
+ * Optional key-value constraints that must match the request metadata
2652
+ * exactly for every key present in this map.
2653
+ */
2654
+ constraints: Record<string, string>;
2655
+ /** Unix timestamp (secs) after which this grant is expired. `null` means never expires. */
2656
+ expires_at_secs: number | null;
2657
+ }
2658
+ /**
2659
+ * Policy record for an organisation's data tier.
2660
+ *
2661
+ * Mirrors the org-data policy-meta wire shape.
2662
+ */
2663
+ interface OrgPolicyMeta {
2664
+ /** DIDs (`did:t3n:<40-hex>`) of users authorised to manage policy and read data. */
2665
+ admins: string[];
2666
+ /** Maximum number of admins allowed for this org. */
2667
+ max_admins: number;
2668
+ /** Unix timestamp (secs) when the policy was first created. */
2669
+ created_at_secs: number;
2670
+ /** Unix timestamp (secs) of the most recent policy update. */
2671
+ updated_at_secs: number;
2672
+ /**
2673
+ * Per-org ceiling on delegation-credential validity (seconds).
2674
+ *
2675
+ * Absent when no org-level cap is set — the contract omits the field
2676
+ * (never serialises null) and the cluster-wide
2677
+ * `delegation.max_credential_validity_secs` governs instead. When set,
2678
+ * the effective cap is `min(max_credential_validity_secs, cluster_ceiling)`.
2679
+ */
2680
+ max_credential_validity_secs?: number;
2681
+ }
2682
+ /** Shallow runtime guard for {@link OrgPolicyMeta}. */
2683
+ declare function isOrgPolicyMeta(value: unknown): value is OrgPolicyMeta;
2684
+ /**
2685
+ * Effective org policy view returned by `org-policy-view`.
2686
+ *
2687
+ * Readable by any authenticated session (non-sensitive metadata).
2688
+ * Returns the **effective** credential validity cap:
2689
+ * `min(org.max_credential_validity_secs, cluster_ceiling)`, equal to the
2690
+ * cluster ceiling when the org has not set a per-org cap. The contract
2691
+ * always returns a value (or errors) — the field is never null.
2692
+ */
2693
+ interface OrgPolicyView {
2694
+ /**
2695
+ * Effective delegation-credential validity cap in seconds:
2696
+ * `min(org value, cluster ceiling)`, equal to the cluster ceiling when the
2697
+ * org has not set a per-org cap. Always present — mirrors the contract's
2698
+ * `PolicyViewResponse.effective_validity_secs` (org-data `org-policy-view`).
2699
+ */
2700
+ effective_validity_secs: number;
2701
+ }
2702
+ /** Shallow runtime guard for {@link OrgPolicyView}. */
2703
+ declare function isOrgPolicyView(value: unknown): value is OrgPolicyView;
2704
+ /**
2705
+ * Standard response returned by all policy write and data mutation operations.
2706
+ *
2707
+ * Mirrors the org-data `MutationResponse` wire shape.
2708
+ */
2709
+ interface MutationResponse {
2710
+ /** `"created"`, `"updated"`, or `"deleted"`. */
2711
+ status: string;
2712
+ /** Hex-encoded entry ID; present on data write/delete operations. */
2713
+ entry_id?: string;
2714
+ /** Whether the target key existed before deletion; present on single-entry deletes. */
2715
+ deleted?: boolean;
2716
+ /** Number of entries removed; present on `org-data-delete-scope`. */
2717
+ deleted_entries?: number;
2718
+ tx_hash: string | null;
2719
+ }
2720
+ /**
2721
+ * Shallow runtime guard for {@link MutationResponse}.
2722
+ *
2723
+ * Only the always-present fields are checked — `status` is mandatory on
2724
+ * every mutation; `tx_hash` is non-optional but nullable. The optional
2725
+ * fields (`entry_id`, `deleted`, `deleted_entries`) are not validated
2726
+ * because their presence depends on which mutation ran.
2727
+ */
2728
+ declare function isMutationResponse(value: unknown): value is MutationResponse;
2729
+ /**
2730
+ * Response type alias for org-writers-get.
2731
+ *
2732
+ * The wire body is `{ writers: string[] }` where each entry is
2733
+ * `did:t3n:<40-hex>`.
2734
+ */
2735
+ interface OrgWriters {
2736
+ writers: string[];
2737
+ }
2738
+ /** Shallow runtime guard for {@link OrgWriters}. */
2739
+ declare function isOrgWriters(value: unknown): value is OrgWriters;
2740
+ /**
2741
+ * Response type alias for org-grants-get.
2742
+ *
2743
+ * The wire body echoes the `contract_id` alongside the grant list.
2784
2744
  */
2785
- declare const Z_PAYROLL_AUDIT_READ_FUNCTIONS: readonly ["get-audit-entry", "list-audit-cycles"];
2786
- /** Payroll-specific request body — the Direct wire variant carries `{ request }`. */
2787
- interface PayrollRunRequest {
2788
- /** `did:t3n:<40-hex>` org id. */
2789
- org_id: string;
2790
- cycle_id: string;
2791
- pay_period_start: string;
2792
- pay_period_end: string;
2793
- /** Total cap on the run's net disbursement, in cents. */
2794
- batch_cap_cents: bigint;
2795
- /** `employee_id` → previous-cycle baseline net disbursement, cents (decimal string). */
2796
- historical_baselines: Record<string, string>;
2797
- /**
2798
- * Per-employee disbursement flag threshold, in cents. When absent the
2799
- * contract applies its own default (SGD 15,000); when present it is included
2800
- * in the wire shape.
2801
- */
2802
- individual_disbursement_threshold_cents?: bigint;
2745
+ interface OrgContractGrants {
2746
+ contract_id: string;
2747
+ grants: UserGrant[];
2803
2748
  }
2804
- /** Default for `individual_disbursement_threshold_cents` — SGD 15,000. */
2805
- declare const DEFAULT_INDIVIDUAL_THRESHOLD_CENTS = 1500000n;
2806
- declare function b64uEncode(input: Uint8Array): string;
2807
2749
  /**
2808
- * Encode raw bytes to base64url-no-pad (RFC 4648 §5 with padding dropped).
2809
- * The encoding the wire uses for binary fields.
2750
+ * Shallow runtime guard for {@link OrgContractGrants}.
2751
+ *
2752
+ * Validates the immediate envelope (`contract_id: string`, `grants:
2753
+ * array`) without recursing into each `UserGrant`. The Rust contract
2754
+ * is the source of truth for grant element shape; widening the predicate
2755
+ * here would create maintenance churn against benign field additions.
2810
2756
  */
2811
- declare function b64uEncodeBytes(input: Uint8Array): string;
2757
+ declare function isOrgContractGrants(value: unknown): value is OrgContractGrants;
2758
+ /** Response for `org-data-list`. */
2759
+ interface DataListResponse {
2760
+ /** Hex-encoded entry IDs for this page. */
2761
+ entry_ids: string[];
2762
+ /** Offset to pass for the next page. `null` when this is the last page. */
2763
+ next_offset: number | null;
2764
+ /** Total number of entries in the scope (across all pages). */
2765
+ total: number;
2766
+ }
2767
+ /** Shallow runtime guard for {@link DataListResponse}. */
2768
+ declare function isDataListResponse(value: unknown): value is DataListResponse;
2769
+ /** Response for `org-data-get`. */
2770
+ interface DataGetResponse {
2771
+ entry_id: string;
2772
+ /** Hex-encoded raw payload bytes. */
2773
+ payload_hex: string;
2774
+ }
2775
+ /** Shallow runtime guard for {@link DataGetResponse}. */
2776
+ declare function isDataGetResponse(value: unknown): value is DataGetResponse;
2812
2777
  /**
2813
- * Decode a base64url-no-pad string. Strict — rejects standard-alphabet
2814
- * `+` / `/` and any padding `=`.
2778
+ * Legacy direct-route org-data envelope shape retained for compatibility.
2779
+ *
2780
+ * This mirrors the removed `/api/user-contract/execute` body format from
2781
+ * the transitional transport. New callers should use `OrgDataClient`,
2782
+ * which now dispatches through authenticated `/api/rpc` +
2783
+ * `action.execute` instead.
2815
2784
  */
2816
- declare function b64uDecodeStrict(input: string): Uint8Array;
2817
- /** @internal — preserved alias for in-tree callers. Prefer
2818
- * {@link b64uEncodeBytes} / {@link b64uDecodeStrict}. */
2819
- declare const _b64uEncode: typeof b64uEncode;
2820
- /** Build a `did:t3n:<40-hex>` from raw 20 bytes. */
2821
- declare function compactDidFromBytes(bytes: Uint8Array): string;
2785
+ interface OrgDataActionWire {
2786
+ nonce: string;
2787
+ user_did: string;
2788
+ authenticator_id: string;
2789
+ contract_id: string;
2790
+ function: string;
2791
+ args_hash: string;
2792
+ expires_at_secs: number;
2793
+ signature: string;
2794
+ }
2822
2795
 
2823
2796
  /**
2824
2797
  * OrgDataClient — typed wrapper over the existing authenticated
@@ -3167,43 +3140,110 @@ declare class SessionOrgDataClient {
3167
3140
  declare function createOrgDataClientFromSession(t3n: T3nClient, baseUrl: string): SessionOrgDataClient;
3168
3141
 
3169
3142
  /**
3170
- * Token fixed-point units (USDT-style 6 decimals).
3171
- *
3172
- * As of `tee:*` metering v2, every token amount on the wire —
3173
- * {@link BalanceRow}'s `available`/`reserved`, {@link UsageEntry.amount},
3174
- * and `total_supply` — is denominated in **base units**, where
3175
- * `1 T3N token = `{@link BASE_UNITS_PER_TOKEN}` base units`. The node
3176
- * meters the *actual* fractional cost of each operation (pro-rata by
3177
- * bytes / byte-seconds / fuel) down to `1e-6` token, so amounts are
3178
- * raw integers in base units, NOT whole tokens.
3143
+ * Payroll request shapes + shared encoding utilities.
3179
3144
  *
3180
- * The wire fields are left as raw base units (no silent rescaling — that
3181
- * would lose precision for large balances and surprise callers doing
3182
- * arithmetic). Use {@link formatTokens} to render a human "tokens"
3183
- * string and {@link toBaseUnits} to convert a token figure back.
3145
+ * Envelope-free authorisation removed the per-call delegation
3146
+ * credential and envelope entirely: authority is now the standing on-chain
3147
+ * `agent_auth` ∩ `UserGrant` intersection the host re-reads every call, and
3148
+ * every payroll call emits the plain Direct wire variant (`{ request }`).
3149
+ * What remains here is the request body shape, its canonical defaults, and
3150
+ * the base64url / DID helpers callers still need.
3184
3151
  *
3185
- * ⚠️ Breaking vs. the pre-decimals SDK: a balance that used to read `5`
3186
- * (whole tokens) now reads `5_000_000` (base units). Multiply existing
3187
- * display code through {@link formatTokens}.
3152
+ * Wire conventions retained from the contract:
3153
+ * - `batch_cap_cents` is emitted as a **JSON string** so JS Number
3154
+ * precision never enters the wire surface.
3155
+ * - binary fields are base64url-no-pad strings.
3156
+ * - `org_id` is `did:t3n:<40-hex>` (the `CompactDid` `Display` form).
3188
3157
  */
3189
- /** Decimal places in a T3N token (fixed; mirrors the node constant). */
3190
- declare const TOKEN_DECIMALS = 6;
3191
- /** Base units per whole T3N token — `10 ** TOKEN_DECIMALS`. */
3192
- declare const BASE_UNITS_PER_TOKEN = 1000000;
3193
3158
  /**
3194
- * Render a base-unit amount as a human-readable token string, e.g.
3195
- * `25_920_000 → "25.92"`, `1_000_000 → "1"`, `4768 → "0.004768"`.
3196
- *
3197
- * Trailing-zero fraction digits are trimmed; an integer amount renders
3198
- * with no decimal point. Accepts `number` or `bigint`; for balances
3199
- * above `Number.MAX_SAFE_INTEGER` prefer passing a `bigint`.
3159
+ * Canonical sorted run-pipeline function surface for the z-payroll contract
3160
+ * (`z:<org-tid>:payroll`). Sorted ascending. Mirrors `Z_PAYROLL_RUN_FUNCTIONS`
3161
+ * in the Trinity harness; used as the `functions` set for agent-auth grants
3162
+ * and `check-delegation` queries.
3200
3163
  */
3201
- declare function formatTokens(baseUnits: number | bigint): string;
3164
+ declare const Z_PAYROLL_RUN_FUNCTIONS: readonly ["compute-payroll", "execute-disbursement", "finalize-audit", "submit-escalations", "validate-credentials"];
3202
3165
  /**
3203
- * Convert a whole-or-fractional token figure to base units (rounded to
3204
- * the nearest base unit). E.g. `25.92 → 25_920_000`, `1 → 1_000_000`.
3166
+ * Canonical sorted audit-read function surface for the z-payroll contract.
3167
+ * These functions authorise history reads via `list-audit-cycles` /
3168
+ * `get-audit-entry`.
3205
3169
  */
3206
- declare function toBaseUnits(tokens: number): number;
3170
+ declare const Z_PAYROLL_AUDIT_READ_FUNCTIONS: readonly ["get-audit-entry", "list-audit-cycles"];
3171
+ /** Payroll-specific request body — the Direct wire variant carries `{ request }`. */
3172
+ interface PayrollRunRequest {
3173
+ /** `did:t3n:<40-hex>` org id. */
3174
+ org_id: string;
3175
+ cycle_id: string;
3176
+ pay_period_start: string;
3177
+ pay_period_end: string;
3178
+ /** Total cap on the run's net disbursement, in cents. */
3179
+ batch_cap_cents: bigint;
3180
+ /** `employee_id` → previous-cycle baseline net disbursement, cents (decimal string). */
3181
+ historical_baselines: Record<string, string>;
3182
+ /**
3183
+ * Per-employee disbursement flag threshold, in cents. When absent the
3184
+ * contract applies its own default (SGD 15,000); when present it is included
3185
+ * in the wire shape.
3186
+ */
3187
+ individual_disbursement_threshold_cents?: bigint;
3188
+ }
3189
+ /** Default for `individual_disbursement_threshold_cents` — SGD 15,000. */
3190
+ declare const DEFAULT_INDIVIDUAL_THRESHOLD_CENTS = 1500000n;
3191
+ declare function b64uEncode(input: Uint8Array): string;
3192
+ /**
3193
+ * Encode raw bytes to base64url-no-pad (RFC 4648 §5 with padding dropped).
3194
+ * The encoding the wire uses for binary fields.
3195
+ */
3196
+ declare function b64uEncodeBytes(input: Uint8Array): string;
3197
+ /**
3198
+ * Decode a base64url-no-pad string. Strict — rejects standard-alphabet
3199
+ * `+` / `/` and any padding `=`.
3200
+ */
3201
+ declare function b64uDecodeStrict(input: string): Uint8Array;
3202
+ /** @internal — preserved alias for in-tree callers. Prefer
3203
+ * {@link b64uEncodeBytes} / {@link b64uDecodeStrict}. */
3204
+ declare const _b64uEncode: typeof b64uEncode;
3205
+ /** Build a `did:t3n:<40-hex>` from raw 20 bytes. */
3206
+ declare function compactDidFromBytes(bytes: Uint8Array): string;
3207
+
3208
+ /** Closed-vocabulary serialisation annotations for a contract function's body. */
3209
+ interface WireBlock {
3210
+ /**
3211
+ * Field paths whose values are emitted as decimal strings. A path ending in
3212
+ * `.*` applies to every value of the named object/array (e.g.
3213
+ * `historical_baselines.*`).
3214
+ */
3215
+ u64_as_string?: string[];
3216
+ /**
3217
+ * Byte fields carried as typed strings. The value is passed through when it
3218
+ * is already a string; a byte input (`Uint8Array` or a `number[]` of octets)
3219
+ * is encoded per its kind.
3220
+ */
3221
+ string_encoded?: Record<string, StringEncoding>;
3222
+ }
3223
+ /** The closed set of `string_encoded` field kinds. */
3224
+ type StringEncoding = "did" | "address" | "hash";
3225
+ /**
3226
+ * Raised when a `wire` block asks for a projection the closed vocabulary cannot
3227
+ * express — an unrepresentable value for a declared field. It signals a
3228
+ * descriptor↔contract mismatch on the author's side, so callers surface it as a
3229
+ * terminal `unsupported_wire_encoding` rather than retrying.
3230
+ */
3231
+ declare class WireEncodingError extends T3nError {
3232
+ readonly field: string;
3233
+ readonly encoding?: StringEncoding | undefined;
3234
+ constructor(message: string, field: string, encoding?: StringEncoding | undefined);
3235
+ }
3236
+ type JsonRecord = Record<string, unknown>;
3237
+ /**
3238
+ * Project `args` into the plain-args body a z-space contract deserialises,
3239
+ * applying the descriptor's `wire` annotations. Returns a new object; `args` is
3240
+ * never mutated. With no `wire` block the body is returned unchanged (the
3241
+ * JSON-plain path).
3242
+ *
3243
+ * @throws {WireEncodingError} when a declared field holds a value the
3244
+ * vocabulary cannot express.
3245
+ */
3246
+ declare function requestToWire(args: JsonRecord, wire?: WireBlock): JsonRecord;
3207
3247
 
3208
3248
  /**
3209
3249
  * SessionUsageReportClient — typed wrapper over the authenticated
@@ -3291,46 +3331,6 @@ declare class SessionUsageReportClient {
3291
3331
  */
3292
3332
  declare function createUsageReportClientFromSession(t3n: T3nClient, baseUrl: string): SessionUsageReportClient;
3293
3333
 
3294
- /** Closed-vocabulary serialisation annotations for a contract function's body. */
3295
- interface WireBlock {
3296
- /**
3297
- * Field paths whose values are emitted as decimal strings. A path ending in
3298
- * `.*` applies to every value of the named object/array (e.g.
3299
- * `historical_baselines.*`).
3300
- */
3301
- u64_as_string?: string[];
3302
- /**
3303
- * Byte fields carried as typed strings. The value is passed through when it
3304
- * is already a string; a byte input (`Uint8Array` or a `number[]` of octets)
3305
- * is encoded per its kind.
3306
- */
3307
- string_encoded?: Record<string, StringEncoding>;
3308
- }
3309
- /** The closed set of `string_encoded` field kinds. */
3310
- type StringEncoding = "did" | "address" | "hash";
3311
- /**
3312
- * Raised when a `wire` block asks for a projection the closed vocabulary cannot
3313
- * express — an unrepresentable value for a declared field. It signals a
3314
- * descriptor↔contract mismatch on the author's side, so callers surface it as a
3315
- * terminal `unsupported_wire_encoding` rather than retrying.
3316
- */
3317
- declare class WireEncodingError extends T3nError {
3318
- readonly field: string;
3319
- readonly encoding?: StringEncoding | undefined;
3320
- constructor(message: string, field: string, encoding?: StringEncoding | undefined);
3321
- }
3322
- type JsonRecord = Record<string, unknown>;
3323
- /**
3324
- * Project `args` into the plain-args body a z-space contract deserialises,
3325
- * applying the descriptor's `wire` annotations. Returns a new object; `args` is
3326
- * never mutated. With no `wire` block the body is returned unchanged (the
3327
- * JSON-plain path).
3328
- *
3329
- * @throws {WireEncodingError} when a declared field holds a value the
3330
- * vocabulary cannot express.
3331
- */
3332
- declare function requestToWire(args: JsonRecord, wire?: WireBlock): JsonRecord;
3333
-
3334
3334
  /**
3335
3335
  * Cryptographic utilities for T3n SDK
3336
3336
  *
@@ -3717,6 +3717,18 @@ type TenantMapUpdatePatch = MapUpdateInput;
3717
3717
  type TenantContractPublishInput = ContractPublishInput;
3718
3718
  type TenantContractSetDescriptorInput = ContractSetDescriptorInput;
3719
3719
  type TenantContractExecuteInput = ContractExecuteInput;
3720
+ /**
3721
+ * Coarse lifecycle state of a tenant map, mirroring the contract's
3722
+ * `MapLifecycleStatus` enum on the `tee:tenant/contracts::map-get-status` op.
3723
+ * Exported from the SDK public barrel so FE code has a single source of truth
3724
+ * for the union instead of re-declaring it.
3725
+ *
3726
+ * - `"active"` — a live map (`Private`/`Public`) exists at this name.
3727
+ * - `"deleting"` — deleted, host sweeper still draining. Poll until `"absent"`.
3728
+ * - `"absent"` — no row (never created, or deletion fully settled); name is
3729
+ * free to (re-)create.
3730
+ */
3731
+ type MapLifecycleStatus = "active" | "deleting" | "absent";
3720
3732
  /**
3721
3733
  * Options shared by the `tee:tenant` management calls that accept an optional
3722
3734
  * `tenant` target argument.
@@ -3769,6 +3781,45 @@ declare class TenantMapsNamespace {
3769
3781
  create(input: MapCreateInput, opts?: TenantTargetOptions): Promise<unknown>;
3770
3782
  update(tail: string, patch: MapUpdateInput, opts?: TenantTargetOptions): Promise<unknown>;
3771
3783
  delete(tail: string, opts?: TenantTargetOptions): Promise<unknown>;
3784
+ /**
3785
+ * Write a UTF-8 key/value entry into an owned map via the tenant management
3786
+ * surface (`map-entry-set`). Mirrors the `.delete` shape: canonical name is
3787
+ * always rooted at the target tenant, and `payload.tenant` is included only
3788
+ * when `opts.tenantTarget` is supplied (`exactOptionalPropertyTypes` — never
3789
+ * pass a stringified `null`, the contract's `deny_unknown_fields` accepts an
3790
+ * absent field, not a literal `null`).
3791
+ *
3792
+ * SDK-only addition: the `map-entry-set` contract op
3793
+ * has always accepted the optional `tenant` field and routed through
3794
+ * `resolve_tenant_did` — this helper simply completes the SDK's map-entry
3795
+ * surface so `set` / `get` / `getStatus` are all first-class alongside the
3796
+ * lifecycle trio.
3797
+ */
3798
+ entrySet(tail: string, key: string, value: string, opts?: TenantTargetOptions): Promise<void>;
3799
+ /**
3800
+ * Read back a UTF-8 key/value entry from an owned map via the tenant
3801
+ * management surface (`map-entry-get`). Requires the paired contract-side
3802
+ * 1.24.0-or-later registration for the `tenant` argument to be accepted —
3803
+ * a node on an earlier contract rejects `tenant`-bearing requests with
3804
+ * `deny_unknown_fields`.
3805
+ *
3806
+ * Unwraps the contract's `{ value: string | null }` envelope to
3807
+ * `string | null` for caller ergonomics; `null` is the deliberate
3808
+ * "key absent" signal, not an error.
3809
+ */
3810
+ entryGet(tail: string, key: string, opts?: TenantTargetOptions): Promise<string | null>;
3811
+ /**
3812
+ * Report the lifecycle status of an owned map (`map-get-status`),
3813
+ * so a client can poll a delete → recreate cycle instead of guessing.
3814
+ * Requires the paired contract-side 1.24.0-or-later registration for the
3815
+ * `tenant` argument to be accepted.
3816
+ *
3817
+ * `"absent"` (never created, or deletion fully settled) is a *success*
3818
+ * answer meaning the name is free to (re-)create; it is not an error.
3819
+ * `"deleting"` means the host sweeper is still draining the map — poll
3820
+ * again until `"absent"`.
3821
+ */
3822
+ getStatus(tail: string, opts?: TenantTargetOptions): Promise<MapLifecycleStatus>;
3772
3823
  }
3773
3824
  declare class TenantContractsNamespace {
3774
3825
  private readonly client;
@@ -3892,4 +3943,4 @@ declare function validateTail(tail: string): string;
3892
3943
  declare function canonicalTenantName(tenantDid: string, tail: string): string;
3893
3944
 
3894
3945
  export { AuthMethod, AuthenticationError, BASE_UNITS_PER_TOKEN, ContractResponseError, DEFAULT_ENVIRONMENT, DEFAULT_INDIVIDUAL_THRESHOLD_CENTS, DEFAULT_KYC_POLL_CADENCE, HandshakeError, HttpTransport, KycStatusTimeoutError, LogLevel, MockTransport, NODE_URLS, OrgDataClient, OtpRateLimitedError, RpcError, SessionExpiredError, SessionOrgDataClient, SessionStateError, SessionStatus, SessionUsageReportClient, T3nClient, T3nError, TERMINAL_KYC_STATUSES, TOKEN_DECIMALS, TenantClient, TenantContractsNamespace, TenantMapsNamespace, TenantNamespace, TenantSdkValidationError, TenantTokenNamespace, UnsupportedTenantSdkOperationError, UserUpsertError, WasmError, WireEncodingError, Z_PAYROLL_AUDIT_READ_FUNCTIONS, Z_PAYROLL_RUN_FUNCTIONS, _b64uEncode, assertShape, b64uDecodeStrict, b64uEncodeBytes, bytesToString, canonicalTenantName, clearKeyCache, compactDidFromBytes, createDefaultHandlers, createEmailOtpAuthInput, createEthAuthInput, createLogger, createMlKemPublicKeyHandler, createOidcAuthInput, createOrgDataClientFromSession, createRandomHandler, createUsageReportClientFromSession, createWorkloadAuthInput, decodeWasmErrorMessage, eth_get_address, extractWasmError, fetchDkgAttestation, fetchMlKemPublicKey, formatTokens, generateRandomString, generateUUID, getEnvironment, getEnvironmentName, getGlobalLogLevel, getLogger, getNodeUrl, getScriptVersion, isDataGetResponse, isDataListResponse, isMutationResponse, isObject, isOrgContractGrants, isOrgPolicyMeta, isOrgPolicyView, isOrgUsageReport, isOrgWriters, isUnsafeTrustServer, loadConfig, loadWasmComponent, maskKeyMaterial, mergeAgentAuthEntries, mergeDiscoverDids, metamask_get_address, metamask_sign, parseAgentAuthGet, parseAgentAuthPolicy, parseContractResponse, redactSecrets, redactSecretsFromJson, requestToWire, setEnvironment, setGlobalLogLevel, setNodeUrl, stringToBytes, tenantDidHex, toAgentAuthUpdateWire, toBaseUnits, validateConfig, validateTail, verifyDkgAttestation, verifyTdxQuote };
3895
- export type { AdmitForOrgOptions, AgentAuthEntry, AgentAuthMergeResult, AgentAuthPolicy, AgentAuthScriptGrant, AgentAuthUpdateInput, AuthInput, BalanceRow, ChargeReason, ClientAuth, ClientHandshake, ConfigValidationResult, ContractExecuteInput, ContractKind, ContractPublishInput, ContractRegisterResult, ContractResponseSchema, ContractSetDescriptorInput, CreatePolicyInput, DataGetInput, DataGetResponse, DataListInput, DataListResponse, DelegationGrantKind, DelegationGrantRef, DeleteDataInput, DeleteGrantsInput, DeleteScopeInput, Did, Direction, DkgAttestation, DkgVerifyResult, EmailOtpAuthInput, EmailOtpCredentials, Environment, EthAuthInput, ExecuteBusinessContractOptions, ExecuteOrgDataActionOptions, GetUsageOptions, GrantCheckParams, GrantCheckResult, GrantsGetInput, GuestToHostHandler, GuestToHostHandlers, HandshakeResult, JsonRpcRequest, JsonRpcResponse, KycPollCadence, KycPollOptions, KycStatus, KycStatusKind, ListContractsParams, ListContractsResult, ListContractsScope, ListedContract, Logger, MapCreateInput, MapResponse, MapUpdateInput, MapVisibility, MutationResponse, OidcAuthInput, OidcCredentials, OrgContractGrants, OrgDataActionWire, OrgDataClientOptions, OrgPolicyMeta, OrgPolicyView, OrgUsageReport, OrgUsageReportInput, OrgWriters, OtpChannel, OtpMergeSuggestion, OtpRequestInput, OtpRequestResult, OtpVerifyInput, OtpVerifyResult, PayrollRunRequest, PeerQuoteResult, PolicyGetInput, QuoteVerifyResult, ReaderSet, SdkConfig, SessionCrypto, SessionId, SetGrantsInput, SetWritersInput, StringEncoding, SubmitUserInputArgs, SubmitUserInputResult, T3nClientConfig, TenantAdmitProjection, TenantAdmitStatus, TenantBaseClient, TenantClientConfig, TenantContractExecuteInput, TenantContractPublishInput, TenantContractSetDescriptorInput, TenantExecutionSession, TenantMapCreateInput, TenantMapUpdatePatch, TenantMeResponse, TenantSdkEnvironment, TenantSelfAdmitResult, TenantStatus, TenantTargetOptions, TokenTxKind, Transport, TrustAnchor, TrustAnchorOrUnsafe, UnsafeTrustServer, UpdateMetaInput, UsageEntry, UsagePage, UsageReportSignature, UserGrant, UserInputProfile, UserUpsertErrorKind, WasmComponent, WasmNextResult, WireBlock, WorkloadAuthInput, WriteDataInput, WriterSet, WritersGetInput };
3946
+ export type { AdmitForOrgOptions, AgentAuthEntry, AgentAuthMergeResult, AgentAuthPolicy, AgentAuthScriptGrant, AgentAuthUpdateInput, AuthInput, BalanceRow, ChargeReason, ClientAuth, ClientHandshake, ConfigValidationResult, ContractExecuteInput, ContractKind, ContractPublishInput, ContractRegisterResult, ContractResponseSchema, ContractSetDescriptorInput, CreatePolicyInput, DataGetInput, DataGetResponse, DataListInput, DataListResponse, DelegationGrantKind, DelegationGrantRef, DeleteDataInput, DeleteGrantsInput, DeleteScopeInput, Did, Direction, DkgAttestation, DkgVerifyResult, EmailOtpAuthInput, EmailOtpCredentials, Environment, EthAuthInput, ExecuteBusinessContractOptions, ExecuteOrgDataActionOptions, GetUsageOptions, GrantCheckParams, GrantCheckResult, GrantsGetInput, GuestToHostHandler, GuestToHostHandlers, HandshakeResult, JsonRpcRequest, JsonRpcResponse, KycPollCadence, KycPollOptions, KycStatus, KycStatusKind, ListContractsParams, ListContractsResult, ListContractsScope, ListedContract, Logger, MapCreateInput, MapLifecycleStatus, MapResponse, MapUpdateInput, MapVisibility, MutationResponse, OidcAuthInput, OidcCredentials, OrgContractGrants, OrgDataActionWire, OrgDataClientOptions, OrgPolicyMeta, OrgPolicyView, OrgUsageReport, OrgUsageReportInput, OrgWriters, OtpChannel, OtpMergeSuggestion, OtpRequestInput, OtpRequestResult, OtpVerifyInput, OtpVerifyResult, PayrollRunRequest, PeerQuoteResult, PolicyGetInput, QuoteVerifyResult, ReaderSet, SdkConfig, SessionCrypto, SessionId, SetGrantsInput, SetWritersInput, StringEncoding, SubmitUserInputArgs, SubmitUserInputResult, T3nClientConfig, TenantAdmitProjection, TenantAdmitStatus, TenantBaseClient, TenantClientConfig, TenantContractExecuteInput, TenantContractPublishInput, TenantContractSetDescriptorInput, TenantExecutionSession, TenantMapCreateInput, TenantMapUpdatePatch, TenantMeResponse, TenantSdkEnvironment, TenantSelfAdmitResult, TenantStatus, TenantTargetOptions, TokenTxKind, Transport, TrustAnchor, TrustAnchorOrUnsafe, UnsafeTrustServer, UpdateMetaInput, UsageEntry, UsagePage, UsageReportSignature, UserGrant, UserInputProfile, UserUpsertErrorKind, WasmComponent, WasmNextResult, WireBlock, WorkloadAuthInput, WriteDataInput, WriterSet, WritersGetInput };