@canonmsg/agent-sdk 12.3.0 → 12.3.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.
@@ -29,7 +29,7 @@ import {
29
29
  sendMessageWithRetryChunked,
30
30
  splitTextByUtf8Bytes,
31
31
  verifyCanonRuntimeConnection
32
- } from "./chunk-GJO7KULY.js";
32
+ } from "./chunk-SKFI2MJA.js";
33
33
  import {
34
34
  CONNECTOR_RELAY_ANSWER_WITHIN_MS,
35
35
  CONNECTOR_RELAY_INSTALLATION_PATTERN,
@@ -6612,6 +6612,7 @@ var view2 = /* @__PURE__ */ __name((value) => Object.freeze({
6612
6612
  roster: value.roster,
6613
6613
  pendingOperationId: value.pending?.operationId ?? null
6614
6614
  }), "view");
6615
+ var CATCH_UP_MAX_AGE_MS = 6e4;
6615
6616
  function createMlsDevGroups(options, context, keyPackages, bridge) {
6616
6617
  const principal = parseMlsDevEnrollmentPrincipal(options.principal);
6617
6618
  const prepareProtected = options.prepareProtectedGroup ?? fail6("MLS_DEV_GROUP_UNAVAILABLE");
@@ -6626,6 +6627,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6626
6627
  }), "transaction") };
6627
6628
  const fence = /* @__PURE__ */ __name(() => store.transaction(async () => {
6628
6629
  }), "fence");
6630
+ const caughtUpAt = /* @__PURE__ */ new Map();
6629
6631
  async function initialized() {
6630
6632
  const ready = await context.ready();
6631
6633
  await fence();
@@ -6909,7 +6911,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6909
6911
  if (!current.envelope?.head || current.envelope.removed || !same6(current.envelope.head, wire.expectedHead) || current.envelope.groupId !== wire.groupId) fail6("MLS_DEV_GROUP_HEAD_CHANGED", true);
6910
6912
  if (await tx.get(appMessageKey(cid, wire.messageId)) !== void 0 && await tx.get(appMessageKey(cid, wire.messageId)) !== receipt.operationId) fail6("MLS_DEV_APPLICATION_ID_CONFLICT");
6911
6913
  const existingMessage = await tx.get(mlsDevTextMessageKey(cid, wire.messageId));
6912
- if (existingMessage?.feedOrdinal !== null && existingMessage?.feedOrdinal !== void 0) fail6("MLS_DEV_APPLICATION_ID_CONFLICT");
6914
+ if (existingMessage?.feedOrdinal !== null && existingMessage?.feedOrdinal !== void 0 && (existingMessage.feedOrdinal !== entry.ordinal || sender.deviceId !== current.device.registration.device.deviceId)) fail6("MLS_DEV_APPLICATION_ID_CONFLICT");
6913
6915
  if (!current.envelope.roster.some((member) => same6(member.device, sender) && member.admissionId === wire.preconditions.admissionId)) fail6("MLS_DEV_GROUP_MEMBER_REQUIRED");
6914
6916
  ownIntent = sender.deviceId === current.device.registration.device.deviceId ? await tx.get(appIntentKey(receipt.operationId)) : void 0;
6915
6917
  if (ownIntent && (!same6(ownIntent.envelope, wire) || ownIntent.envelopeHash !== receipt.envelopeHash)) fail6("MLS_DEV_APPLICATION_ID_CONFLICT");
@@ -6922,7 +6924,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
6922
6924
  let message, integrity, wrappedState = group.wrappedState;
6923
6925
  if (ownIntent) {
6924
6926
  const local = await store.transaction((tx) => tx.get(mlsDevTextMessageKey(cid, wire.messageId))) ?? fail6();
6925
- message = local.message.kind === "text" ? parseMlsDevTextPayload(local.message) : parseMlsDevApplicationPayload(local.message);
6927
+ message = local.deleted ? local.message : local.message.kind === "text" ? parseMlsDevTextPayload(local.message) : parseMlsDevApplicationPayload(local.message);
6926
6928
  } else {
6927
6929
  const prepared = await native(init, current, state, cid, group.groupId, { op: "process_application", entry });
6928
6930
  if (prepared.payload.kind !== "text" && prepared.payload.kind !== "item" && prepared.payload.kind !== "interaction" && prepared.payload.kind !== "invalid" || prepared.payload.messageId !== wire.messageId || prepared.payload.operationId !== receipt.operationId || prepared.payload.senderDeviceId !== sender2.deviceId || prepared.payload.feedOrdinal !== entry.ordinal) fail6("MLS_DEV_GROUP_BINDING_MISMATCH");
@@ -7030,7 +7032,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7030
7032
  let operation;
7031
7033
  if (prepared.own) {
7032
7034
  const own = await tx.get(`operation/${receipt.operationId}`) ?? fail6();
7033
- operation = await bridge.settle(tx, own, {
7035
+ if (own.status !== "succeeded") operation = await bridge.settle(tx, own, {
7034
7036
  protocol: CANON_ENDPOINT_PROTOCOL,
7035
7037
  operationId: own.operationId,
7036
7038
  installationId: own.installationId,
@@ -7111,6 +7113,17 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7111
7113
  }
7112
7114
  __name(reduceInteraction, "reduceInteraction");
7113
7115
  async function refreshFeed(cid, init) {
7116
+ const applied = { entries: false };
7117
+ try {
7118
+ const view3 = await catchUpFeed(cid, init, applied);
7119
+ caughtUpAt.set(cid, context.clock());
7120
+ return view3;
7121
+ } finally {
7122
+ if (applied.entries) bridge.historyChanged?.(cid);
7123
+ }
7124
+ }
7125
+ __name(refreshFeed, "refreshFeed");
7126
+ async function catchUpFeed(cid, init, applied) {
7114
7127
  if (!bridge.queryFeed) fail6("MLS_DEV_GROUP_UNAVAILABLE");
7115
7128
  for (let page = 0; page < 8; page++) {
7116
7129
  const captured = await store.transaction(async (tx) => capture(tx, await readPreparationSnapshot(tx, scope4(cid)), init, cid));
@@ -7149,6 +7162,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7149
7162
  if (entry.kind === "control") {
7150
7163
  if (!await applyAccepted(init, entry.accepted, void 0, entry)) fail6("MLS_DEV_GROUP_FEED_GAP", true);
7151
7164
  } else await applyApplication(init, entry);
7165
+ applied.entries = true;
7152
7166
  }
7153
7167
  if (!response.hasMore) {
7154
7168
  await store.transaction(async (tx) => {
@@ -7166,7 +7180,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7166
7180
  }
7167
7181
  return fail6("MLS_DEV_GROUP_CATCHUP_REQUIRED", true);
7168
7182
  }
7169
- __name(refreshFeed, "refreshFeed");
7183
+ __name(catchUpFeed, "catchUpFeed");
7170
7184
  const canonItemReference = /* @__PURE__ */ __name((item, conversationId, environmentId) => isCanonUploadUrl(item.url, { environmentId, conversationId, uploadId: item.uploadId }) && (item.thumbnailUrl === void 0 || isCanonUploadUrl(item.thumbnailUrl, { environmentId, conversationId, uploadId: item.uploadId, poster: true })), "canonItemReference");
7171
7185
  function itemInput(attachment, caption) {
7172
7186
  const data = record5(attachment, ["kind", "url", "mimeType", "sizeBytes", "uploadId"], ["fileName", "width", "height", "durationMs", "thumbnailUrl", "processingStatus"]);
@@ -7246,7 +7260,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7246
7260
  __name(existing, "existing");
7247
7261
  const previous = await store.transaction(existing);
7248
7262
  if (previous) return previous;
7249
- await refresh(cid);
7263
+ if (!await caughtUp(cid)) await refresh(cid);
7250
7264
  await assertNotHeld(cid);
7251
7265
  const messageId = input.messageId ?? id(context.ids());
7252
7266
  let captured, replyTo;
@@ -7771,6 +7785,28 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7771
7785
  }
7772
7786
  }
7773
7787
  __name(refresh, "refresh");
7788
+ function textReady(conversationId) {
7789
+ return store.transaction(async (tx) => {
7790
+ const ready = await tx.get(`mls-dev-group/feed-ready/${encodeURIComponent(conversationId)}`);
7791
+ return !!ready?.admitted && !ready.terminal && ready.ordinal === await tx.get(feedKey(conversationId)) && await tx.get(recoveryFailureKey(conversationId)) === void 0;
7792
+ });
7793
+ }
7794
+ __name(textReady, "textReady");
7795
+ async function caughtUp(conversationId) {
7796
+ return !!bridge.streamLive?.() && context.clock() - (caughtUpAt.get(conversationId) ?? -Infinity) < CATCH_UP_MAX_AGE_MS && await textReady(conversationId);
7797
+ }
7798
+ __name(caughtUp, "caughtUp");
7799
+ async function settleOwnApplication(tx, operation, accepted, receipt) {
7800
+ if (operation.status === "succeeded" && same6(operation.receipt?.result, receipt.result)) return {};
7801
+ if (operation.status === "succeeded" || operation.status === "rejected") return void 0;
7802
+ const local = await tx.get(mlsDevTextMessageKey(accepted.conversationId, accepted.messageId));
7803
+ if (local?.message.kind !== "text" && local?.message.kind !== "item") return void 0;
7804
+ const intent2 = await tx.get(appIntentKey(operation.operationId));
7805
+ if (accepted.messageId !== operation.messageId || local.message.operationId !== operation.operationId || intent2?.envelopeHash !== accepted.envelopeHash || intent2.envelope.groupId !== accepted.groupId) fail6("MLS_DEV_GROUP_BINDING_MISMATCH");
7806
+ await putMlsDevTextMessage(tx, { ...local, status: "sent", createdAtMs: accepted.acceptedAtMs, feedOrdinal: accepted.feedOrdinal });
7807
+ return { operation: await bridge.settle(tx, operation, receipt, true) };
7808
+ }
7809
+ __name(settleOwnApplication, "settleOwnApplication");
7774
7810
  async function existingOperation(tx, operationId, authored) {
7775
7811
  const intent2 = await tx.get(`mls-dev-group/intent/${operationId}`), operation = await tx.get(`operation/${operationId}`);
7776
7812
  if (!intent2 && !operation) return void 0;
@@ -7903,10 +7939,12 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
7903
7939
  const verified = await store.transaction(async (tx) => {
7904
7940
  const local = await tx.get(mlsDevTextMessageKey(conversationId, messageId));
7905
7941
  if (!local || local.feedOrdinal === null || local.message.kind !== "text" && local.message.kind !== "item" || local.deleted) return void 0;
7942
+ const stored = await tx.get(feedEntryKey(conversationId, local.feedOrdinal));
7943
+ if (stored === void 0) return void 0;
7906
7944
  const text = local.message.kind === "item" ? parseMlsDevItemPayload(local.message) : parseMlsDevTextPayload(local.message);
7907
7945
  const protocol2 = await tx.get(mlsDevTextProtocolKey(conversationId));
7908
7946
  if (!isMlsDevTextProtocol(protocol2)) fail6("MLS_DEV_VERIFIED_MESSAGE_MISMATCH");
7909
- const entry = parseMlsDevGroupFeedEntry(await tx.get(feedEntryKey(conversationId, local.feedOrdinal)));
7947
+ const entry = parseMlsDevGroupFeedEntry(stored);
7910
7948
  if (entry.kind !== "application") fail6("MLS_DEV_VERIFIED_MESSAGE_MISMATCH");
7911
7949
  const { envelope: wire2, receipt: receipt2 } = entry;
7912
7950
  const sender2 = parseMlsDevCredentialToken(wire2.senderCredential).unsigned.claims.device;
@@ -8105,9 +8143,12 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8105
8143
  const input = textInput(value), selectedId = id(operationId), selectedGuard = guardId === void 0 ? void 0 : id(guardId);
8106
8144
  return run(() => serializeInboundPreparation(store, input.conversationId, () => sendText(input, selectedId, selectedGuard)));
8107
8145
  },
8146
+ /** A room this device holds caught up is read as it holds it: what fetches the room's new entries says so
8147
+ * (bridge.historyChanged). Otherwise a read fetches the feed first, which the polling fallback and the connected
8148
+ * stream's minute-long safety poll rely on. */
8108
8149
  queryTextHistory(conversationId, name, parameters) {
8109
8150
  return run(() => serializeInboundPreparation(store, id(conversationId), async () => {
8110
- await refresh(conversationId);
8151
+ if (!await caughtUp(conversationId)) await refresh(conversationId);
8111
8152
  const init = await initialized();
8112
8153
  await primeIdentities(conversationId, init);
8113
8154
  return store.transaction(async (tx) => {
@@ -8131,12 +8172,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8131
8172
  });
8132
8173
  }));
8133
8174
  },
8134
- async isTextReady(conversationId) {
8135
- return store.transaction(async (tx) => {
8136
- const ready = await tx.get(`mls-dev-group/feed-ready/${encodeURIComponent(conversationId)}`);
8137
- return !!ready?.admitted && !ready.terminal && ready.ordinal === await tx.get(feedKey(conversationId)) && await tx.get(recoveryFailureKey(conversationId)) === void 0;
8138
- });
8139
- },
8175
+ isTextReady: textReady,
8140
8176
  async readAcceptedReceipt(operation) {
8141
8177
  if (!["mls_group_control", "mls_application"].includes(operation.action)) return void 0;
8142
8178
  return store.transaction(async (tx) => {
@@ -8152,13 +8188,18 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
8152
8188
  const cid = operation.conversationId;
8153
8189
  const accepted2 = operation.action === "mls_application" ? parseMlsDevApplicationReceipt(receipt.result) : parseMlsDevGroupAcceptedControl(receipt.result);
8154
8190
  if (accepted2.operationId !== operation.operationId || ("control" in accepted2 ? accepted2.control.conversationId : accepted2.conversationId) !== cid) fail6("MLS_DEV_GROUP_BINDING_MISMATCH");
8155
- await store.transaction(async (tx) => {
8191
+ const own = await store.transaction(async (tx) => {
8156
8192
  const current = await tx.get(`operation/${operation.operationId}`) ?? fail6();
8157
8193
  if (receipt.installationId !== current.installationId || receipt.action !== current.action) fail6("MLS_DEV_GROUP_BINDING_MISMATCH");
8158
8194
  const previous = await tx.get(acceptedReceiptKey(operation.operationId));
8159
8195
  if (previous && !same6(previous, receipt)) fail6("MLS_DEV_GROUP_BINDING_MISMATCH");
8160
8196
  if (!previous) await tx.put(acceptedReceiptKey(operation.operationId), receipt);
8197
+ return "control" in accepted2 ? void 0 : settleOwnApplication(tx, current, accepted2, receipt);
8161
8198
  });
8199
+ if (own) {
8200
+ if (own.operation) await bridge.afterReceipt(own.operation);
8201
+ return true;
8202
+ }
8162
8203
  await run(() => serializeInboundPreparation(store, cid, async () => {
8163
8204
  try {
8164
8205
  await refresh(cid);
@@ -8385,7 +8426,10 @@ function createEndpoint(options) {
8385
8426
  clearTimeout(admissionTimers.get(conversationId));
8386
8427
  admissionTimers.delete(conversationId);
8387
8428
  scheduleAdmission(conversationId, 0);
8388
- }, "controlSettled")
8429
+ }, "controlSettled"),
8430
+ // Whatever fetched them (a hint, the reconnect sweep, a send, recovery), the room's local reads show them now.
8431
+ historyChanged: /* @__PURE__ */ __name((conversationId) => queryObserver.invalidate((query, parameters) => parameters.conversationId === conversationId && ["list_messages", "get_messages", "get_runtime_card_state"].includes(query)), "historyChanged"),
8432
+ streamLive: /* @__PURE__ */ __name(() => events.isConnected(), "streamLive")
8389
8433
  }) : void 0;
8390
8434
  async function inMlsEnvironment() {
8391
8435
  return isMlsEnvironmentId((await initialization).environmentId);
package/dist/cli.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  runnerLockName,
23
23
  startRelayChannel,
24
24
  writeRelayCredential
25
- } from "./chunks/chunk-HHODEJNL.js";
25
+ } from "./chunks/chunk-HI2SVCUL.js";
26
26
  import {
27
27
  CanonApiError,
28
28
  CanonClient,
@@ -33,7 +33,7 @@ import {
33
33
  resolveCanonRuntimeConnection,
34
34
  runCli,
35
35
  verifyCanonRuntimeConnection
36
- } from "./chunks/chunk-GJO7KULY.js";
36
+ } from "./chunks/chunk-SKFI2MJA.js";
37
37
  import {
38
38
  __name,
39
39
  isCanonClientVersion
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  runMlsDevSetup,
19
19
  runtimeDescriptor,
20
20
  startRelayChannel
21
- } from "./chunks/chunk-HHODEJNL.js";
21
+ } from "./chunks/chunk-HI2SVCUL.js";
22
22
  import {
23
23
  CanonApiError,
24
24
  DEFAULT_ANTHROPIC_REQUEST_HEADROOM_BYTES,
@@ -43,7 +43,7 @@ import {
43
43
  toAnthropicImageBlock,
44
44
  toAnthropicImageBlocksWithinBudget,
45
45
  uploadMediaFile
46
- } from "./chunks/chunk-GJO7KULY.js";
46
+ } from "./chunks/chunk-SKFI2MJA.js";
47
47
  import "./chunks/chunk-5MI6Q3VS.js";
48
48
  export {
49
49
  CanonApiError,
@@ -149,6 +149,10 @@ export interface MlsDevGroupBridge {
149
149
  /** A control of this device's settled, accepted or lost to another device's: what waited on it can go on now. A hint
150
150
  * announcing the room's change may have come while it was pending, and none may follow (D2 holds the room's sends). */
151
151
  controlSettled?(conversationId: string): void;
152
+ /** A fetch applied entries of the room's feed: its encrypted history, read locally, has changed. */
153
+ historyChanged?(conversationId: string): void;
154
+ /** This device's event stream is connected, so each room's hints announce what Canon accepts into its feed. */
155
+ streamLive?(): boolean;
152
156
  /** Reads and applies the room's conversation snapshot from Canon, for openings of members admitted since. */
153
157
  readRoom?(conversationId: string): Promise<unknown>;
154
158
  }
@@ -161,13 +165,16 @@ export declare function createMlsDevGroups(options: MlsDevDeviceClientOptions, c
161
165
  * encrypted room (mls-payload-v3.md §4). Items and cards are not forwarded yet. */
162
166
  forwardText(value: unknown, operationId: string, guardId?: string): Promise<EndpointOperation>;
163
167
  sendText(value: unknown, operationId: string, guardId?: string): Promise<EndpointOperation>;
168
+ /** A room this device holds caught up is read as it holds it: what fetches the room's new entries says so
169
+ * (bridge.historyChanged). Otherwise a read fetches the feed first, which the polling fallback and the connected
170
+ * stream's minute-long safety poll rely on. */
164
171
  queryTextHistory(conversationId: string, name: string, parameters: Record<string, unknown>): Promise<{
165
172
  messages: Record<string, unknown>[];
166
173
  nextBefore?: string | null;
167
174
  missingMessageIds?: string[];
168
175
  }>;
169
176
  syncText(conversationId: string, options?: EndpointSyncOptions): Promise<EndpointSyncPage>;
170
- isTextReady(conversationId: string): Promise<boolean>;
177
+ isTextReady: (conversationId: string) => Promise<boolean>;
171
178
  readAcceptedReceipt(operation: EndpointOperation): Promise<EndpointOperationReceipt | undefined>;
172
179
  settleReceipt(operation: EndpointOperation, receipt: EndpointOperationReceipt): Promise<boolean>;
173
180
  };
@@ -29,8 +29,8 @@ export declare const mlsDevTextDeletedKey: (conversationId: string, messageId: s
29
29
  export declare const mlsDevTextReactionsKey: (conversationId: string, messageId: string) => string;
30
30
  /**
31
31
  * Its sender deleted a message for everyone. The decrypted text is erased now, and the tombstone keeps it erased if the
32
- * feed brings the message again, on catch-up or replay. An own send whose echo is not applied yet keeps its text until
33
- * the echo applies (the echo is checked against it); the history already shows it deleted. Cards are not deleted this
32
+ * feed brings the message again, on catch-up or replay. An own send Canon has not accepted yet keeps its text until its
33
+ * receipt or echo settles it; the history already shows it deleted. Cards are not deleted this
34
34
  * way: Canon refuses to delete an encrypted card, which is withdrawn instead. Returns whether anything changed.
35
35
  */
36
36
  export declare function deleteMlsDevTextMessage(tx: EndpointStoreTransaction, conversationId: string, messageId: string): Promise<boolean>;
package/dist/media.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  toAnthropicImageBlock,
19
19
  toAnthropicImageBlocksWithinBudget,
20
20
  uploadMediaFile
21
- } from "./chunks/chunk-GJO7KULY.js";
21
+ } from "./chunks/chunk-SKFI2MJA.js";
22
22
  import "./chunks/chunk-5MI6Q3VS.js";
23
23
  export {
24
24
  DEFAULT_ANTHROPIC_REQUEST_HEADROOM_BYTES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "12.3.0",
3
+ "version": "12.3.1",
4
4
  "description": "Canon Agent SDK — build AI agents that participate in Canon conversations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",