@enbox/agent 0.6.1 → 0.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 (38) hide show
  1. package/dist/browser.mjs +7 -7
  2. package/dist/browser.mjs.map +3 -3
  3. package/dist/esm/hd-identity-vault.js +49 -0
  4. package/dist/esm/hd-identity-vault.js.map +1 -1
  5. package/dist/esm/index.js +1 -0
  6. package/dist/esm/index.js.map +1 -1
  7. package/dist/esm/store-data.js +7 -1
  8. package/dist/esm/store-data.js.map +1 -1
  9. package/dist/esm/sync-closure-resolver.js +48 -26
  10. package/dist/esm/sync-closure-resolver.js.map +1 -1
  11. package/dist/esm/sync-closure-types.js +2 -1
  12. package/dist/esm/sync-closure-types.js.map +1 -1
  13. package/dist/esm/sync-engine-level.js +3 -1
  14. package/dist/esm/sync-engine-level.js.map +1 -1
  15. package/dist/esm/sync-messages.js +23 -9
  16. package/dist/esm/sync-messages.js.map +1 -1
  17. package/dist/types/hd-identity-vault.d.ts +26 -0
  18. package/dist/types/hd-identity-vault.d.ts.map +1 -1
  19. package/dist/types/index.d.ts +1 -0
  20. package/dist/types/index.d.ts.map +1 -1
  21. package/dist/types/store-data.d.ts.map +1 -1
  22. package/dist/types/sync-closure-resolver.d.ts.map +1 -1
  23. package/dist/types/sync-closure-types.d.ts +20 -1
  24. package/dist/types/sync-closure-types.d.ts.map +1 -1
  25. package/dist/types/sync-engine-level.d.ts.map +1 -1
  26. package/dist/types/sync-messages.d.ts +6 -2
  27. package/dist/types/sync-messages.d.ts.map +1 -1
  28. package/dist/types/types/identity-vault.d.ts +20 -0
  29. package/dist/types/types/identity-vault.d.ts.map +1 -1
  30. package/package.json +1 -1
  31. package/src/hd-identity-vault.ts +50 -0
  32. package/src/index.ts +1 -0
  33. package/src/store-data.ts +7 -1
  34. package/src/sync-closure-resolver.ts +49 -25
  35. package/src/sync-closure-types.ts +29 -6
  36. package/src/sync-engine-level.ts +3 -1
  37. package/src/sync-messages.ts +26 -11
  38. package/src/types/identity-vault.ts +18 -0
@@ -13,9 +13,13 @@ export type SyncMessageEntry = {
13
13
  * 202: message was successfully written to the remote DWN
14
14
  * 204: an initial write message was written without any data
15
15
  * 409: message was already present on the remote DWN
16
- * RecordsDelete + 404: the initial write was not found or already deleted
16
+ *
17
+ * When the *pushed* message is known (e.g. during push-sync), pass it as the
18
+ * second argument so that RecordsDelete + 404 ("initial write was not found or
19
+ * already deleted") can be detected. The DWN's 404 reply omits `entry`, so
20
+ * checking `reply.entry` alone is insufficient.
17
21
  */
18
- export declare function syncMessageReplyIsSuccessful(reply: UnionMessageReply): boolean;
22
+ export declare function syncMessageReplyIsSuccessful(reply: UnionMessageReply, pushedMessage?: GenericMessage): boolean;
19
23
  /**
20
24
  * Determines whether a failed push reply represents a permanent failure that
21
25
  * should NOT be retried. Permanent failures include protocol violations (400),
@@ -1 +1 @@
1
- {"version":3,"file":"sync-messages.d.ts","sourceRoot":"","sources":["../../src/sync-messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-messages.d.ts","sourceRoot":"","sources":["../../src/sync-messages.ts"],"names":[],"mappings":"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"}
@@ -111,6 +111,26 @@ export interface IdentityVault<T extends Record<string, any> = {
111
111
  unlock(params: {
112
112
  password: string;
113
113
  }): Promise<void>;
114
+ /**
115
+ * Encrypts arbitrary data using the vault's content encryption key.
116
+ * The vault must be unlocked.
117
+ *
118
+ * @returns A compact JWE string that can be safely stored in untrusted storage.
119
+ * @throws An error if the vault is locked.
120
+ */
121
+ encryptData(params: {
122
+ plaintext: Uint8Array;
123
+ }): Promise<string>;
124
+ /**
125
+ * Decrypts data that was previously encrypted with {@link encryptData}.
126
+ * The vault must be unlocked.
127
+ *
128
+ * @returns The original plaintext bytes.
129
+ * @throws An error if the vault is locked or the JWE is invalid.
130
+ */
131
+ decryptData(params: {
132
+ jwe: string;
133
+ }): Promise<Uint8Array>;
114
134
  }
115
135
  export type IdentityVaultStatus = {
116
136
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"identity-vault.d.ts","sourceRoot":"","sources":["../../../src/types/identity-vault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IAEpB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IAEb,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IAEZ,mFAAmF;IACnF,oBAAoB,EAAE,MAAM,CAAC;IAE7B,qFAAqF;IACrF,MAAM,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,+FAA+F;IAC/F,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAAE,gBAAgB,EAAE,GAAG,CAAA;CAAE;IACtF;;;;;OAKG;IACH,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEvC;;;;;;;OAOG;IACH,cAAc,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpF;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC,CAAA;IAE5B;;;OAGG;IACH,SAAS,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAEzC;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAEzE;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAElC;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC;IAEpB;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,mBAAmB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElF;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B;;OAEG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC"}
1
+ {"version":3,"file":"identity-vault.d.ts","sourceRoot":"","sources":["../../../src/types/identity-vault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IAEpB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IAEb,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IAEZ,mFAAmF;IACnF,oBAAoB,EAAE,MAAM,CAAC;IAE7B,qFAAqF;IACrF,MAAM,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,+FAA+F;IAC/F,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAAE,gBAAgB,EAAE,GAAG,CAAA;CAAE;IACtF;;;;;OAKG;IACH,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEvC;;;;;;;OAOG;IACH,cAAc,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpF;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC,CAAA;IAE5B;;;OAGG;IACH,SAAS,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAEzC;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAEzE;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAElC;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC;IAEpB;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,mBAAmB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElF;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,UAAU,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhE;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC3D;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B;;OAEG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enbox/agent",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "main": "./dist/esm/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -792,6 +792,56 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string
792
792
  }
793
793
  }
794
794
 
795
+ /**
796
+ * Encrypts arbitrary data using the vault's content encryption key (CEK).
797
+ *
798
+ * The vault must be unlocked. The returned compact JWE string can be safely
799
+ * stored in untrusted storage (e.g. `localStorage`). Only the vault password
800
+ * can decrypt the data.
801
+ *
802
+ * @param params.plaintext - The data to encrypt.
803
+ * @returns A compact JWE string.
804
+ * @throws If the vault is locked.
805
+ */
806
+ public async encryptData({ plaintext }: { plaintext: Uint8Array }): Promise<string> {
807
+ if (this.isLocked() || !this._contentEncryptionKey) {
808
+ throw new Error('HdIdentityVault: Cannot encrypt data — vault is locked.');
809
+ }
810
+
811
+ return CompactJwe.encrypt({
812
+ key : this._contentEncryptionKey,
813
+ plaintext,
814
+ protectedHeader : { alg: 'dir', enc: 'A256GCM' },
815
+ crypto : this.crypto,
816
+ keyManager : new LocalKeyManager(),
817
+ });
818
+ }
819
+
820
+ /**
821
+ * Decrypts data that was previously encrypted with {@link encryptData}.
822
+ *
823
+ * The vault must be unlocked.
824
+ *
825
+ * @param params.jwe - The compact JWE string to decrypt.
826
+ * @returns The original plaintext bytes.
827
+ * @throws If the vault is locked or the JWE is invalid.
828
+ */
829
+ public async decryptData({ jwe }: { jwe: string }): Promise<Uint8Array> {
830
+ if (this.isLocked() || !this._contentEncryptionKey) {
831
+ throw new Error('HdIdentityVault: Cannot decrypt data — vault is locked.');
832
+ }
833
+
834
+ const { plaintext } = await CompactJwe.decrypt({
835
+ jwe,
836
+ key : this._contentEncryptionKey,
837
+ crypto : this.crypto,
838
+ keyManager : new LocalKeyManager(),
839
+ options : { minP2cCount: 1 },
840
+ });
841
+
842
+ return plaintext;
843
+ }
844
+
795
845
  /**
796
846
  * Retrieves the Decentralized Identifier (DID) associated with the identity vault from the vault
797
847
  * store.
package/src/index.ts CHANGED
@@ -41,3 +41,4 @@ export * from './test-harness.js';
41
41
  export * from './utils.js';
42
42
  export * from './enbox-connect-protocol.js';
43
43
  export * from './enbox-user-agent.js';
44
+ export { KeyDeliveryProtocolDefinition } from './store-data-protocols.js';
package/src/store-data.ts CHANGED
@@ -291,7 +291,13 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
291
291
 
292
292
  // Read the record from the store. If encryption is active for this tenant,
293
293
  // the agent's auto-decryption pipeline handles ECIES key unwrapping and AES decryption.
294
- const encryptionActive = this.isEncryptionActive(tenantDid);
294
+ //
295
+ // When the protocol definition declares `encryptionRequired: true`, always
296
+ // request decryption — even if `initialize()` has not been called for this
297
+ // tenant yet. This covers delegate sessions where key records arrive via
298
+ // sync (`processRawMessage`) rather than `set()`, leaving the per-tenant
299
+ // `_tenantEncryptionActive` cache cold after an agent restart.
300
+ const encryptionActive = this.encryptionRequired || this.isEncryptionActive(tenantDid);
295
301
  const { reply: readReply } = await agent.dwn.processRequest({
296
302
  author : tenantDid,
297
303
  target : tenantDid,
@@ -200,6 +200,7 @@ function getRuleSetAtProtocolPath(
200
200
  function extractProtocolAwareDeps(
201
201
  message: GenericMessage,
202
202
  protocolDef: any,
203
+ isDelegateSession?: boolean,
203
204
  ): ClosureDependencyEdge[] {
204
205
  const desc = message.descriptor as Record<string, unknown>;
205
206
  if (desc.interface !== 'Records') { return []; }
@@ -246,15 +247,39 @@ function extractProtocolAwareDeps(
246
247
  identifierType : 'protocol',
247
248
  });
248
249
 
249
- // Level 2: key-delivery protocol record.
250
- // The key-delivery protocol must be installed locally.
251
- const keyDeliveryProtocol = 'https://identity.foundation/protocols/key-delivery';
252
- edges.push({
253
- dependencyClass : 5,
254
- label : 'keyDeliveryProtocol',
255
- identifier : keyDeliveryProtocol,
256
- identifierType : 'protocol',
257
- });
250
+ // Determine whether this record uses multi-party (ProtocolContext)
251
+ // encryption before emitting key-delivery edges.
252
+ const contextId = (message as any).contextId as string | undefined;
253
+ let isMultiParty = false;
254
+ if (contextId) {
255
+ const rootProtocolPath = protocolPath.split('/')[0];
256
+ isMultiParty = isMultiPartyContext(protocolDef, rootProtocolPath);
257
+ }
258
+
259
+ // Level 2: key-delivery protocol ProtocolsConfigure.
260
+ //
261
+ // For **owner** sessions this is always emitted — the DWN needs the
262
+ // protocol installed to authorize contextKey record access.
263
+ //
264
+ // For **delegate** sessions:
265
+ // - Single-party: the delegate decrypts via pre-derived
266
+ // `delegateDecryptionKeys` (ProtocolPath keys). No key-delivery
267
+ // records are involved. Edge is suppressed.
268
+ // - Multi-party: on in-memory cache miss the runtime falls back to
269
+ // `fetchCrossDeviceContextKey()` (dwn-encryption.ts:453,
270
+ // dwn-key-delivery.ts:268) which does a local RecordsQuery +
271
+ // RecordsRead against the owner's tenant. That authorization path
272
+ // requires the key-delivery ProtocolsConfigure. Edge is emitted.
273
+ const suppressKeyDelivery = isDelegateSession && !isMultiParty;
274
+ if (!suppressKeyDelivery) {
275
+ const keyDeliveryProtocol = 'https://identity.foundation/protocols/key-delivery';
276
+ edges.push({
277
+ dependencyClass : 5,
278
+ label : 'keyDeliveryProtocol',
279
+ identifier : keyDeliveryProtocol,
280
+ identifierType : 'protocol',
281
+ });
282
+ }
258
283
 
259
284
  // Level 3: Context key enforcement for multi-party contexts.
260
285
  // Uses isMultiPartyContext() from protocol-utils to determine whether the
@@ -264,21 +289,20 @@ function extractProtocolAwareDeps(
264
289
  //
265
290
  // Single-party contexts use ProtocolPath encryption (owner-only) and do
266
291
  // NOT need a context key — the edge is skipped entirely.
267
- const contextId = (message as any).contextId as string | undefined;
268
- if (contextId) {
269
- const rootProtocolPath = protocolPath.split('/')[0];
270
- const multiParty = isMultiPartyContext(protocolDef, rootProtocolPath);
271
- if (multiParty) {
272
- const rootContextId = contextId.split('/')[0];
273
- // Separator is '|' (not ':') because protocol URIs contain '://'
274
- // which would break indexOf(':') parsing in resolveDependency.
275
- edges.push({
276
- dependencyClass : 5,
277
- label : 'contextKeyRecord',
278
- identifier : `${desc.protocol}|${rootContextId}`,
279
- identifierType : 'messageCid',
280
- });
281
- }
292
+ //
293
+ // For delegates this edge is always emitted when applicable: the
294
+ // runtime's cross-device fallback still needs the contextKey record
295
+ // locally (on cache miss).
296
+ if (isMultiParty && contextId) {
297
+ const rootContextId = contextId.split('/')[0];
298
+ // Separator is '|' (not ':') because protocol URIs contain '://'
299
+ // which would break indexOf(':') parsing in resolveDependency.
300
+ edges.push({
301
+ dependencyClass : 5,
302
+ label : 'contextKeyRecord',
303
+ identifier : `${desc.protocol}|${rootContextId}`,
304
+ identifierType : 'messageCid',
305
+ });
282
306
  }
283
307
  }
284
308
  }
@@ -496,7 +520,7 @@ export async function evaluateClosure(
496
520
  const cachedProtocolMsg = context.protocolCache.get(currentProtocol);
497
521
  const protocolDef = (cachedProtocolMsg?.descriptor as any)?.definition;
498
522
  if (protocolDef) {
499
- const protoAwareEdges = extractProtocolAwareDeps(current, protocolDef);
523
+ const protoAwareEdges = extractProtocolAwareDeps(current, protocolDef, context.isDelegateSession);
500
524
  const protoResult = await resolveEdges(
501
525
  protoAwareEdges, allEdges, messageStore, context, visited, queue, rootCid, currentDepth
502
526
  );
@@ -122,19 +122,42 @@ export type ClosureEvaluationContext = {
122
122
  missingDeps: Set<string>;
123
123
  /** Maximum traversal depth. Default 32. */
124
124
  maxDepth: number;
125
+
126
+ /**
127
+ * When `true`, the sync link is operating as a delegated session.
128
+ *
129
+ * Affects class 5 (encryption) dependency extraction:
130
+ *
131
+ * - **Single-party** protocols: the delegate decrypts via pre-derived
132
+ * `delegateDecryptionKeys` (ProtocolPath keys). No key-delivery
133
+ * records are involved, so the `keyDeliveryProtocol` edge is
134
+ * suppressed entirely in `extractProtocolAwareDeps()`.
135
+ *
136
+ * - **Multi-party** protocols: on in-memory cache miss the runtime
137
+ * falls back to `fetchCrossDeviceContextKey()` (see
138
+ * `dwn-encryption.ts:453`, `dwn-key-delivery.ts:268`) which queries
139
+ * the local DWN. Both `keyDeliveryProtocol` and `contextKeyRecord`
140
+ * are emitted and resolved normally.
141
+ */
142
+ isDelegateSession?: boolean;
125
143
  };
126
144
 
127
145
  /**
128
146
  * Create a fresh evaluation context for a batch of closure evaluations.
129
147
  */
130
- export function createClosureContext(tenantDid: string, maxDepth?: number): ClosureEvaluationContext {
148
+ export function createClosureContext(
149
+ tenantDid: string,
150
+ maxDepth?: number,
151
+ options?: { isDelegateSession?: boolean },
152
+ ): ClosureEvaluationContext {
131
153
  return {
132
154
  tenantDid,
133
- protocolCache : new Map(),
134
- grantCache : new Map(),
135
- satisfiedDeps : new Set(),
136
- missingDeps : new Set(),
137
- maxDepth : maxDepth ?? 32,
155
+ protocolCache : new Map(),
156
+ grantCache : new Map(),
157
+ satisfiedDeps : new Set(),
158
+ missingDeps : new Set(),
159
+ maxDepth : maxDepth ?? 32,
160
+ isDelegateSession : options?.isDelegateSession,
138
161
  };
139
162
  }
140
163
 
@@ -1476,7 +1476,9 @@ export class SyncEngineLevel implements SyncEngine {
1476
1476
  const messageStore = this.agent.dwn.node.storage.messageStore;
1477
1477
  let closureCtx = this._closureContexts.get(did);
1478
1478
  if (!closureCtx) {
1479
- closureCtx = createClosureContext(did);
1479
+ closureCtx = createClosureContext(did, undefined, {
1480
+ isDelegateSession: !!delegateDid,
1481
+ });
1480
1482
  this._closureContexts.set(did, closureCtx);
1481
1483
  }
1482
1484
 
@@ -25,17 +25,32 @@ export type SyncMessageEntry = {
25
25
  * 202: message was successfully written to the remote DWN
26
26
  * 204: an initial write message was written without any data
27
27
  * 409: message was already present on the remote DWN
28
- * RecordsDelete + 404: the initial write was not found or already deleted
28
+ *
29
+ * When the *pushed* message is known (e.g. during push-sync), pass it as the
30
+ * second argument so that RecordsDelete + 404 ("initial write was not found or
31
+ * already deleted") can be detected. The DWN's 404 reply omits `entry`, so
32
+ * checking `reply.entry` alone is insufficient.
29
33
  */
30
- export function syncMessageReplyIsSuccessful(reply: UnionMessageReply): boolean {
31
- return reply.status.code === 202 ||
32
- reply.status.code === 204 ||
33
- reply.status.code === 409 ||
34
- (
35
- reply.entry?.message.descriptor.interface === DwnInterfaceName.Records &&
36
- reply.entry?.message.descriptor.method === DwnMethodName.Delete &&
37
- reply.status.code === 404
38
- );
34
+ export function syncMessageReplyIsSuccessful(reply: UnionMessageReply, pushedMessage?: GenericMessage): boolean {
35
+ if (reply.status.code === 202 || reply.status.code === 204 || reply.status.code === 409) {
36
+ return true;
37
+ }
38
+
39
+ if (reply.status.code === 404) {
40
+ // Check the pushed message first (always available during push-sync).
41
+ if (pushedMessage?.descriptor.interface === DwnInterfaceName.Records &&
42
+ pushedMessage?.descriptor.method === DwnMethodName.Delete) {
43
+ return true;
44
+ }
45
+
46
+ // Fallback: check the reply entry (for callers that don't pass the pushed message).
47
+ if (reply.entry?.message.descriptor.interface === DwnInterfaceName.Records &&
48
+ reply.entry?.message.descriptor.method === DwnMethodName.Delete) {
49
+ return true;
50
+ }
51
+ }
52
+
53
+ return false;
39
54
  }
40
55
 
41
56
  /**
@@ -364,7 +379,7 @@ export async function pushMessages({ did, dwnUrl, delegateDid, protocol, message
364
379
  message : entry.message
365
380
  });
366
381
 
367
- if (syncMessageReplyIsSuccessful(reply)) {
382
+ if (syncMessageReplyIsSuccessful(reply, entry.message)) {
368
383
  succeeded.push(cid);
369
384
  } else if (isPermanentPushFailure(reply)) {
370
385
  // Permanent failures (400/401/403) will never succeed — do NOT retry.
@@ -117,6 +117,24 @@ export interface IdentityVault<T extends Record<string, any> = { InitializeResul
117
117
  * @throws An error if the password is incorrect.
118
118
  */
119
119
  unlock(params: { password: string }): Promise<void>;
120
+
121
+ /**
122
+ * Encrypts arbitrary data using the vault's content encryption key.
123
+ * The vault must be unlocked.
124
+ *
125
+ * @returns A compact JWE string that can be safely stored in untrusted storage.
126
+ * @throws An error if the vault is locked.
127
+ */
128
+ encryptData(params: { plaintext: Uint8Array }): Promise<string>;
129
+
130
+ /**
131
+ * Decrypts data that was previously encrypted with {@link encryptData}.
132
+ * The vault must be unlocked.
133
+ *
134
+ * @returns The original plaintext bytes.
135
+ * @throws An error if the vault is locked or the JWE is invalid.
136
+ */
137
+ decryptData(params: { jwe: string }): Promise<Uint8Array>;
120
138
  }
121
139
 
122
140
  export type IdentityVaultStatus = {