@dsh-enhanced/assistant-delivery 0.1.24 → 0.1.31

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 (66) hide show
  1. package/README.md +81 -10
  2. package/lib/acceptance.d.ts +46 -0
  3. package/lib/acceptance.d.ts.map +1 -0
  4. package/lib/acceptance.js +2 -0
  5. package/lib/acceptance.js.map +1 -0
  6. package/lib/agent-runtime.d.ts +33 -3
  7. package/lib/agent-runtime.d.ts.map +1 -1
  8. package/lib/agent-runtime.js +835 -62
  9. package/lib/agent-runtime.js.map +1 -1
  10. package/lib/coordinator.d.ts +4 -1
  11. package/lib/coordinator.d.ts.map +1 -1
  12. package/lib/coordinator.js +9 -0
  13. package/lib/coordinator.js.map +1 -1
  14. package/lib/feedback-command.d.ts +21 -1
  15. package/lib/feedback-command.d.ts.map +1 -1
  16. package/lib/feedback-command.js +18 -0
  17. package/lib/feedback-command.js.map +1 -1
  18. package/lib/goal-wake-types.d.ts +93 -0
  19. package/lib/goal-wake-types.d.ts.map +1 -0
  20. package/lib/goal-wake-types.js +70 -0
  21. package/lib/goal-wake-types.js.map +1 -0
  22. package/lib/inbound-images.d.ts.map +1 -1
  23. package/lib/inbound-images.js +11 -9
  24. package/lib/inbound-images.js.map +1 -1
  25. package/lib/index.d.ts +4 -0
  26. package/lib/index.d.ts.map +1 -1
  27. package/lib/index.js +1 -0
  28. package/lib/index.js.map +1 -1
  29. package/lib/native-web-owner.d.ts +78 -0
  30. package/lib/native-web-owner.d.ts.map +1 -0
  31. package/lib/native-web-owner.js +291 -0
  32. package/lib/native-web-owner.js.map +1 -0
  33. package/lib/operator-snapshot.d.ts +80 -0
  34. package/lib/operator-snapshot.d.ts.map +1 -0
  35. package/lib/operator-snapshot.js +935 -0
  36. package/lib/operator-snapshot.js.map +1 -0
  37. package/lib/operator.d.ts +2 -0
  38. package/lib/operator.d.ts.map +1 -1
  39. package/lib/operator.js +10 -0
  40. package/lib/operator.js.map +1 -1
  41. package/lib/service.d.ts +63 -13
  42. package/lib/service.d.ts.map +1 -1
  43. package/lib/service.js +917 -349
  44. package/lib/service.js.map +1 -1
  45. package/lib/session-lease-runtime.d.ts +69 -0
  46. package/lib/session-lease-runtime.d.ts.map +1 -0
  47. package/lib/session-lease-runtime.js +395 -0
  48. package/lib/session-lease-runtime.js.map +1 -0
  49. package/lib/session-lease-types.d.ts +27 -0
  50. package/lib/session-lease-types.d.ts.map +1 -0
  51. package/lib/session-lease-types.js +2 -0
  52. package/lib/session-lease-types.js.map +1 -0
  53. package/lib/sqlite.d.ts +1 -1
  54. package/lib/sqlite.d.ts.map +1 -1
  55. package/lib/sqlite.js +201 -3
  56. package/lib/sqlite.js.map +1 -1
  57. package/lib/store.d.ts +133 -4
  58. package/lib/store.d.ts.map +1 -1
  59. package/lib/store.js +771 -43
  60. package/lib/store.js.map +1 -1
  61. package/lib/types.d.ts +9 -2
  62. package/lib/types.d.ts.map +1 -1
  63. package/lib/types.js.map +1 -1
  64. package/lib/version.d.ts +1 -1
  65. package/lib/version.js +1 -1
  66. package/package.json +61 -44
@@ -0,0 +1,935 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { closeSync, constants, fstatSync, lstatSync, mkdtempSync, openSync, readdirSync, readSync, realpathSync, rmSync, rmdirSync, statSync, unlinkSync, writeSync, } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, isAbsolute, join, basename, resolve } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { DatabaseSync } from 'node:sqlite';
7
+ import { canonicalConversation, canonicalPrincipal } from './canonical.js';
8
+ import { deliverySchemaVersion } from './sqlite.js';
9
+ function unavailable() { return Object.freeze({ status: 'unavailable' }); }
10
+ function mismatch() { return Object.freeze({ status: 'mismatch' }); }
11
+ function policyRef(value) {
12
+ return typeof value === 'string' && value === value.normalize('NFC').trim() && value.length > 0
13
+ && Buffer.byteLength(value, 'utf8') <= 256 && ![...value].some(character => (character.codePointAt(0) ?? 0) <= 0x1f || character.codePointAt(0) === 0x7f);
14
+ }
15
+ function privateDatabase(path) {
16
+ try {
17
+ if (!isAbsolute(path))
18
+ return undefined;
19
+ const entry = lstatSync(path);
20
+ if (!entry.isFile() || entry.isSymbolicLink() || entry.uid !== process.getuid?.() || (entry.mode & 0o077) !== 0)
21
+ return undefined;
22
+ const parent = realpathSync(dirname(path));
23
+ const resolved = realpathSync(path);
24
+ if (resolved !== join(parent, basename(path)))
25
+ return undefined;
26
+ const directory = statSync(parent);
27
+ if (!directory.isDirectory() || directory.uid !== process.getuid?.() || (directory.mode & 0o077) !== 0)
28
+ return undefined;
29
+ return resolved;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ function number(value, min = 0) { return typeof value === 'number' && Number.isSafeInteger(value) && value >= min; }
36
+ function equal(left, right) { return JSON.stringify(left) === JSON.stringify(right); }
37
+ function identifier(value, prefix) { return typeof value === 'string' && new RegExp(`^${prefix}[0-9a-f-]{8,}$`, 'u').test(value); }
38
+ function inspectRow(input, expected, row) {
39
+ if (row.lease_state !== null && row.lease_state !== 'released')
40
+ return Object.freeze({ status: 'busy' });
41
+ const conversation = canonicalConversation(JSON.parse(row.conversation_json));
42
+ const bindingPrincipal = canonicalPrincipal(JSON.parse(row.binding_principal_json));
43
+ const ownerPrincipal = canonicalPrincipal(JSON.parse(row.owner_principal_json));
44
+ const expectedConversation = { channel: 'web', account: expected.account, tenant: expected.tenant, kind: 'dm', chat: input.sessionId };
45
+ if (row.conversation_json !== JSON.stringify(conversation) || row.binding_principal_json !== JSON.stringify(bindingPrincipal) || row.owner_principal_json !== JSON.stringify(ownerPrincipal)
46
+ || !equal(conversation, expectedConversation) || row.session_id !== input.sessionId || !identifier(row.binding_id, 'binding_') || !identifier(row.owner_id, 'principal_')
47
+ || bindingPrincipal.channel !== 'web' || !equal(bindingPrincipal, expected) || !equal(ownerPrincipal, expected)
48
+ || row.binding_status !== 'active' || row.owner_role !== 'owner' || row.owner_status !== 'active' || row.linked_to_id !== null
49
+ || row.workspace !== input.workspace || row.agent_preset !== input.agentPreset || !policyRef(row.policy_ref)
50
+ || ![row.generation, row.binding_version, row.owner_version].every(value => number(value, 1))
51
+ || ![row.binding_created_at, row.binding_updated_at, row.owner_created_at, row.owner_updated_at].every(value => number(value)))
52
+ return mismatch();
53
+ const binding = Object.freeze({ id: row.binding_id, conversation: Object.freeze(conversation), principal: Object.freeze(bindingPrincipal), workspace: row.workspace,
54
+ agentPreset: row.agent_preset, sessionId: row.session_id, generation: row.generation, policyRef: row.policy_ref, status: 'active', createdAt: row.binding_created_at, updatedAt: row.binding_updated_at, version: row.binding_version });
55
+ const owner = Object.freeze({ id: row.owner_id, principal: Object.freeze(ownerPrincipal), role: 'owner', status: 'active', createdAt: row.owner_created_at, updatedAt: row.owner_updated_at, version: row.owner_version });
56
+ return Object.freeze({ status: 'matched', snapshot: Object.freeze({ binding, owner }) });
57
+ }
58
+ /**
59
+ * Opens an already-existing Delivery database read-only. It never invokes the Delivery migrator,
60
+ * creates a database, pairs an owner, reads message content, or acquires a Session lease.
61
+ */
62
+ export function inspectActiveWebOwnerBindingLocally(input) {
63
+ let expected;
64
+ try {
65
+ expected = canonicalPrincipal(input.expectedPrincipal);
66
+ if (expected.channel !== 'web' || !isAbsolute(input.workspace) || typeof input.agentPreset !== 'string' || input.agentPreset.length === 0 || typeof input.sessionId !== 'string' || input.sessionId.length === 0)
67
+ return mismatch();
68
+ }
69
+ catch {
70
+ return mismatch();
71
+ }
72
+ const path = privateDatabase(input.databasePath);
73
+ if (path === undefined)
74
+ return unavailable();
75
+ let database;
76
+ try {
77
+ database = new DatabaseSync(path, { readOnly: true });
78
+ database.exec('PRAGMA query_only = ON; PRAGMA busy_timeout = 100');
79
+ database.exec('BEGIN');
80
+ const version = database.prepare('PRAGMA user_version').get();
81
+ if (version.user_version !== deliverySchemaVersion)
82
+ return unavailable();
83
+ const rows = database.prepare(`
84
+ SELECT binding.id AS binding_id, binding.conversation_json, binding.principal_json AS binding_principal_json,
85
+ binding.workspace, binding.agent_preset, binding.session_id, binding.generation, binding.policy_ref,
86
+ binding.status AS binding_status, binding.created_at AS binding_created_at, binding.updated_at AS binding_updated_at, binding.version AS binding_version,
87
+ owner.id AS owner_id, owner.principal_json AS owner_principal_json, owner.role AS owner_role, owner.status AS owner_status,
88
+ owner.linked_to_id, owner.created_at AS owner_created_at, owner.updated_at AS owner_updated_at, owner.version AS owner_version,
89
+ lease.state AS lease_state
90
+ FROM conversation_bindings AS binding
91
+ JOIN delivery_principals AS owner ON owner.id = binding.principal_id
92
+ LEFT JOIN delivery_session_leases AS lease ON lease.session_id = binding.session_id
93
+ WHERE binding.session_id = ?
94
+ LIMIT 2
95
+ `).all(input.sessionId);
96
+ if (rows.length !== 1)
97
+ return mismatch();
98
+ const row = rows[0];
99
+ return inspectRow(input, expected, row);
100
+ }
101
+ catch (error) {
102
+ if (error instanceof Error && /(?:busy|locked)/iu.test(error.message))
103
+ return Object.freeze({ status: 'busy' });
104
+ return unavailable();
105
+ }
106
+ finally {
107
+ try {
108
+ database?.exec('ROLLBACK');
109
+ }
110
+ catch { }
111
+ try {
112
+ database?.close();
113
+ }
114
+ catch { }
115
+ }
116
+ }
117
+ /** Bounded operator discovery in one read-only snapshot; it creates no sessions or bindings. */
118
+ export function listActiveIdleWebOwnerBindingsLocally(input) {
119
+ let expected;
120
+ try {
121
+ expected = canonicalPrincipal(input.expectedPrincipal);
122
+ if (expected.channel !== 'web' || !isAbsolute(input.workspace) || typeof input.agentPreset !== 'string' || !input.agentPreset)
123
+ return { status: 'unavailable', reason: 'invalid-scope' };
124
+ }
125
+ catch {
126
+ return { status: 'unavailable', reason: 'invalid-scope' };
127
+ }
128
+ const path = privateDatabase(input.databasePath);
129
+ if (!path)
130
+ return { status: 'unavailable', reason: 'snapshot-unavailable' };
131
+ let database;
132
+ try {
133
+ database = new DatabaseSync(path, { readOnly: true });
134
+ database.exec('PRAGMA query_only = ON; PRAGMA busy_timeout = 100; BEGIN');
135
+ if (database.prepare('PRAGMA user_version').get().user_version !== deliverySchemaVersion)
136
+ return { status: 'unavailable', reason: 'snapshot-unavailable' };
137
+ const rows = database.prepare(`
138
+ SELECT binding.id AS binding_id, binding.conversation_json, binding.principal_json AS binding_principal_json,
139
+ binding.workspace, binding.agent_preset, binding.session_id, binding.generation, binding.policy_ref,
140
+ binding.status AS binding_status, binding.created_at AS binding_created_at, binding.updated_at AS binding_updated_at, binding.version AS binding_version,
141
+ owner.id AS owner_id, owner.principal_json AS owner_principal_json, owner.role AS owner_role, owner.status AS owner_status,
142
+ owner.linked_to_id, owner.created_at AS owner_created_at, owner.updated_at AS owner_updated_at, owner.version AS owner_version,
143
+ lease.state AS lease_state
144
+ FROM conversation_bindings AS binding
145
+ JOIN delivery_principals AS owner ON owner.id = binding.principal_id
146
+ LEFT JOIN delivery_session_leases AS lease ON lease.session_id = binding.session_id
147
+ WHERE owner.principal_json = ? AND binding.workspace = ? AND binding.agent_preset = ?
148
+ AND owner.role = 'owner' AND owner.status = 'active' AND binding.status = 'active'
149
+ AND (lease.state IS NULL OR lease.state = 'released')
150
+ ORDER BY binding.session_id LIMIT 101
151
+ `).all(JSON.stringify(expected), input.workspace, input.agentPreset);
152
+ if (rows.length > 100)
153
+ return { status: 'unavailable', reason: 'too-many-sessions' };
154
+ const snapshots = [];
155
+ const seen = new Set();
156
+ for (const row of rows) {
157
+ if (seen.has(row.session_id))
158
+ return { status: 'unavailable', reason: 'snapshot-unavailable' };
159
+ const checked = inspectRow({ ...input, sessionId: row.session_id }, expected, row);
160
+ if (checked.status !== 'matched')
161
+ return { status: 'unavailable', reason: 'snapshot-unavailable' };
162
+ seen.add(row.session_id);
163
+ snapshots.push(checked.snapshot);
164
+ }
165
+ return Object.freeze({ status: 'matched', snapshots: Object.freeze(snapshots) });
166
+ }
167
+ catch {
168
+ return { status: 'unavailable', reason: 'snapshot-unavailable' };
169
+ }
170
+ finally {
171
+ try {
172
+ database?.exec('ROLLBACK');
173
+ }
174
+ catch { }
175
+ try {
176
+ database?.close();
177
+ }
178
+ catch { }
179
+ }
180
+ }
181
+ export const activeLarkOwnerBindingsSnapshotProtocol = 'assistant-delivery/active-lark-owner-bindings-snapshot/v1';
182
+ export class ActiveLarkOwnerBindingsSnapshotError extends Error {
183
+ code;
184
+ constructor(code, message) {
185
+ super(message);
186
+ this.code = code;
187
+ this.name = 'ActiveLarkOwnerBindingsSnapshotError';
188
+ }
189
+ }
190
+ const operatorDigestPattern = /^[a-f0-9]{64}$/u;
191
+ const operatorKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._@/-]{0,255}$/u;
192
+ const operatorMaximumBindings = 100;
193
+ const operatorBufferBytes = 64 * 1024;
194
+ function operatorFail(code, message) {
195
+ throw new ActiveLarkOwnerBindingsSnapshotError(code, `delivery Lark owner snapshot: ${message}`);
196
+ }
197
+ function operatorFreeze(value) {
198
+ if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
199
+ for (const child of Object.values(value))
200
+ operatorFreeze(child);
201
+ Object.freeze(value);
202
+ }
203
+ return value;
204
+ }
205
+ function operatorCanonicalJson(value) {
206
+ if (value === null || typeof value !== 'object')
207
+ return JSON.stringify(value);
208
+ if (Array.isArray(value))
209
+ return `[${value.map(operatorCanonicalJson).join(',')}]`;
210
+ const record = value;
211
+ return `{${Object.keys(record).sort().map(key => `${JSON.stringify(key)}:${operatorCanonicalJson(record[key])}`).join(',')}}`;
212
+ }
213
+ function operatorSha256(value) {
214
+ return createHash('sha256').update(value).digest('hex');
215
+ }
216
+ function operatorSameFile(left, right) {
217
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size
218
+ && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs
219
+ && left.mode === right.mode && left.uid === right.uid && left.nlink === right.nlink;
220
+ }
221
+ function operatorDigestDescriptor(descriptor, size) {
222
+ const hash = createHash('sha256');
223
+ const buffer = Buffer.allocUnsafe(operatorBufferBytes);
224
+ let position = 0n;
225
+ while (position < size) {
226
+ const remaining = size - position;
227
+ const length = Number(remaining > BigInt(buffer.length) ? BigInt(buffer.length) : remaining);
228
+ const count = readSync(descriptor, buffer, 0, length, Number(position));
229
+ if (count <= 0)
230
+ operatorFail('database-drift', 'a storage file changed while it was hashed');
231
+ hash.update(buffer.subarray(0, count));
232
+ position += BigInt(count);
233
+ }
234
+ return hash.digest('hex');
235
+ }
236
+ function operatorFileIdentity(file) {
237
+ return operatorFreeze({
238
+ device: file.stat.dev.toString(), inode: file.stat.ino.toString(), size: file.stat.size.toString(),
239
+ mtimeNs: file.stat.mtimeNs.toString(), digest: file.digest,
240
+ });
241
+ }
242
+ function operatorPinPrivateParent(path) {
243
+ let descriptor;
244
+ try {
245
+ descriptor = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
246
+ }
247
+ catch (error) {
248
+ if (error.code === 'ENOENT') {
249
+ operatorFail('database-missing', 'database parent disappeared before the snapshot');
250
+ }
251
+ operatorFail('unsafe-parent', 'database parent is unavailable');
252
+ }
253
+ try {
254
+ const pathname = lstatSync(path, { bigint: true });
255
+ const metadata = fstatSync(descriptor, { bigint: true });
256
+ const uid = process.getuid?.();
257
+ const physical = realpathSync(path);
258
+ if (!pathname.isDirectory() || pathname.isSymbolicLink() || !metadata.isDirectory()
259
+ || pathname.dev !== metadata.dev || pathname.ino !== metadata.ino || physical !== path
260
+ || uid === undefined || metadata.uid !== BigInt(uid)
261
+ || (metadata.mode & 3647n) !== 0n || (metadata.mode & 320n) !== 320n) {
262
+ operatorFail('unsafe-parent', 'database parent must be a private owner directory without symlinks');
263
+ }
264
+ const pinned = { path, descriptor, device: metadata.dev, inode: metadata.ino, mode: metadata.mode };
265
+ descriptor = undefined;
266
+ return pinned;
267
+ }
268
+ catch (error) {
269
+ if (error instanceof ActiveLarkOwnerBindingsSnapshotError)
270
+ throw error;
271
+ operatorFail('unsafe-parent', 'database parent cannot be resolved');
272
+ }
273
+ finally {
274
+ if (descriptor !== undefined)
275
+ closeSync(descriptor);
276
+ }
277
+ throw new Error('unreachable');
278
+ }
279
+ function operatorPinnedParentMatches(parent) {
280
+ try {
281
+ const pathname = lstatSync(parent.path, { bigint: true });
282
+ const descriptor = fstatSync(parent.descriptor, { bigint: true });
283
+ return pathname.isDirectory() && !pathname.isSymbolicLink() && descriptor.isDirectory()
284
+ && pathname.dev === parent.device && pathname.ino === parent.inode
285
+ && descriptor.dev === parent.device && descriptor.ino === parent.inode
286
+ && descriptor.mode === parent.mode;
287
+ }
288
+ catch {
289
+ return false;
290
+ }
291
+ }
292
+ // Inode binding is enforced through Linux /proc/self/fd. On other platforms the
293
+ // snapshot keeps its historical path-based pinning; CI still exercises that code.
294
+ const operatorInodeBindSupported = process.platform === 'linux';
295
+ const operatorProcSelfFd = '/proc/self/fd';
296
+ /**
297
+ * Enumerate the regular-file inodes held by THIS process, excluding the
298
+ * descriptors the snapshot pinned itself. After the exclusion the only
299
+ * remaining database fds are SQLite's, so a (dev,ino) match proves the
300
+ * connection genuinely opened the pinned inode rather than an attacker's
301
+ * same-name replacement. An attacker in another process cannot plant an fd
302
+ * here because /proc/self/fd never lists another process's descriptors.
303
+ */
304
+ function operatorConnectionInodes(exclude) {
305
+ let entries;
306
+ try {
307
+ entries = readdirSync(operatorProcSelfFd);
308
+ }
309
+ catch {
310
+ operatorFail('database-corrupt', 'cannot inspect the database connection file descriptors');
311
+ }
312
+ const inodes = [];
313
+ for (const entry of entries) {
314
+ const descriptor = Number(entry);
315
+ if (!Number.isInteger(descriptor) || descriptor <= 2 || exclude.has(descriptor))
316
+ continue;
317
+ let stat;
318
+ try {
319
+ stat = fstatSync(descriptor, { bigint: true });
320
+ }
321
+ catch {
322
+ continue;
323
+ } // transient/closed fd
324
+ if (stat.isFile())
325
+ inodes.push({ device: stat.dev, inode: stat.ino });
326
+ }
327
+ return inodes;
328
+ }
329
+ function operatorAssertConnectionBound(files, exclude) {
330
+ const held = operatorConnectionInodes(exclude);
331
+ for (const file of files) {
332
+ if (!held.some(inode => inode.device === file.stat.dev && inode.inode === file.stat.ino)) {
333
+ operatorFail('database-drift', 'database connection is not bound to the pinned inode');
334
+ }
335
+ }
336
+ }
337
+ /** Pin the private mkdtemp copy directory itself so a same-name swap cannot retarget the copies. */
338
+ function operatorPinTempDirectory(path) {
339
+ let descriptor;
340
+ try {
341
+ descriptor = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
342
+ const linked = lstatSync(path, { bigint: true });
343
+ const opened = fstatSync(descriptor, { bigint: true });
344
+ const uid = process.getuid?.();
345
+ if (!linked.isDirectory() || linked.isSymbolicLink() || !opened.isDirectory()
346
+ || linked.dev !== opened.dev || linked.ino !== opened.ino
347
+ || (opened.mode & 4095n) !== 448n
348
+ || uid === undefined || opened.uid !== BigInt(uid)) {
349
+ operatorFail('unsafe-path', 'temporary snapshot directory cannot be pinned');
350
+ }
351
+ const pinned = { path, descriptor, device: opened.dev, inode: opened.ino };
352
+ descriptor = undefined;
353
+ return pinned;
354
+ }
355
+ catch (error) {
356
+ if (error instanceof ActiveLarkOwnerBindingsSnapshotError)
357
+ throw error;
358
+ operatorFail('unsafe-path', 'temporary snapshot directory cannot be pinned');
359
+ }
360
+ finally {
361
+ if (descriptor !== undefined)
362
+ try {
363
+ closeSync(descriptor);
364
+ }
365
+ catch { }
366
+ }
367
+ throw new Error('unreachable');
368
+ }
369
+ /**
370
+ * Materialise one private byte-for-byte copy THROUGH the pinned directory fd
371
+ * (`/proc/self/fd/<dirfd>/<name>`), then reopen it O_NOFOLLOW through the same
372
+ * anchor and keep that descriptor open. Neither the create, the verify, the
373
+ * SQLite open, or the cleanup ever resolve a swappable path component, so a
374
+ * same-name directory swap cannot retarget the replica. The returned file is
375
+ * left open and must be closed by the caller.
376
+ */
377
+ function operatorCopyAndPin(source, targetRef) {
378
+ const buffer = Buffer.allocUnsafe(operatorBufferBytes);
379
+ let destination;
380
+ try {
381
+ destination = openSync(targetRef, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
382
+ let position = 0n;
383
+ while (position < source.stat.size) {
384
+ const remaining = source.stat.size - position;
385
+ const length = Number(remaining > BigInt(buffer.length) ? BigInt(buffer.length) : remaining);
386
+ const count = readSync(source.descriptor, buffer, 0, length, Number(position));
387
+ if (count <= 0)
388
+ operatorFail('database-drift', `${basename(source.path)} changed while it was copied`);
389
+ let written = 0;
390
+ while (written < count)
391
+ written += writeSync(destination, buffer, written, count - written);
392
+ position += BigInt(count);
393
+ }
394
+ }
395
+ catch (error) {
396
+ if (destination !== undefined)
397
+ try {
398
+ closeSync(destination);
399
+ }
400
+ catch { }
401
+ if (error instanceof ActiveLarkOwnerBindingsSnapshotError)
402
+ throw error;
403
+ throw error;
404
+ }
405
+ if (destination !== undefined)
406
+ closeSync(destination);
407
+ const copied = operatorOpenPrivateFile(targetRef, false);
408
+ if (copied.stat.size !== source.stat.size || copied.digest !== source.digest) {
409
+ try {
410
+ closeSync(copied.descriptor);
411
+ }
412
+ catch { }
413
+ operatorFail('database-drift', `${basename(source.path)} copy does not match its pinned source`);
414
+ }
415
+ return copied;
416
+ }
417
+ /**
418
+ * Remove the replica THROUGH the pinned directory fd, unlinking only the fixed
419
+ * names SQLite could create. The directory itself is rmdir'd by path only after
420
+ * the pinned fd proves the path still names the same empty inode; a swapped or
421
+ * non-empty directory is left untouched and the snapshot fails closed rather
422
+ * than recursively deleting an attacker-controlled replacement.
423
+ */
424
+ function operatorRemovePinnedTemp(pinned, names) {
425
+ const anchor = `${operatorProcSelfFd}/${pinned.descriptor}`;
426
+ for (const name of names) {
427
+ try {
428
+ unlinkSync(`${anchor}/${name}`);
429
+ }
430
+ catch (error) {
431
+ if (error.code !== 'ENOENT') {
432
+ operatorFail('database-drift', 'temporary snapshot copy cannot be removed through its pinned directory');
433
+ }
434
+ }
435
+ }
436
+ let entries;
437
+ try {
438
+ entries = readdirSync(anchor);
439
+ }
440
+ catch {
441
+ operatorFail('database-drift', 'temporary snapshot directory became unavailable');
442
+ }
443
+ if (entries.length !== 0)
444
+ operatorFail('database-drift', 'temporary snapshot directory contains unexpected entries');
445
+ const linked = lstatSync(pinned.path, { bigint: true });
446
+ const opened = fstatSync(pinned.descriptor, { bigint: true });
447
+ if (linked.dev !== pinned.device || linked.ino !== pinned.inode
448
+ || opened.dev !== pinned.device || opened.ino !== pinned.inode) {
449
+ operatorFail('database-drift', 'temporary snapshot directory was retargeted before cleanup');
450
+ }
451
+ closeSync(pinned.descriptor);
452
+ try {
453
+ rmdirSync(pinned.path);
454
+ }
455
+ catch (error) {
456
+ if (error.code !== 'ENOENT') {
457
+ operatorFail('database-drift', 'temporary snapshot directory could not be removed');
458
+ }
459
+ }
460
+ }
461
+ function operatorOpenPrivateFile(path, missing) {
462
+ let linked;
463
+ try {
464
+ linked = lstatSync(path, { bigint: true });
465
+ }
466
+ catch (error) {
467
+ if (missing && error.code === 'ENOENT')
468
+ operatorFail('database-missing', 'database does not exist');
469
+ operatorFail('unsafe-path', `${basename(path)} cannot be inspected`);
470
+ }
471
+ const uid = process.getuid?.();
472
+ if (!linked.isFile() || linked.isSymbolicLink() || linked.nlink !== 1n
473
+ || (linked.mode & 4095n) !== 384n
474
+ || (uid !== undefined && linked.uid !== BigInt(uid))
475
+ || linked.size > BigInt(Number.MAX_SAFE_INTEGER)) {
476
+ operatorFail('unsafe-path', `${basename(path)} must be one private owned regular file`);
477
+ }
478
+ let descriptor;
479
+ try {
480
+ descriptor = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
481
+ }
482
+ catch {
483
+ operatorFail('unsafe-path', `${basename(path)} cannot be opened safely`);
484
+ }
485
+ try {
486
+ const opened = fstatSync(descriptor, { bigint: true });
487
+ const after = lstatSync(path, { bigint: true });
488
+ if (!operatorSameFile(linked, opened) || !operatorSameFile(opened, after)) {
489
+ operatorFail('database-drift', `${basename(path)} changed while it was opened`);
490
+ }
491
+ const digest = operatorDigestDescriptor(descriptor, opened.size);
492
+ if (!operatorSameFile(opened, fstatSync(descriptor, { bigint: true }))) {
493
+ operatorFail('database-drift', `${basename(path)} changed while it was hashed`);
494
+ }
495
+ return { path, descriptor, stat: opened, digest };
496
+ }
497
+ catch (error) {
498
+ closeSync(descriptor);
499
+ throw error;
500
+ }
501
+ }
502
+ function operatorOptionalFile(path) {
503
+ try {
504
+ lstatSync(path);
505
+ }
506
+ catch (error) {
507
+ if (error.code === 'ENOENT')
508
+ return null;
509
+ operatorFail('unsafe-path', `${basename(path)} cannot be inspected`);
510
+ }
511
+ return operatorOpenPrivateFile(path, false);
512
+ }
513
+ function operatorPinnedFileIdentityMatches(file) {
514
+ try {
515
+ const pathname = lstatSync(file.path, { bigint: true });
516
+ const opened = fstatSync(file.descriptor, { bigint: true });
517
+ return pathname.isFile() && !pathname.isSymbolicLink()
518
+ && pathname.dev === file.stat.dev && pathname.ino === file.stat.ino
519
+ && opened.dev === file.stat.dev && opened.ino === file.stat.ino;
520
+ }
521
+ catch {
522
+ return false;
523
+ }
524
+ }
525
+ function operatorAssertUnchanged(file) {
526
+ try {
527
+ const linked = lstatSync(file.path, { bigint: true });
528
+ const opened = fstatSync(file.descriptor, { bigint: true });
529
+ if (!operatorSameFile(file.stat, linked) || !operatorSameFile(file.stat, opened)
530
+ || operatorDigestDescriptor(file.descriptor, opened.size) !== file.digest) {
531
+ operatorFail('database-drift', `${basename(file.path)} changed during the snapshot`);
532
+ }
533
+ }
534
+ catch (error) {
535
+ if (error instanceof ActiveLarkOwnerBindingsSnapshotError)
536
+ throw error;
537
+ operatorFail('database-drift', `${basename(file.path)} changed during the snapshot`);
538
+ }
539
+ }
540
+ function operatorAssertSidecar(path, file) {
541
+ if (file !== null)
542
+ return operatorAssertUnchanged(file);
543
+ try {
544
+ lstatSync(path);
545
+ operatorFail('database-drift', `${basename(path)} appeared during the snapshot`);
546
+ }
547
+ catch (error) {
548
+ if (error instanceof ActiveLarkOwnerBindingsSnapshotError)
549
+ throw error;
550
+ if (error.code !== 'ENOENT') {
551
+ operatorFail('database-drift', `${basename(path)} changed during the snapshot`);
552
+ }
553
+ }
554
+ }
555
+ function operatorDatabasePath(path) {
556
+ if (typeof path !== 'string' || !isAbsolute(path) || resolve(path) !== path || path === '/'
557
+ || path !== path.normalize('NFC') || /[\p{Cc}]/u.test(path)) {
558
+ operatorFail('invalid-path', 'database path must be canonical and absolute');
559
+ }
560
+ let physical;
561
+ try {
562
+ physical = realpathSync(path);
563
+ }
564
+ catch (error) {
565
+ if (error.code === 'ENOENT')
566
+ operatorFail('database-missing', 'database does not exist');
567
+ operatorFail('unsafe-path', 'database path cannot be resolved');
568
+ }
569
+ let parentPhysical;
570
+ try {
571
+ parentPhysical = realpathSync(dirname(path));
572
+ }
573
+ catch (error) {
574
+ if (error.code === 'ENOENT')
575
+ operatorFail('database-missing', 'database parent disappeared');
576
+ operatorFail('unsafe-path', 'database parent cannot be resolved');
577
+ }
578
+ if (physical !== path || parentPhysical !== dirname(path)) {
579
+ operatorFail('unsafe-path', 'database path must not traverse a symbolic link');
580
+ }
581
+ return physical;
582
+ }
583
+ function operatorScope(input) {
584
+ const validKey = (value) => typeof value === 'string' && value === value.normalize('NFC').trim()
585
+ && operatorKeyPattern.test(value);
586
+ const validText = (value, maximum) => typeof value === 'string' && value.length > 0
587
+ && value === value.normalize('NFC').trim() && Buffer.byteLength(value, 'utf8') <= maximum
588
+ && !/[\p{Cc}]/u.test(value);
589
+ if (!validKey(input.account) || !validKey(input.tenant)
590
+ || !validText(input.workspace, 4_096) || !isAbsolute(input.workspace) || resolve(input.workspace) !== input.workspace
591
+ || !validText(input.agentPreset, 128))
592
+ operatorFail('invalid-scope', 'query scope is invalid');
593
+ return operatorFreeze({ account: input.account, tenant: input.tenant, workspace: input.workspace, agentPreset: input.agentPreset });
594
+ }
595
+ function operatorCopyFile(source, target) {
596
+ const buffer = Buffer.allocUnsafe(operatorBufferBytes);
597
+ let destination;
598
+ try {
599
+ destination = openSync(target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
600
+ let position = 0n;
601
+ while (position < source.stat.size) {
602
+ const remaining = source.stat.size - position;
603
+ const length = Number(remaining > BigInt(buffer.length) ? BigInt(buffer.length) : remaining);
604
+ const count = readSync(source.descriptor, buffer, 0, length, Number(position));
605
+ if (count <= 0)
606
+ operatorFail('database-drift', `${basename(source.path)} changed while it was copied`);
607
+ let written = 0;
608
+ while (written < count)
609
+ written += writeSync(destination, buffer, written, count - written);
610
+ position += BigInt(count);
611
+ }
612
+ }
613
+ finally {
614
+ if (destination !== undefined)
615
+ closeSync(destination);
616
+ }
617
+ // Close the write window, then re-open the private copy as an independent
618
+ // file and prove its bytes equal the pinned source before it is ever read.
619
+ const copied = operatorOpenPrivateFile(target, false);
620
+ try {
621
+ if (copied.stat.size !== source.stat.size || copied.digest !== source.digest) {
622
+ operatorFail('database-drift', `${basename(source.path)} copy does not match its pinned source`);
623
+ }
624
+ }
625
+ finally {
626
+ closeSync(copied.descriptor);
627
+ }
628
+ }
629
+ function operatorText(value, maximum) {
630
+ return typeof value === 'string' && value.length > 0 && value === value.normalize('NFC').trim()
631
+ && Buffer.byteLength(value, 'utf8') <= maximum && !/[\p{Cc}]/u.test(value);
632
+ }
633
+ function operatorInteger(value, minimum = 0) {
634
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum;
635
+ }
636
+ function operatorBinding(row, scope) {
637
+ let conversation;
638
+ let principal;
639
+ try {
640
+ if (typeof row.conversation_json !== 'string' || typeof row.binding_principal_json !== 'string'
641
+ || typeof row.owner_principal_json !== 'string')
642
+ throw new Error('invalid JSON metadata');
643
+ conversation = canonicalConversation(JSON.parse(row.conversation_json));
644
+ principal = canonicalPrincipal(JSON.parse(row.binding_principal_json));
645
+ const ownerPrincipal = canonicalPrincipal(JSON.parse(row.owner_principal_json));
646
+ if (row.conversation_json !== JSON.stringify(conversation)
647
+ || row.binding_principal_json !== JSON.stringify(principal)
648
+ || row.owner_principal_json !== JSON.stringify(ownerPrincipal)
649
+ || !equal(principal, ownerPrincipal))
650
+ throw new Error('non-canonical identity metadata');
651
+ }
652
+ catch {
653
+ return operatorFail('database-corrupt', 'a matching binding has invalid canonical identity metadata');
654
+ }
655
+ if (conversation.channel !== 'lark' || conversation.account !== scope.account
656
+ || conversation.tenant !== scope.tenant || conversation.kind !== 'dm' || conversation.thread !== undefined
657
+ || principal.channel !== 'lark' || principal.account !== scope.account || principal.tenant !== scope.tenant
658
+ || row.workspace !== scope.workspace || row.agent_preset !== scope.agentPreset
659
+ || row.binding_status !== 'active' || row.owner_role !== 'owner' || row.owner_status !== 'active'
660
+ || row.linked_to_id !== null || !identifier(row.binding_id, 'binding_') || !identifier(row.owner_id, 'principal_')
661
+ || row.principal_id !== row.owner_id || !operatorDigestPattern.test(String(row.conversation_hash))
662
+ || !operatorDigestPattern.test(String(row.owner_key_hash))
663
+ || row.conversation_hash !== operatorSha256(row.conversation_json)
664
+ || row.owner_key_hash !== operatorSha256(row.owner_principal_json)
665
+ || !operatorText(row.session_id, 512) || !operatorText(row.policy_ref, 256)
666
+ || !operatorInteger(row.generation, 1) || !operatorInteger(row.binding_version, 1)
667
+ || !operatorInteger(row.owner_version, 1) || !operatorInteger(row.binding_created_at)
668
+ || !operatorInteger(row.binding_updated_at) || row.binding_updated_at < row.binding_created_at
669
+ || !operatorInteger(row.owner_created_at) || !operatorInteger(row.owner_updated_at)
670
+ || row.owner_updated_at < row.owner_created_at) {
671
+ operatorFail('database-corrupt', 'a matching binding row is inconsistent');
672
+ }
673
+ const owner = { id: row.owner_id, principal, role: 'owner', status: 'active',
674
+ createdAt: row.owner_created_at, updatedAt: row.owner_updated_at, version: row.owner_version };
675
+ return operatorFreeze({
676
+ id: row.binding_id, conversation, principal, workspace: row.workspace, agentPreset: row.agent_preset,
677
+ sessionId: row.session_id, generation: row.generation, policyRef: row.policy_ref, status: 'active',
678
+ createdAt: row.binding_created_at, updatedAt: row.binding_updated_at, version: row.binding_version, owner,
679
+ });
680
+ }
681
+ function operatorClassifySqlite(error) {
682
+ const message = error instanceof Error ? error.message.toLowerCase() : '';
683
+ const code = error.errcode;
684
+ if (code === 5 || code === 6 || /(?:busy|locked)/u.test(message))
685
+ operatorFail('database-busy', 'database is busy');
686
+ operatorFail('database-corrupt', 'database cannot produce a valid snapshot');
687
+ }
688
+ /**
689
+ * Produces one exact, content-free Lark owner-DM inventory without opening a
690
+ * writable connection to the source. A temporary private byte-for-byte view
691
+ * is used when WAL state exists so SQLite cannot update the source WAL index.
692
+ */
693
+ export function inspectActiveLarkOwnerBindingsLocally(input) {
694
+ const scope = operatorScope(input);
695
+ const path = operatorDatabasePath(input.databasePath);
696
+ // Pin the private parent directory for the whole snapshot so an ancestor
697
+ // rename or a same-name directory swap cannot retarget the database path
698
+ // between the path checks and the read.
699
+ const pinnedParent = operatorPinPrivateParent(dirname(path));
700
+ const walPath = `${path}-wal`;
701
+ const shmPath = `${path}-shm`;
702
+ let source;
703
+ let wal = null;
704
+ let shm = null;
705
+ let temporaryPath;
706
+ let temporaryPinned;
707
+ let copyFiles = [];
708
+ let database;
709
+ let transaction = false;
710
+ let deferred;
711
+ let result;
712
+ // Descriptors the snapshot itself holds; these are excluded when proving that
713
+ // SQLite opened the pinned inode, so the connection can never vouch for itself.
714
+ const selfDescriptors = () => new Set([
715
+ pinnedParent.descriptor, source?.descriptor, wal?.descriptor, shm?.descriptor,
716
+ temporaryPinned?.descriptor, ...copyFiles.map(file => file.descriptor),
717
+ ].filter((descriptor) => descriptor !== undefined));
718
+ try {
719
+ try {
720
+ lstatSync(`${path}-journal`);
721
+ operatorFail('database-busy', 'database has a rollback journal');
722
+ }
723
+ catch (error) {
724
+ if (error instanceof ActiveLarkOwnerBindingsSnapshotError)
725
+ throw error;
726
+ if (error.code !== 'ENOENT')
727
+ operatorFail('unsafe-path', 'rollback journal cannot be inspected');
728
+ }
729
+ source = operatorOpenPrivateFile(path, true);
730
+ wal = operatorOptionalFile(walPath);
731
+ shm = operatorOptionalFile(shmPath);
732
+ if ((wal === null) !== (shm === null))
733
+ operatorFail('database-drift', 'WAL sidecar set is incomplete');
734
+ operatorAssertUnchanged(source);
735
+ operatorAssertSidecar(walPath, wal);
736
+ operatorAssertSidecar(shmPath, shm);
737
+ // Files SQLite must prove it is reading. Without WAL that is the pinned
738
+ // source inode; with WAL it is the pinned private replica inodes.
739
+ let bound = [];
740
+ let target;
741
+ if (wal === null) {
742
+ // Open the pinned descriptor itself by inode (Linux) so a same-name swap
743
+ // cannot retarget the connection; immutable=1 forbids WAL/journal access.
744
+ target = operatorInodeBindSupported
745
+ ? `file://${operatorProcSelfFd}/${source.descriptor}?mode=ro&immutable=1`
746
+ : `${pathToFileURL(path).href}?immutable=1`;
747
+ bound = [source];
748
+ }
749
+ else {
750
+ temporaryPath = mkdtempSync(join(tmpdir(), 'delivery-operator-snapshot-'));
751
+ const temporaryMetadata = lstatSync(temporaryPath, { bigint: true });
752
+ const temporaryUid = process.getuid?.();
753
+ if (!temporaryMetadata.isDirectory() || temporaryMetadata.isSymbolicLink()
754
+ || (temporaryMetadata.mode & 4095n) !== 448n
755
+ || (temporaryUid !== undefined && temporaryMetadata.uid !== BigInt(temporaryUid))) {
756
+ operatorFail('unsafe-path', 'temporary snapshot directory is not private');
757
+ }
758
+ if (operatorInodeBindSupported) {
759
+ temporaryPinned = operatorPinTempDirectory(temporaryPath);
760
+ const anchor = `${operatorProcSelfFd}/${temporaryPinned.descriptor}`;
761
+ const mainRef = `${anchor}/delivery.sqlite`;
762
+ const copiedMain = operatorCopyAndPin(source, mainRef);
763
+ let copiedWal;
764
+ try {
765
+ copiedWal = operatorCopyAndPin(wal, `${mainRef}-wal`);
766
+ }
767
+ catch (error) {
768
+ try {
769
+ closeSync(copiedMain.descriptor);
770
+ }
771
+ catch { }
772
+ ;
773
+ throw error;
774
+ }
775
+ copyFiles = Object.freeze([copiedMain, copiedWal]);
776
+ target = mainRef;
777
+ bound = copyFiles;
778
+ }
779
+ else {
780
+ const copy = join(temporaryPath, 'delivery.sqlite');
781
+ operatorCopyFile(source, copy);
782
+ operatorCopyFile(wal, `${copy}-wal`);
783
+ target = copy;
784
+ }
785
+ operatorAssertUnchanged(source);
786
+ operatorAssertSidecar(walPath, wal);
787
+ operatorAssertSidecar(shmPath, shm);
788
+ }
789
+ if (!operatorPinnedFileIdentityMatches(source)) {
790
+ operatorFail('database-drift', 'database path changed before open');
791
+ }
792
+ database = new DatabaseSync(target, { readOnly: true });
793
+ // The WAL/SHM fds are opened lazily on first query, so right after the
794
+ // constructor only the main database inode can be claimed.
795
+ if (operatorInodeBindSupported)
796
+ operatorAssertConnectionBound([bound[0]], selfDescriptors());
797
+ if (!operatorPinnedFileIdentityMatches(source)) {
798
+ operatorFail('database-drift', 'database path changed during open');
799
+ }
800
+ database.exec('PRAGMA query_only = ON; PRAGMA busy_timeout = 0; BEGIN');
801
+ transaction = true;
802
+ const queryOnly = database.prepare('PRAGMA query_only').get();
803
+ if (queryOnly.query_only !== 1)
804
+ operatorFail('database-corrupt', 'read-only guard was not enabled');
805
+ const version = database.prepare('PRAGMA user_version').get().user_version;
806
+ if (version !== deliverySchemaVersion)
807
+ operatorFail('schema-unsupported', `expected schema ${deliverySchemaVersion}`);
808
+ const quick = database.prepare('PRAGMA quick_check').all();
809
+ if (quick.length !== 1 || quick[0]?.quick_check !== 'ok')
810
+ operatorFail('database-corrupt', 'SQLite quick_check failed');
811
+ if (database.prepare('PRAGMA foreign_key_check').all().length !== 0)
812
+ operatorFail('database-corrupt', 'foreign-key integrity check failed');
813
+ const rows = database.prepare(`
814
+ SELECT binding.id AS binding_id, binding.conversation_hash, binding.conversation_json,
815
+ binding.principal_id, binding.principal_json AS binding_principal_json, binding.workspace,
816
+ binding.agent_preset, binding.session_id, binding.generation, binding.policy_ref,
817
+ binding.status AS binding_status, binding.created_at AS binding_created_at,
818
+ binding.updated_at AS binding_updated_at, binding.version AS binding_version,
819
+ owner.id AS owner_id, owner.key_hash AS owner_key_hash, owner.principal_json AS owner_principal_json,
820
+ owner.role AS owner_role, owner.status AS owner_status, owner.linked_to_id,
821
+ owner.created_at AS owner_created_at, owner.updated_at AS owner_updated_at, owner.version AS owner_version,
822
+ NULL AS lease_state
823
+ FROM conversation_bindings AS binding
824
+ JOIN delivery_principals AS owner ON owner.id = binding.principal_id
825
+ WHERE binding.status = 'active' AND owner.role = 'owner' AND owner.status = 'active'
826
+ AND binding.workspace = ? AND binding.agent_preset = ?
827
+ AND json_extract(owner.principal_json, '$.channel') = 'lark'
828
+ AND json_extract(owner.principal_json, '$.account') = ?
829
+ AND json_extract(owner.principal_json, '$.tenant') = ?
830
+ AND json_extract(binding.conversation_json, '$.channel') = 'lark'
831
+ AND json_extract(binding.conversation_json, '$.account') = ?
832
+ AND json_extract(binding.conversation_json, '$.tenant') = ?
833
+ AND json_extract(binding.conversation_json, '$.kind') = 'dm'
834
+ ORDER BY binding.id COLLATE BINARY
835
+ LIMIT ?
836
+ `).all(scope.workspace, scope.agentPreset, scope.account, scope.tenant, scope.account, scope.tenant, operatorMaximumBindings + 1);
837
+ if (rows.length > operatorMaximumBindings)
838
+ operatorFail('too-many-bindings', `more than ${operatorMaximumBindings} bindings match`);
839
+ // The main query has now opened the WAL/SHM fds; re-prove every inode the
840
+ // result depends on is still the pinned one (non-WAL keeps just the source).
841
+ if (operatorInodeBindSupported)
842
+ operatorAssertConnectionBound(bound, selfDescriptors());
843
+ const bindings = operatorFreeze(rows.map(row => operatorBinding(row, scope)));
844
+ database.exec('ROLLBACK');
845
+ transaction = false;
846
+ database.close();
847
+ database = undefined;
848
+ operatorAssertUnchanged(source);
849
+ operatorAssertSidecar(walPath, wal);
850
+ operatorAssertSidecar(shmPath, shm);
851
+ const databaseIdentity = operatorFileIdentity(source);
852
+ const sidecars = operatorFreeze({ wal: wal === null ? null : operatorFileIdentity(wal), shm: shm === null ? null : operatorFileIdentity(shm) });
853
+ const storageDigest = operatorSha256(operatorCanonicalJson({
854
+ protocol: 'assistant-delivery/operator-storage/v1', database: source.digest, wal: wal?.digest ?? null,
855
+ }));
856
+ const unsigned = operatorFreeze({
857
+ protocol: activeLarkOwnerBindingsSnapshotProtocol, schemaVersion: deliverySchemaVersion, scope,
858
+ database: databaseIdentity, sidecars, storageDigest, bindings,
859
+ });
860
+ result = operatorFreeze({ ...unsigned, snapshotDigest: operatorSha256(operatorCanonicalJson(unsigned)) });
861
+ }
862
+ catch (error) {
863
+ deferred = error;
864
+ }
865
+ finally {
866
+ if (transaction)
867
+ try {
868
+ database?.exec('ROLLBACK');
869
+ }
870
+ catch (error) {
871
+ deferred ??= error;
872
+ }
873
+ try {
874
+ database?.close();
875
+ }
876
+ catch (error) {
877
+ deferred ??= error;
878
+ }
879
+ if (source !== undefined) {
880
+ try {
881
+ operatorAssertUnchanged(source);
882
+ operatorAssertSidecar(walPath, wal);
883
+ operatorAssertSidecar(shmPath, shm);
884
+ }
885
+ catch (error) {
886
+ deferred ??= error;
887
+ }
888
+ }
889
+ if (!operatorPinnedParentMatches(pinnedParent)) {
890
+ deferred = new ActiveLarkOwnerBindingsSnapshotError('unsafe-parent', 'delivery Lark owner snapshot: database parent changed while it was inspected');
891
+ }
892
+ for (const file of [shm, wal, source])
893
+ if (file !== null && file !== undefined) {
894
+ try {
895
+ closeSync(file.descriptor);
896
+ }
897
+ catch { }
898
+ }
899
+ for (const file of copyFiles) {
900
+ try {
901
+ closeSync(file.descriptor);
902
+ }
903
+ catch { }
904
+ }
905
+ if (temporaryPinned !== undefined) {
906
+ try {
907
+ operatorRemovePinnedTemp(temporaryPinned, ['delivery.sqlite', 'delivery.sqlite-wal', 'delivery.sqlite-shm']);
908
+ }
909
+ catch (error) {
910
+ deferred ??= error;
911
+ }
912
+ }
913
+ else if (temporaryPath !== undefined) {
914
+ try {
915
+ rmSync(temporaryPath, { recursive: true, force: true });
916
+ }
917
+ catch (error) {
918
+ deferred ??= error;
919
+ }
920
+ }
921
+ try {
922
+ closeSync(pinnedParent.descriptor);
923
+ }
924
+ catch (error) {
925
+ deferred ??= error;
926
+ }
927
+ }
928
+ if (deferred !== undefined) {
929
+ if (deferred instanceof ActiveLarkOwnerBindingsSnapshotError)
930
+ throw deferred;
931
+ operatorClassifySqlite(deferred);
932
+ }
933
+ return result;
934
+ }
935
+ //# sourceMappingURL=operator-snapshot.js.map