@canonmsg/agent-sdk 12.4.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 (40) hide show
  1. package/README.md +37 -6
  2. package/dist/chunks/{chunk-6PKXASSK.js → chunk-5J4YPLNW.js} +394 -1687
  3. package/dist/chunks/{chunk-NXL4C2R6.js → chunk-DAJ7L7TN.js} +954 -429
  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-GWQF4WFT.js → chunk-S7BV74DN.js} +734 -812
  8. package/dist/chunks/{mls-crypto-LGPCXM3K.js → mls-crypto-4DZT2UFH.js} +2 -1
  9. package/dist/cli.js +70 -32
  10. package/dist/drivers/acp.d.ts +56 -0
  11. package/dist/drivers/codex.d.ts +1 -2
  12. package/dist/drivers/connector.d.ts +5 -4
  13. package/dist/drivers/subprocess.d.ts +35 -0
  14. package/dist/index.d.ts +3 -1
  15. package/dist/index.js +12 -3
  16. package/dist/internal/backend-contracts/connectorRelay.d.ts +34 -0
  17. package/dist/internal/backend-contracts/endpoint.d.ts +1 -1
  18. package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +2 -2
  19. package/dist/internal/backend-contracts/index.d.ts +1 -0
  20. package/dist/internal/backend-contracts/loggedOutSessions.d.ts +7 -0
  21. package/dist/internal/backend-contracts/mlsDevCredential.d.ts +3 -0
  22. package/dist/internal/endpoint/engine.d.ts +8 -1
  23. package/dist/internal/endpoint/mls-dev-client.d.ts +11 -0
  24. package/dist/internal/endpoint/mls-dev-text.d.ts +3 -0
  25. package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
  26. package/dist/internal/endpoint/node.d.ts +1 -0
  27. package/dist/media.js +4 -2
  28. package/dist/mls/darwin-arm64/canon-mls-custody +0 -0
  29. package/dist/mls/linux-arm64/canon-mls-custody +0 -0
  30. package/dist/mls/linux-x64/canon-mls-custody +0 -0
  31. package/dist/mls/wasm/canon_mls_wasm.js +363 -0
  32. package/dist/mls/wasm/canon_mls_wasm_bg.wasm +0 -0
  33. package/dist/mls-wasm-host.js +258 -0
  34. package/dist/runner/mls-bundled.d.ts +15 -2
  35. package/dist/runner/mls-managed.d.ts +10 -2
  36. package/dist/runner/mls.d.ts +7 -1
  37. package/dist/runner/relay.d.ts +3 -3
  38. package/dist/runner/runner.d.ts +18 -7
  39. package/dist/runner/types.d.ts +12 -4
  40. 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-GWQF4WFT.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) => {
@@ -5219,7 +3841,7 @@ function createMlsDevDeviceClient(options, context) {
5219
3841
  const store = guardedStore();
5220
3842
  const fence = /* @__PURE__ */ __name(() => store.transaction(async () => {
5221
3843
  }), "fence");
5222
- async function initialized() {
3844
+ async function initialized(storedPrincipalId) {
5223
3845
  context.assertOpen();
5224
3846
  const { identity, installationGeneration } = await context.ready();
5225
3847
  await fence();
@@ -5230,7 +3852,7 @@ function createMlsDevDeviceClient(options, context) {
5230
3852
  const binding = parseMlsDevDeviceBinding({
5231
3853
  version: 1,
5232
3854
  environmentId: identity.environmentId,
5233
- principal: { ...principal, id: await context.mlsPrincipalId() },
3855
+ principal: { ...principal, id: storedPrincipalId ?? await context.mlsPrincipalId() },
5234
3856
  installationId: identity.installationId
5235
3857
  });
5236
3858
  const coordinator = /* @__PURE__ */ __name((journal) => createMlsDevDeviceCoordinator({
@@ -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);
@@ -5517,6 +4142,20 @@ function createMlsDevDeviceClient(options, context) {
5517
4142
  }
5518
4143
  }
5519
4144
  __name(ensureReady, "ensureReady");
4145
+ async function revoke() {
4146
+ const { coordinator, transport: http } = await initialized();
4147
+ const device = coordinator(store), view3 = await device.read();
4148
+ if (view3.status === "revoked" || !view3.registration) return view3;
4149
+ const receipt = await http.revokeCredential({ version: 1, deviceId: view3.registration.device.deviceId }, context.signal);
4150
+ return device.acceptRevocation(receipt);
4151
+ }
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");
5520
4159
  function run(goal) {
5521
4160
  context.assertOpen();
5522
4161
  const preceding = running;
@@ -5543,12 +4182,27 @@ function createMlsDevDeviceClient(options, context) {
5543
4182
  return promise;
5544
4183
  }
5545
4184
  __name(run, "run");
4185
+ function read(options2) {
4186
+ return context.run(async () => {
4187
+ let storedPrincipalId;
4188
+ if (options2?.localOnly) {
4189
+ await context.ready();
4190
+ const saved = await store.transaction((tx) => tx.get("mls-dev-device/identity"));
4191
+ if (!saved) return void 0;
4192
+ storedPrincipalId = parseMlsDevDeviceBinding(saved.binding).principal.id;
4193
+ }
4194
+ return (await initialized(storedPrincipalId)).coordinator(store).read();
4195
+ });
4196
+ }
4197
+ __name(read, "read");
5546
4198
  return Object.freeze({
5547
- read: /* @__PURE__ */ __name(() => context.run(async () => (await initialized()).coordinator(store).read()), "read"),
4199
+ read,
5548
4200
  enroll: /* @__PURE__ */ __name(() => run("enroll"), "enroll"),
5549
4201
  renew: /* @__PURE__ */ __name(() => run("renew"), "renew"),
5550
4202
  reconcile: /* @__PURE__ */ __name(() => run("reconcile"), "reconcile"),
5551
- ensureReady: /* @__PURE__ */ __name(() => run("ready"), "ensureReady")
4203
+ ensureReady: /* @__PURE__ */ __name(() => run("ready"), "ensureReady"),
4204
+ revoke: /* @__PURE__ */ __name(() => context.run(revoke), "revoke"),
4205
+ revokeOther: /* @__PURE__ */ __name((deviceId) => context.run(() => revokeOther(deviceId)), "revokeOther")
5552
4206
  });
5553
4207
  }
5554
4208
  __name(createMlsDevDeviceClient, "createMlsDevDeviceClient");
@@ -5559,20 +4213,20 @@ function fail3(code = "MLS_DEV_KEY_PACKAGE_STATE_INVALID", retryable = false) {
5559
4213
  throw new EndpointError(code, { code, retryable });
5560
4214
  }
5561
4215
  __name(fail3, "fail");
5562
- function record3(value, keys) {
4216
+ function record2(value, keys) {
5563
4217
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
5564
4218
  const fields = Object.getOwnPropertyDescriptors(value);
5565
4219
  if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value") || !fields[key].enumerable)) fail3();
5566
4220
  return Object.fromEntries(keys.map((key) => [key, fields[key].value]));
5567
4221
  }
5568
- __name(record3, "record");
4222
+ __name(record2, "record");
5569
4223
  function createMlsDevKeyPackageService(options, context, store, initialized, readPrepared) {
5570
4224
  const verifyCredential = options.verifyCredential;
5571
4225
  const fence = /* @__PURE__ */ __name(() => store.transaction(async () => {
5572
4226
  }), "fence");
5573
4227
  const network = /* @__PURE__ */ __name(() => context.transport?.keyPackages ?? fail3("MLS_DEV_KEY_PACKAGE_TRANSPORT_REQUIRED"), "network");
5574
4228
  function parseJournal(raw, binding, current, op) {
5575
- const value = record3(raw, ["version", "binding", "registration", "request", "receipt"]);
4229
+ const value = record2(raw, ["version", "binding", "registration", "request", "receipt"]);
5576
4230
  const registration = parseMlsDevEnrollmentResult(value.registration);
5577
4231
  if (value.version !== 1 || !same3(parseMlsDevDeviceBinding(value.binding), binding) || !same3(registration, current.registration)) fail3("MLS_DEV_KEY_PACKAGE_BINDING_MISMATCH");
5578
4232
  return {
@@ -5678,7 +4332,7 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
5678
4332
  equivalent: /* @__PURE__ */ __name((a, b) => same3({ ...a, claimantCredential: null }, { ...b, claimantCredential: null }), "equivalent"),
5679
4333
  forgetsRefusal: true,
5680
4334
  async make(tx, _binding, current) {
5681
- const raw = input ?? record3(
4335
+ const raw = input ?? record2(
5682
4336
  (await tx.get("mls-dev-key-package/claim/" + id2))?.request,
5683
4337
  ["version", "requestId", "claimantCredential", "recipientDeviceId", "preconditions"]
5684
4338
  );
@@ -5710,11 +4364,11 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
5710
4364
  __name(run, "run");
5711
4365
  return {
5712
4366
  publish(input) {
5713
- const id2 = parseMlsDevKeyPackageId(record3(input, ["keyPackageId"]).keyPackageId);
4367
+ const id2 = parseMlsDevKeyPackageId(record2(input, ["keyPackageId"]).keyPackageId);
5714
4368
  return run(async () => Object.freeze({ status: "published", receipt: await exchange(id2, true, publication(id2)) ?? fail3() }));
5715
4369
  },
5716
4370
  claim(input) {
5717
- const data = record3(input, ["requestId", "recipientDeviceId", "preconditions"]);
4371
+ const data = record2(input, ["requestId", "recipientDeviceId", "preconditions"]);
5718
4372
  const frozen = Object.freeze({
5719
4373
  requestId: parseMlsDevKeyPackageId(data.requestId),
5720
4374
  recipientDeviceId: parseMlsDevKeyPackageId(data.recipientDeviceId),
@@ -5791,20 +4445,20 @@ function fail4(code = "MLS_DEV_KEY_PACKAGE_STATE_INVALID", retryable = false) {
5791
4445
  }
5792
4446
  __name(fail4, "fail");
5793
4447
  var same4 = /* @__PURE__ */ __name((a, b) => canonicalEndpointJson(a) === canonicalEndpointJson(b), "same");
5794
- function record4(value, keys) {
4448
+ function record3(value, keys) {
5795
4449
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
5796
4450
  const fields = Object.getOwnPropertyDescriptors(value);
5797
4451
  if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value") || !fields[key].enumerable)) fail4();
5798
4452
  return Object.fromEntries(keys.map((key) => [key, fields[key].value]));
5799
4453
  }
5800
- __name(record4, "record");
4454
+ __name(record3, "record");
5801
4455
  function boundPayload(command, payload) {
5802
4456
  const claims = parseMlsDevCredentialToken(command.credential).unsigned.claims;
5803
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");
5804
4458
  }
5805
4459
  __name(boundPayload, "boundPayload");
5806
4460
  function intent(value, binding, id2) {
5807
- const data = record4(value, ["version", "binding", "command", "payload"]);
4461
+ const data = record3(value, ["version", "binding", "command", "payload"]);
5808
4462
  const command = parseMlsDevKeyPackageCommand(data.command), payload = parseMlsDevKeyPackagePayload(data.payload);
5809
4463
  if (data.version !== 1 || !same4(parseMlsDevDeviceBinding(data.binding), binding) || command.keyPackageId !== id2) fail4();
5810
4464
  const device = parseMlsDevCredentialToken(command.credential).unsigned.claims.device;
@@ -5823,7 +4477,7 @@ async function captureMlsDevPreparedKeyPackage(tx, binding, id2, current) {
5823
4477
  const stored = intent(raw, binding, id2);
5824
4478
  await assertMlsDevKeyPackageUnconsumed(tx, id2, stored.payload.keyPackageRef);
5825
4479
  if (!same4(parseMlsDevCredentialToken(stored.command.credential).unsigned.claims.device, current.registration.device)) fail4("MLS_DEV_KEY_PACKAGE_BINDING_MISMATCH");
5826
- const state = record4(snapshot.protocolState, ["version", "binding", "keyPackageId", "wrappedState", "payload"]);
4480
+ const state = record3(snapshot.protocolState, ["version", "binding", "keyPackageId", "wrappedState", "payload"]);
5827
4481
  const preparation = parseMlsDevKeyPackagePreparation({ wrappedState: state.wrappedState, payload: state.payload });
5828
4482
  if (state.version !== 1 || !same4(parseMlsDevDeviceBinding(state.binding), binding) || state.keyPackageId !== id2 || !same4(preparation.payload, stored.payload)) fail4();
5829
4483
  return {
@@ -5890,7 +4544,7 @@ function createMlsDevKeyPackageClient(options, context) {
5890
4544
  if (historical) {
5891
4545
  const consumed = await tx.get(`mls-dev-key-package/consumed/${id2}`);
5892
4546
  if (consumed !== void 0) {
5893
- const data = record4(consumed, ["version", "binding", "payload", "conversationId", "groupId", "controlSequence"]);
4547
+ const data = record3(consumed, ["version", "binding", "payload", "conversationId", "groupId", "controlSequence"]);
5894
4548
  const payload = parseMlsDevKeyPackagePayload(data.payload);
5895
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();
5896
4550
  return payload;
@@ -6048,7 +4702,7 @@ function createMlsDevKeyPackageClient(options, context) {
6048
4702
  return {
6049
4703
  ...service,
6050
4704
  prepare(input) {
6051
- const data = record4(input, ["keyPackageId"]);
4705
+ const data = record3(input, ["keyPackageId"]);
6052
4706
  return run(parseMlsDevKeyPackageId(data.keyPackageId), true).then((result) => result ?? fail4());
6053
4707
  },
6054
4708
  read: /* @__PURE__ */ __name((id2) => run(id2, false), "read"),
@@ -6298,6 +4952,27 @@ function mlsDevReplyQuote(value, maximum) {
6298
4952
  return quoted ? [...quoted.trim()].slice(0, maximum).join("") || void 0 : void 0;
6299
4953
  }
6300
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");
6301
4976
  function projectMlsDevTextMessage(value, account) {
6302
4977
  const message = value.message;
6303
4978
  if (message.kind === "item") return projectMlsDevItemMessage({ ...value, message }, account);
@@ -6413,7 +5088,7 @@ var MLS_ERASED_SENDER_ID = "erased_user";
6413
5088
  var checked = /* @__PURE__ */ new Map();
6414
5089
  var checkKey = /* @__PURE__ */ __name((environmentId, alias, opening) => JSON.stringify([environmentId, alias, opening.type, opening.accountId, opening.salt]), "checkKey");
6415
5090
  var hexBytes = /* @__PURE__ */ __name((hex) => Uint8Array.from(hex.match(/../g) ?? [], (pair) => Number.parseInt(pair, 16)), "hexBytes");
6416
- async function opensMlsAlias(environmentId, alias, opening, hash3) {
5091
+ async function opensMlsAlias(environmentId, alias, opening, hash) {
6417
5092
  let value;
6418
5093
  try {
6419
5094
  value = parseMlsPrincipalOpening(opening);
@@ -6424,7 +5099,7 @@ async function opensMlsAlias(environmentId, alias, opening, hash3) {
6424
5099
  const key = checkKey(environmentId, alias, value);
6425
5100
  let opens = checked.get(key);
6426
5101
  if (opens === void 0) {
6427
- const digest = await hash3(encodeMlsPrincipalAliasInput(environmentId, value));
5102
+ const digest = await hash(encodeMlsPrincipalAliasInput(environmentId, value));
6428
5103
  opens = /^[0-9a-f]{64}$/.test(digest) && MLS_PRINCIPAL_ALIAS_PREFIX + encodeCanonicalMlsBase64Url(hexBytes(digest)) === alias;
6429
5104
  if (checked.size >= 4096) checked.clear();
6430
5105
  checked.set(key, opens);
@@ -6442,8 +5117,8 @@ function openings(room) {
6442
5117
  }
6443
5118
  }
6444
5119
  __name(openings, "openings");
6445
- async function checkMlsRoomOpenings(room, environmentId, hash3) {
6446
- 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);
6447
5122
  }
6448
5123
  __name(checkMlsRoomOpenings, "checkMlsRoomOpenings");
6449
5124
  function mlsRoomAccounts(room, environmentId) {
@@ -6459,8 +5134,8 @@ function mlsAccountOf(accounts, principal) {
6459
5134
  }
6460
5135
  __name(mlsAccountOf, "mlsAccountOf");
6461
5136
  function mlsAliasOf(accounts, accountId, type, admitted) {
6462
- const matches2 = [...accounts].filter(([alias, identity]) => identity.accountId === accountId && identity.type === type && (!admitted || admitted.includes(alias))).map(([alias]) => alias);
6463
- 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;
6464
5139
  }
6465
5140
  __name(mlsAliasOf, "mlsAliasOf");
6466
5141
  async function primeMlsRoomIdentities(input) {
@@ -6507,6 +5182,11 @@ async function roomRemoval(tx, conversationId) {
6507
5182
  }
6508
5183
  }
6509
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");
6510
5190
  async function assertRoomOpen(tx, conversationId, roster) {
6511
5191
  const removal = await roomRemoval(tx, conversationId);
6512
5192
  if (removal && removal.status !== "pending") fail6("MLS_DEV_GROUP_CLOSED");
@@ -6523,19 +5203,19 @@ function id(value) {
6523
5203
  return value;
6524
5204
  }
6525
5205
  __name(id, "id");
6526
- function record5(value, required, optional = []) {
5206
+ function record4(value, required, optional = []) {
6527
5207
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail6();
6528
5208
  const descriptors = Object.getOwnPropertyDescriptors(value), fields = Reflect.ownKeys(descriptors);
6529
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();
6530
5210
  return Object.fromEntries(fields.map((key) => [key, descriptors[key].value]));
6531
5211
  }
6532
- __name(record5, "record");
5212
+ __name(record4, "record");
6533
5213
  function envelope(value, binding, conversationId) {
6534
5214
  if (value === null) return null;
6535
- 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"]);
6536
5216
  if (data.version !== 1 || !same6(parseMlsDevDeviceBinding(data.binding), binding) || data.conversationId !== conversationId || typeof data.removed !== "boolean") fail6();
6537
5217
  const head = data.head === null ? null : parseMlsDevGroupHead(data.head), roster = parseMlsDevGroupRoster(data.roster);
6538
- const pending = data.pending === null ? null : record5(data.pending, ["operationId", "control"]);
5218
+ const pending = data.pending === null ? null : record4(data.pending, ["operationId", "control"]);
6539
5219
  const control = pending ? parseMlsDevGroupControl(pending.control) : null;
6540
5220
  parseMlsDevGroupPreparation({ wrappedState: data.wrappedState, payload: control ? { kind: "control", control } : { kind: "applied", head: head ?? fail6(), roster, removed: data.removed, consumedKeyPackageId: null } });
6541
5221
  if (control && (control.conversationId !== conversationId || control.groupId !== data.groupId || !same6(control.expectedHead, head))) fail6();
@@ -6557,7 +5237,7 @@ function envelope(value, binding, conversationId) {
6557
5237
  __name(envelope, "envelope");
6558
5238
  function authority(value, conversationId) {
6559
5239
  if (value === void 0) return void 0;
6560
- const data = record5(value, ["version", "marker", "membershipRevision", "admissionId", "roster", "terminal", "observedSequence"]);
5240
+ const data = record4(value, ["version", "marker", "membershipRevision", "admissionId", "roster", "terminal", "observedSequence"]);
6561
5241
  const parsed = parseMlsDevGroupQueryResult({
6562
5242
  version: data.version,
6563
5243
  conversationId,
@@ -6583,7 +5263,7 @@ function authority(value, conversationId) {
6583
5263
  __name(authority, "authority");
6584
5264
  function recoveryFailure(value, conversationId) {
6585
5265
  if (value === void 0) return void 0;
6586
- const data = record5(value, ["version", "conversationId", "code", "lastAppliedSequence", "observedAtMs"]);
5266
+ const data = record4(value, ["version", "conversationId", "code", "lastAppliedSequence", "observedAtMs"]);
6587
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();
6588
5268
  return Object.freeze({
6589
5269
  version: 1,
@@ -7186,7 +5866,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7186
5866
  __name(catchUpFeed, "catchUpFeed");
7187
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");
7188
5868
  function itemInput(attachment, caption) {
7189
- 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"]);
7190
5870
  if (data.processingStatus !== void 0 && data.processingStatus !== null && data.processingStatus !== "ready") fail6("MLS_DEV_ITEM_PROCESSING", true);
7191
5871
  const raw = {
7192
5872
  type: data.kind,
@@ -7218,7 +5898,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7218
5898
  }
7219
5899
  __name(validItem, "validItem");
7220
5900
  function textInput(value) {
7221
- 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"]);
7222
5902
  const attachments = data.attachments ?? [];
7223
5903
  if (!Array.isArray(attachments) || attachments.length > 1) fail6(Array.isArray(attachments) ? "MLS_DEV_ONE_ITEM_PER_MESSAGE" : "MLS_DEV_TEXT_ONLY");
7224
5904
  const item = attachments.length ? itemInput(attachments[0], data.text) : void 0;
@@ -7238,11 +5918,18 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7238
5918
  await primeIdentities(conversationId, init);
7239
5919
  return store.transaction(async (tx) => {
7240
5920
  const room = await tx.get(`conversation/${conversationId}`);
7241
- if (room?.mlsAdmissionHold !== true) return Object.freeze({ held: false, waitingPrincipalIds: Object.freeze([]) });
7242
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
+ }
7243
5926
  const accounts = mlsRoomAccounts(room, init.binding.environmentId);
7244
5927
  const admitted = new Set((group && !group.removed ? group.roster : []).map((entry) => mlsAccountOf(accounts, entry.device.principal)));
7245
- const waiting = [...Array.isArray(room.memberIds) ? room.memberIds : [], ...typeof room.mlsInviteeId === "string" ? [room.mlsInviteeId] : []].filter((memberId) => typeof memberId === "string" && !admitted.has(memberId));
5928
+ const waiting = [.../* @__PURE__ */ new Set([
5929
+ ...Array.isArray(room.memberIds) ? room.memberIds : [],
5930
+ ...typeof room.mlsInviteeId === "string" ? [room.mlsInviteeId] : [],
5931
+ ...Array.isArray(room.mlsPendingFounderIds) ? room.mlsPendingFounderIds : []
5932
+ ])].filter((memberId) => typeof memberId === "string" && !admitted.has(memberId));
7246
5933
  return Object.freeze({ held: waiting.length > 0, waitingPrincipalIds: Object.freeze(waiting) });
7247
5934
  });
7248
5935
  }
@@ -7417,14 +6104,14 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7417
6104
  }
7418
6105
  __name(aliasedInteraction, "aliasedInteraction");
7419
6106
  function interactionInput(value) {
7420
- const data = record5(value, ["conversationId"], ["messageId", "operationId", "guardId", "request", "response", "withdraw"]);
6107
+ const data = record4(value, ["conversationId"], ["messageId", "operationId", "guardId", "request", "response", "withdraw"]);
7421
6108
  const conversationId = id(data.conversationId), messageId = data.messageId === void 0 ? void 0 : id(data.messageId);
7422
6109
  const roles = ["request", "response", "withdraw"].filter((role) => data[role] !== void 0);
7423
6110
  if (roles.length !== 1) fail6("INVALID_MLS_DEV_INTERACTION_INPUT");
7424
6111
  try {
7425
6112
  if (data.request !== void 0) {
7426
6113
  if (principal.type !== "ai_agent") fail6("MLS_DEV_INTERACTION_REQUESTER_INVALID");
7427
- const input = record5(
6114
+ const input = record4(
7428
6115
  data.request,
7429
6116
  ["requestId", "kind", "title", "responderIds", "expiresAtMs"],
7430
6117
  ["details", "options", "multiple", "risk", "allowSession", "scopeKey"]
@@ -7440,7 +6127,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7440
6127
  }
7441
6128
  if (data.response !== void 0) {
7442
6129
  if (principal.type !== "human") fail6("MLS_DEV_INTERACTION_RESPONDER_INVALID");
7443
- const input = record5(data.response, ["requestId", "agentId"], ["answer", "secret"]);
6130
+ const input = record4(data.response, ["requestId", "agentId"], ["answer", "secret"]);
7444
6131
  const binding = { role: "response", requestId: input.requestId, agentId: input.agentId };
7445
6132
  if (Object.hasOwn(input, "secret") === Object.hasOwn(input, "answer")) fail6("INVALID_MLS_DEV_INTERACTION_INPUT");
7446
6133
  if (Object.hasOwn(input, "answer")) {
@@ -7918,12 +6605,13 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7918
6605
  async function commitHeldRemoval(conversationId) {
7919
6606
  const init = await initialized();
7920
6607
  const target = await store.transaction(async (tx) => {
7921
- const removal = await roomRemoval(tx, conversationId);
7922
- 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;
7923
6610
  const current = await capture(tx, await readPreparationSnapshot(tx, scope4(conversationId)), init, conversationId), group = current.envelope;
7924
- const held = new Set(removal.pending.flatMap((entry) => entry.deviceIds));
7925
- if (!group?.head || group.removed || group.pending || current.authority?.terminal || held.has(current.device.registration.device.deviceId)) return void 0;
7926
- 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];
7927
6615
  return removedDeviceId ? {
7928
6616
  deviceId: current.device.registration.device.deviceId,
7929
6617
  removedDeviceId,
@@ -8017,7 +6705,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8017
6705
  __name(run, "run");
8018
6706
  const client = {
8019
6707
  createRoom(input) {
8020
- const data = record5(input, ["conversationId", "name", "memberIds"], ["operationId", "protocol"]);
6708
+ const data = record4(input, ["conversationId", "name", "memberIds"], ["operationId", "protocol"]);
8021
6709
  const conversationId = id(data.conversationId), operationId = id(data.operationId ?? context.ids());
8022
6710
  if (typeof data.name !== "string" || data.name.trim().length > 100) fail6("INVALID_MLS_DEV_GROUP_INPUT");
8023
6711
  if (data.protocol !== void 0 && !isMlsDevTextProtocol(data.protocol)) fail6("MLS_DEV_TEXT_PROTOCOL_UNSUPPORTED");
@@ -8071,15 +6759,15 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8071
6759
  });
8072
6760
  },
8073
6761
  create: /* @__PURE__ */ __name((input) => {
8074
- const data = record5(input, ["conversationId"], ["operationId"]);
6762
+ const data = record4(input, ["conversationId"], ["operationId"]);
8075
6763
  return run(() => serializeInboundPreparation(store, id(data.conversationId), () => submit("create", data)));
8076
6764
  }, "create"),
8077
6765
  add: /* @__PURE__ */ __name((input) => {
8078
- const data = record5(input, ["conversationId", "claimRequestId"], ["operationId"]);
6766
+ const data = record4(input, ["conversationId", "claimRequestId"], ["operationId"]);
8079
6767
  return run(() => serializeInboundPreparation(store, id(data.conversationId), () => submit("add", data)));
8080
6768
  }, "add"),
8081
6769
  remove: /* @__PURE__ */ __name((input) => {
8082
- const data = record5(input, ["conversationId", "removedDeviceId"], ["operationId"]);
6770
+ const data = record4(input, ["conversationId", "removedDeviceId"], ["operationId"]);
8083
6771
  return run(() => serializeInboundPreparation(store, id(data.conversationId), () => submit("remove", data)));
8084
6772
  }, "remove"),
8085
6773
  refresh: /* @__PURE__ */ __name((value) => run(() => serializeInboundPreparation(store, id(value), () => refresh(id(value)))), "refresh"),
@@ -8094,13 +6782,13 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8094
6782
  }, false), "heldCurrent"),
8095
6783
  read: /* @__PURE__ */ __name((value) => run(() => read(id(value)), false), "read"),
8096
6784
  readVerifiedMessageBinding: /* @__PURE__ */ __name((input) => {
8097
- const data = record5(input, ["conversationId", "messageId"]);
6785
+ const data = record4(input, ["conversationId", "messageId"]);
8098
6786
  const conversationId = id(data.conversationId), messageId = id(data.messageId);
8099
6787
  return run(() => readVerifiedMessageBinding(conversationId, messageId), false);
8100
6788
  }, "readVerifiedMessageBinding"),
8101
6789
  readCurrentAudience: /* @__PURE__ */ __name((value) => run(() => serializeInboundPreparation(store, id(value), () => readCurrentAudience(id(value)))), "readCurrentAudience"),
8102
6790
  assertCurrentAudience: /* @__PURE__ */ __name((value) => {
8103
- 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"]);
8104
6792
  const expected = JSON.parse(canonicalEndpointJson(data)), conversationId = id(expected.conversationId);
8105
6793
  if (!Number.isSafeInteger(expected.feedOrdinal) || expected.feedOrdinal < 1) fail6("MLS_DEV_AUDIENCE_CHANGED");
8106
6794
  return run(() => serializeInboundPreparation(store, conversationId, async () => {
@@ -8122,12 +6810,12 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8122
6810
  return run(() => serializeInboundPreparation(store, input.conversationId, () => sendInteraction(input, operationId, guardId)));
8123
6811
  }, "sendInteraction"),
8124
6812
  readInteraction: /* @__PURE__ */ __name(async (value) => {
8125
- const data = record5(value, ["conversationId", "agentId", "requestId"]);
6813
+ const data = record4(value, ["conversationId", "agentId", "requestId"]);
8126
6814
  const conversationId = id(data.conversationId), agentId = id(data.agentId), requestId = id(data.requestId);
8127
6815
  return run(() => serializeInboundPreparation(store, conversationId, () => readInteraction(conversationId, agentId, requestId)), false);
8128
6816
  }, "readInteraction"),
8129
6817
  openSecretAnswer: /* @__PURE__ */ __name(async (value) => {
8130
- 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);
8131
6819
  return run(() => serializeInboundPreparation(store, conversationId, () => openSecretAnswer(conversationId, requestId)));
8132
6820
  }, "openSecretAnswer")
8133
6821
  };
@@ -8135,13 +6823,14 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8135
6823
  client,
8136
6824
  async commitHeldRemoval(conversationId) {
8137
6825
  const cid = id(conversationId);
8138
- 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;
8139
6828
  return run(() => serializeInboundPreparation(store, cid, () => commitHeldRemoval(cid)));
8140
6829
  },
8141
6830
  /** Forwards an encrypted text by re-encryption: this device's own decrypted copy, sent as new text to another
8142
6831
  * encrypted room (mls-payload-v3.md §4). Items and cards are not forwarded yet. */
8143
6832
  forwardText(value, operationId, guardId) {
8144
- const data = record5(value, ["sourceConversationId", "messageId", "targetConversationId"]);
6833
+ const data = record4(value, ["sourceConversationId", "messageId", "targetConversationId"]);
8145
6834
  const source = id(data.sourceConversationId), messageId = id(data.messageId), target = id(data.targetConversationId);
8146
6835
  const selectedId = id(operationId), selectedGuard = guardId === void 0 ? void 0 : id(guardId);
8147
6836
  return run(async () => {
@@ -8298,7 +6987,7 @@ function createEndpoint(options) {
8298
6987
  const { store, transport } = options;
8299
6988
  const clock = options.clock ?? Date.now;
8300
6989
  const ids = options.ids ?? randomId;
8301
- const hash3 = options.hash ?? sha256;
6990
+ const hash = options.hash ?? sha256;
8302
6991
  const hashBytes = options.hashBytes ?? sha256Bytes;
8303
6992
  const owner = ids();
8304
6993
  const leaseMs = Math.max(5e3, options.leaseMs ?? 6e4);
@@ -8411,7 +7100,7 @@ function createEndpoint(options) {
8411
7100
  const mlsDevDevice = options.mlsDevDevice ? createMlsDevDeviceClient(options.mlsDevDevice, mlsDevContext) : void 0;
8412
7101
  const mlsDevKeyPackages = options.mlsDevDevice?.prepareProtectedKeyPackage ? createMlsDevKeyPackageClient(options.mlsDevDevice, mlsDevContext) : void 0;
8413
7102
  const mlsDevGroups = options.mlsDevDevice?.prepareProtectedGroup ? createMlsDevGroups(options.mlsDevDevice, mlsDevContext, mlsDevKeyPackages, {
8414
- hash: hash3,
7103
+ hash,
8415
7104
  hashBytes,
8416
7105
  readRoom,
8417
7106
  resolveProtocol: /* @__PURE__ */ __name((conversationId) => resolveMlsRoomMode(conversationId).then((value) => value.protocol), "resolveProtocol"),
@@ -8440,7 +7129,7 @@ function createEndpoint(options) {
8440
7129
  scheduleAdmission(conversationId, 0);
8441
7130
  }, "controlSettled"),
8442
7131
  // Whatever fetched them (a hint, the reconnect sweep, a send, recovery), the room's local reads show them now.
8443
- 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"),
8444
7133
  streamLive: /* @__PURE__ */ __name(() => events.isConnected(), "streamLive")
8445
7134
  }) : void 0;
8446
7135
  async function inMlsEnvironment() {
@@ -8571,13 +7260,14 @@ function createEndpoint(options) {
8571
7260
  if (!mlsDevGroups || await mlsDevGroups.client.read(conversationId)) return;
8572
7261
  if (!isMlsDevTextProtocol((await resolveMlsRoomMode(conversationId, false)).protocol) || await readServerRoomMode() !== "default") return;
8573
7262
  const { installationId } = await initialization;
8574
- 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]));
8575
7264
  await mlsDevGroups.client.create({ conversationId, operationId: `mls-birth-${digest.slice(0, 48)}` }).catch((error) => {
8576
7265
  if (!(error instanceof EndpointError) || ["ENDPOINT_CLOSED", "PREPARATION_INSTALLATION_FENCED"].includes(error.code)) throw error;
8577
7266
  });
8578
7267
  }
8579
7268
  __name(createBornGroup, "createBornGroup");
8580
7269
  const admissionRuns = /* @__PURE__ */ new Map(), admitting = /* @__PURE__ */ new Set(), admissionDelays = /* @__PURE__ */ new Map();
7270
+ const announcementsSeen = /* @__PURE__ */ new Map();
8581
7271
  const admissionTimers = /* @__PURE__ */ new Map();
8582
7272
  const ADMISSION_IDLE_MS = 10 * 6e4, CLAIM_JOURNAL_KEEP_MS = 60 * 6e4;
8583
7273
  const admissionUnfinished = /* @__PURE__ */ new Set();
@@ -8609,9 +7299,11 @@ function createEndpoint(options) {
8609
7299
  const missing = [...local?.memberIds ?? [], ...typeof invitee === "string" ? [invitee] : []].some((memberId) => !inRoster.has(memberId)) || admissionUnfinished.has(conversationId);
8610
7300
  if (!missing) admissionDelays.delete(conversationId);
8611
7301
  const delay = admissionDelays.get(conversationId) ?? admissionRetry.initial;
8612
- 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);
8613
7304
  if (clock() < due) return missing ? scheduleAdmission(conversationId, due - clock()) : void 0;
8614
7305
  admitting.add(conversationId);
7306
+ announcementsSeen.set(conversationId, announced);
8615
7307
  try {
8616
7308
  admissionRuns.set(conversationId, clock());
8617
7309
  await mlsDevKeyPackages.pruneClaims(clock() - CLAIM_JOURNAL_KEEP_MS);
@@ -8988,7 +7680,7 @@ function createEndpoint(options) {
8988
7680
  observe: events.observe,
8989
7681
  isProtocolReady: /* @__PURE__ */ __name(async (id2) => isMlsDevTextProtocol((await resolveMlsRoomMode(id2)).protocol) ? await mlsDevGroups?.isTextReady(id2) ?? false : void 0, "isProtocolReady")
8990
7682
  });
8991
- 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 });
8992
7684
  const handoffs = createInboundHandoffs({
8993
7685
  store,
8994
7686
  owner,
@@ -9000,16 +7692,16 @@ function createEndpoint(options) {
9000
7692
  assertCurrent: assertCurrentInstallation,
9001
7693
  assertReady: /* @__PURE__ */ __name(async (records) => {
9002
7694
  if (options.admitRecoveredMessages) {
9003
- 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);
9004
7696
  }
9005
7697
  }, "assertReady")
9006
7698
  });
9007
- 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 });
9008
7700
  const messageGroups = createMessageGroupCoordinator({
9009
7701
  store,
9010
7702
  endpoint: /* @__PURE__ */ __name(() => endpoint, "endpoint"),
9011
7703
  ready: /* @__PURE__ */ __name(() => initialization, "ready"),
9012
- hash: hash3,
7704
+ hash,
9013
7705
  clock,
9014
7706
  wake: /* @__PURE__ */ __name(() => scheduleRetry(0), "wake"),
9015
7707
  preparationGeneration: /* @__PURE__ */ __name(() => installationGeneration, "preparationGeneration"),
@@ -9157,7 +7849,7 @@ function createEndpoint(options) {
9157
7849
  const index = group?.children.findIndex((child) => child.operationId === operationId) ?? -1;
9158
7850
  const previous = group && index > 0 ? await tx.get(key(group.children[index - 1].operationId)) : void 0;
9159
7851
  if (previous?.status === "rejected" || previous?.status === "succeeded" && !messageGroupChildSucceeded(previous)) {
9160
- const rejected2 = { ...current, status: "rejected", updatedAt: now(), receipt: {
7852
+ const rejected = { ...current, status: "rejected", updatedAt: now(), receipt: {
9161
7853
  protocol: CANON_ENDPOINT_PROTOCOL,
9162
7854
  operationId,
9163
7855
  installationId: current.installationId,
@@ -9167,12 +7859,12 @@ function createEndpoint(options) {
9167
7859
  updatedAt: now(),
9168
7860
  error: { code: "DEPENDENCY_REJECTED", message: "An earlier part of this reply could not be delivered", retryable: false }
9169
7861
  } };
9170
- delete rejected2.request;
9171
- delete rejected2.preparedContent;
9172
- await tx.put(key(operationId), rejected2);
9173
- await removeOperationOutbox(tx, rejected2);
9174
- await settleOperationOrder(tx, rejected2);
9175
- 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 };
9176
7868
  }
9177
7869
  if (!messageGroups.allowed(current.messageGroupId) || !group || index < 0 || group.cancelled || index > 0 && !messageGroupChildSucceeded(previous)) return { operation: current, acquired: false };
9178
7870
  }
@@ -9299,8 +7991,8 @@ function createEndpoint(options) {
9299
7991
  await persistReceipt(operation, receipt);
9300
7992
  return receipt;
9301
7993
  } catch (error) {
9302
- const failure2 = safeError(error);
9303
- if (reconcileOnly && failure2.retryable) {
7994
+ const failure = safeError(error);
7995
+ if (reconcileOnly && failure.retryable) {
9304
7996
  await store.transaction(async (tx) => {
9305
7997
  const current = await tx.get(key(operationId));
9306
7998
  if (current?.lease?.owner === owner) {
@@ -9309,17 +8001,17 @@ function createEndpoint(options) {
9309
8001
  await tx.put(key(operationId), next);
9310
8002
  }
9311
8003
  });
9312
- return deferredReceipt(operation, failure2);
8004
+ return deferredReceipt(operation, failure);
9313
8005
  }
9314
8006
  const updated = await store.transaction(async (tx) => {
9315
8007
  const current = await tx.get(key(operationId));
9316
- if (!current) throw failure2;
8008
+ if (!current) throw failure;
9317
8009
  if (current.lease?.owner !== owner || terminal(current.status)) return current;
9318
- const lookupUncertain = resolvingReceipt && !failure2.retryable;
9319
- const generationUncertain = current.attempted && failure2.code === "PREPARATION_INSTALLATION_FENCED";
9320
- const contentModeUncertain = current.attempted && checkingContentMode && !failure2.retryable;
9321
- const guardUncertain = current.attempted && checkingDeliveryGuard && !failure2.retryable;
9322
- 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))) };
9323
8015
  delete next.lease;
9324
8016
  if (current.sensitive) {
9325
8017
  delete next.request;
@@ -9329,14 +8021,14 @@ function createEndpoint(options) {
9329
8021
  next.receipt = { protocol: CANON_ENDPOINT_PROTOCOL, operationId: current.operationId, installationId: current.installationId, action: current.action, status: "uncertain", error: {
9330
8022
  code: generationUncertain ? "PREPARATION_INSTALLATION_FENCED" : guardUncertain ? "DELIVERY_GUARD_RECONCILIATION_REQUIRED" : contentModeUncertain ? "CONTENT_MODE_RECONCILIATION_REQUIRED" : "RECEIPT_LOOKUP_REJECTED",
9331
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",
9332
- status: failure2.status,
8024
+ status: failure.status,
9333
8025
  retryable: false
9334
8026
  }, createdAt: current.createdAt, updatedAt: now() };
9335
8027
  delete next.request;
9336
8028
  delete next.preparedContent;
9337
8029
  delete next.nextAttemptAt;
9338
- } else if (!failure2.retryable && current.status !== "uncertain") {
9339
- 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() };
9340
8032
  delete next.nextAttemptAt;
9341
8033
  }
9342
8034
  await tx.put(key(operationId), next);
@@ -9357,8 +8049,8 @@ function createEndpoint(options) {
9357
8049
  if (updated.messageGroupId && terminal(updated.status)) await messageGroups.settled(updated);
9358
8050
  if (terminal(updated.status)) scheduleRetry(0);
9359
8051
  notify(updated);
9360
- if (failure2.retryable && updated.status !== "uncertain" && !closed) scheduleRetry(Math.max(1e3, (updated.nextAttemptAt ?? clock()) - clock()));
9361
- 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);
9362
8054
  } finally {
9363
8055
  clearInterval(heartbeat);
9364
8056
  }
@@ -9409,7 +8101,7 @@ function createEndpoint(options) {
9409
8101
  }
9410
8102
  const authored = JSON.parse(canonicalEndpointJson(intent2));
9411
8103
  const sensitive = executeOptions.sensitive === true || endpointOperationRequestRetention(authored.action, authored.body, authored.envelope) === "memory";
9412
- 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 } : {} }));
9413
8105
  const previous = await store.transaction(async (tx) => {
9414
8106
  await assertCurrentInstallation(tx);
9415
8107
  return tx.get(key(operationId));
@@ -9461,8 +8153,8 @@ function createEndpoint(options) {
9461
8153
  if (!sensitive && !previous) await reservePreparedIdentity(store, { operationId, action: authored.action, fingerprint: inputFingerprint, installationGeneration });
9462
8154
  const normalized = await prepareEndpointIntent({ intent: authored, previousPreconditions: previous?.preconditions, query: /* @__PURE__ */ __name((name, parameters) => endpoint.query(name, parameters), "query") });
9463
8155
  const routedBody = { ...normalized.body, ...normalized.envelope };
9464
- const sensitiveFingerprint = sensitive ? await hash3(canonicalEndpointJson({ ...normalized, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} })) : void 0;
9465
- 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 } : {} }));
9466
8158
  assertOpen();
9467
8159
  const includePreparedContent = !sensitive && negotiatedCapabilities?.preparedContentVersion === 1;
9468
8160
  const request = {
@@ -9514,6 +8206,13 @@ function createEndpoint(options) {
9514
8206
  createdAt: timestamp,
9515
8207
  updatedAt: timestamp
9516
8208
  };
8209
+ if (normalized.action === "start_conversation") {
8210
+ const opener = normalized.body.opener;
8211
+ if (opener?.withheld && typeof opener.messageId === "string") {
8212
+ const pending = await tx.get(`${openerPrefix()}/${encodeURIComponent(opener.messageId)}`);
8213
+ if (pending?.start?.operationId === operationId) created.openerFingerprint = pending.openerFingerprint;
8214
+ }
8215
+ }
9517
8216
  return commitOperationRecord(tx, created);
9518
8217
  }, "commitOperation");
9519
8218
  const operation = sensitive ? await store.transaction(async (tx) => {
@@ -9607,7 +8306,7 @@ function createEndpoint(options) {
9607
8306
  return store.transaction((tx) => tx.get(key(operationId)));
9608
8307
  },
9609
8308
  waitForOperation(operationId, waitOptions = {}) {
9610
- return new Promise((resolve6, reject) => {
8309
+ return new Promise((resolve3, reject) => {
9611
8310
  let finished = false;
9612
8311
  let checking = false;
9613
8312
  let poll;
@@ -9621,7 +8320,7 @@ function createEndpoint(options) {
9621
8320
  controller.signal.removeEventListener("abort", abort);
9622
8321
  waitOptions.signal?.removeEventListener("abort", abort);
9623
8322
  if (error) reject(error);
9624
- else resolve6(receipt);
8323
+ else resolve3(receipt);
9625
8324
  }, "finish");
9626
8325
  const abort = /* @__PURE__ */ __name(() => finish(void 0, new EndpointError("Delivery remains in the endpoint journal for reconciliation", {
9627
8326
  code: "OPERATION_PENDING",
@@ -9706,6 +8405,7 @@ function createEndpoint(options) {
9706
8405
  }
9707
8406
  const result = await transport.query(name, normalized, controller.signal);
9708
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));
9709
8409
  return result;
9710
8410
  }).finally(() => {
9711
8411
  reads.delete(queryKey);
@@ -9740,7 +8440,7 @@ function createEndpoint(options) {
9740
8440
  observeEvents: events.observe,
9741
8441
  createMediaPipeline(bytes, mediaOptions) {
9742
8442
  assertOpen();
9743
- 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 });
9744
8444
  mediaPipelines2.add(pipeline3);
9745
8445
  return pipeline3;
9746
8446
  },
@@ -9766,19 +8466,27 @@ function createEndpoint(options) {
9766
8466
  },
9767
8467
  async startWithheldOpener(input) {
9768
8468
  assertOpen();
8469
+ input = copyJson(input);
9769
8470
  await initialization;
9770
8471
  if (typeof input.messageId !== "string" || !input.messageId || typeof input.text !== "string")
9771
8472
  throw new EndpointError("Invalid deferred opening line", { status: 400, code: "INVALID_DEFERRED_OPENER", retryable: false });
9772
- const digest = await hash3(canonicalEndpointJson(["canon.withheld-opener.v1", input.messageId]));
8473
+ const digest = await hash(canonicalEndpointJson(["canon.withheld-opener.v1", input.messageId]));
9773
8474
  const operationId = `start-opener-${digest.slice(0, 48)}`;
9774
8475
  const body = {
9775
8476
  targetUserId: input.targetUserId,
9776
8477
  opener: { withheld: true, messageId: input.messageId },
9777
8478
  ...input.selection ? { selection: copyJson(input.selection) } : {}
9778
8479
  };
8480
+ const openerFingerprint = await hash(canonicalEndpointJson([body, input.text]));
9779
8481
  const recordKey = `${openerPrefix()}/${encodeURIComponent(input.messageId)}`;
9780
8482
  await store.transaction(async (tx) => {
9781
8483
  await assertCurrentInstallation(tx);
8484
+ const operation = await tx.get(key(operationId));
8485
+ if (operation) {
8486
+ if (operation.openerFingerprint !== openerFingerprint)
8487
+ throw new EndpointError("Opening line ID was already used for different content", { status: 409, code: "OPERATION_ID_COLLISION", retryable: false, operationId });
8488
+ return;
8489
+ }
9782
8490
  const existing = await tx.get(recordKey);
9783
8491
  if (existing && (existing.text !== input.text || canonicalEndpointJson(existing.start?.body) !== canonicalEndpointJson(body)))
9784
8492
  throw new EndpointError("Opening line ID was already used for different content", { status: 409, code: "OPERATION_ID_COLLISION", retryable: false, operationId });
@@ -9787,6 +8495,7 @@ function createEndpoint(options) {
9787
8495
  messageId: input.messageId,
9788
8496
  text: input.text,
9789
8497
  requestId: null,
8498
+ openerFingerprint,
9790
8499
  operationId: `send-opener-${digest.slice(0, 48)}`,
9791
8500
  start: { operationId, body }
9792
8501
  });
@@ -9905,15 +8614,15 @@ function createEndpoint(options) {
9905
8614
  if (!eventIds.length) return false;
9906
8615
  if (options.admitRecoveredMessages) {
9907
8616
  const records = await Promise.all(eventIds.map((id2) => endpoint.getInbound(id2)));
9908
- 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);
9909
8618
  }
9910
8619
  return store.transaction(async (tx) => {
9911
8620
  if (await tx.get("migration/active")) throw new EndpointError("Migration is incomplete; native execution is gated", { code: "MIGRATION_IN_PROGRESS", retryable: false });
9912
8621
  const records = await Promise.all(eventIds.map((id2) => tx.get(`inbound/${id2}`)));
9913
8622
  let projection = false;
9914
- 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;
9915
8624
  if (projection) return false;
9916
- 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;
9917
8626
  for (let i = 0; i < records.length; i++) {
9918
8627
  const next = { ...records[i], state: "uncertain", updatedAt: now() };
9919
8628
  delete next.lease;
@@ -10068,7 +8777,7 @@ function createEndpoint(options) {
10068
8777
  return endpoint;
10069
8778
  }
10070
8779
  __name(createEndpoint, "createEndpoint");
10071
- async function matchingRecords(tx, prefix3, options, matches2) {
8780
+ async function matchingRecords(tx, prefix3, options, matches) {
10072
8781
  const limit = Math.max(1, Math.min(options.limit ?? 100, 1e3));
10073
8782
  const result = [];
10074
8783
  let after = options.after ? `${prefix3}${options.after}` : void 0;
@@ -10076,7 +8785,7 @@ async function matchingRecords(tx, prefix3, options, matches2) {
10076
8785
  const batch = await tx.scan(prefix3, { after, limit: 100 });
10077
8786
  for (const entry of batch) {
10078
8787
  after = entry.key;
10079
- if (matches2(entry.value)) result.push(entry.value);
8788
+ if (matches(entry.value)) result.push(entry.value);
10080
8789
  if (result.length === limit) return result;
10081
8790
  }
10082
8791
  if (batch.length < 100) return result;
@@ -10085,9 +8794,9 @@ async function matchingRecords(tx, prefix3, options, matches2) {
10085
8794
  __name(matchingRecords, "matchingRecords");
10086
8795
 
10087
8796
  // ../core/src/endpoint-client.ts
10088
- import { createHash as createHash5 } from "node:crypto";
8797
+ import { createHash as createHash2 } from "node:crypto";
10089
8798
  import { homedir } from "node:os";
10090
- import { join as join4 } from "node:path";
8799
+ import { join as join2 } from "node:path";
10091
8800
  var mediaPipelines = /* @__PURE__ */ new WeakMap();
10092
8801
  function acquireFileMediaPipeline(endpoint, options, fetchImpl) {
10093
8802
  const existing = mediaPipelines.get(endpoint);
@@ -10097,7 +8806,7 @@ function acquireFileMediaPipeline(endpoint, options, fetchImpl) {
10097
8806
  if (options.endpoint && !options.mediaBytes && !options.mediaDirectory && !options.stateDirectory) {
10098
8807
  throw new Error("An injected Canon cloud endpoint requires durable mediaBytes or a persistent mediaDirectory.");
10099
8808
  }
10100
- 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);
10101
8810
  const pipeline3 = endpoint.createMediaPipeline(options.mediaBytes ?? createNodeEndpointMediaBytes({ directory, fetch: fetchImpl }));
10102
8811
  if (options.autoDrain !== false) void pipeline3.resumePending();
10103
8812
  return pipeline3;
@@ -10119,7 +8828,7 @@ async function acquireAgentEndpoint(input) {
10119
8828
  }
10120
8829
  const requestedMls = input.options.mlsDev;
10121
8830
  const mls = requestedMls ? await (async () => {
10122
- const crypto2 = await import("./mls-crypto-LGPCXM3K.js");
8831
+ const crypto2 = await import("./mls-crypto-4DZT2UFH.js");
10123
8832
  const configuration = crypto2.parseMlsDevClientConfiguration(requestedMls.configuration);
10124
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");
10125
8834
  const custody = requestedMls.custody;
@@ -10148,8 +8857,8 @@ async function acquireAgentEndpoint(input) {
10148
8857
  }
10149
8858
  const streamUrl = input.options.streamUrl ?? Object.entries(CANON_RUNTIME_ENDPOINT_DEFAULTS).find(([id2]) => id2 === environmentId)?.[1]?.streamUrl;
10150
8859
  if (!streamUrl) throw new Error("Canon endpoint requires a streamUrl for this environment.");
10151
- const directory = input.options.stateDirectory ?? process.env.CANON_ENDPOINT_STATE_DIR ?? join4(process.env.CANON_HOME || join4(homedir(), ".canon"), "endpoint");
10152
- 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}
10153
8862
  ${baseUrl}
10154
8863
  ${input.principalId}
10155
8864
  ${input.ownershipVersion}`).digest("hex");
@@ -10891,7 +9600,7 @@ function asCanonApiError(error) {
10891
9600
  }
10892
9601
  __name(asCanonApiError, "asCanonApiError");
10893
9602
  function stableOperationId(...parts) {
10894
- return createHash6("sha256").update(JSON.stringify(parts)).digest("hex");
9603
+ return createHash3("sha256").update(JSON.stringify(parts)).digest("hex");
10895
9604
  }
10896
9605
  __name(stableOperationId, "stableOperationId");
10897
9606
  function normalizeMessage(raw) {
@@ -11151,14 +9860,14 @@ async function resumeRegistration(options) {
11151
9860
  if (status.status === "rejected") return { status: "rejected", requestId };
11152
9861
  const remaining = deadline - Date.now();
11153
9862
  if (remaining <= 0) return { status: "pending", requestId };
11154
- await new Promise((resolve6, reject) => {
9863
+ await new Promise((resolve3, reject) => {
11155
9864
  const abort = /* @__PURE__ */ __name(() => {
11156
9865
  clearTimeout(timer);
11157
9866
  reject(signal.reason);
11158
9867
  }, "abort");
11159
9868
  const timer = setTimeout(() => {
11160
9869
  signal?.removeEventListener("abort", abort);
11161
- resolve6();
9870
+ resolve3();
11162
9871
  }, Math.min(pollIntervalMs, remaining));
11163
9872
  signal?.addEventListener("abort", abort, { once: true });
11164
9873
  if (signal?.aborted) abort();
@@ -11171,7 +9880,7 @@ __name(resumeRegistration, "resumeRegistration");
11171
9880
  import { existsSync, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync } from "node:fs";
11172
9881
  import { closeSync as closeSync2, openSync as openSync2 } from "node:fs";
11173
9882
  import { randomUUID as randomUUID5 } from "node:crypto";
11174
- import { join as join5, resolve as resolve4 } from "node:path";
9883
+ import { join as join3, resolve } from "node:path";
11175
9884
  import { homedir as homedir2 } from "node:os";
11176
9885
 
11177
9886
  // ../core/src/process-lock.ts
@@ -11212,15 +9921,15 @@ function claimStaleLockReaper(lockPath) {
11212
9921
  __name(claimStaleLockReaper, "claimStaleLockReaper");
11213
9922
 
11214
9923
  // ../core/src/agent-profiles.ts
11215
- var CANON_DIR = process.env.CANON_HOME ? resolve4(process.env.CANON_HOME) : join5(homedir2(), ".canon");
11216
- var AGENTS_PATH = join5(CANON_DIR, "agents.json");
11217
- var LOCKS_DIR = join5(CANON_DIR, "locks");
11218
- 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");
11219
9928
  var AGENTS_JSON_BOOTSTRAP_ENV = "CANON_AGENTS_JSON_BOOTSTRAP";
11220
9929
  function canonPaths(home) {
11221
9930
  if (!home) return { dir: CANON_DIR, agents: AGENTS_PATH, locks: LOCKS_DIR };
11222
- const dir = resolve4(home);
11223
- 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") };
11224
9933
  }
11225
9934
  __name(canonPaths, "canonPaths");
11226
9935
  function normalizeAgentProfile(name, value, source) {
@@ -11387,7 +10096,7 @@ function sameProcess(owner) {
11387
10096
  }
11388
10097
  __name(sameProcess, "sameProcess");
11389
10098
  function isProfileLocked(profile, home) {
11390
- const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
10099
+ const lockPath = join3(canonPaths(home).locks, `${profile}.lock`);
11391
10100
  try {
11392
10101
  const lock = parseLock(readFileSync2(lockPath, "utf-8"));
11393
10102
  const pid = lock?.pid;
@@ -11412,7 +10121,7 @@ __name(parseLock, "parseLock");
11412
10121
  function acquireLock(profile, home) {
11413
10122
  const locks = canonPaths(home).locks;
11414
10123
  mkdirSync(locks, { recursive: true });
11415
- const lockPath = join5(locks, `${profile}.lock`);
10124
+ const lockPath = join3(locks, `${profile}.lock`);
11416
10125
  const token = randomUUID5();
11417
10126
  const payload = JSON.stringify({
11418
10127
  pid: process.pid,
@@ -11464,7 +10173,7 @@ function acquireLock(profile, home) {
11464
10173
  }
11465
10174
  __name(acquireLock, "acquireLock");
11466
10175
  function releaseLock(profile, token, home) {
11467
- const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
10176
+ const lockPath = join3(canonPaths(home).locks, `${profile}.lock`);
11468
10177
  if (token) {
11469
10178
  try {
11470
10179
  const lock = parseLock(readFileSync2(lockPath, "utf-8"));
@@ -11761,7 +10470,7 @@ var RuntimeRequestManager = class {
11761
10470
  }
11762
10471
  const key = pendingKey(kind, conversationId, effectiveId);
11763
10472
  const signal = options.signal;
11764
- return await new Promise((resolve6, reject) => {
10473
+ return await new Promise((resolve3, reject) => {
11765
10474
  let settled = false;
11766
10475
  const finishCommon = /* @__PURE__ */ __name(() => {
11767
10476
  settled = true;
@@ -11787,7 +10496,7 @@ var RuntimeRequestManager = class {
11787
10496
  } catch {
11788
10497
  }
11789
10498
  }
11790
- resolve6(finalResult);
10499
+ resolve3(finalResult);
11791
10500
  })();
11792
10501
  }, "settle");
11793
10502
  const abort = /* @__PURE__ */ __name((error) => {
@@ -11921,7 +10630,7 @@ var RuntimeRequestManager = class {
11921
10630
  pending.wakeRequested = false;
11922
10631
  return Promise.resolve();
11923
10632
  }
11924
- return new Promise((resolve6) => {
10633
+ return new Promise((resolve3) => {
11925
10634
  let done = false;
11926
10635
  const finish = /* @__PURE__ */ __name(() => {
11927
10636
  if (done) return;
@@ -11933,7 +10642,7 @@ var RuntimeRequestManager = class {
11933
10642
  current.wake = () => {
11934
10643
  };
11935
10644
  }
11936
- resolve6();
10645
+ resolve3();
11937
10646
  }, "finish");
11938
10647
  const timer = setTimeout(finish, ms);
11939
10648
  const entry = this.pending.get(key);
@@ -12710,11 +11419,11 @@ function buildRuntimeActivityItemPath(conversationId, agentId, itemId) {
12710
11419
  return `${buildRuntimeActivityPath(conversationId, agentId)}/${runtimeActivityItemKey(itemId)}`;
12711
11420
  }
12712
11421
  __name(buildRuntimeActivityItemPath, "buildRuntimeActivityItemPath");
12713
- function record6(value) {
11422
+ function record5(value) {
12714
11423
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected runtime projection object");
12715
11424
  return value;
12716
11425
  }
12717
- __name(record6, "record");
11426
+ __name(record5, "record");
12718
11427
  var PROJECTIONS = /* @__PURE__ */ new Set(["agent-session", "turn-state", "runtime-info", "runtime-activity", "streaming"]);
12719
11428
  function createRTDBClientHandle(client) {
12720
11429
  async function scope5(path) {
@@ -12741,40 +11450,40 @@ function createRTDBClientHandle(client) {
12741
11450
  const target = await scope5(path);
12742
11451
  const clear = method === "remove" || data === null;
12743
11452
  if (target.family === "agent-runtime") {
12744
- if (clear || record6(data).updatedAt === 0) return client.updateRuntimeStatus({ online: false });
12745
- const value = record6(data);
11453
+ if (clear || record5(data).updatedAt === 0) return client.updateRuntimeStatus({ online: false });
11454
+ const value = record5(data);
12746
11455
  if (typeof value.clientType !== "string" || !value.clientType) throw new Error("Missing runtime client type");
12747
11456
  return client.updateRuntimeStatus({
12748
11457
  runtime: value.clientType,
12749
11458
  ...typeof value.hostMode === "boolean" ? { hostMode: value.hostMode } : {},
12750
- ...value.runtimeDescriptor ? { runtimeDescriptor: record6(value.runtimeDescriptor) } : {}
11459
+ ...value.runtimeDescriptor ? { runtimeDescriptor: record5(value.runtimeDescriptor) } : {}
12751
11460
  });
12752
11461
  }
12753
11462
  if (target.family === "agent-session") {
12754
11463
  if (clear) return client.publishRuntimeSession(target.conversationId, "clear");
12755
- const state = sanitizeAgentSessionSnapshotPatch(record6(data));
11464
+ const state = sanitizeAgentSessionSnapshotPatch(record5(data));
12756
11465
  return client.publishRuntimeSession(target.conversationId, "patch", state);
12757
11466
  }
12758
11467
  if (target.family === "runtime-info") {
12759
11468
  if (clear) return client.publishRuntimeInfo(target.conversationId, "clear");
12760
- const { updatedAt: _updatedAt, ...info } = record6(data);
11469
+ const { updatedAt: _updatedAt, ...info } = record5(data);
12761
11470
  return client.publishRuntimeInfo(target.conversationId, method === "patch" ? "patch" : "replace", info);
12762
11471
  }
12763
11472
  if (target.family === "runtime-activity") {
12764
11473
  if (clear) return client.publishRuntimeActivity(target.conversationId, target.itemId ? { mode: "remove", itemId: target.itemId } : { mode: "clear" });
12765
11474
  if (!target.itemId) throw new Error("Write one runtime activity item");
12766
- const item = record6(data);
11475
+ const item = record5(data);
12767
11476
  if (typeof item.id !== "string" || runtimeActivityItemKey(item.id) !== target.itemId) throw new Error("Runtime activity identity mismatch");
12768
11477
  return client.publishRuntimeActivity(target.conversationId, { mode: "upsert", item });
12769
11478
  }
12770
11479
  if (target.family === "streaming") {
12771
11480
  if (clear) return client.clearStreaming(target.conversationId);
12772
- const { updatedAt: _updatedAt, startedAt: _startedAt, ...patch2 } = record6(data);
11481
+ const { updatedAt: _updatedAt, startedAt: _startedAt, ...patch2 } = record5(data);
12773
11482
  if (Object.keys(patch2).some((key) => !["text", "status", "messageId", "turnId", "blocks"].includes(key))) throw new Error("Unsupported streaming projection field");
12774
11483
  return client.patchRuntimeStreaming(target.conversationId, patch2);
12775
11484
  }
12776
11485
  if (target.family === "turn-state") {
12777
- 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);
12778
11487
  const { updatedAt: _updatedAt, completedAt: _completedAt, openedAt, turnUpdatedAt, ...turn } = value;
12779
11488
  return client.setRuntimeTurn({
12780
11489
  ...turn,
@@ -13159,12 +11868,12 @@ __name(applyFenceLine, "applyFenceLine");
13159
11868
  function scanFenceState(text, fence) {
13160
11869
  if (!text) return fence;
13161
11870
  const lines = text.split("\n");
13162
- let open5 = fence;
11871
+ let open3 = fence;
13163
11872
  for (let index = 0; index < lines.length; index += 1) {
13164
11873
  if (index === lines.length - 1 && lines[index] === "") continue;
13165
- open5 = applyFenceLine(open5, lines[index]);
11874
+ open3 = applyFenceLine(open3, lines[index]);
13166
11875
  }
13167
- return open5;
11876
+ return open3;
13168
11877
  }
13169
11878
  __name(scanFenceState, "scanFenceState");
13170
11879
  function codePointUtf8Bytes(codePoint) {
@@ -13375,12 +12084,12 @@ __name(sendMessageWithRetryChunked, "sendMessageWithRetryChunked");
13375
12084
 
13376
12085
  // ../core/src/cli-metadata.ts
13377
12086
  import { readFileSync as readFileSync3, realpathSync } from "node:fs";
13378
- import { dirname as dirname2, resolve as resolve5 } from "node:path";
12087
+ import { dirname, resolve as resolve2 } from "node:path";
13379
12088
  import { fileURLToPath, pathToFileURL } from "node:url";
13380
12089
  function isDirectExecution(moduleUrl) {
13381
12090
  const entry = process.argv[1];
13382
12091
  if (!entry) return false;
13383
- const abs = resolve5(entry);
12092
+ const abs = resolve2(entry);
13384
12093
  let real = abs;
13385
12094
  try {
13386
12095
  real = realpathSync(abs);
@@ -13390,13 +12099,13 @@ function isDirectExecution(moduleUrl) {
13390
12099
  }
13391
12100
  __name(isDirectExecution, "isDirectExecution");
13392
12101
  function readCliPackageVersion(moduleUrl) {
13393
- let current = dirname2(fileURLToPath(moduleUrl));
12102
+ let current = dirname(fileURLToPath(moduleUrl));
13394
12103
  while (true) {
13395
12104
  try {
13396
- const manifest = JSON.parse(readFileSync3(resolve5(current, "package.json"), "utf8"));
12105
+ const manifest = JSON.parse(readFileSync3(resolve2(current, "package.json"), "utf8"));
13397
12106
  return typeof manifest.version === "string" ? manifest.version : null;
13398
12107
  } catch {
13399
- const parent = dirname2(current);
12108
+ const parent = dirname(current);
13400
12109
  if (parent === current) return null;
13401
12110
  current = parent;
13402
12111
  }
@@ -13435,7 +12144,7 @@ function runCli(moduleUrl, main, onError, metadata) {
13435
12144
  __name(runCli, "runCli");
13436
12145
 
13437
12146
  // ../core/src/completed-output-journal.ts
13438
- import { createHash as createHash7, randomUUID as randomUUID7 } from "node:crypto";
12147
+ import { createHash as createHash4, randomUUID as randomUUID7 } from "node:crypto";
13439
12148
  import {
13440
12149
  closeSync as closeSync3,
13441
12150
  existsSync as existsSync2,
@@ -13448,12 +12157,13 @@ import {
13448
12157
  unlinkSync as unlinkSync3,
13449
12158
  writeFileSync as writeFileSync3
13450
12159
  } from "node:fs";
13451
- import { join as join6 } from "node:path";
12160
+ import { join as join4 } from "node:path";
13452
12161
  function createCompletedOutputJournal(directory, scope5, options) {
13453
12162
  const label = options.label;
13454
- const folder = join6(directory, createHash7("sha256").update(JSON.stringify(scope5)).digest("hex"));
13455
- 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");
13456
12165
  function syncDirectory() {
12166
+ if (process.platform === "win32") return;
13457
12167
  const fd = openSync3(folder, "r");
13458
12168
  try {
13459
12169
  fsyncSync(fd);
@@ -13462,8 +12172,8 @@ function createCompletedOutputJournal(directory, scope5, options) {
13462
12172
  }
13463
12173
  }
13464
12174
  __name(syncDirectory, "syncDirectory");
13465
- function validate(record7) {
13466
- 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)) {
13467
12177
  throw new Error(`Invalid completed ${label} output record; operator reconciliation is required`);
13468
12178
  }
13469
12179
  }
@@ -13478,21 +12188,21 @@ function createCompletedOutputJournal(directory, scope5, options) {
13478
12188
  } catch (error) {
13479
12189
  if (error.code !== "ESRCH") continue;
13480
12190
  }
13481
- const path = join6(folder, name);
13482
- let record7;
12191
+ const path = join4(folder, name);
12192
+ let record6;
13483
12193
  try {
13484
- record7 = JSON.parse(readFileSync4(path, "utf8"));
13485
- validate(record7);
12194
+ record6 = JSON.parse(readFileSync4(path, "utf8"));
12195
+ validate(record6);
13486
12196
  } catch {
13487
12197
  unlinkSync3(path);
13488
12198
  throw new Error(`Incomplete ${label} output write removed; reconcile its native transcript`);
13489
12199
  }
13490
- const destination = filename(record7.turnId);
13491
- if (destination !== join6(folder, `${match[1]}.json`)) {
12200
+ const destination = filename(record6.turnId);
12201
+ if (destination !== join4(folder, `${match[1]}.json`)) {
13492
12202
  throw new Error(`Completed ${label} output temporary file identity mismatch`);
13493
12203
  }
13494
12204
  if (existsSync2(destination)) {
13495
- if (readFileSync4(destination, "utf8") !== JSON.stringify(record7)) {
12205
+ if (readFileSync4(destination, "utf8") !== JSON.stringify(record6)) {
13496
12206
  throw new Error(`Conflicting completed ${label} outputs require reconciliation`);
13497
12207
  }
13498
12208
  unlinkSync3(path);
@@ -13502,11 +12212,11 @@ function createCompletedOutputJournal(directory, scope5, options) {
13502
12212
  }
13503
12213
  __name(recoverTemporaryFiles, "recoverTemporaryFiles");
13504
12214
  return {
13505
- save(record7) {
13506
- validate(record7);
12215
+ save(record6) {
12216
+ validate(record6);
13507
12217
  mkdirSync2(folder, { recursive: true, mode: 448 });
13508
- const path = filename(record7.turnId);
13509
- const serialized = JSON.stringify(record7);
12218
+ const path = filename(record6.turnId);
12219
+ const serialized = JSON.stringify(record6);
13510
12220
  if (existsSync2(path)) {
13511
12221
  if (readFileSync4(path, "utf8") !== serialized) throw new Error(`Completed ${label} output cannot be replaced`);
13512
12222
  return;
@@ -13534,10 +12244,10 @@ function createCompletedOutputJournal(directory, scope5, options) {
13534
12244
  if (!existsSync2(folder)) return [];
13535
12245
  recoverTemporaryFiles();
13536
12246
  return readdirSync(folder).filter((name) => name.endsWith(".json")).map((name) => {
13537
- const record7 = JSON.parse(readFileSync4(join6(folder, name), "utf8"));
13538
- validate(record7);
13539
- if (filename(record7.turnId) !== join6(folder, name)) throw new Error(`Completed ${label} output identity mismatch`);
13540
- 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;
13541
12251
  }).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
13542
12252
  },
13543
12253
  remove(turnId) {
@@ -13551,8 +12261,8 @@ function createCompletedOutputJournal(directory, scope5, options) {
13551
12261
  };
13552
12262
  }
13553
12263
  __name(createCompletedOutputJournal, "createCompletedOutputJournal");
13554
- function assertCompletedOutputAudience(record7, current, label) {
13555
- 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())) {
13556
12266
  throw new Error(`Completed ${label} output audience changed; operator reconciliation is required`);
13557
12267
  }
13558
12268
  }
@@ -13560,9 +12270,9 @@ __name(assertCompletedOutputAudience, "assertCompletedOutputAudience");
13560
12270
 
13561
12271
  // src/bounded-file-read.ts
13562
12272
  var BoundedFileReadError = class extends Error {
13563
- constructor(failure2) {
13564
- super(failure2);
13565
- this.failure = failure2;
12273
+ constructor(failure) {
12274
+ super(failure);
12275
+ this.failure = failure;
13566
12276
  this.name = "BoundedFileReadError";
13567
12277
  }
13568
12278
  failure;
@@ -13601,7 +12311,7 @@ var ANTHROPIC_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
13601
12311
  "image/gif",
13602
12312
  "image/webp"
13603
12313
  ]);
13604
- var DEFAULT_MEDIA_CACHE_DIR = join7(CANON_DIR, "media-cache");
12314
+ var DEFAULT_MEDIA_CACHE_DIR = join5(CANON_DIR, "media-cache");
13605
12315
  var DEFAULT_MEDIA_MATERIALIZATION_BYTES = 10 * 1024 * 1024;
13606
12316
  var MAX_CANON_MEDIA_BYTES = 100 * 1024 * 1024;
13607
12317
  var MAX_ANTHROPIC_IMAGE_RAW_BYTES = 7 * 1024 * 1024;
@@ -13669,7 +12379,7 @@ function inferExtension(input) {
13669
12379
  }
13670
12380
  __name(inferExtension, "inferExtension");
13671
12381
  function conversationMediaCacheDir(input) {
13672
- return join7(
12382
+ return join5(
13673
12383
  input.rootDir ?? DEFAULT_MEDIA_CACHE_DIR,
13674
12384
  sanitizeSegment(input.agentId, "agent"),
13675
12385
  sanitizeSegment(input.conversationId, "conversation")
@@ -13685,7 +12395,7 @@ function buildCachePath(input) {
13685
12395
  const fallbackName = `${input.attachment.kind}-${input.index}${extension}`;
13686
12396
  const preferredName = input.attachment.fileName ? sanitizeDownloadFileName(input.attachment.fileName, fallbackName) : fallbackName;
13687
12397
  const fileName = extname(preferredName) ? preferredName : `${preferredName}${extension}`;
13688
- return join7(
12398
+ return join5(
13689
12399
  conversationMediaCacheDir({ agentId: input.agentId, conversationId: input.conversationId, rootDir }),
13690
12400
  sanitizeSegment(input.messageId, "message"),
13691
12401
  `${String(input.index).padStart(2, "0")}-${fileName}`
@@ -13798,7 +12508,7 @@ async function materializeAttachment(attachment, options) {
13798
12508
  index: options.index ?? 0,
13799
12509
  rootDir: options.rootDir
13800
12510
  });
13801
- await mkdir3(dirname3(path), { recursive: true });
12511
+ await mkdir2(dirname2(path), { recursive: true });
13802
12512
  const maxBytes = resolveMaxBytes(options.maxBytes);
13803
12513
  const expectedBytes = typeof attachment.sizeBytes === "number" && Number.isSafeInteger(attachment.sizeBytes) && attachment.sizeBytes >= 0 ? attachment.sizeBytes : null;
13804
12514
  if (expectedBytes !== null && expectedBytes > maxBytes) {
@@ -14014,7 +12724,7 @@ async function toAnthropicImageBlock(attachment, options) {
14014
12724
  }
14015
12725
  __name(toAnthropicImageBlock, "toAnthropicImageBlock");
14016
12726
  async function readAnthropicImageBytes(attachment, maxRawBytes) {
14017
- const handle = await open4(attachment.path, "r");
12727
+ const handle = await open2(attachment.path, "r");
14018
12728
  try {
14019
12729
  return await readBoundedRegularFileHandle(handle, maxRawBytes);
14020
12730
  } catch (error) {
@@ -14120,9 +12830,6 @@ function getCodexImagePath(attachment) {
14120
12830
  __name(getCodexImagePath, "getCodexImagePath");
14121
12831
 
14122
12832
  export {
14123
- NodeMlsDevCustodyError,
14124
- createNodeMlsDevCustody,
14125
- createPortableNodeMlsDevCustody,
14126
12833
  resolveCanonRuntimeConnection,
14127
12834
  verifyCanonRuntimeConnection,
14128
12835
  CanonClient,