@code-yeongyu/senpi 2026.11.6-2 → 2026.11.6-3

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.
Files changed (165) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/bundle/chunks/{bun-extension-importer-RCGPQTI2.js → bun-extension-importer-GQ6IKEXD.js} +2 -2
  3. package/dist/bundle/chunks/{chunk-QN2BHP3M.js → chunk-44BJPS5I.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-C7OKUC7L.js → chunk-4BATXPB7.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-RQ6CHXQ2.js → chunk-5LKB732P.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-X5ZVYNOL.js → chunk-6CEDL5S2.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-HLM46XEB.js → chunk-7DRWPZ3N.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-7PSBD5KS.js → chunk-BASS47UD.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-ECW7VBMB.js → chunk-CFEHJ4VU.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-TIPKAMSS.js → chunk-EO2VPZ2K.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-LNPAT22L.js → chunk-F5JLDCYD.js} +8 -7
  12. package/dist/bundle/chunks/{chunk-GSKCWXXG.js → chunk-JMQOLOC2.js} +1 -1
  13. package/dist/bundle/chunks/chunk-L7ZIS3MC.js +106 -0
  14. package/dist/bundle/chunks/{chunk-47PPZKPO.js → chunk-LM6DOWQH.js} +4 -4
  15. package/dist/bundle/chunks/chunk-LPYBKMPT.js +2 -0
  16. package/dist/bundle/chunks/{chunk-GNSMNSL7.js → chunk-LUZVGPQ7.js} +2 -2
  17. package/dist/bundle/chunks/{chunk-7III44PV.js → chunk-MJTCZUXS.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-2FDW2LQL.js → chunk-SB6MXNO5.js} +1 -1
  19. package/dist/bundle/chunks/chunk-UODSYQ2D.js +2 -0
  20. package/dist/bundle/chunks/{chunk-D5MRJWUK.js → chunk-V2QAYGOK.js} +216 -224
  21. package/dist/bundle/chunks/{chunk-WGRKM3N6.js → chunk-X53PD4ZC.js} +1 -1
  22. package/dist/bundle/chunks/{cli-main-T74YJGIF.js → cli-main-RWHDZKTQ.js} +1 -1
  23. package/dist/bundle/chunks/{dispatch-BAK2YZKD.js → dispatch-VDIYSXQN.js} +1 -1
  24. package/dist/bundle/chunks/{hand-entry-AYRUGE2A.js → hand-entry-G5LYF62Q.js} +1 -1
  25. package/dist/bundle/chunks/{host-command-YRXZE4TV.js → host-command-I2JZ5JM7.js} +1 -1
  26. package/dist/bundle/chunks/{host-session-command-N7IN7ZDV.js → host-session-command-Y52NFPP2.js} +1 -1
  27. package/dist/bundle/chunks/interactive-mode-PA7DGVS6.js +2 -0
  28. package/dist/bundle/chunks/main-OBL6375C.js +2 -0
  29. package/dist/bundle/chunks/{multi-session-host-XSUJDHFZ.js → multi-session-host-NM6I5BWZ.js} +20 -17
  30. package/dist/bundle/chunks/{package-manager-cli-GLUNPQJU.js → package-manager-cli-GNRQ5MVR.js} +1 -1
  31. package/dist/bundle/chunks/{role-RRFFHZF5.js → role-F56BS2ZQ.js} +1 -1
  32. package/dist/bundle/chunks/rpc-mode-G4KIP7XS.js +2 -0
  33. package/dist/bundle/chunks/{run-attached-tui-W236SEOR.js → run-attached-tui-XTGPAGYM.js} +1 -1
  34. package/dist/bundle/chunks/{runtime-session-OBXYK4PI.js → runtime-session-VO3X4OE3.js} +1 -1
  35. package/dist/bundle/chunks/{schedule-command-B2EJTBOE.js → schedule-command-L4M3TSQ3.js} +1 -1
  36. package/dist/bundle/chunks/{session-control-endpoint-YIMLHGLG.js → session-control-endpoint-V46WMCDF.js} +1 -1
  37. package/dist/bundle/chunks/{session-picker-E6ZONUJB.js → session-picker-AG6KSR5A.js} +1 -1
  38. package/dist/bundle/chunks/session-worker.js +87 -83
  39. package/dist/bundle/cli.js +2 -2
  40. package/dist/bundle/index.js +1 -1
  41. package/dist/bundle/rpc-entry.js +1 -1
  42. package/dist/bundle/runtime-manifest.json +1 -1
  43. package/dist/core/agent-session-runtime.d.ts +2 -0
  44. package/dist/core/agent-session-runtime.js +9 -2
  45. package/dist/core/agent-session.d.ts +24 -3
  46. package/dist/core/agent-session.js +94 -28
  47. package/dist/core/credential-pool/rotation-slots.d.ts +3 -3
  48. package/dist/core/credential-pool/state-store.d.ts +1 -1
  49. package/dist/core/egress/hpke.d.ts +63 -0
  50. package/dist/core/egress/hpke.js +188 -0
  51. package/dist/core/egress/placement-claim.d.ts +69 -0
  52. package/dist/core/egress/placement-claim.js +115 -0
  53. package/dist/core/egress/pool-state.d.ts +18 -0
  54. package/dist/core/egress/pool-state.js +5 -0
  55. package/dist/core/egress/sealed-format.d.ts +101 -0
  56. package/dist/core/egress/sealed-format.js +174 -0
  57. package/dist/core/egress/wire.d.ts +226 -0
  58. package/dist/core/egress/wire.js +47 -0
  59. package/dist/core/extensions/builtin/ask-user/escape-residue.d.ts +50 -0
  60. package/dist/core/extensions/builtin/ask-user/escape-residue.js +113 -0
  61. package/dist/core/extensions/builtin/ask-user/extension.js +7 -1
  62. package/dist/core/extensions/builtin/ask-user/resume.js +2 -1
  63. package/dist/core/extensions/builtin/ask-user/tool.d.ts +3 -0
  64. package/dist/core/extensions/builtin/ask-user/tool.js +8 -0
  65. package/dist/core/extensions/builtin/goal/index.js +46 -0
  66. package/dist/core/extensions/builtin/goal/stop-pause.d.ts +1 -0
  67. package/dist/core/extensions/builtin/goal/stop-pause.js +4 -2
  68. package/dist/core/extensions/builtin/permission-system/index.js +10 -4
  69. package/dist/core/extensions/builtin/permission-system/preset-registry.d.ts +2 -0
  70. package/dist/core/extensions/builtin/permission-system/preset-registry.js +11 -0
  71. package/dist/core/extensions/builtin/permission-system/settings.d.ts +2 -0
  72. package/dist/core/extensions/builtin/permission-system/settings.js +10 -1
  73. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts +1 -1
  74. package/dist/core/extensions/builtin/terminal/tools/bash.js +38 -9
  75. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +3 -0
  76. package/dist/core/extensions/bun-extension-importer.js +4 -3
  77. package/dist/core/extensions/bun-extension-load-turns.d.ts +4 -0
  78. package/dist/core/extensions/bun-extension-load-turns.js +45 -0
  79. package/dist/core/extensions/bun-extension-registry.d.ts +1 -0
  80. package/dist/core/extensions/bun-extension-registry.js +19 -1
  81. package/dist/core/extensions/loader.js +5 -0
  82. package/dist/core/extensions/session-dialog-api.d.ts +5 -0
  83. package/dist/core/extensions/session-dialog-api.js +13 -0
  84. package/dist/core/extensions/types.d.ts +14 -1
  85. package/dist/core/permission-decision.d.ts +22 -0
  86. package/dist/core/permission-decision.js +53 -0
  87. package/dist/core/permission-mode.d.ts +56 -0
  88. package/dist/core/permission-mode.js +133 -0
  89. package/dist/core/retry-fallback/controller-types.d.ts +6 -0
  90. package/dist/core/retry-fallback/controller.d.ts +8 -0
  91. package/dist/core/retry-fallback/controller.js +31 -3
  92. package/dist/core/retry-fallback/unusable.d.ts +23 -1
  93. package/dist/core/retry-fallback/unusable.js +80 -1
  94. package/dist/core/retry-fallback/usage-limit.d.ts +1 -1
  95. package/dist/core/retry-fallback/usage-limit.js +6 -3
  96. package/dist/core/sdk.js +2 -0
  97. package/dist/core/session-log.js +1 -1
  98. package/dist/core/stall-warning.d.ts +41 -0
  99. package/dist/core/stall-warning.js +72 -0
  100. package/dist/core/turn-origin.d.ts +27 -0
  101. package/dist/core/turn-origin.js +54 -0
  102. package/dist/modes/rpc/client-input-handler.d.ts +2 -0
  103. package/dist/modes/rpc/client-input-handler.js +2 -0
  104. package/dist/modes/rpc/connection-handler.js +16 -5
  105. package/dist/modes/rpc/connection-roles.d.ts +6 -0
  106. package/dist/modes/rpc/connection-roles.js +13 -0
  107. package/dist/modes/rpc/delegated-drive-origin.d.ts +9 -0
  108. package/dist/modes/rpc/delegated-drive-origin.js +16 -0
  109. package/dist/modes/rpc/effect-commands.d.ts +6 -0
  110. package/dist/modes/rpc/host-bare-endpoint-registration.d.ts +7 -0
  111. package/dist/modes/rpc/host-bare-endpoint-registration.js +35 -0
  112. package/dist/modes/rpc/host-capabilities.d.ts +2 -0
  113. package/dist/modes/rpc/host-capabilities.js +3 -0
  114. package/dist/modes/rpc/host-handover-parked.d.ts +11 -0
  115. package/dist/modes/rpc/host-handover-parked.js +17 -0
  116. package/dist/modes/rpc/host-handover-reopen.js +18 -6
  117. package/dist/modes/rpc/host-handover-resident-writer.d.ts +16 -0
  118. package/dist/modes/rpc/host-handover-resident-writer.js +57 -0
  119. package/dist/modes/rpc/host-warm.d.ts +16 -1
  120. package/dist/modes/rpc/host-warm.js +33 -6
  121. package/dist/modes/rpc/method-classes-schema-session.d.ts +9 -0
  122. package/dist/modes/rpc/method-classes-schema-session.js +1 -0
  123. package/dist/modes/rpc/method-classes-schema.d.ts +9 -0
  124. package/dist/modes/rpc/method-classes.d.ts +1 -0
  125. package/dist/modes/rpc/method-classes.js +2 -0
  126. package/dist/modes/rpc/multi-session-host.js +6 -1
  127. package/dist/modes/rpc/permission-preset-command.d.ts +6 -0
  128. package/dist/modes/rpc/permission-preset-command.js +27 -0
  129. package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
  130. package/dist/modes/rpc/rpc-session-command-types.d.ts +4 -0
  131. package/dist/modes/rpc/rpc-session-event-types.d.ts +5 -2
  132. package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -1
  133. package/dist/modes/rpc/rpc-session-state.js +2 -0
  134. package/dist/modes/rpc/session-attachment-commands.js +3 -3
  135. package/dist/modes/rpc/session-event-writer-connections.js +1 -0
  136. package/dist/modes/rpc/session-event-writer.d.ts +1 -1
  137. package/dist/modes/rpc/session-registry-attach.d.ts +0 -9
  138. package/dist/modes/rpc/session-registry-attach.js +16 -3
  139. package/dist/modes/rpc/session-registry-types.d.ts +2 -0
  140. package/dist/modes/rpc/session-registry.d.ts +2 -0
  141. package/dist/modes/rpc/session-registry.js +16 -3
  142. package/dist/modes/rpc/session-router-close.js +43 -6
  143. package/dist/modes/rpc/session-router-context.js +2 -0
  144. package/dist/modes/rpc/session-router-occupancy.js +2 -11
  145. package/dist/modes/rpc/session-router-open-profile.js +4 -0
  146. package/dist/modes/rpc/session-router-open.d.ts +1 -1
  147. package/dist/modes/rpc/session-router-open.js +6 -17
  148. package/dist/modes/rpc/session-router-residency.d.ts +24 -0
  149. package/dist/modes/rpc/session-router-residency.js +90 -0
  150. package/dist/modes/rpc/session-router-routing.js +7 -1
  151. package/dist/modes/rpc/session-status.js +4 -1
  152. package/dist/modes/rpc/session-worker.js +4 -0
  153. package/dist/modes/rpc/worker-session-registry.js +5 -2
  154. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  155. package/docs/engine-socket/fixtures/rpc-inbound-samples.json +12 -2
  156. package/docs/extensions.md +12 -0
  157. package/docs/rpc.md +72 -12
  158. package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
  159. package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
  160. package/package.json +6 -6
  161. package/dist/bundle/chunks/chunk-C7HEEBFH.js +0 -2
  162. package/dist/bundle/chunks/chunk-XCMII2VQ.js +0 -98
  163. package/dist/bundle/chunks/interactive-mode-B5SQDLH7.js +0 -2
  164. package/dist/bundle/chunks/main-UQFVFCNY.js +0 -2
  165. package/dist/bundle/chunks/rpc-mode-JYN3E7P7.js +0 -2
@@ -1,11 +1,11 @@
1
1
  import type { Credential } from "@earendil-works/pi-ai";
2
2
  import { type ConfigValue } from "@earendil-works/pi-ai/utils/config-reference";
3
+ import type { PoolState } from "../egress/pool-state.ts";
3
4
  import type { RunSlot } from "./failover.ts";
4
5
  import type { AccountStrategy } from "./selection-order.ts";
5
- import { type CredentialSlotRepository } from "./state-store.ts";
6
6
  export type RotationLane = "stored" | "env";
7
7
  /** Hands a half-open probe lease back, if the slot still holds that exact lease. */
8
- export declare function releaseProbeLease(repository: CredentialSlotRepository, providerId: string, lane: RotationLane, slotName: string, leaseId: string): Promise<void>;
8
+ export declare function releaseProbeLease(repository: PoolState, providerId: string, lane: RotationLane, slotName: string, leaseId: string): Promise<void>;
9
9
  export type RotationSlot = RunSlot & {
10
10
  lane: RotationLane;
11
11
  /** Env-lane key material for the attempt; never serialized or persisted. */
@@ -25,7 +25,7 @@ export type RotationSources = {
25
25
  providerId: string;
26
26
  credential: Credential | undefined;
27
27
  env: (name: string) => string | undefined;
28
- repository: CredentialSlotRepository;
28
+ repository: PoolState;
29
29
  policy?: {
30
30
  affinity?: boolean;
31
31
  accountStrategy?: AccountStrategy;
@@ -149,7 +149,7 @@ export type HalfOpenLease = {
149
149
  * wins the probe lease; everyone else keeps treating the slot as unavailable
150
150
  * until the lease expires or the probe settles the block.
151
151
  */
152
- export declare function acquireHalfOpenLease(repository: CredentialSlotRepository, providerId: string, laneId: string, slotId: string, options?: {
152
+ export declare function acquireHalfOpenLease(repository: Pick<CredentialSlotRepository, "mutateSlotState">, providerId: string, laneId: string, slotId: string, options?: {
153
153
  now?: number;
154
154
  leaseTtlMs?: number;
155
155
  }): Promise<HalfOpenLease | undefined>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * HPKE base mode (RFC 9180): DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-256-GCM, on Web Crypto only so the same code
3
+ * runs on Bun, Node, compiled binaries, the desktop renderer and Workers. Every wrap is single-shot (sequence 0).
4
+ *
5
+ * The recipient side takes a Diffie-Hellman function instead of a private key, so a recipient whose key never leaves
6
+ * hardware (a KMS key that only performs ECDH) opens wraps through the same code as a device key.
7
+ *
8
+ * Base mode authenticates nothing about the sender: anyone holding a recipient public key can produce a wrap that opens.
9
+ * A successful open proves only that the wrap was made for this key, so origin is established above this layer
10
+ * (signed wraps documents, per-dekId owners), never by an open.
11
+ */
12
+ export type SealedFormatErrorCode = "open_failed" | "unsupported_version" | "invalid_public_key" | "invalid_private_key" | "invalid_key" | "invalid_aad" | "empty_secret";
13
+ /** Errors carry a code only: no key material, ciphertext or plaintext is ever echoed. */
14
+ export declare class SealedFormatError extends Error {
15
+ readonly code: SealedFormatErrorCode;
16
+ constructor(code: SealedFormatErrorCode);
17
+ }
18
+ /** A P-256 HPKE recipient. `dh` returns the 32-byte x-coordinate of the ECDH shared point with `peer`. */
19
+ export interface HpkeRecipient {
20
+ /** Uncompressed SEC1 encoding (65 bytes, leading 0x04). */
21
+ readonly publicKey: Uint8Array;
22
+ dh(peer: Uint8Array): Promise<Uint8Array>;
23
+ }
24
+ export interface HpkeSealInput {
25
+ readonly info: Uint8Array;
26
+ readonly aad: Uint8Array;
27
+ readonly pt: Uint8Array;
28
+ }
29
+ export interface HpkeOpenInput {
30
+ readonly enc: Uint8Array;
31
+ readonly ct: Uint8Array;
32
+ readonly info: Uint8Array;
33
+ readonly aad: Uint8Array;
34
+ }
35
+ export interface HpkeSealed {
36
+ readonly enc: Uint8Array;
37
+ readonly ct: Uint8Array;
38
+ }
39
+ type Bytes = Uint8Array<ArrayBuffer>;
40
+ /** A recipient whose private scalar is held in this process (device keys, tests). */
41
+ export declare function p256Recipient(privateKey: Uint8Array, publicKey: Uint8Array): Promise<HpkeRecipient>;
42
+ /** RFC 9180 Decap: the shared secret for `enc` addressed to `recipient`. */
43
+ export declare function hpkeDecap(recipient: HpkeRecipient, enc: Uint8Array): Promise<Bytes>;
44
+ /**
45
+ * RFC 9180 Encap with a caller-supplied ephemeral key pair. KNOWN-ANSWER TESTS ONLY: reusing an ephemeral key
46
+ * across wraps breaks HPKE's guarantees. Production sealing goes through {@link hpkeSealBase}, which mints one.
47
+ * @internal
48
+ */
49
+ export declare function hpkeEncapDeterministic(recipientPublicKey: Uint8Array, ephemeralPrivateKey: Uint8Array, ephemeralPublicKey: Uint8Array): Promise<{
50
+ readonly enc: Uint8Array;
51
+ readonly sharedSecret: Uint8Array;
52
+ }>;
53
+ /** RFC 9180 KeySchedule for mode_base (no PSK). The exporter secret is not derived: the format never exports. */
54
+ export declare function hpkeKeySchedule(sharedSecret: Uint8Array, info: Uint8Array): Promise<{
55
+ readonly key: Bytes;
56
+ readonly baseNonce: Bytes;
57
+ }>;
58
+ /** Single-shot HPKE seal (sequence 0) to `recipientPublicKey` under a freshly generated ephemeral key. */
59
+ export declare function hpkeSealBase(recipientPublicKey: Uint8Array, input: HpkeSealInput): Promise<HpkeSealed>;
60
+ /** Single-shot HPKE open (sequence 0). Any failure is the one code `open_failed`. */
61
+ export declare function hpkeOpenBase(recipient: HpkeRecipient, input: HpkeOpenInput): Promise<Bytes>;
62
+ export {};
63
+ //# sourceMappingURL=hpke.d.ts.map
@@ -0,0 +1,188 @@
1
+ /**
2
+ * HPKE base mode (RFC 9180): DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-256-GCM, on Web Crypto only so the same code
3
+ * runs on Bun, Node, compiled binaries, the desktop renderer and Workers. Every wrap is single-shot (sequence 0).
4
+ *
5
+ * The recipient side takes a Diffie-Hellman function instead of a private key, so a recipient whose key never leaves
6
+ * hardware (a KMS key that only performs ECDH) opens wraps through the same code as a device key.
7
+ *
8
+ * Base mode authenticates nothing about the sender: anyone holding a recipient public key can produce a wrap that opens.
9
+ * A successful open proves only that the wrap was made for this key, so origin is established above this layer
10
+ * (signed wraps documents, per-dekId owners), never by an open.
11
+ */
12
+ const HPKE_V1 = "HPKE-v1";
13
+ const KEM_ID = 0x0010; // DHKEM(P-256, HKDF-SHA256)
14
+ const KDF_ID = 0x0001; // HKDF-SHA256
15
+ const AEAD_ID = 0x0002; // AES-256-GCM
16
+ const MODE_BASE = 0x00;
17
+ const N_SECRET = 32;
18
+ const N_DH = 32;
19
+ const N_K = 32;
20
+ const N_N = 12;
21
+ const N_T = 16;
22
+ const N_PK = 65;
23
+ const HASH_LEN = 32;
24
+ /** Errors carry a code only: no key material, ciphertext or plaintext is ever echoed. */
25
+ export class SealedFormatError extends Error {
26
+ constructor(code) {
27
+ super(`sealed format: ${code}`);
28
+ this.name = "SealedFormatError";
29
+ this.code = code;
30
+ }
31
+ }
32
+ const encoder = new TextEncoder();
33
+ const own = (view) => Uint8Array.from(view);
34
+ function i2osp(value, length) {
35
+ const out = new Uint8Array(length);
36
+ for (let index = length - 1, rest = value; index >= 0; index--, rest = Math.floor(rest / 256))
37
+ out[index] = rest & 0xff;
38
+ return out;
39
+ }
40
+ function concat(...parts) {
41
+ const out = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
42
+ let offset = 0;
43
+ for (const part of parts) {
44
+ out.set(part, offset);
45
+ offset += part.length;
46
+ }
47
+ return out;
48
+ }
49
+ const KEM_SUITE_ID = concat(encoder.encode("KEM"), i2osp(KEM_ID, 2));
50
+ const HPKE_SUITE_ID = concat(encoder.encode("HPKE"), i2osp(KEM_ID, 2), i2osp(KDF_ID, 2), i2osp(AEAD_ID, 2));
51
+ async function hmacSha256(key, data) {
52
+ const hmacKey = await crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
53
+ return new Uint8Array(await crypto.subtle.sign("HMAC", hmacKey, data));
54
+ }
55
+ // RFC 5869: an absent salt is HashLen zero bytes.
56
+ async function hkdfExtract(salt, ikm) {
57
+ return hmacSha256(salt.length === 0 ? new Uint8Array(HASH_LEN) : own(salt), ikm);
58
+ }
59
+ async function hkdfExpand(prk, info, length) {
60
+ const out = new Uint8Array(length);
61
+ let previous = new Uint8Array();
62
+ for (let block = 1, offset = 0; offset < length; block++) {
63
+ previous = await hmacSha256(prk, concat(previous, info, Uint8Array.of(block)));
64
+ out.set(previous.subarray(0, Math.min(previous.length, length - offset)), offset);
65
+ offset += previous.length;
66
+ }
67
+ return out;
68
+ }
69
+ function labeledExtract(suiteId, salt, label, ikm) {
70
+ return hkdfExtract(salt, concat(encoder.encode(HPKE_V1), suiteId, encoder.encode(label), ikm));
71
+ }
72
+ function labeledExpand(suiteId, prk, label, info, length) {
73
+ return hkdfExpand(prk, concat(i2osp(length, 2), encoder.encode(HPKE_V1), suiteId, encoder.encode(label), info), length);
74
+ }
75
+ async function extractAndExpand(dh, kemContext) {
76
+ const eaePrk = await labeledExtract(KEM_SUITE_ID, new Uint8Array(), "eae_prk", dh);
77
+ return labeledExpand(KEM_SUITE_ID, eaePrk, "shared_secret", kemContext, N_SECRET);
78
+ }
79
+ async function importPublicKey(raw) {
80
+ if (raw.length !== N_PK || raw[0] !== 0x04)
81
+ throw new SealedFormatError("invalid_public_key");
82
+ try {
83
+ return await crypto.subtle.importKey("raw", own(raw), { name: "ECDH", namedCurve: "P-256" }, true, []);
84
+ }
85
+ catch {
86
+ throw new SealedFormatError("invalid_public_key");
87
+ }
88
+ }
89
+ function base64Url(bytes) {
90
+ let binary = "";
91
+ for (const byte of bytes)
92
+ binary += String.fromCharCode(byte);
93
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
94
+ }
95
+ async function importPrivateKey(privateKey, publicKey) {
96
+ if (privateKey.length !== 32 || publicKey.length !== N_PK || publicKey[0] !== 0x04) {
97
+ throw new SealedFormatError("invalid_private_key");
98
+ }
99
+ try {
100
+ return await crypto.subtle.importKey("jwk", {
101
+ kty: "EC",
102
+ crv: "P-256",
103
+ d: base64Url(privateKey),
104
+ x: base64Url(publicKey.subarray(1, 33)),
105
+ y: base64Url(publicKey.subarray(33, 65)),
106
+ }, { name: "ECDH", namedCurve: "P-256" }, false, ["deriveBits"]);
107
+ }
108
+ catch {
109
+ throw new SealedFormatError("invalid_private_key");
110
+ }
111
+ }
112
+ async function ecdh(privateKey, peer) {
113
+ const peerKey = await importPublicKey(peer);
114
+ return new Uint8Array(await crypto.subtle.deriveBits({ name: "ECDH", public: peerKey }, privateKey, N_DH * 8));
115
+ }
116
+ /** A recipient whose private scalar is held in this process (device keys, tests). */
117
+ export async function p256Recipient(privateKey, publicKey) {
118
+ const key = await importPrivateKey(privateKey, publicKey);
119
+ return { publicKey: Uint8Array.from(publicKey), dh: (peer) => ecdh(key, peer) };
120
+ }
121
+ /** RFC 9180 Decap: the shared secret for `enc` addressed to `recipient`. */
122
+ export async function hpkeDecap(recipient, enc) {
123
+ await importPublicKey(enc);
124
+ const dh = await recipient.dh(enc);
125
+ return extractAndExpand(dh, concat(enc, recipient.publicKey));
126
+ }
127
+ /**
128
+ * RFC 9180 Encap with a caller-supplied ephemeral key pair. KNOWN-ANSWER TESTS ONLY: reusing an ephemeral key
129
+ * across wraps breaks HPKE's guarantees. Production sealing goes through {@link hpkeSealBase}, which mints one.
130
+ * @internal
131
+ */
132
+ export async function hpkeEncapDeterministic(recipientPublicKey, ephemeralPrivateKey, ephemeralPublicKey) {
133
+ const ephemeral = await importPrivateKey(ephemeralPrivateKey, ephemeralPublicKey);
134
+ return encapWith(recipientPublicKey, ephemeral, Uint8Array.from(ephemeralPublicKey));
135
+ }
136
+ async function encapWith(recipientPublicKey, ephemeral, enc) {
137
+ const dh = await ecdh(ephemeral, recipientPublicKey);
138
+ return { enc, sharedSecret: await extractAndExpand(dh, concat(enc, recipientPublicKey)) };
139
+ }
140
+ /** RFC 9180 KeySchedule for mode_base (no PSK). The exporter secret is not derived: the format never exports. */
141
+ export async function hpkeKeySchedule(sharedSecret, info) {
142
+ const empty = new Uint8Array();
143
+ const pskIdHash = await labeledExtract(HPKE_SUITE_ID, empty, "psk_id_hash", empty);
144
+ const infoHash = await labeledExtract(HPKE_SUITE_ID, empty, "info_hash", info);
145
+ const context = concat(Uint8Array.of(MODE_BASE), pskIdHash, infoHash);
146
+ const secret = await labeledExtract(HPKE_SUITE_ID, sharedSecret, "secret", empty);
147
+ return {
148
+ key: await labeledExpand(HPKE_SUITE_ID, secret, "key", context, N_K),
149
+ baseNonce: await labeledExpand(HPKE_SUITE_ID, secret, "base_nonce", context, N_N),
150
+ };
151
+ }
152
+ async function aesGcmKey(key, usage) {
153
+ return crypto.subtle.importKey("raw", key, { name: "AES-GCM", length: N_K * 8 }, false, [usage]);
154
+ }
155
+ /** Single-shot HPKE seal (sequence 0) to `recipientPublicKey` under a freshly generated ephemeral key. */
156
+ export async function hpkeSealBase(recipientPublicKey, input) {
157
+ await importPublicKey(recipientPublicKey);
158
+ const ephemeral = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, false, ["deriveBits"]);
159
+ const enc = new Uint8Array(await crypto.subtle.exportKey("raw", ephemeral.publicKey));
160
+ const { sharedSecret } = await encapWith(recipientPublicKey, ephemeral.privateKey, enc);
161
+ const { key, baseNonce } = await hpkeKeySchedule(sharedSecret, input.info);
162
+ const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv: baseNonce, additionalData: own(input.aad), tagLength: N_T * 8 }, await aesGcmKey(key, "encrypt"), own(input.pt));
163
+ sharedSecret.fill(0);
164
+ key.fill(0);
165
+ return { enc, ct: new Uint8Array(ct) };
166
+ }
167
+ /** Single-shot HPKE open (sequence 0). Any failure is the one code `open_failed`. */
168
+ export async function hpkeOpenBase(recipient, input) {
169
+ if (input.ct.length < N_T)
170
+ throw new SealedFormatError("open_failed");
171
+ let sharedSecret;
172
+ let key;
173
+ try {
174
+ sharedSecret = await hpkeDecap(recipient, input.enc);
175
+ const schedule = await hpkeKeySchedule(sharedSecret, input.info);
176
+ key = schedule.key;
177
+ const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: schedule.baseNonce, additionalData: own(input.aad), tagLength: N_T * 8 }, await aesGcmKey(key, "decrypt"), own(input.ct));
178
+ return new Uint8Array(pt);
179
+ }
180
+ catch {
181
+ throw new SealedFormatError("open_failed");
182
+ }
183
+ finally {
184
+ sharedSecret?.fill(0);
185
+ key?.fill(0);
186
+ }
187
+ }
188
+ //# sourceMappingURL=hpke.js.map
@@ -0,0 +1,69 @@
1
+ import { type RosterView, type ServiceSig } from "./roster-entries.ts";
2
+ import { type LocalKeyVerification } from "./verified-keys.ts";
3
+ import type { VerifierRevocation } from "./verifier-revocations.ts";
4
+ export interface PlacementClaim {
5
+ readonly accountId: string;
6
+ readonly placementId: string;
7
+ readonly epoch: number;
8
+ /** Generation of the placement-registry key that signed this claim (4.6). */
9
+ readonly registryGeneration: number;
10
+ readonly issuedAt: number;
11
+ readonly expiresAt: number;
12
+ }
13
+ export interface SignedPlacementClaim {
14
+ readonly claim: PlacementClaim;
15
+ readonly sig: ServiceSig;
16
+ }
17
+ /** A claim may live at most 24 h; the brain renews it at half-life under the same epoch (4.6). */
18
+ export declare const CLAIM_MAX_LIFETIME_MS = 86400000;
19
+ /** Device-side tolerance for a claim's `issuedAt` in the hand's future (15.15 M-1); `MOVE_SKEW_MS` stays server-side. */
20
+ export declare const CLAIM_ISSUE_SKEW_MS = 300000;
21
+ export type ClaimVerdict = "valid" | "invalid" | "malformed" | "expired" | "superseded" | "unknown";
22
+ /** The `provider.call.error` each refusal answers with (4.6, 15.14 M-3, C.P1). */
23
+ export declare const CLAIM_VERDICT_ERRORS: {
24
+ readonly invalid: {
25
+ readonly code: "stale_epoch";
26
+ readonly retryable: false;
27
+ };
28
+ readonly malformed: {
29
+ readonly code: "claim_invalid";
30
+ readonly retryable: false;
31
+ };
32
+ readonly expired: {
33
+ readonly code: "claim_expired";
34
+ readonly retryable: true;
35
+ };
36
+ readonly superseded: {
37
+ readonly code: "registry_key_revoked";
38
+ readonly retryable: true;
39
+ };
40
+ readonly unknown: {
41
+ readonly code: "registry_key_unknown";
42
+ readonly retryable: true;
43
+ };
44
+ };
45
+ /** The highest epoch this hand accepted for one (account, registry generation), and the placement that held it. */
46
+ export interface EpochFloor {
47
+ readonly epoch: number;
48
+ readonly placementId: string;
49
+ }
50
+ /** What this hand trusts when it checks a claim; supplied by the hand's executor (CK8), read fresh per check. */
51
+ export interface ClaimTrust {
52
+ readonly accountId: string;
53
+ readonly records: readonly LocalKeyVerification[];
54
+ readonly roster: RosterView;
55
+ readonly revocations: readonly VerifierRevocation[];
56
+ epochFloor(accountId: string, registryGeneration: number): EpochFloor | undefined;
57
+ }
58
+ export interface ClaimClock {
59
+ readonly now: number;
60
+ readonly nowMono: number;
61
+ }
62
+ export interface ClaimVerifier {
63
+ verify(claim: unknown, clock: ClaimClock): Promise<ClaimVerdict>;
64
+ }
65
+ /** `ASCII(domain) || 0x00 || UTF8(canonicalJson(claim))`: the exact bytes the registry key signs (C.P1). */
66
+ export declare function placementClaimSigningInput(claim: PlacementClaim): Uint8Array<ArrayBuffer>;
67
+ /** The claim's verdict for this hand (custody design 4.6, 15.15, 15.16 and the total order of C.P1). */
68
+ export declare function verifyPlacementClaim(value: unknown, trust: ClaimTrust, clock: ClaimClock): Promise<ClaimVerdict>;
69
+ //# sourceMappingURL=placement-claim.d.ts.map
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Placement claims for hosted model-key custody (row CK7; custody design 2.2, 4.6, 15.15, 15.16, Appendix C.P1).
3
+ *
4
+ * The placement registry signs `{ claim, sig }` for the placement that currently owns a session. A hand executes a
5
+ * brain's request (provider.call.start/result, ToolHost dispatch, hook.model_request, custody.refresh.request) only
6
+ * when `verifyPlacementClaim` answers `valid`. The verdict is total and ordered (C.P1): closed envelope, roster
7
+ * freshness, key selection and local trust, signature/account/epoch/placement, lifetime bounds, expiry. Every input
8
+ * is untrusted; nothing here throws on bad input, and the hand records a new epoch floor only after `valid`.
9
+ *
10
+ * C.P1 checks local supersession before the signature, so a forged claim under a superseded generation also answers
11
+ * `superseded`. The hand executor (CK8) must audit-log every `superseded` verdict with `sig.keyId` and
12
+ * `claim.placementId`, as it does for `malformed`, so such a probe stays visible.
13
+ */
14
+ import { canonicalJson } from "@earendil-works/pi-agent-core";
15
+ import { verifyEcdsaP256Sha256 } from "./ecdsa-p256.js";
16
+ import { rosterViewAt } from "./roster-entries.js";
17
+ import { hasExactKeys, isRecord } from "./signed-statement.js";
18
+ import { serviceEntryValidity } from "./verified-keys.js";
19
+ /** A claim may live at most 24 h; the brain renews it at half-life under the same epoch (4.6). */
20
+ export const CLAIM_MAX_LIFETIME_MS = 86_400_000;
21
+ /** Device-side tolerance for a claim's `issuedAt` in the hand's future (15.15 M-1); `MOVE_SKEW_MS` stays server-side. */
22
+ export const CLAIM_ISSUE_SKEW_MS = 300_000;
23
+ const PLACEMENT_CLAIM_DOMAIN = "omo-custody/placement-claim/v1";
24
+ /** The `provider.call.error` each refusal answers with (4.6, 15.14 M-3, C.P1). */
25
+ export const CLAIM_VERDICT_ERRORS = {
26
+ invalid: { code: "stale_epoch", retryable: false },
27
+ malformed: { code: "claim_invalid", retryable: false },
28
+ expired: { code: "claim_expired", retryable: true },
29
+ superseded: { code: "registry_key_revoked", retryable: true },
30
+ unknown: { code: "registry_key_unknown", retryable: true },
31
+ };
32
+ /** `ASCII(domain) || 0x00 || UTF8(canonicalJson(claim))`: the exact bytes the registry key signs (C.P1). */
33
+ export function placementClaimSigningInput(claim) {
34
+ const { accountId, placementId, epoch, registryGeneration, issuedAt, expiresAt } = claim;
35
+ const payload = canonicalJson({ accountId, placementId, epoch, registryGeneration, issuedAt, expiresAt });
36
+ const encoder = new TextEncoder();
37
+ return new Uint8Array([...encoder.encode(PLACEMENT_CLAIM_DOMAIN), 0, ...encoder.encode(payload)]);
38
+ }
39
+ const CLAIM_KEYS = ["accountId", "placementId", "epoch", "registryGeneration", "issuedAt", "expiresAt"];
40
+ function closedRecord(value, keys) {
41
+ return isRecord(value) && hasExactKeys(value, keys) ? value : undefined;
42
+ }
43
+ const isText = (value) => typeof value === "string" && value.length > 0;
44
+ /** A nonnegative safe integer, never -0 (C.P1 boundary rules: no fractions, non-finite values or -0 enter `C`). */
45
+ const isCount = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && !Object.is(value, -0);
46
+ /** The closed `{ claim, sig }` envelope, or `undefined` for any other shape (C.P1 canonical input). */
47
+ function parseSignedPlacementClaim(value) {
48
+ const envelope = closedRecord(value, ["claim", "sig"]);
49
+ const claim = closedRecord(envelope?.claim, CLAIM_KEYS);
50
+ const sig = closedRecord(envelope?.sig, ["alg", "keyId", "sig"]);
51
+ if (!claim || !sig || sig.alg !== "ECDSA-P256-SHA256" || !isText(sig.keyId) || !isText(sig.sig))
52
+ return undefined;
53
+ const { accountId, placementId, epoch, registryGeneration, issuedAt, expiresAt } = claim;
54
+ if (!isText(accountId) || !isText(placementId))
55
+ return undefined;
56
+ if (!isCount(epoch) || !isCount(registryGeneration) || !isCount(issuedAt) || !isCount(expiresAt))
57
+ return undefined;
58
+ return {
59
+ claim: { accountId, placementId, epoch, registryGeneration, issuedAt, expiresAt },
60
+ sig: { alg: "ECDSA-P256-SHA256", keyId: sig.keyId, sig: sig.sig },
61
+ };
62
+ }
63
+ /**
64
+ * The one registry entry of the claim's generation this hand may verify under. A roster entry of that generation that
65
+ * this device's records supersede is `superseded`; a missing, unchecked or ambiguous key is `unknown` (also when a
66
+ * superseded generation's entry has left the roster); only bad entries left is `invalid`.
67
+ */
68
+ async function selectRegistryKey(claim, trust, view, clock) {
69
+ const candidates = view.serviceEntries.filter((entry) => entry.kind === "placement-registry" &&
70
+ entry.accountId === trust.accountId &&
71
+ entry.generation === claim.registryGeneration);
72
+ const context = { revocations: trust.revocations, now: clock.now, nowMono: clock.nowMono };
73
+ const validities = await Promise.all(candidates.map((entry) => serviceEntryValidity(entry, trust.accountId, trust.records, view, context)));
74
+ if (validities.includes("superseded"))
75
+ return { verdict: "superseded" };
76
+ const valid = candidates.filter((_, index) => validities[index] === "valid");
77
+ const [first] = valid;
78
+ if (first && valid.every((entry) => entry.sigKeyId === first.sigKeyId && entry.sigPk === first.sigPk))
79
+ return { entry: first };
80
+ const onlyBad = candidates.length > 0 && validities.every((validity) => validity === "invalid");
81
+ return { verdict: onlyBad ? "invalid" : "unknown" };
82
+ }
83
+ function belowFloor(claim, floor) {
84
+ if (!floor)
85
+ return false;
86
+ return claim.epoch < floor.epoch || (claim.epoch === floor.epoch && claim.placementId !== floor.placementId);
87
+ }
88
+ /** The claim's verdict for this hand (custody design 4.6, 15.15, 15.16 and the total order of C.P1). */
89
+ export async function verifyPlacementClaim(value, trust, clock) {
90
+ const signed = parseSignedPlacementClaim(value);
91
+ if (!signed)
92
+ return "malformed";
93
+ const view = rosterViewAt(trust.roster, clock.now, clock.nowMono);
94
+ if (view.state === "unknown")
95
+ return "unknown";
96
+ const { claim, sig } = signed;
97
+ if (claim.accountId !== trust.accountId)
98
+ return "invalid";
99
+ const selection = await selectRegistryKey(claim, trust, view, clock);
100
+ if ("verdict" in selection)
101
+ return selection.verdict;
102
+ if (sig.keyId !== selection.entry.sigKeyId)
103
+ return "invalid";
104
+ if (!(await verifyEcdsaP256Sha256(selection.entry.sigPk, placementClaimSigningInput(claim), sig.sig)))
105
+ return "invalid";
106
+ if (belowFloor(claim, trust.epochFloor(claim.accountId, claim.registryGeneration)))
107
+ return "invalid";
108
+ const effectiveNow = Math.max(clock.now, view.rosterIssuedAt);
109
+ if (claim.expiresAt - claim.issuedAt > CLAIM_MAX_LIFETIME_MS ||
110
+ claim.issuedAt > effectiveNow + CLAIM_ISSUE_SKEW_MS ||
111
+ claim.issuedAt > view.rosterIssuedAt + CLAIM_MAX_LIFETIME_MS)
112
+ return "malformed";
113
+ return claim.expiresAt > effectiveNow ? "valid" : "expired";
114
+ }
115
+ //# sourceMappingURL=placement-claim.js.map
@@ -0,0 +1,18 @@
1
+ import type { CredentialSlotState, StoredSlotRevisions } from "../credential-pool/state-store.ts";
2
+ /**
3
+ * The credential-pool health that rotation reads and writes (senpi#5835): cooldowns, half-open probe leases and
4
+ * the revisions they are keyed by. On one machine it is the file-backed `CredentialSlotRepository`; a hosted brain
5
+ * supplies its own durable state behind the same calls.
6
+ */
7
+ export interface PoolState {
8
+ listSlots(providerId: string, laneId: string): Promise<Record<string, CredentialSlotState>>;
9
+ /** Atomic read-modify-write of one slot; `stateVersion` increments on every write, undefined removes the slot. */
10
+ mutateSlotState(providerId: string, laneId: string, slotId: string, fn: (current: CredentialSlotState | undefined) => Omit<CredentialSlotState, "stateVersion"> | undefined): Promise<CredentialSlotState | undefined>;
11
+ storedSlotRevisions(providerId: string, slotName: string, material: {
12
+ key?: string;
13
+ access?: string;
14
+ refresh?: string;
15
+ }): Promise<StoredSlotRevisions>;
16
+ envCredentialRevision(envVarName: string, envValue: string): Promise<string>;
17
+ }
18
+ //# sourceMappingURL=pool-state.d.ts.map
@@ -0,0 +1,5 @@
1
+ // Compile-time proof that the fused implementation is today's repository, used as is.
2
+ const fusedPoolState = (repository) => repository;
3
+ void fusedPoolState;
4
+ export {};
5
+ //# sourceMappingURL=pool-state.js.map
@@ -0,0 +1,101 @@
1
+ import { type HpkeRecipient } from "./hpke.ts";
2
+ export { SealedFormatError, type SealedFormatErrorCode } from "./hpke.ts";
3
+ export type ProviderId = string;
4
+ export type DeviceId = string;
5
+ export type SecretField = "apiKey" | `credential:${number}` | `header:${string}` | `modelHeader:${string}:${string}` | "urlUserinfo" | `urlPath:${number}` | `urlQuery:${string}`;
6
+ declare const sourceDigestBrand: unique symbol;
7
+ declare const dekIdBrand: unique symbol;
8
+ export type SourceDigest = string & {
9
+ readonly [sourceDigestBrand]: true;
10
+ };
11
+ export type DekId = string & {
12
+ readonly [dekIdBrand]: true;
13
+ };
14
+ /** The one boundary parser for a source digest read from an entry; a FieldAad cannot be built without one. */
15
+ export declare function parseSourceDigest(value: string): SourceDigest;
16
+ /** The one boundary parser for a dekId read from an entry; a FieldAad cannot be built without one. */
17
+ export declare function parseDekId(value: string): DekId;
18
+ export interface FieldAad {
19
+ readonly account: string;
20
+ readonly provider: ProviderId;
21
+ readonly owner: DeviceId;
22
+ readonly sourceDigest: SourceDigest;
23
+ readonly field: SecretField;
24
+ readonly dekId: DekId;
25
+ }
26
+ export type WrapKind = "cfg" | "dek" | "proj";
27
+ export interface WrapPosition {
28
+ readonly account: string;
29
+ readonly kind: WrapKind;
30
+ readonly id: string;
31
+ }
32
+ export interface WrapAad extends WrapPosition {
33
+ readonly recipientKeyId: string;
34
+ }
35
+ export interface ConfigAad {
36
+ readonly accountId: string;
37
+ readonly version: number;
38
+ readonly cfgKeyId: string;
39
+ }
40
+ export declare const HPKE_SUITE = "DHKEM(P-256,HKDF-SHA256)/HKDF-SHA256/AES-256-GCM";
41
+ export interface HpkeWrap {
42
+ readonly suite: typeof HPKE_SUITE;
43
+ readonly enc: string;
44
+ readonly ct: string;
45
+ }
46
+ export interface SealedBlob {
47
+ readonly nonce: string;
48
+ readonly ct: string;
49
+ }
50
+ export type FieldSource = "literal" | "env" | "command" | "oauth_access";
51
+ export interface SealedFieldMeta {
52
+ readonly source: FieldSource;
53
+ readonly resolvedAt: number;
54
+ readonly expiresAt?: number;
55
+ readonly refreshable: boolean;
56
+ }
57
+ export type SealedField = ({
58
+ readonly v: 1;
59
+ readonly state: "sealed";
60
+ readonly meta: SealedFieldMeta;
61
+ } & SealedBlob) | {
62
+ readonly v: 1;
63
+ readonly state: "unresolved";
64
+ readonly meta: {
65
+ readonly source: "env" | "command" | "oauth_access";
66
+ readonly attemptedAt: number;
67
+ readonly refreshable: true;
68
+ };
69
+ };
70
+ export interface ConfigEnvelope extends ConfigAad {
71
+ readonly v: 1;
72
+ readonly cfg: SealedBlob;
73
+ }
74
+ /** A recipient that knows its own key id: the opener's id, never one read from a stored wrap, enters the AAD. */
75
+ export interface KeyedRecipient extends HpkeRecipient {
76
+ readonly keyId: string;
77
+ }
78
+ type Bytes = Uint8Array<ArrayBuffer>;
79
+ /** @internal Exposed for the encoding vectors. */
80
+ export declare function fieldAadBytes(aad: FieldAad): Bytes;
81
+ /** @internal Exposed for the encoding vectors. */
82
+ export declare function wrapAadBytes(aad: WrapAad): Bytes;
83
+ /** @internal Exposed for the encoding vectors. */
84
+ export declare function configAadBytes(aad: ConfigAad): Bytes;
85
+ /** Strict: no padding, no foreign characters, no non-zero trailing bits, so one byte string has one encoding. */
86
+ export declare function decodeBase64Url(value: string): Bytes;
87
+ /** @internal Raw AES-256-GCM open, exposed for the external known-answer vector. Any failure is `open_failed`. */
88
+ export declare function aes256GcmOpen(key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, ct: Uint8Array): Promise<Bytes>;
89
+ /** Seals one secret under the provider DEK. Empty secrets are never sealed. */
90
+ export declare function sealField(dek: Uint8Array, aad: FieldAad, secret: string, meta: SealedFieldMeta): Promise<Extract<SealedField, {
91
+ state: "sealed";
92
+ }>>;
93
+ /** Opens a sealed field under the AAD the opener rebuilt from the entry and the requested position. */
94
+ export declare function openField(dek: Uint8Array, aad: FieldAad, field: SealedField): Promise<Bytes>;
95
+ export declare function sealConfig(kCfg: Uint8Array, aad: ConfigAad, plaintext: Uint8Array): Promise<SealedBlob>;
96
+ /** Opens the config object; its AAD is rebuilt from the envelope's own public fields. */
97
+ export declare function openConfig(kCfg: Uint8Array, envelope: ConfigEnvelope): Promise<Bytes>;
98
+ export declare function wrapKey(recipientPublicKey: Uint8Array, aad: WrapAad, key: Uint8Array): Promise<HpkeWrap>;
99
+ /** Opens a wrap at `position` with the recipient's own key id; nothing read from the wrap enters the AAD. */
100
+ export declare function unwrapKey(recipient: KeyedRecipient, position: WrapPosition, wrap: HpkeWrap): Promise<Bytes>;
101
+ //# sourceMappingURL=sealed-format.d.ts.map