@canonmsg/agent-sdk 12.5.1 → 12.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +34 -3
  2. package/dist/chunks/{chunk-TFSURFF6.js → chunk-5J4YPLNW.js} +349 -1684
  3. package/dist/chunks/{chunk-3KFVBOVI.js → chunk-DAJ7L7TN.js} +751 -371
  4. package/dist/chunks/chunk-FVYKLH6I.js +1475 -0
  5. package/dist/chunks/chunk-HH7KR22P.js +406 -0
  6. package/dist/chunks/chunk-QK37HAHC.js +118 -0
  7. package/dist/chunks/{chunk-C2WJT3VX.js → chunk-S7BV74DN.js} +719 -813
  8. package/dist/chunks/{mls-crypto-Y44BOSEM.js → mls-crypto-4DZT2UFH.js} +2 -1
  9. package/dist/cli.js +67 -25
  10. package/dist/drivers/acp.d.ts +56 -0
  11. package/dist/drivers/codex.d.ts +1 -2
  12. package/dist/drivers/subprocess.d.ts +35 -0
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.js +12 -3
  15. package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +2 -2
  16. package/dist/internal/backend-contracts/index.d.ts +1 -0
  17. package/dist/internal/backend-contracts/loggedOutSessions.d.ts +7 -0
  18. package/dist/internal/backend-contracts/mlsDevCredential.d.ts +3 -0
  19. package/dist/internal/endpoint/engine.d.ts +3 -0
  20. package/dist/internal/endpoint/mls-dev-client.d.ts +2 -0
  21. package/dist/internal/endpoint/mls-dev-text.d.ts +3 -0
  22. package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
  23. package/dist/internal/endpoint/node.d.ts +1 -0
  24. package/dist/media.js +4 -2
  25. package/dist/mls/darwin-arm64/canon-mls-custody +0 -0
  26. package/dist/mls/linux-arm64/canon-mls-custody +0 -0
  27. package/dist/mls/linux-x64/canon-mls-custody +0 -0
  28. package/dist/mls/wasm/canon_mls_wasm.js +363 -0
  29. package/dist/mls/wasm/canon_mls_wasm_bg.wasm +0 -0
  30. package/dist/mls-wasm-host.js +258 -0
  31. package/dist/runner/mls-bundled.d.ts +15 -2
  32. package/dist/runner/mls-managed.d.ts +6 -3
  33. package/dist/runner/types.d.ts +6 -1
  34. package/package.json +1 -1
@@ -1,18 +1,20 @@
1
1
  import {
2
- CANON_CLIENT_HEADER,
2
+ EndpointError,
3
+ copyJson,
4
+ createEndpointEventHub,
5
+ createHttpEndpointTransport,
6
+ createNodeEndpointStore,
7
+ validateEndpointContentBoundary
8
+ } from "./chunk-FVYKLH6I.js";
9
+ import {
3
10
  CANON_ENDPOINT_PROTOCOL,
4
11
  CANON_VERB_WIRE_SCHEMA_VERSION,
5
- CLIENT_UPDATE_REQUIRED,
6
12
  ENDPOINT_ACTION_PATTERN,
7
13
  ENDPOINT_ID_PATTERN,
8
14
  MAX_PUBLIC_RUNTIME_COMMANDS,
9
- MLS_DEV_CREDENTIAL_MAX_BYTES,
10
- MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES,
11
15
  MLS_DEV_DISCLOSED_PROCESSING_FIELDS,
12
- MLS_DEV_ENROLLMENT_MAX_BYTES,
13
16
  MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW,
14
17
  MLS_DEV_INTERACTION_LIMITS,
15
- MLS_DEV_KEY_PACKAGES_MAX_BYTES,
16
18
  MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS,
17
19
  MLS_DEV_TEXT_PROTOCOL_V3,
18
20
  MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS,
@@ -22,8 +24,6 @@ import {
22
24
  __name,
23
25
  assertMlsDevInteractionPayloadBinding,
24
26
  canonicalEndpointJson,
25
- classifyTransferHttpFailure,
26
- classifyTransferReceipt,
27
27
  createMlsDevCredentialSigningInput,
28
28
  decodePreparedJsonContent,
29
29
  describeAttachment,
@@ -32,14 +32,11 @@ import {
32
32
  encodeMlsPrincipalAliasInput,
33
33
  endpointContentBoundary,
34
34
  endpointOperationRequestRetention,
35
- formatCanonClientHeader,
36
35
  formatMembershipChangeNotice,
37
36
  getCanonEnvironmentContract,
38
37
  isCanonMediaOrigin,
39
38
  isCanonUploadUrl,
40
- isEndpointOperationReceipt,
41
39
  isMlsDevInteractionAnswerAllowed,
42
- isMlsDevReceiptClockSkew,
43
40
  isMlsDevTextProtocol,
44
41
  isMlsEnvironmentId,
45
42
  isMlsPrincipalAlias,
@@ -61,7 +58,6 @@ import {
61
58
  parseMlsDevCredentialReconciliationComplete,
62
59
  parseMlsDevCredentialReconciliationReceipt,
63
60
  parseMlsDevCredentialRevocationReceipt,
64
- parseMlsDevCredentialRevoke,
65
61
  parseMlsDevCredentialToken,
66
62
  parseMlsDevDeviceBinding,
67
63
  parseMlsDevDeviceCommand,
@@ -110,1428 +106,44 @@ import {
110
106
  readMembershipChangeMetadata,
111
107
  resolveAllowedUploadMimeType,
112
108
  sanitizeDownloadFileName
113
- } from "./chunk-C2WJT3VX.js";
109
+ } from "./chunk-S7BV74DN.js";
114
110
 
115
111
  // src/media.ts
116
112
  import { createWriteStream as createWriteStream2 } from "node:fs";
117
113
  import { randomUUID as randomUUID8 } from "node:crypto";
118
- import { mkdir as mkdir3, open as open4, rename as rename2, stat as stat2, unlink as unlink2 } from "node:fs/promises";
119
- import { basename, dirname as dirname3, extname, join as join7 } from "node:path";
114
+ import { mkdir as mkdir2, open as open2, rename as rename2, stat as stat2, unlink as unlink2 } from "node:fs/promises";
115
+ import { basename, dirname as dirname2, extname, join as join5 } from "node:path";
120
116
  import { Readable, Transform as Transform2 } from "node:stream";
121
117
  import { pipeline as pipeline2 } from "node:stream/promises";
122
118
 
123
- // ../endpoint/src/node.ts
124
- import { mkdir, chmod } from "node:fs/promises";
125
- import { createHash as createHash3 } from "node:crypto";
126
- import { join as join2, resolve as resolve3 } from "node:path";
127
- import { DatabaseSync } from "node:sqlite";
128
-
129
- // ../endpoint/src/sqlite.ts
130
- async function createSqliteEndpointStore(options) {
131
- const { database, namespace } = options;
132
- if (!namespace) throw new Error("Endpoint store namespace is required");
133
- await database.transaction(async (tx) => {
134
- await tx.run("CREATE TABLE IF NOT EXISTS canon_endpoint_records (namespace TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY(namespace, key))");
135
- });
136
- return {
137
- transaction: /* @__PURE__ */ __name((work) => database.transaction(async (sql) => {
138
- const transaction = {
139
- async get(key) {
140
- const rows = await sql.all("SELECT value FROM canon_endpoint_records WHERE namespace = ? AND key = ?", [namespace, key]);
141
- return rows[0] ? JSON.parse(rows[0].value) : void 0;
142
- },
143
- async put(key, value) {
144
- await sql.run("INSERT INTO canon_endpoint_records(namespace, key, value) VALUES (?, ?, ?) ON CONFLICT(namespace, key) DO UPDATE SET value = excluded.value", [namespace, key, JSON.stringify(value)]);
145
- },
146
- async delete(key) {
147
- await sql.run("DELETE FROM canon_endpoint_records WHERE namespace = ? AND key = ?", [namespace, key]);
148
- },
149
- async scan(prefix3, scanOptions = {}) {
150
- const limit = Math.max(1, Math.min(scanOptions.limit ?? 100, 1e3));
151
- const rows = await sql.all(
152
- "SELECT key, value FROM canon_endpoint_records WHERE namespace = ? AND key >= ? AND key < ? AND key > ? ORDER BY key LIMIT ?",
153
- [namespace, prefix3, `${prefix3}\uFFFF`, scanOptions.after ?? "", limit]
154
- );
155
- return rows.map((row) => ({ key: row.key, value: JSON.parse(row.value) }));
156
- }
157
- };
158
- return work(transaction);
159
- }), "transaction"),
160
- async close() {
161
- await database.close?.();
162
- }
163
- };
164
- }
165
- __name(createSqliteEndpointStore, "createSqliteEndpointStore");
166
-
167
- // ../endpoint/src/node-mls-custody.ts
168
- import { spawn } from "node:child_process";
169
- import { createHash } from "node:crypto";
170
- import { lstat, open, realpath } from "node:fs/promises";
171
- import { constants } from "node:fs";
172
- import { isAbsolute, join, resolve } from "node:path";
173
- import { platform } from "node:os";
174
-
175
- // ../endpoint/src/events.ts
176
- function createEndpointEventHub(input) {
177
- const observers = /* @__PURE__ */ new Set();
178
- let controller;
179
- let running;
180
- let closed = false;
181
- let connected = false;
182
- let terminalError = false;
183
- let lastEventId;
184
- let subscribedScopes = "";
185
- const principalScopes = /* @__PURE__ */ __name(() => [...new Set([...observers].flatMap(({ hooks }) => hooks?.principalScopes ?? []))].sort(), "principalScopes");
186
- const families = /* @__PURE__ */ __name(() => [...new Set([...observers].flatMap(({ hooks }) => hooks?.families ?? ["messages", "contacts"]))].sort(), "families");
187
- const subscriptionKey = /* @__PURE__ */ __name(() => JSON.stringify([families(), principalScopes()]), "subscriptionKey");
188
- const safe = /* @__PURE__ */ __name((callback) => {
189
- try {
190
- callback?.();
191
- } catch {
192
- }
193
- }, "safe");
194
- async function loop(signal) {
195
- let attempt = 0;
196
- while (!signal.aborted && observers.size) {
197
- let retryAfterMs = 0;
198
- let handling = false;
199
- try {
200
- if (!input.transport.events) return;
201
- for await (const event of input.transport.events({ lastEventId, signal, families: families(), principalScopes: principalScopes(), onConnected: /* @__PURE__ */ __name(() => {
202
- connected = true;
203
- for (const { hooks } of observers) safe(hooks?.onConnected);
204
- input.invalidate();
205
- }, "onConnected") })) {
206
- if (signal.aborted) break;
207
- validateEndpointContentBoundary("event", event.kind, event.data);
208
- if (event.kind === "error") throw decodeEndpointError(event.data, { stream: true });
209
- handling = true;
210
- await input.onEvent?.(event);
211
- handling = false;
212
- attempt = 0;
213
- if (event.kind === "replay.expired") lastEventId = void 0;
214
- else if (event.id) lastEventId = event.id;
215
- for (const observer of observers) safe(() => observer.listener(event));
216
- if (/^(artifact\.|message\.|conversations?\.|contacts?\.|agent\.context$|replay\.|voice\.session\.)/.test(event.kind)) input.invalidate(event);
217
- }
218
- } catch (error) {
219
- if (!signal.aborted) {
220
- terminalError = !handling && error instanceof EndpointError && !error.retryable;
221
- if (error instanceof EndpointError && typeof error.retryAfterMs === "number" && Number.isFinite(error.retryAfterMs)) retryAfterMs = Math.max(0, Math.min(3e5, error.retryAfterMs));
222
- for (const { hooks } of observers) safe(() => hooks?.onError?.(error instanceof Error ? error : new EndpointError("Stream connection failed")));
223
- }
224
- } finally {
225
- if (connected) {
226
- connected = false;
227
- safe(input.onDisconnected);
228
- for (const { hooks } of observers) safe(hooks?.onDisconnected);
229
- }
230
- }
231
- if (signal.aborted || !observers.size || terminalError) break;
232
- const delay = Math.max(retryAfterMs, Math.min(3e4, 500 * 2 ** Math.min(attempt++, 6)) * (0.8 + Math.random() * 0.4));
233
- await new Promise((resolve6) => {
234
- const finish = /* @__PURE__ */ __name(() => {
235
- clearTimeout(timer);
236
- signal.removeEventListener("abort", finish);
237
- resolve6();
238
- }, "finish");
239
- const timer = setTimeout(finish, delay);
240
- timer.unref?.();
241
- signal.addEventListener("abort", finish, { once: true });
242
- });
243
- }
244
- }
245
- __name(loop, "loop");
246
- function start() {
247
- if (running || closed || terminalError || !observers.size || !input.transport.events) return;
248
- controller = new AbortController();
249
- subscribedScopes = subscriptionKey();
250
- running = loop(controller.signal).finally(() => {
251
- running = void 0;
252
- controller = void 0;
253
- if (!closed && observers.size) start();
254
- });
255
- }
256
- __name(start, "start");
257
- return {
258
- isConnected: /* @__PURE__ */ __name(() => connected, "isConnected"),
259
- observe(listener, hooks) {
260
- if (closed) throw new EndpointError("Endpoint is closed", { code: "ENDPOINT_CLOSED", retryable: false });
261
- const observer = { listener, hooks };
262
- observers.add(observer);
263
- if (connected) safe(hooks?.onConnected);
264
- if (running && subscribedScopes !== subscriptionKey()) controller?.abort();
265
- start();
266
- return () => {
267
- observers.delete(observer);
268
- if (!observers.size || subscribedScopes !== subscriptionKey()) controller?.abort();
269
- };
270
- },
271
- async close() {
272
- closed = true;
273
- controller?.abort();
274
- observers.clear();
275
- await running;
276
- }
277
- };
278
- }
279
- __name(createEndpointEventHub, "createEndpointEventHub");
280
- var MAX_EVENT_FRAME_CHARS = 2 * 1024 * 1024;
281
- var DECODE_SLICE_BYTES = 64 * 1024;
282
- async function* readEndpointEvents(response, options) {
283
- if (!response.body) throw new EndpointError("Canon returned an empty event stream", { code: "INVALID_EVENT_STREAM" });
284
- const reader = response.body.getReader();
285
- const decoder = new TextDecoder();
286
- let buffer = "";
287
- let kind = "message";
288
- let id2;
289
- let data = [];
290
- let dataSize = 0;
291
- let frameSize = 0;
292
- const abort = /* @__PURE__ */ __name(() => {
293
- void reader.cancel().catch(() => void 0);
294
- }, "abort");
295
- options.signal.addEventListener("abort", abort, { once: true });
296
- try {
297
- while (!options.signal.aborted) {
298
- let timer;
299
- const chunk = await Promise.race([
300
- reader.read(),
301
- new Promise((_, reject) => {
302
- timer = setTimeout(() => {
303
- void reader.cancel().catch(() => void 0);
304
- reject(new EndpointError("Canon event stream stalled", { code: "STREAM_IDLE" }));
305
- }, options.idleTimeoutMs ?? 6e4);
306
- })
307
- ]).finally(() => {
308
- if (timer) clearTimeout(timer);
309
- });
310
- if (chunk.done) break;
311
- for (let offset = 0; offset < chunk.value.length; offset += DECODE_SLICE_BYTES) {
312
- buffer += decoder.decode(chunk.value.subarray(offset, offset + DECODE_SLICE_BYTES), { stream: true });
313
- let newline;
314
- while ((newline = buffer.indexOf("\n")) !== -1) {
315
- const line = buffer.slice(0, newline).replace(/\r$/, "");
316
- buffer = buffer.slice(newline + 1);
317
- frameSize += line.length + 1;
318
- if (frameSize > MAX_EVENT_FRAME_CHARS) throw new EndpointError("Canon event frame is too large", { code: "INVALID_EVENT_STREAM" });
319
- if (!line) {
320
- if (data.length) {
321
- const value2 = JSON.parse(data.join("\n"));
322
- if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new EndpointError("Invalid Canon event payload", { code: "INVALID_EVENT_STREAM" });
323
- validateEndpointContentBoundary("event", kind, value2);
324
- if (kind === "error") throw decodeEndpointError(value2, { stream: true, ...!response.ok ? { status: response.status } : {}, retryAfter: response.headers.get("Retry-After") });
325
- yield { ...id2 ? { id: id2 } : {}, kind, data: value2 };
326
- }
327
- kind = "message";
328
- id2 = void 0;
329
- data = [];
330
- dataSize = 0;
331
- frameSize = 0;
332
- continue;
333
- }
334
- if (line.startsWith(":")) continue;
335
- const separator = line.indexOf(":");
336
- const field = separator < 0 ? line : line.slice(0, separator);
337
- const value = separator < 0 ? "" : line.slice(separator + 1).replace(/^ /, "");
338
- if (field === "event") kind = value;
339
- else if (field === "id" && !value.includes("\0")) id2 = value;
340
- else if (field === "data") {
341
- data.push(value);
342
- dataSize += value.length + 1;
343
- if (dataSize > MAX_EVENT_FRAME_CHARS) throw new EndpointError("Canon event frame is too large", { code: "INVALID_EVENT_STREAM" });
344
- }
345
- }
346
- if (frameSize + buffer.length > MAX_EVENT_FRAME_CHARS) throw new EndpointError("Canon event frame is too large", { code: "INVALID_EVENT_STREAM" });
347
- }
348
- }
349
- } finally {
350
- options.signal.removeEventListener("abort", abort);
351
- await reader.cancel().catch(() => void 0);
352
- reader.releaseLock();
353
- }
354
- }
355
- __name(readEndpointEvents, "readEndpointEvents");
356
-
357
- // ../endpoint/src/mls-dev-transport.ts
358
- var utf8 = new TextEncoder();
359
- function failure(code, status = 0, retryable = false, retryAfterMs) {
360
- return new EndpointError("MLS Dev request failed", { code, status, retryable, ...retryAfterMs === void 0 ? {} : { retryAfterMs } });
361
- }
362
- __name(failure, "failure");
363
- function discard(response) {
364
- try {
365
- void response.body?.cancel().catch(() => {
366
- });
367
- } catch {
368
- }
369
- }
370
- __name(discard, "discard");
371
- function createMlsDevDeviceHttpTransport(config, dependencies) {
372
- const invalidConfig = /* @__PURE__ */ __name(() => failure("INVALID_MLS_DEV_TRANSPORT_CONFIG"), "invalidConfig");
373
- const timeoutMs = dependencies.timeoutMs ?? 3e4;
374
- const environment = dependencies.environmentId;
375
- if (!isMlsEnvironmentId(environment) || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 12e4 || !config || !Array.isArray(config.trustedOrigins) || !config.trustedOrigins.length || config.trustedOrigins.length > 16 || config.allowSyntheticLoopbackHttp !== void 0 && typeof config.allowSyntheticLoopbackHttp !== "boolean") throw invalidConfig();
376
- function url(value) {
377
- try {
378
- if (typeof value !== "string" || value.length > 2048) throw invalidConfig();
379
- const parsed = new URL(value);
380
- const localHttp = config.allowSyntheticLoopbackHttp === true && parsed.protocol === "http:" && (parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]");
381
- if (parsed.href !== value || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.protocol !== "https:" && !localHttp) throw invalidConfig();
382
- return parsed;
383
- } catch {
384
- throw invalidConfig();
385
- }
386
- }
387
- __name(url, "url");
388
- const origins = new Set(config.trustedOrigins.map((value) => {
389
- const parsed = url(value + "/");
390
- if (parsed.origin !== value || parsed.pathname !== "/") throw invalidConfig();
391
- return parsed.origin;
392
- }));
393
- function route(value) {
394
- const parsed = url(value);
395
- if (!origins.has(parsed.origin) || !/^\/(?:[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*\/?)?$/.test(parsed.pathname)) throw invalidConfig();
396
- return parsed.href.replace(/\/$/, "");
397
- }
398
- __name(route, "route");
399
- const enrollmentUrl = route(config.enrollmentUrl);
400
- const credentialUrl = route(config.credentialUrl);
401
- const reconciliationUrl = route(config.reconciliationUrl);
402
- const keyPackagesUrl = config.keyPackagesUrl === void 0 ? void 0 : route(config.keyPackagesUrl);
403
- const observations = /* @__PURE__ */ new Map();
404
- async function call(base, action, input, parseInput, parseOutput, maxBytes, signal) {
405
- let request;
406
- let body;
407
- try {
408
- request = parseInput(input);
409
- body = JSON.stringify(request);
410
- if (utf8.encode(body).length > maxBytes) throw new Error();
411
- } catch {
412
- throw failure("INVALID_MLS_DEV_REQUEST");
413
- }
414
- const controller = new AbortController();
415
- let stopError;
416
- let rejectStopped;
417
- const stopped = new Promise((_resolve, reject) => {
418
- rejectStopped = reject;
419
- });
420
- void stopped.catch(() => {
421
- });
422
- const stop = /* @__PURE__ */ __name((error) => {
423
- if (stopError) return;
424
- stopError = error;
425
- controller.abort();
426
- rejectStopped(error);
427
- }, "stop");
428
- const abort = /* @__PURE__ */ __name(() => stop(failure("MLS_DEV_REQUEST_ABORTED")), "abort");
429
- if (signal?.aborted) abort();
430
- else signal?.addEventListener("abort", abort, { once: true });
431
- const timeout = setTimeout(() => stop(failure("MLS_DEV_REQUEST_TIMEOUT", 0, true)), timeoutMs);
432
- async function bounded(work) {
433
- if (stopError) throw stopError;
434
- const result = await Promise.race([Promise.resolve().then(() => {
435
- if (stopError) throw stopError;
436
- return work();
437
- }), stopped]);
438
- if (stopError) throw stopError;
439
- return result;
440
- }
441
- __name(bounded, "bounded");
442
- const target = base + "/" + action;
443
- let response;
444
- try {
445
- for (let attempt = 0; attempt < 2; attempt++) {
446
- let token;
447
- try {
448
- token = await bounded(() => dependencies.credentials.getToken(attempt > 0));
449
- } catch {
450
- throw stopError ?? failure("MLS_DEV_AUTHENTICATION_UNAVAILABLE", 0, true);
451
- }
452
- if (typeof token !== "string" || !token || token.length > 8185 || /\s/.test(token)) throw failure("UNAUTHENTICATED", 401);
453
- let ownership;
454
- try {
455
- ownership = dependencies.ownershipHeaders();
456
- } catch {
457
- throw failure("INVALID_OWNERSHIP_VERSION");
458
- }
459
- response = await bounded(async () => {
460
- let value;
461
- try {
462
- value = await dependencies.fetch(target, {
463
- method: "POST",
464
- redirect: "error",
465
- cache: "no-store",
466
- credentials: "omit",
467
- referrerPolicy: "no-referrer",
468
- signal: controller.signal,
469
- headers: {
470
- Authorization: "Bearer " + token,
471
- "Content-Type": "application/json",
472
- Accept: "application/json",
473
- "X-Canon-Endpoint-Protocol": CANON_ENDPOINT_PROTOCOL,
474
- ...ownership
475
- },
476
- body
477
- });
478
- } catch {
479
- throw stopError ?? failure("MLS_DEV_REQUEST_UNAVAILABLE", 0, true);
480
- }
481
- if (stopError) discard(value);
482
- return value;
483
- });
484
- if (response.redirected || response.url !== target || response.status >= 300 && response.status < 400) {
485
- throw failure("MLS_DEV_REDIRECT_REJECTED");
486
- }
487
- const responseEnvironment = response.headers.get("X-Canon-Environment-Id");
488
- const gateway = response.status === 429 || response.status >= 500;
489
- if (responseEnvironment !== environment && !(responseEnvironment === null && gateway)) throw failure("ENVIRONMENT_MISMATCH", 409);
490
- if (response.status === 401 && attempt === 0) {
491
- discard(response);
492
- response = void 0;
493
- continue;
494
- }
495
- if (!response.ok) {
496
- const retry = response.headers.get("Retry-After");
497
- const seconds = retry && /^\d{1,6}$/.test(retry) ? Number(retry) : 0;
498
- throw failure("HTTP_" + response.status, response.status, gateway, seconds > 0 ? seconds * 1e3 : void 0);
499
- }
500
- if (!/^application\/json(?:\s*;|$)/i.test(response.headers.get("Content-Type") ?? "")) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
501
- const length = response.headers.get("Content-Length");
502
- if (length !== null && (!/^\d+$/.test(length) || Number(length) > maxBytes)) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
503
- const reader = response.body?.getReader();
504
- if (!reader) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
505
- let count = 0;
506
- let text = "";
507
- const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
508
- try {
509
- for (; ; ) {
510
- const chunk = await bounded(() => reader.read());
511
- if (chunk.done) break;
512
- count += chunk.value.byteLength;
513
- if (count > maxBytes) throw failure("INVALID_MLS_DEV_RESPONSE", 502);
514
- text += decoder.decode(chunk.value, { stream: true });
515
- }
516
- text += decoder.decode();
517
- const result = parseOutput(JSON.parse(text));
518
- if (stopError) throw stopError;
519
- return { request, result };
520
- } catch (error) {
521
- if (error === stopError) throw error;
522
- throw failure("INVALID_MLS_DEV_RESPONSE", 502);
523
- } finally {
524
- try {
525
- void reader.cancel().catch(() => {
526
- });
527
- } catch {
528
- }
529
- reader.releaseLock();
530
- }
531
- }
532
- throw failure("UNAUTHENTICATED", 401);
533
- } catch (error) {
534
- if (error instanceof EndpointError) throw error;
535
- throw stopError ?? failure("MLS_DEV_REQUEST_UNAVAILABLE", 0, true);
536
- } finally {
537
- if (response) discard(response);
538
- clearTimeout(timeout);
539
- signal?.removeEventListener("abort", abort);
540
- }
541
- }
542
- __name(call, "call");
543
- const invalidResponse = /* @__PURE__ */ __name(() => failure("INVALID_MLS_DEV_RESPONSE", 502), "invalidResponse");
544
- return {
545
- ...keyPackagesUrl ? { keyPackages: {
546
- async publish(input, signal) {
547
- const { request, result } = await call(
548
- keyPackagesUrl,
549
- "publish",
550
- input,
551
- parseMlsDevKeyPackagePublish,
552
- parseMlsDevKeyPackagePublicationReceipt,
553
- MLS_DEV_KEY_PACKAGES_MAX_BYTES,
554
- signal
555
- );
556
- if (result.requestId !== request.requestId) throw invalidResponse();
557
- return result;
558
- },
559
- async claim(input, signal) {
560
- const { request, result } = await call(
561
- keyPackagesUrl,
562
- "claim",
563
- input,
564
- parseMlsDevKeyPackageClaim,
565
- parseMlsDevKeyPackageClaimReceipt,
566
- MLS_DEV_KEY_PACKAGES_MAX_BYTES,
567
- signal
568
- );
569
- if (result.requestId !== request.requestId || result.recipientDeviceId !== request.recipientDeviceId || result.claimantDeviceId !== parseMlsDevCredentialToken(request.claimantCredential).unsigned.claims.device.deviceId || canonicalEndpointJson(result.preconditions) !== canonicalEndpointJson(request.preconditions)) throw invalidResponse();
570
- return result;
571
- }
572
- } } : {},
573
- async beginEnrollment(input, signal) {
574
- const { request, result } = await call(enrollmentUrl, "begin", input, parseMlsDevEnrollmentBegin, parseMlsDevEnrollmentChallenge, MLS_DEV_ENROLLMENT_MAX_BYTES, signal);
575
- if (result.installationId !== request.installationId || result.signaturePublicKey !== request.signaturePublicKey) throw invalidResponse();
576
- return result;
577
- },
578
- async completeEnrollment(input, signal) {
579
- return (await call(enrollmentUrl, "complete", input, parseMlsDevEnrollmentComplete, parseMlsDevEnrollmentResult, MLS_DEV_ENROLLMENT_MAX_BYTES, signal)).result;
580
- },
581
- async beginCredential(input, signal) {
582
- const { request, result } = await call(credentialUrl, "begin", input, parseMlsDevCredentialBegin, parseMlsDevCredentialChallenge, MLS_DEV_CREDENTIAL_MAX_BYTES, signal);
583
- if (result.credential.claims.device.deviceId !== request.deviceId || result.credential.claims.generation !== request.expectedCredentialGeneration + 1) throw invalidResponse();
584
- return result;
585
- },
586
- async completeCredential(input, signal) {
587
- return (await call(credentialUrl, "complete", input, parseMlsDevCredentialComplete, parseMlsDevCredentialReceipt, MLS_DEV_CREDENTIAL_MAX_BYTES, signal)).result;
588
- },
589
- async beginReconciliation(input, signal) {
590
- const { request, result } = await call(reconciliationUrl, "begin", input, parseMlsDevCredentialReconciliationBegin, parseMlsDevCredentialReconciliationChallenge, MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES, signal);
591
- if (result.device.deviceId !== request.deviceId || result.requestId !== request.requestId || result.expectedCredentialGeneration !== request.expectedCredentialGeneration) throw invalidResponse();
592
- return result;
593
- },
594
- async completeReconciliation(input, signal) {
595
- const { request, result } = await call(reconciliationUrl, "complete", input, parseMlsDevCredentialReconciliationComplete, parseMlsDevCredentialReconciliationReceipt, MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES, signal);
596
- if (result.challenge.challengeId !== request.challengeId) throw invalidResponse();
597
- const key = JSON.stringify(result);
598
- observations.set(key, true);
599
- if (observations.size > 32) observations.delete(observations.keys().next().value);
600
- return result;
601
- },
602
- async revokeCredential(input, signal) {
603
- const { request, result } = await call(credentialUrl, "revoke", input, parseMlsDevCredentialRevoke, parseMlsDevCredentialRevocationReceipt, MLS_DEV_CREDENTIAL_MAX_BYTES, signal);
604
- if (result.deviceId !== request.deviceId) throw invalidResponse();
605
- return result;
606
- },
607
- verifyGenerationObservation(receipt) {
608
- try {
609
- return observations.has(JSON.stringify(parseMlsDevCredentialReconciliationReceipt(receipt)));
610
- } catch {
611
- return false;
612
- }
613
- }
614
- };
615
- }
616
- __name(createMlsDevDeviceHttpTransport, "createMlsDevDeviceHttpTransport");
617
-
618
- // ../endpoint/src/transport.ts
619
- var EndpointError = class extends Error {
620
- static {
621
- __name(this, "EndpointError");
622
- }
623
- status;
624
- code;
625
- retryable;
626
- operationId;
627
- retryAfterMs;
628
- constructor(message, options = {}) {
629
- super(message);
630
- this.name = "EndpointError";
631
- this.status = options.status ?? 0;
632
- this.code = options.code ?? "ENDPOINT_UNAVAILABLE";
633
- this.retryable = options.retryable ?? classifyTransferHttpFailure(this.status) === "retry";
634
- this.operationId = options.operationId;
635
- this.retryAfterMs = options.retryAfterMs;
636
- }
637
- };
638
- var retryableStreamErrors = /* @__PURE__ */ new Set([
639
- "rate_limited",
640
- "connection_limit",
641
- "service_overloaded",
642
- "listener_failed",
643
- "connection_reclaimed",
644
- "connection_stale",
645
- "server_shutting_down",
646
- "authorization_changed",
647
- "authorization_check_failed"
648
- ]);
649
- function decodeEndpointError(value, options = {}) {
650
- const details = value && typeof value === "object" && !Array.isArray(value) ? value : {};
651
- const status = options.status ?? (typeof details.status === "number" && Number.isInteger(details.status) && details.status >= 400 && details.status <= 599 ? details.status : 0);
652
- const code = typeof details.code === "string" ? details.code : options.stream ? "STREAM_UNAVAILABLE" : `HTTP_${status}`;
653
- const positive = /* @__PURE__ */ __name((value2) => typeof value2 === "number" && Number.isFinite(value2) && value2 > 0 ? value2 : void 0, "positive");
654
- const retryHeader = options.retryAfter?.trim();
655
- const retryAfterMs = positive(retryHeader ? /^\d+(?:\.\d+)?$/.test(retryHeader) ? Number(retryHeader) * 1e3 : Date.parse(retryHeader) - Date.now() : void 0) ?? positive(details.retryAfterMs) ?? positive(typeof details.retryAfter === "number" ? details.retryAfter * 1e3 : void 0);
656
- const accessTokenExpired = status === 401 && code === "CONNECTOR_ACCESS_TOKEN_EXPIRED";
657
- const retryable = details.retryable !== false && (status ? accessTokenExpired || status === 429 || status >= 500 : options.stream ? retryableStreamErrors.has(code) : true);
658
- return new EndpointError(typeof details.error === "string" ? details.error : typeof details.message === "string" ? details.message : `Canon ${options.stream ? "stream" : "request"} failed${status ? ` (${status})` : ""}`, {
659
- status,
660
- code,
661
- retryable,
662
- ...retryAfterMs === void 0 ? {} : { retryAfterMs }
663
- });
664
- }
665
- __name(decodeEndpointError, "decodeEndpointError");
666
- async function readHttpError(response, signal) {
667
- if (response.headers.get("Content-Type")?.toLowerCase().startsWith("text/event-stream")) {
668
- try {
669
- for await (const _event of readEndpointEvents(response, { signal })) {
670
- }
671
- } catch (error) {
672
- if (error instanceof EndpointError) return error;
673
- throw error;
674
- }
675
- } else {
676
- return decodeEndpointError(await response.json().catch(() => null), { status: response.status, retryAfter: response.headers.get("Retry-After") });
677
- }
678
- return decodeEndpointError(null, { status: response.status, retryAfter: response.headers.get("Retry-After"), stream: true });
679
- }
680
- __name(readHttpError, "readHttpError");
681
- async function readBoundedGroupJson(response, signal) {
682
- const maximum = 4 * 1024 * 1024;
683
- const invalid2 = /* @__PURE__ */ __name(() => new EndpointError("Invalid MLS group response", { status: 502, code: "INVALID_MLS_DEV_GROUP_RESPONSE", retryable: false }), "invalid");
684
- const length = response.headers.get("Content-Length");
685
- if (length !== null && (!/^\d+$/.test(length) || Number(length) > maximum)) throw invalid2();
686
- const reader = response.body?.getReader();
687
- if (!reader) throw invalid2();
688
- let rejectAbort;
689
- const stopped = new Promise((_resolve, reject) => {
690
- rejectAbort = reject;
691
- });
692
- void stopped.catch(() => {
693
- });
694
- const abort = /* @__PURE__ */ __name(() => rejectAbort(new EndpointError("MLS group request aborted", { code: "MLS_DEV_REQUEST_ABORTED", retryable: true })), "abort");
695
- if (signal.aborted) abort();
696
- else signal.addEventListener("abort", abort, { once: true });
697
- try {
698
- const decoder = new TextDecoder("utf-8", { fatal: true }), parts = [];
699
- let size = 0;
700
- for (; ; ) {
701
- if (signal.aborted) throw new EndpointError("MLS group request aborted", { code: "MLS_DEV_REQUEST_ABORTED", retryable: true });
702
- const chunk = await Promise.race([reader.read(), stopped]);
703
- if (chunk.done) break;
704
- size += chunk.value.byteLength;
705
- if (size > maximum) throw invalid2();
706
- parts.push(decoder.decode(chunk.value, { stream: true }));
707
- }
708
- parts.push(decoder.decode());
709
- return JSON.parse(parts.join(""));
710
- } catch (error) {
711
- if (error instanceof EndpointError) throw error;
712
- throw invalid2();
713
- } finally {
714
- signal.removeEventListener("abort", abort);
715
- try {
716
- void reader.cancel().catch(() => {
717
- });
718
- } catch {
719
- }
720
- reader.releaseLock();
721
- }
722
- }
723
- __name(readBoundedGroupJson, "readBoundedGroupJson");
724
- function hasEnvironmentMismatch(response, expected) {
725
- const environment = response.headers.get("X-Canon-Environment-Id");
726
- return environment !== expected && !(environment === null && (response.status === 429 || response.status >= 500));
727
- }
728
- __name(hasEnvironmentMismatch, "hasEnvironmentMismatch");
729
- function validateEndpointContentBoundary(lane, name, payload) {
730
- try {
731
- endpointContentBoundary(lane, name, payload);
732
- } catch {
733
- throw new EndpointError("Endpoint content lane requires a protocol update", { code: "ENDPOINT_CONTENT_UNCLASSIFIED", retryable: false });
734
- }
735
- }
736
- __name(validateEndpointContentBoundary, "validateEndpointContentBoundary");
737
- function createHttpEndpointTransport(options) {
738
- const fetcher = options.fetch ?? globalThis.fetch;
739
- const clientHeaders = options.client ? { [CANON_CLIENT_HEADER]: formatCanonClientHeader(options.client) } : {};
740
- const updateRequired = /* @__PURE__ */ __name((error) => {
741
- if (error.code === CLIENT_UPDATE_REQUIRED) try {
742
- options.onClientUpdateRequired?.(error);
743
- } catch {
744
- }
745
- return error;
746
- }, "updateRequired");
747
- const baseUrl = options.baseUrl.replace(/\/$/, "");
748
- let resolvedStreamId;
749
- function streamIdentity() {
750
- return resolvedStreamId ??= Promise.resolve().then(async () => {
751
- const value = typeof options.streamId === "function" ? await options.streamId() : options.streamId;
752
- if (value !== void 0 && (typeof value !== "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(value))) {
753
- throw new EndpointError("Invalid logical stream identity", { code: "INVALID_STREAM_ID", retryable: false });
754
- }
755
- return value;
756
- });
757
- }
758
- __name(streamIdentity, "streamIdentity");
759
- function ownershipHeaders() {
760
- const version = typeof options.ownershipVersion === "function" ? options.ownershipVersion() : options.ownershipVersion;
761
- if (version === void 0) return {};
762
- if (!Number.isSafeInteger(version) || version < 0) throw new EndpointError("Invalid endpoint ownership generation", { code: "INVALID_OWNERSHIP_VERSION", retryable: false });
763
- return { "X-Canon-Ownership-Version": String(version) };
764
- }
765
- __name(ownershipHeaders, "ownershipHeaders");
766
- async function call(path, method, body, signal, origin = baseUrl, groupResponse = false) {
767
- for (let authAttempt = 0; authAttempt < 2; authAttempt++) {
768
- const token = await options.credentials.getToken(authAttempt > 0);
769
- if (!token) throw new EndpointError("Authentication is required", { status: 401, code: "UNAUTHENTICATED", retryable: false });
770
- const controller = new AbortController();
771
- const abort = /* @__PURE__ */ __name(() => controller.abort(signal?.reason), "abort");
772
- if (signal?.aborted) abort();
773
- else signal?.addEventListener("abort", abort, { once: true });
774
- const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 3e4);
775
- try {
776
- const response = await fetcher(`${origin.replace(/\/$/, "")}${path}`, {
777
- method,
778
- signal: controller.signal,
779
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "X-Canon-Endpoint-Protocol": CANON_ENDPOINT_PROTOCOL, ...clientHeaders, ...ownershipHeaders() },
780
- ...body === void 0 ? {} : { body: JSON.stringify(body) }
781
- });
782
- if (hasEnvironmentMismatch(response, options.environmentId)) {
783
- throw new EndpointError("Canon environment does not match this endpoint", { status: 409, code: "ENVIRONMENT_MISMATCH", retryable: false });
784
- }
785
- if (response.status === 401 && authAttempt === 0) {
786
- await response.body?.cancel();
787
- continue;
788
- }
789
- if (!response.ok) throw updateRequired(await readHttpError(response, controller.signal));
790
- return groupResponse ? await readBoundedGroupJson(response, controller.signal) : await response.json().catch(() => null);
791
- } finally {
792
- clearTimeout(timeout);
793
- signal?.removeEventListener("abort", abort);
794
- }
795
- }
796
- throw new EndpointError("Authentication is required", { status: 401, code: "UNAUTHENTICATED", retryable: false });
797
- }
798
- __name(call, "call");
799
- function receipt(value, operationId, installationId, action) {
800
- if (!isEndpointOperationReceipt(value) || classifyTransferReceipt(value, { protocol: CANON_ENDPOINT_PROTOCOL, operationId, installationId, action: action ?? value.action }) === "invalid") throw new EndpointError("Canon returned an invalid operation receipt", { status: 502, code: "INVALID_RECEIPT" });
801
- if (value.operationId !== operationId || value.installationId !== installationId || action !== void 0 && value.action !== action) throw new EndpointError("Canon returned another operation receipt", { status: 502, code: "INVALID_RECEIPT" });
802
- validateEndpointContentBoundary("operation.receipt", value.action, value);
803
- return value;
804
- }
805
- __name(receipt, "receipt");
806
- return {
807
- ...options.mlsDevDevice ? { mlsDevDevice: createMlsDevDeviceHttpTransport(options.mlsDevDevice, {
808
- environmentId: options.environmentId,
809
- credentials: options.credentials,
810
- fetch: fetcher,
811
- timeoutMs: options.timeoutMs,
812
- ownershipHeaders
813
- }) } : {},
814
- async capabilities() {
815
- const result = await call("/endpoint/capabilities", "GET", void 0);
816
- if (!result || typeof result !== "object" || result.protocol !== CANON_ENDPOINT_PROTOCOL || !Array.isArray(result.actions) || !Array.isArray(result.queries)) {
817
- throw new EndpointError("Canon returned invalid endpoint capabilities", { status: 502, code: "INVALID_CAPABILITIES" });
818
- }
819
- return result;
820
- },
821
- ...options.streamUrl ? {
822
- async *events(streamOptions) {
823
- const streamId = await streamIdentity();
824
- for (let attempt = 0; attempt < 2; attempt++) {
825
- const token = await options.credentials.getToken(attempt > 0);
826
- if (!token) throw new EndpointError("Authentication is required", { status: 401, code: "UNAUTHENTICATED", retryable: false });
827
- const connection = new AbortController();
828
- const abort = /* @__PURE__ */ __name(() => connection.abort(streamOptions.signal.reason), "abort");
829
- if (streamOptions.signal.aborted) abort();
830
- else streamOptions.signal.addEventListener("abort", abort, { once: true });
831
- const handshake = setTimeout(() => connection.abort(), options.timeoutMs ?? 3e4);
832
- try {
833
- const scopeQuery = streamOptions.principalScopes?.length ? `&principalScopes=${encodeURIComponent([...new Set(streamOptions.principalScopes)].sort().join(","))}` : "";
834
- const response = await fetcher(`${options.streamUrl.replace(/\/$/, "")}/endpoint/events?events=${encodeURIComponent((streamOptions.families ?? ["messages", "contacts"]).join(","))}${scopeQuery}`, {
835
- signal: connection.signal,
836
- headers: { Authorization: `Bearer ${token}`, Accept: "text/event-stream", "X-Canon-Endpoint-Protocol": CANON_ENDPOINT_PROTOCOL, ...clientHeaders, ...streamId ? { "X-Canon-Stream-Id": streamId } : {}, ...ownershipHeaders(), ...streamOptions.lastEventId ? { "Last-Event-ID": streamOptions.lastEventId } : {} }
837
- });
838
- if (hasEnvironmentMismatch(response, options.environmentId)) {
839
- await response.body?.cancel();
840
- throw new EndpointError("Canon stream environment does not match this endpoint", { status: 409, code: "ENVIRONMENT_MISMATCH", retryable: false });
841
- }
842
- if (response.status === 401 && attempt === 0) {
843
- await response.body?.cancel();
844
- continue;
845
- }
846
- if (!response.ok) throw updateRequired(await readHttpError(response, connection.signal));
847
- if (!response.headers.get("Content-Type")?.toLowerCase().startsWith("text/event-stream")) {
848
- await response.body?.cancel();
849
- throw new EndpointError("Canon returned an invalid event stream", { status: 502, code: "INVALID_EVENT_STREAM" });
850
- }
851
- clearTimeout(handshake);
852
- let connected = false;
853
- for await (const event of readEndpointEvents(response, { ...streamOptions, signal: connection.signal })) {
854
- if (!connected) {
855
- connected = true;
856
- streamOptions.onConnected?.();
857
- }
858
- yield event;
859
- }
860
- return;
861
- } finally {
862
- clearTimeout(handshake);
863
- streamOptions.signal.removeEventListener("abort", abort);
864
- connection.abort();
865
- }
866
- }
867
- }
868
- } : {},
869
- async execute(operation, signal) {
870
- validateEndpointContentBoundary("operation.request", operation.action, operation);
871
- return receipt(await call("/endpoint/operations", "POST", operation, signal, baseUrl, operation.action === "mls_group_control"), operation.operationId, operation.installationId, operation.action);
872
- },
873
- async getOperation(operationId, installationId, signal) {
874
- try {
875
- return receipt(await call(`/endpoint/operations/${encodeURIComponent(operationId)}?installationId=${encodeURIComponent(installationId)}`, "GET", void 0, signal, baseUrl, true), operationId, installationId);
876
- } catch (error) {
877
- if (error instanceof EndpointError && error.status === 404) return null;
878
- throw error;
879
- }
880
- },
881
- async query(query, parameters, signal) {
882
- validateEndpointContentBoundary("query.request", query, parameters);
883
- if (query === "admit_messages" && !options.streamUrl) throw new EndpointError("Agent recovery requires the shared stream endpoint", { code: "STREAM_CONFIGURATION_REQUIRED", retryable: false });
884
- const response = await call("/endpoint/query", "POST", { protocol: CANON_ENDPOINT_PROTOCOL, query, parameters }, signal, query === "admit_messages" ? options.streamUrl : baseUrl, ["mls_group_controls", "mls_group_feed"].includes(query));
885
- if (!response || typeof response !== "object" || response.protocol !== CANON_ENDPOINT_PROTOCOL || !Object.hasOwn(response, "result")) {
886
- throw new EndpointError("Canon returned an invalid query response", { status: 502, code: "INVALID_QUERY_RESPONSE" });
887
- }
888
- validateEndpointContentBoundary("query.result", query, response.result);
889
- return response.result;
890
- },
891
- async publish(action, body, signal) {
892
- validateEndpointContentBoundary("ephemeral.request", action, body);
893
- const response = await call("/endpoint/ephemeral", "POST", { protocol: CANON_ENDPOINT_PROTOCOL, action, body }, signal);
894
- if (!response || typeof response !== "object" || response.protocol !== CANON_ENDPOINT_PROTOCOL || !Object.hasOwn(response, "result")) {
895
- throw new EndpointError("Canon returned an invalid ephemeral response", { status: 502, code: "INVALID_EPHEMERAL_RESPONSE" });
896
- }
897
- validateEndpointContentBoundary("ephemeral.result", action, response.result);
898
- return response.result;
899
- }
900
- };
901
- }
902
- __name(createHttpEndpointTransport, "createHttpEndpointTransport");
903
-
904
- // ../endpoint/src/node-mls-custody-protocol.ts
905
- var NodeMlsDevCustodyError = class extends Error {
906
- static {
907
- __name(this, "NodeMlsDevCustodyError");
908
- }
909
- code = "MLS_NODE_CUSTODY_REJECTED";
910
- constructor() {
911
- super("MLS_NODE_CUSTODY_REJECTED");
912
- this.name = "NodeMlsDevCustodyError";
913
- }
914
- };
915
- var MAX_INPUT = 8 * 1024 * 1024;
916
- var MAX_OUTPUT = 4 * 1024 * 1024;
917
- var DOMAIN = Buffer.from("canon.mls.agent-host.v1\0");
918
- function rejected() {
919
- throw new NodeMlsDevCustodyError();
920
- }
921
- __name(rejected, "rejected");
922
- function custodyValue(output, operation) {
923
- const parsed = parseJson(output);
924
- if (operation === 4 && isMlsDevReceiptClockSkew(parsed)) throw new EndpointError("Canon accepted this entry ahead of this device's clock", { code: MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW, retryable: true });
925
- const result = record(parsed, ["version", "ok", "value"]);
926
- requireValue(result.version === 1 && result.ok === true);
927
- return result.value;
928
- }
929
- __name(custodyValue, "custodyValue");
930
- function custodyFailure(error) {
931
- if (error instanceof EndpointError && error.code === MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW) throw error;
932
- return rejected();
933
- }
934
- __name(custodyFailure, "custodyFailure");
935
- function requireValue(value) {
936
- if (!value) rejected();
937
- }
938
- __name(requireValue, "requireValue");
939
- function record(value, fields) {
940
- requireValue(value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype);
941
- const descriptors = Object.getOwnPropertyDescriptors(value);
942
- requireValue(Object.keys(descriptors).length === fields.length && fields.every((key) => descriptors[key] && "value" in descriptors[key]));
943
- return value;
944
- }
945
- __name(record, "record");
946
- function sealed(value, maximum, nullable = false) {
947
- if (nullable && value === null) return null;
948
- requireValue(typeof value === "string" && value.length <= maximum && value.startsWith("v1:"));
949
- const body = value.slice(3);
950
- requireValue(body.length >= 40 && Buffer.from(body, "base64").toString("base64") === body);
951
- return value;
952
- }
953
- __name(sealed, "sealed");
954
- function frame(operation, values) {
955
- const chunks = [DOMAIN, Buffer.from([operation])];
956
- let length = DOMAIN.length + 1;
957
- for (const value of values) {
958
- const bytes = value === null ? null : Buffer.from(value, "utf8");
959
- const prefix3 = Buffer.alloc(4);
960
- prefix3.writeUInt32BE(bytes?.length ?? 4294967295);
961
- chunks.push(prefix3);
962
- if (bytes) chunks.push(bytes);
963
- length += 4 + (bytes?.length ?? 0);
964
- requireValue(length <= MAX_INPUT);
965
- }
966
- return Buffer.concat(chunks, length);
967
- }
968
- __name(frame, "frame");
969
- function parseJson(bytes) {
970
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
971
- const value = JSON.parse(text);
972
- let offset = 0;
973
- const whitespace = /* @__PURE__ */ __name(() => {
974
- while (/\s/.test(text[offset] ?? "") && offset < text.length) offset++;
975
- }, "whitespace");
976
- const string = /* @__PURE__ */ __name(() => {
977
- const start = offset++;
978
- while (offset < text.length) {
979
- const c = text[offset++];
980
- if (c === "\\") offset++;
981
- else if (c === '"') return JSON.parse(text.slice(start, offset));
982
- }
983
- return rejected();
984
- }, "string");
985
- const scan = /* @__PURE__ */ __name((depth) => {
986
- requireValue(depth <= 64);
987
- whitespace();
988
- const c = text[offset];
989
- if (c === "{") {
990
- offset++;
991
- whitespace();
992
- const keys = /* @__PURE__ */ new Set();
993
- if (text[offset] === "}") {
994
- offset++;
995
- return;
996
- }
997
- while (true) {
998
- whitespace();
999
- requireValue(text[offset] === '"');
1000
- const key = string();
1001
- requireValue(!keys.has(key));
1002
- keys.add(key);
1003
- whitespace();
1004
- requireValue(text[offset++] === ":");
1005
- scan(depth + 1);
1006
- whitespace();
1007
- if (text[offset++] === "}") return;
1008
- }
1009
- }
1010
- if (c === "[") {
1011
- offset++;
1012
- whitespace();
1013
- if (text[offset] === "]") {
1014
- offset++;
1015
- return;
1016
- }
1017
- while (true) {
1018
- scan(depth + 1);
1019
- whitespace();
1020
- if (text[offset++] === "]") return;
1021
- }
1022
- }
1023
- if (c === '"') {
1024
- string();
1025
- return;
1026
- }
1027
- while (offset < text.length && !/[\s,\]}]/.test(text[offset])) offset++;
1028
- }, "scan");
1029
- scan(0);
1030
- whitespace();
1031
- requireValue(offset === text.length);
1032
- return value;
1033
- }
1034
- __name(parseJson, "parseJson");
1035
- function typedNodeMlsDevCustody(job, invoke, close) {
1036
- return Object.freeze({
1037
- prepareProtected: /* @__PURE__ */ __name(((context, commandValue, wrappedState) => job(async (signal) => {
1038
- const contextJson = JSON.stringify(parseMlsDevDeviceContext(context)), command = parseMlsDevDeviceCommand(commandValue);
1039
- const wrapped = sealed(wrappedState, 6144, true);
1040
- requireValue(command.op === "create" === (wrapped === null));
1041
- requireValue(await invoke(1, [contextJson], signal) === null);
1042
- return parseMlsDevDevicePreparation(await invoke(2, [contextJson, JSON.stringify(command), wrapped], signal), command.op);
1043
- })), "prepareProtected"),
1044
- prepareProtectedKeyPackage: /* @__PURE__ */ __name(((context, command, wrappedState) => job(async (signal) => {
1045
- const contextJson = JSON.stringify(parseMlsDevDeviceContext(context)), commandJson = JSON.stringify(parseMlsDevKeyPackageCommand(command));
1046
- const wrapped = sealed(wrappedState, 6144);
1047
- requireValue(await invoke(1, [contextJson], signal) === null);
1048
- return parseMlsDevKeyPackagePreparation(await invoke(3, [contextJson, commandJson, wrapped], signal));
1049
- })), "prepareProtectedKeyPackage"),
1050
- prepareProtectedGroup: /* @__PURE__ */ __name(((identity, group, command, identityState, groupState, packageState) => job(async (signal) => {
1051
- const contextJson = JSON.stringify(parseMlsDevDeviceContext(identity)), groupJson = JSON.stringify(parseMlsDevGroupContext(group)), commandJson = JSON.stringify(parseMlsDevGroupCommand(command));
1052
- const wrapped = sealed(identityState, 6144), protectedGroup = sealed(groupState, 3 * 1024 * 1024, true), protectedPackage = sealed(packageState, 192 * 1024, true);
1053
- requireValue(await invoke(1, [contextJson], signal) === null);
1054
- const prepared = parseMlsDevGroupPreparation(await invoke(4, [contextJson, groupJson, commandJson, wrapped, protectedGroup, protectedPackage], signal));
1055
- const expected = ["create", "add", "remove"].includes(command.op) ? ["control"] : ["join", "process", "finalize"].includes(command.op) ? ["applied"] : command.op === "encrypt_text" || command.op === "encrypt_item" ? ["application"] : command.op === "encrypt_interaction" ? ["interaction_application"] : command.op === "process_application" ? ["text", "item", "interaction", "invalid"] : command.op === "open_secret_answer" ? ["secret"] : ["discarded"];
1056
- requireValue(expected.includes(prepared.payload.kind));
1057
- if (prepared.payload.kind === "application") requireValue(command.op === "encrypt_item" === (prepared.payload.payload !== void 0));
1058
- return prepared;
1059
- })), "prepareProtectedGroup"),
1060
- close
1061
- });
1062
- }
1063
- __name(typedNodeMlsDevCustody, "typedNodeMlsDevCustody");
1064
-
1065
- // ../endpoint/src/node-mls-custody.ts
1066
- var EXECUTABLE = "Contents/MacOS/CanonMlsAgent";
1067
- var HASH = /^[0-9a-f]{64}$/;
1068
- var matches = /* @__PURE__ */ __name((pattern, value) => pattern.exec(value)?.[0] === value, "matches");
1069
- var hash = /* @__PURE__ */ __name((bytes) => createHash("sha256").update(bytes).digest("hex"), "hash");
1070
- function createNodeMlsDevCustody(options) {
1071
- const captured = (() => {
1072
- try {
1073
- const keys = ["bundlePath", "manifestSha256", "executableSha256", "bundleIdentifier", "teamIdentifier"];
1074
- if (Object.hasOwn(options, "timeoutMs")) keys.push("timeoutMs");
1075
- if (Object.hasOwn(options, "signal")) keys.push("signal");
1076
- const data = record(options, keys);
1077
- requireValue(typeof data.bundlePath === "string" && isAbsolute(data.bundlePath) && resolve(data.bundlePath) === data.bundlePath && data.bundlePath.endsWith(".app"));
1078
- for (const key of ["manifestSha256", "executableSha256"]) requireValue(typeof data[key] === "string" && HASH.test(data[key]));
1079
- requireValue(typeof data.bundleIdentifier === "string" && matches(/^[A-Za-z0-9][A-Za-z0-9.-]{2,150}$/, data.bundleIdentifier));
1080
- requireValue(typeof data.teamIdentifier === "string" && matches(/^[A-Z0-9]{10}$/, data.teamIdentifier));
1081
- const timeoutMs = data.timeoutMs ?? 3e4;
1082
- requireValue(typeof timeoutMs === "number" && Number.isSafeInteger(timeoutMs) && timeoutMs >= 100 && timeoutMs <= 6e4);
1083
- requireValue(data.signal === void 0 || data.signal instanceof AbortSignal);
1084
- return Object.freeze({ ...data, timeoutMs });
1085
- } catch {
1086
- return rejected();
1087
- }
1088
- })();
1089
- let closed = false;
1090
- const jobs = /* @__PURE__ */ new Set(), children = /* @__PURE__ */ new Set();
1091
- const alive = /* @__PURE__ */ __name((signal) => requireValue(!closed && !signal.aborted && !captured.signal?.aborted), "alive");
1092
- async function file(path, maximum) {
1093
- const info = await lstat(path);
1094
- requireValue(info.isFile() && !info.isSymbolicLink() && (info.mode & 18) === 0 && info.size > 0 && info.size <= maximum && (info.uid === process.getuid?.() || info.uid === 0) && await realpath(path) === path);
1095
- const fd = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
1096
- try {
1097
- const current = await fd.stat();
1098
- requireValue(current.ino === info.ino && current.dev === info.dev && current.size === info.size);
1099
- const bytes = Buffer.alloc(current.size);
1100
- let count = 0;
1101
- while (count < bytes.length) {
1102
- const read = await fd.read(bytes, count, bytes.length - count, count);
1103
- requireValue(read.bytesRead > 0);
1104
- count += read.bytesRead;
1105
- }
1106
- const after = await fd.stat();
1107
- requireValue(after.size === current.size && after.mtimeMs === current.mtimeMs);
1108
- return bytes;
1109
- } finally {
1110
- await fd.close();
1111
- }
1112
- }
1113
- __name(file, "file");
1114
- function command(binary, args, input, signal, maximum) {
1115
- alive(signal);
1116
- const result = new Promise((resolveCommand, rejectCommand) => {
1117
- const child = spawn(binary, args, { detached: true, stdio: ["pipe", "pipe", "pipe"], env: { PATH: "/usr/bin:/bin" } });
1118
- let invalid2 = false, stdoutBytes = 0, stderrBytes = 0;
1119
- const chunks = [];
1120
- const kill = /* @__PURE__ */ __name(() => {
1121
- try {
1122
- if (child.pid) process.kill(-child.pid, "SIGKILL");
1123
- } catch (error) {
1124
- if (error.code !== "ESRCH") invalid2 = true;
1125
- }
1126
- }, "kill");
1127
- const cancel = /* @__PURE__ */ __name(() => {
1128
- invalid2 = true;
1129
- kill();
1130
- }, "cancel");
1131
- signal.addEventListener("abort", cancel, { once: true });
1132
- child.stdout.on("data", (chunk) => {
1133
- stdoutBytes += chunk.length;
1134
- if (stdoutBytes > maximum) cancel();
1135
- else chunks.push(chunk);
1136
- });
1137
- child.stderr.on("data", (chunk) => {
1138
- stderrBytes += chunk.length;
1139
- if (stderrBytes > 65536) cancel();
1140
- });
1141
- child.on("error", cancel);
1142
- child.stdin.on("error", cancel);
1143
- child.on("close", (code) => {
1144
- signal.removeEventListener("abort", cancel);
1145
- kill();
1146
- if (invalid2 || code !== 0 || signal.aborted || closed) rejectCommand(new NodeMlsDevCustodyError());
1147
- else resolveCommand(Buffer.concat(chunks));
1148
- });
1149
- if (signal.aborted) cancel();
1150
- else child.stdin.end(input);
1151
- });
1152
- children.add(result);
1153
- void result.then(() => children.delete(result), () => children.delete(result));
1154
- return result;
1155
- }
1156
- __name(command, "command");
1157
- async function verify(signal) {
1158
- alive(signal);
1159
- requireValue(platform() === "darwin");
1160
- const directory = await lstat(captured.bundlePath);
1161
- requireValue(directory.isDirectory() && !directory.isSymbolicLink() && (directory.mode & 18) === 0 && await realpath(captured.bundlePath) === captured.bundlePath);
1162
- const manifestBytes = await file(captured.bundlePath + ".manifest.json", 65536);
1163
- alive(signal);
1164
- requireValue(hash(manifestBytes) === captured.manifestSha256);
1165
- const manifest = record(parseJson(manifestBytes), ["schemaVersion", "purpose", "buildStatus", "bundleIdentifier", "teamIdentifier", "executable", "infoPlist", "nativeLibrarySha256", "sourceSha", "sourceDirty", "sourceHashes"]);
1166
- requireValue(manifest.schemaVersion === 1 && manifest.purpose === "canon.mls.dev-macos-custody" && manifest.buildStatus === "signed" && manifest.bundleIdentifier === captured.bundleIdentifier && manifest.teamIdentifier === captured.teamIdentifier);
1167
- for (const [name, expectedPath, maximum] of [["executable", EXECUTABLE, 128 * 1024 * 1024], ["infoPlist", "Contents/Info.plist", 32768]]) {
1168
- const artifact = record(manifest[name], ["path", "sha256", "bytes"]);
1169
- requireValue(artifact.path === expectedPath && typeof artifact.sha256 === "string" && HASH.test(artifact.sha256));
1170
- const bytes = await file(join(captured.bundlePath, expectedPath), maximum);
1171
- alive(signal);
1172
- requireValue(artifact.bytes === bytes.length && artifact.sha256 === hash(bytes));
1173
- if (name === "executable") requireValue(artifact.sha256 === captured.executableSha256);
1174
- }
1175
- const requirement = `anchor apple generic and identifier "${captured.bundleIdentifier}" and certificate leaf[subject.OU] = "${captured.teamIdentifier}"`;
1176
- await command("/usr/bin/codesign", ["--verify", "--strict", "--all-architectures", `-R=${requirement}`, captured.bundlePath], null, signal, 16384);
1177
- alive(signal);
1178
- return join(captured.bundlePath, EXECUTABLE);
1179
- }
1180
- __name(verify, "verify");
1181
- async function invoke(operation, values, signal) {
1182
- const executable = await verify(signal);
1183
- alive(signal);
1184
- const output = await command(executable, [], frame(operation, values), signal, MAX_OUTPUT);
1185
- alive(signal);
1186
- await verify(signal);
1187
- alive(signal);
1188
- return custodyValue(output, operation);
1189
- }
1190
- __name(invoke, "invoke");
1191
- async function job(work) {
1192
- const controller = new AbortController();
1193
- let timer;
1194
- const cancel = /* @__PURE__ */ __name(() => controller.abort(), "cancel");
1195
- jobs.add(controller);
1196
- try {
1197
- alive(controller.signal);
1198
- captured.signal?.addEventListener("abort", cancel, { once: true });
1199
- const cancelled = new Promise((_, reject) => {
1200
- controller.signal.addEventListener("abort", () => reject(new NodeMlsDevCustodyError()), { once: true });
1201
- timer = setTimeout(cancel, captured.timeoutMs);
1202
- });
1203
- return await Promise.race([work(controller.signal), cancelled]);
1204
- } catch (error) {
1205
- return custodyFailure(error);
1206
- } finally {
1207
- if (timer) clearTimeout(timer);
1208
- controller.abort();
1209
- captured.signal?.removeEventListener("abort", cancel);
1210
- jobs.delete(controller);
1211
- }
1212
- }
1213
- __name(job, "job");
1214
- return typedNodeMlsDevCustody(job, invoke, async () => {
1215
- closed = true;
1216
- for (const job2 of jobs) job2.abort();
1217
- await Promise.allSettled([...children]);
1218
- });
1219
- }
1220
- __name(createNodeMlsDevCustody, "createNodeMlsDevCustody");
1221
-
1222
- // ../endpoint/src/portable-node-mls-custody.ts
1223
- import { spawn as spawn2 } from "node:child_process";
1224
- import { createHash as createHash2 } from "node:crypto";
1225
- import { constants as constants2 } from "node:fs";
1226
- import { lstat as lstat2, open as open2, realpath as realpath2 } from "node:fs/promises";
1227
- import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2 } from "node:path";
1228
- import { platform as platform2 } from "node:os";
1229
- var HASH2 = /^[0-9a-f]{64}$/;
1230
- var absolute = /* @__PURE__ */ __name((value) => typeof value === "string" && isAbsolute2(value) && resolve2(value) === value && !value.includes("\0"), "absolute");
1231
- var hash2 = /* @__PURE__ */ __name((bytes) => createHash2("sha256").update(bytes).digest("hex"), "hash");
1232
- var sameIdentity = /* @__PURE__ */ __name((a, b) => a.version === b.version && a.environmentId === b.environmentId && a.installationId === b.installationId && a.principal.type === b.principal.type && a.principal.id === b.principal.id && ("ownershipVersion" in a.principal ? a.principal.ownershipVersion : void 0) === ("ownershipVersion" in b.principal ? b.principal.ownershipVersion : void 0), "sameIdentity");
1233
- function createPortableNodeMlsDevCustody(options) {
1234
- const captured = (() => {
1235
- try {
1236
- const fields = ["executablePath", "executableSha256", "configurationPath", "configurationSha256", "secretSource"];
1237
- if (Object.hasOwn(options, "timeoutMs")) fields.push("timeoutMs");
1238
- if (Object.hasOwn(options, "signal")) fields.push("signal");
1239
- const value = record(options, fields), source = record(value.secretSource, ["kind", "path"]);
1240
- requireValue(absolute(value.executablePath) && absolute(value.configurationPath) && absolute(source.path) && source.kind === "managed-file");
1241
- for (const field of ["executableSha256", "configurationSha256"]) requireValue(typeof value[field] === "string" && HASH2.test(value[field]));
1242
- const timeoutMs = value.timeoutMs ?? 3e4;
1243
- requireValue(typeof timeoutMs === "number" && Number.isSafeInteger(timeoutMs) && timeoutMs >= 100 && timeoutMs <= 6e4);
1244
- requireValue(value.signal === void 0 || value.signal instanceof AbortSignal);
1245
- return Object.freeze({ ...value, secretSource: Object.freeze({ ...source }), timeoutMs });
1246
- } catch {
1247
- return rejected();
1248
- }
1249
- })();
1250
- let closed = false;
1251
- const jobs = /* @__PURE__ */ new Set(), children = /* @__PURE__ */ new Set();
1252
- const alive = /* @__PURE__ */ __name((signal) => requireValue(!closed && !signal.aborted && !captured.signal?.aborted), "alive");
1253
- async function trustedExecutableAncestry() {
1254
- for (let path = dirname(captured.executablePath); ; path = dirname(path)) {
1255
- const info = await lstat2(path);
1256
- requireValue(info.isDirectory() && !info.isSymbolicLink() && (info.uid === process.getuid?.() || info.uid === 0) && (info.mode & 18) === 0);
1257
- if (dirname(path) === path) return;
1258
- }
1259
- }
1260
- __name(trustedExecutableAncestry, "trustedExecutableAncestry");
1261
- async function checkedFile(path, maximum, secret = false) {
1262
- requireValue(await realpath2(path) === path);
1263
- const info = await lstat2(path);
1264
- requireValue(info.isFile() && !info.isSymbolicLink() && info.nlink === 1 && info.size > 0 && info.size <= maximum && (info.uid === process.getuid?.() || info.uid === 0) && (info.mode & (secret ? 63 : 18)) === 0);
1265
- const file = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
1266
- try {
1267
- const current = await file.stat();
1268
- requireValue(current.ino === info.ino && current.dev === info.dev && current.size === info.size && current.mode === info.mode && current.uid === info.uid && current.nlink === 1 && await realpath2(path) === path);
1269
- return file;
1270
- } catch (error) {
1271
- await file.close();
1272
- throw error;
1273
- }
1274
- }
1275
- __name(checkedFile, "checkedFile");
1276
- async function publicBytes(path, maximum) {
1277
- const file = await checkedFile(path, maximum);
1278
- try {
1279
- const before = await file.stat(), bytes = Buffer.alloc(before.size);
1280
- let length = 0;
1281
- while (length < bytes.length) {
1282
- const read = await file.read(bytes, length, bytes.length - length, length);
1283
- requireValue(read.bytesRead > 0);
1284
- length += read.bytesRead;
1285
- }
1286
- const after = await file.stat();
1287
- requireValue(after.size === before.size && after.mtimeMs === before.mtimeMs && after.ctimeMs === before.ctimeMs);
1288
- return bytes;
1289
- } finally {
1290
- await file.close();
1291
- }
1292
- }
1293
- __name(publicBytes, "publicBytes");
1294
- async function verify(signal) {
1295
- alive(signal);
1296
- requireValue(platform2() === "darwin" || platform2() === "linux");
1297
- await trustedExecutableAncestry();
1298
- alive(signal);
1299
- const binary = await publicBytes(captured.executablePath, 128 * 1024 * 1024);
1300
- alive(signal);
1301
- requireValue(hash2(binary) === captured.executableSha256);
1302
- const bytes = await publicBytes(captured.configurationPath, 65536);
1303
- alive(signal);
1304
- requireValue(hash2(bytes) === captured.configurationSha256);
1305
- const config = record(parseJson(bytes), ["version", "environmentId", "context", "vaultPath", "issuerKeys", "receiptKeys"]);
1306
- requireValue(config.version === 1 && isMlsEnvironmentId(config.environmentId) && absolute(config.vaultPath));
1307
- const configuredContext = record(config.context, ["version", "environmentId", "principal", "installationId"]);
1308
- const context = parseMlsDevDeviceContext({ ...configuredContext, revision: 0 });
1309
- requireValue(context.environmentId === config.environmentId);
1310
- const vaultParent = dirname(config.vaultPath);
1311
- requireValue(await realpath2(vaultParent) === vaultParent);
1312
- const parent = await lstat2(vaultParent);
1313
- requireValue(parent.isDirectory() && parent.uid === process.getuid?.() && (parent.mode & 63) === 0);
1314
- const fromVault = relative(vaultParent, captured.secretSource.path);
1315
- requireValue(fromVault.startsWith("../") || isAbsolute2(fromVault));
1316
- requireValue(captured.secretSource.path !== captured.configurationPath && captured.secretSource.path !== captured.executablePath);
1317
- return { context, vaultParent };
1318
- }
1319
- __name(verify, "verify");
1320
- async function command(input, provision, signal) {
1321
- const secret = await checkedFile(captured.secretSource.path, 32, true);
1322
- try {
1323
- requireValue((await secret.stat()).size === 32);
1324
- alive(signal);
1325
- const result = new Promise((resolveCommand, rejectCommand) => {
1326
- const child = spawn2(
1327
- captured.executablePath,
1328
- [
1329
- "--config",
1330
- captured.configurationPath,
1331
- "--config-sha256",
1332
- captured.configurationSha256,
1333
- "--secret-fd",
1334
- "3",
1335
- ...provision ? ["--provision"] : []
1336
- ],
1337
- { detached: true, stdio: ["pipe", "pipe", "pipe", secret.fd], env: { PATH: "/usr/bin:/bin" } }
1338
- );
1339
- let invalid2 = false, stdoutBytes = 0, stderrBytes = 0;
1340
- const chunks = [];
1341
- const kill = /* @__PURE__ */ __name(() => {
1342
- try {
1343
- if (child.pid) process.kill(-child.pid, "SIGKILL");
1344
- } catch (error) {
1345
- if (error.code !== "ESRCH") invalid2 = true;
1346
- }
1347
- }, "kill");
1348
- const cancel = /* @__PURE__ */ __name(() => {
1349
- invalid2 = true;
1350
- kill();
1351
- }, "cancel");
1352
- signal.addEventListener("abort", cancel, { once: true });
1353
- child.stdout.on("data", (chunk) => {
1354
- stdoutBytes += chunk.length;
1355
- if (stdoutBytes > MAX_OUTPUT) cancel();
1356
- else chunks.push(chunk);
1357
- });
1358
- child.stderr.on("data", (chunk) => {
1359
- stderrBytes += chunk.length;
1360
- if (stderrBytes > 65536) cancel();
1361
- });
1362
- child.on("error", cancel);
1363
- child.stdin.on("error", cancel);
1364
- child.on("close", (code) => {
1365
- signal.removeEventListener("abort", cancel);
1366
- kill();
1367
- if (invalid2 || code !== 0 || signal.aborted || closed) rejectCommand(new NodeMlsDevCustodyError());
1368
- else resolveCommand(Buffer.concat(chunks));
1369
- });
1370
- if (signal.aborted) cancel();
1371
- else child.stdin.end(input);
1372
- });
1373
- children.add(result);
1374
- void result.then(() => children.delete(result), () => children.delete(result));
1375
- return await result;
1376
- } finally {
1377
- await secret.close();
1378
- }
1379
- }
1380
- __name(command, "command");
1381
- async function invoke(operation, values, signal) {
1382
- const config = await verify(signal);
1383
- requireValue(values[0] !== null && sameIdentity(parseMlsDevDeviceContext(JSON.parse(values[0])), config.context));
1384
- const output = await command(frame(operation, values), false, signal);
1385
- alive(signal);
1386
- await verify(signal);
1387
- alive(signal);
1388
- return custodyValue(output, operation);
1389
- }
1390
- __name(invoke, "invoke");
1391
- async function job(work) {
1392
- const controller = new AbortController();
1393
- let timer;
1394
- const cancel = /* @__PURE__ */ __name(() => controller.abort(), "cancel");
1395
- jobs.add(controller);
1396
- try {
1397
- alive(controller.signal);
1398
- captured.signal?.addEventListener("abort", cancel, { once: true });
1399
- const cancellation = new Promise((_, reject) => {
1400
- controller.signal.addEventListener("abort", () => reject(new NodeMlsDevCustodyError()), { once: true });
1401
- timer = setTimeout(cancel, captured.timeoutMs);
1402
- });
1403
- return await Promise.race([work(controller.signal), cancellation]);
1404
- } catch (error) {
1405
- return custodyFailure(error);
1406
- } finally {
1407
- if (timer) clearTimeout(timer);
1408
- controller.abort();
1409
- captured.signal?.removeEventListener("abort", cancel);
1410
- jobs.delete(controller);
1411
- }
1412
- }
1413
- __name(job, "job");
1414
- const custody = typedNodeMlsDevCustody(job, invoke, async () => {
1415
- closed = true;
1416
- for (const job2 of jobs) job2.abort();
1417
- await Promise.allSettled([...children]);
1418
- });
1419
- return Object.freeze({ ...custody, provision: /* @__PURE__ */ __name((context) => job(async (signal) => {
1420
- const config = await verify(signal);
1421
- requireValue(sameIdentity(parseMlsDevDeviceContext(context), config.context));
1422
- const output = await command(null, true, signal);
1423
- alive(signal);
1424
- await verify(signal);
1425
- alive(signal);
1426
- const result = record(parseJson(output), ["version", "ok", "value"]);
1427
- requireValue(result.version === 1 && result.ok === true && result.value === null);
1428
- }), "provision") });
1429
- }
1430
- __name(createPortableNodeMlsDevCustody, "createPortableNodeMlsDevCustody");
1431
-
1432
- // ../endpoint/src/node.ts
1433
- var databases = /* @__PURE__ */ new Map();
1434
- async function createNodeEndpointStore(options) {
1435
- const directory = resolve3(options.directory);
1436
- await mkdir(directory, { recursive: true, mode: 448 });
1437
- const filename = join2(directory, `${createHash3("sha256").update(options.namespace).digest("hex")}.sqlite`);
1438
- let opening = databases.get(filename);
1439
- if (!opening) {
1440
- opening = (async () => {
1441
- const sqlite = new DatabaseSync(filename);
1442
- try {
1443
- await chmod(filename, 384);
1444
- sqlite.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
1445
- return { sqlite, tail: Promise.resolve(), references: 0 };
1446
- } catch (error) {
1447
- sqlite.close();
1448
- throw error;
1449
- }
1450
- })();
1451
- databases.set(filename, opening);
1452
- void opening.catch(() => {
1453
- if (databases.get(filename) === opening) databases.delete(filename);
1454
- });
1455
- }
1456
- const shared = await opening;
1457
- if (shared.closed) return createNodeEndpointStore(options);
1458
- shared.references++;
1459
- let closing;
1460
- const database = {
1461
- transaction(work) {
1462
- if (closing) return Promise.reject(new Error("Endpoint store is closed"));
1463
- const result = shared.tail.then(async () => {
1464
- shared.sqlite.exec("BEGIN IMMEDIATE");
1465
- try {
1466
- const transaction = {
1467
- async all(sql, parameters = []) {
1468
- return shared.sqlite.prepare(sql).all(...parameters);
1469
- },
1470
- async run(sql, parameters = []) {
1471
- shared.sqlite.prepare(sql).run(...parameters);
1472
- }
1473
- };
1474
- const value = await work(transaction);
1475
- shared.sqlite.exec("COMMIT");
1476
- return value;
1477
- } catch (error) {
1478
- shared.sqlite.exec("ROLLBACK");
1479
- throw error;
1480
- }
1481
- });
1482
- shared.tail = result.catch(() => void 0);
1483
- return result;
1484
- },
1485
- close() {
1486
- if (closing) return closing;
1487
- closing = shared.tail.then(() => {
1488
- shared.references--;
1489
- if (!shared.references) {
1490
- shared.closed = true;
1491
- if (databases.get(filename) === opening) databases.delete(filename);
1492
- shared.sqlite.close();
1493
- }
1494
- });
1495
- return closing;
1496
- }
1497
- };
1498
- try {
1499
- return await createSqliteEndpointStore({ database, namespace: options.namespace });
1500
- } catch (error) {
1501
- await database.close?.();
1502
- throw error;
1503
- }
1504
- }
1505
- __name(createNodeEndpointStore, "createNodeEndpointStore");
1506
-
1507
119
  // ../core/src/node-media.ts
1508
- import { createHash as createHash4, randomUUID } from "node:crypto";
120
+ import { createHash, randomUUID } from "node:crypto";
1509
121
  import { createReadStream, createWriteStream } from "node:fs";
1510
- import { mkdir as mkdir2, open as open3, readdir, rename, stat, unlink } from "node:fs/promises";
1511
- import { join as join3 } from "node:path";
122
+ import { mkdir, open, readdir, rename, stat, unlink } from "node:fs/promises";
123
+ import { join } from "node:path";
1512
124
  import { Transform } from "node:stream";
1513
125
  import { pipeline } from "node:stream/promises";
1514
126
  var MAX_BYTES = 100 * 1024 * 1024;
1515
127
  function createNodeEndpointMediaBytes(options) {
1516
128
  const expectedDigests = /* @__PURE__ */ new Map();
1517
- const initialized = mkdir2(options.directory, { recursive: true, mode: 448 });
129
+ const initialized = mkdir(options.directory, { recursive: true, mode: 448 });
1518
130
  const referencePath = /* @__PURE__ */ __name((reference) => {
1519
131
  if (!/^[a-f0-9]{64}\.bytes$/.test(reference)) throw new Error("Invalid retained Canon media reference");
1520
- return join3(options.directory, reference);
132
+ return join(options.directory, reference);
1521
133
  }, "referencePath");
1522
134
  async function digest(source) {
1523
- const handle = await open3(source, "r");
135
+ const handle = await open(source, "r");
1524
136
  try {
1525
137
  const info = await handle.stat();
1526
138
  if (!info.isFile() || info.size < 1 || info.size > MAX_BYTES) throw new Error("Canon media must be a regular file from 1 byte to 100 MiB");
1527
- const hash3 = createHash4("sha256");
139
+ const hash = createHash("sha256");
1528
140
  let size = 0;
1529
141
  for await (const chunk of handle.createReadStream({ autoClose: false })) {
1530
142
  size += chunk.length;
1531
143
  if (size > MAX_BYTES) throw new Error("Canon media grew beyond its upload limit");
1532
- hash3.update(chunk);
144
+ hash.update(chunk);
1533
145
  }
1534
- const result = hash3.digest("hex");
146
+ const result = hash.digest("hex");
1535
147
  expectedDigests.set(source, result);
1536
148
  return result;
1537
149
  } finally {
@@ -1545,36 +157,38 @@ function createNodeEndpointMediaBytes(options) {
1545
157
  await initialized;
1546
158
  const expected = expectedDigests.get(source);
1547
159
  if (!expected) throw new Error("Canon media must be fingerprinted before retaining bytes");
1548
- const reference = `${createHash4("sha256").update(key).digest("hex")}.bytes`;
160
+ const reference = `${createHash("sha256").update(key).digest("hex")}.bytes`;
1549
161
  const destination = referencePath(reference);
1550
162
  const temporary = `${destination}.${randomUUID()}.tmp`;
1551
- const sourceHandle = await open3(source, "r");
163
+ const sourceHandle = await open(source, "r");
1552
164
  try {
1553
165
  const info = await sourceHandle.stat();
1554
166
  if (!info.isFile()) throw new Error("Canon media source is no longer a regular file");
1555
- const hash3 = createHash4("sha256");
167
+ const hash = createHash("sha256");
1556
168
  let size = 0;
1557
169
  await pipeline(sourceHandle.createReadStream({ autoClose: false }), new Transform({
1558
170
  transform(chunk, _encoding, callback) {
1559
171
  size += chunk.length;
1560
172
  if (size > MAX_BYTES) return callback(new Error("Canon media grew beyond its upload limit"));
1561
- hash3.update(chunk);
173
+ hash.update(chunk);
1562
174
  callback(null, chunk);
1563
175
  }
1564
176
  }), createWriteStream(temporary, { flags: "wx", mode: 384 }));
1565
- if (hash3.digest("hex") !== expected) throw new Error("Canon media changed while preparing delivery");
1566
- const retained = await open3(temporary, "r");
177
+ if (hash.digest("hex") !== expected) throw new Error("Canon media changed while preparing delivery");
178
+ const retained = await open(temporary, "r+");
1567
179
  try {
1568
180
  await retained.sync();
1569
181
  } finally {
1570
182
  await retained.close();
1571
183
  }
1572
184
  await rename(temporary, destination);
1573
- const directory = await open3(options.directory, "r");
1574
- try {
1575
- await directory.sync();
1576
- } finally {
1577
- await directory.close();
185
+ if (process.platform !== "win32") {
186
+ const directory = await open(options.directory, "r");
187
+ try {
188
+ await directory.sync();
189
+ } finally {
190
+ await directory.close();
191
+ }
1578
192
  }
1579
193
  return reference;
1580
194
  } catch (error) {
@@ -1625,7 +239,7 @@ function createNodeEndpointMediaBytes(options) {
1625
239
  async discardAll() {
1626
240
  await initialized;
1627
241
  for (const name of await readdir(options.directory)) {
1628
- if (/^[a-f0-9]{64}\.bytes(?:\.[a-f0-9-]+\.tmp)?$/.test(name)) await unlink(join3(options.directory, name));
242
+ if (/^[a-f0-9]{64}\.bytes(?:\.[a-f0-9-]+\.tmp)?$/.test(name)) await unlink(join(options.directory, name));
1629
243
  }
1630
244
  }
1631
245
  };
@@ -1777,15 +391,15 @@ function normalizeDefaults(environmentId, value) {
1777
391
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1778
392
  throw new Error(`Invalid Canon runtime endpoint defaults for ${environmentId}`);
1779
393
  }
1780
- const record7 = value;
1781
- const firebaseWebApiKey = typeof record7.firebaseWebApiKey === "string" ? record7.firebaseWebApiKey.trim() : "";
394
+ const record6 = value;
395
+ const firebaseWebApiKey = typeof record6.firebaseWebApiKey === "string" ? record6.firebaseWebApiKey.trim() : "";
1782
396
  if (!firebaseWebApiKey) {
1783
397
  throw new Error(`Missing Canon runtime endpoint default: ${environmentId}.firebaseWebApiKey`);
1784
398
  }
1785
399
  return Object.freeze({
1786
- apiBaseUrl: normalizedHttpsUrl(record7.apiBaseUrl, `${environmentId}.apiBaseUrl`),
1787
- streamUrl: normalizedHttpsUrl(record7.streamUrl, `${environmentId}.streamUrl`),
1788
- rtdbUrl: normalizedHttpsUrl(record7.rtdbUrl, `${environmentId}.rtdbUrl`),
400
+ apiBaseUrl: normalizedHttpsUrl(record6.apiBaseUrl, `${environmentId}.apiBaseUrl`),
401
+ streamUrl: normalizedHttpsUrl(record6.streamUrl, `${environmentId}.streamUrl`),
402
+ rtdbUrl: normalizedHttpsUrl(record6.rtdbUrl, `${environmentId}.rtdbUrl`),
1789
403
  firebaseWebApiKey
1790
404
  });
1791
405
  }
@@ -1879,13 +493,7 @@ var DEFAULT_CANON_RUNTIME_ENDPOINTS = requireCanonRuntimeEndpointDefaults(
1879
493
  var DEFAULT_BASE_URL = DEFAULT_CANON_RUNTIME_ENDPOINTS.apiBaseUrl;
1880
494
 
1881
495
  // ../core/src/client.ts
1882
- import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
1883
-
1884
- // ../endpoint/src/store.ts
1885
- function copyJson(value) {
1886
- return JSON.parse(JSON.stringify(value));
1887
- }
1888
- __name(copyJson, "copyJson");
496
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
1889
497
 
1890
498
  // ../endpoint/src/prepared-content.ts
1891
499
  var protocolKey = /* @__PURE__ */ __name((scope5) => `protocol-state/${encodeURIComponent(scope5)}`, "protocolKey");
@@ -2052,23 +660,23 @@ async function isFirstOrderedOperation(tx, operation) {
2052
660
  }
2053
661
  }
2054
662
  __name(isFirstOrderedOperation, "isFirstOrderedOperation");
2055
- var inboundReadyKey = /* @__PURE__ */ __name((record7) => record7.queueKey ?? `inbound-ready/${record7.event.id}`, "inboundReadyKey");
2056
- async function orderInbound(tx, record7) {
2057
- if (record7.queueKey) return record7;
2058
- const message = record7.event.data.message;
2059
- const sourceTime = message?.createdAt ?? record7.event.data.createdAt;
663
+ var inboundReadyKey = /* @__PURE__ */ __name((record6) => record6.queueKey ?? `inbound-ready/${record6.event.id}`, "inboundReadyKey");
664
+ async function orderInbound(tx, record6) {
665
+ if (record6.queueKey) return record6;
666
+ const message = record6.event.data.message;
667
+ const sourceTime = message?.createdAt ?? record6.event.data.createdAt;
2060
668
  const millis = typeof sourceTime === "string" ? Date.parse(sourceTime) : typeof sourceTime === "number" ? sourceTime : NaN;
2061
- const timestamp = Number.isFinite(millis) ? new Date(millis).toISOString() : record7.createdAt;
2062
- const tie = Number.isFinite(millis) ? `0-${lexicalIdentity(record7.event.id)}` : `1-${ordinal(await nextQueueSequence(tx, "inbound"))}`;
2063
- return { ...record7, queueKey: `inbound-ready/${scope(record7.event.conversationId)}/${timestamp}/${tie}` };
669
+ const timestamp = Number.isFinite(millis) ? new Date(millis).toISOString() : record6.createdAt;
670
+ const tie = Number.isFinite(millis) ? `0-${lexicalIdentity(record6.event.id)}` : `1-${ordinal(await nextQueueSequence(tx, "inbound"))}`;
671
+ return { ...record6, queueKey: `inbound-ready/${scope(record6.event.conversationId)}/${timestamp}/${tie}` };
2064
672
  }
2065
673
  __name(orderInbound, "orderInbound");
2066
- async function enqueueInbound(tx, record7) {
2067
- await tx.put(inboundReadyKey(record7), { id: record7.event.id, kind: record7.event.kind, conversationId: record7.event.conversationId });
674
+ async function enqueueInbound(tx, record6) {
675
+ await tx.put(inboundReadyKey(record6), { id: record6.event.id, kind: record6.event.kind, conversationId: record6.event.conversationId });
2068
676
  }
2069
677
  __name(enqueueInbound, "enqueueInbound");
2070
- async function removeInboundReady(tx, record7) {
2071
- await tx.delete(inboundReadyKey(record7));
678
+ async function removeInboundReady(tx, record6) {
679
+ await tx.delete(inboundReadyKey(record6));
2072
680
  }
2073
681
  __name(removeInboundReady, "removeInboundReady");
2074
682
 
@@ -2203,11 +811,11 @@ function createMessageGroupCoordinator(input) {
2203
811
  const group = await tx.get(groupKey(id2));
2204
812
  if (!group) return;
2205
813
  const children = await Promise.all(group.children.map((child) => tx.get(`operation/${child.operationId}`)));
2206
- const rejected2 = children.find((child) => child?.status === "rejected" || child?.status === "succeeded" && !messageGroupChildSucceeded(child));
814
+ const rejected = children.find((child) => child?.status === "rejected" || child?.status === "succeeded" && !messageGroupChildSucceeded(child));
2207
815
  const uncertain = children.find((child) => child?.status === "uncertain");
2208
816
  const succeeded = children.every(messageGroupChildSucceeded);
2209
- const status = succeeded ? "succeeded" : rejected2 || group.cancelled ? "rejected" : uncertain ? "uncertain" : "pending";
2210
- const error = rejected2?.receipt?.error ?? (rejected2 ? { code: "PROACTIVE_NOT_ADMITTED", status: 403, message: "Proactive message was not admitted", retryable: false } : void 0) ?? (group.cancelled ? { code: "OPERATION_DISCARDED", message: "Remaining message parts were cancelled", retryable: false } : uncertain?.receipt?.error);
817
+ const status = succeeded ? "succeeded" : rejected || group.cancelled ? "rejected" : uncertain ? "uncertain" : "pending";
818
+ const error = rejected?.receipt?.error ?? (rejected ? { code: "PROACTIVE_NOT_ADMITTED", status: 403, message: "Proactive message was not admitted", retryable: false } : void 0) ?? (group.cancelled ? { code: "OPERATION_DISCARDED", message: "Remaining message parts were cancelled", retryable: false } : uncertain?.receipt?.error);
2211
819
  return {
2212
820
  operationId: id2,
2213
821
  installationId: group.installationId,
@@ -2597,12 +1205,12 @@ async function assertMlsDevPlaintextAllowed(tx, intent2, lane = "operation.reque
2597
1205
  const conversationId = endpointIntentConversationId(intent2);
2598
1206
  if (!conversationId) return;
2599
1207
  if (!await isMlsDevRoomProtected(tx, conversationId)) return;
2600
- const failure2 = /* @__PURE__ */ __name(() => new EndpointError("This MLS conversation requires encrypted content", {
1208
+ const failure = /* @__PURE__ */ __name(() => new EndpointError("This MLS conversation requires encrypted content", {
2601
1209
  code: "MLS_DEV_ENCRYPTED_CONTENT_REQUIRED",
2602
1210
  retryable: false
2603
1211
  }), "failure");
2604
1212
  const action = intent2.action === "verb_execute" ? intent2.body.verb : intent2.action;
2605
- if (typeof action === "string" && MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS.some((unsupported) => unsupported === action)) throw failure2();
1213
+ if (typeof action === "string" && MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS.some((unsupported) => unsupported === action)) throw failure();
2606
1214
  const disclosed = lane === "operation.request" && Object.hasOwn(MLS_DEV_DISCLOSED_PROCESSING_FIELDS, intent2.action) ? MLS_DEV_DISCLOSED_PROCESSING_FIELDS[intent2.action] : void 0;
2607
1215
  if (disclosed && !intent2.envelope && Object.keys(intent2.body).every((field) => disclosed.includes(field))) return;
2608
1216
  const payload = lane === "operation.request" ? { body: { encoding: "json", value: intent2.body }, ...intent2.envelope ? { envelope: intent2.envelope } : {} } : intent2.body;
@@ -2610,7 +1218,7 @@ async function assertMlsDevPlaintextAllowed(tx, intent2, lane = "operation.reque
2610
1218
  try {
2611
1219
  boundary = endpointContentBoundary(lane, intent2.action, payload);
2612
1220
  } catch {
2613
- throw failure2();
1221
+ throw failure();
2614
1222
  }
2615
1223
  const privateLeaf = /* @__PURE__ */ __name((value, path) => {
2616
1224
  if (value && typeof value === "object") {
@@ -2620,8 +1228,8 @@ async function assertMlsDevPlaintextAllowed(tx, intent2, lane = "operation.reque
2620
1228
  return boundary.classify(path) !== "public-metadata";
2621
1229
  }, "privateLeaf");
2622
1230
  if (lane === "operation.request") {
2623
- if (privateLeaf(intent2.body, ["body", "value"]) || intent2.envelope && privateLeaf(intent2.envelope, ["envelope"])) throw failure2();
2624
- } else if (privateLeaf(intent2.body, [])) throw failure2();
1231
+ if (privateLeaf(intent2.body, ["body", "value"]) || intent2.envelope && privateLeaf(intent2.envelope, ["envelope"])) throw failure();
1232
+ } else if (privateLeaf(intent2.body, [])) throw failure();
2625
1233
  }
2626
1234
  __name(assertMlsDevPlaintextAllowed, "assertMlsDevPlaintextAllowed");
2627
1235
 
@@ -2630,15 +1238,15 @@ function isInboundProjectionKind(kind) {
2630
1238
  return ["artifact.changed", "message.updated", "message.media_terminal"].includes(kind);
2631
1239
  }
2632
1240
  __name(isInboundProjectionKind, "isInboundProjectionKind");
2633
- async function settleInboundProjection(tx, record7, timestamp) {
2634
- if (!isInboundProjectionKind(record7.event.kind)) return false;
2635
- await removeInboundReady(tx, record7);
2636
- if (record7.state !== "settled" && record7.state !== "uncertain" && !record7.externalClaim) {
2637
- await tx.put(`inbound/${record7.event.id}`, {
2638
- event: copyJson(record7.event),
1241
+ async function settleInboundProjection(tx, record6, timestamp) {
1242
+ if (!isInboundProjectionKind(record6.event.kind)) return false;
1243
+ await removeInboundReady(tx, record6);
1244
+ if (record6.state !== "settled" && record6.state !== "uncertain" && !record6.externalClaim) {
1245
+ await tx.put(`inbound/${record6.event.id}`, {
1246
+ event: copyJson(record6.event),
2639
1247
  state: "settled",
2640
1248
  reason: "projection_hint",
2641
- createdAt: record7.createdAt,
1249
+ createdAt: record6.createdAt,
2642
1250
  updatedAt: timestamp
2643
1251
  });
2644
1252
  }
@@ -2707,9 +1315,9 @@ async function recordInboundEvent(tx, incoming, timestamp) {
2707
1315
  return true;
2708
1316
  }
2709
1317
  if (existing) return false;
2710
- const record7 = await orderInbound(tx, { event: copyJson(event), state: "observed", createdAt: timestamp, updatedAt: timestamp });
2711
- await tx.put(key, record7);
2712
- await enqueueInbound(tx, record7);
1318
+ const record6 = await orderInbound(tx, { event: copyJson(event), state: "observed", createdAt: timestamp, updatedAt: timestamp });
1319
+ await tx.put(key, record6);
1320
+ await enqueueInbound(tx, record6);
2713
1321
  return true;
2714
1322
  }
2715
1323
  __name(recordInboundEvent, "recordInboundEvent");
@@ -2740,15 +1348,15 @@ async function recordVerifiedMlsInboundEvent(tx, event, feedOrdinal, timestamp,
2740
1348
  if (!event.conversationId || !Number.isSafeInteger(feedOrdinal) || feedOrdinal < 1) throw new Error("Invalid verified MLS input");
2741
1349
  const key = `inbound/${event.id}`, existing = await tx.get(key);
2742
1350
  if (existing && existing.reason !== "mls_plaintext_withheld") return false;
2743
- const record7 = {
1351
+ const record6 = {
2744
1352
  event: copyJson(event),
2745
1353
  state: own ? "settled" : "observed",
2746
1354
  ...own ? { reason: "own_mls_message" } : { queueKey: `inbound-ready/${encodeURIComponent(event.conversationId)}/mls/${String(feedOrdinal).padStart(16, "0")}/${encodeURIComponent(event.id)}` },
2747
1355
  createdAt: timestamp,
2748
1356
  updatedAt: timestamp
2749
1357
  };
2750
- await tx.put(key, record7);
2751
- if (!own) await enqueueInbound(tx, record7);
1358
+ await tx.put(key, record6);
1359
+ if (!own) await enqueueInbound(tx, record6);
2752
1360
  return true;
2753
1361
  }
2754
1362
  __name(recordVerifiedMlsInboundEvent, "recordVerifiedMlsInboundEvent");
@@ -2763,10 +1371,10 @@ function createInboundConsumer(input) {
2763
1371
  let timer;
2764
1372
  const scheduled = /* @__PURE__ */ new Set();
2765
1373
  const rooms = /* @__PURE__ */ new Map();
2766
- function matches2(record7) {
2767
- return record7.event.kind === options.kind && (!options.conversationId || record7.event.conversationId === options.conversationId);
1374
+ function matches(record6) {
1375
+ return record6.event.kind === options.kind && (!options.conversationId || record6.event.conversationId === options.conversationId);
2768
1376
  }
2769
- __name(matches2, "matches");
1377
+ __name(matches, "matches");
2770
1378
  function report(error) {
2771
1379
  try {
2772
1380
  options.onError?.(error);
@@ -2788,15 +1396,15 @@ function createInboundConsumer(input) {
2788
1396
  if (await input.refreshHandoff?.(id2)) return true;
2789
1397
  const selected = await store.transaction(async (tx) => {
2790
1398
  const current = await tx.get(`inbound/${id2}`);
2791
- if (!current || current.deleted || !matches2(current) || current.state === "uncertain" || current.state === "settled") return { blocked: false };
1399
+ if (!current || current.deleted || !matches(current) || current.state === "uncertain" || current.state === "settled") return { blocked: false };
2792
1400
  if (current.lease && current.lease.expiresAt > clock()) return { blocked: current.lease.owner !== owner };
2793
1401
  const next = { ...current, state: "offered", lease: { owner, expiresAt: clock() + leaseMs }, updatedAt: new Date(clock()).toISOString() };
2794
1402
  delete next.handoffGeneration;
2795
1403
  await tx.put(`inbound/${id2}`, next);
2796
1404
  return { record: next, blocked: false };
2797
1405
  });
2798
- const record7 = selected.record;
2799
- if (!record7) return !selected.blocked;
1406
+ const record6 = selected.record;
1407
+ if (!record6) return !selected.blocked;
2800
1408
  const heartbeat = setInterval(() => {
2801
1409
  void store.transaction(async (tx) => {
2802
1410
  const current = await tx.get(`inbound/${id2}`);
@@ -2807,8 +1415,8 @@ function createInboundConsumer(input) {
2807
1415
  }, Math.floor(leaseMs / 3));
2808
1416
  heartbeat.unref?.();
2809
1417
  try {
2810
- await input.beforeOffer?.(record7.event);
2811
- await options.offer(record7.event);
1418
+ await input.beforeOffer?.(record6.event);
1419
+ await options.offer(record6.event);
2812
1420
  const handedOff = await endpoint.getInbound(id2);
2813
1421
  return !handedOff || handedOff.state !== "deferred" && handedOff.state !== "observed";
2814
1422
  } catch (error) {
@@ -2874,8 +1482,8 @@ function createInboundConsumer(input) {
2874
1482
  const rows = await tx.scan("inbound-ready/", { after, limit: 100 });
2875
1483
  for (const row of rows) {
2876
1484
  if (!isInboundProjectionKind(row.value.kind)) continue;
2877
- const record7 = await tx.get(`inbound/${row.value.id}`);
2878
- if (record7 && await settleInboundProjection(tx, record7, new Date(clock()).toISOString())) await tx.delete(row.key);
1485
+ const record6 = await tx.get(`inbound/${row.value.id}`);
1486
+ if (record6 && await settleInboundProjection(tx, record6, new Date(clock()).toISOString())) await tx.delete(row.key);
2879
1487
  }
2880
1488
  return rows;
2881
1489
  });
@@ -3362,16 +1970,16 @@ function createEndpointMigration(input) {
3362
1970
  if (canonicalEndpointJson(await tx.get(activeKey)) !== canonicalEndpointJson(progress)) return;
3363
1971
  const cursor = await tx.get(`cursor/${room.conversationId}`);
3364
1972
  if (cursor && (cursor.migrationInventorySha256 !== manifest.inventorySha256 || cursor.cursor !== room.cursor)) invalid("Recovery cursor changed during migration", "MIGRATION_BASELINE_EXISTS");
3365
- for (const record7 of records) {
3366
- const existing = await tx.get(`inbound/${record7.event.id}`);
1973
+ for (const record6 of records) {
1974
+ const existing = await tx.get(`inbound/${record6.event.id}`);
3367
1975
  if (existing && existing.migrationInventorySha256 !== manifest.inventorySha256 && !["observed", "deferred"].includes(existing.state)) invalid("Existing native work requires reconciliation before importing", "MIGRATION_INBOX_CONFLICT");
3368
1976
  }
3369
- for (const record7 of records) {
3370
- const previous = await tx.get(`inbound/${record7.event.id}`);
1977
+ for (const record6 of records) {
1978
+ const previous = await tx.get(`inbound/${record6.event.id}`);
3371
1979
  if (previous) await removeInboundReady(tx, previous);
3372
- const ordered = await orderInbound(tx, record7);
3373
- await tx.put(`inbound/${record7.event.id}`, ordered);
3374
- if (record7.state === "observed" || record7.state === "deferred") await enqueueInbound(tx, ordered);
1980
+ const ordered = await orderInbound(tx, record6);
1981
+ await tx.put(`inbound/${record6.event.id}`, ordered);
1982
+ if (record6.state === "observed" || record6.state === "deferred") await enqueueInbound(tx, ordered);
3375
1983
  }
3376
1984
  const done = progress.item + batch.length >= room.items.length;
3377
1985
  await tx.put(activeKey, { ...progress, room: progress.room + (done ? 1 : 0), item: done ? 0 : progress.item + batch.length });
@@ -3466,7 +2074,7 @@ function createEndpointMediaPipeline(options) {
3466
2074
  const running = /* @__PURE__ */ new Map();
3467
2075
  const staging = /* @__PURE__ */ new Map();
3468
2076
  const cancelledStages = /* @__PURE__ */ new Set();
3469
- const hash3 = options.hash ?? (async (value) => {
2077
+ const hash = options.hash ?? (async (value) => {
3470
2078
  const result = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
3471
2079
  return [...new Uint8Array(result)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
3472
2080
  });
@@ -3528,7 +2136,7 @@ function createEndpointMediaPipeline(options) {
3528
2136
  let activeUploads = 0;
3529
2137
  const uploadWaiters = [];
3530
2138
  async function upload(work) {
3531
- if (activeUploads >= 2) await new Promise((resolve6) => uploadWaiters.push(resolve6));
2139
+ if (activeUploads >= 2) await new Promise((resolve3) => uploadWaiters.push(resolve3));
3532
2140
  else activeUploads += 1;
3533
2141
  try {
3534
2142
  if (closed) throw new Error("Endpoint media pipeline is closed");
@@ -3591,11 +2199,11 @@ function createEndpointMediaPipeline(options) {
3591
2199
  const check = probeBackgroundCapability();
3592
2200
  const waitMs = Math.max(0, options2.maxProbeWaitMs);
3593
2201
  if (waitMs === 0) return false;
3594
- return new Promise((resolve6) => {
3595
- const timer2 = setTimeout(() => resolve6(false), waitMs);
2202
+ return new Promise((resolve3) => {
2203
+ const timer2 = setTimeout(() => resolve3(false), waitMs);
3596
2204
  void check.then((enabled) => {
3597
2205
  clearTimeout(timer2);
3598
- resolve6(enabled);
2206
+ resolve3(enabled);
3599
2207
  });
3600
2208
  });
3601
2209
  }
@@ -3744,7 +2352,7 @@ function createEndpointMediaPipeline(options) {
3744
2352
  }
3745
2353
  const staged = (async () => {
3746
2354
  if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
3747
- const fingerprint = await hash3(canonicalEndpointJson({ message: input.message, files: input.files.map((file) => ({ descriptor: file.descriptor, source: background.sourceIdentity(file.source) })) }));
2355
+ const fingerprint = await hash(canonicalEndpointJson({ message: input.message, files: input.files.map((file) => ({ descriptor: file.descriptor, source: background.sourceIdentity(file.source) })) }));
3748
2356
  if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
3749
2357
  const owner = await acquire(id2);
3750
2358
  try {
@@ -3782,10 +2390,10 @@ function createEndpointMediaPipeline(options) {
3782
2390
  }) }), "prepare"),
3783
2391
  commit: /* @__PURE__ */ __name(async (tx, preparedContent) => {
3784
2392
  if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
3785
- const sealed2 = { ...flow, preparedContent };
3786
- await writeInTransaction(tx, sealed2, owner);
2393
+ const sealed = { ...flow, preparedContent };
2394
+ await writeInTransaction(tx, sealed, owner);
3787
2395
  await tx.delete(`preparation/${operationId}`);
3788
- return sealed2;
2396
+ return sealed;
3789
2397
  }, "commit")
3790
2398
  });
3791
2399
  notifyFlow(committed);
@@ -3839,7 +2447,7 @@ function createEndpointMediaPipeline(options) {
3839
2447
  checkOpen();
3840
2448
  digests.push(await bytes.digest(file.source));
3841
2449
  }
3842
- const fingerprint = await hash3(canonicalEndpointJson({ ...prepareOnly ? { purpose: "prepare" } : {}, message: input.message, files: input.files.map((file, index) => ({ ...file.descriptor, digest: digests[index] })) }));
2450
+ const fingerprint = await hash(canonicalEndpointJson({ ...prepareOnly ? { purpose: "prepare" } : {}, message: input.message, files: input.files.map((file, index) => ({ ...file.descriptor, digest: digests[index] })) }));
3843
2451
  checkOpen();
3844
2452
  const owner = await acquire(id2);
3845
2453
  const controller = new AbortController();
@@ -3937,8 +2545,8 @@ function createEndpointMediaPipeline(options) {
3937
2545
  if (!operation || ["rejected", "uncertain", "succeeded"].includes(operation.status)) throw error;
3938
2546
  const receipt = await endpoint.waitForOperation(error.operationId, { signal: controller.signal });
3939
2547
  if (receipt.status !== "succeeded" || receipt.resultError) {
3940
- const failure2 = receipt.resultError ?? receipt.error;
3941
- throw new EndpointError(failure2?.message ?? "Attachment delivery requires attention", { code: failure2?.code ?? "OPERATION_UNCERTAIN", status: failure2?.status, retryable: failure2?.retryable ?? false, operationId: error.operationId });
2548
+ const failure = receipt.resultError ?? receipt.error;
2549
+ throw new EndpointError(failure?.message ?? "Attachment delivery requires attention", { code: failure?.code ?? "OPERATION_UNCERTAIN", status: failure?.status, retryable: failure?.retryable ?? false, operationId: error.operationId });
3942
2550
  }
3943
2551
  return receipt.result;
3944
2552
  }
@@ -4090,7 +2698,7 @@ function createEndpointMediaPipeline(options) {
4090
2698
  await write(flow, owner);
4091
2699
  }
4092
2700
  for (let wait = 1e3; file.attachment?.processingStatus === "processing"; wait = Math.min(wait * 2, 15e3)) {
4093
- await new Promise((resolve6) => setTimeout(resolve6, wait));
2701
+ await new Promise((resolve3) => setTimeout(resolve3, wait));
4094
2702
  if (closed || controller.signal.aborted) throw new DOMException("Attachment cancelled", "AbortError");
4095
2703
  const current = await endpoint.query("get_upload", { uploadId: file.uploadId });
4096
2704
  if (current.attachment) {
@@ -4272,8 +2880,8 @@ function createInboundHandoffs(input) {
4272
2880
  if (currentEntry(entry)) entries2.delete(entry.handle.id);
4273
2881
  }
4274
2882
  __name(forget, "forget");
4275
- function owns(entry, record7) {
4276
- return !!record7 && record7.handoffGeneration === entry.generation && (record7.state === "uncertain" || !record7.lease || record7.lease.owner === entry.leaseOwner);
2883
+ function owns(entry, record6) {
2884
+ return !!record6 && record6.handoffGeneration === entry.generation && (record6.state === "uncertain" || !record6.lease || record6.lease.owner === entry.leaseOwner);
4277
2885
  }
4278
2886
  __name(owns, "owns");
4279
2887
  async function applyDisposition(entry, kind, reason) {
@@ -4361,7 +2969,7 @@ function createInboundHandoffs(input) {
4361
2969
  entries2.set(id2, entry);
4362
2970
  capabilities.set(handle, entry);
4363
2971
  try {
4364
- const record7 = await store.transaction(async (tx) => {
2972
+ const record6 = await store.transaction(async (tx) => {
4365
2973
  const current = await tx.get(`inbound/${id2}`);
4366
2974
  if (current && await settleInboundProjection(tx, current, now())) return void 0;
4367
2975
  if (!current || current.deleted || current.state === "uncertain" || current.state === "settled") return void 0;
@@ -4377,11 +2985,11 @@ function createInboundHandoffs(input) {
4377
2985
  await tx.put(`inbound/${id2}`, next);
4378
2986
  return next;
4379
2987
  });
4380
- if (!record7) {
2988
+ if (!record6) {
4381
2989
  forget(entry, "released");
4382
2990
  return null;
4383
2991
  }
4384
- conversationId = record7.event.conversationId;
2992
+ conversationId = record6.event.conversationId;
4385
2993
  if (closing) {
4386
2994
  await handle.release("consumer-closed");
4387
2995
  return null;
@@ -4403,8 +3011,8 @@ function createInboundHandoffs(input) {
4403
3011
  if (!selected.length || closing || selected.some((entry) => !currentEntry(entry) || entry.phase !== "queued")) return { status: "unavailable" };
4404
3012
  if (selected.some((entry) => entry.transition || entry.request)) return { status: "deferred" };
4405
3013
  let finishClaim;
4406
- const boundary = new Promise((resolve6) => {
4407
- finishClaim = resolve6;
3014
+ const boundary = new Promise((resolve3) => {
3015
+ finishClaim = resolve3;
4408
3016
  });
4409
3017
  for (const entry of selected) {
4410
3018
  entry.phase = "claiming";
@@ -4413,11 +3021,11 @@ function createInboundHandoffs(input) {
4413
3021
  let claimed = false;
4414
3022
  try {
4415
3023
  const records = await store.transaction(async (tx) => Promise.all(selected.map((entry) => tx.get(`inbound/${entry.handle.id}`))));
4416
- await input.assertReady(records.filter((record7) => !!record7));
3024
+ await input.assertReady(records.filter((record6) => !!record6));
4417
3025
  const claim = await store.transaction(async (tx) => {
4418
3026
  if (await tx.get("migration/active")) throw new EndpointError("Migration is incomplete; native execution is gated", { code: "MIGRATION_IN_PROGRESS", retryable: false });
4419
3027
  const current = await Promise.all(selected.map((entry) => tx.get(`inbound/${entry.handle.id}`)));
4420
- const eligible = current.map((record7, index) => owns(selected[index], record7) && !record7.deleted && record7.state !== "uncertain" && record7.state !== "settled");
3028
+ const eligible = current.map((record6, index) => owns(selected[index], record6) && !record6.deleted && record6.state !== "uncertain" && record6.state !== "settled");
4421
3029
  if (eligible.some((value) => !value)) return { claimed: false, eligible };
4422
3030
  for (let index = 0; index < current.length; index++) {
4423
3031
  const next = { ...current[index], state: "uncertain", updatedAt: now() };
@@ -4442,9 +3050,9 @@ function createInboundHandoffs(input) {
4442
3050
  await store.transaction(async (tx) => {
4443
3051
  for (const entry of selected) {
4444
3052
  if (entry.request) continue;
4445
- const record7 = await tx.get(`inbound/${entry.handle.id}`);
4446
- if (!owns(entry, record7) || record7.state !== "uncertain") continue;
4447
- const next = { ...record7, state: "offered", lease: { owner: entry.leaseOwner, expiresAt: clock() + leaseMs }, updatedAt: now() };
3053
+ const record6 = await tx.get(`inbound/${entry.handle.id}`);
3054
+ if (!owns(entry, record6) || record6.state !== "uncertain") continue;
3055
+ const next = { ...record6, state: "offered", lease: { owner: entry.leaseOwner, expiresAt: clock() + leaseMs }, updatedAt: now() };
4448
3056
  await tx.put(`inbound/${entry.handle.id}`, next);
4449
3057
  await enqueueInbound(tx, next);
4450
3058
  }
@@ -4545,9 +3153,9 @@ function createInboundHandoffs(input) {
4545
3153
  if (!entry) return false;
4546
3154
  if (entry.phase !== "queued" || entry.transition) return true;
4547
3155
  const held = await store.transaction(async (tx) => {
4548
- const record7 = await tx.get(`inbound/${id2}`);
4549
- if (!owns(entry, record7) || record7.deleted || record7.state === "settled" || record7.state === "uncertain") return false;
4550
- await tx.put(`inbound/${id2}`, { ...record7, lease: { owner: entry.leaseOwner, expiresAt: clock() + leaseMs } });
3156
+ const record6 = await tx.get(`inbound/${id2}`);
3157
+ if (!owns(entry, record6) || record6.deleted || record6.state === "settled" || record6.state === "uncertain") return false;
3158
+ await tx.put(`inbound/${id2}`, { ...record6, lease: { owner: entry.leaseOwner, expiresAt: clock() + leaseMs } });
4551
3159
  return true;
4552
3160
  });
4553
3161
  if (!held) forget(entry, "released");
@@ -4666,9 +3274,9 @@ function createExternalInboundClaims(input) {
4666
3274
  if (!current || current.state !== "uncertain" || !claim?.replyIntent || claim.requestId !== requestId || claim.installationGeneration !== generation2 || claim.replyOperationId && claim.replyOperationId !== operationId) {
4667
3275
  throw new EndpointError("Reply no longer belongs to this claim", { code: "INBOUND_CLAIM_CONFLICT", retryable: false });
4668
3276
  }
4669
- const sealed2 = !!await tx.get(`preparation/${operationId}`) || !!await tx.get(`operation/${operationId}`);
4670
- if (current.deleted && !sealed2) throw new EndpointError("Reply source was deleted before preparation", { code: "INBOUND_CLAIM_CONFLICT", retryable: false });
4671
- return { current, claim, generation: generation2, sealed: sealed2 };
3277
+ const sealed = !!await tx.get(`preparation/${operationId}`) || !!await tx.get(`operation/${operationId}`);
3278
+ if (current.deleted && !sealed) throw new EndpointError("Reply source was deleted before preparation", { code: "INBOUND_CLAIM_CONFLICT", retryable: false });
3279
+ return { current, claim, generation: generation2, sealed };
4672
3280
  }, "read");
4673
3281
  const before = await input.store.transaction(read);
4674
3282
  if (before.sealed) return { event: before.current.event, replayed: true };
@@ -4676,8 +3284,8 @@ function createExternalInboundClaims(input) {
4676
3284
  const authored = copyJson(prepared.intent);
4677
3285
  const fingerprint = await input.hash(canonicalEndpointJson({ ...authored, ...input.ownershipVersion !== void 0 ? { ownershipVersion: input.ownershipVersion } : {} }));
4678
3286
  return input.store.transaction(async (tx) => {
4679
- const { current, claim, generation: generation2, sealed: sealed2 } = await read(tx);
4680
- if (sealed2) return { event: current.event, replayed: true };
3287
+ const { current, claim, generation: generation2, sealed } = await read(tx);
3288
+ if (sealed) return { event: current.event, replayed: true };
4681
3289
  const event = prepared.event;
4682
3290
  const admissionId = event.data.replyAuthority?.admissionId ?? null;
4683
3291
  if (event.id !== id2 || event.kind !== "message.created" || !event.durable || event.conversationId !== claim.source.conversationId || event.data.message?.id !== claim.source.messageId || admissionId !== claim.source.admissionId || authored.action !== "send_message" || authored.envelope || authored.preconditions || authored.body.conversationId !== claim.source.conversationId || authored.body.replyTo !== claim.source.messageId || authored.body.messageId !== claim.replyIntent.messageId || authored.body.metadata?.turnId !== claim.replyIntent.turnId || canonicalEndpointJson(authored.body.replyAuthority) !== canonicalEndpointJson(event.data.replyAuthority)) {
@@ -4752,7 +3360,7 @@ function same(left, right) {
4752
3360
  return canonicalEndpointJson(left) === canonicalEndpointJson(right);
4753
3361
  }
4754
3362
  __name(same, "same");
4755
- function record2(value, required, optional = []) {
3363
+ function record(value, required, optional = []) {
4756
3364
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail();
4757
3365
  const descriptors = Object.getOwnPropertyDescriptors(value);
4758
3366
  const keys = Reflect.ownKeys(descriptors);
@@ -4765,7 +3373,7 @@ function record2(value, required, optional = []) {
4765
3373
  }
4766
3374
  return result;
4767
3375
  }
4768
- __name(record2, "record");
3376
+ __name(record, "record");
4769
3377
  function uuid(value) {
4770
3378
  if (typeof value !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value)) fail();
4771
3379
  return value;
@@ -4813,7 +3421,7 @@ function createMlsDevDeviceCoordinator(input) {
4813
3421
  return void 0;
4814
3422
  }
4815
3423
  if (snapshot.protocolState === null || raw === void 0 || snapshot.protocolRevision === 0) fail();
4816
- const stored = record2(snapshot.protocolState, ["version", "binding", "wrappedState", "signaturePublicKey"]);
3424
+ const stored = record(snapshot.protocolState, ["version", "binding", "wrappedState", "signaturePublicKey"]);
4817
3425
  if (stored.version !== 1 || !same(parseMlsDevDeviceBinding(stored.binding), binding)) fail();
4818
3426
  const prepared = parseMlsDevDevicePreparation({
4819
3427
  wrappedState: stored.wrappedState,
@@ -4825,7 +3433,7 @@ function createMlsDevDeviceCoordinator(input) {
4825
3433
  wrappedState: prepared.wrappedState,
4826
3434
  signaturePublicKey: prepared.payload.signaturePublicKey
4827
3435
  };
4828
- const data = record2(raw, ["version", "binding", "signaturePublicKey"], ["registration", "credential", "generationObservation", "revocation"]);
3436
+ const data = record(raw, ["version", "binding", "signaturePublicKey"], ["registration", "credential", "generationObservation", "revocation"]);
4829
3437
  if (data.version !== 1 || !same(parseMlsDevDeviceBinding(data.binding), binding) || data.signaturePublicKey !== envelope2.signaturePublicKey) fail();
4830
3438
  const metadata = { version: 1, binding, signaturePublicKey: envelope2.signaturePublicKey };
4831
3439
  if (Object.hasOwn(data, "registration")) {
@@ -4893,7 +3501,7 @@ function createMlsDevDeviceCoordinator(input) {
4893
3501
  async function readProof(tx, op, id2) {
4894
3502
  const raw = await tx.get(proofKey(op, id2));
4895
3503
  if (raw === void 0) return void 0;
4896
- const data = record2(raw, ["command", "request"]);
3504
+ const data = record(raw, ["command", "request"]);
4897
3505
  const command = parseMlsDevDeviceCommand(data.command);
4898
3506
  if (command.op !== op || command.challenge.challengeId !== id2) fail();
4899
3507
  const request = parseComplete(op, data.request);
@@ -5198,12 +3806,26 @@ function parseFlow(value) {
5198
3806
  return fail2();
5199
3807
  }
5200
3808
  __name(parseFlow, "parseFlow");
3809
+ var MAX_CHALLENGE_CLOCK_LAG_MS = 3e4;
5201
3810
  function createMlsDevDeviceClient(options, context) {
5202
3811
  const principal = JSON.parse(JSON.stringify(options.principal));
5203
3812
  const { prepareProtected, verifyCredential } = options;
5204
3813
  const transport = context.transport;
5205
3814
  let running;
5206
3815
  let renewalRetryAtMs = 0;
3816
+ async function untilIssued(challenge) {
3817
+ const lag = challenge.issuedAtMs - context.clock();
3818
+ if (lag <= 0 || lag > MAX_CHALLENGE_CLOCK_LAG_MS) return;
3819
+ await new Promise((resolve3) => {
3820
+ const timer = setTimeout(resolve3, lag + 1);
3821
+ context.signal.addEventListener("abort", () => {
3822
+ clearTimeout(timer);
3823
+ resolve3();
3824
+ }, { once: true });
3825
+ });
3826
+ context.assertOpen();
3827
+ }
3828
+ __name(untilIssued, "untilIssued");
5207
3829
  function guardedStore(expected) {
5208
3830
  return { close: /* @__PURE__ */ __name(async () => {
5209
3831
  }, "close"), transaction: /* @__PURE__ */ __name((work) => context.store.transaction(async (tx) => {
@@ -5373,6 +3995,7 @@ function createMlsDevDeviceClient(options, context) {
5373
3995
  await replace({ version: 1, id: context.ids(), goal: "enroll", phase: "enrollment", begin: captured.begin });
5374
3996
  continue;
5375
3997
  }
3998
+ if (!previous2) await untilIssued(captured.challenge);
5376
3999
  const request2 = previous2 ?? await device.prepareEnrollmentProof(captured.challenge);
5377
4000
  const receipt2 = await external(() => http.completeEnrollment(request2, context.signal));
5378
4001
  const view3 = await device.acceptRegistration(captured.challenge.challengeId, receipt2);
@@ -5398,6 +4021,7 @@ function createMlsDevDeviceClient(options, context) {
5398
4021
  await replace({ version: 1, id: context.ids(), goal: "renew", phase: "credential", begin: captured.begin });
5399
4022
  continue;
5400
4023
  }
4024
+ if (!previous2) await untilIssued(captured.challenge);
5401
4025
  const request2 = previous2 ?? await device.prepareCredentialProof(captured.challenge, captured.begin.expectedCredentialGeneration);
5402
4026
  const receipt2 = await external(() => http.completeCredential(request2, context.signal));
5403
4027
  const accepted2 = await device.acceptCredential(captured.challenge.challengeId, receipt2);
@@ -5420,6 +4044,7 @@ function createMlsDevDeviceClient(options, context) {
5420
4044
  await replace(await reconciliation(await device.read()));
5421
4045
  continue;
5422
4046
  }
4047
+ if (!previous) await untilIssued(captured.challenge);
5423
4048
  const request = previous ?? await device.prepareCredentialReconciliationProof(captured.challenge, captured.begin.expectedCredentialGeneration);
5424
4049
  const receipt = await external(() => http.completeReconciliation(request, context.signal));
5425
4050
  const accepted = await device.acceptGenerationObservation(captured.challenge.challengeId, receipt);
@@ -5525,6 +4150,12 @@ function createMlsDevDeviceClient(options, context) {
5525
4150
  return device.acceptRevocation(receipt);
5526
4151
  }
5527
4152
  __name(revoke, "revoke");
4153
+ async function revokeOther(deviceId) {
4154
+ const { coordinator, transport: http } = await initialized();
4155
+ if ((await coordinator(store).read()).registration?.device.deviceId === deviceId) throw new EndpointError("This device leaves by signing out", { code: "MLS_DEV_DEVICE_IS_CURRENT", retryable: false });
4156
+ await http.revokeCredential({ version: 1, deviceId }, context.signal);
4157
+ }
4158
+ __name(revokeOther, "revokeOther");
5528
4159
  function run(goal) {
5529
4160
  context.assertOpen();
5530
4161
  const preceding = running;
@@ -5570,7 +4201,8 @@ function createMlsDevDeviceClient(options, context) {
5570
4201
  renew: /* @__PURE__ */ __name(() => run("renew"), "renew"),
5571
4202
  reconcile: /* @__PURE__ */ __name(() => run("reconcile"), "reconcile"),
5572
4203
  ensureReady: /* @__PURE__ */ __name(() => run("ready"), "ensureReady"),
5573
- revoke: /* @__PURE__ */ __name(() => context.run(revoke), "revoke")
4204
+ revoke: /* @__PURE__ */ __name(() => context.run(revoke), "revoke"),
4205
+ revokeOther: /* @__PURE__ */ __name((deviceId) => context.run(() => revokeOther(deviceId)), "revokeOther")
5574
4206
  });
5575
4207
  }
5576
4208
  __name(createMlsDevDeviceClient, "createMlsDevDeviceClient");
@@ -5581,20 +4213,20 @@ function fail3(code = "MLS_DEV_KEY_PACKAGE_STATE_INVALID", retryable = false) {
5581
4213
  throw new EndpointError(code, { code, retryable });
5582
4214
  }
5583
4215
  __name(fail3, "fail");
5584
- function record3(value, keys) {
4216
+ function record2(value, keys) {
5585
4217
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
5586
4218
  const fields = Object.getOwnPropertyDescriptors(value);
5587
4219
  if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value") || !fields[key].enumerable)) fail3();
5588
4220
  return Object.fromEntries(keys.map((key) => [key, fields[key].value]));
5589
4221
  }
5590
- __name(record3, "record");
4222
+ __name(record2, "record");
5591
4223
  function createMlsDevKeyPackageService(options, context, store, initialized, readPrepared) {
5592
4224
  const verifyCredential = options.verifyCredential;
5593
4225
  const fence = /* @__PURE__ */ __name(() => store.transaction(async () => {
5594
4226
  }), "fence");
5595
4227
  const network = /* @__PURE__ */ __name(() => context.transport?.keyPackages ?? fail3("MLS_DEV_KEY_PACKAGE_TRANSPORT_REQUIRED"), "network");
5596
4228
  function parseJournal(raw, binding, current, op) {
5597
- const value = record3(raw, ["version", "binding", "registration", "request", "receipt"]);
4229
+ const value = record2(raw, ["version", "binding", "registration", "request", "receipt"]);
5598
4230
  const registration = parseMlsDevEnrollmentResult(value.registration);
5599
4231
  if (value.version !== 1 || !same3(parseMlsDevDeviceBinding(value.binding), binding) || !same3(registration, current.registration)) fail3("MLS_DEV_KEY_PACKAGE_BINDING_MISMATCH");
5600
4232
  return {
@@ -5700,7 +4332,7 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
5700
4332
  equivalent: /* @__PURE__ */ __name((a, b) => same3({ ...a, claimantCredential: null }, { ...b, claimantCredential: null }), "equivalent"),
5701
4333
  forgetsRefusal: true,
5702
4334
  async make(tx, _binding, current) {
5703
- const raw = input ?? record3(
4335
+ const raw = input ?? record2(
5704
4336
  (await tx.get("mls-dev-key-package/claim/" + id2))?.request,
5705
4337
  ["version", "requestId", "claimantCredential", "recipientDeviceId", "preconditions"]
5706
4338
  );
@@ -5732,11 +4364,11 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
5732
4364
  __name(run, "run");
5733
4365
  return {
5734
4366
  publish(input) {
5735
- const id2 = parseMlsDevKeyPackageId(record3(input, ["keyPackageId"]).keyPackageId);
4367
+ const id2 = parseMlsDevKeyPackageId(record2(input, ["keyPackageId"]).keyPackageId);
5736
4368
  return run(async () => Object.freeze({ status: "published", receipt: await exchange(id2, true, publication(id2)) ?? fail3() }));
5737
4369
  },
5738
4370
  claim(input) {
5739
- const data = record3(input, ["requestId", "recipientDeviceId", "preconditions"]);
4371
+ const data = record2(input, ["requestId", "recipientDeviceId", "preconditions"]);
5740
4372
  const frozen = Object.freeze({
5741
4373
  requestId: parseMlsDevKeyPackageId(data.requestId),
5742
4374
  recipientDeviceId: parseMlsDevKeyPackageId(data.recipientDeviceId),
@@ -5813,20 +4445,20 @@ function fail4(code = "MLS_DEV_KEY_PACKAGE_STATE_INVALID", retryable = false) {
5813
4445
  }
5814
4446
  __name(fail4, "fail");
5815
4447
  var same4 = /* @__PURE__ */ __name((a, b) => canonicalEndpointJson(a) === canonicalEndpointJson(b), "same");
5816
- function record4(value, keys) {
4448
+ function record3(value, keys) {
5817
4449
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
5818
4450
  const fields = Object.getOwnPropertyDescriptors(value);
5819
4451
  if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value") || !fields[key].enumerable)) fail4();
5820
4452
  return Object.fromEntries(keys.map((key) => [key, fields[key].value]));
5821
4453
  }
5822
- __name(record4, "record");
4454
+ __name(record3, "record");
5823
4455
  function boundPayload(command, payload) {
5824
4456
  const claims = parseMlsDevCredentialToken(command.credential).unsigned.claims;
5825
4457
  if (payload.keyPackageId !== command.keyPackageId || payload.deviceId !== claims.device.deviceId || payload.credentialId !== claims.credentialId || payload.credentialGeneration !== claims.generation || payload.signaturePublicKey !== claims.device.signaturePublicKey || payload.ciphersuite !== claims.device.ciphersuite || payload.notBeforeMs < claims.issuedAtMs || payload.expiresAtMs > claims.expiresAtMs) fail4("MLS_DEV_KEY_PACKAGE_BINDING_MISMATCH");
5826
4458
  }
5827
4459
  __name(boundPayload, "boundPayload");
5828
4460
  function intent(value, binding, id2) {
5829
- const data = record4(value, ["version", "binding", "command", "payload"]);
4461
+ const data = record3(value, ["version", "binding", "command", "payload"]);
5830
4462
  const command = parseMlsDevKeyPackageCommand(data.command), payload = parseMlsDevKeyPackagePayload(data.payload);
5831
4463
  if (data.version !== 1 || !same4(parseMlsDevDeviceBinding(data.binding), binding) || command.keyPackageId !== id2) fail4();
5832
4464
  const device = parseMlsDevCredentialToken(command.credential).unsigned.claims.device;
@@ -5845,7 +4477,7 @@ async function captureMlsDevPreparedKeyPackage(tx, binding, id2, current) {
5845
4477
  const stored = intent(raw, binding, id2);
5846
4478
  await assertMlsDevKeyPackageUnconsumed(tx, id2, stored.payload.keyPackageRef);
5847
4479
  if (!same4(parseMlsDevCredentialToken(stored.command.credential).unsigned.claims.device, current.registration.device)) fail4("MLS_DEV_KEY_PACKAGE_BINDING_MISMATCH");
5848
- const state = record4(snapshot.protocolState, ["version", "binding", "keyPackageId", "wrappedState", "payload"]);
4480
+ const state = record3(snapshot.protocolState, ["version", "binding", "keyPackageId", "wrappedState", "payload"]);
5849
4481
  const preparation = parseMlsDevKeyPackagePreparation({ wrappedState: state.wrappedState, payload: state.payload });
5850
4482
  if (state.version !== 1 || !same4(parseMlsDevDeviceBinding(state.binding), binding) || state.keyPackageId !== id2 || !same4(preparation.payload, stored.payload)) fail4();
5851
4483
  return {
@@ -5912,7 +4544,7 @@ function createMlsDevKeyPackageClient(options, context) {
5912
4544
  if (historical) {
5913
4545
  const consumed = await tx.get(`mls-dev-key-package/consumed/${id2}`);
5914
4546
  if (consumed !== void 0) {
5915
- const data = record4(consumed, ["version", "binding", "payload", "conversationId", "groupId", "controlSequence"]);
4547
+ const data = record3(consumed, ["version", "binding", "payload", "conversationId", "groupId", "controlSequence"]);
5916
4548
  const payload = parseMlsDevKeyPackagePayload(data.payload);
5917
4549
  if (data.version !== 1 || !same4(parseMlsDevDeviceBinding(data.binding), binding) || payload.keyPackageId !== id2 || payload.deviceId !== current.registration.device.deviceId || payload.signaturePublicKey !== current.registration.device.signaturePublicKey) fail4();
5918
4550
  return payload;
@@ -6070,7 +4702,7 @@ function createMlsDevKeyPackageClient(options, context) {
6070
4702
  return {
6071
4703
  ...service,
6072
4704
  prepare(input) {
6073
- const data = record4(input, ["keyPackageId"]);
4705
+ const data = record3(input, ["keyPackageId"]);
6074
4706
  return run(parseMlsDevKeyPackageId(data.keyPackageId), true).then((result) => result ?? fail4());
6075
4707
  },
6076
4708
  read: /* @__PURE__ */ __name((id2) => run(id2, false), "read"),
@@ -6320,6 +4952,27 @@ function mlsDevReplyQuote(value, maximum) {
6320
4952
  return quoted ? [...quoted.trim()].slice(0, maximum).join("") || void 0 : void 0;
6321
4953
  }
6322
4954
  __name(mlsDevReplyQuote, "mlsDevReplyQuote");
4955
+ var MLS_DEV_INBOX_PREVIEW_MAX = 200;
4956
+ async function readMlsDevInboxPreview(tx, conversationId, messageId) {
4957
+ const value = await tx.get(mlsDevTextMessageKey(conversationId, messageId));
4958
+ if (!value || value.deleted || await tx.get(mlsDevTextDeletedKey(conversationId, messageId)) !== void 0) return void 0;
4959
+ if (value.message.kind === "interaction.request") return { text: value.message.interaction.title, contentType: "text" };
4960
+ const text = mlsDevReplyQuote(value, MLS_DEV_INBOX_PREVIEW_MAX);
4961
+ return text ? { text, contentType: value.message.kind === "item" ? value.message.item.type : "text" } : void 0;
4962
+ }
4963
+ __name(readMlsDevInboxPreview, "readMlsDevInboxPreview");
4964
+ async function overlayMlsDevInboxPreviews(tx, page) {
4965
+ const rows = page?.conversations;
4966
+ if (!Array.isArray(rows)) return page;
4967
+ const conversations = [];
4968
+ for (const row of rows) {
4969
+ const messageId = row.lastMessage?.messageId;
4970
+ const preview = typeof row.id === "string" && typeof messageId === "string" ? await readMlsDevInboxPreview(tx, row.id, messageId) : void 0;
4971
+ conversations.push(preview ? { ...row, lastMessage: { ...row.lastMessage, ...preview } } : row);
4972
+ }
4973
+ return { ...page, conversations };
4974
+ }
4975
+ __name(overlayMlsDevInboxPreviews, "overlayMlsDevInboxPreviews");
6323
4976
  function projectMlsDevTextMessage(value, account) {
6324
4977
  const message = value.message;
6325
4978
  if (message.kind === "item") return projectMlsDevItemMessage({ ...value, message }, account);
@@ -6435,7 +5088,7 @@ var MLS_ERASED_SENDER_ID = "erased_user";
6435
5088
  var checked = /* @__PURE__ */ new Map();
6436
5089
  var checkKey = /* @__PURE__ */ __name((environmentId, alias, opening) => JSON.stringify([environmentId, alias, opening.type, opening.accountId, opening.salt]), "checkKey");
6437
5090
  var hexBytes = /* @__PURE__ */ __name((hex) => Uint8Array.from(hex.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16)), "hexBytes");
6438
- async function opensMlsAlias(environmentId, alias, opening, hash3) {
5091
+ async function opensMlsAlias(environmentId, alias, opening, hash) {
6439
5092
  let value;
6440
5093
  try {
6441
5094
  value = parseMlsPrincipalOpening(opening);
@@ -6446,7 +5099,7 @@ async function opensMlsAlias(environmentId, alias, opening, hash3) {
6446
5099
  const key = checkKey(environmentId, alias, value);
6447
5100
  let opens = checked.get(key);
6448
5101
  if (opens === void 0) {
6449
- const digest = await hash3(encodeMlsPrincipalAliasInput(environmentId, value));
5102
+ const digest = await hash(encodeMlsPrincipalAliasInput(environmentId, value));
6450
5103
  opens = /^[0-9a-f]{64}$/.test(digest) && MLS_PRINCIPAL_ALIAS_PREFIX + encodeCanonicalMlsBase64Url(hexBytes(digest)) === alias;
6451
5104
  if (checked.size >= 4096) checked.clear();
6452
5105
  checked.set(key, opens);
@@ -6464,8 +5117,8 @@ function openings(room) {
6464
5117
  }
6465
5118
  }
6466
5119
  __name(openings, "openings");
6467
- async function checkMlsRoomOpenings(room, environmentId, hash3) {
6468
- for (const [alias, opening] of Object.entries(openings(room))) await opensMlsAlias(environmentId, alias, opening, hash3);
5120
+ async function checkMlsRoomOpenings(room, environmentId, hash) {
5121
+ for (const [alias, opening] of Object.entries(openings(room))) await opensMlsAlias(environmentId, alias, opening, hash);
6469
5122
  }
6470
5123
  __name(checkMlsRoomOpenings, "checkMlsRoomOpenings");
6471
5124
  function mlsRoomAccounts(room, environmentId) {
@@ -6481,8 +5134,8 @@ function mlsAccountOf(accounts, principal) {
6481
5134
  }
6482
5135
  __name(mlsAccountOf, "mlsAccountOf");
6483
5136
  function mlsAliasOf(accounts, accountId, type, admitted) {
6484
- const matches2 = [...accounts].filter(([alias, identity]) => identity.accountId === accountId && identity.type === type && (!admitted || admitted.includes(alias))).map(([alias]) => alias);
6485
- return matches2.length === 1 ? matches2[0] : void 0;
5137
+ const matches = [...accounts].filter(([alias, identity]) => identity.accountId === accountId && identity.type === type && (!admitted || admitted.includes(alias))).map(([alias]) => alias);
5138
+ return matches.length === 1 ? matches[0] : void 0;
6486
5139
  }
6487
5140
  __name(mlsAliasOf, "mlsAliasOf");
6488
5141
  async function primeMlsRoomIdentities(input) {
@@ -6529,6 +5182,11 @@ async function roomRemoval(tx, conversationId) {
6529
5182
  }
6530
5183
  }
6531
5184
  __name(roomRemoval, "roomRemoval");
5185
+ async function roomRetired(tx, conversationId) {
5186
+ const room = await tx.get(`conversation/${conversationId}`);
5187
+ return Array.isArray(room?.mlsRetiredDeviceIds) ? room.mlsRetiredDeviceIds : [];
5188
+ }
5189
+ __name(roomRetired, "roomRetired");
6532
5190
  async function assertRoomOpen(tx, conversationId, roster) {
6533
5191
  const removal = await roomRemoval(tx, conversationId);
6534
5192
  if (removal && removal.status !== "pending") fail6("MLS_DEV_GROUP_CLOSED");
@@ -6545,19 +5203,19 @@ function id(value) {
6545
5203
  return value;
6546
5204
  }
6547
5205
  __name(id, "id");
6548
- function record5(value, required, optional = []) {
5206
+ function record4(value, required, optional = []) {
6549
5207
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail6();
6550
5208
  const descriptors = Object.getOwnPropertyDescriptors(value), fields = Reflect.ownKeys(descriptors);
6551
5209
  if (required.some((key) => !Object.hasOwn(descriptors, key)) || fields.some((key) => typeof key !== "string" || ![...required, ...optional].includes(key) || !Object.hasOwn(descriptors[key], "value") || !descriptors[key].enumerable)) fail6();
6552
5210
  return Object.fromEntries(fields.map((key) => [key, descriptors[key].value]));
6553
5211
  }
6554
- __name(record5, "record");
5212
+ __name(record4, "record");
6555
5213
  function envelope(value, binding, conversationId) {
6556
5214
  if (value === null) return null;
6557
- const data = record5(value, ["version", "binding", "conversationId", "groupId", "wrappedState", "head", "roster", "removed", "pending"], ["protocol"]);
5215
+ const data = record4(value, ["version", "binding", "conversationId", "groupId", "wrappedState", "head", "roster", "removed", "pending"], ["protocol"]);
6558
5216
  if (data.version !== 1 || !same6(parseMlsDevDeviceBinding(data.binding), binding) || data.conversationId !== conversationId || typeof data.removed !== "boolean") fail6();
6559
5217
  const head = data.head === null ? null : parseMlsDevGroupHead(data.head), roster = parseMlsDevGroupRoster(data.roster);
6560
- const pending = data.pending === null ? null : record5(data.pending, ["operationId", "control"]);
5218
+ const pending = data.pending === null ? null : record4(data.pending, ["operationId", "control"]);
6561
5219
  const control = pending ? parseMlsDevGroupControl(pending.control) : null;
6562
5220
  parseMlsDevGroupPreparation({ wrappedState: data.wrappedState, payload: control ? { kind: "control", control } : { kind: "applied", head: head ?? fail6(), roster, removed: data.removed, consumedKeyPackageId: null } });
6563
5221
  if (control && (control.conversationId !== conversationId || control.groupId !== data.groupId || !same6(control.expectedHead, head))) fail6();
@@ -6579,7 +5237,7 @@ function envelope(value, binding, conversationId) {
6579
5237
  __name(envelope, "envelope");
6580
5238
  function authority(value, conversationId) {
6581
5239
  if (value === void 0) return void 0;
6582
- const data = record5(value, ["version", "marker", "membershipRevision", "admissionId", "roster", "terminal", "observedSequence"]);
5240
+ const data = record4(value, ["version", "marker", "membershipRevision", "admissionId", "roster", "terminal", "observedSequence"]);
6583
5241
  const parsed = parseMlsDevGroupQueryResult({
6584
5242
  version: data.version,
6585
5243
  conversationId,
@@ -6605,7 +5263,7 @@ function authority(value, conversationId) {
6605
5263
  __name(authority, "authority");
6606
5264
  function recoveryFailure(value, conversationId) {
6607
5265
  if (value === void 0) return void 0;
6608
- const data = record5(value, ["version", "conversationId", "code", "lastAppliedSequence", "observedAtMs"]);
5266
+ const data = record4(value, ["version", "conversationId", "code", "lastAppliedSequence", "observedAtMs"]);
6609
5267
  if (data.version !== 1 || data.conversationId !== conversationId || !roomRecoveryCodes.some((code) => code === data.code) || !Number.isSafeInteger(data.lastAppliedSequence) || data.lastAppliedSequence < 0 || !Number.isSafeInteger(data.observedAtMs) || data.observedAtMs < 0) fail6();
6610
5268
  return Object.freeze({
6611
5269
  version: 1,
@@ -7208,7 +5866,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7208
5866
  __name(catchUpFeed, "catchUpFeed");
7209
5867
  const canonItemReference = /* @__PURE__ */ __name((item, conversationId, environmentId) => isCanonUploadUrl(item.url, { environmentId, conversationId, uploadId: item.uploadId }) && (item.thumbnailUrl === void 0 || isCanonUploadUrl(item.thumbnailUrl, { environmentId, conversationId, uploadId: item.uploadId, poster: true })), "canonItemReference");
7210
5868
  function itemInput(attachment, caption) {
7211
- const data = record5(attachment, ["kind", "url", "mimeType", "sizeBytes", "uploadId"], ["fileName", "width", "height", "durationMs", "thumbnailUrl", "processingStatus"]);
5869
+ const data = record4(attachment, ["kind", "url", "mimeType", "sizeBytes", "uploadId"], ["fileName", "width", "height", "durationMs", "thumbnailUrl", "processingStatus"]);
7212
5870
  if (data.processingStatus !== void 0 && data.processingStatus !== null && data.processingStatus !== "ready") fail6("MLS_DEV_ITEM_PROCESSING", true);
7213
5871
  const raw = {
7214
5872
  type: data.kind,
@@ -7240,7 +5898,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7240
5898
  }
7241
5899
  __name(validItem, "validItem");
7242
5900
  function textInput(value) {
7243
- const data = record5(value, ["conversationId"], ["text", "messageId", "contentType", "mentions", "attachments", "replyTo", "replyToPosition", "metadata", "forwarded", "contactCardUserId"]);
5901
+ const data = record4(value, ["conversationId"], ["text", "messageId", "contentType", "mentions", "attachments", "replyTo", "replyToPosition", "metadata", "forwarded", "contactCardUserId"]);
7244
5902
  const attachments = data.attachments ?? [];
7245
5903
  if (!Array.isArray(attachments) || attachments.length > 1) fail6(Array.isArray(attachments) ? "MLS_DEV_ONE_ITEM_PER_MESSAGE" : "MLS_DEV_TEXT_ONLY");
7246
5904
  const item = attachments.length ? itemInput(attachments[0], data.text) : void 0;
@@ -7260,8 +5918,11 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7260
5918
  await primeIdentities(conversationId, init);
7261
5919
  return store.transaction(async (tx) => {
7262
5920
  const room = await tx.get(`conversation/${conversationId}`);
7263
- if (room?.mlsAdmissionHold !== true) return Object.freeze({ held: false, waitingPrincipalIds: Object.freeze([]) });
7264
5921
  const group = envelope((await readPreparationSnapshot(tx, scope4(conversationId))).protocolState, init.binding, conversationId);
5922
+ if (room?.mlsAdmissionHold !== true) {
5923
+ const joining = !!room && !!room.mls && (!group || group.removed);
5924
+ return Object.freeze({ held: joining, waitingPrincipalIds: Object.freeze(joining ? [init.identity.principalId] : []) });
5925
+ }
7265
5926
  const accounts = mlsRoomAccounts(room, init.binding.environmentId);
7266
5927
  const admitted = new Set((group && !group.removed ? group.roster : []).map((entry) => mlsAccountOf(accounts, entry.device.principal)));
7267
5928
  const waiting = [.../* @__PURE__ */ new Set([
@@ -7443,14 +6104,14 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7443
6104
  }
7444
6105
  __name(aliasedInteraction, "aliasedInteraction");
7445
6106
  function interactionInput(value) {
7446
- const data = record5(value, ["conversationId"], ["messageId", "operationId", "guardId", "request", "response", "withdraw"]);
6107
+ const data = record4(value, ["conversationId"], ["messageId", "operationId", "guardId", "request", "response", "withdraw"]);
7447
6108
  const conversationId = id(data.conversationId), messageId = data.messageId === void 0 ? void 0 : id(data.messageId);
7448
6109
  const roles = ["request", "response", "withdraw"].filter((role) => data[role] !== void 0);
7449
6110
  if (roles.length !== 1) fail6("INVALID_MLS_DEV_INTERACTION_INPUT");
7450
6111
  try {
7451
6112
  if (data.request !== void 0) {
7452
6113
  if (principal.type !== "ai_agent") fail6("MLS_DEV_INTERACTION_REQUESTER_INVALID");
7453
- const input = record5(
6114
+ const input = record4(
7454
6115
  data.request,
7455
6116
  ["requestId", "kind", "title", "responderIds", "expiresAtMs"],
7456
6117
  ["details", "options", "multiple", "risk", "allowSession", "scopeKey"]
@@ -7466,7 +6127,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7466
6127
  }
7467
6128
  if (data.response !== void 0) {
7468
6129
  if (principal.type !== "human") fail6("MLS_DEV_INTERACTION_RESPONDER_INVALID");
7469
- const input = record5(data.response, ["requestId", "agentId"], ["answer", "secret"]);
6130
+ const input = record4(data.response, ["requestId", "agentId"], ["answer", "secret"]);
7470
6131
  const binding = { role: "response", requestId: input.requestId, agentId: input.agentId };
7471
6132
  if (Object.hasOwn(input, "secret") === Object.hasOwn(input, "answer")) fail6("INVALID_MLS_DEV_INTERACTION_INPUT");
7472
6133
  if (Object.hasOwn(input, "answer")) {
@@ -7944,12 +6605,13 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7944
6605
  async function commitHeldRemoval(conversationId) {
7945
6606
  const init = await initialized();
7946
6607
  const target = await store.transaction(async (tx) => {
7947
- const removal = await roomRemoval(tx, conversationId);
7948
- if (removal?.status !== "pending") return void 0;
6608
+ const removal = await roomRemoval(tx, conversationId), retired = await roomRetired(tx, conversationId);
6609
+ if (removal ? removal.status !== "pending" : !retired.length) return void 0;
7949
6610
  const current = await capture(tx, await readPreparationSnapshot(tx, scope4(conversationId)), init, conversationId), group = current.envelope;
7950
- const held = new Set(removal.pending.flatMap((entry) => entry.deviceIds));
7951
- if (!group?.head || group.removed || group.pending || current.authority?.terminal || held.has(current.device.registration.device.deviceId)) return void 0;
7952
- const removedDeviceId = group.roster.map((entry) => entry.device.deviceId).filter((deviceId) => held.has(deviceId)).sort()[0];
6611
+ const held = new Set(removal?.pending.flatMap((entry) => entry.deviceIds)), own = current.device.registration.device.deviceId;
6612
+ if (!group?.head || group.removed || group.pending || current.authority?.terminal || held.has(own)) return void 0;
6613
+ const stays = /* @__PURE__ */ __name((entry) => group.roster.some((other) => other !== entry && other.device.principal.id === entry.device.principal.id), "stays");
6614
+ const removedDeviceId = group.roster.filter((entry) => removal ? held.has(entry.device.deviceId) : entry.device.deviceId !== own && retired.includes(entry.device.deviceId) && stays(entry)).map((entry) => entry.device.deviceId).sort()[0];
7953
6615
  return removedDeviceId ? {
7954
6616
  deviceId: current.device.registration.device.deviceId,
7955
6617
  removedDeviceId,
@@ -8043,7 +6705,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8043
6705
  __name(run, "run");
8044
6706
  const client = {
8045
6707
  createRoom(input) {
8046
- const data = record5(input, ["conversationId", "name", "memberIds"], ["operationId", "protocol"]);
6708
+ const data = record4(input, ["conversationId", "name", "memberIds"], ["operationId", "protocol"]);
8047
6709
  const conversationId = id(data.conversationId), operationId = id(data.operationId ?? context.ids());
8048
6710
  if (typeof data.name !== "string" || data.name.trim().length > 100) fail6("INVALID_MLS_DEV_GROUP_INPUT");
8049
6711
  if (data.protocol !== void 0 && !isMlsDevTextProtocol(data.protocol)) fail6("MLS_DEV_TEXT_PROTOCOL_UNSUPPORTED");
@@ -8097,15 +6759,15 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8097
6759
  });
8098
6760
  },
8099
6761
  create: /* @__PURE__ */ __name((input) => {
8100
- const data = record5(input, ["conversationId"], ["operationId"]);
6762
+ const data = record4(input, ["conversationId"], ["operationId"]);
8101
6763
  return run(() => serializeInboundPreparation(store, id(data.conversationId), () => submit("create", data)));
8102
6764
  }, "create"),
8103
6765
  add: /* @__PURE__ */ __name((input) => {
8104
- const data = record5(input, ["conversationId", "claimRequestId"], ["operationId"]);
6766
+ const data = record4(input, ["conversationId", "claimRequestId"], ["operationId"]);
8105
6767
  return run(() => serializeInboundPreparation(store, id(data.conversationId), () => submit("add", data)));
8106
6768
  }, "add"),
8107
6769
  remove: /* @__PURE__ */ __name((input) => {
8108
- const data = record5(input, ["conversationId", "removedDeviceId"], ["operationId"]);
6770
+ const data = record4(input, ["conversationId", "removedDeviceId"], ["operationId"]);
8109
6771
  return run(() => serializeInboundPreparation(store, id(data.conversationId), () => submit("remove", data)));
8110
6772
  }, "remove"),
8111
6773
  refresh: /* @__PURE__ */ __name((value) => run(() => serializeInboundPreparation(store, id(value), () => refresh(id(value)))), "refresh"),
@@ -8120,13 +6782,13 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8120
6782
  }, false), "heldCurrent"),
8121
6783
  read: /* @__PURE__ */ __name((value) => run(() => read(id(value)), false), "read"),
8122
6784
  readVerifiedMessageBinding: /* @__PURE__ */ __name((input) => {
8123
- const data = record5(input, ["conversationId", "messageId"]);
6785
+ const data = record4(input, ["conversationId", "messageId"]);
8124
6786
  const conversationId = id(data.conversationId), messageId = id(data.messageId);
8125
6787
  return run(() => readVerifiedMessageBinding(conversationId, messageId), false);
8126
6788
  }, "readVerifiedMessageBinding"),
8127
6789
  readCurrentAudience: /* @__PURE__ */ __name((value) => run(() => serializeInboundPreparation(store, id(value), () => readCurrentAudience(id(value)))), "readCurrentAudience"),
8128
6790
  assertCurrentAudience: /* @__PURE__ */ __name((value) => {
8129
- const data = record5(value, ["version", "protocol", "conversationId", "groupId", "ownDeviceId", "head", "membershipRevision", "admissionId", "roster", "feedOrdinal"]);
6791
+ const data = record4(value, ["version", "protocol", "conversationId", "groupId", "ownDeviceId", "head", "membershipRevision", "admissionId", "roster", "feedOrdinal"]);
8130
6792
  const expected = JSON.parse(canonicalEndpointJson(data)), conversationId = id(expected.conversationId);
8131
6793
  if (!Number.isSafeInteger(expected.feedOrdinal) || expected.feedOrdinal < 1) fail6("MLS_DEV_AUDIENCE_CHANGED");
8132
6794
  return run(() => serializeInboundPreparation(store, conversationId, async () => {
@@ -8148,12 +6810,12 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8148
6810
  return run(() => serializeInboundPreparation(store, input.conversationId, () => sendInteraction(input, operationId, guardId)));
8149
6811
  }, "sendInteraction"),
8150
6812
  readInteraction: /* @__PURE__ */ __name(async (value) => {
8151
- const data = record5(value, ["conversationId", "agentId", "requestId"]);
6813
+ const data = record4(value, ["conversationId", "agentId", "requestId"]);
8152
6814
  const conversationId = id(data.conversationId), agentId = id(data.agentId), requestId = id(data.requestId);
8153
6815
  return run(() => serializeInboundPreparation(store, conversationId, () => readInteraction(conversationId, agentId, requestId)), false);
8154
6816
  }, "readInteraction"),
8155
6817
  openSecretAnswer: /* @__PURE__ */ __name(async (value) => {
8156
- const data = record5(value, ["conversationId", "requestId"]), conversationId = id(data.conversationId), requestId = id(data.requestId);
6818
+ const data = record4(value, ["conversationId", "requestId"]), conversationId = id(data.conversationId), requestId = id(data.requestId);
8157
6819
  return run(() => serializeInboundPreparation(store, conversationId, () => openSecretAnswer(conversationId, requestId)));
8158
6820
  }, "openSecretAnswer")
8159
6821
  };
@@ -8161,13 +6823,14 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8161
6823
  client,
8162
6824
  async commitHeldRemoval(conversationId) {
8163
6825
  const cid = id(conversationId);
8164
- if ((await store.transaction((tx) => roomRemoval(tx, cid)))?.status !== "pending") return void 0;
6826
+ const due = await store.transaction(async (tx) => (await roomRemoval(tx, cid))?.status === "pending" || (await roomRetired(tx, cid)).length > 0);
6827
+ if (!due) return void 0;
8165
6828
  return run(() => serializeInboundPreparation(store, cid, () => commitHeldRemoval(cid)));
8166
6829
  },
8167
6830
  /** Forwards an encrypted text by re-encryption: this device's own decrypted copy, sent as new text to another
8168
6831
  * encrypted room (mls-payload-v3.md §4). Items and cards are not forwarded yet. */
8169
6832
  forwardText(value, operationId, guardId) {
8170
- const data = record5(value, ["sourceConversationId", "messageId", "targetConversationId"]);
6833
+ const data = record4(value, ["sourceConversationId", "messageId", "targetConversationId"]);
8171
6834
  const source = id(data.sourceConversationId), messageId = id(data.messageId), target = id(data.targetConversationId);
8172
6835
  const selectedId = id(operationId), selectedGuard = guardId === void 0 ? void 0 : id(guardId);
8173
6836
  return run(async () => {
@@ -8324,7 +6987,7 @@ function createEndpoint(options) {
8324
6987
  const { store, transport } = options;
8325
6988
  const clock = options.clock ?? Date.now;
8326
6989
  const ids = options.ids ?? randomId;
8327
- const hash3 = options.hash ?? sha256;
6990
+ const hash = options.hash ?? sha256;
8328
6991
  const hashBytes = options.hashBytes ?? sha256Bytes;
8329
6992
  const owner = ids();
8330
6993
  const leaseMs = Math.max(5e3, options.leaseMs ?? 6e4);
@@ -8437,7 +7100,7 @@ function createEndpoint(options) {
8437
7100
  const mlsDevDevice = options.mlsDevDevice ? createMlsDevDeviceClient(options.mlsDevDevice, mlsDevContext) : void 0;
8438
7101
  const mlsDevKeyPackages = options.mlsDevDevice?.prepareProtectedKeyPackage ? createMlsDevKeyPackageClient(options.mlsDevDevice, mlsDevContext) : void 0;
8439
7102
  const mlsDevGroups = options.mlsDevDevice?.prepareProtectedGroup ? createMlsDevGroups(options.mlsDevDevice, mlsDevContext, mlsDevKeyPackages, {
8440
- hash: hash3,
7103
+ hash,
8441
7104
  hashBytes,
8442
7105
  readRoom,
8443
7106
  resolveProtocol: /* @__PURE__ */ __name((conversationId) => resolveMlsRoomMode(conversationId).then((value) => value.protocol), "resolveProtocol"),
@@ -8466,7 +7129,7 @@ function createEndpoint(options) {
8466
7129
  scheduleAdmission(conversationId, 0);
8467
7130
  }, "controlSettled"),
8468
7131
  // Whatever fetched them (a hint, the reconnect sweep, a send, recovery), the room's local reads show them now.
8469
- historyChanged: /* @__PURE__ */ __name((conversationId) => queryObserver.invalidate((query, parameters) => parameters.conversationId === conversationId && ["list_messages", "get_messages", "get_runtime_card_state"].includes(query)), "historyChanged"),
7132
+ historyChanged: /* @__PURE__ */ __name((conversationId) => queryObserver.invalidate((query, parameters) => options.localInboxPreviews && query === "list_conversations" || parameters.conversationId === conversationId && ["list_messages", "get_messages", "get_runtime_card_state"].includes(query)), "historyChanged"),
8470
7133
  streamLive: /* @__PURE__ */ __name(() => events.isConnected(), "streamLive")
8471
7134
  }) : void 0;
8472
7135
  async function inMlsEnvironment() {
@@ -8597,13 +7260,14 @@ function createEndpoint(options) {
8597
7260
  if (!mlsDevGroups || await mlsDevGroups.client.read(conversationId)) return;
8598
7261
  if (!isMlsDevTextProtocol((await resolveMlsRoomMode(conversationId, false)).protocol) || await readServerRoomMode() !== "default") return;
8599
7262
  const { installationId } = await initialization;
8600
- const digest = await hash3(canonicalEndpointJson(["canon.mls.room-birth.v1", installationId, conversationId]));
7263
+ const digest = await hash(canonicalEndpointJson(["canon.mls.room-birth.v1", installationId, conversationId]));
8601
7264
  await mlsDevGroups.client.create({ conversationId, operationId: `mls-birth-${digest.slice(0, 48)}` }).catch((error) => {
8602
7265
  if (!(error instanceof EndpointError) || ["ENDPOINT_CLOSED", "PREPARATION_INSTALLATION_FENCED"].includes(error.code)) throw error;
8603
7266
  });
8604
7267
  }
8605
7268
  __name(createBornGroup, "createBornGroup");
8606
7269
  const admissionRuns = /* @__PURE__ */ new Map(), admitting = /* @__PURE__ */ new Set(), admissionDelays = /* @__PURE__ */ new Map();
7270
+ const announcementsSeen = /* @__PURE__ */ new Map();
8607
7271
  const admissionTimers = /* @__PURE__ */ new Map();
8608
7272
  const ADMISSION_IDLE_MS = 10 * 6e4, CLAIM_JOURNAL_KEEP_MS = 60 * 6e4;
8609
7273
  const admissionUnfinished = /* @__PURE__ */ new Set();
@@ -8635,9 +7299,11 @@ function createEndpoint(options) {
8635
7299
  const missing = [...local?.memberIds ?? [], ...typeof invitee === "string" ? [invitee] : []].some((memberId) => !inRoster.has(memberId)) || admissionUnfinished.has(conversationId);
8636
7300
  if (!missing) admissionDelays.delete(conversationId);
8637
7301
  const delay = admissionDelays.get(conversationId) ?? admissionRetry.initial;
8638
- const due = (admissionRuns.get(conversationId) ?? -Infinity) + (missing ? delay : ADMISSION_IDLE_MS);
7302
+ const announced = local?.mlsDeviceAnnouncedAtMs;
7303
+ const due = announced !== announcementsSeen.get(conversationId) ? -Infinity : (admissionRuns.get(conversationId) ?? -Infinity) + (missing ? delay : ADMISSION_IDLE_MS);
8639
7304
  if (clock() < due) return missing ? scheduleAdmission(conversationId, due - clock()) : void 0;
8640
7305
  admitting.add(conversationId);
7306
+ announcementsSeen.set(conversationId, announced);
8641
7307
  try {
8642
7308
  admissionRuns.set(conversationId, clock());
8643
7309
  await mlsDevKeyPackages.pruneClaims(clock() - CLAIM_JOURNAL_KEEP_MS);
@@ -9014,7 +7680,7 @@ function createEndpoint(options) {
9014
7680
  observe: events.observe,
9015
7681
  isProtocolReady: /* @__PURE__ */ __name(async (id2) => isMlsDevTextProtocol((await resolveMlsRoomMode(id2)).protocol) ? await mlsDevGroups?.isTextReady(id2) ?? false : void 0, "isProtocolReady")
9016
7682
  });
9017
- const migration = createEndpointMigration({ store, ready: /* @__PURE__ */ __name(() => initialization, "ready"), query: /* @__PURE__ */ __name((name, parameters) => endpoint.query(name, parameters), "query"), hash: hash3, clock });
7683
+ const migration = createEndpointMigration({ store, ready: /* @__PURE__ */ __name(() => initialization, "ready"), query: /* @__PURE__ */ __name((name, parameters) => endpoint.query(name, parameters), "query"), hash, clock });
9018
7684
  const handoffs = createInboundHandoffs({
9019
7685
  store,
9020
7686
  owner,
@@ -9026,16 +7692,16 @@ function createEndpoint(options) {
9026
7692
  assertCurrent: assertCurrentInstallation,
9027
7693
  assertReady: /* @__PURE__ */ __name(async (records) => {
9028
7694
  if (options.admitRecoveredMessages) {
9029
- for (const id2 of new Set(records.map((record7) => record7.event.conversationId).filter((id3) => !!id3))) await recovery.assertReady(id2);
7695
+ for (const id2 of new Set(records.map((record6) => record6.event.conversationId).filter((id3) => !!id3))) await recovery.assertReady(id2);
9030
7696
  }
9031
7697
  }, "assertReady")
9032
7698
  });
9033
- const externalInbound = createExternalInboundClaims({ store, clock, hash: hash3, ownershipVersion: options.identity.ownershipVersion, ready: /* @__PURE__ */ __name(() => initialization, "ready"), assertOpen, assertCurrent: assertCurrentInstallation });
7699
+ const externalInbound = createExternalInboundClaims({ store, clock, hash, ownershipVersion: options.identity.ownershipVersion, ready: /* @__PURE__ */ __name(() => initialization, "ready"), assertOpen, assertCurrent: assertCurrentInstallation });
9034
7700
  const messageGroups = createMessageGroupCoordinator({
9035
7701
  store,
9036
7702
  endpoint: /* @__PURE__ */ __name(() => endpoint, "endpoint"),
9037
7703
  ready: /* @__PURE__ */ __name(() => initialization, "ready"),
9038
- hash: hash3,
7704
+ hash,
9039
7705
  clock,
9040
7706
  wake: /* @__PURE__ */ __name(() => scheduleRetry(0), "wake"),
9041
7707
  preparationGeneration: /* @__PURE__ */ __name(() => installationGeneration, "preparationGeneration"),
@@ -9183,7 +7849,7 @@ function createEndpoint(options) {
9183
7849
  const index = group?.children.findIndex((child) => child.operationId === operationId) ?? -1;
9184
7850
  const previous = group && index > 0 ? await tx.get(key(group.children[index - 1].operationId)) : void 0;
9185
7851
  if (previous?.status === "rejected" || previous?.status === "succeeded" && !messageGroupChildSucceeded(previous)) {
9186
- const rejected2 = { ...current, status: "rejected", updatedAt: now(), receipt: {
7852
+ const rejected = { ...current, status: "rejected", updatedAt: now(), receipt: {
9187
7853
  protocol: CANON_ENDPOINT_PROTOCOL,
9188
7854
  operationId,
9189
7855
  installationId: current.installationId,
@@ -9193,12 +7859,12 @@ function createEndpoint(options) {
9193
7859
  updatedAt: now(),
9194
7860
  error: { code: "DEPENDENCY_REJECTED", message: "An earlier part of this reply could not be delivered", retryable: false }
9195
7861
  } };
9196
- delete rejected2.request;
9197
- delete rejected2.preparedContent;
9198
- await tx.put(key(operationId), rejected2);
9199
- await removeOperationOutbox(tx, rejected2);
9200
- await settleOperationOrder(tx, rejected2);
9201
- return { operation: rejected2, acquired: false };
7862
+ delete rejected.request;
7863
+ delete rejected.preparedContent;
7864
+ await tx.put(key(operationId), rejected);
7865
+ await removeOperationOutbox(tx, rejected);
7866
+ await settleOperationOrder(tx, rejected);
7867
+ return { operation: rejected, acquired: false };
9202
7868
  }
9203
7869
  if (!messageGroups.allowed(current.messageGroupId) || !group || index < 0 || group.cancelled || index > 0 && !messageGroupChildSucceeded(previous)) return { operation: current, acquired: false };
9204
7870
  }
@@ -9325,8 +7991,8 @@ function createEndpoint(options) {
9325
7991
  await persistReceipt(operation, receipt);
9326
7992
  return receipt;
9327
7993
  } catch (error) {
9328
- const failure2 = safeError(error);
9329
- if (reconcileOnly && failure2.retryable) {
7994
+ const failure = safeError(error);
7995
+ if (reconcileOnly && failure.retryable) {
9330
7996
  await store.transaction(async (tx) => {
9331
7997
  const current = await tx.get(key(operationId));
9332
7998
  if (current?.lease?.owner === owner) {
@@ -9335,17 +8001,17 @@ function createEndpoint(options) {
9335
8001
  await tx.put(key(operationId), next);
9336
8002
  }
9337
8003
  });
9338
- return deferredReceipt(operation, failure2);
8004
+ return deferredReceipt(operation, failure);
9339
8005
  }
9340
8006
  const updated = await store.transaction(async (tx) => {
9341
8007
  const current = await tx.get(key(operationId));
9342
- if (!current) throw failure2;
8008
+ if (!current) throw failure;
9343
8009
  if (current.lease?.owner !== owner || terminal(current.status)) return current;
9344
- const lookupUncertain = resolvingReceipt && !failure2.retryable;
9345
- const generationUncertain = current.attempted && failure2.code === "PREPARATION_INSTALLATION_FENCED";
9346
- const contentModeUncertain = current.attempted && checkingContentMode && !failure2.retryable;
9347
- const guardUncertain = current.attempted && checkingDeliveryGuard && !failure2.retryable;
9348
- const next = { ...current, status: current.status === "uncertain" || lookupUncertain || generationUncertain || contentModeUncertain || guardUncertain ? "uncertain" : failure2.retryable ? "deferred" : "rejected", updatedAt: now(), nextAttemptAt: clock() + Math.max(failure2.retryAfterMs ?? 0, Math.min(3e4, 1e3 * 2 ** Math.min(current.attempts, 5))) };
8010
+ const lookupUncertain = resolvingReceipt && !failure.retryable;
8011
+ const generationUncertain = current.attempted && failure.code === "PREPARATION_INSTALLATION_FENCED";
8012
+ const contentModeUncertain = current.attempted && checkingContentMode && !failure.retryable;
8013
+ const guardUncertain = current.attempted && checkingDeliveryGuard && !failure.retryable;
8014
+ const next = { ...current, status: current.status === "uncertain" || lookupUncertain || generationUncertain || contentModeUncertain || guardUncertain ? "uncertain" : failure.retryable ? "deferred" : "rejected", updatedAt: now(), nextAttemptAt: clock() + Math.max(failure.retryAfterMs ?? 0, Math.min(3e4, 1e3 * 2 ** Math.min(current.attempts, 5))) };
9349
8015
  delete next.lease;
9350
8016
  if (current.sensitive) {
9351
8017
  delete next.request;
@@ -9355,14 +8021,14 @@ function createEndpoint(options) {
9355
8021
  next.receipt = { protocol: CANON_ENDPOINT_PROTOCOL, operationId: current.operationId, installationId: current.installationId, action: current.action, status: "uncertain", error: {
9356
8022
  code: generationUncertain ? "PREPARATION_INSTALLATION_FENCED" : guardUncertain ? "DELIVERY_GUARD_RECONCILIATION_REQUIRED" : contentModeUncertain ? "CONTENT_MODE_RECONCILIATION_REQUIRED" : "RECEIPT_LOOKUP_REJECTED",
9357
8023
  message: generationUncertain ? "The installation changed before the attempted operation could be reconciled" : contentModeUncertain || guardUncertain ? "The attempted operation requires receipt reconciliation before its outcome is known" : "The attempted operation could not be reconciled with the current credentials",
9358
- status: failure2.status,
8024
+ status: failure.status,
9359
8025
  retryable: false
9360
8026
  }, createdAt: current.createdAt, updatedAt: now() };
9361
8027
  delete next.request;
9362
8028
  delete next.preparedContent;
9363
8029
  delete next.nextAttemptAt;
9364
- } else if (!failure2.retryable && current.status !== "uncertain") {
9365
- next.receipt = { protocol: CANON_ENDPOINT_PROTOCOL, operationId: current.operationId, installationId: current.installationId, action: current.action, status: "rejected", error: { code: failure2.code, message: current.sensitive ? "Sensitive operation could not complete" : failure2.message, status: failure2.status, retryable: false }, createdAt: current.createdAt, updatedAt: now() };
8030
+ } else if (!failure.retryable && current.status !== "uncertain") {
8031
+ next.receipt = { protocol: CANON_ENDPOINT_PROTOCOL, operationId: current.operationId, installationId: current.installationId, action: current.action, status: "rejected", error: { code: failure.code, message: current.sensitive ? "Sensitive operation could not complete" : failure.message, status: failure.status, retryable: false }, createdAt: current.createdAt, updatedAt: now() };
9366
8032
  delete next.nextAttemptAt;
9367
8033
  }
9368
8034
  await tx.put(key(operationId), next);
@@ -9383,8 +8049,8 @@ function createEndpoint(options) {
9383
8049
  if (updated.messageGroupId && terminal(updated.status)) await messageGroups.settled(updated);
9384
8050
  if (terminal(updated.status)) scheduleRetry(0);
9385
8051
  notify(updated);
9386
- if (failure2.retryable && updated.status !== "uncertain" && !closed) scheduleRetry(Math.max(1e3, (updated.nextAttemptAt ?? clock()) - clock()));
9387
- return deferredReceipt(updated, failure2);
8052
+ if (failure.retryable && updated.status !== "uncertain" && !closed) scheduleRetry(Math.max(1e3, (updated.nextAttemptAt ?? clock()) - clock()));
8053
+ return deferredReceipt(updated, failure);
9388
8054
  } finally {
9389
8055
  clearInterval(heartbeat);
9390
8056
  }
@@ -9435,7 +8101,7 @@ function createEndpoint(options) {
9435
8101
  }
9436
8102
  const authored = JSON.parse(canonicalEndpointJson(intent2));
9437
8103
  const sensitive = executeOptions.sensitive === true || endpointOperationRequestRetention(authored.action, authored.body, authored.envelope) === "memory";
9438
- const inputFingerprint = sensitive ? void 0 : await hash3(canonicalEndpointJson({ ...authored, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} }));
8104
+ const inputFingerprint = sensitive ? void 0 : await hash(canonicalEndpointJson({ ...authored, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} }));
9439
8105
  const previous = await store.transaction(async (tx) => {
9440
8106
  await assertCurrentInstallation(tx);
9441
8107
  return tx.get(key(operationId));
@@ -9487,8 +8153,8 @@ function createEndpoint(options) {
9487
8153
  if (!sensitive && !previous) await reservePreparedIdentity(store, { operationId, action: authored.action, fingerprint: inputFingerprint, installationGeneration });
9488
8154
  const normalized = await prepareEndpointIntent({ intent: authored, previousPreconditions: previous?.preconditions, query: /* @__PURE__ */ __name((name, parameters) => endpoint.query(name, parameters), "query") });
9489
8155
  const routedBody = { ...normalized.body, ...normalized.envelope };
9490
- const sensitiveFingerprint = sensitive ? await hash3(canonicalEndpointJson({ ...normalized, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} })) : void 0;
9491
- const fingerprint = await hash3(canonicalEndpointJson({ ...sensitive ? { action: normalized.action, preconditions: normalized.preconditions } : normalized, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} }));
8156
+ const sensitiveFingerprint = sensitive ? await hash(canonicalEndpointJson({ ...normalized, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} })) : void 0;
8157
+ const fingerprint = await hash(canonicalEndpointJson({ ...sensitive ? { action: normalized.action, preconditions: normalized.preconditions } : normalized, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} }));
9492
8158
  assertOpen();
9493
8159
  const includePreparedContent = !sensitive && negotiatedCapabilities?.preparedContentVersion === 1;
9494
8160
  const request = {
@@ -9640,7 +8306,7 @@ function createEndpoint(options) {
9640
8306
  return store.transaction((tx) => tx.get(key(operationId)));
9641
8307
  },
9642
8308
  waitForOperation(operationId, waitOptions = {}) {
9643
- return new Promise((resolve6, reject) => {
8309
+ return new Promise((resolve3, reject) => {
9644
8310
  let finished = false;
9645
8311
  let checking = false;
9646
8312
  let poll;
@@ -9654,7 +8320,7 @@ function createEndpoint(options) {
9654
8320
  controller.signal.removeEventListener("abort", abort);
9655
8321
  waitOptions.signal?.removeEventListener("abort", abort);
9656
8322
  if (error) reject(error);
9657
- else resolve6(receipt);
8323
+ else resolve3(receipt);
9658
8324
  }, "finish");
9659
8325
  const abort = /* @__PURE__ */ __name(() => finish(void 0, new EndpointError("Delivery remains in the endpoint journal for reconciliation", {
9660
8326
  code: "OPERATION_PENDING",
@@ -9739,6 +8405,7 @@ function createEndpoint(options) {
9739
8405
  }
9740
8406
  const result = await transport.query(name, normalized, controller.signal);
9741
8407
  if (name === "mls_interactions") return await accountMlsInteractions(result);
8408
+ if (name === "list_conversations" && options.localInboxPreviews && mlsDevGroups) return await store.transaction((tx) => overlayMlsDevInboxPreviews(tx, result));
9742
8409
  return result;
9743
8410
  }).finally(() => {
9744
8411
  reads.delete(queryKey);
@@ -9773,7 +8440,7 @@ function createEndpoint(options) {
9773
8440
  observeEvents: events.observe,
9774
8441
  createMediaPipeline(bytes, mediaOptions) {
9775
8442
  assertOpen();
9776
- const pipeline3 = createEndpointMediaPipeline({ endpoint, store, bytes, hash: hash3, ids, clock, background: mediaOptions?.background });
8443
+ const pipeline3 = createEndpointMediaPipeline({ endpoint, store, bytes, hash, ids, clock, background: mediaOptions?.background });
9777
8444
  mediaPipelines2.add(pipeline3);
9778
8445
  return pipeline3;
9779
8446
  },
@@ -9803,14 +8470,14 @@ function createEndpoint(options) {
9803
8470
  await initialization;
9804
8471
  if (typeof input.messageId !== "string" || !input.messageId || typeof input.text !== "string")
9805
8472
  throw new EndpointError("Invalid deferred opening line", { status: 400, code: "INVALID_DEFERRED_OPENER", retryable: false });
9806
- const digest = await hash3(canonicalEndpointJson(["canon.withheld-opener.v1", input.messageId]));
8473
+ const digest = await hash(canonicalEndpointJson(["canon.withheld-opener.v1", input.messageId]));
9807
8474
  const operationId = `start-opener-${digest.slice(0, 48)}`;
9808
8475
  const body = {
9809
8476
  targetUserId: input.targetUserId,
9810
8477
  opener: { withheld: true, messageId: input.messageId },
9811
8478
  ...input.selection ? { selection: copyJson(input.selection) } : {}
9812
8479
  };
9813
- const openerFingerprint = await hash3(canonicalEndpointJson([body, input.text]));
8480
+ const openerFingerprint = await hash(canonicalEndpointJson([body, input.text]));
9814
8481
  const recordKey = `${openerPrefix()}/${encodeURIComponent(input.messageId)}`;
9815
8482
  await store.transaction(async (tx) => {
9816
8483
  await assertCurrentInstallation(tx);
@@ -9947,15 +8614,15 @@ function createEndpoint(options) {
9947
8614
  if (!eventIds.length) return false;
9948
8615
  if (options.admitRecoveredMessages) {
9949
8616
  const records = await Promise.all(eventIds.map((id2) => endpoint.getInbound(id2)));
9950
- for (const id2 of new Set(records.map((record7) => record7?.event.conversationId).filter((id3) => !!id3))) await recovery.assertReady(id2);
8617
+ for (const id2 of new Set(records.map((record6) => record6?.event.conversationId).filter((id3) => !!id3))) await recovery.assertReady(id2);
9951
8618
  }
9952
8619
  return store.transaction(async (tx) => {
9953
8620
  if (await tx.get("migration/active")) throw new EndpointError("Migration is incomplete; native execution is gated", { code: "MIGRATION_IN_PROGRESS", retryable: false });
9954
8621
  const records = await Promise.all(eventIds.map((id2) => tx.get(`inbound/${id2}`)));
9955
8622
  let projection = false;
9956
- for (const record7 of records) if (record7 && await settleInboundProjection(tx, record7, now())) projection = true;
8623
+ for (const record6 of records) if (record6 && await settleInboundProjection(tx, record6, now())) projection = true;
9957
8624
  if (projection) return false;
9958
- if (records.some((record7) => !record7 || record7.deleted || record7.externalClaim || record7.handoffGeneration && record7.lease || record7.state === "uncertain" || record7.state === "settled" || record7.lease && !record7.lease.owner.startsWith(`${owner}:`) && record7.lease.expiresAt > clock())) return false;
8625
+ if (records.some((record6) => !record6 || record6.deleted || record6.externalClaim || record6.handoffGeneration && record6.lease || record6.state === "uncertain" || record6.state === "settled" || record6.lease && !record6.lease.owner.startsWith(`${owner}:`) && record6.lease.expiresAt > clock())) return false;
9959
8626
  for (let i = 0; i < records.length; i++) {
9960
8627
  const next = { ...records[i], state: "uncertain", updatedAt: now() };
9961
8628
  delete next.lease;
@@ -10110,7 +8777,7 @@ function createEndpoint(options) {
10110
8777
  return endpoint;
10111
8778
  }
10112
8779
  __name(createEndpoint, "createEndpoint");
10113
- async function matchingRecords(tx, prefix3, options, matches2) {
8780
+ async function matchingRecords(tx, prefix3, options, matches) {
10114
8781
  const limit = Math.max(1, Math.min(options.limit ?? 100, 1e3));
10115
8782
  const result = [];
10116
8783
  let after = options.after ? `${prefix3}${options.after}` : void 0;
@@ -10118,7 +8785,7 @@ async function matchingRecords(tx, prefix3, options, matches2) {
10118
8785
  const batch = await tx.scan(prefix3, { after, limit: 100 });
10119
8786
  for (const entry of batch) {
10120
8787
  after = entry.key;
10121
- if (matches2(entry.value)) result.push(entry.value);
8788
+ if (matches(entry.value)) result.push(entry.value);
10122
8789
  if (result.length === limit) return result;
10123
8790
  }
10124
8791
  if (batch.length < 100) return result;
@@ -10127,9 +8794,9 @@ async function matchingRecords(tx, prefix3, options, matches2) {
10127
8794
  __name(matchingRecords, "matchingRecords");
10128
8795
 
10129
8796
  // ../core/src/endpoint-client.ts
10130
- import { createHash as createHash5 } from "node:crypto";
8797
+ import { createHash as createHash2 } from "node:crypto";
10131
8798
  import { homedir } from "node:os";
10132
- import { join as join4 } from "node:path";
8799
+ import { join as join2 } from "node:path";
10133
8800
  var mediaPipelines = /* @__PURE__ */ new WeakMap();
10134
8801
  function acquireFileMediaPipeline(endpoint, options, fetchImpl) {
10135
8802
  const existing = mediaPipelines.get(endpoint);
@@ -10139,7 +8806,7 @@ function acquireFileMediaPipeline(endpoint, options, fetchImpl) {
10139
8806
  if (options.endpoint && !options.mediaBytes && !options.mediaDirectory && !options.stateDirectory) {
10140
8807
  throw new Error("An injected Canon cloud endpoint requires durable mediaBytes or a persistent mediaDirectory.");
10141
8808
  }
10142
- const directory = options.mediaDirectory ?? join4(options.stateDirectory ?? process.env.CANON_ENDPOINT_STATE_DIR ?? join4(process.env.CANON_HOME || join4(homedir(), ".canon"), "endpoint"), "media", identity.installationId);
8809
+ const directory = options.mediaDirectory ?? join2(options.stateDirectory ?? process.env.CANON_ENDPOINT_STATE_DIR ?? join2(process.env.CANON_HOME || join2(homedir(), ".canon"), "endpoint"), "media", identity.installationId);
10143
8810
  const pipeline3 = endpoint.createMediaPipeline(options.mediaBytes ?? createNodeEndpointMediaBytes({ directory, fetch: fetchImpl }));
10144
8811
  if (options.autoDrain !== false) void pipeline3.resumePending();
10145
8812
  return pipeline3;
@@ -10161,7 +8828,7 @@ async function acquireAgentEndpoint(input) {
10161
8828
  }
10162
8829
  const requestedMls = input.options.mlsDev;
10163
8830
  const mls = requestedMls ? await (async () => {
10164
- const crypto2 = await import("./mls-crypto-Y44BOSEM.js");
8831
+ const crypto2 = await import("./mls-crypto-4DZT2UFH.js");
10165
8832
  const configuration = crypto2.parseMlsDevClientConfiguration(requestedMls.configuration);
10166
8833
  if (configuration.environmentId !== environmentId || baseUrl !== CANON_RUNTIME_ENDPOINT_DEFAULTS[configuration.environmentId]?.apiBaseUrl || configuration.principalIds && !configuration.principalIds.includes(input.principalId) || !Number.isSafeInteger(input.ownershipVersion) || input.ownershipVersion < 0) throw new Error("MLS_DEV_AGENT_CONFIGURATION_BINDING_MISMATCH");
10167
8834
  const custody = requestedMls.custody;
@@ -10190,8 +8857,8 @@ async function acquireAgentEndpoint(input) {
10190
8857
  }
10191
8858
  const streamUrl = input.options.streamUrl ?? Object.entries(CANON_RUNTIME_ENDPOINT_DEFAULTS).find(([id2]) => id2 === environmentId)?.[1]?.streamUrl;
10192
8859
  if (!streamUrl) throw new Error("Canon endpoint requires a streamUrl for this environment.");
10193
- const directory = input.options.stateDirectory ?? process.env.CANON_ENDPOINT_STATE_DIR ?? join4(process.env.CANON_HOME || join4(homedir(), ".canon"), "endpoint");
10194
- const namespace = createHash5("sha256").update(`${environmentId}
8860
+ const directory = input.options.stateDirectory ?? process.env.CANON_ENDPOINT_STATE_DIR ?? join2(process.env.CANON_HOME || join2(homedir(), ".canon"), "endpoint");
8861
+ const namespace = createHash2("sha256").update(`${environmentId}
10195
8862
  ${baseUrl}
10196
8863
  ${input.principalId}
10197
8864
  ${input.ownershipVersion}`).digest("hex");
@@ -10933,7 +9600,7 @@ function asCanonApiError(error) {
10933
9600
  }
10934
9601
  __name(asCanonApiError, "asCanonApiError");
10935
9602
  function stableOperationId(...parts) {
10936
- return createHash6("sha256").update(JSON.stringify(parts)).digest("hex");
9603
+ return createHash3("sha256").update(JSON.stringify(parts)).digest("hex");
10937
9604
  }
10938
9605
  __name(stableOperationId, "stableOperationId");
10939
9606
  function normalizeMessage(raw) {
@@ -11193,14 +9860,14 @@ async function resumeRegistration(options) {
11193
9860
  if (status.status === "rejected") return { status: "rejected", requestId };
11194
9861
  const remaining = deadline - Date.now();
11195
9862
  if (remaining <= 0) return { status: "pending", requestId };
11196
- await new Promise((resolve6, reject) => {
9863
+ await new Promise((resolve3, reject) => {
11197
9864
  const abort = /* @__PURE__ */ __name(() => {
11198
9865
  clearTimeout(timer);
11199
9866
  reject(signal.reason);
11200
9867
  }, "abort");
11201
9868
  const timer = setTimeout(() => {
11202
9869
  signal?.removeEventListener("abort", abort);
11203
- resolve6();
9870
+ resolve3();
11204
9871
  }, Math.min(pollIntervalMs, remaining));
11205
9872
  signal?.addEventListener("abort", abort, { once: true });
11206
9873
  if (signal?.aborted) abort();
@@ -11213,7 +9880,7 @@ __name(resumeRegistration, "resumeRegistration");
11213
9880
  import { existsSync, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync } from "node:fs";
11214
9881
  import { closeSync as closeSync2, openSync as openSync2 } from "node:fs";
11215
9882
  import { randomUUID as randomUUID5 } from "node:crypto";
11216
- import { join as join5, resolve as resolve4 } from "node:path";
9883
+ import { join as join3, resolve } from "node:path";
11217
9884
  import { homedir as homedir2 } from "node:os";
11218
9885
 
11219
9886
  // ../core/src/process-lock.ts
@@ -11254,15 +9921,15 @@ function claimStaleLockReaper(lockPath) {
11254
9921
  __name(claimStaleLockReaper, "claimStaleLockReaper");
11255
9922
 
11256
9923
  // ../core/src/agent-profiles.ts
11257
- var CANON_DIR = process.env.CANON_HOME ? resolve4(process.env.CANON_HOME) : join5(homedir2(), ".canon");
11258
- var AGENTS_PATH = join5(CANON_DIR, "agents.json");
11259
- var LOCKS_DIR = join5(CANON_DIR, "locks");
11260
- var PENDING_REGISTRATIONS_PATH = join5(CANON_DIR, "pending-registrations.json");
9924
+ var CANON_DIR = process.env.CANON_HOME ? resolve(process.env.CANON_HOME) : join3(homedir2(), ".canon");
9925
+ var AGENTS_PATH = join3(CANON_DIR, "agents.json");
9926
+ var LOCKS_DIR = join3(CANON_DIR, "locks");
9927
+ var PENDING_REGISTRATIONS_PATH = join3(CANON_DIR, "pending-registrations.json");
11261
9928
  var AGENTS_JSON_BOOTSTRAP_ENV = "CANON_AGENTS_JSON_BOOTSTRAP";
11262
9929
  function canonPaths(home) {
11263
9930
  if (!home) return { dir: CANON_DIR, agents: AGENTS_PATH, locks: LOCKS_DIR };
11264
- const dir = resolve4(home);
11265
- return { dir, agents: join5(dir, "agents.json"), locks: join5(dir, "locks") };
9931
+ const dir = resolve(home);
9932
+ return { dir, agents: join3(dir, "agents.json"), locks: join3(dir, "locks") };
11266
9933
  }
11267
9934
  __name(canonPaths, "canonPaths");
11268
9935
  function normalizeAgentProfile(name, value, source) {
@@ -11429,7 +10096,7 @@ function sameProcess(owner) {
11429
10096
  }
11430
10097
  __name(sameProcess, "sameProcess");
11431
10098
  function isProfileLocked(profile, home) {
11432
- const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
10099
+ const lockPath = join3(canonPaths(home).locks, `${profile}.lock`);
11433
10100
  try {
11434
10101
  const lock = parseLock(readFileSync2(lockPath, "utf-8"));
11435
10102
  const pid = lock?.pid;
@@ -11454,7 +10121,7 @@ __name(parseLock, "parseLock");
11454
10121
  function acquireLock(profile, home) {
11455
10122
  const locks = canonPaths(home).locks;
11456
10123
  mkdirSync(locks, { recursive: true });
11457
- const lockPath = join5(locks, `${profile}.lock`);
10124
+ const lockPath = join3(locks, `${profile}.lock`);
11458
10125
  const token = randomUUID5();
11459
10126
  const payload = JSON.stringify({
11460
10127
  pid: process.pid,
@@ -11506,7 +10173,7 @@ function acquireLock(profile, home) {
11506
10173
  }
11507
10174
  __name(acquireLock, "acquireLock");
11508
10175
  function releaseLock(profile, token, home) {
11509
- const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
10176
+ const lockPath = join3(canonPaths(home).locks, `${profile}.lock`);
11510
10177
  if (token) {
11511
10178
  try {
11512
10179
  const lock = parseLock(readFileSync2(lockPath, "utf-8"));
@@ -11803,7 +10470,7 @@ var RuntimeRequestManager = class {
11803
10470
  }
11804
10471
  const key = pendingKey(kind, conversationId, effectiveId);
11805
10472
  const signal = options.signal;
11806
- return await new Promise((resolve6, reject) => {
10473
+ return await new Promise((resolve3, reject) => {
11807
10474
  let settled = false;
11808
10475
  const finishCommon = /* @__PURE__ */ __name(() => {
11809
10476
  settled = true;
@@ -11829,7 +10496,7 @@ var RuntimeRequestManager = class {
11829
10496
  } catch {
11830
10497
  }
11831
10498
  }
11832
- resolve6(finalResult);
10499
+ resolve3(finalResult);
11833
10500
  })();
11834
10501
  }, "settle");
11835
10502
  const abort = /* @__PURE__ */ __name((error) => {
@@ -11963,7 +10630,7 @@ var RuntimeRequestManager = class {
11963
10630
  pending.wakeRequested = false;
11964
10631
  return Promise.resolve();
11965
10632
  }
11966
- return new Promise((resolve6) => {
10633
+ return new Promise((resolve3) => {
11967
10634
  let done = false;
11968
10635
  const finish = /* @__PURE__ */ __name(() => {
11969
10636
  if (done) return;
@@ -11975,7 +10642,7 @@ var RuntimeRequestManager = class {
11975
10642
  current.wake = () => {
11976
10643
  };
11977
10644
  }
11978
- resolve6();
10645
+ resolve3();
11979
10646
  }, "finish");
11980
10647
  const timer = setTimeout(finish, ms);
11981
10648
  const entry = this.pending.get(key);
@@ -12752,11 +11419,11 @@ function buildRuntimeActivityItemPath(conversationId, agentId, itemId) {
12752
11419
  return `${buildRuntimeActivityPath(conversationId, agentId)}/${runtimeActivityItemKey(itemId)}`;
12753
11420
  }
12754
11421
  __name(buildRuntimeActivityItemPath, "buildRuntimeActivityItemPath");
12755
- function record6(value) {
11422
+ function record5(value) {
12756
11423
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected runtime projection object");
12757
11424
  return value;
12758
11425
  }
12759
- __name(record6, "record");
11426
+ __name(record5, "record");
12760
11427
  var PROJECTIONS = /* @__PURE__ */ new Set(["agent-session", "turn-state", "runtime-info", "runtime-activity", "streaming"]);
12761
11428
  function createRTDBClientHandle(client) {
12762
11429
  async function scope5(path) {
@@ -12783,40 +11450,40 @@ function createRTDBClientHandle(client) {
12783
11450
  const target = await scope5(path);
12784
11451
  const clear = method === "remove" || data === null;
12785
11452
  if (target.family === "agent-runtime") {
12786
- if (clear || record6(data).updatedAt === 0) return client.updateRuntimeStatus({ online: false });
12787
- const value = record6(data);
11453
+ if (clear || record5(data).updatedAt === 0) return client.updateRuntimeStatus({ online: false });
11454
+ const value = record5(data);
12788
11455
  if (typeof value.clientType !== "string" || !value.clientType) throw new Error("Missing runtime client type");
12789
11456
  return client.updateRuntimeStatus({
12790
11457
  runtime: value.clientType,
12791
11458
  ...typeof value.hostMode === "boolean" ? { hostMode: value.hostMode } : {},
12792
- ...value.runtimeDescriptor ? { runtimeDescriptor: record6(value.runtimeDescriptor) } : {}
11459
+ ...value.runtimeDescriptor ? { runtimeDescriptor: record5(value.runtimeDescriptor) } : {}
12793
11460
  });
12794
11461
  }
12795
11462
  if (target.family === "agent-session") {
12796
11463
  if (clear) return client.publishRuntimeSession(target.conversationId, "clear");
12797
- const state = sanitizeAgentSessionSnapshotPatch(record6(data));
11464
+ const state = sanitizeAgentSessionSnapshotPatch(record5(data));
12798
11465
  return client.publishRuntimeSession(target.conversationId, "patch", state);
12799
11466
  }
12800
11467
  if (target.family === "runtime-info") {
12801
11468
  if (clear) return client.publishRuntimeInfo(target.conversationId, "clear");
12802
- const { updatedAt: _updatedAt, ...info } = record6(data);
11469
+ const { updatedAt: _updatedAt, ...info } = record5(data);
12803
11470
  return client.publishRuntimeInfo(target.conversationId, method === "patch" ? "patch" : "replace", info);
12804
11471
  }
12805
11472
  if (target.family === "runtime-activity") {
12806
11473
  if (clear) return client.publishRuntimeActivity(target.conversationId, target.itemId ? { mode: "remove", itemId: target.itemId } : { mode: "clear" });
12807
11474
  if (!target.itemId) throw new Error("Write one runtime activity item");
12808
- const item = record6(data);
11475
+ const item = record5(data);
12809
11476
  if (typeof item.id !== "string" || runtimeActivityItemKey(item.id) !== target.itemId) throw new Error("Runtime activity identity mismatch");
12810
11477
  return client.publishRuntimeActivity(target.conversationId, { mode: "upsert", item });
12811
11478
  }
12812
11479
  if (target.family === "streaming") {
12813
11480
  if (clear) return client.clearStreaming(target.conversationId);
12814
- const { updatedAt: _updatedAt, startedAt: _startedAt, ...patch2 } = record6(data);
11481
+ const { updatedAt: _updatedAt, startedAt: _startedAt, ...patch2 } = record5(data);
12815
11482
  if (Object.keys(patch2).some((key) => !["text", "status", "messageId", "turnId", "blocks"].includes(key))) throw new Error("Unsupported streaming projection field");
12816
11483
  return client.patchRuntimeStreaming(target.conversationId, patch2);
12817
11484
  }
12818
11485
  if (target.family === "turn-state") {
12819
- const value = clear ? { state: "idle", queueDepth: 0, turnId: null, openedAt: null, turnUpdatedAt: null } : record6(data);
11486
+ const value = clear ? { state: "idle", queueDepth: 0, turnId: null, openedAt: null, turnUpdatedAt: null } : record5(data);
12820
11487
  const { updatedAt: _updatedAt, completedAt: _completedAt, openedAt, turnUpdatedAt, ...turn } = value;
12821
11488
  return client.setRuntimeTurn({
12822
11489
  ...turn,
@@ -13201,12 +11868,12 @@ __name(applyFenceLine, "applyFenceLine");
13201
11868
  function scanFenceState(text, fence) {
13202
11869
  if (!text) return fence;
13203
11870
  const lines = text.split("\n");
13204
- let open5 = fence;
11871
+ let open3 = fence;
13205
11872
  for (let index = 0; index < lines.length; index += 1) {
13206
11873
  if (index === lines.length - 1 && lines[index] === "") continue;
13207
- open5 = applyFenceLine(open5, lines[index]);
11874
+ open3 = applyFenceLine(open3, lines[index]);
13208
11875
  }
13209
- return open5;
11876
+ return open3;
13210
11877
  }
13211
11878
  __name(scanFenceState, "scanFenceState");
13212
11879
  function codePointUtf8Bytes(codePoint) {
@@ -13417,12 +12084,12 @@ __name(sendMessageWithRetryChunked, "sendMessageWithRetryChunked");
13417
12084
 
13418
12085
  // ../core/src/cli-metadata.ts
13419
12086
  import { readFileSync as readFileSync3, realpathSync } from "node:fs";
13420
- import { dirname as dirname2, resolve as resolve5 } from "node:path";
12087
+ import { dirname, resolve as resolve2 } from "node:path";
13421
12088
  import { fileURLToPath, pathToFileURL } from "node:url";
13422
12089
  function isDirectExecution(moduleUrl) {
13423
12090
  const entry = process.argv[1];
13424
12091
  if (!entry) return false;
13425
- const abs = resolve5(entry);
12092
+ const abs = resolve2(entry);
13426
12093
  let real = abs;
13427
12094
  try {
13428
12095
  real = realpathSync(abs);
@@ -13432,13 +12099,13 @@ function isDirectExecution(moduleUrl) {
13432
12099
  }
13433
12100
  __name(isDirectExecution, "isDirectExecution");
13434
12101
  function readCliPackageVersion(moduleUrl) {
13435
- let current = dirname2(fileURLToPath(moduleUrl));
12102
+ let current = dirname(fileURLToPath(moduleUrl));
13436
12103
  while (true) {
13437
12104
  try {
13438
- const manifest = JSON.parse(readFileSync3(resolve5(current, "package.json"), "utf8"));
12105
+ const manifest = JSON.parse(readFileSync3(resolve2(current, "package.json"), "utf8"));
13439
12106
  return typeof manifest.version === "string" ? manifest.version : null;
13440
12107
  } catch {
13441
- const parent = dirname2(current);
12108
+ const parent = dirname(current);
13442
12109
  if (parent === current) return null;
13443
12110
  current = parent;
13444
12111
  }
@@ -13477,7 +12144,7 @@ function runCli(moduleUrl, main, onError, metadata) {
13477
12144
  __name(runCli, "runCli");
13478
12145
 
13479
12146
  // ../core/src/completed-output-journal.ts
13480
- import { createHash as createHash7, randomUUID as randomUUID7 } from "node:crypto";
12147
+ import { createHash as createHash4, randomUUID as randomUUID7 } from "node:crypto";
13481
12148
  import {
13482
12149
  closeSync as closeSync3,
13483
12150
  existsSync as existsSync2,
@@ -13490,12 +12157,13 @@ import {
13490
12157
  unlinkSync as unlinkSync3,
13491
12158
  writeFileSync as writeFileSync3
13492
12159
  } from "node:fs";
13493
- import { join as join6 } from "node:path";
12160
+ import { join as join4 } from "node:path";
13494
12161
  function createCompletedOutputJournal(directory, scope5, options) {
13495
12162
  const label = options.label;
13496
- const folder = join6(directory, createHash7("sha256").update(JSON.stringify(scope5)).digest("hex"));
13497
- const filename = /* @__PURE__ */ __name((turnId) => join6(folder, `${createHash7("sha256").update(turnId).digest("hex")}.json`), "filename");
12163
+ const folder = join4(directory, createHash4("sha256").update(JSON.stringify(scope5)).digest("hex"));
12164
+ const filename = /* @__PURE__ */ __name((turnId) => join4(folder, `${createHash4("sha256").update(turnId).digest("hex")}.json`), "filename");
13498
12165
  function syncDirectory() {
12166
+ if (process.platform === "win32") return;
13499
12167
  const fd = openSync3(folder, "r");
13500
12168
  try {
13501
12169
  fsyncSync(fd);
@@ -13504,8 +12172,8 @@ function createCompletedOutputJournal(directory, scope5, options) {
13504
12172
  }
13505
12173
  }
13506
12174
  __name(syncDirectory, "syncDirectory");
13507
- function validate(record7) {
13508
- if (record7?.version !== 1 || typeof record7.turnId !== "string" || !record7.turnId || typeof record7.conversationId !== "string" || !record7.conversationId || typeof record7.createdAt !== "string" || options.validate && !options.validate(record7)) {
12175
+ function validate(record6) {
12176
+ if (record6?.version !== 1 || typeof record6.turnId !== "string" || !record6.turnId || typeof record6.conversationId !== "string" || !record6.conversationId || typeof record6.createdAt !== "string" || options.validate && !options.validate(record6)) {
13509
12177
  throw new Error(`Invalid completed ${label} output record; operator reconciliation is required`);
13510
12178
  }
13511
12179
  }
@@ -13520,21 +12188,21 @@ function createCompletedOutputJournal(directory, scope5, options) {
13520
12188
  } catch (error) {
13521
12189
  if (error.code !== "ESRCH") continue;
13522
12190
  }
13523
- const path = join6(folder, name);
13524
- let record7;
12191
+ const path = join4(folder, name);
12192
+ let record6;
13525
12193
  try {
13526
- record7 = JSON.parse(readFileSync4(path, "utf8"));
13527
- validate(record7);
12194
+ record6 = JSON.parse(readFileSync4(path, "utf8"));
12195
+ validate(record6);
13528
12196
  } catch {
13529
12197
  unlinkSync3(path);
13530
12198
  throw new Error(`Incomplete ${label} output write removed; reconcile its native transcript`);
13531
12199
  }
13532
- const destination = filename(record7.turnId);
13533
- if (destination !== join6(folder, `${match[1]}.json`)) {
12200
+ const destination = filename(record6.turnId);
12201
+ if (destination !== join4(folder, `${match[1]}.json`)) {
13534
12202
  throw new Error(`Completed ${label} output temporary file identity mismatch`);
13535
12203
  }
13536
12204
  if (existsSync2(destination)) {
13537
- if (readFileSync4(destination, "utf8") !== JSON.stringify(record7)) {
12205
+ if (readFileSync4(destination, "utf8") !== JSON.stringify(record6)) {
13538
12206
  throw new Error(`Conflicting completed ${label} outputs require reconciliation`);
13539
12207
  }
13540
12208
  unlinkSync3(path);
@@ -13544,11 +12212,11 @@ function createCompletedOutputJournal(directory, scope5, options) {
13544
12212
  }
13545
12213
  __name(recoverTemporaryFiles, "recoverTemporaryFiles");
13546
12214
  return {
13547
- save(record7) {
13548
- validate(record7);
12215
+ save(record6) {
12216
+ validate(record6);
13549
12217
  mkdirSync2(folder, { recursive: true, mode: 448 });
13550
- const path = filename(record7.turnId);
13551
- const serialized = JSON.stringify(record7);
12218
+ const path = filename(record6.turnId);
12219
+ const serialized = JSON.stringify(record6);
13552
12220
  if (existsSync2(path)) {
13553
12221
  if (readFileSync4(path, "utf8") !== serialized) throw new Error(`Completed ${label} output cannot be replaced`);
13554
12222
  return;
@@ -13576,10 +12244,10 @@ function createCompletedOutputJournal(directory, scope5, options) {
13576
12244
  if (!existsSync2(folder)) return [];
13577
12245
  recoverTemporaryFiles();
13578
12246
  return readdirSync(folder).filter((name) => name.endsWith(".json")).map((name) => {
13579
- const record7 = JSON.parse(readFileSync4(join6(folder, name), "utf8"));
13580
- validate(record7);
13581
- if (filename(record7.turnId) !== join6(folder, name)) throw new Error(`Completed ${label} output identity mismatch`);
13582
- return record7;
12247
+ const record6 = JSON.parse(readFileSync4(join4(folder, name), "utf8"));
12248
+ validate(record6);
12249
+ if (filename(record6.turnId) !== join4(folder, name)) throw new Error(`Completed ${label} output identity mismatch`);
12250
+ return record6;
13583
12251
  }).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
13584
12252
  },
13585
12253
  remove(turnId) {
@@ -13593,8 +12261,8 @@ function createCompletedOutputJournal(directory, scope5, options) {
13593
12261
  };
13594
12262
  }
13595
12263
  __name(createCompletedOutputJournal, "createCompletedOutputJournal");
13596
- function assertCompletedOutputAudience(record7, current, label) {
13597
- if (!record7.audience || !current || record7.audience.membershipRevision !== void 0 && record7.audience.membershipRevision !== current.membershipRevision || JSON.stringify([...record7.audience.memberIds].sort()) !== JSON.stringify([...current.memberIds].sort())) {
12264
+ function assertCompletedOutputAudience(record6, current, label) {
12265
+ if (!record6.audience || !current || record6.audience.membershipRevision !== void 0 && record6.audience.membershipRevision !== current.membershipRevision || JSON.stringify([...record6.audience.memberIds].sort()) !== JSON.stringify([...current.memberIds].sort())) {
13598
12266
  throw new Error(`Completed ${label} output audience changed; operator reconciliation is required`);
13599
12267
  }
13600
12268
  }
@@ -13602,9 +12270,9 @@ __name(assertCompletedOutputAudience, "assertCompletedOutputAudience");
13602
12270
 
13603
12271
  // src/bounded-file-read.ts
13604
12272
  var BoundedFileReadError = class extends Error {
13605
- constructor(failure2) {
13606
- super(failure2);
13607
- this.failure = failure2;
12273
+ constructor(failure) {
12274
+ super(failure);
12275
+ this.failure = failure;
13608
12276
  this.name = "BoundedFileReadError";
13609
12277
  }
13610
12278
  failure;
@@ -13643,7 +12311,7 @@ var ANTHROPIC_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
13643
12311
  "image/gif",
13644
12312
  "image/webp"
13645
12313
  ]);
13646
- var DEFAULT_MEDIA_CACHE_DIR = join7(CANON_DIR, "media-cache");
12314
+ var DEFAULT_MEDIA_CACHE_DIR = join5(CANON_DIR, "media-cache");
13647
12315
  var DEFAULT_MEDIA_MATERIALIZATION_BYTES = 10 * 1024 * 1024;
13648
12316
  var MAX_CANON_MEDIA_BYTES = 100 * 1024 * 1024;
13649
12317
  var MAX_ANTHROPIC_IMAGE_RAW_BYTES = 7 * 1024 * 1024;
@@ -13711,7 +12379,7 @@ function inferExtension(input) {
13711
12379
  }
13712
12380
  __name(inferExtension, "inferExtension");
13713
12381
  function conversationMediaCacheDir(input) {
13714
- return join7(
12382
+ return join5(
13715
12383
  input.rootDir ?? DEFAULT_MEDIA_CACHE_DIR,
13716
12384
  sanitizeSegment(input.agentId, "agent"),
13717
12385
  sanitizeSegment(input.conversationId, "conversation")
@@ -13727,7 +12395,7 @@ function buildCachePath(input) {
13727
12395
  const fallbackName = `${input.attachment.kind}-${input.index}${extension}`;
13728
12396
  const preferredName = input.attachment.fileName ? sanitizeDownloadFileName(input.attachment.fileName, fallbackName) : fallbackName;
13729
12397
  const fileName = extname(preferredName) ? preferredName : `${preferredName}${extension}`;
13730
- return join7(
12398
+ return join5(
13731
12399
  conversationMediaCacheDir({ agentId: input.agentId, conversationId: input.conversationId, rootDir }),
13732
12400
  sanitizeSegment(input.messageId, "message"),
13733
12401
  `${String(input.index).padStart(2, "0")}-${fileName}`
@@ -13840,7 +12508,7 @@ async function materializeAttachment(attachment, options) {
13840
12508
  index: options.index ?? 0,
13841
12509
  rootDir: options.rootDir
13842
12510
  });
13843
- await mkdir3(dirname3(path), { recursive: true });
12511
+ await mkdir2(dirname2(path), { recursive: true });
13844
12512
  const maxBytes = resolveMaxBytes(options.maxBytes);
13845
12513
  const expectedBytes = typeof attachment.sizeBytes === "number" && Number.isSafeInteger(attachment.sizeBytes) && attachment.sizeBytes >= 0 ? attachment.sizeBytes : null;
13846
12514
  if (expectedBytes !== null && expectedBytes > maxBytes) {
@@ -14056,7 +12724,7 @@ async function toAnthropicImageBlock(attachment, options) {
14056
12724
  }
14057
12725
  __name(toAnthropicImageBlock, "toAnthropicImageBlock");
14058
12726
  async function readAnthropicImageBytes(attachment, maxRawBytes) {
14059
- const handle = await open4(attachment.path, "r");
12727
+ const handle = await open2(attachment.path, "r");
14060
12728
  try {
14061
12729
  return await readBoundedRegularFileHandle(handle, maxRawBytes);
14062
12730
  } catch (error) {
@@ -14162,9 +12830,6 @@ function getCodexImagePath(attachment) {
14162
12830
  __name(getCodexImagePath, "getCodexImagePath");
14163
12831
 
14164
12832
  export {
14165
- NodeMlsDevCustodyError,
14166
- createNodeMlsDevCustody,
14167
- createPortableNodeMlsDevCustody,
14168
12833
  resolveCanonRuntimeConnection,
14169
12834
  verifyCanonRuntimeConnection,
14170
12835
  CanonClient,