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

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 (244) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/bundle/chunks/{bun-extension-importer-RCGPQTI2.js → bun-extension-importer-GQ6IKEXD.js} +2 -2
  3. package/dist/bundle/chunks/{chunk-RQ6CHXQ2.js → chunk-3TZQXZKH.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-QN2BHP3M.js → chunk-44BJPS5I.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-WGRKM3N6.js → chunk-45MVD7BU.js} +1 -1
  6. package/dist/bundle/chunks/chunk-4JIBEZZE.js +106 -0
  7. package/dist/bundle/chunks/{chunk-CF5OGBC4.js → chunk-4NYNMAYF.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-X5ZVYNOL.js → chunk-4UR6UFBH.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-LNPAT22L.js → chunk-527Q2UYM.js} +9 -8
  10. package/dist/bundle/chunks/{chunk-HLM46XEB.js → chunk-622VYTRI.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-D5MRJWUK.js → chunk-7CFORZPE.js} +221 -229
  12. package/dist/bundle/chunks/{chunk-GKAYPJ4E.js → chunk-7PROV6BB.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-4E7GDG4H.js → chunk-7VMEE62L.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-C7OKUC7L.js → chunk-K3UHGDUQ.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-7III44PV.js → chunk-LHSWAJJL.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-47PPZKPO.js → chunk-LM6DOWQH.js} +4 -4
  17. package/dist/bundle/chunks/chunk-LPYBKMPT.js +2 -0
  18. package/dist/bundle/chunks/{chunk-2FDW2LQL.js → chunk-M7GF73FO.js} +2 -2
  19. package/dist/bundle/chunks/{chunk-ECW7VBMB.js → chunk-NDSWTAYJ.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-GSKCWXXG.js → chunk-Q6NR6644.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-TIPKAMSS.js → chunk-SGAFIMAU.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-7PSBD5KS.js → chunk-STGOYIXC.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-DSWDJ7HT.js → chunk-WQ4RBPMS.js} +1 -1
  24. package/dist/bundle/chunks/chunk-XZDCMDUH.js +2 -0
  25. package/dist/bundle/chunks/{chunk-GNSMNSL7.js → chunk-Y5L4UFMI.js} +2 -2
  26. package/dist/bundle/chunks/{cli-main-T74YJGIF.js → cli-main-XBEFQW5A.js} +1 -1
  27. package/dist/bundle/chunks/{dispatch-BAK2YZKD.js → dispatch-VDIYSXQN.js} +1 -1
  28. package/dist/bundle/chunks/{hand-entry-AYRUGE2A.js → hand-entry-QFEY3JMQ.js} +1 -1
  29. package/dist/bundle/chunks/{headless-host-ES74ZB6M.js → headless-host-YXUGONR3.js} +1 -1
  30. package/dist/bundle/chunks/{host-command-YRXZE4TV.js → host-command-VHX2ILLM.js} +1 -1
  31. package/dist/bundle/chunks/{host-session-command-N7IN7ZDV.js → host-session-command-P3MRW63Z.js} +1 -1
  32. package/dist/bundle/chunks/interactive-mode-YGL3QHU6.js +2 -0
  33. package/dist/bundle/chunks/main-5SD4DHMC.js +2 -0
  34. package/dist/bundle/chunks/{multi-session-host-XSUJDHFZ.js → multi-session-host-PGRHY3JP.js} +20 -17
  35. package/dist/bundle/chunks/{package-manager-cli-GLUNPQJU.js → package-manager-cli-SADLU7QX.js} +1 -1
  36. package/dist/bundle/chunks/{role-RRFFHZF5.js → role-4BUU46VW.js} +3 -3
  37. package/dist/bundle/chunks/{rotation-stream-BN2YK2MC.js → rotation-stream-TODBIHXJ.js} +1 -1
  38. package/dist/bundle/chunks/rpc-mode-27TC7W6K.js +2 -0
  39. package/dist/bundle/chunks/{run-attached-tui-W236SEOR.js → run-attached-tui-MXCC5IZR.js} +1 -1
  40. package/dist/bundle/chunks/{runtime-session-OBXYK4PI.js → runtime-session-VO3X4OE3.js} +1 -1
  41. package/dist/bundle/chunks/{schedule-command-B2EJTBOE.js → schedule-command-MX56D6UE.js} +1 -1
  42. package/dist/bundle/chunks/{session-control-endpoint-YIMLHGLG.js → session-control-endpoint-IL6ERD4U.js} +1 -1
  43. package/dist/bundle/chunks/{session-picker-E6ZONUJB.js → session-picker-6VRC25MJ.js} +1 -1
  44. package/dist/bundle/chunks/session-worker.js +114 -110
  45. package/dist/bundle/cli.js +2 -2
  46. package/dist/bundle/index.js +1 -1
  47. package/dist/bundle/rpc-entry.js +1 -1
  48. package/dist/bundle/runtime-manifest.json +1 -1
  49. package/dist/core/agent-session-runtime.d.ts +2 -0
  50. package/dist/core/agent-session-runtime.js +9 -2
  51. package/dist/core/agent-session-services.d.ts +2 -0
  52. package/dist/core/agent-session-services.js +1 -0
  53. package/dist/core/agent-session.d.ts +32 -3
  54. package/dist/core/agent-session.js +142 -29
  55. package/dist/core/credential-pool/rotation-slots.d.ts +3 -3
  56. package/dist/core/credential-pool/state-store.d.ts +1 -1
  57. package/dist/core/egress/classify.d.ts +8 -3
  58. package/dist/core/egress/classify.js +177 -158
  59. package/dist/core/egress/console-redactor.d.ts +7 -0
  60. package/dist/core/egress/console-redactor.js +74 -0
  61. package/dist/core/egress/credentials.d.ts +73 -0
  62. package/dist/core/egress/credentials.js +20 -0
  63. package/dist/core/egress/device-custody.d.ts +1 -1
  64. package/dist/core/egress/device-custody.js +18 -15
  65. package/dist/core/egress/device-proxy-call.d.ts +59 -0
  66. package/dist/core/egress/device-proxy-call.js +148 -0
  67. package/dist/core/egress/device-proxy-lost-calls.d.ts +27 -0
  68. package/dist/core/egress/device-proxy-lost-calls.js +28 -0
  69. package/dist/core/egress/device-proxy-ports.d.ts +49 -0
  70. package/dist/core/egress/device-proxy-ports.js +2 -0
  71. package/dist/core/egress/device-proxy-request.d.ts +34 -0
  72. package/dist/core/egress/device-proxy-request.js +69 -0
  73. package/dist/core/egress/device-proxy-waits.d.ts +40 -0
  74. package/dist/core/egress/device-proxy-waits.js +67 -0
  75. package/dist/core/egress/device-proxy.d.ts +3 -0
  76. package/dist/core/egress/device-proxy.js +214 -0
  77. package/dist/core/egress/endpoint.js +2 -1
  78. package/dist/core/egress/entry-verify.d.ts +13 -0
  79. package/dist/core/egress/entry-verify.js +33 -0
  80. package/dist/core/egress/hpke.d.ts +63 -0
  81. package/dist/core/egress/hpke.js +192 -0
  82. package/dist/core/egress/key-policy-template.d.ts +7 -1
  83. package/dist/core/egress/ledger-schema.d.ts +80 -0
  84. package/dist/core/egress/ledger-schema.js +117 -0
  85. package/dist/core/egress/ledger.d.ts +55 -0
  86. package/dist/core/egress/ledger.js +159 -0
  87. package/dist/core/egress/pinned-infra.d.ts +23 -2
  88. package/dist/core/egress/pinned-infra.js +9 -2
  89. package/dist/core/egress/placement-claim.d.ts +69 -0
  90. package/dist/core/egress/placement-claim.js +115 -0
  91. package/dist/core/egress/pool-state.d.ts +18 -0
  92. package/dist/core/egress/pool-state.js +5 -0
  93. package/dist/core/egress/redact.d.ts +10 -0
  94. package/dist/core/egress/redact.js +134 -0
  95. package/dist/core/egress/sealed-format.d.ts +101 -0
  96. package/dist/core/egress/sealed-format.js +174 -0
  97. package/dist/core/egress/sealed-source.d.ts +35 -0
  98. package/dist/core/egress/sealed-source.js +140 -0
  99. package/dist/core/egress/verified-keys.js +1 -1
  100. package/dist/core/egress/wire.d.ts +228 -0
  101. package/dist/core/egress/wire.js +49 -0
  102. package/dist/core/extensions/builtin/ask-user/escape-residue.d.ts +50 -0
  103. package/dist/core/extensions/builtin/ask-user/escape-residue.js +113 -0
  104. package/dist/core/extensions/builtin/ask-user/extension.js +7 -1
  105. package/dist/core/extensions/builtin/ask-user/resume.js +2 -1
  106. package/dist/core/extensions/builtin/ask-user/tool.d.ts +3 -0
  107. package/dist/core/extensions/builtin/ask-user/tool.js +8 -0
  108. package/dist/core/extensions/builtin/goal/index.js +46 -0
  109. package/dist/core/extensions/builtin/goal/stop-pause.d.ts +1 -0
  110. package/dist/core/extensions/builtin/goal/stop-pause.js +4 -2
  111. package/dist/core/extensions/builtin/monitor-state-event.d.ts +26 -0
  112. package/dist/core/extensions/builtin/monitor-state-event.js +2 -0
  113. package/dist/core/extensions/builtin/permission-system/index.js +10 -4
  114. package/dist/core/extensions/builtin/permission-system/preset-registry.d.ts +2 -0
  115. package/dist/core/extensions/builtin/permission-system/preset-registry.js +11 -0
  116. package/dist/core/extensions/builtin/permission-system/settings.d.ts +2 -0
  117. package/dist/core/extensions/builtin/permission-system/settings.js +10 -1
  118. package/dist/core/extensions/builtin/terminal/background-age.d.ts +25 -0
  119. package/dist/core/extensions/builtin/terminal/background-age.js +91 -0
  120. package/dist/core/extensions/builtin/terminal/extension-state.d.ts +1 -1
  121. package/dist/core/extensions/builtin/terminal/extension-state.js +21 -3
  122. package/dist/core/extensions/builtin/terminal/notify.d.ts +6 -0
  123. package/dist/core/extensions/builtin/terminal/notify.js +39 -5
  124. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +9 -2
  125. package/dist/core/extensions/builtin/terminal/session-bundle.js +36 -1
  126. package/dist/core/extensions/builtin/terminal/settings.d.ts +2 -0
  127. package/dist/core/extensions/builtin/terminal/settings.js +8 -0
  128. package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.d.ts +15 -0
  129. package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.js +87 -0
  130. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts +3 -1
  131. package/dist/core/extensions/builtin/terminal/tools/bash.js +44 -15
  132. package/dist/core/extensions/builtin/terminal/tools/child-placement.d.ts +8 -0
  133. package/dist/core/extensions/builtin/terminal/tools/child-placement.js +43 -6
  134. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +14 -2
  135. package/dist/core/extensions/bun-extension-importer.js +4 -3
  136. package/dist/core/extensions/bun-extension-load-turns.d.ts +4 -0
  137. package/dist/core/extensions/bun-extension-load-turns.js +45 -0
  138. package/dist/core/extensions/bun-extension-registry.d.ts +1 -0
  139. package/dist/core/extensions/bun-extension-registry.js +19 -1
  140. package/dist/core/extensions/loader.js +5 -0
  141. package/dist/core/extensions/session-control-types.d.ts +15 -0
  142. package/dist/core/extensions/session-control-types.js +11 -0
  143. package/dist/core/extensions/session-dialog-api.d.ts +5 -0
  144. package/dist/core/extensions/session-dialog-api.js +13 -0
  145. package/dist/core/extensions/types.d.ts +14 -1
  146. package/dist/core/external-admission.d.ts +16 -2
  147. package/dist/core/external-admission.js +22 -4
  148. package/dist/core/permission-decision.d.ts +22 -0
  149. package/dist/core/permission-decision.js +53 -0
  150. package/dist/core/permission-mode.d.ts +56 -0
  151. package/dist/core/permission-mode.js +133 -0
  152. package/dist/core/retry-fallback/controller-types.d.ts +6 -0
  153. package/dist/core/retry-fallback/controller.d.ts +9 -0
  154. package/dist/core/retry-fallback/controller.js +34 -3
  155. package/dist/core/retry-fallback/unusable.d.ts +25 -1
  156. package/dist/core/retry-fallback/unusable.js +87 -2
  157. package/dist/core/retry-fallback/usage-limit.d.ts +6 -1
  158. package/dist/core/retry-fallback/usage-limit.js +12 -4
  159. package/dist/core/sdk.d.ts +6 -1
  160. package/dist/core/sdk.js +3 -0
  161. package/dist/core/session-log.js +1 -1
  162. package/dist/core/stall-warning.d.ts +41 -0
  163. package/dist/core/stall-warning.js +72 -0
  164. package/dist/core/terminal-settings.d.ts +1 -0
  165. package/dist/core/turn-origin.d.ts +31 -0
  166. package/dist/core/turn-origin.js +64 -0
  167. package/dist/modes/interactive/components/remote-delivery-message.d.ts +2 -1
  168. package/dist/modes/interactive/components/remote-delivery-message.js +13 -5
  169. package/dist/modes/rpc/admit-external-message-command.d.ts +37 -0
  170. package/dist/modes/rpc/admit-external-message-command.js +61 -0
  171. package/dist/modes/rpc/admit-external-message-wire.d.ts +42 -0
  172. package/dist/modes/rpc/admit-external-message-wire.js +55 -0
  173. package/dist/modes/rpc/client-input-handler.d.ts +2 -0
  174. package/dist/modes/rpc/client-input-handler.js +2 -0
  175. package/dist/modes/rpc/connection-handler.js +30 -5
  176. package/dist/modes/rpc/connection-roles.d.ts +6 -0
  177. package/dist/modes/rpc/connection-roles.js +13 -0
  178. package/dist/modes/rpc/delegated-drive-origin.d.ts +9 -0
  179. package/dist/modes/rpc/delegated-drive-origin.js +16 -0
  180. package/dist/modes/rpc/effect-commands.d.ts +7 -1
  181. package/dist/modes/rpc/host-bare-endpoint-registration.d.ts +7 -0
  182. package/dist/modes/rpc/host-bare-endpoint-registration.js +35 -0
  183. package/dist/modes/rpc/host-capabilities.d.ts +2 -0
  184. package/dist/modes/rpc/host-capabilities.js +3 -0
  185. package/dist/modes/rpc/host-handover-parked.d.ts +11 -0
  186. package/dist/modes/rpc/host-handover-parked.js +17 -0
  187. package/dist/modes/rpc/host-handover-reopen.js +18 -6
  188. package/dist/modes/rpc/host-handover-resident-writer.d.ts +16 -0
  189. package/dist/modes/rpc/host-handover-resident-writer.js +57 -0
  190. package/dist/modes/rpc/host-idle-handover.js +1 -0
  191. package/dist/modes/rpc/host-warm.d.ts +16 -1
  192. package/dist/modes/rpc/host-warm.js +33 -6
  193. package/dist/modes/rpc/method-classes-schema-delivery.d.ts +32 -0
  194. package/dist/modes/rpc/method-classes-schema-delivery.js +22 -0
  195. package/dist/modes/rpc/method-classes-schema-session.d.ts +36 -0
  196. package/dist/modes/rpc/method-classes-schema-session.js +3 -0
  197. package/dist/modes/rpc/method-classes-schema.d.ts +36 -0
  198. package/dist/modes/rpc/method-classes.d.ts +2 -0
  199. package/dist/modes/rpc/method-classes.js +4 -0
  200. package/dist/modes/rpc/multi-session-host.js +6 -1
  201. package/dist/modes/rpc/permission-preset-command.d.ts +6 -0
  202. package/dist/modes/rpc/permission-preset-command.js +27 -0
  203. package/dist/modes/rpc/rpc-input-validation.js +4 -3
  204. package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
  205. package/dist/modes/rpc/rpc-response-types.d.ts +2 -1
  206. package/dist/modes/rpc/rpc-session-command-types.d.ts +6 -1
  207. package/dist/modes/rpc/rpc-session-event-types.d.ts +5 -2
  208. package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -1
  209. package/dist/modes/rpc/rpc-session-state.js +2 -0
  210. package/dist/modes/rpc/session-attachment-commands.js +3 -3
  211. package/dist/modes/rpc/session-event-writer-connections.js +1 -0
  212. package/dist/modes/rpc/session-event-writer.d.ts +1 -1
  213. package/dist/modes/rpc/session-registry-attach.d.ts +0 -9
  214. package/dist/modes/rpc/session-registry-attach.js +16 -3
  215. package/dist/modes/rpc/session-registry-types.d.ts +2 -0
  216. package/dist/modes/rpc/session-registry.d.ts +2 -0
  217. package/dist/modes/rpc/session-registry.js +16 -3
  218. package/dist/modes/rpc/session-router-close.js +43 -6
  219. package/dist/modes/rpc/session-router-context.js +2 -0
  220. package/dist/modes/rpc/session-router-occupancy.js +2 -11
  221. package/dist/modes/rpc/session-router-open-profile.js +4 -0
  222. package/dist/modes/rpc/session-router-open.d.ts +1 -1
  223. package/dist/modes/rpc/session-router-open.js +6 -17
  224. package/dist/modes/rpc/session-router-residency.d.ts +24 -0
  225. package/dist/modes/rpc/session-router-residency.js +90 -0
  226. package/dist/modes/rpc/session-router-routing.js +7 -1
  227. package/dist/modes/rpc/session-status.js +4 -1
  228. package/dist/modes/rpc/session-worker.js +4 -0
  229. package/dist/modes/rpc/tool-child/terminal-jobs.d.ts +1 -0
  230. package/dist/modes/rpc/tool-child/terminal-jobs.js +17 -1
  231. package/dist/modes/rpc/worker-session-registry.js +5 -2
  232. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  233. package/docs/engine-socket/fixtures/rpc-inbound-samples.json +29 -2
  234. package/docs/extensions.md +12 -0
  235. package/docs/rpc.md +129 -12
  236. package/docs/terminal-tools.md +25 -1
  237. package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
  238. package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
  239. package/package.json +6 -6
  240. package/dist/bundle/chunks/chunk-C7HEEBFH.js +0 -2
  241. package/dist/bundle/chunks/chunk-XCMII2VQ.js +0 -98
  242. package/dist/bundle/chunks/interactive-mode-B5SQDLH7.js +0 -2
  243. package/dist/bundle/chunks/main-UQFVFCNY.js +0 -2
  244. package/dist/bundle/chunks/rpc-mode-JYN3E7P7.js +0 -2
@@ -0,0 +1,134 @@
1
+ export const REDACTED = "<redacted>";
2
+ const MIN_FRAGMENT = 8;
3
+ const NO_NODE = -1;
4
+ function newNode() {
5
+ return { next: new Map(), fail: 0, pattern: undefined, dictionary: NO_NODE };
6
+ }
7
+ function dedupe(patterns) {
8
+ const byText = new Map();
9
+ for (const pattern of patterns) {
10
+ const seen = byText.get(pattern.text);
11
+ if (seen === undefined || (seen.wholeToken && !pattern.wholeToken))
12
+ byText.set(pattern.text, pattern);
13
+ }
14
+ return [...byText.values()];
15
+ }
16
+ function isTokenChar(char) {
17
+ return char !== undefined && /[A-Za-z0-9]/.test(char);
18
+ }
19
+ /**
20
+ * Base64 characters that depend only on the secret's bytes when the secret starts `offset` bytes into a longer
21
+ * stream: character i covers bits [6i, 6i + 6), and it is kept only when that range lies inside the secret's bits.
22
+ */
23
+ function alignedBase64Core(secret, offset, alphabet) {
24
+ const encoded = Buffer.concat([Buffer.alloc(offset), secret])
25
+ .toString(alphabet)
26
+ .replace(/=+$/, "");
27
+ const firstBit = offset * 8;
28
+ const lastBit = firstBit + secret.length * 8;
29
+ let core = "";
30
+ for (let i = 0; i < encoded.length; i++) {
31
+ if (i * 6 >= firstBit && i * 6 + 6 <= lastBit)
32
+ core += encoded[i];
33
+ }
34
+ return core;
35
+ }
36
+ function patternsFor(secret, publicPrefixes) {
37
+ const bytes = Buffer.from(secret, "utf8");
38
+ const patterns = [{ text: secret, wholeToken: true }];
39
+ const urlEncoded = encodeURIComponent(secret);
40
+ if (urlEncoded !== secret)
41
+ patterns.push({ text: urlEncoded, wholeToken: true });
42
+ for (const alphabet of ["base64", "base64url"]) {
43
+ patterns.push({ text: bytes.toString(alphabet), wholeToken: true });
44
+ for (const offset of [0, 1, 2]) {
45
+ const core = alignedBase64Core(bytes, offset, alphabet);
46
+ if (core.length >= MIN_FRAGMENT)
47
+ patterns.push({ text: core, wholeToken: false });
48
+ }
49
+ }
50
+ for (let i = 0; i + MIN_FRAGMENT <= secret.length; i++) {
51
+ const gram = secret.slice(i, i + MIN_FRAGMENT);
52
+ if (!publicPrefixes.some((prefix) => prefix.includes(gram)))
53
+ patterns.push({ text: gram, wholeToken: false });
54
+ }
55
+ return patterns;
56
+ }
57
+ function buildAutomaton(patterns) {
58
+ const nodes = [newNode()];
59
+ patterns.forEach((pattern, index) => {
60
+ let state = 0;
61
+ for (const char of pattern.text) {
62
+ let target = nodes[state].next.get(char);
63
+ if (target === undefined) {
64
+ target = nodes.length;
65
+ nodes.push(newNode());
66
+ nodes[state].next.set(char, target);
67
+ }
68
+ state = target;
69
+ }
70
+ nodes[state].pattern = index;
71
+ });
72
+ const queue = [...nodes[0].next.values()];
73
+ for (let head = 0; head < queue.length; head++) {
74
+ const state = queue[head];
75
+ for (const [char, target] of nodes[state].next) {
76
+ let fallback = nodes[state].fail;
77
+ while (fallback !== 0 && !nodes[fallback].next.has(char))
78
+ fallback = nodes[fallback].fail;
79
+ const candidate = nodes[fallback].next.get(char);
80
+ nodes[target].fail = candidate !== undefined && candidate !== target ? candidate : 0;
81
+ const fail = nodes[nodes[target].fail];
82
+ nodes[target].dictionary = fail.pattern !== undefined ? nodes[target].fail : fail.dictionary;
83
+ queue.push(target);
84
+ }
85
+ }
86
+ return nodes;
87
+ }
88
+ export function createRedactor(options) {
89
+ const patterns = dedupe(options.secrets
90
+ .filter((secret) => secret.length > 0)
91
+ .flatMap((secret) => patternsFor(secret, options.publicPrefixes)));
92
+ if (patterns.length === 0)
93
+ return { redact: (text) => text };
94
+ const nodes = buildAutomaton(patterns);
95
+ const lengths = patterns.map((pattern) => [...pattern.text].length);
96
+ return {
97
+ redact(text) {
98
+ const chars = [...text];
99
+ const spans = [];
100
+ let state = 0;
101
+ for (let end = 0; end < chars.length; end++) {
102
+ const char = chars[end];
103
+ while (state !== 0 && !nodes[state].next.has(char))
104
+ state = nodes[state].fail;
105
+ state = nodes[state].next.get(char) ?? 0;
106
+ for (let match = nodes[state].pattern !== undefined ? state : nodes[state].dictionary; match !== NO_NODE; match = nodes[match].dictionary) {
107
+ const index = nodes[match].pattern;
108
+ const start = end - lengths[index] + 1;
109
+ if (patterns[index].wholeToken && (isTokenChar(chars[start - 1]) || isTokenChar(chars[end + 1])))
110
+ continue;
111
+ spans.push([start, end + 1]);
112
+ }
113
+ }
114
+ if (spans.length === 0)
115
+ return text;
116
+ spans.sort((a, b) => a[0] - b[0]);
117
+ let out = "";
118
+ let cursor = 0;
119
+ let current = spans[0];
120
+ for (const span of spans.slice(1)) {
121
+ if (span[0] <= current[1]) {
122
+ current = [current[0], Math.max(current[1], span[1])];
123
+ continue;
124
+ }
125
+ out += chars.slice(cursor, current[0]).join("") + REDACTED;
126
+ cursor = current[1];
127
+ current = span;
128
+ }
129
+ out += chars.slice(cursor, current[0]).join("") + REDACTED + chars.slice(current[1]).join("");
130
+ return out;
131
+ },
132
+ };
133
+ }
134
+ //# sourceMappingURL=redact.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
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Sealed custody formats (C6, row CK3): provider fields under the provider DEK, the account config object under K_cfg,
3
+ * and HPKE key wraps (K_cfg, K_proj, DEK) to device and custody keys.
4
+ *
5
+ * Every AAD is rebuilt by the opener from its own context and never stored. Its bytes are a per-kind ASCII label, a NUL
6
+ * and the canonical JSON of a flat record whose members are all required strings or one safe integer: on that domain
7
+ * the encoding is injective, so a ciphertext cannot move between accounts, providers, owners, digests, positions, DEK
8
+ * generations, wrap kinds, recipient keys or format versions. The label names the format version and, for the raw
9
+ * AES-GCM paths, the exact algorithm: AES-256-GCM, 96-bit nonce, 128-bit tag.
10
+ *
11
+ * Nonces come from the CSPRNG only, never a counter: K_cfg is sealed by every enrolled device. NIST SP 800-38D allows
12
+ * 2^32 random-nonce seals per key; a DEK sees about fields x refreshes and K_cfg one seal per publish per device, both
13
+ * orders of magnitude below. AES-GCM is not key-committing; that is safe here because every key is 256-bit random and
14
+ * only a dekId's owner can submit a wrap for it, who could plant any value anyway. AES-GCM-SIV is not in Web Crypto.
15
+ */
16
+ import { canonicalJson } from "@earendil-works/pi-agent-core";
17
+ import { hpkeOpenBase, hpkeSealBase, SealedFormatError } from "./hpke.js";
18
+ export { SealedFormatError } from "./hpke.js";
19
+ const isSourceDigest = (value) => /^[A-Za-z0-9:_-]{1,256}$/.test(value);
20
+ const isDekId = (value) => /^[A-Za-z0-9_-]{1,128}$/.test(value);
21
+ /** The one boundary parser for a source digest read from an entry; a FieldAad cannot be built without one. */
22
+ export function parseSourceDigest(value) {
23
+ if (!isSourceDigest(value))
24
+ throw new SealedFormatError("invalid_aad");
25
+ return value;
26
+ }
27
+ /** The one boundary parser for a dekId read from an entry; a FieldAad cannot be built without one. */
28
+ export function parseDekId(value) {
29
+ if (!isDekId(value))
30
+ throw new SealedFormatError("invalid_aad");
31
+ return value;
32
+ }
33
+ export const HPKE_SUITE = "DHKEM(P-256,HKDF-SHA256)/HKDF-SHA256/AES-256-GCM";
34
+ const NONCE_BYTES = 12;
35
+ const TAG_BYTES = 16;
36
+ const KEY_BYTES = 32;
37
+ const encoder = new TextEncoder();
38
+ function text(value) {
39
+ if (typeof value !== "string")
40
+ throw new SealedFormatError("invalid_aad");
41
+ return value;
42
+ }
43
+ function counter(value) {
44
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
45
+ throw new SealedFormatError("invalid_aad");
46
+ return value;
47
+ }
48
+ function aadBytes(label, record) {
49
+ return encoder.encode(`${label}\u0000${canonicalJson(record)}`);
50
+ }
51
+ /** @internal Exposed for the encoding vectors. */
52
+ export function fieldAadBytes(aad) {
53
+ return aadBytes("omo-custody/field-aad/v1", {
54
+ account: text(aad.account),
55
+ provider: text(aad.provider),
56
+ owner: text(aad.owner),
57
+ sourceDigest: text(aad.sourceDigest),
58
+ field: text(aad.field),
59
+ dekId: text(aad.dekId),
60
+ });
61
+ }
62
+ /** @internal Exposed for the encoding vectors. */
63
+ export function wrapAadBytes(aad) {
64
+ return aadBytes("omo-custody/wrap-aad/v1", {
65
+ account: text(aad.account),
66
+ kind: text(aad.kind),
67
+ id: text(aad.id),
68
+ recipientKeyId: text(aad.recipientKeyId),
69
+ });
70
+ }
71
+ /** @internal Exposed for the encoding vectors. */
72
+ export function configAadBytes(aad) {
73
+ return aadBytes("omo-custody/cfg-aad/v1", {
74
+ accountId: text(aad.accountId),
75
+ version: counter(aad.version),
76
+ cfgKeyId: text(aad.cfgKeyId),
77
+ });
78
+ }
79
+ const wrapInfo = (kind) => encoder.encode(`omo-custody/wrap/v1/${kind}`);
80
+ function encodeBase64Url(bytes) {
81
+ let binary = "";
82
+ for (const byte of bytes)
83
+ binary += String.fromCharCode(byte);
84
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
85
+ }
86
+ /** Strict: no padding, no foreign characters, no non-zero trailing bits, so one byte string has one encoding. */
87
+ export function decodeBase64Url(value) {
88
+ if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1)
89
+ throw new SealedFormatError("open_failed");
90
+ const binary = atob(value.replaceAll("-", "+").replaceAll("_", "/"));
91
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
92
+ if (encodeBase64Url(bytes) !== value)
93
+ throw new SealedFormatError("open_failed");
94
+ return bytes;
95
+ }
96
+ async function aesKey(key, usage) {
97
+ if (key.length !== KEY_BYTES)
98
+ throw new SealedFormatError("invalid_key");
99
+ return crypto.subtle.importKey("raw", Uint8Array.from(key), { name: "AES-GCM" }, false, [usage]);
100
+ }
101
+ async function aes256GcmSeal(key, aad, plaintext) {
102
+ const nonce = crypto.getRandomValues(new Uint8Array(NONCE_BYTES));
103
+ const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce, additionalData: aad, tagLength: TAG_BYTES * 8 }, await aesKey(key, "encrypt"), plaintext);
104
+ return { nonce: encodeBase64Url(nonce), ct: encodeBase64Url(new Uint8Array(ct)) };
105
+ }
106
+ /** @internal Raw AES-256-GCM open, exposed for the external known-answer vector. Any failure is `open_failed`. */
107
+ export async function aes256GcmOpen(key, nonce, aad, ct) {
108
+ const cryptoKey = await aesKey(key, "decrypt");
109
+ if (nonce.length !== NONCE_BYTES || ct.length < TAG_BYTES)
110
+ throw new SealedFormatError("open_failed");
111
+ try {
112
+ const pt = await crypto.subtle.decrypt({
113
+ name: "AES-GCM",
114
+ iv: Uint8Array.from(nonce),
115
+ additionalData: Uint8Array.from(aad),
116
+ tagLength: TAG_BYTES * 8,
117
+ }, cryptoKey, Uint8Array.from(ct));
118
+ return new Uint8Array(pt);
119
+ }
120
+ catch {
121
+ throw new SealedFormatError("open_failed");
122
+ }
123
+ }
124
+ const openBlob = (key, aad, blob) => aes256GcmOpen(key, decodeBase64Url(blob.nonce), aad, decodeBase64Url(blob.ct));
125
+ function requireVersion1(v) {
126
+ if (v !== 1)
127
+ throw new SealedFormatError("unsupported_version");
128
+ }
129
+ /** Seals one secret under the provider DEK. Empty secrets are never sealed. */
130
+ export async function sealField(dek, aad, secret, meta) {
131
+ if (secret.length === 0)
132
+ throw new SealedFormatError("empty_secret");
133
+ const blob = await aes256GcmSeal(dek, fieldAadBytes(aad), encoder.encode(secret));
134
+ return { v: 1, state: "sealed", ...blob, meta };
135
+ }
136
+ /** Opens a sealed field under the AAD the opener rebuilt from the entry and the requested position. */
137
+ export async function openField(dek, aad, field) {
138
+ requireVersion1(field.v);
139
+ if (field.state !== "sealed")
140
+ throw new SealedFormatError("open_failed");
141
+ return openBlob(dek, fieldAadBytes(aad), field);
142
+ }
143
+ export function sealConfig(kCfg, aad, plaintext) {
144
+ return aes256GcmSeal(kCfg, configAadBytes(aad), Uint8Array.from(plaintext));
145
+ }
146
+ /** Opens the config object; its AAD is rebuilt from the envelope's own public fields. */
147
+ export async function openConfig(kCfg, envelope) {
148
+ requireVersion1(envelope.v);
149
+ return openBlob(kCfg, configAadBytes(envelope), envelope.cfg);
150
+ }
151
+ export async function wrapKey(recipientPublicKey, aad, key) {
152
+ if (key.length === 0)
153
+ throw new SealedFormatError("invalid_key");
154
+ const sealed = await hpkeSealBase(recipientPublicKey, { info: wrapInfo(aad.kind), aad: wrapAadBytes(aad), pt: key });
155
+ return { suite: HPKE_SUITE, enc: encodeBase64Url(sealed.enc), ct: encodeBase64Url(sealed.ct) };
156
+ }
157
+ /** Opens a wrap at `position` with the recipient's own key id; nothing read from the wrap enters the AAD. */
158
+ export async function unwrapKey(recipient, position, wrap) {
159
+ if (wrap.suite !== HPKE_SUITE)
160
+ throw new SealedFormatError("unsupported_version");
161
+ const aad = {
162
+ account: position.account,
163
+ kind: position.kind,
164
+ id: position.id,
165
+ recipientKeyId: recipient.keyId,
166
+ };
167
+ return hpkeOpenBase(recipient, {
168
+ enc: decodeBase64Url(wrap.enc),
169
+ ct: decodeBase64Url(wrap.ct),
170
+ info: wrapInfo(aad.kind),
171
+ aad: wrapAadBytes(aad),
172
+ });
173
+ }
174
+ //# sourceMappingURL=sealed-format.js.map
@@ -0,0 +1,35 @@
1
+ import { type CredentialSource, type EpochLeaseToken, type ReleaseScope, type SecretField, type SecretLease, type Unsealer } from "./credentials.ts";
2
+ import type { HpkeWrap, ProviderId } from "./sealed-format.ts";
3
+ export interface ReleasedItemPosition {
4
+ readonly accountId: string;
5
+ readonly providerId: ProviderId;
6
+ readonly entryDigest: string;
7
+ readonly field: SecretField;
8
+ /** The lease this release was made under: the opener rebuilds epoch and placementId from it (r11.2 C.5). */
9
+ readonly lease: EpochLeaseToken;
10
+ }
11
+ export interface SealedSourceDeps {
12
+ readonly accountId: string;
13
+ readonly unsealer: Unsealer;
14
+ readonly lease: () => Promise<EpochLeaseToken>;
15
+ readonly recipientPk: Uint8Array;
16
+ readonly reloadEntryDigest: (providerId: ProviderId) => Promise<string>;
17
+ readonly readHead: () => Promise<string>;
18
+ /** Rejects with the signal's reason once the step aborts. */
19
+ readonly sleep: (ms: number, signal: AbortSignal) => Promise<void>;
20
+ /** The abort signal of the step this release serves; it bounds the churn wait (custody design L844, C.3). */
21
+ readonly stepSignal: (scope: ReleaseScope) => AbortSignal;
22
+ /** Called once per step when the step enters the config-churn wait (C.3), so the caller can show one notice. */
23
+ readonly onChurnNotice: (scope: ReleaseScope) => void;
24
+ /** Opens one released item with this instance's recipient key; its HPKE info/AAD binding is fixed by r11.1. */
25
+ readonly openItem: (item: HpkeWrap, position: ReleasedItemPosition) => Promise<Uint8Array>;
26
+ }
27
+ /** Per egress instance (custody design 3.4): its leases live on the instance, never in module scope. */
28
+ export declare class SealedCredentialSource implements CredentialSource {
29
+ #private;
30
+ constructor(deps: SealedSourceDeps);
31
+ resolve(providerId: ProviderId, entryDigest: string, fields: readonly SecretField[], scope: ReleaseScope): Promise<SecretLease>;
32
+ /** Disposes every live lease, and voids any lease still being resolved when the bump lands. */
33
+ bumpGeneration(): void;
34
+ }
35
+ //# sourceMappingURL=sealed-source.d.ts.map
@@ -0,0 +1,140 @@
1
+ import { CredentialWait, SecretNotLeased, } from "./credentials.js";
2
+ const RELOADS_PER_STEP = 3;
3
+ const STABLE_HEAD_MS = 5000;
4
+ const REDACTED = "<redacted>";
5
+ const RETRYABLE = [
6
+ "stale_snapshot",
7
+ "epoch_mismatch",
8
+ "unresolved_field",
9
+ "wrap_missing",
10
+ "unavailable",
11
+ ];
12
+ class SealedLease {
13
+ #values;
14
+ #onDispose;
15
+ constructor(values, onDispose) {
16
+ this.#values = values;
17
+ this.#onDispose = onDispose;
18
+ }
19
+ value(field) {
20
+ const bytes = this.#values?.get(field);
21
+ if (bytes === undefined)
22
+ throw new SecretNotLeased(field);
23
+ return new TextDecoder().decode(bytes);
24
+ }
25
+ [Symbol.dispose]() {
26
+ const values = this.#values;
27
+ if (values === undefined)
28
+ return;
29
+ this.#values = undefined;
30
+ for (const bytes of values.values())
31
+ bytes.fill(0);
32
+ this.#onDispose(this);
33
+ }
34
+ toJSON() {
35
+ return REDACTED;
36
+ }
37
+ toString() {
38
+ return REDACTED;
39
+ }
40
+ [Symbol.for("nodejs.util.inspect.custom")]() {
41
+ return REDACTED;
42
+ }
43
+ }
44
+ /** Per egress instance (custody design 3.4): its leases live on the instance, never in module scope. */
45
+ export class SealedCredentialSource {
46
+ #deps;
47
+ #live = new Set();
48
+ #generation = 0;
49
+ constructor(deps) {
50
+ this.#deps = deps;
51
+ }
52
+ async resolve(providerId, entryDigest, fields, scope) {
53
+ if (new Set(fields).size !== fields.length)
54
+ throw new TypeError("a sealed release names each field at most once");
55
+ const step = { digest: entryDigest, reloads: 0, noticed: false };
56
+ for (let attempt = 0; attempt < 2; attempt++) {
57
+ const generation = this.#generation;
58
+ const values = await this.#resolveValues(providerId, step, fields, scope);
59
+ if (generation === this.#generation) {
60
+ const lease = new SealedLease(values, (disposed) => this.#live.delete(disposed));
61
+ this.#live.add(lease);
62
+ return lease;
63
+ }
64
+ for (const bytes of values.values())
65
+ bytes.fill(0);
66
+ }
67
+ throw new CredentialWait("config-churn", true);
68
+ }
69
+ /** Disposes every live lease, and voids any lease still being resolved when the bump lands. */
70
+ bumpGeneration() {
71
+ this.#generation++;
72
+ for (const lease of [...this.#live])
73
+ lease[Symbol.dispose]();
74
+ }
75
+ /** One release attempt; `step` carries the reload count, notice latch and digest across bump retries (C.3). */
76
+ async #resolveValues(providerId, step, fields, scope) {
77
+ const signal = this.#deps.stepSignal(scope);
78
+ for (;;) {
79
+ signal.throwIfAborted();
80
+ const lease = await this.#deps.lease();
81
+ const result = await this.#release(providerId, step.digest, fields, scope, lease);
82
+ if (result.ok)
83
+ return this.#open(providerId, step.digest, fields, result.items, lease);
84
+ if (result.code !== "stale_snapshot")
85
+ throw new CredentialWait(result.code, RETRYABLE.some((code) => code === result.code));
86
+ if (step.reloads < RELOADS_PER_STEP) {
87
+ step.reloads++;
88
+ }
89
+ else {
90
+ // config-churn (C.3): every further stale retry re-enters the stable-head wait, never the immediate reloads.
91
+ if (!step.noticed) {
92
+ step.noticed = true;
93
+ this.#deps.onChurnNotice(scope);
94
+ }
95
+ await this.#waitForStableHead(signal);
96
+ }
97
+ step.digest = await this.#deps.reloadEntryDigest(providerId);
98
+ }
99
+ }
100
+ async #release(providerId, entryDigest, fields, scope, lease) {
101
+ return this.#deps.unsealer.release({
102
+ accountId: this.#deps.accountId,
103
+ providerId,
104
+ entryDigest,
105
+ fields,
106
+ lease,
107
+ recipientPk: this.#deps.recipientPk,
108
+ scope,
109
+ });
110
+ }
111
+ /** Returns once the head held still for STABLE_HEAD_MS; rejects when the step aborts first. */
112
+ async #waitForStableHead(signal) {
113
+ let previous = await this.#deps.readHead();
114
+ for (;;) {
115
+ await this.#deps.sleep(STABLE_HEAD_MS, signal);
116
+ const current = await this.#deps.readHead();
117
+ if (current === previous)
118
+ return;
119
+ previous = current;
120
+ }
121
+ }
122
+ async #open(providerId, entryDigest, fields, items, epochLease) {
123
+ if (items.length !== fields.length)
124
+ throw new CredentialWait("denied", false);
125
+ const values = new Map();
126
+ try {
127
+ for (const [index, field] of fields.entries()) {
128
+ const position = { accountId: this.#deps.accountId, providerId, entryDigest, field, lease: epochLease };
129
+ values.set(field, await this.#deps.openItem(items[index], position));
130
+ }
131
+ }
132
+ catch (error) {
133
+ for (const bytes of values.values())
134
+ bytes.fill(0);
135
+ throw error;
136
+ }
137
+ return values;
138
+ }
139
+ }
140
+ //# sourceMappingURL=sealed-source.js.map
@@ -39,7 +39,7 @@ function parseRecord(value) {
39
39
  if (!isRecord(value))
40
40
  return undefined;
41
41
  const raw = value;
42
- if (!Object.keys(raw).every((key) => RECORD_KEYS.includes(key)))
42
+ if (!Object.keys(raw).every((key) => RECORD_KEYS.some((known) => known === key)))
43
43
  return undefined;
44
44
  const { kind, accountId, generation, sigKeyId, sigPkSha256, kemKeyId, kemPkSha256, kmsKeyArns, templateVersion, verifierKeyId, vouchedAt, supersedes, checkedAt, } = raw;
45
45
  if (kind !== "custody" && kind !== "placement-registry")