@canonmsg/agent-sdk 13.1.0 → 13.2.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.
@@ -5,7 +5,7 @@ import {
5
5
  createHttpEndpointTransport,
6
6
  createNodeEndpointStore,
7
7
  validateEndpointContentBoundary
8
- } from "./chunk-YJUPKU47.js";
8
+ } from "./chunk-BPSFHEAA.js";
9
9
  import {
10
10
  CANON_ENDPOINT_PROTOCOL,
11
11
  CANON_VERB_WIRE_SCHEMA_VERSION,
@@ -40,8 +40,6 @@ import {
40
40
  isMlsDevTextProtocol,
41
41
  isMlsEnvironmentId,
42
42
  isMlsPrincipalAlias,
43
- logicalMediaBlobId,
44
- mergeDeliveryObservation,
45
43
  mlsAdmissionWait,
46
44
  mlsDevProtocolCarriesCards,
47
45
  normalizePublicRuntimeDescriptor,
@@ -102,18 +100,17 @@ import {
102
100
  parseMlsPrincipalOpening,
103
101
  parseMlsRoomIdentities,
104
102
  prepareJsonContent,
105
- projectDeliveryStatus,
106
103
  projectVerbIntentToWire,
107
104
  readMembershipChangeMetadata,
108
105
  resolveAllowedUploadMimeType,
109
106
  sanitizeDownloadFileName
110
- } from "./chunk-5WPAB5MB.js";
107
+ } from "./chunk-VIS5WXNH.js";
111
108
 
112
109
  // src/media.ts
113
110
  import { createWriteStream as createWriteStream2 } from "node:fs";
114
- import { randomUUID as randomUUID7 } from "node:crypto";
111
+ import { randomUUID as randomUUID6 } from "node:crypto";
115
112
  import { mkdir as mkdir2, open as open2, rename as rename2, stat as stat2, unlink as unlink2 } from "node:fs/promises";
116
- import { basename, dirname as dirname2, extname, join as join5 } from "node:path";
113
+ import { basename, dirname as dirname2, extname, join as join4 } from "node:path";
117
114
  import { Readable, Transform as Transform2 } from "node:stream";
118
115
  import { pipeline as pipeline2 } from "node:stream/promises";
119
116
 
@@ -154,11 +151,11 @@ function createNodeEndpointMediaBytes(options) {
154
151
  __name(digest, "digest");
155
152
  return {
156
153
  digest,
157
- async retain(key, source) {
154
+ async retain(key2, source) {
158
155
  await initialized;
159
156
  const expected = expectedDigests.get(source);
160
157
  if (!expected) throw new Error("Canon media must be fingerprinted before retaining bytes");
161
- const reference = `${createHash("sha256").update(key).digest("hex")}.bytes`;
158
+ const reference = `${createHash("sha256").update(key2).digest("hex")}.bytes`;
162
159
  const destination = referencePath(reference);
163
160
  const temporary = `${destination}.${randomUUID()}.tmp`;
164
161
  const sourceHandle = await open(source, "r");
@@ -522,8 +519,8 @@ async function readPreparationSnapshot(tx, scope5) {
522
519
  __name(readPreparationSnapshot, "readPreparationSnapshot");
523
520
  function assertPreparationAdmission(expected, current) {
524
521
  if (!expected || !current.admission) return;
525
- for (const key of ["conversationId", "membershipRevision", "admissionId"]) {
526
- if (expected[key] !== void 0 && current.admission[key] !== void 0 && expected[key] !== current.admission[key]) {
522
+ for (const key2 of ["conversationId", "membershipRevision", "admissionId"]) {
523
+ if (expected[key2] !== void 0 && current.admission[key2] !== void 0 && expected[key2] !== current.admission[key2]) {
527
524
  throw new EndpointError("The authored admission changed before content could commit", { code: "PREPARATION_ADMISSION_CHANGED", retryable: false });
528
525
  }
529
526
  }
@@ -535,12 +532,12 @@ function assertGeneration(current, expected) {
535
532
  __name(assertGeneration, "assertGeneration");
536
533
  async function reservePreparedIdentityInTransaction(tx, input) {
537
534
  assertGeneration(await tx.get(PREPARATION_GENERATION_KEY) ?? 0, input.installationGeneration);
538
- const key = `preparation/${input.operationId}`;
539
- const existing = await tx.get(key);
535
+ const key2 = `preparation/${input.operationId}`;
536
+ const existing = await tx.get(key2);
540
537
  if (existing && (existing.action !== input.action || existing.fingerprint !== input.fingerprint || existing.installationGeneration !== input.installationGeneration)) {
541
538
  throw new EndpointError("Operation ID was already reserved for different content", { status: 409, code: "OPERATION_ID_COLLISION", retryable: false, operationId: input.operationId });
542
539
  }
543
- if (!existing) await tx.put(key, input);
540
+ if (!existing) await tx.put(key2, input);
544
541
  }
545
542
  __name(reservePreparedIdentityInTransaction, "reservePreparedIdentityInTransaction");
546
543
  async function reservePreparedIdentity(store, input) {
@@ -587,7 +584,6 @@ function endpointIntentOrdering(intent2) {
587
584
  const body = { ...intent2.body, ...intent2.envelope };
588
585
  if (intent2.action === "consume_runtime_signal") return "independent";
589
586
  if (intent2.action === "generate_image") return "provider";
590
- if (["append_artifact_command", "retain_artifact_asset"].includes(intent2.action) && typeof body.artifactId === "string") return "artifact";
591
587
  if (intent2.action === "session_control" && !body.primitive && typeof body.agentId === "string") {
592
588
  const type = body.signal?.type;
593
589
  if (type === "interrupt" || type === "stop_and_drop") return "signal";
@@ -597,10 +593,10 @@ function endpointIntentOrdering(intent2) {
597
593
  }
598
594
  __name(endpointIntentOrdering, "endpointIntentOrdering");
599
595
  async function nextQueueSequence(tx, name) {
600
- const key = `queue-sequence/${name}`;
601
- const next = (await tx.get(key) ?? 0) + 1;
596
+ const key2 = `queue-sequence/${name}`;
597
+ const next = (await tx.get(key2) ?? 0) + 1;
602
598
  if (!Number.isSafeInteger(next)) throw new Error("Endpoint queue sequence exhausted");
603
- await tx.put(key, next);
599
+ await tx.put(key2, next);
604
600
  return next;
605
601
  }
606
602
  __name(nextQueueSequence, "nextQueueSequence");
@@ -609,19 +605,17 @@ async function orderOperation(tx, operation) {
609
605
  }
610
606
  __name(orderOperation, "orderOperation");
611
607
  var outboxKey = /* @__PURE__ */ __name((operation) => operation.queueOrder === void 0 ? `outbox/${operation.operationId}` : `outbox/${ordinal(operation.queueOrder)}/${operation.operationId}`, "outboxKey");
612
- var artifactOrderingPrefix = /* @__PURE__ */ __name((conversationId, artifactId) => `outbound-artifact-order/${scope(conversationId)}/${scope(artifactId)}/`, "artifactOrderingPrefix");
613
608
  var controlOrderingKey = /* @__PURE__ */ __name((operation) => operation.action === "session_control" && operation.conversationId && operation.agentId ? `outbound-control-order/${scope(operation.conversationId)}/${scope(operation.agentId)}/` : void 0, "controlOrderingKey");
614
609
  var operationOrderingKey = /* @__PURE__ */ __name((operation) => {
615
610
  if (!operation.conversationId || operation.ordering === "independent" || operation.ordering === "provider") return void 0;
616
611
  if (operation.ordering === "signal") return controlOrderingKey(operation);
617
- if (operation.ordering === "artifact") return artifactOrderingPrefix(operation.conversationId, operation.artifactId ?? "~");
618
612
  return operation.ordering === "message" ? `outbound-ack-order/${scope(operation.conversationId)}/${scope(operation.messageId)}/` : `outbound-order/${scope(operation.conversationId)}/`;
619
613
  }, "operationOrderingKey");
620
614
  var outboundPrefix = /* @__PURE__ */ __name((operation) => operationOrderingKey(operation), "outboundPrefix");
621
615
  var outboundKeys = /* @__PURE__ */ __name((operation) => [.../* @__PURE__ */ new Set([operationOrderingKey(operation), controlOrderingKey(operation)])].filter((prefix3) => Boolean(prefix3)).map((prefix3) => `${prefix3}${ordinal(operation.queueOrder)}/${operation.operationId}`), "outboundKeys");
622
616
  async function trackOperationOrder(tx, operation) {
623
617
  if (operation.queueOrder !== void 0 && !["succeeded", "rejected"].includes(operation.status)) {
624
- for (const key of outboundKeys(operation)) await tx.put(key, { operationId: operation.operationId });
618
+ for (const key2 of outboundKeys(operation)) await tx.put(key2, { operationId: operation.operationId });
625
619
  }
626
620
  }
627
621
  __name(trackOperationOrder, "trackOperationOrder");
@@ -634,7 +628,7 @@ async function removeOperationOutbox(tx, operation) {
634
628
  }
635
629
  __name(removeOperationOutbox, "removeOperationOutbox");
636
630
  async function settleOperationOrder(tx, operation) {
637
- if (operation.queueOrder !== void 0) for (const key of outboundKeys(operation)) await tx.delete(key);
631
+ if (operation.queueOrder !== void 0) for (const key2 of outboundKeys(operation)) await tx.delete(key2);
638
632
  }
639
633
  __name(settleOperationOrder, "settleOperationOrder");
640
634
  async function isFirstOrderedOperation(tx, operation) {
@@ -913,9 +907,9 @@ async function compactExpiredPayloads(store, clock) {
913
907
  const rows = await tx.scan("payload-expiry/", { limit: 100 });
914
908
  const expired = rows.filter((row) => row.value.expiresAt <= clock);
915
909
  for (const row of expired) {
916
- const key = `operation/${row.value.operationId}`;
917
- const value = await tx.get(key);
918
- if (value && ["succeeded", "rejected", "uncertain"].includes(value.status)) await tx.put(key, compactOperation(value));
910
+ const key2 = `operation/${row.value.operationId}`;
911
+ const value = await tx.get(key2);
912
+ if (value && ["succeeded", "rejected", "uncertain"].includes(value.status)) await tx.put(key2, compactOperation(value));
919
913
  await tx.delete(row.key);
920
914
  }
921
915
  return expired.length;
@@ -958,7 +952,7 @@ async function eraseEndpointPlaintext(store) {
958
952
  after = rows[rows.length - 1].key;
959
953
  }
960
954
  }
961
- for (const prefix3 of ["preparation/", "protocol-state/", "mls-principal", "mls-dev-device/", "mls-dev-key-package/", "mls-dev-group/", "deferred-opener/", "artifact-v2/", "artifact/", "artifact-intent/", "artifact-admission/", "artifact-key/", "conversation/", "inbound-ready/", "outbox/", "payload-expiry/", "media-flow/", "media-visible/", "media-pending/", "media-lease/", "media-prepared-upload/"]) {
955
+ for (const prefix3 of ["completion/", "preparation/", "protocol-state/", "mls-principal", "mls-dev-device/", "mls-dev-key-package/", "mls-dev-group/", "deferred-opener/", "artifact-v2/", "artifact/", "artifact-intent/", "artifact-admission/", "artifact-key/", "conversation/", "inbound-ready/", "outbox/", "payload-expiry/", "media-flow/", "media-visible/", "media-pending/", "media-lease/", "media-prepared-upload/"]) {
962
956
  for (; ; ) {
963
957
  const count = await store.transaction(async (tx) => {
964
958
  const rows = await tx.scan(prefix3, { limit: 100 });
@@ -1052,11 +1046,11 @@ function createEndpointQueryObserver(options) {
1052
1046
  return {
1053
1047
  observe(query, parameters, listener, onError) {
1054
1048
  if (closed) throw new Error("Endpoint query observer is closed");
1055
- const key = canonicalEndpointJson({ query, parameters });
1056
- let entry = entries2.get(key);
1049
+ const key2 = canonicalEndpointJson({ query, parameters });
1050
+ let entry = entries2.get(key2);
1057
1051
  if (!entry) {
1058
1052
  entry = { query, parameters, listeners: /* @__PURE__ */ new Set(), running: false, dirty: false, hasValue: false, hadError: false, retryAttempts: 0 };
1059
- entries2.set(key, entry);
1053
+ entries2.set(key2, entry);
1060
1054
  }
1061
1055
  const observer = { value: listener, error: onError };
1062
1056
  entry.listeners.add(observer);
@@ -1068,7 +1062,7 @@ function createEndpointQueryObserver(options) {
1068
1062
  ownedEntry.listeners.delete(observer);
1069
1063
  if (ownedEntry.listeners.size === 0) {
1070
1064
  if (ownedEntry.timer) clearTimeout(ownedEntry.timer);
1071
- entries2.delete(key);
1065
+ entries2.delete(key2);
1072
1066
  }
1073
1067
  };
1074
1068
  },
@@ -1106,42 +1100,18 @@ var ROOM_QUERIES = /* @__PURE__ */ new Set([
1106
1100
  "list_voice_sessions",
1107
1101
  "get_voice_session"
1108
1102
  ]);
1109
- var ARTIFACT_QUERIES = /* @__PURE__ */ new Set(["list_artifacts", "get_artifact_log", "get_artifact_asset"]);
1110
1103
  var INBOX_QUERIES = /* @__PURE__ */ new Set(["list_conversations", "get_unread_summary"]);
1111
1104
  var CONTACT_QUERIES = /* @__PURE__ */ new Set(["list_contacts", "get_contact", "list_contact_requests"]);
1112
1105
  var ROOM_OPERATIONS = /* @__PURE__ */ new Set(["send_message", "mls_application", "mls_group_control", "react", "delete_message", "mark_read"]);
1113
- var ARTIFACT_OPERATIONS = /* @__PURE__ */ new Set(["append_artifact_command", "retain_artifact_asset"]);
1114
1106
  function queryAffectedByOperation(operation, query, parameters) {
1115
- if (ARTIFACT_OPERATIONS.has(operation.action)) {
1116
- if (operation.status !== "succeeded" || !ARTIFACT_QUERIES.has(query)) return false;
1117
- return typeof parameters.conversationId !== "string" || !operation.conversationId || parameters.conversationId === operation.conversationId;
1118
- }
1119
- if (ARTIFACT_QUERIES.has(query)) return false;
1120
1107
  if (!ROOM_OPERATIONS.has(operation.action)) return true;
1121
1108
  if (operation.status !== "succeeded" && !(operation.action === "mls_application" && ["rejected", "uncertain"].includes(operation.status))) return false;
1122
1109
  return queryAffectedByEvent({ kind: "conversation.changed", data: { conversationId: operation.conversationId } }, query, parameters);
1123
1110
  }
1124
1111
  __name(queryAffectedByOperation, "queryAffectedByOperation");
1125
- function artifactQueryAffected(event, query, parameters) {
1126
- const conversationId = parameters.conversationId;
1127
- const sameRoom = typeof conversationId !== "string" || typeof event.data.conversationId !== "string" || event.data.conversationId === conversationId;
1128
- if (event.kind === "artifact.changed") {
1129
- if (!sameRoom) return false;
1130
- return query === "list_artifacts" || typeof parameters.artifactId !== "string" || typeof event.data.artifactId !== "string" || parameters.artifactId === event.data.artifactId;
1131
- }
1132
- if (event.kind === "conversation.removed") return sameRoom;
1133
- if (event.kind === "conversations.changed") {
1134
- const removed = event.data.removedConversationIds;
1135
- return Array.isArray(removed) && (typeof conversationId !== "string" || removed.includes(conversationId));
1136
- }
1137
- return false;
1138
- }
1139
- __name(artifactQueryAffected, "artifactQueryAffected");
1140
1112
  function queryAffectedByEvent(event, query, parameters) {
1141
- if (!ROOM_QUERIES.has(query) && !ARTIFACT_QUERIES.has(query) && !INBOX_QUERIES.has(query) && !CONTACT_QUERIES.has(query)) return true;
1113
+ if (!ROOM_QUERIES.has(query) && !INBOX_QUERIES.has(query) && !CONTACT_QUERIES.has(query)) return true;
1142
1114
  if (event.kind === "agent.context" || event.kind.startsWith("replay.")) return true;
1143
- if (ARTIFACT_QUERIES.has(query)) return artifactQueryAffected(event, query, parameters);
1144
- if (event.kind === "artifact.changed") return false;
1145
1115
  if (/^contacts?\./.test(event.kind)) return CONTACT_QUERIES.has(query);
1146
1116
  if (!/^(message\.|conversations?\.|voice\.session\.)/.test(event.kind)) return true;
1147
1117
  if (CONTACT_QUERIES.has(query)) return false;
@@ -1172,10 +1142,10 @@ async function prepareEndpointIntent(input) {
1172
1142
  const { intent: intent2 } = input;
1173
1143
  const nested = intent2.action === "verb_execute";
1174
1144
  const action = nested ? intent2.body.verb : intent2.action;
1175
- if (!["remove_member", "update_member_role", "leave_conversation", "append_artifact_command", "retain_artifact_asset"].includes(String(action))) return intent2;
1145
+ if (!["remove_member", "update_member_role", "leave_conversation"].includes(String(action))) return intent2;
1176
1146
  const body = intentBody(intent2);
1177
1147
  const conversationId = body.conversationId ?? body.targetConversationId;
1178
- const ownAdmission = ["leave_conversation", "append_artifact_command", "retain_artifact_asset"].includes(String(action));
1148
+ const ownAdmission = action === "leave_conversation";
1179
1149
  const targetUserId = body.targetUserId ?? body.userId ?? body.agentId;
1180
1150
  if (typeof conversationId !== "string" || !ownAdmission && typeof targetUserId !== "string") {
1181
1151
  throw new EndpointError("Membership control requires a conversation and target", { status: 400, code: "INVALID_MEMBERSHIP_CONTROL", retryable: false });
@@ -1224,7 +1194,7 @@ async function assertMlsDevPlaintextAllowed(tx, intent2, lane = "operation.reque
1224
1194
  const privateLeaf = /* @__PURE__ */ __name((value, path) => {
1225
1195
  if (value && typeof value === "object") {
1226
1196
  const entries2 = Object.entries(value);
1227
- if (entries2.length) return entries2.some(([key, child]) => privateLeaf(child, [...path, key]));
1197
+ if (entries2.length) return entries2.some(([key2, child]) => privateLeaf(child, [...path, key2]));
1228
1198
  }
1229
1199
  return boundary.classify(path) !== "public-metadata";
1230
1200
  }, "privateLeaf");
@@ -1263,8 +1233,8 @@ function normalizeInboundDeletion(event) {
1263
1233
  __name(normalizeInboundDeletion, "normalizeInboundDeletion");
1264
1234
  async function recordInboundEvent(tx, incoming, timestamp) {
1265
1235
  const event = normalizeInboundDeletion(incoming);
1266
- const key = `inbound/${event.id}`;
1267
- const existing = await tx.get(key);
1236
+ const key2 = `inbound/${event.id}`;
1237
+ const existing = await tx.get(key2);
1268
1238
  if (event.conversationId && ["message.created", "message.snapshot", "message.updated"].includes(event.kind) && await isMlsDevRoomProtected(tx, event.conversationId)) {
1269
1239
  if (existing) return false;
1270
1240
  const raw = event.kind === "message.snapshot" ? event.data : event.data.message;
@@ -1278,7 +1248,7 @@ async function recordInboundEvent(tx, incoming, timestamp) {
1278
1248
  durable: true,
1279
1249
  data: notice ? { id: message.id, metadata, text: formatMembershipChangeNotice(metadata), contentType: "text" } : {}
1280
1250
  };
1281
- await tx.put(key, {
1251
+ await tx.put(key2, {
1282
1252
  event: sanitized,
1283
1253
  state: "settled",
1284
1254
  reason: notice ? "membership_control" : "mls_plaintext_withheld",
@@ -1312,12 +1282,12 @@ async function recordInboundEvent(tx, incoming, timestamp) {
1312
1282
  };
1313
1283
  delete next.lease;
1314
1284
  if (existing) await removeInboundReady(tx, existing);
1315
- await tx.put(key, next);
1285
+ await tx.put(key2, next);
1316
1286
  return true;
1317
1287
  }
1318
1288
  if (existing) return false;
1319
1289
  const record6 = await orderInbound(tx, { event: copyJson(event), state: "observed", createdAt: timestamp, updatedAt: timestamp });
1320
- await tx.put(key, record6);
1290
+ await tx.put(key2, record6);
1321
1291
  await enqueueInbound(tx, record6);
1322
1292
  return true;
1323
1293
  }
@@ -1340,14 +1310,14 @@ function serializeInboundPreparation(store, scope5, prepare) {
1340
1310
  }
1341
1311
  __name(serializeInboundPreparation, "serializeInboundPreparation");
1342
1312
  async function recordMlsInteractionProjection(tx, event, timestamp) {
1343
- const key = `inbound/${event.id}`, existing = await tx.get(key);
1313
+ const key2 = `inbound/${event.id}`, existing = await tx.get(key2);
1344
1314
  if (existing && existing.reason !== "mls_plaintext_withheld") return;
1345
- await tx.put(key, { event: copyJson(event), state: "settled", reason: "mls_interaction", createdAt: timestamp, updatedAt: timestamp });
1315
+ await tx.put(key2, { event: copyJson(event), state: "settled", reason: "mls_interaction", createdAt: timestamp, updatedAt: timestamp });
1346
1316
  }
1347
1317
  __name(recordMlsInteractionProjection, "recordMlsInteractionProjection");
1348
1318
  async function recordVerifiedMlsInboundEvent(tx, event, feedOrdinal, timestamp, own = false) {
1349
1319
  if (!event.conversationId || !Number.isSafeInteger(feedOrdinal) || feedOrdinal < 1) throw new Error("Invalid verified MLS input");
1350
- const key = `inbound/${event.id}`, existing = await tx.get(key);
1320
+ const key2 = `inbound/${event.id}`, existing = await tx.get(key2);
1351
1321
  if (existing && existing.reason !== "mls_plaintext_withheld") return false;
1352
1322
  const record6 = {
1353
1323
  event: copyJson(event),
@@ -1356,7 +1326,7 @@ async function recordVerifiedMlsInboundEvent(tx, event, feedOrdinal, timestamp,
1356
1326
  createdAt: timestamp,
1357
1327
  updatedAt: timestamp
1358
1328
  };
1359
- await tx.put(key, record6);
1329
+ await tx.put(key2, record6);
1360
1330
  if (!own) await enqueueInbound(tx, record6);
1361
1331
  return true;
1362
1332
  }
@@ -1563,8 +1533,8 @@ function createConversationSynchronizer(input) {
1563
1533
  if (protocolPage) return protocolPage;
1564
1534
  const protectedMode = await input.prepareProtocol?.(conversationId) ?? false;
1565
1535
  assertOpen();
1566
- const key = `cursor/${conversationId}`;
1567
- const before = await input.store.transaction((tx) => tx.get(key));
1536
+ const key2 = `cursor/${conversationId}`;
1537
+ const before = await input.store.transaction((tx) => tx.get(key2));
1568
1538
  const resuming = before?.paused !== void 0 && options.resumeAdmissionChange === "checkpoint";
1569
1539
  if (before?.paused && !resuming) throw new EndpointError("Conversation admission changed; pickup is paused until an explicit new checkpoint is chosen", { code: "SYNC_ADMISSION_PAUSED", retryable: false });
1570
1540
  if (!before && !options.bootstrap) throw new EndpointError("Choose a reconciled history baseline before receiving conversation work", { code: "SYNC_BASELINE_REQUIRED", retryable: false });
@@ -1608,11 +1578,11 @@ function createConversationSynchronizer(input) {
1608
1578
  commit: /* @__PURE__ */ __name(async (tx, preparedChanges) => {
1609
1579
  assertOpen();
1610
1580
  if (await tx.get("migration/active")) throw new EndpointError("Migration started during synchronization", { code: "MIGRATION_IN_PROGRESS", retryable: false });
1611
- const current = await tx.get(key);
1581
+ const current = await tx.get(key2);
1612
1582
  if (canonicalEndpointJson(current ?? null) !== canonicalEndpointJson(before ?? null)) return false;
1613
1583
  if (page.reset) {
1614
- if (options.pauseOnAdmissionChange) await tx.put(key, { ...before ?? { cursor: null, admissionId: null, mode, bootstrapping: false }, paused: { admissionId: page.admissionId } });
1615
- else await tx.delete(key);
1584
+ if (options.pauseOnAdmissionChange) await tx.put(key2, { ...before ?? { cursor: null, admissionId: null, mode, bootstrapping: false }, paused: { admissionId: page.admissionId } });
1585
+ else await tx.delete(key2);
1616
1586
  return true;
1617
1587
  }
1618
1588
  for (const event of preparedChanges) {
@@ -1620,7 +1590,7 @@ function createConversationSynchronizer(input) {
1620
1590
  if (mode === "checkpoint") continue;
1621
1591
  await recordInboundEvent(tx, event, new Date(input.clock()).toISOString());
1622
1592
  }
1623
- await tx.put(key, { cursor: page.cursor, admissionId: page.admissionId, bootstrapping: page.bootstrap && page.hasMore, mode });
1593
+ await tx.put(key2, { cursor: page.cursor, admissionId: page.admissionId, bootstrapping: page.bootstrap && page.hasMore, mode });
1624
1594
  return true;
1625
1595
  }, "commit")
1626
1596
  }));
@@ -2035,7 +2005,7 @@ __name(createEndpointMigration, "createEndpointMigration");
2035
2005
 
2036
2006
  // ../endpoint/src/media.ts
2037
2007
  function createEndpointMediaPipeline(options) {
2038
- const { endpoint, store, bytes, background } = options;
2008
+ const { endpoint, store, bytes } = options;
2039
2009
  const clock = options.clock ?? Date.now;
2040
2010
  const ids = options.ids ?? (() => crypto.randomUUID());
2041
2011
  const leaseMs = Math.max(1e3, options.leaseMs ?? 3e4);
@@ -2090,14 +2060,6 @@ function createEndpointMediaPipeline(options) {
2090
2060
  const listeners = /* @__PURE__ */ new Set();
2091
2061
  let closed = false;
2092
2062
  let foreground = true;
2093
- let restoredNativeObservations = false;
2094
- const reconciliation = /* @__PURE__ */ new Map();
2095
- const observations = /* @__PURE__ */ new Map();
2096
- let backgroundCapability;
2097
- let lastServerBackgroundCapability;
2098
- let serverPreparedContentVersion;
2099
- const preparationGeneration = background ? endpoint.ready().then(() => readPreparationGeneration(store)) : Promise.resolve(0);
2100
- void preparationGeneration.catch(() => void 0);
2101
2063
  let timer;
2102
2064
  const prefix3 = "media-flow/";
2103
2065
  const attachmentFor = /* @__PURE__ */ __name((file, value, resultUploadId) => {
@@ -2117,7 +2079,6 @@ function createEndpointMediaPipeline(options) {
2117
2079
  for (const file of previous?.files ?? []) if (file.uploadId && !currentIds.has(file.uploadId)) await tx.delete(`media-prepared-upload/${file.uploadId}`);
2118
2080
  for (const uploadId of currentIds) await tx.put(`media-prepared-upload/${uploadId}`, { id: flow.id });
2119
2081
  }
2120
- if (flow.status === "succeeded" || flow.status === "cancelled") delete flow.preparedContent;
2121
2082
  await tx.put(`${prefix3}${flow.id}`, flow);
2122
2083
  if (flow.status === "pending" || flow.status === "sending") await tx.put(`media-pending/${flow.id}`, { id: flow.id });
2123
2084
  else await tx.delete(`media-pending/${flow.id}`);
@@ -2158,10 +2119,6 @@ function createEndpointMediaPipeline(options) {
2158
2119
  timer.unref?.();
2159
2120
  }
2160
2121
  __name(schedule, "schedule");
2161
- const stopConnections = background ? endpoint.observeEvents?.(() => void 0, { families: ["messages"], onConnected: /* @__PURE__ */ __name(() => {
2162
- for (const state of reconciliation.values()) state.nextAt = Math.min(state.nextAt, clock() + 5e3);
2163
- schedule();
2164
- }, "onConnected") }) : void 0;
2165
2122
  const unsubscribe = endpoint.subscribe((operation) => {
2166
2123
  if (operation.status === "succeeded" && operation.action.endsWith("_upload")) schedule();
2167
2124
  });
@@ -2170,265 +2127,21 @@ function createEndpointMediaPipeline(options) {
2170
2127
  await Promise.allSettled(flow.files.filter((file) => file.reference).map((file) => bytes.release(file.reference)));
2171
2128
  }
2172
2129
  __name(release, "release");
2173
- function probeBackgroundCapability() {
2174
- if (!background) return Promise.resolve(false);
2175
- if (!backgroundCapability) {
2176
- const check = Promise.resolve().then(() => Promise.all([background.available(), endpoint.capabilities()])).then(([nativeAvailable, capabilities]) => {
2177
- if (capabilities) serverPreparedContentVersion = capabilities.preparedMediaContentVersion;
2178
- if (capabilities) lastServerBackgroundCapability = capabilities.actions.includes("stage_media_message") && (background.observationVersion !== 2 || capabilities.mediaDeliveryObservationVersion === 2);
2179
- return nativeAvailable && lastServerBackgroundCapability === true;
2180
- }).catch(() => false).then((enabled) => {
2181
- if (!enabled && backgroundCapability === check) backgroundCapability = void 0;
2182
- return enabled;
2183
- });
2184
- backgroundCapability = check;
2185
- }
2186
- return backgroundCapability;
2187
- }
2188
- __name(probeBackgroundCapability, "probeBackgroundCapability");
2189
2130
  const encryptedRoom = /* @__PURE__ */ __name(async (conversationId) => await endpoint.getConversationContentMode(conversationId) !== "plaintext", "encryptedRoom");
2190
- async function canSendInBackground(options2) {
2191
- if (!background) return false;
2192
- if (options2?.conversationId !== void 0 && await encryptedRoom(options2.conversationId).catch(() => true)) return false;
2193
- if (options2?.maxProbeWaitMs === void 0) return probeBackgroundCapability();
2194
- const nativeAvailable = await Promise.resolve().then(() => background.available()).catch(() => false);
2195
- if (!nativeAvailable) return false;
2196
- if (lastServerBackgroundCapability === false) {
2197
- void probeBackgroundCapability();
2198
- return false;
2199
- }
2200
- const check = probeBackgroundCapability();
2201
- const waitMs = Math.max(0, options2.maxProbeWaitMs);
2202
- if (waitMs === 0) return false;
2203
- return new Promise((resolve3) => {
2204
- const timer2 = setTimeout(() => resolve3(false), waitMs);
2205
- void check.then((enabled) => {
2206
- clearTimeout(timer2);
2207
- resolve3(enabled);
2208
- });
2209
- });
2210
- }
2211
- __name(canSendInBackground, "canSendInBackground");
2212
- async function applyBackgroundSnapshot(snapshot) {
2213
- if (closed || !background) return;
2214
- const flow = await read(snapshot.deliveryId);
2215
- if (!flow || flow.deliveryMode !== "background") return;
2216
- if (snapshot.schemaVersion === 2) {
2217
- const identity = await endpoint.ready();
2218
- if (!snapshot.identity || snapshot.identity.installationId !== identity.installationId || snapshot.identity.environmentId !== identity.environmentId || snapshot.identity.conversationId !== flow.conversationId || snapshot.identity.messageId !== flow.messageId) {
2219
- schedule();
2220
- return;
2221
- }
2222
- }
2223
- const owner = await acquire(flow.id).catch(() => null);
2224
- if (!owner) {
2225
- schedule();
2226
- return;
2227
- }
2228
- try {
2229
- const current = await read(flow.id);
2230
- if (!current || current.deliveryMode !== "background" || current.status === "succeeded" || current.status === "cancelled") return;
2231
- if (snapshot.schemaVersion === 2) {
2232
- const identity = await endpoint.ready();
2233
- if (snapshot.observation) current.observation = mergeDeliveryObservation(current.observation, snapshot.observation, {
2234
- deliveryId: flow.id,
2235
- conversationId: flow.conversationId,
2236
- messageId: flow.messageId,
2237
- installationId: identity.installationId,
2238
- environmentId: identity.environmentId
2239
- });
2240
- current.attention = snapshot.attention;
2241
- const phase = ["published", "cancelled", "expired", "rejected"].includes(snapshot.status) ? "idle" : snapshot.status;
2242
- current.status = projectDeliveryStatus(current.observation?.outcome ?? "unknown", phase);
2243
- current.background = { phase: snapshot.status, uploadedBytes: Math.max(0, snapshot.uploadedBytes), totalBytes: Math.max(0, snapshot.totalBytes) };
2244
- if (current.attention) current.error = { code: current.attention.code, message: snapshot.errorMessage ?? "Delivery needs attention. Its server outcome is being checked." };
2245
- else delete current.error;
2246
- } else {
2247
- current.background = {
2248
- phase: snapshot.status,
2249
- uploadedBytes: Math.max(0, snapshot.uploadedBytes),
2250
- totalBytes: Math.max(0, snapshot.totalBytes)
2251
- };
2252
- if (snapshot.status === "published") {
2253
- current.status = "succeeded";
2254
- delete current.error;
2255
- } else if (snapshot.status === "cancelled") {
2256
- current.status = "cancelled";
2257
- delete current.error;
2258
- } else if (snapshot.status === "expired" || snapshot.status === "rejected") {
2259
- current.status = "rejected";
2260
- current.error = { code: snapshot.errorCode ?? (snapshot.status === "expired" ? "MEDIA_BACKGROUND_EXPIRED" : "MEDIA_BACKGROUND_REJECTED"), message: snapshot.errorMessage ?? "Attachment delivery needs attention" };
2261
- } else {
2262
- current.status = snapshot.status === "sending" ? "sending" : "pending";
2263
- delete current.error;
2264
- }
2265
- }
2266
- await write(current, owner);
2267
- if (current.status === "succeeded" || current.status === "cancelled") {
2268
- delete current.attention;
2269
- delete current.error;
2270
- await Promise.allSettled(current.files.filter((file) => file.descriptor.cleanupAfterSend).map((file) => background.cleanupSource?.(file.reference)));
2271
- await write({ ...current, message: {}, files: [] }, owner);
2272
- }
2273
- if (current.status === "pending" || current.status === "sending") schedule();
2274
- } finally {
2275
- await unlock(flow.id, owner);
2276
- }
2277
- }
2278
- __name(applyBackgroundSnapshot, "applyBackgroundSnapshot");
2279
- async function authoritativeSnapshot(id2) {
2280
- if (!background?.reconcile || !foreground) return void 0;
2281
- const state = reconciliation.get(id2) ?? { nextAt: 0, failures: 0 };
2282
- if (state.pending) return state.pending;
2283
- if (clock() < state.nextAt) {
2284
- schedule();
2285
- return void 0;
2286
- }
2287
- state.nextAt = clock() + 5e3;
2288
- const pending = background.reconcile(id2).then((snapshot) => {
2289
- state.failures = 0;
2290
- state.nextAt = clock() + 5e3;
2291
- return snapshot;
2292
- }, (error) => {
2293
- state.failures += 1;
2294
- state.nextAt = clock() + Math.min(3e5, 5e3 * 2 ** Math.min(6, state.failures));
2295
- schedule();
2296
- throw error;
2297
- }).finally(() => {
2298
- delete state.pending;
2299
- });
2300
- state.pending = pending;
2301
- reconciliation.set(id2, state);
2302
- return pending;
2303
- }
2304
- __name(authoritativeSnapshot, "authoritativeSnapshot");
2305
- async function resumeBackground(flow, onProgress) {
2306
- if (!background) throw new EndpointError("This delivery needs a Canon version with background transfer support", { code: "MEDIA_BACKGROUND_MODULE_UNAVAILABLE", retryable: false });
2131
+ async function retireBackground(flow) {
2307
2132
  if (flow.status === "succeeded") return flow.messageId;
2308
- if (flow.status === "cancelled") throw new EndpointError("Attachment was cancelled", { code: "MEDIA_FLOW_CANCELLED", retryable: false });
2309
- const snapshots = await background.inspect();
2310
- let snapshot = snapshots.find((item) => item.deliveryId === flow.id);
2311
- if (!snapshot && flow.background?.phase === "cancelling") {
2312
- throw new EndpointError("Cancellation is being reconciled", { code: "MEDIA_CANCELLING", retryable: true });
2313
- }
2314
- if (!snapshot) {
2315
- const identity = await endpoint.ready();
2316
- snapshot = await background.enqueue({
2317
- deliveryId: flow.id,
2318
- installationId: identity.installationId,
2319
- conversationId: flow.conversationId,
2320
- messageId: flow.messageId,
2321
- message: copyJson(flow.message),
2322
- ...flow.preparedContent ? { preparedContent: copyJson(flow.preparedContent) } : {},
2323
- files: flow.files.map((file) => ({ source: file.reference, descriptor: copyJson(file.descriptor) }))
2324
- });
2325
- }
2326
- await applyBackgroundSnapshot(snapshot);
2327
- if (background.reconcile) {
2328
- const authoritative = await authoritativeSnapshot(flow.id).catch(() => void 0);
2329
- if (authoritative) {
2330
- snapshot = authoritative;
2331
- await applyBackgroundSnapshot(snapshot);
2332
- }
2333
- schedule();
2334
- }
2335
- if (snapshot.schemaVersion !== 2 && (snapshot.status === "expired" || snapshot.status === "rejected")) {
2336
- throw new EndpointError(snapshot.errorMessage ?? "Attach this file again to send it", {
2337
- code: snapshot.errorCode ?? (snapshot.status === "expired" ? "MEDIA_BACKGROUND_EXPIRED" : "MEDIA_BACKGROUND_REJECTED"),
2338
- retryable: false
2339
- });
2340
- }
2341
- const phase = snapshot.status === "waiting_for_connection" ? "waiting_for_connection" : snapshot.status === "cancelling" ? "cancelling" : snapshot.status === "sending" || snapshot.status === "published" ? "sending" : snapshot.status === "uploading" ? "uploading" : "preparing";
2342
- onProgress?.({ phase, uploadedBytes: snapshot.uploadedBytes, totalBytes: snapshot.totalBytes });
2343
- return flow.messageId;
2344
- }
2345
- __name(resumeBackground, "resumeBackground");
2346
- async function stageBackground(input, sendOptions) {
2347
- if (!background) throw new Error("Background media adapter is missing");
2348
- const id2 = flowId(input.conversationId, input.messageId);
2349
- const existing = staging.get(id2);
2350
- if (existing) {
2351
- await existing;
2352
- return stageBackground(input, sendOptions);
2353
- }
2354
- const staged = (async () => {
2355
- if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
2356
- const fingerprint = await hash(canonicalEndpointJson({ message: input.message, files: input.files.map((file) => ({ descriptor: file.descriptor, source: background.sourceIdentity(file.source) })) }));
2357
- if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
2358
- const owner = await acquire(id2);
2133
+ if (flow.status !== "cancelled") {
2134
+ const owner = await acquire(flow.id);
2359
2135
  try {
2360
- if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
2361
- const prior = await read(id2);
2362
- if (prior) {
2363
- if (prior.deliveryMode !== "background" || prior.fingerprint !== fingerprint) throw new EndpointError("Message identity already belongs to another attachment", { status: 409, code: "MEDIA_FLOW_CONFLICT", retryable: false });
2364
- } else {
2365
- const flow = {
2366
- id: id2,
2367
- conversationId: input.conversationId,
2368
- messageId: input.messageId,
2369
- message: copyJson(input.message),
2370
- fingerprint,
2371
- createdAt: new Date(clock()).toISOString(),
2372
- files: input.files.map((file) => ({ descriptor: copyJson(file.descriptor), reference: background.sourceIdentity(file.source) })),
2373
- deliveryMode: "background",
2374
- status: "pending",
2375
- background: { phase: "preparing", uploadedBytes: 0, totalBytes: input.files.reduce((sum, file) => sum + file.descriptor.sizeBytes, 0) }
2376
- };
2377
- if (background.preparedContentVersion === 1 && serverPreparedContentVersion === 1) {
2378
- const generation2 = await preparationGeneration;
2379
- const operationId = `media:${id2}`;
2380
- await reservePreparedIdentity(store, { operationId, action: "send_message", fingerprint, installationGeneration: generation2 });
2381
- const committed = await prepareAndCommitContent({
2382
- store,
2383
- scope: input.conversationId,
2384
- installationGeneration: generation2,
2385
- existing: /* @__PURE__ */ __name((tx) => tx.get(`${prefix3}${id2}`), "existing"),
2386
- prepare: /* @__PURE__ */ __name(async (snapshot) => ({ nextProtocolState: snapshot.protocolState, payload: prepareJsonContent({
2387
- context: { action: "send_message", conversationId: input.conversationId, messageId: input.messageId },
2388
- body: { ...input.message, conversationId: input.conversationId, messageId: input.messageId },
2389
- blobs: input.files.map((_, index) => ({ id: logicalMediaBlobId(input.conversationId, input.messageId, index), index })),
2390
- expected: { installationGeneration: generation2, protocolRevision: snapshot.protocolRevision, ...snapshot.admission ? { admission: snapshot.admission } : {} }
2391
- }) }), "prepare"),
2392
- commit: /* @__PURE__ */ __name(async (tx, preparedContent) => {
2393
- if (closed || sendOptions?.signal?.aborted) throw new DOMException("Attachment cancelled", "AbortError");
2394
- const sealed = { ...flow, preparedContent };
2395
- await writeInTransaction(tx, sealed, owner);
2396
- await tx.delete(`preparation/${operationId}`);
2397
- return sealed;
2398
- }, "commit")
2399
- });
2400
- notifyFlow(committed);
2401
- } else await write(flow, owner);
2402
- }
2136
+ const { id: id2, conversationId, messageId, createdAt, deliveryMode } = flow;
2137
+ await write({ id: id2, conversationId, messageId, createdAt, deliveryMode, message: {}, files: [], status: "cancelled" }, owner);
2403
2138
  } finally {
2404
- await unlock(id2, owner);
2405
- }
2406
- const onAbort = /* @__PURE__ */ __name(() => {
2407
- void pipeline3.cancel(id2).catch(() => void 0);
2408
- }, "onAbort");
2409
- sendOptions?.signal?.addEventListener("abort", onAbort, { once: true });
2410
- try {
2411
- if (sendOptions?.signal?.aborted) onAbort();
2412
- return await resumeBackground(await read(id2), sendOptions?.onProgress);
2413
- } finally {
2414
- sendOptions?.signal?.removeEventListener("abort", onAbort);
2139
+ await unlock(flow.id, owner);
2415
2140
  }
2416
- })();
2417
- staging.set(id2, staged);
2418
- try {
2419
- return await staged;
2420
- } finally {
2421
- if (staging.get(id2) === staged) staging.delete(id2);
2422
2141
  }
2142
+ throw new EndpointError("Attachment was cancelled", { code: "MEDIA_FLOW_CANCELLED", retryable: false });
2423
2143
  }
2424
- __name(stageBackground, "stageBackground");
2425
- const stopBackground = background?.subscribe((snapshot) => {
2426
- const next = (observations.get(snapshot.deliveryId) ?? Promise.resolve()).then(() => applyBackgroundSnapshot(snapshot)).catch(() => schedule());
2427
- observations.set(snapshot.deliveryId, next);
2428
- void next.finally(() => {
2429
- if (observations.get(snapshot.deliveryId) === next) observations.delete(snapshot.deliveryId);
2430
- });
2431
- });
2144
+ __name(retireBackground, "retireBackground");
2432
2145
  async function stage(input, sendOptions, prepareOnly = false) {
2433
2146
  if (closed) throw new Error("Endpoint media pipeline is closed");
2434
2147
  const id2 = flowId(input.conversationId, input.messageId);
@@ -2470,7 +2183,7 @@ function createEndpointMediaPipeline(options) {
2470
2183
  files.push({ descriptor: copyJson(file.descriptor), reference: await bytes.retain(`${id2}:${fingerprint}:${index}`, file.source) });
2471
2184
  }
2472
2185
  checkOpen();
2473
- await write({ id: id2, ...prepareOnly ? { purpose: "prepare" } : {}, conversationId: input.conversationId, messageId: input.messageId, fingerprint, createdAt: new Date(clock()).toISOString(), ...sendOptions?.beforePublish ? { requiresPublicationApproval: true } : {}, message: copyJson(input.message), files, deliveryMode: "legacy", status: "pending" }, owner);
2186
+ await write({ id: id2, ...prepareOnly ? { purpose: "prepare" } : {}, conversationId: input.conversationId, messageId: input.messageId, fingerprint, createdAt: new Date(clock()).toISOString(), ...sendOptions?.beforePublish ? { requiresPublicationApproval: true } : {}, message: copyJson(input.message), files, status: "pending" }, owner);
2474
2187
  }
2475
2188
  } catch (error) {
2476
2189
  await Promise.allSettled(files.map((file) => bytes.release(file.reference)));
@@ -2492,18 +2205,9 @@ function createEndpointMediaPipeline(options) {
2492
2205
  }
2493
2206
  __name(stage, "stage");
2494
2207
  const pipeline3 = {
2495
- canSendInBackground,
2496
2208
  async send(input, sendOptions) {
2497
- const id2 = flowId(input.conversationId, input.messageId);
2498
- const prior = await read(id2);
2499
- if (prior?.deliveryMode === "background") return stageBackground(input, sendOptions);
2500
- if (!prior && !sendOptions?.beforePublish && sendOptions?.handoffMode !== "legacy") {
2501
- const encrypted = await encryptedRoom(input.conversationId);
2502
- if (encrypted && sendOptions?.handoffMode === "background") {
2503
- throw new EndpointError("An encrypted room's attachments are prepared on this device", { code: "MEDIA_BACKGROUND_UNAVAILABLE", retryable: false });
2504
- }
2505
- if (!encrypted && (sendOptions?.handoffMode === "background" || await canSendInBackground())) return stageBackground(input, sendOptions);
2506
- }
2209
+ const prior = await read(flowId(input.conversationId, input.messageId));
2210
+ if (prior?.deliveryMode === "background") return retireBackground(prior);
2507
2211
  return stage(input, sendOptions);
2508
2212
  },
2509
2213
  async prepare(input, prepareOptions) {
@@ -2515,7 +2219,7 @@ function createEndpointMediaPipeline(options) {
2515
2219
  },
2516
2220
  async resume(id2, resumeOptions) {
2517
2221
  const persisted = await read(id2);
2518
- if (persisted?.deliveryMode === "background") return resumeBackground(persisted, resumeOptions?.onProgress);
2222
+ if (persisted?.deliveryMode === "background") return retireBackground(persisted);
2519
2223
  installGuard(id2, resumeOptions?.beforePublish);
2520
2224
  const existing = running.get(id2);
2521
2225
  if (existing) return existing;
@@ -2771,15 +2475,6 @@ function createEndpointMediaPipeline(options) {
2771
2475
  },
2772
2476
  async resumePending() {
2773
2477
  if (closed) return;
2774
- if (background?.reconcile && !restoredNativeObservations) {
2775
- restoredNativeObservations = true;
2776
- try {
2777
- for (const snapshot of await background.inspect()) await applyBackgroundSnapshot(snapshot);
2778
- } catch {
2779
- restoredNativeObservations = false;
2780
- schedule();
2781
- }
2782
- }
2783
2478
  let after;
2784
2479
  do {
2785
2480
  const records = await store.transaction((tx) => tx.scan("media-pending/", { after, limit: 100 }));
@@ -2793,20 +2488,9 @@ function createEndpointMediaPipeline(options) {
2793
2488
  if (saved) await pipeline3.cancel(saved.id);
2794
2489
  },
2795
2490
  async cancel(id2) {
2796
- const backgroundFlow = await read(id2);
2797
- if (backgroundFlow?.deliveryMode === "background") {
2798
- if (!background || backgroundFlow.status === "succeeded" || backgroundFlow.status === "cancelled") return;
2799
- const owner2 = await acquire(id2);
2800
- try {
2801
- const current = await read(id2);
2802
- if (!current || current.status === "succeeded" || current.status === "cancelled") return;
2803
- current.background = { phase: "cancelling", uploadedBytes: current.background?.uploadedBytes ?? 0, totalBytes: current.background?.totalBytes ?? 0 };
2804
- await write(current, owner2);
2805
- } finally {
2806
- await unlock(id2, owner2);
2807
- }
2808
- await background.cancel(id2);
2809
- schedule();
2491
+ const retired = await read(id2);
2492
+ if (retired?.deliveryMode === "background") {
2493
+ await retireBackground(retired).catch(() => void 0);
2810
2494
  return;
2811
2495
  }
2812
2496
  if (staging.has(id2)) cancelledStages.add(id2);
@@ -2829,15 +2513,12 @@ function createEndpointMediaPipeline(options) {
2829
2513
  async close(closeOptions) {
2830
2514
  closed = true;
2831
2515
  unsubscribe();
2832
- stopConnections?.();
2833
- stopBackground?.();
2834
2516
  listeners.clear();
2835
2517
  for (const guard of publicationGuards.values()) guard.unregister();
2836
2518
  publicationGuards.clear();
2837
2519
  if (timer) clearTimeout(timer);
2838
2520
  for (const controller of controllers.values()) controller.abort();
2839
2521
  await Promise.allSettled([...staging.values(), ...running.values()]);
2840
- if (closeOptions?.discard) await background?.cleanup();
2841
2522
  if (closeOptions?.discard) {
2842
2523
  let after;
2843
2524
  do {
@@ -3184,6 +2865,90 @@ function createInboundHandoffs(input) {
3184
2865
  }
3185
2866
  __name(createInboundHandoffs, "createInboundHandoffs");
3186
2867
 
2868
+ // ../endpoint/src/completions.ts
2869
+ var PREFIX = "completion/";
2870
+ var key = /* @__PURE__ */ __name((id2) => `${PREFIX}${encodeURIComponent(id2)}`, "key");
2871
+ function createCompletions(input) {
2872
+ function validate(record6) {
2873
+ if (record6?.version !== 1 || typeof record6.id !== "string" || !record6.id || record6.id.length > 600 || typeof record6.conversationId !== "string" || !record6.conversationId || typeof record6.createdAt !== "string" || !Array.isArray(record6.inputIds) || record6.inputIds.some((id2) => typeof id2 !== "string" || !id2) || record6.output === void 0) {
2874
+ throw new EndpointError("Invalid completion", { code: "INVALID_COMPLETION", retryable: false });
2875
+ }
2876
+ }
2877
+ __name(validate, "validate");
2878
+ async function put(tx, record6) {
2879
+ const existing = await tx.get(key(record6.id));
2880
+ if (existing && canonicalEndpointJson(existing) !== canonicalEndpointJson(record6)) {
2881
+ throw new EndpointError("A completion cannot be replaced", { code: "COMPLETION_CONFLICT", retryable: false });
2882
+ }
2883
+ if (!existing) await tx.put(key(record6.id), record6);
2884
+ }
2885
+ __name(put, "put");
2886
+ async function all(tx) {
2887
+ const found = [];
2888
+ for (let after; ; ) {
2889
+ const batch = await tx.scan(PREFIX, { ...after ? { after } : {}, limit: 100 });
2890
+ found.push(...batch.map((entry) => entry.value));
2891
+ if (batch.length < 100) return found;
2892
+ after = batch.at(-1).key;
2893
+ }
2894
+ }
2895
+ __name(all, "all");
2896
+ const transaction = /* @__PURE__ */ __name(async (work) => {
2897
+ input.assertOpen();
2898
+ await input.ready();
2899
+ return input.store.transaction(async (tx) => {
2900
+ await input.assertCurrent(tx);
2901
+ return work(tx);
2902
+ });
2903
+ }, "transaction");
2904
+ return {
2905
+ async save(record6) {
2906
+ const frozen = copyJson(record6);
2907
+ validate(frozen);
2908
+ await transaction((tx) => put(tx, frozen));
2909
+ },
2910
+ async get(id2) {
2911
+ input.assertOpen();
2912
+ await input.ready();
2913
+ return input.store.transaction((tx) => tx.get(key(id2)));
2914
+ },
2915
+ /** Oldest first. */
2916
+ async list() {
2917
+ input.assertOpen();
2918
+ await input.ready();
2919
+ const found = await input.store.transaction(all);
2920
+ return found.sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
2921
+ },
2922
+ /** One record for another, in one step: a Stop's cut keeps only what was already queued. */
2923
+ async replace(previousId, next) {
2924
+ const frozen = copyJson(next);
2925
+ validate(frozen);
2926
+ await transaction(async (tx) => {
2927
+ await put(tx, frozen);
2928
+ if (previousId !== frozen.id) await tx.delete(key(previousId));
2929
+ });
2930
+ },
2931
+ /**
2932
+ * The output is durable or withdrawn: its record goes, and in the same step `settle`'s inputs settle. An input already
2933
+ * settled, or no longer in the inbox, is left as it is.
2934
+ */
2935
+ async finish(id2, settle) {
2936
+ await transaction(async (tx) => {
2937
+ for (const inputId of new Set(settle?.inputIds ?? [])) {
2938
+ const current = await tx.get(`inbound/${inputId}`);
2939
+ if (current && current.state !== "settled") await input.settleInbound(tx, inputId, settle.reason);
2940
+ }
2941
+ await tx.delete(key(id2));
2942
+ });
2943
+ },
2944
+ /** The input ids every completion names, read inside the caller's transaction. */
2945
+ async answered(tx) {
2946
+ return new Set((await all(tx)).flatMap((record6) => record6.inputIds));
2947
+ }
2948
+ };
2949
+ }
2950
+ __name(createCompletions, "createCompletions");
2951
+
3187
2952
  // ../endpoint/src/external-inbound.ts
3188
2953
  function createExternalInboundClaims(input) {
3189
2954
  function validate(id2, requestId) {
@@ -3328,13 +3093,6 @@ function createExternalInboundClaims(input) {
3328
3093
  }
3329
3094
  __name(createExternalInboundClaims, "createExternalInboundClaims");
3330
3095
 
3331
- // ../endpoint/src/artifact-context.ts
3332
- var contexts = /* @__PURE__ */ new WeakMap();
3333
- function registerArtifactEndpoint(endpoint, context) {
3334
- contexts.set(endpoint, context);
3335
- }
3336
- __name(registerArtifactEndpoint, "registerArtifactEndpoint");
3337
-
3338
3096
  // ../endpoint/src/receipts.ts
3339
3097
  function endpointReceiptResult(receipt) {
3340
3098
  if (receipt.resultError) throw new EndpointError(receipt.resultError.message, { status: receipt.resultError.status, code: receipt.resultError.code, retryable: receipt.resultError.retryable, operationId: receipt.operationId });
@@ -3365,12 +3123,12 @@ function record(value, required, optional = []) {
3365
3123
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail();
3366
3124
  const descriptors = Object.getOwnPropertyDescriptors(value);
3367
3125
  const keys = Reflect.ownKeys(descriptors);
3368
- if (keys.some((key) => typeof key !== "string" || ![...required, ...optional].includes(key)) || required.some((key) => !Object.hasOwn(descriptors, key))) fail();
3126
+ if (keys.some((key2) => typeof key2 !== "string" || ![...required, ...optional].includes(key2)) || required.some((key2) => !Object.hasOwn(descriptors, key2))) fail();
3369
3127
  const result = {};
3370
- for (const key of keys) {
3371
- const descriptor = descriptors[key];
3128
+ for (const key2 of keys) {
3129
+ const descriptor = descriptors[key2];
3372
3130
  if (!Object.hasOwn(descriptor, "value") || !descriptor.enumerable) fail();
3373
- result[key] = descriptor.value;
3131
+ result[key2] = descriptor.value;
3374
3132
  }
3375
3133
  return result;
3376
3134
  }
@@ -3409,8 +3167,8 @@ function createMlsDevDeviceCoordinator(input) {
3409
3167
  installationId: binding.installationId,
3410
3168
  ...binding.principal.type === "ai_agent" ? { ownershipVersion: binding.principal.ownershipVersion } : {}
3411
3169
  });
3412
- function boundDevice(device, key) {
3413
- if (device.environmentId !== binding.environmentId || !same(device.principal, binding.principal) || device.installationId !== binding.installationId || device.signaturePublicKey !== key) fail("MLS_DEV_DEVICE_BINDING_MISMATCH");
3170
+ function boundDevice(device, key2) {
3171
+ if (device.environmentId !== binding.environmentId || !same(device.principal, binding.principal) || device.installationId !== binding.installationId || device.signaturePublicKey !== key2) fail("MLS_DEV_DEVICE_BINDING_MISMATCH");
3414
3172
  }
3415
3173
  __name(boundDevice, "boundDevice");
3416
3174
  async function load(tx, snapshot) {
@@ -3781,7 +3539,7 @@ __name(same2, "same");
3781
3539
  function parseFlow(value) {
3782
3540
  if (!value || typeof value !== "object" || Array.isArray(value)) fail2();
3783
3541
  const raw = value;
3784
- if (Object.keys(raw).some((key) => !["version", "id", "goal", "phase", "begin", "challenge"].includes(key)) || raw.version !== 1 || typeof raw.id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(raw.id)) fail2();
3542
+ if (Object.keys(raw).some((key2) => !["version", "id", "goal", "phase", "begin", "challenge"].includes(key2)) || raw.version !== 1 || typeof raw.id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(raw.id)) fail2();
3785
3543
  const head = { version: 1, id: raw.id };
3786
3544
  if (raw.goal === "enroll" && raw.phase === "enrollment") return {
3787
3545
  ...head,
@@ -4217,8 +3975,8 @@ __name(fail3, "fail");
4217
3975
  function record2(value, keys) {
4218
3976
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
4219
3977
  const fields = Object.getOwnPropertyDescriptors(value);
4220
- if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value") || !fields[key].enumerable)) fail3();
4221
- return Object.fromEntries(keys.map((key) => [key, fields[key].value]));
3978
+ if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key2) => !Object.hasOwn(fields, key2) || !Object.hasOwn(fields[key2], "value") || !fields[key2].enumerable)) fail3();
3979
+ return Object.fromEntries(keys.map((key2) => [key2, fields[key2].value]));
4222
3980
  }
4223
3981
  __name(record2, "record");
4224
3982
  function createMlsDevKeyPackageService(options, context, store, initialized, readPrepared) {
@@ -4242,9 +4000,9 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
4242
4000
  async function exchange(id2, online, op) {
4243
4001
  const { binding, device } = await initialized();
4244
4002
  if (online) network();
4245
- const key = op.prefix + id2;
4003
+ const key2 = op.prefix + id2;
4246
4004
  const captured = await store.transaction(async (tx) => {
4247
- const current = await device.captureKeyPackagePrerequisite(tx), raw = await tx.get(key);
4005
+ const current = await device.captureKeyPackagePrerequisite(tx), raw = await tx.get(key2);
4248
4006
  if (!online && raw === void 0) return { current, raw, request: void 0, previous: void 0 };
4249
4007
  const request = await op.make(tx, binding, current);
4250
4008
  const previous = raw === void 0 ? void 0 : parseJournal(raw, binding, current, op);
@@ -4266,7 +4024,7 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
4266
4024
  if (current.fingerprint !== captured.current.fingerprint) fail3("MLS_DEV_KEY_PACKAGE_DEVICE_CHANGED", true);
4267
4025
  const claims = parseMlsDevCredentialToken(current.credential.credential).unsigned.claims, now = context.clock();
4268
4026
  if (!Number.isSafeInteger(now) || now < claims.issuedAtMs || now >= claims.expiresAtMs) fail3("MLS_DEV_KEY_PACKAGE_EXPIRED");
4269
- if (!same3(await tx.get(key) ?? null, captured.raw ?? null)) fail3("MLS_DEV_KEY_PACKAGE_STATE_CHANGED", true);
4027
+ if (!same3(await tx.get(key2) ?? null, captured.raw ?? null)) fail3("MLS_DEV_KEY_PACKAGE_STATE_CHANGED", true);
4270
4028
  if (captured.raw === void 0 && (await tx.scan(op.prefix, { limit: 33 })).length >= 32) fail3("MLS_DEV_KEY_PACKAGE_INVENTORY_FULL");
4271
4029
  const request = captured.request ?? fail3();
4272
4030
  await op.validate(tx, binding, current, request, null);
@@ -4277,7 +4035,7 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
4277
4035
  request,
4278
4036
  receipt: captured.previous?.receipt ?? null
4279
4037
  };
4280
- await tx.put(key, value);
4038
+ await tx.put(key2, value);
4281
4039
  return value;
4282
4040
  });
4283
4041
  let receipt;
@@ -4287,7 +4045,7 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
4287
4045
  if (!(error instanceof EndpointError)) fail3("MLS_DEV_KEY_PACKAGE_REQUEST_FAILED", true);
4288
4046
  if (op.forgetsRefusal && attempt.receipt === null && !error.retryable && error.status >= 400 && error.status < 500 && error.code === "HTTP_" + error.status) {
4289
4047
  await store.transaction(async (tx) => {
4290
- if (same3(await tx.get(key) ?? null, attempt)) await tx.delete(key);
4048
+ if (same3(await tx.get(key2) ?? null, attempt)) await tx.delete(key2);
4291
4049
  });
4292
4050
  }
4293
4051
  throw error;
@@ -4297,11 +4055,11 @@ function createMlsDevKeyPackageService(options, context, store, initialized, rea
4297
4055
  return store.transaction(async (tx) => {
4298
4056
  const current = await device.captureKeyPackagePrerequisite(tx);
4299
4057
  if (current.fingerprint !== captured.current.fingerprint) fail3("MLS_DEV_KEY_PACKAGE_DEVICE_CHANGED", true);
4300
- const stored = parseJournal(await tx.get(key), binding, current, op);
4058
+ const stored = parseJournal(await tx.get(key2), binding, current, op);
4301
4059
  if (!same3(stored.request, attempt.request)) fail3("MLS_DEV_KEY_PACKAGE_STATE_CHANGED", true);
4302
4060
  await op.validate(tx, binding, current, attempt.request, receipt);
4303
4061
  if (stored.receipt !== null && !same3(stored.receipt, receipt)) fail3("MLS_DEV_KEY_PACKAGE_RECEIPT_CONFLICT");
4304
- await tx.put(key, { ...stored, receipt });
4062
+ await tx.put(key2, { ...stored, receipt });
4305
4063
  return receipt;
4306
4064
  });
4307
4065
  }
@@ -4449,8 +4207,8 @@ var same4 = /* @__PURE__ */ __name((a, b) => canonicalEndpointJson(a) === canoni
4449
4207
  function record3(value, keys) {
4450
4208
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
4451
4209
  const fields = Object.getOwnPropertyDescriptors(value);
4452
- if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key) => !Object.hasOwn(fields, key) || !Object.hasOwn(fields[key], "value") || !fields[key].enumerable)) fail4();
4453
- return Object.fromEntries(keys.map((key) => [key, fields[key].value]));
4210
+ if (Reflect.ownKeys(fields).length !== keys.length || keys.some((key2) => !Object.hasOwn(fields, key2) || !Object.hasOwn(fields[key2], "value") || !fields[key2].enumerable)) fail4();
4211
+ return Object.fromEntries(keys.map((key2) => [key2, fields[key2].value]));
4454
4212
  }
4455
4213
  __name(record3, "record");
4456
4214
  function boundPayload(command, payload) {
@@ -4948,28 +4706,28 @@ var erased = /* @__PURE__ */ __name((value) => {
4948
4706
  async function deleteMlsDevTextMessage(tx, conversationId, messageId) {
4949
4707
  const tombstone = mlsDevTextDeletedKey(conversationId, messageId), known = await tx.get(tombstone) !== void 0;
4950
4708
  if (!known) await tx.put(tombstone, { version: 1 });
4951
- const key = mlsDevTextMessageKey(conversationId, messageId), value = await tx.get(key);
4709
+ const key2 = mlsDevTextMessageKey(conversationId, messageId), value = await tx.get(key2);
4952
4710
  if (value?.message.kind !== "text" && value?.message.kind !== "item" || value.deleted || value.feedOrdinal === null) return !known;
4953
- await tx.put(key, erased(value));
4711
+ await tx.put(key2, erased(value));
4954
4712
  return true;
4955
4713
  }
4956
4714
  __name(deleteMlsDevTextMessage, "deleteMlsDevTextMessage");
4957
4715
  async function setMlsDevTextReactions(tx, conversationId, messageId, reactions) {
4958
- const key = mlsDevTextReactionsKey(conversationId, messageId), next = reactions && Object.keys(reactions).length ? reactions : void 0;
4959
- const previous = await tx.get(key);
4716
+ const key2 = mlsDevTextReactionsKey(conversationId, messageId), next = reactions && Object.keys(reactions).length ? reactions : void 0;
4717
+ const previous = await tx.get(key2);
4960
4718
  if (JSON.stringify(previous ?? null) === JSON.stringify(next ?? null)) return false;
4961
- if (next) await tx.put(key, next);
4962
- else await tx.delete(key);
4719
+ if (next) await tx.put(key2, next);
4720
+ else await tx.delete(key2);
4963
4721
  return true;
4964
4722
  }
4965
4723
  __name(setMlsDevTextReactions, "setMlsDevTextReactions");
4966
4724
  async function putMlsDevTextMessage(tx, value) {
4967
4725
  if (await tx.get(mlsDevTextDeletedKey(value.message.conversationId, value.message.messageId)) !== void 0) value = erased(value);
4968
4726
  const { conversationId, messageId } = value.message;
4969
- const key = mlsDevTextMessageKey(conversationId, messageId);
4970
- const old = await tx.get(key);
4727
+ const key2 = mlsDevTextMessageKey(conversationId, messageId);
4728
+ const old = await tx.get(key2);
4971
4729
  if (old?.feedOrdinal !== null && old?.feedOrdinal !== void 0 && old.feedOrdinal !== value.feedOrdinal) fail5();
4972
- await tx.put(key, value);
4730
+ await tx.put(key2, value);
4973
4731
  if (value.message.kind !== "text" && value.message.kind !== "item" && value.message.kind !== "interaction.request") return;
4974
4732
  if (value.feedOrdinal === null) await tx.put(`${pendingPrefix(conversationId)}${encoded2(messageId)}`, { messageId });
4975
4733
  else {
@@ -5114,10 +4872,10 @@ async function queryMlsDevTextHistory(tx, conversationId, name, parameters, audi
5114
4872
  __name(queryMlsDevTextHistory, "queryMlsDevTextHistory");
5115
4873
  async function settleMlsDevTextOperation(tx, operation) {
5116
4874
  if (operation.action !== "mls_application" || !operation.conversationId || !operation.messageId || !["rejected", "uncertain"].includes(operation.status)) return;
5117
- const key = mlsDevTextMessageKey(operation.conversationId, operation.messageId);
5118
- const value = await tx.get(key);
4875
+ const key2 = mlsDevTextMessageKey(operation.conversationId, operation.messageId);
4876
+ const value = await tx.get(key2);
5119
4877
  if (value && value.message.operationId === operation.operationId && value.feedOrdinal === null) {
5120
- await tx.put(key, { ...value, status: operation.status });
4878
+ await tx.put(key2, { ...value, status: operation.status });
5121
4879
  }
5122
4880
  }
5123
4881
  __name(settleMlsDevTextOperation, "settleMlsDevTextOperation");
@@ -5135,13 +4893,13 @@ async function opensMlsAlias(environmentId, alias, opening, hash) {
5135
4893
  return false;
5136
4894
  }
5137
4895
  if (!isMlsPrincipalAlias(alias)) return false;
5138
- const key = checkKey(environmentId, alias, value);
5139
- let opens = checked.get(key);
4896
+ const key2 = checkKey(environmentId, alias, value);
4897
+ let opens = checked.get(key2);
5140
4898
  if (opens === void 0) {
5141
4899
  const digest = await hash(encodeMlsPrincipalAliasInput(environmentId, value));
5142
4900
  opens = /^[0-9a-f]{64}$/.test(digest) && MLS_PRINCIPAL_ALIAS_PREFIX + encodeCanonicalMlsBase64Url(hexBytes(digest)) === alias;
5143
4901
  if (checked.size >= 4096) checked.clear();
5144
- checked.set(key, opens);
4902
+ checked.set(key2, opens);
5145
4903
  }
5146
4904
  return opens;
5147
4905
  }
@@ -5246,8 +5004,8 @@ __name(id, "id");
5246
5004
  function record4(value, required, optional = []) {
5247
5005
  if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail6();
5248
5006
  const descriptors = Object.getOwnPropertyDescriptors(value), fields = Reflect.ownKeys(descriptors);
5249
- 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();
5250
- return Object.fromEntries(fields.map((key) => [key, descriptors[key].value]));
5007
+ if (required.some((key2) => !Object.hasOwn(descriptors, key2)) || fields.some((key2) => typeof key2 !== "string" || ![...required, ...optional].includes(key2) || !Object.hasOwn(descriptors[key2], "value") || !descriptors[key2].enumerable)) fail6();
5008
+ return Object.fromEntries(fields.map((key2) => [key2, descriptors[key2].value]));
5251
5009
  }
5252
5010
  __name(record4, "record");
5253
5011
  function envelope(value, binding, conversationId) {
@@ -5386,7 +5144,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
5386
5144
  "mlsRemoval",
5387
5145
  "mlsIdentities",
5388
5146
  "mlsInviteeId"
5389
- ].map((key) => [key, conversation[key] ?? null])) : null;
5147
+ ].map((key2) => [key2, conversation[key2] ?? null])) : null;
5390
5148
  const cursor = await tx.get(`cursor/${conversationId}`);
5391
5149
  const protocol = await tx.get(mlsDevTextProtocolKey(conversationId));
5392
5150
  if (protocol !== void 0 && !isMlsDevTextProtocol(protocol) || current && current.protocol !== protocol) fail6("MLS_DEV_TEXT_PROTOCOL_UNSUPPORTED");
@@ -5782,13 +5540,13 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
5782
5540
  const request = message.interaction, responder = request.responderIds[0];
5783
5541
  if (sender.principal.type !== "ai_agent") return "MLS_DEV_INTERACTION_REQUESTER_INVALID";
5784
5542
  if (responder === sender.principal.id || !current.envelope.roster.some((member) => member.device.principal.type === "human" && member.device.principal.id === responder)) return "MLS_DEV_INTERACTION_RESPONDER_UNAVAILABLE";
5785
- const key2 = mlsDevInteractionKey(cid, sender.principal.id, request.requestId), existing2 = await tx.get(key2);
5543
+ const key3 = mlsDevInteractionKey(cid, sender.principal.id, request.requestId), existing2 = await tx.get(key3);
5786
5544
  if (existing2) {
5787
5545
  const earlier = await tx.get(acceptedKey(cid, existing2.head.sequence));
5788
5546
  const principals = /* @__PURE__ */ __name((roster) => JSON.stringify([...new Set(roster.map((member) => JSON.stringify([member.device.principal.type, member.device.principal.id, member.admissionId])))].sort()), "principals");
5789
5547
  if (existing2.status !== "pending" || existing2.requesterDeviceId !== sender.deviceId || !same6(existing2.request, request) || existing2.membershipRevision !== wire.preconditions.membershipRevision || same6(existing2.head, wire.expectedHead) || earlier === void 0 || principals(parseMlsDevGroupAcceptedControl(earlier).control.roster) !== principals(current.envelope.roster)) return "MLS_DEV_INTERACTION_ID_CONFLICT";
5790
5548
  }
5791
- await tx.put(key2, {
5549
+ await tx.put(key3, {
5792
5550
  version: 1,
5793
5551
  conversationId: cid,
5794
5552
  agentId: sender.principal.id,
@@ -5807,13 +5565,13 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
5807
5565
  if (message.kind === "interaction.response") {
5808
5566
  const response = message.interaction;
5809
5567
  if (sender.principal.type !== "human") return "MLS_DEV_INTERACTION_RESPONDER_INVALID";
5810
- const key2 = mlsDevInteractionKey(cid, response.agentId, response.requestId), existing2 = await tx.get(key2);
5568
+ const key3 = mlsDevInteractionKey(cid, response.agentId, response.requestId), existing2 = await tx.get(key3);
5811
5569
  if (!existing2) return "MLS_DEV_INTERACTION_UNKNOWN";
5812
5570
  if (!existing2.request.responderIds.includes(sender.principal.id)) return "MLS_DEV_INTERACTION_RESPONDER_INVALID";
5813
5571
  if (existing2.status !== "pending") return "MLS_DEV_INTERACTION_CLOSED";
5814
5572
  if (existing2.requestOrdinal >= entry.ordinal || !same6(existing2.head, wire.expectedHead) || existing2.membershipRevision !== wire.preconditions.membershipRevision) return "MLS_DEV_INTERACTION_AUDIENCE_CHANGED";
5815
5573
  if (!isMlsDevInteractionAnswerAllowed(existing2.request, response.answer)) return "MLS_DEV_INTERACTION_ANSWER_INVALID";
5816
- await tx.put(key2, { ...existing2, status: "answered", response: {
5574
+ await tx.put(key3, { ...existing2, status: "answered", response: {
5817
5575
  responderId: sender.principal.id,
5818
5576
  responderDeviceId: sender.deviceId,
5819
5577
  messageId: wire.messageId,
@@ -5824,11 +5582,11 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
5824
5582
  return void 0;
5825
5583
  }
5826
5584
  if (sender.principal.type !== "ai_agent") return "MLS_DEV_INTERACTION_REQUESTER_INVALID";
5827
- const key = mlsDevInteractionKey(cid, sender.principal.id, message.interaction.requestId), existing = await tx.get(key);
5585
+ const key2 = mlsDevInteractionKey(cid, sender.principal.id, message.interaction.requestId), existing = await tx.get(key2);
5828
5586
  if (!existing) return "MLS_DEV_INTERACTION_UNKNOWN";
5829
5587
  if (existing.requesterDeviceId !== sender.deviceId) return "MLS_DEV_INTERACTION_REQUESTER_INVALID";
5830
5588
  if (existing.status !== "pending") return "MLS_DEV_INTERACTION_CLOSED";
5831
- await tx.put(key, { ...existing, status: "withdrawn", withdrawal: {
5589
+ await tx.put(key2, { ...existing, status: "withdrawn", withdrawal: {
5832
5590
  reason: message.interaction.reason,
5833
5591
  messageId: wire.messageId,
5834
5592
  ordinal: entry.ordinal
@@ -5917,7 +5675,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
5917
5675
  url: data.url,
5918
5676
  mimeType: data.mimeType,
5919
5677
  sizeBytes: data.sizeBytes,
5920
- ...Object.fromEntries(["fileName", "width", "height", "durationMs", "thumbnailUrl"].filter((key) => data[key] !== void 0 && data[key] !== null).map((key) => [key, data[key]])),
5678
+ ...Object.fromEntries(["fileName", "width", "height", "durationMs", "thumbnailUrl"].filter((key2) => data[key2] !== void 0 && data[key2] !== null).map((key2) => [key2, data[key2]])),
5921
5679
  ...typeof caption === "string" && caption.trim() ? { caption } : {}
5922
5680
  };
5923
5681
  return validItem(raw);
@@ -5962,7 +5720,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
5962
5720
  const attachments = data.attachments ?? [];
5963
5721
  if (!Array.isArray(attachments) || attachments.length > 1) fail6(Array.isArray(attachments) ? "MLS_DEV_ONE_ITEM_PER_MESSAGE" : "MLS_DEV_TEXT_ONLY");
5964
5722
  const item = attachments.length ? itemInput(attachments[0], data.text) : void 0;
5965
- if (data.contentType !== void 0 && data.contentType !== (item ? item.type : "text") || !item && typeof data.text !== "string" || item && data.text !== void 0 && data.text !== null && typeof data.text !== "string" || data.mentions !== void 0 && (!Array.isArray(data.mentions) || data.mentions.some((mention) => typeof mention !== "string")) || data.replyTo !== void 0 && data.replyTo !== null && typeof data.replyTo !== "string" || ["replyToPosition", "metadata", "forwarded", "contactCardUserId"].some((key) => data[key] !== void 0 && data[key] !== null) || data.replyAuthority !== void 0 && (!!item || !data.replyAuthority || typeof data.replyAuthority !== "object" || Array.isArray(data.replyAuthority))) fail6("MLS_DEV_TEXT_ONLY");
5723
+ if (data.contentType !== void 0 && data.contentType !== (item ? item.type : "text") || !item && typeof data.text !== "string" || item && data.text !== void 0 && data.text !== null && typeof data.text !== "string" || data.mentions !== void 0 && (!Array.isArray(data.mentions) || data.mentions.some((mention) => typeof mention !== "string")) || data.replyTo !== void 0 && data.replyTo !== null && typeof data.replyTo !== "string" || ["replyToPosition", "metadata", "forwarded", "contactCardUserId"].some((key2) => data[key2] !== void 0 && data[key2] !== null) || data.replyAuthority !== void 0 && (!!item || !data.replyAuthority || typeof data.replyAuthority !== "object" || Array.isArray(data.replyAuthority))) fail6("MLS_DEV_TEXT_ONLY");
5966
5724
  const mentions = [...new Set(data.mentions ?? [])].map(id);
5967
5725
  return {
5968
5726
  conversationId: id(data.conversationId),
@@ -6150,14 +5908,14 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6150
5908
  ...input.replyTo ? { replyTo: input.replyTo } : {},
6151
5909
  ...input.mentions ? { mentions: input.mentions } : {}
6152
5910
  };
6153
- const init = await initialized(), key = mlsDevHeldTextKey(cid, messageId);
5911
+ const init = await initialized(), key2 = mlsDevHeldTextKey(cid, messageId);
6154
5912
  const kept = await store.transaction(async (tx) => {
6155
- const existing = await tx.get(key);
5913
+ const existing = await tx.get(key2);
6156
5914
  if (!existing) return await tx.get(`operation/${operationId}`) !== void 0 ? "made" : (await readMlsDevHeldTexts(tx, cid)).some((held) => !held.failed) ? "behind" : "none";
6157
5915
  if (existing.operationId !== operationId || canonicalEndpointJson(existing.body) !== canonicalEndpointJson(body)) fail6("OPERATION_ID_COLLISION");
6158
5916
  if (existing.failed) {
6159
5917
  const { failed: _failed, ...again } = existing;
6160
- await tx.put(key, again);
5918
+ await tx.put(key2, again);
6161
5919
  }
6162
5920
  return "kept";
6163
5921
  });
@@ -6173,7 +5931,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6173
5931
  await store.transaction(async (tx) => {
6174
5932
  const held = await readMlsDevHeldTexts(tx, cid);
6175
5933
  if (held.length >= MLS_DEV_HELD_TEXT_MAX) fail6("MLS_DEV_HELD_TEXT_LIMIT");
6176
- await tx.put(key, {
5934
+ await tx.put(key2, {
6177
5935
  version: 1,
6178
5936
  body,
6179
5937
  operationId,
@@ -6191,20 +5949,20 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6191
5949
  const sent = [], failed = [];
6192
5950
  const kept = (await store.transaction((tx) => readMlsDevHeldTexts(tx, conversationId))).filter((held) => !held.failed);
6193
5951
  for (const held of kept) {
6194
- const key = mlsDevHeldTextKey(conversationId, held.body.messageId);
5952
+ const key2 = mlsDevHeldTextKey(conversationId, held.body.messageId);
6195
5953
  try {
6196
5954
  sent.push(await sendText(textInput(held.body), held.operationId));
6197
5955
  } catch (error) {
6198
5956
  if (error instanceof EndpointError && ["ENDPOINT_CLOSED", "PREPARATION_INSTALLATION_FENCED"].includes(error.code)) throw error;
6199
5957
  if (!(error instanceof EndpointError) || error.retryable) return { sent, failed, waiting: true };
6200
5958
  await store.transaction(async (tx) => {
6201
- if (await tx.get(key) !== void 0) await tx.put(key, { ...held, failed: error.code });
5959
+ if (await tx.get(key2) !== void 0) await tx.put(key2, { ...held, failed: error.code });
6202
5960
  });
6203
5961
  failed.push({ operationId: held.operationId, error });
6204
5962
  bridge.historyChanged?.(conversationId);
6205
5963
  continue;
6206
5964
  }
6207
- await store.transaction((tx) => tx.delete(key));
5965
+ await store.transaction((tx) => tx.delete(key2));
6208
5966
  }
6209
5967
  return { sent, failed, waiting: false };
6210
5968
  }
@@ -6452,7 +6210,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6452
6210
  }
6453
6211
  __name(readInteraction, "readInteraction");
6454
6212
  async function openSecretAnswer(conversationId, requestId) {
6455
- const init = await initialized(), key = mlsDevInteractionKey(conversationId, init.binding.principal.id, requestId);
6213
+ const init = await initialized(), key2 = mlsDevInteractionKey(conversationId, init.binding.principal.id, requestId);
6456
6214
  if (principal.type !== "ai_agent") fail6("MLS_DEV_INTERACTION_REQUESTER_INVALID");
6457
6215
  let captured, known, text;
6458
6216
  await prepareAndCommitContent({
@@ -6463,7 +6221,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6463
6221
  const current = await capture(tx, state, init, conversationId), group = current.envelope;
6464
6222
  if (!mlsDevProtocolCarriesCards(current.protocol) || !group?.head || group.removed) fail6("MLS_DEV_INTERACTION_PROTOCOL_REQUIRED");
6465
6223
  if (group.pending) fail6("MLS_DEV_GROUP_PENDING_CONTROL", true);
6466
- known = await tx.get(key) ?? fail6("MLS_DEV_INTERACTION_UNKNOWN");
6224
+ known = await tx.get(key2) ?? fail6("MLS_DEV_INTERACTION_UNKNOWN");
6467
6225
  if (known.requesterDeviceId !== current.device.registration.device.deviceId || known.request.kind !== "secret") fail6("MLS_DEV_INTERACTION_REQUESTER_INVALID");
6468
6226
  if (known.opened) fail6("MLS_DEV_INTERACTION_OPENED");
6469
6227
  const view3 = viewMlsDevInteraction(known, context.clock(), void 0);
@@ -6486,8 +6244,8 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6486
6244
  }, "prepare"),
6487
6245
  commit: /* @__PURE__ */ __name(async (tx, prepared, state) => {
6488
6246
  const current = await capture(tx, state, init, conversationId);
6489
- if (current.fingerprint !== prepared.fingerprint || !same6(await tx.get(key), prepared.opened)) fail6("MLS_DEV_GROUP_DEPENDENCY_CHANGED", true);
6490
- await tx.put(key, { ...prepared.opened, opened: true });
6247
+ if (current.fingerprint !== prepared.fingerprint || !same6(await tx.get(key2), prepared.opened)) fail6("MLS_DEV_GROUP_DEPENDENCY_CHANGED", true);
6248
+ await tx.put(key2, { ...prepared.opened, opened: true });
6491
6249
  return true;
6492
6250
  }, "commit")
6493
6251
  });
@@ -7158,7 +6916,7 @@ function createEndpoint(options) {
7158
6916
  let retryDue = Number.POSITIVE_INFINITY;
7159
6917
  let negotiatedCapabilities = null;
7160
6918
  const now = /* @__PURE__ */ __name(() => new Date(clock()).toISOString(), "now");
7161
- const key = /* @__PURE__ */ __name((operationId) => `operation/${operationId}`, "key");
6919
+ const key2 = /* @__PURE__ */ __name((operationId) => `operation/${operationId}`, "key");
7162
6920
  function assertOpen() {
7163
6921
  if (closed) throw new EndpointError("Endpoint is closed", { code: "ENDPOINT_CLOSED", retryable: false });
7164
6922
  }
@@ -7736,9 +7494,9 @@ function createEndpoint(options) {
7736
7494
  function rememberStubName(conversationId, stub) {
7737
7495
  const name = typeof stub.senderName === "string" ? stub.senderName.trim() : "";
7738
7496
  if (typeof stub.senderId !== "string" || !stub.senderId || !name || name === stub.senderId || name.length > 80) return;
7739
- const key2 = stubKey(conversationId, stub.id);
7740
- stubNames.delete(key2);
7741
- stubNames.set(key2, { senderId: stub.senderId, name });
7497
+ const key3 = stubKey(conversationId, stub.id);
7498
+ stubNames.delete(key3);
7499
+ stubNames.set(key3, { senderId: stub.senderId, name });
7742
7500
  if (stubNames.size > 1e3) stubNames.delete(stubNames.keys().next().value);
7743
7501
  }
7744
7502
  __name(rememberStubName, "rememberStubName");
@@ -7752,13 +7510,13 @@ function createEndpoint(options) {
7752
7510
  }
7753
7511
  __name(withStubNames, "withStubNames");
7754
7512
  function forgetStubReads(conversationId) {
7755
- for (const key2 of stubReads.keys()) if (key2.startsWith(`${conversationId}\0`)) stubReads.delete(key2);
7513
+ for (const key3 of stubReads.keys()) if (key3.startsWith(`${conversationId}\0`)) stubReads.delete(key3);
7756
7514
  }
7757
7515
  __name(forgetStubReads, "forgetStubReads");
7758
7516
  async function recoverMlsDevMessageActions(conversationId, messageIds) {
7759
7517
  const at = clock(), due = [...new Set(messageIds)].filter((id2) => at >= (stubReads.get(stubKey(conversationId, id2)) ?? -Infinity) + 3e4).slice(0, 100);
7760
7518
  if (!due.length) return false;
7761
- for (const [key2, readAt] of stubReads) if (at >= readAt + 3e4) stubReads.delete(key2);
7519
+ for (const [key3, readAt] of stubReads) if (at >= readAt + 3e4) stubReads.delete(key3);
7762
7520
  for (const id2 of due) stubReads.set(stubKey(conversationId, id2), at);
7763
7521
  const beforeRead = liveActionCounter;
7764
7522
  let stubs;
@@ -7847,6 +7605,51 @@ function createEndpoint(options) {
7847
7605
  }
7848
7606
  }
7849
7607
  __name(refreshMlsRoomFromHint, "refreshMlsRoomFromHint");
7608
+ const roomHints = /* @__PURE__ */ new Map();
7609
+ let roomHintWorker;
7610
+ function queueRoomHint(key3, run, last = false) {
7611
+ if (closed) return Promise.resolve();
7612
+ let hint = roomHints.get(key3);
7613
+ if (hint && last) roomHints.delete(key3);
7614
+ if (!hint) {
7615
+ let settle;
7616
+ const done = new Promise((resolve3, reject) => {
7617
+ settle = /* @__PURE__ */ __name((error) => error === void 0 ? resolve3() : reject(error), "settle");
7618
+ });
7619
+ done.catch(() => void 0);
7620
+ hint = { run, done, settle };
7621
+ }
7622
+ roomHints.set(key3, hint);
7623
+ drainRoomHints();
7624
+ return hint.done;
7625
+ }
7626
+ __name(queueRoomHint, "queueRoomHint");
7627
+ function drainRoomHints() {
7628
+ if (roomHintWorker) return;
7629
+ roomHintWorker = (async () => {
7630
+ for (const [key3, hint] of roomHints) {
7631
+ roomHints.delete(key3);
7632
+ if (closed) {
7633
+ hint.settle();
7634
+ continue;
7635
+ }
7636
+ try {
7637
+ await hint.run();
7638
+ hint.settle();
7639
+ } catch (error) {
7640
+ hint.settle(error);
7641
+ }
7642
+ }
7643
+ })().finally(() => {
7644
+ roomHintWorker = void 0;
7645
+ if (roomHints.size) drainRoomHints();
7646
+ });
7647
+ }
7648
+ __name(drainRoomHints, "drainRoomHints");
7649
+ const hintRoom = /* @__PURE__ */ __name((conversationId) => queueRoomHint(`room/${conversationId}`, async () => {
7650
+ await refreshMlsRoomFromHint(conversationId);
7651
+ await sendDeferredOpeners(conversationId);
7652
+ }), "hintRoom");
7850
7653
  const queryObserver = createEndpointQueryObserver({ query: /* @__PURE__ */ __name((name, parameters) => endpoint.query(name, parameters), "query"), getPollInterval: /* @__PURE__ */ __name(() => events.isConnected() ? Math.max(options.queryIntervalMs ?? 0, 6e4) : options.queryIntervalMs ?? 3e3, "getPollInterval") });
7851
7654
  const invalidateEvent = /* @__PURE__ */ __name((event) => queryObserver.invalidate(event ? (query, parameters) => queryAffectedByEvent(event, query, parameters) : void 0), "invalidateEvent");
7852
7655
  const syncConversation = createConversationSynchronizer({ store, query: /* @__PURE__ */ __name((name, parameters) => endpoint.query(name, parameters), "query"), ready: /* @__PURE__ */ __name(() => initialization, "ready"), clock, invalidate: /* @__PURE__ */ __name((conversationId) => invalidateEvent({ kind: "conversation.changed", data: { conversationId } }), "invalidate"), admitRecoveredMessages: options.admitRecoveredMessages, preparationGeneration: /* @__PURE__ */ __name(() => installationGeneration, "preparationGeneration"), prepareProtocol: /* @__PURE__ */ __name((id2) => refreshMlsRoom(id2, true), "prepareProtocol"), syncProtocol: /* @__PURE__ */ __name(async (id2, opts) => {
@@ -7855,19 +7658,20 @@ function createEndpoint(options) {
7855
7658
  return mlsDevGroups.syncText(id2, opts);
7856
7659
  }, "syncProtocol") });
7857
7660
  const events = createEndpointEventHub({ transport, invalidate: invalidateEvent, onDisconnected: /* @__PURE__ */ __name(() => queryObserver.reschedule(), "onDisconnected"), onEvent: /* @__PURE__ */ __name(async (event) => {
7661
+ const hinted = [];
7858
7662
  if (mlsDevGroups && ["conversation.changed", "conversation.updated", "conversation.removed"].includes(event.kind)) {
7859
7663
  const id2 = event.data.conversationId ?? event.data.id;
7860
7664
  if (typeof id2 === "string" && ENDPOINT_ID_PATTERN.test(id2)) {
7861
7665
  if (event.kind === "conversation.changed" && event.data.projection === void 0) forgetStubReads(id2);
7862
7666
  const unseenRemoval = event.kind === "conversation.removed" && await store.transaction(async (tx) => await tx.get(`conversation/${id2}`) === void 0 && !await isMlsDevRoomProtected(tx, id2));
7863
- if (!unseenRemoval) await refreshMlsRoomFromHint(id2);
7864
- await sendDeferredOpeners(id2);
7667
+ if (!unseenRemoval) hinted.push(hintRoom(id2));
7668
+ else await sendDeferredOpeners(id2);
7865
7669
  }
7866
7670
  }
7867
7671
  if (mlsDevGroups && event.kind === "conversations.changed" && Array.isArray(event.data.conversationIds)) {
7868
7672
  for (const id2 of event.data.conversationIds) {
7869
7673
  if (typeof id2 === "string" && ENDPOINT_ID_PATTERN.test(id2) && await store.transaction((tx) => isMlsDevRoomProtected(tx, id2))) {
7870
- await refreshMlsRoomFromHint(id2);
7674
+ hinted.push(hintRoom(id2));
7871
7675
  }
7872
7676
  }
7873
7677
  }
@@ -7879,13 +7683,16 @@ function createEndpoint(options) {
7879
7683
  if (event.kind === "connected") serverRoomMode = void 0;
7880
7684
  for (let after; ; ) {
7881
7685
  const page = await store.transaction((tx) => tx.scan("mls-dev-group/mode/", { after, limit: 100 }));
7882
- for (const item of page) await refreshMlsRoomFromHint(decodeURIComponent(item.key.slice("mls-dev-group/mode/".length)));
7686
+ for (const item of page) hinted.push(hintRoom(decodeURIComponent(item.key.slice("mls-dev-group/mode/".length))));
7883
7687
  if (page.length < 100) break;
7884
7688
  after = page[page.length - 1].key;
7885
7689
  }
7886
- await sendDeferredOpeners();
7887
- await discoverInviteeRooms().catch(() => void 0);
7690
+ hinted.push(queueRoomHint("after/reconnect", async () => {
7691
+ await sendDeferredOpeners();
7692
+ await discoverInviteeRooms().catch(() => void 0);
7693
+ }, true));
7888
7694
  }
7695
+ if (event.id) await Promise.all(hinted);
7889
7696
  if ((event.kind === "message.updated" || event.kind === "message.deleted") && typeof event.data.conversationId === "string" && typeof event.data.messageId === "string" && ENDPOINT_ID_PATTERN.test(event.data.conversationId)) {
7890
7697
  const room = event.data.conversationId, message = event.data.messageId;
7891
7698
  const applied = await store.transaction(async (tx) => {
@@ -7929,6 +7736,13 @@ function createEndpoint(options) {
7929
7736
  }, "assertReady")
7930
7737
  });
7931
7738
  const externalInbound = createExternalInboundClaims({ store, clock, hash, ownershipVersion: options.identity.ownershipVersion, ready: /* @__PURE__ */ __name(() => initialization, "ready"), assertOpen, assertCurrent: assertCurrentInstallation });
7739
+ const completions = createCompletions({
7740
+ store,
7741
+ ready: /* @__PURE__ */ __name(() => initialization, "ready"),
7742
+ assertOpen,
7743
+ assertCurrent: assertCurrentInstallation,
7744
+ settleInbound: /* @__PURE__ */ __name((tx, id2, reason) => writeInboundState(tx, id2, "settled", reason), "settleInbound")
7745
+ });
7932
7746
  const messageGroups = createMessageGroupCoordinator({
7933
7747
  store,
7934
7748
  endpoint: /* @__PURE__ */ __name(() => endpoint, "endpoint"),
@@ -7939,6 +7753,26 @@ function createEndpoint(options) {
7939
7753
  preparationGeneration: /* @__PURE__ */ __name(() => installationGeneration, "preparationGeneration"),
7940
7754
  supportsPreparedContent: /* @__PURE__ */ __name(() => Promise.resolve(negotiatedCapabilities?.preparedContentVersion === 1), "supportsPreparedContent")
7941
7755
  });
7756
+ async function writeInboundState(tx, id2, state, reason) {
7757
+ const recordKey = `inbound/${id2}`;
7758
+ const current = await tx.get(recordKey);
7759
+ if (!current) throw new EndpointError("Inbound operation was not durably received", { code: "INBOUND_NOT_FOUND", retryable: false });
7760
+ if (current.externalClaim) throw new EndpointError("Use the bound external claim to settle this input", { code: "EXTERNAL_INBOUND_CLAIM_REQUIRED", retryable: false });
7761
+ if (current.handoffGeneration && state !== "settled" && state !== "uncertain") throw new EndpointError("Use the input handoff to release or defer managed work", { code: "INBOUND_HANDOFF_REQUIRED", retryable: false });
7762
+ if (current.deleted && !["settled", "uncertain"].includes(state)) throw new EndpointError("Deleted input cannot be replayed", { code: "INBOUND_DELETED", retryable: false });
7763
+ if (current.state === "settled" && state !== "settled") throw new EndpointError("Settled input cannot be replayed", { code: "INBOUND_ALREADY_SETTLED", retryable: false });
7764
+ const next = { ...current, state, ...reason ? { reason } : {}, updatedAt: now() };
7765
+ delete next.lease;
7766
+ if (state === "settled") {
7767
+ delete next.handoffGeneration;
7768
+ delete next.handoffInstallationGeneration;
7769
+ }
7770
+ if (state === "settled") next.event = { ...next.event, data: {} };
7771
+ await tx.put(recordKey, next);
7772
+ if (state === "settled" || state === "uncertain" || current.deleted) await removeInboundReady(tx, current);
7773
+ else await enqueueInbound(tx, next);
7774
+ }
7775
+ __name(writeInboundState, "writeInboundState");
7942
7776
  function deferredReceipt(operation, error) {
7943
7777
  return operation.receipt ?? {
7944
7778
  protocol: CANON_ENDPOINT_PROTOCOL,
@@ -7972,7 +7806,7 @@ function createEndpoint(options) {
7972
7806
  const index = group?.children.findIndex((child) => child.operationId === next.operationId) ?? -1;
7973
7807
  if (group && index >= 0 && !group.cancelled) {
7974
7808
  for (const reference of group.children.slice(index + 1)) {
7975
- const child = await tx.get(key(reference.operationId));
7809
+ const child = await tx.get(key2(reference.operationId));
7976
7810
  if (!child || messageGroupChildSucceeded(child)) continue;
7977
7811
  if (messageGroupChildSucceeded(next)) {
7978
7812
  if (!terminal(child.status) && child.status !== "uncertain") await enqueueOperation(tx, child);
@@ -7980,7 +7814,7 @@ function createEndpoint(options) {
7980
7814
  }
7981
7815
  if (!child.attempted && !terminal(child.status)) {
7982
7816
  const { request: _plaintext, preparedContent: _sealedPlaintext, ...correlation } = child;
7983
- await tx.put(key(child.operationId), { ...correlation, status: "rejected", updatedAt: now(), receipt: {
7817
+ await tx.put(key2(child.operationId), { ...correlation, status: "rejected", updatedAt: now(), receipt: {
7984
7818
  protocol: CANON_ENDPOINT_PROTOCOL,
7985
7819
  operationId: child.operationId,
7986
7820
  installationId: child.installationId,
@@ -8000,7 +7834,7 @@ function createEndpoint(options) {
8000
7834
  __name(advanceMessageGroup, "advanceMessageGroup");
8001
7835
  async function commitOperationRecord(tx, created) {
8002
7836
  const ordered = await orderOperation(tx, created);
8003
- await tx.put(key(created.operationId), ordered);
7837
+ await tx.put(key2(created.operationId), ordered);
8004
7838
  await trackOperationOrder(tx, ordered);
8005
7839
  await enqueueOperation(tx, ordered);
8006
7840
  if (!created.sensitive) await tx.delete(`preparation/${created.operationId}`);
@@ -8011,7 +7845,7 @@ function createEndpoint(options) {
8011
7845
  if (receipt.operationId !== operation.operationId || receipt.installationId !== operation.installationId || receipt.action !== operation.action) {
8012
7846
  throw new EndpointError("Operation receipt identity mismatch", { status: 502, code: "INVALID_RECEIPT" });
8013
7847
  }
8014
- const current = await tx.get(key(operation.operationId));
7848
+ const current = await tx.get(key2(operation.operationId));
8015
7849
  if (!current) throw new EndpointError("Pending operation was discarded", { code: "OPERATION_DISCARDED", retryable: false });
8016
7850
  const recoveredGroupSuccess = acceptedEcho && ["mls_group_control", "mls_application"].includes(current.action) && receipt.status === "succeeded" && current.status === "rejected";
8017
7851
  if (terminal(current.status) && !recoveredGroupSuccess) return current;
@@ -8026,7 +7860,7 @@ function createEndpoint(options) {
8026
7860
  }
8027
7861
  if (terminal(next.status)) delete next.nextAttemptAt;
8028
7862
  else next.nextAttemptAt = clock() + (typeof receipt.error?.retryAfterMs === "number" && Number.isFinite(receipt.error.retryAfterMs) ? Math.max(1e3, Math.min(3e5, receipt.error.retryAfterMs)) : 1e3);
8029
- await tx.put(key(operation.operationId), next);
7863
+ await tx.put(key2(operation.operationId), next);
8030
7864
  await settleMlsDevTextOperation(tx, next);
8031
7865
  if (next.action === "delete_message" && next.status === "succeeded" && next.conversationId && next.messageId && await isMlsDevRoomProtected(tx, next.conversationId)) await applyMlsDevDeletion(tx, next.conversationId, next.messageId);
8032
7866
  if (terminal(next.status) || next.status === "uncertain") {
@@ -8070,7 +7904,7 @@ function createEndpoint(options) {
8070
7904
  assertOpen();
8071
7905
  await initialization;
8072
7906
  const { operation, acquired } = await store.transaction(async (tx) => {
8073
- const current = await tx.get(key(operationId));
7907
+ const current = await tx.get(key2(operationId));
8074
7908
  if (!current) throw new EndpointError("Unknown endpoint operation", { status: 404, code: "OPERATION_NOT_FOUND", retryable: false });
8075
7909
  if (terminal(current.status)) return { operation: current, acquired: false };
8076
7910
  await assertCurrentInstallation(tx);
@@ -8079,7 +7913,7 @@ function createEndpoint(options) {
8079
7913
  if (!reconcileOnly && current.messageGroupId && current.status !== "uncertain") {
8080
7914
  const group = await tx.get(`message-group/${current.messageGroupId}`);
8081
7915
  const index = group?.children.findIndex((child) => child.operationId === operationId) ?? -1;
8082
- const previous = group && index > 0 ? await tx.get(key(group.children[index - 1].operationId)) : void 0;
7916
+ const previous = group && index > 0 ? await tx.get(key2(group.children[index - 1].operationId)) : void 0;
8083
7917
  if (previous?.status === "rejected" || previous?.status === "succeeded" && !messageGroupChildSucceeded(previous)) {
8084
7918
  const rejected = { ...current, status: "rejected", updatedAt: now(), receipt: {
8085
7919
  protocol: CANON_ENDPOINT_PROTOCOL,
@@ -8093,7 +7927,7 @@ function createEndpoint(options) {
8093
7927
  } };
8094
7928
  delete rejected.request;
8095
7929
  delete rejected.preparedContent;
8096
- await tx.put(key(operationId), rejected);
7930
+ await tx.put(key2(operationId), rejected);
8097
7931
  await removeOperationOutbox(tx, rejected);
8098
7932
  await settleOperationOrder(tx, rejected);
8099
7933
  return { operation: rejected, acquired: false };
@@ -8102,7 +7936,7 @@ function createEndpoint(options) {
8102
7936
  }
8103
7937
  if (current.lease && current.lease.owner !== owner && current.lease.expiresAt > clock()) return { operation: current, acquired: false };
8104
7938
  const next = { ...current, status: reconcileOnly ? current.status : current.status === "uncertain" ? "uncertain" : "sending", lease: { owner, expiresAt: clock() + leaseMs }, attempts: current.attempts + 1, updatedAt: now() };
8105
- await tx.put(key(operationId), next);
7939
+ await tx.put(key2(operationId), next);
8106
7940
  return { operation: next, acquired: true };
8107
7941
  });
8108
7942
  if (terminal(operation.status) && operation.receipt) {
@@ -8120,8 +7954,8 @@ function createEndpoint(options) {
8120
7954
  notify(operation);
8121
7955
  const heartbeat = setInterval(() => {
8122
7956
  void store.transaction(async (tx) => {
8123
- const current = await tx.get(key(operationId));
8124
- if (current?.lease?.owner === owner) await tx.put(key(operationId), { ...current, lease: { owner, expiresAt: clock() + leaseMs } });
7957
+ const current = await tx.get(key2(operationId));
7958
+ if (current?.lease?.owner === owner) await tx.put(key2(operationId), { ...current, lease: { owner, expiresAt: clock() + leaseMs } });
8125
7959
  }).catch(() => void 0);
8126
7960
  }, Math.floor(leaseMs / 3));
8127
7961
  heartbeat.unref?.();
@@ -8143,11 +7977,11 @@ function createEndpoint(options) {
8143
7977
  const receipt2 = found?.status === "succeeded" || found?.status === "rejected" ? found : { ...deferredReceipt(operation), status: operation.status === "uncertain" ? "uncertain" : "pending" };
8144
7978
  if (reconcileOnly && !terminal(receipt2.status)) {
8145
7979
  await store.transaction(async (tx) => {
8146
- const current = await tx.get(key(operationId));
7980
+ const current = await tx.get(key2(operationId));
8147
7981
  if (current?.lease?.owner === owner) {
8148
7982
  const next = { ...current };
8149
7983
  delete next.lease;
8150
- await tx.put(key(operationId), next);
7984
+ await tx.put(key2(operationId), next);
8151
7985
  }
8152
7986
  });
8153
7987
  return receipt2;
@@ -8199,7 +8033,7 @@ function createEndpoint(options) {
8199
8033
  });
8200
8034
  checkingContentMode = false;
8201
8035
  await store.transaction(async (tx) => {
8202
- const current = await tx.get(key(operationId));
8036
+ const current = await tx.get(key2(operationId));
8203
8037
  if (!current || current.lease?.owner !== owner || terminal(current.status)) throw new EndpointError("Delivery ownership changed", { code: "DELIVERY_FENCED" });
8204
8038
  await assertCurrentInstallation(tx);
8205
8039
  if (current.preparedContent && (await tx.get(PREPARATION_GENERATION_KEY) ?? 0) !== current.preparedContent.expected.installationGeneration) throw new EndpointError("This endpoint installation can no longer dispatch its prepared content", { code: "PREPARATION_INSTALLATION_FENCED", retryable: false });
@@ -8217,7 +8051,7 @@ function createEndpoint(options) {
8217
8051
  const group = await tx.get(`message-group/${current.messageGroupId}`);
8218
8052
  if (group?.cancelled || !messageGroups.allowed(current.messageGroupId)) throw new EndpointError("Message group was cancelled", { code: "OPERATION_DISCARDED", retryable: false });
8219
8053
  }
8220
- await tx.put(key(operationId), { ...current, attempted: true });
8054
+ await tx.put(key2(operationId), { ...current, attempted: true });
8221
8055
  });
8222
8056
  const receipt = await transport.execute(request, controller.signal);
8223
8057
  await persistReceipt(operation, receipt);
@@ -8226,17 +8060,17 @@ function createEndpoint(options) {
8226
8060
  const failure = safeError(error);
8227
8061
  if (reconcileOnly && failure.retryable) {
8228
8062
  await store.transaction(async (tx) => {
8229
- const current = await tx.get(key(operationId));
8063
+ const current = await tx.get(key2(operationId));
8230
8064
  if (current?.lease?.owner === owner) {
8231
8065
  const next = { ...current };
8232
8066
  delete next.lease;
8233
- await tx.put(key(operationId), next);
8067
+ await tx.put(key2(operationId), next);
8234
8068
  }
8235
8069
  });
8236
8070
  return deferredReceipt(operation, failure);
8237
8071
  }
8238
8072
  const updated = await store.transaction(async (tx) => {
8239
- const current = await tx.get(key(operationId));
8073
+ const current = await tx.get(key2(operationId));
8240
8074
  if (!current) throw failure;
8241
8075
  if (current.lease?.owner !== owner || terminal(current.status)) return current;
8242
8076
  const lookupUncertain = resolvingReceipt && !failure.retryable;
@@ -8263,7 +8097,7 @@ function createEndpoint(options) {
8263
8097
  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() };
8264
8098
  delete next.nextAttemptAt;
8265
8099
  }
8266
- await tx.put(key(operationId), next);
8100
+ await tx.put(key2(operationId), next);
8267
8101
  await settleMlsDevTextOperation(tx, next);
8268
8102
  if (terminal(next.status) || next.status === "uncertain") {
8269
8103
  await removeOperationOutbox(tx, next);
@@ -8336,7 +8170,7 @@ function createEndpoint(options) {
8336
8170
  const inputFingerprint = sensitive ? void 0 : await hash(canonicalEndpointJson({ ...authored, ...identity.ownershipVersion !== void 0 ? { ownershipVersion: identity.ownershipVersion } : {} }));
8337
8171
  const previous = await store.transaction(async (tx) => {
8338
8172
  await assertCurrentInstallation(tx);
8339
- return tx.get(key(operationId));
8173
+ return tx.get(key2(operationId));
8340
8174
  });
8341
8175
  if (previous?.inputFingerprint !== void 0 && !sensitive) {
8342
8176
  if (previous.inputFingerprint !== inputFingerprint || previous.guardId !== executeOptions.guardId) {
@@ -8401,7 +8235,7 @@ function createEndpoint(options) {
8401
8235
  ...normalized.preconditions ? { preconditions: normalized.preconditions } : {}
8402
8236
  };
8403
8237
  const existingOperation = /* @__PURE__ */ __name(async (tx) => {
8404
- const existing = await tx.get(key(operationId));
8238
+ const existing = await tx.get(key2(operationId));
8405
8239
  if (!existing) return void 0;
8406
8240
  if (existing.fingerprint !== fingerprint || existing.sensitive !== sensitive || existing.guardId !== executeOptions.guardId) {
8407
8241
  throw new EndpointError("Operation ID was already used for different content", { status: 409, code: "OPERATION_ID_COLLISION", retryable: false, operationId });
@@ -8429,7 +8263,6 @@ function createEndpoint(options) {
8429
8263
  ...endpointIntentConversationId(normalized) ? { conversationId: endpointIntentConversationId(normalized) } : {},
8430
8264
  ...typeof routedBody.messageId === "string" ? { messageId: routedBody.messageId } : {},
8431
8265
  ...normalized.action === "session_control" && typeof routedBody.agentId === "string" ? { agentId: routedBody.agentId } : {},
8432
- ...endpointIntentOrdering(normalized) === "artifact" && typeof routedBody.artifactId === "string" ? { artifactId: routedBody.artifactId } : {},
8433
8266
  sensitive,
8434
8267
  status: "queued",
8435
8268
  attempts: 0,
@@ -8495,12 +8328,12 @@ function createEndpoint(options) {
8495
8328
  let dispatched = 0;
8496
8329
  let hasDeferred = false;
8497
8330
  const blockedKeys = /* @__PURE__ */ new Set();
8498
- for (const ordering of ["independent", "signal", "message", "provider", "artifact", "conversation"]) {
8331
+ for (const ordering of ["independent", "signal", "message", "provider", "conversation"]) {
8499
8332
  let after;
8500
8333
  do {
8501
8334
  const batch = await store.transaction(async (tx) => {
8502
8335
  const entries2 = await tx.scan("outbox/", { after, limit: 100 });
8503
- return Promise.all(entries2.map(async (entry) => ({ key: entry.key, value: await tx.get(key(entry.value.operationId)) })));
8336
+ return Promise.all(entries2.map(async (entry) => ({ key: entry.key, value: await tx.get(key2(entry.value.operationId)) })));
8504
8337
  });
8505
8338
  for (const entry of batch) {
8506
8339
  after = entry.key;
@@ -8536,7 +8369,7 @@ function createEndpoint(options) {
8536
8369
  },
8537
8370
  async getOperation(operationId) {
8538
8371
  await initialization;
8539
- return store.transaction((tx) => tx.get(key(operationId)));
8372
+ return store.transaction((tx) => tx.get(key2(operationId)));
8540
8373
  },
8541
8374
  waitForOperation(operationId, waitOptions = {}) {
8542
8375
  return new Promise((resolve3, reject) => {
@@ -8671,9 +8504,9 @@ function createEndpoint(options) {
8671
8504
  },
8672
8505
  refreshQueries: /* @__PURE__ */ __name(() => queryObserver.invalidate(), "refreshQueries"),
8673
8506
  observeEvents: events.observe,
8674
- createMediaPipeline(bytes, mediaOptions) {
8507
+ createMediaPipeline(bytes) {
8675
8508
  assertOpen();
8676
- const pipeline3 = createEndpointMediaPipeline({ endpoint, store, bytes, hash, ids, clock, background: mediaOptions?.background });
8509
+ const pipeline3 = createEndpointMediaPipeline({ endpoint, store, bytes, hash, ids, clock });
8677
8510
  mediaPipelines2.add(pipeline3);
8678
8511
  return pipeline3;
8679
8512
  },
@@ -8714,7 +8547,7 @@ function createEndpoint(options) {
8714
8547
  const recordKey = `${openerPrefix()}/${encodeURIComponent(input.messageId)}`;
8715
8548
  await store.transaction(async (tx) => {
8716
8549
  await assertCurrentInstallation(tx);
8717
- const operation = await tx.get(key(operationId));
8550
+ const operation = await tx.get(key2(operationId));
8718
8551
  if (operation) {
8719
8552
  if (operation.openerFingerprint !== openerFingerprint)
8720
8553
  throw new EndpointError("Opening line ID was already used for different content", { status: 409, code: "OPERATION_ID_COLLISION", retryable: false, operationId });
@@ -8735,7 +8568,7 @@ function createEndpoint(options) {
8735
8568
  });
8736
8569
  try {
8737
8570
  const result = await endpoint.executeResult({ action: "start_conversation", body: { ...body } }, { operationId });
8738
- if (result.status === "unavailable" && result.reason === "opening-text-required") {
8571
+ if (result.status === "unavailable" && (result.reason === "opening-text-required" || result.reason === "encrypted-room-required")) {
8739
8572
  await store.transaction(async (tx) => {
8740
8573
  await assertCurrentInstallation(tx);
8741
8574
  await tx.delete(recordKey);
@@ -8906,29 +8739,27 @@ function createEndpoint(options) {
8906
8739
  }
8907
8740
  }
8908
8741
  await initialization;
8909
- await store.transaction(async (tx) => {
8910
- const recordKey = `inbound/${id2}`;
8911
- const current = await tx.get(recordKey);
8912
- if (!current) throw new EndpointError("Inbound operation was not durably received", { code: "INBOUND_NOT_FOUND", retryable: false });
8913
- if (current.externalClaim) throw new EndpointError("Use the bound external claim to settle this input", { code: "EXTERNAL_INBOUND_CLAIM_REQUIRED", retryable: false });
8914
- if (current.handoffGeneration && state !== "settled" && state !== "uncertain") throw new EndpointError("Use the input handoff to release or defer managed work", { code: "INBOUND_HANDOFF_REQUIRED", retryable: false });
8915
- if (current.deleted && !["settled", "uncertain"].includes(state)) throw new EndpointError("Deleted input cannot be replayed", { code: "INBOUND_DELETED", retryable: false });
8916
- if (current.state === "settled" && state !== "settled") throw new EndpointError("Settled input cannot be replayed", { code: "INBOUND_ALREADY_SETTLED", retryable: false });
8917
- const next = { ...current, state, ...reason ? { reason } : {}, updatedAt: now() };
8918
- delete next.lease;
8919
- if (state === "settled") {
8920
- delete next.handoffGeneration;
8921
- delete next.handoffInstallationGeneration;
8922
- }
8923
- if (state === "settled") next.event = { ...next.event, data: {} };
8924
- await tx.put(recordKey, next);
8925
- if (state === "settled" || state === "uncertain" || current.deleted) await removeInboundReady(tx, current);
8926
- else await enqueueInbound(tx, next);
8927
- });
8742
+ await store.transaction((tx) => writeInboundState(tx, id2, state, reason));
8928
8743
  },
8929
8744
  async listInbound(listOptions = {}) {
8930
8745
  await initialization;
8931
- return store.transaction((tx) => matchingRecords(tx, "inbound/", listOptions, (entry) => !listOptions.states || listOptions.states.includes(entry.state)));
8746
+ return store.transaction(async (tx) => {
8747
+ const answered = listOptions.unanswered ? await completions.answered(tx) : void 0;
8748
+ return matchingRecords(tx, "inbound/", listOptions, (entry) => (!listOptions.states || listOptions.states.includes(entry.state)) && !answered?.has(entry.event.id));
8749
+ });
8750
+ },
8751
+ saveCompletion: completions.save,
8752
+ getCompletion: completions.get,
8753
+ listCompletions: completions.list,
8754
+ replaceCompletion: completions.replace,
8755
+ async finishCompletion(id2, settle) {
8756
+ for (const inputId of new Set(settle?.inputIds ?? [])) {
8757
+ const managed = handoffs.settle(inputId, settle.reason);
8758
+ if (managed && !await managed && (await endpoint.getInbound(inputId))?.state !== "settled") {
8759
+ throw new EndpointError("Input ownership changed before reconciliation", { code: "INBOUND_HANDOFF_LOST", retryable: false });
8760
+ }
8761
+ }
8762
+ await completions.finish(id2, settle);
8932
8763
  },
8933
8764
  async discardPending() {
8934
8765
  await initialization;
@@ -8979,7 +8810,7 @@ function createEndpoint(options) {
8979
8810
  listeners.clear();
8980
8811
  closing = (async () => {
8981
8812
  await Promise.allSettled([handoffs.close(), recovery.close(), syncConversation.close(), events.close(), ...[...consumers].map((consumer) => consumer.close()), ...[...mediaPipelines2].map((pipeline3) => pipeline3.close({ discard: true }))]);
8982
- await Promise.allSettled([...running.values(), ...reads.values(), ...deviceRuns, initialization]);
8813
+ await Promise.allSettled([...running.values(), ...reads.values(), ...deviceRuns, initialization, roomHintWorker]);
8983
8814
  await endpoint.discardPending();
8984
8815
  await eraseEndpointPlaintext(store);
8985
8816
  await store.close();
@@ -9002,11 +8833,10 @@ function createEndpoint(options) {
9002
8833
  sensitiveRequests.clear();
9003
8834
  sensitiveFingerprints.clear();
9004
8835
  listeners.clear();
9005
- closing = Promise.allSettled([handoffs.close(), recovery.close(), syncConversation.close(), events.close(), ...[...consumers].map((consumer) => consumer.close()), ...[...mediaPipelines2].map((pipeline3) => pipeline3.close())]).then(() => Promise.allSettled([...running.values(), ...reads.values(), ...deviceRuns, initialization])).then(() => store.close());
8836
+ closing = Promise.allSettled([handoffs.close(), recovery.close(), syncConversation.close(), events.close(), ...[...consumers].map((consumer) => consumer.close()), ...[...mediaPipelines2].map((pipeline3) => pipeline3.close())]).then(() => Promise.allSettled([...running.values(), ...reads.values(), ...deviceRuns, initialization, roomHintWorker])).then(() => store.close());
9006
8837
  return closing;
9007
8838
  }
9008
8839
  };
9009
- registerArtifactEndpoint(endpoint, { store, closed: /* @__PURE__ */ __name(() => closed, "closed"), assertCurrent: assertCurrentInstallation });
9010
8840
  void initialization.then(() => scheduleRetry(0)).catch(() => void 0);
9011
8841
  void initialization.then(() => sendDeferredOpeners()).catch(() => void 0);
9012
8842
  if (mlsDevGroups) void initialization.then(() => resumeHeldTexts()).catch(() => void 0);
@@ -9064,7 +8894,7 @@ async function acquireAgentEndpoint(input) {
9064
8894
  }
9065
8895
  const requestedMls = input.options.mlsDev;
9066
8896
  const mls = requestedMls ? await (async () => {
9067
- const crypto2 = await import("./mls-crypto-NZVMSUFU.js");
8897
+ const crypto2 = await import("./mls-crypto-ZBEC26B5.js");
9068
8898
  const configuration = crypto2.parseMlsDevClientConfiguration(requestedMls.configuration);
9069
8899
  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");
9070
8900
  const custody = requestedMls.custody;
@@ -9098,12 +8928,12 @@ async function acquireAgentEndpoint(input) {
9098
8928
  ${baseUrl}
9099
8929
  ${input.principalId}
9100
8930
  ${input.ownershipVersion}`).digest("hex");
9101
- const key = `${directory}
8931
+ const key2 = `${directory}
9102
8932
  ${namespace}
9103
8933
  ${input.options.autoDrain === false ? "operator" : "live"}`;
9104
- let entry = endpoints.get(key);
8934
+ let entry = endpoints.get(key2);
9105
8935
  if (entry && entry.installationId !== input.options.installationId) throw new Error("Canon endpoint is already open under another installation.");
9106
- if (entry && (!!entry.mlsDev !== !!mls || mls && (entry.mlsDev?.fingerprint !== mls.fingerprint || entry.mlsDev.custody !== mls.custody || Object.keys(mls.callbacks).some((key2) => entry.mlsDev.callbacks[key2] !== mls.callbacks[key2])))) {
8936
+ if (entry && (!!entry.mlsDev !== !!mls || mls && (entry.mlsDev?.fingerprint !== mls.fingerprint || entry.mlsDev.custody !== mls.custody || Object.keys(mls.callbacks).some((key3) => entry.mlsDev.callbacks[key3] !== mls.callbacks[key3])))) {
9107
8937
  throw new Error("MLS_DEV_AGENT_SHARED_CONFIGURATION_MISMATCH");
9108
8938
  }
9109
8939
  if (!entry) {
@@ -9143,9 +8973,9 @@ ${input.options.autoDrain === false ? "operator" : "live"}`;
9143
8973
  autoDrain: input.options.autoDrain
9144
8974
  }));
9145
8975
  entry = shared;
9146
- endpoints.set(key, entry);
8976
+ endpoints.set(key2, entry);
9147
8977
  void entry.endpoint.catch(() => {
9148
- if (endpoints.get(key) === entry) endpoints.delete(key);
8978
+ if (endpoints.get(key2) === entry) endpoints.delete(key2);
9149
8979
  });
9150
8980
  }
9151
8981
  entry.token = input.apiKey;
@@ -9162,8 +8992,8 @@ ${input.options.autoDrain === false ? "operator" : "live"}`;
9162
8992
  if (released) return;
9163
8993
  released = true;
9164
8994
  entry.references--;
9165
- if (entry.references === 0 && endpoints.get(key) === entry) {
9166
- endpoints.delete(key);
8995
+ if (entry.references === 0 && endpoints.get(key2) === entry) {
8996
+ endpoints.delete(key2);
9167
8997
  await endpoint.close();
9168
8998
  }
9169
8999
  } };
@@ -9395,42 +9225,6 @@ var CanonClient = class {
9395
9225
  delivery?.operationId ?? (options?.messageId ? stableOperationId("send_message", conversationId, options.messageId) : void 0)
9396
9226
  );
9397
9227
  }
9398
- /** Inspect a trusted reply's durable delivery without rebuilding its body. */
9399
- async getMessageOperation(conversationId, messageId, delivery) {
9400
- const endpoint = await this.getEndpoint();
9401
- const group = await endpoint.getMessageGroup(delivery?.operationId ?? stableOperationId("send_group", conversationId, messageId));
9402
- if (group) return group;
9403
- const operation = await endpoint.getOperation(delivery?.operationId ?? stableOperationId("send_message", conversationId, messageId));
9404
- if (operation) return operation;
9405
- const firstPart = `${messageId.slice(0, 153)}-part-1`;
9406
- if (await endpoint.getOperation(delivery?.operationId ? `${delivery.operationId.slice(0, 153)}-part-1` : stableOperationId("send_message", conversationId, firstPart))) {
9407
- throw new CanonApiError(409, JSON.stringify({ code: "OPERATION_UNCERTAIN", message: "Existing message parts require aggregate delivery reconciliation" }));
9408
- }
9409
- return void 0;
9410
- }
9411
- /** Core's chunker records all ordered message intents before publishing any part. */
9412
- async prepareMessageGroup(conversationId, messageId, parts, delivery) {
9413
- const id2 = delivery?.operationId ?? stableOperationId("send_group", conversationId, messageId);
9414
- try {
9415
- const endpoint = await this.getEndpoint();
9416
- const group = await endpoint.registerMessageGroup({ id: id2, ...delivery?.replacesOperationId ? { replacesOperationId: delivery.replacesOperationId } : {}, children: parts.map((part, index) => ({
9417
- operationId: delivery?.operationId ? `${delivery.operationId.slice(0, 160 - `-part-${index + 1}`.length)}-part-${index + 1}` : stableOperationId("send_message", conversationId, part.options.messageId),
9418
- intent: { action: "send_message", body: { conversationId, text: part.text, ...part.options } }
9419
- })) }, { signal: delivery?.signal });
9420
- return { id: id2, sendPart: /* @__PURE__ */ __name(async (index, options) => {
9421
- const operationId = group.childOperationIds?.[index];
9422
- if (!operationId) throw new Error("Unknown message group part");
9423
- if (options?.signal?.aborted) throw new CanonApiError(409, JSON.stringify({ code: "OPERATION_PENDING", message: "Reply delivery was interrupted" }));
9424
- await endpoint.retry(operationId);
9425
- return this.waitForOperation(operationId, options);
9426
- }, "sendPart") };
9427
- } catch (error) {
9428
- throw asCanonApiError(error);
9429
- }
9430
- }
9431
- async cancelMessageGroup(id2) {
9432
- await (await this.getEndpoint()).cancelMessageGroup(id2);
9433
- }
9434
9228
  /**
9435
9229
  * Execute a canonical verb over the wire contract (canon.verb-wire.v1).
9436
9230
  * Bindings build the request with projectVerbIntentToWire; the endpoint
@@ -9674,27 +9468,18 @@ var CanonClient = class {
9674
9468
  async deleteMessage(conversationId, messageId) {
9675
9469
  await this.endpointAction("delete_message", { conversationId, messageId });
9676
9470
  }
9677
- async updateMessageDisposition(conversationId, messageId, inboundDisposition) {
9678
- await this.endpointAction("update_message_disposition", { conversationId, messageId, inboundDisposition });
9679
- }
9680
9471
  async markAsRead(conversationId) {
9681
9472
  await this.endpointAction("mark_read", { conversationId });
9682
9473
  }
9683
9474
  async react(conversationId, messageId, emoji) {
9684
9475
  await this.endpointAction("react", { conversationId, messageId, emoji });
9685
9476
  }
9686
- async setStreaming(options) {
9687
- await this.endpointPublish("publish_streaming", { ...options });
9688
- }
9689
9477
  async clearStreaming(conversationId) {
9690
9478
  await this.endpointPublish("publish_streaming", { conversationId, streaming: false });
9691
9479
  }
9692
9480
  async setRuntimeTurn(options) {
9693
9481
  await this.endpointPublish("publish_turn", { ...options });
9694
9482
  }
9695
- async setTyping(conversationId, typing, status) {
9696
- await this.endpointPublish("publish_typing", { conversationId, typing, ...status ? { status } : {} });
9697
- }
9698
9483
  async peekRuntimeSignal(conversationId) {
9699
9484
  return this.endpointQuery("peek_runtime_signal", { conversationId });
9700
9485
  }
@@ -9736,38 +9521,6 @@ var CanonClient = class {
9736
9521
  async patchRuntimeStreaming(conversationId, patch) {
9737
9522
  await this.endpointPublish("publish_streaming", { ...patch, conversationId });
9738
9523
  }
9739
- async createRuntimeInputRequest(options) {
9740
- return this.endpointAction("create_interaction", { ...options, interactionKind: "input" });
9741
- }
9742
- async consumeRuntimeInputResponse(options) {
9743
- return this.endpointAction("consume_interaction", { ...options, interactionKind: "input" });
9744
- }
9745
- async createRuntimeApprovalRequest(options) {
9746
- return this.endpointAction("create_interaction", { ...options, interactionKind: "approval" });
9747
- }
9748
- async consumeRuntimeApprovalResponse(options) {
9749
- return this.endpointAction("consume_interaction", { ...options, interactionKind: "approval" });
9750
- }
9751
- async createRuntimeCardRequest(options) {
9752
- return this.endpointAction("create_interaction", { ...options, interactionKind: "card" });
9753
- }
9754
- async consumeRuntimeCardResponse(options) {
9755
- return this.endpointAction("consume_interaction", { ...options, interactionKind: "card" });
9756
- }
9757
- /**
9758
- * Create a server-owned plan-approval interaction.
9759
- *
9760
- * Seeds the pending node + attention + runtime-card state and authors the
9761
- * `plan_approval` card message, mirroring the other `createRuntime*Request`
9762
- * spine methods. Hosts resolve it through `consumeRuntimePlanResponse()` so
9763
- * the canonical pending state, rather than a timeline receipt, is authoritative.
9764
- */
9765
- async createRuntimePlanRequest(options) {
9766
- return this.endpointAction("create_interaction", { ...options, interactionKind: "plan" });
9767
- }
9768
- async consumeRuntimePlanResponse(options) {
9769
- return this.endpointAction("consume_interaction", { ...options, interactionKind: "plan" });
9770
- }
9771
9524
  async updateRuntimeStatus(options) {
9772
9525
  await this.endpointPublish("publish_runtime_status", { ...options });
9773
9526
  }
@@ -10027,7 +9780,7 @@ async function resumeRegistration(options) {
10027
9780
  };
10028
9781
  let session = await store.load();
10029
9782
  if (session) {
10030
- if (Object.entries(binding).some(([key, value]) => session[key] !== value)) {
9783
+ if (Object.entries(binding).some(([key2, value]) => session[key2] !== value)) {
10031
9784
  throw new Error("Pending registration belongs to another Canon environment or endpoint set");
10032
9785
  }
10033
9786
  if (session.ownerPhone && session.ownerPhone !== input.ownerPhone || input.requestedAgentId && input.requestedAgentId !== session.requestedAgentId || input.localRegistrationId && input.localRegistrationId !== session.localRegistrationId) {
@@ -10440,7 +10193,7 @@ function normalizePendingRegistration(name, value) {
10440
10193
  "createdAt",
10441
10194
  "updatedAt"
10442
10195
  ];
10443
- if (requiredStrings.some((field) => typeof pending[field] !== "string" || !pending[field]) || pending.profile !== name || pending.requestId !== void 0 && typeof pending.requestId !== "string" || pending.pollToken !== void 0 && typeof pending.pollToken !== "string" || pending.clientType !== void 0 && typeof pending.clientType !== "string" || ["ownerPhone", "requestedAgentId", "credentialAgentId"].some((key) => pending[key] !== void 0 && typeof pending[key] !== "string")) {
10196
+ if (requiredStrings.some((field) => typeof pending[field] !== "string" || !pending[field]) || pending.profile !== name || pending.requestId !== void 0 && typeof pending.requestId !== "string" || pending.pollToken !== void 0 && typeof pending.pollToken !== "string" || pending.clientType !== void 0 && typeof pending.clientType !== "string" || ["ownerPhone", "requestedAgentId", "credentialAgentId"].some((key2) => pending[key2] !== void 0 && typeof pending[key2] !== "string")) {
10444
10197
  throw new Error(
10445
10198
  `${PENDING_REGISTRATIONS_PATH} entry "${name}" must include a complete environment binding`
10446
10199
  );
@@ -10521,7 +10274,7 @@ async function ensureAgentProfile(options) {
10521
10274
  const existing = loadProfiles()[profile];
10522
10275
  const pending = loadPendingRegistrations()[profile];
10523
10276
  if (!pending && existing && !options.reconnect) {
10524
- if (Object.entries(binding).some(([key, value]) => existing[key] !== value) || input.requestedAgentId && input.requestedAgentId !== existing.agentId) {
10277
+ if (Object.entries(binding).some(([key2, value]) => existing[key2] !== value) || input.requestedAgentId && input.requestedAgentId !== existing.agentId) {
10525
10278
  throw new Error("Saved Canon profile belongs to another environment or agent identity");
10526
10279
  }
10527
10280
  await verifyCanonRuntimeConnection(connection);
@@ -10547,7 +10300,7 @@ async function ensureAgentProfile(options) {
10547
10300
  }, "save"),
10548
10301
  loadCredentials: /* @__PURE__ */ __name(() => {
10549
10302
  const saved = loadProfiles()[profile];
10550
- if (saved && Object.entries(binding).some(([key, value]) => saved[key] !== value)) {
10303
+ if (saved && Object.entries(binding).some(([key2, value]) => saved[key2] !== value)) {
10551
10304
  throw new Error("Saved registration credential belongs to another Canon environment or endpoint set");
10552
10305
  }
10553
10306
  return saved;
@@ -10701,7 +10454,7 @@ function createRTDBClientHandle(client) {
10701
10454
  if (target.family === "streaming") {
10702
10455
  if (clear) return client.clearStreaming(target.conversationId);
10703
10456
  const { updatedAt: _updatedAt, startedAt: _startedAt, ...patch2 } = record5(data);
10704
- if (Object.keys(patch2).some((key) => !["text", "status", "messageId", "turnId", "blocks"].includes(key))) throw new Error("Unsupported streaming projection field");
10457
+ if (Object.keys(patch2).some((key2) => !["text", "status", "messageId", "turnId", "blocks"].includes(key2))) throw new Error("Unsupported streaming projection field");
10705
10458
  return client.patchRuntimeStreaming(target.conversationId, patch2);
10706
10459
  }
10707
10460
  if (target.family === "turn-state") {
@@ -11206,125 +10959,6 @@ function runCli(moduleUrl, main, onError, metadata) {
11206
10959
  }
11207
10960
  __name(runCli, "runCli");
11208
10961
 
11209
- // ../core/src/completed-output-journal.ts
11210
- import { createHash as createHash4, randomUUID as randomUUID6 } from "node:crypto";
11211
- import {
11212
- closeSync as closeSync3,
11213
- existsSync as existsSync2,
11214
- fsyncSync,
11215
- mkdirSync as mkdirSync2,
11216
- openSync as openSync3,
11217
- readFileSync as readFileSync4,
11218
- readdirSync,
11219
- renameSync as renameSync2,
11220
- unlinkSync as unlinkSync3,
11221
- writeFileSync as writeFileSync3
11222
- } from "node:fs";
11223
- import { join as join4 } from "node:path";
11224
- function createCompletedOutputJournal(directory, scope5, options) {
11225
- const label = options.label;
11226
- const folder = join4(directory, createHash4("sha256").update(JSON.stringify(scope5)).digest("hex"));
11227
- const filename = /* @__PURE__ */ __name((turnId) => join4(folder, `${createHash4("sha256").update(turnId).digest("hex")}.json`), "filename");
11228
- function syncDirectory() {
11229
- if (process.platform === "win32") return;
11230
- const fd = openSync3(folder, "r");
11231
- try {
11232
- fsyncSync(fd);
11233
- } finally {
11234
- closeSync3(fd);
11235
- }
11236
- }
11237
- __name(syncDirectory, "syncDirectory");
11238
- function validate(record6) {
11239
- 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)) {
11240
- throw new Error(`Invalid completed ${label} output record; operator reconciliation is required`);
11241
- }
11242
- }
11243
- __name(validate, "validate");
11244
- function recoverTemporaryFiles() {
11245
- for (const name of readdirSync(folder)) {
11246
- const match = /^([a-f0-9]{64})\.(\d+)\.[a-f0-9-]+\.tmp$/.exec(name);
11247
- if (!match) continue;
11248
- try {
11249
- process.kill(Number(match[2]), 0);
11250
- continue;
11251
- } catch (error) {
11252
- if (error.code !== "ESRCH") continue;
11253
- }
11254
- const path = join4(folder, name);
11255
- let record6;
11256
- try {
11257
- record6 = JSON.parse(readFileSync4(path, "utf8"));
11258
- validate(record6);
11259
- } catch {
11260
- unlinkSync3(path);
11261
- throw new Error(`Incomplete ${label} output write removed; reconcile its native transcript`);
11262
- }
11263
- const destination = filename(record6.turnId);
11264
- if (destination !== join4(folder, `${match[1]}.json`)) {
11265
- throw new Error(`Completed ${label} output temporary file identity mismatch`);
11266
- }
11267
- if (existsSync2(destination)) {
11268
- if (readFileSync4(destination, "utf8") !== JSON.stringify(record6)) {
11269
- throw new Error(`Conflicting completed ${label} outputs require reconciliation`);
11270
- }
11271
- unlinkSync3(path);
11272
- } else renameSync2(path, destination);
11273
- syncDirectory();
11274
- }
11275
- }
11276
- __name(recoverTemporaryFiles, "recoverTemporaryFiles");
11277
- return {
11278
- save(record6) {
11279
- validate(record6);
11280
- mkdirSync2(folder, { recursive: true, mode: 448 });
11281
- const path = filename(record6.turnId);
11282
- const serialized = JSON.stringify(record6);
11283
- if (existsSync2(path)) {
11284
- if (readFileSync4(path, "utf8") !== serialized) throw new Error(`Completed ${label} output cannot be replaced`);
11285
- return;
11286
- }
11287
- const temporary = `${path.slice(0, -5)}.${process.pid}.${randomUUID6()}.tmp`;
11288
- try {
11289
- const fd = openSync3(temporary, "wx", 384);
11290
- try {
11291
- writeFileSync3(fd, serialized);
11292
- fsyncSync(fd);
11293
- } finally {
11294
- closeSync3(fd);
11295
- }
11296
- renameSync2(temporary, path);
11297
- syncDirectory();
11298
- } catch (error) {
11299
- try {
11300
- unlinkSync3(temporary);
11301
- } catch {
11302
- }
11303
- throw error;
11304
- }
11305
- },
11306
- list() {
11307
- if (!existsSync2(folder)) return [];
11308
- recoverTemporaryFiles();
11309
- return readdirSync(folder).filter((name) => name.endsWith(".json")).map((name) => {
11310
- const record6 = JSON.parse(readFileSync4(join4(folder, name), "utf8"));
11311
- validate(record6);
11312
- if (filename(record6.turnId) !== join4(folder, name)) throw new Error(`Completed ${label} output identity mismatch`);
11313
- return record6;
11314
- }).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
11315
- },
11316
- remove(turnId) {
11317
- try {
11318
- unlinkSync3(filename(turnId));
11319
- syncDirectory();
11320
- } catch (error) {
11321
- if (error.code !== "ENOENT") throw error;
11322
- }
11323
- }
11324
- };
11325
- }
11326
- __name(createCompletedOutputJournal, "createCompletedOutputJournal");
11327
-
11328
10962
  // src/bounded-file-read.ts
11329
10963
  var BoundedFileReadError = class extends Error {
11330
10964
  constructor(failure) {
@@ -11368,7 +11002,7 @@ var ANTHROPIC_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
11368
11002
  "image/gif",
11369
11003
  "image/webp"
11370
11004
  ]);
11371
- var DEFAULT_MEDIA_CACHE_DIR = join5(CANON_DIR, "media-cache");
11005
+ var DEFAULT_MEDIA_CACHE_DIR = join4(CANON_DIR, "media-cache");
11372
11006
  var DEFAULT_MEDIA_MATERIALIZATION_BYTES = 10 * 1024 * 1024;
11373
11007
  var MAX_CANON_MEDIA_BYTES = 100 * 1024 * 1024;
11374
11008
  var MAX_ANTHROPIC_IMAGE_RAW_BYTES = 7 * 1024 * 1024;
@@ -11436,7 +11070,7 @@ function inferExtension(input) {
11436
11070
  }
11437
11071
  __name(inferExtension, "inferExtension");
11438
11072
  function conversationMediaCacheDir(input) {
11439
- return join5(
11073
+ return join4(
11440
11074
  input.rootDir ?? DEFAULT_MEDIA_CACHE_DIR,
11441
11075
  sanitizeSegment(input.agentId, "agent"),
11442
11076
  sanitizeSegment(input.conversationId, "conversation")
@@ -11452,7 +11086,7 @@ function buildCachePath(input) {
11452
11086
  const fallbackName = `${input.attachment.kind}-${input.index}${extension}`;
11453
11087
  const preferredName = input.attachment.fileName ? sanitizeDownloadFileName(input.attachment.fileName, fallbackName) : fallbackName;
11454
11088
  const fileName = extname(preferredName) ? preferredName : `${preferredName}${extension}`;
11455
- return join5(
11089
+ return join4(
11456
11090
  conversationMediaCacheDir({ agentId: input.agentId, conversationId: input.conversationId, rootDir }),
11457
11091
  sanitizeSegment(input.messageId, "message"),
11458
11092
  `${String(input.index).padStart(2, "0")}-${fileName}`
@@ -11602,7 +11236,7 @@ async function materializeAttachment(attachment, options) {
11602
11236
  if (!response.body) {
11603
11237
  throw new Error("Canon media response had no body");
11604
11238
  }
11605
- const tempPath = `${path}.${process.pid}.${randomUUID7()}.part`;
11239
+ const tempPath = `${path}.${process.pid}.${randomUUID6()}.part`;
11606
11240
  const limiter = byteLimitTransform(maxBytes);
11607
11241
  try {
11608
11242
  await pipeline2(
@@ -11698,7 +11332,7 @@ async function uploadMediaFile(client, conversationId, filePath, options) {
11698
11332
  const media = await client.getFileMediaPipeline(options?.fetchImpl);
11699
11333
  const [attachment] = await media.prepare({
11700
11334
  conversationId,
11701
- attachmentId: options?.attachmentId ?? randomUUID7(),
11335
+ attachmentId: options?.attachmentId ?? randomUUID6(),
11702
11336
  files: [{ source: filePath, descriptor: {
11703
11337
  mimeType,
11704
11338
  sizeBytes: fileStat.size,
@@ -11727,7 +11361,7 @@ async function sendMediaFileMessage(client, conversationId, filePath, text = "",
11727
11361
  const current = await client.getConversation(conversationId);
11728
11362
  return !!current && originalMembers.size > 0 && current.memberIds.every((id2) => originalMembers.has(id2)) && canPublish();
11729
11363
  } : void 0;
11730
- const messageId = sendOptions.messageId ?? randomUUID7();
11364
+ const messageId = sendOptions.messageId ?? randomUUID6();
11731
11365
  const pipeline3 = await client.getFileMediaPipeline(fetchImpl);
11732
11366
  const delivered = await pipeline3.send({
11733
11367
  conversationId,
@@ -11909,7 +11543,6 @@ export {
11909
11543
  splitTextWithinBytes,
11910
11544
  readCliPackageVersion,
11911
11545
  runCli,
11912
- createCompletedOutputJournal,
11913
11546
  BoundedFileReadError,
11914
11547
  readBoundedRegularFileHandle,
11915
11548
  DEFAULT_MEDIA_CACHE_DIR,