openmates 0.27.0-alpha.26 → 0.27.0-alpha.28

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.
@@ -5236,6 +5236,9 @@ var OpenMatesWsClient = class {
5236
5236
  if (this.recoveryOutputDiscoveryError) throw this.recoveryOutputDiscoveryError;
5237
5237
  }
5238
5238
  waitForMessage(expectedType, predicate, timeoutMs = 2e4) {
5239
+ if (this.socket.readyState === WebSocket2.CLOSING || this.socket.readyState === WebSocket2.CLOSED) {
5240
+ return Promise.reject(new Error("WebSocket closed while waiting for message"));
5241
+ }
5239
5242
  return new Promise((resolve19, reject) => {
5240
5243
  const seenTypes = /* @__PURE__ */ new Set();
5241
5244
  let predicateMisses = 0;
@@ -5295,7 +5298,10 @@ var OpenMatesWsClient = class {
5295
5298
  * Returns every frame received before the terminator, in order.
5296
5299
  * Used by ensureSynced to consume the full phased-sync event stream.
5297
5300
  */
5298
- collectMessages(terminatorType, timeoutMs = 9e4) {
5301
+ collectMessages(terminatorType, timeoutMs = 9e4, requireTerminator = false) {
5302
+ if (this.socket.readyState === WebSocket2.CLOSING || this.socket.readyState === WebSocket2.CLOSED) {
5303
+ return Promise.reject(new Error(`WebSocket closed before '${terminatorType}'`));
5304
+ }
5299
5305
  return new Promise((resolve19, reject) => {
5300
5306
  const collected = [];
5301
5307
  const onMessage = (rawData) => {
@@ -5322,7 +5328,8 @@ var OpenMatesWsClient = class {
5322
5328
  };
5323
5329
  const onClose = () => {
5324
5330
  cleanup();
5325
- resolve19(collected);
5331
+ if (requireTerminator) reject(new Error(`WebSocket closed before '${terminatorType}'`));
5332
+ else resolve19(collected);
5326
5333
  };
5327
5334
  const timeout = setTimeout(() => {
5328
5335
  cleanup();
@@ -12759,11 +12766,30 @@ var OpenMatesClient = class _OpenMatesClient {
12759
12766
  ws.close();
12760
12767
  };
12761
12768
  }
12769
+ /** Read encrypted local history without refreshing or opening a connection. */
12770
+ async listCachedChats(limit = 10, page = 1, options = {}) {
12771
+ const teamId = this.resolveTeamContext(options);
12772
+ const masterKey = Buffer.from(this.getMasterKeyBytes());
12773
+ const cache = loadSyncCache(teamId);
12774
+ if (!cache) return null;
12775
+ const localTeamKey = teamId ? loadLocalTeamKey(this.requireSession().hashedEmail, teamId) : null;
12776
+ if (teamId && !localTeamKey) return null;
12777
+ const wrappingKey = localTeamKey ? base64ToBytes(localTeamKey) : masterKey;
12778
+ return this.decryptChatListPage(cache, limit, page, options, teamId, masterKey, wrappingKey);
12779
+ }
12762
12780
  async listChats(limit = 10, page = 1, options = {}) {
12763
12781
  const teamId = this.resolveTeamContext(options);
12764
12782
  const masterKey = Buffer.from(this.getMasterKeyBytes());
12765
- const cache = await this.ensureSynced(false, [], options);
12766
- const wrappingKey = await this.getChatWrappingKey(teamId, masterKey);
12783
+ const cache = await this.ensureSynced(options.forceRefresh === true, [], options, false, options.onSyncedChats ? async (synced) => {
12784
+ const syncedPage = await this.decryptChatListPage(synced, limit, page, options, teamId, masterKey);
12785
+ options.signal?.throwIfAborted();
12786
+ saveSyncCache({ ...synced, syncedAt: 0 }, teamId);
12787
+ options.onSyncedChats?.(syncedPage);
12788
+ } : void 0, options.signal);
12789
+ return this.decryptChatListPage(cache, limit, page, options, teamId, masterKey);
12790
+ }
12791
+ async decryptChatListPage(cache, limit, page, options, teamId, masterKey, cachedWrappingKey) {
12792
+ const wrappingKey = cachedWrappingKey ?? await this.getChatWrappingKey(teamId, masterKey);
12767
12793
  const total = cache.chats.length;
12768
12794
  const offset = (page - 1) * limit;
12769
12795
  const slice = cache.chats.slice(offset, offset + limit);
@@ -14597,22 +14623,30 @@ var OpenMatesClient = class _OpenMatesClient {
14597
14623
  try {
14598
14624
  acknowledged += await this.persistAvailableRecoveryOutputs(ws, ownerId, cache, group, teamId);
14599
14625
  } finally {
14600
- clearSyncCache(teamId);
14626
+ this.invalidateSyncedHistory(teamId);
14601
14627
  }
14602
14628
  }
14603
14629
  return acknowledged;
14604
14630
  }
14605
- async replayRecoveryOutputPages(ws, ownerId, pages, cache, teamId, onPending) {
14631
+ /** Refresh canonical history after recovery without removing offline browsing data. */
14632
+ invalidateSyncedHistory(teamId) {
14633
+ const cache = loadSyncCache(teamId);
14634
+ if (cache) saveSyncCache({ ...cache, syncedAt: 0 }, teamId);
14635
+ }
14636
+ async replayRecoveryOutputPages(ws, ownerId, pages, cache, teamId, onPending, signal) {
14606
14637
  let acknowledged = 0;
14607
14638
  for (const page of pages) {
14639
+ signal?.throwIfAborted();
14608
14640
  if (!onPending) {
14609
14641
  acknowledged += await this.replayAvailableRecoveryOutputs(ws, ownerId, page, cache, teamId);
14610
14642
  continue;
14611
14643
  }
14612
14644
  for (const output of page) {
14645
+ signal?.throwIfAborted();
14613
14646
  try {
14614
14647
  acknowledged += await this.replayAvailableRecoveryOutputs(ws, ownerId, [output], cache, teamId);
14615
14648
  } catch {
14649
+ signal?.throwIfAborted();
14616
14650
  onPending();
14617
14651
  }
14618
14652
  }
@@ -20271,7 +20305,8 @@ Required: ${schema.required.join(", ")}`
20271
20305
  * The sync cache stores encrypted data — decryption is always on-demand.
20272
20306
  * SECURITY: decrypted user content is NEVER written to disk.
20273
20307
  */
20274
- async ensureSynced(forceRefresh = false, refreshChatIds = [], options = {}, includeMessageContentRefresh = false) {
20308
+ async ensureSynced(forceRefresh = false, refreshChatIds = [], options = {}, includeMessageContentRefresh = false, onSyncedCache, signal) {
20309
+ signal?.throwIfAborted();
20275
20310
  const teamId = this.resolveTeamContext(options);
20276
20311
  const refreshChatIdSet = new Set(refreshChatIds.filter(Boolean));
20277
20312
  if (!forceRefresh && refreshChatIdSet.size === 0 && isSyncCacheFresh(3e5, teamId)) {
@@ -20302,6 +20337,12 @@ Required: ${schema.required.join(", ")}`
20302
20337
  }
20303
20338
  }
20304
20339
  const { ws, ownerId } = await this.openWsClient();
20340
+ const abort = () => ws.close();
20341
+ if (signal?.aborted) {
20342
+ ws.close();
20343
+ signal.throwIfAborted();
20344
+ }
20345
+ signal?.addEventListener("abort", abort, { once: true });
20305
20346
  const chats = [];
20306
20347
  const embeds = [];
20307
20348
  const embedKeys = [];
@@ -20320,20 +20361,25 @@ Required: ${schema.required.join(", ")}`
20320
20361
  client_chat_ids: clientChatIds,
20321
20362
  client_embed_ids: clientEmbedIds
20322
20363
  };
20323
- const syncFrames = ws.collectMessages("phased_sync_complete", 9e4);
20364
+ const syncFrames = ws.collectMessages("phased_sync_complete", 9e4, true);
20324
20365
  ws.send("phased_sync_request", {
20325
20366
  phase: "all",
20326
20367
  ...baseSyncPayload,
20327
20368
  ...refreshChatIdSet.size > 0 ? { refresh_chat_ids: Array.from(refreshChatIdSet) } : {}
20328
20369
  });
20329
20370
  const frames = await syncFrames;
20371
+ signal?.throwIfAborted();
20372
+ if (onSyncedCache && !frames.some((frame) => frame.type === "phase_2_last_20_chats_ready")) {
20373
+ throw new Error("Chat metadata sync ended before history arrived.");
20374
+ }
20330
20375
  if (includeMessageContentRefresh && refreshChatIdSet.size > 0) {
20331
- const phase3Frames = ws.collectMessages("phased_sync_complete", 9e4);
20376
+ const phase3Frames = ws.collectMessages("phased_sync_complete", 9e4, true);
20332
20377
  ws.send("phased_sync_request", {
20333
20378
  phase: "phase3",
20334
20379
  ...baseSyncPayload
20335
20380
  });
20336
20381
  const collectedPhase3Frames = await phase3Frames;
20382
+ signal?.throwIfAborted();
20337
20383
  frames.push(...collectedPhase3Frames);
20338
20384
  }
20339
20385
  for (const frame of frames) {
@@ -20388,6 +20434,7 @@ Required: ${schema.required.join(", ")}`
20388
20434
  }
20389
20435
  if (totalChatCount === 0) totalChatCount = chats.length;
20390
20436
  } catch (error) {
20437
+ signal?.removeEventListener("abort", abort);
20391
20438
  ws.close();
20392
20439
  throw error;
20393
20440
  }
@@ -20481,8 +20528,20 @@ Required: ${schema.required.join(", ")}`
20481
20528
  let recoveredOutputCount = 0;
20482
20529
  let pendingRecoveryOutputs = 0;
20483
20530
  try {
20531
+ await onSyncedCache?.({
20532
+ syncedAt: 0,
20533
+ totalChatCount,
20534
+ loadedChatCount: chats.length,
20535
+ chats,
20536
+ embeds,
20537
+ embedKeys,
20538
+ chatKeyWrappers,
20539
+ newChatSuggestions
20540
+ });
20541
+ signal?.throwIfAborted();
20484
20542
  await this.persistPendingAIResponsesFromSync(ws, chats, pendingAIResponses, teamId);
20485
20543
  await ws.waitForRecoveryOutputDiscovery();
20544
+ signal?.throwIfAborted();
20486
20545
  const pages = ws.drainAvailableRecoveryOutputPages();
20487
20546
  if (pages.length > 0) {
20488
20547
  if (!ownerId) throw new Error("Authenticated owner identity is required for recovery output replay.");
@@ -20503,7 +20562,8 @@ Required: ${schema.required.join(", ")}`
20503
20562
  teamId,
20504
20563
  () => {
20505
20564
  pendingRecoveryOutputs += 1;
20506
- }
20565
+ },
20566
+ signal
20507
20567
  );
20508
20568
  }
20509
20569
  if (!teamId && ownerId) {
@@ -20521,11 +20581,13 @@ Required: ${schema.required.join(", ")}`
20521
20581
  requireActiveTurnEvent: false
20522
20582
  });
20523
20583
  } finally {
20584
+ signal?.removeEventListener("abort", abort);
20524
20585
  ws.close();
20525
20586
  }
20587
+ signal?.throwIfAborted();
20526
20588
  if (persistedTaskJobIds.size > 0 || persistedWorkflowDeliveryCount > 0 || recoveredOutputCount > 0) {
20527
- clearSyncCache(teamId);
20528
- return this.ensureSynced(true, refreshChatIds, { teamId });
20589
+ this.invalidateSyncedHistory(teamId);
20590
+ return this.ensureSynced(true, refreshChatIds, { teamId }, includeMessageContentRefresh, onSyncedCache, signal);
20529
20591
  }
20530
20592
  const cache = {
20531
20593
  syncedAt: Date.now(),
@@ -86207,7 +86269,7 @@ async function checkTuiUpdate(now = Date.now()) {
86207
86269
  }
86208
86270
  const plan = buildSelfUpdatePlan({});
86209
86271
  const forced = process.env.OPENMATES_CLI_LATEST_VERSION?.trim();
86210
- const latestVersion = forced || (await executeFile(commandName("npm"), ["view", `${PACKAGE_NAME}@${plan.target}`, "version"], {
86272
+ const latestVersion = forced || (await executeFile(commandName("npm"), ["view", `${PACKAGE_NAME}@${plan.target}`, "version", "--prefer-online"], {
86211
86273
  encoding: "utf8",
86212
86274
  timeout: 1e4
86213
86275
  })).stdout.trim().split("\n").at(-1)?.trim();
@@ -86221,7 +86283,11 @@ async function installTuiUpdate(offer) {
86221
86283
  const plan = offer.plan;
86222
86284
  if (plan.dryRun) return;
86223
86285
  try {
86224
- await executeFile(plan.command, plan.args, { encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
86286
+ await executeFile(plan.command, plan.args, {
86287
+ encoding: "utf8",
86288
+ maxBuffer: 4 * 1024 * 1024,
86289
+ env: { ...process.env, npm_config_prefer_online: "true" }
86290
+ });
86225
86291
  } catch {
86226
86292
  throw new Error(`Update failed. Retry or skip for now; you can also run openmates update --verbose.`);
86227
86293
  }
@@ -86334,7 +86400,8 @@ function runSelfUpdate(plan, options = {}) {
86334
86400
  if (plan.dryRun) return;
86335
86401
  const result = spawnSync2(plan.command, plan.args, {
86336
86402
  encoding: "utf-8",
86337
- stdio: ["ignore", "pipe", "pipe"]
86403
+ stdio: ["ignore", "pipe", "pipe"],
86404
+ env: { ...process.env, npm_config_prefer_online: "true" }
86338
86405
  });
86339
86406
  if (options.verbose && result.stdout) process.stderr.write(result.stdout);
86340
86407
  if (options.verbose && result.stderr) process.stderr.write(result.stderr);
@@ -86360,7 +86427,7 @@ function resolveTargetVersion(target) {
86360
86427
  if (/^\d+\.\d+\.\d+(?:[-+][a-zA-Z0-9._-]+)?$/.test(target)) {
86361
86428
  return { version: target, error: null };
86362
86429
  }
86363
- const result = spawnSync2(commandName("npm"), ["view", `${PACKAGE_NAME}@${target}`, "version"], {
86430
+ const result = spawnSync2(commandName("npm"), ["view", `${PACKAGE_NAME}@${target}`, "version", "--prefer-online"], {
86364
86431
  encoding: "utf-8",
86365
86432
  stdio: ["ignore", "pipe", "pipe"],
86366
86433
  timeout: 3e4
@@ -88880,10 +88947,37 @@ function currentInspiration(state) {
88880
88947
  const list = workspaceInspirations(state);
88881
88948
  return list[(state.inspirationIndices[state.workspace] ?? 0) % Math.max(1, list.length)];
88882
88949
  }
88950
+ function updateHomeChats(state, chats) {
88951
+ const preserveSelection = isWorkspaceHome(state) && state.workspace === "chats";
88952
+ const selectedId = preserveSelection ? homeChatItems(state)[state.selectedIndex]?.id : void 0;
88953
+ updateTuiChatSidebar(state, () => {
88954
+ state.recentChats = chats;
88955
+ });
88956
+ if (preserveSelection) {
88957
+ const items = homeChatItems(state), index = items.findIndex((chat) => chat.id === selectedId);
88958
+ state.selectedIndex = index >= 0 ? index : Math.max(0, Math.min(state.selectedIndex, items.length - 1));
88959
+ }
88960
+ }
88883
88961
  async function loadHomeData(state, client2, render) {
88962
+ state.homeAbortController?.abort();
88963
+ const controller = new AbortController();
88964
+ state.homeAbortController = controller;
88884
88965
  const request = ++state.homeLoadVersion, signedIn = state.signedIn;
88885
- const current = () => request === state.homeLoadVersion && state.signedIn === signedIn;
88966
+ const current = () => !controller.signal.aborted && request === state.homeLoadVersion && state.signedIn === signedIn;
88886
88967
  state.homeLoading = true;
88968
+ state.homeError = null;
88969
+ state.homeChatsLoading = signedIn && homeChatItems(state).length === 0;
88970
+ if (signedIn && typeof client2.listCachedChats === "function") {
88971
+ try {
88972
+ const cached = await client2.listCachedChats(Number.MAX_SAFE_INTEGER, 1);
88973
+ if (!current()) return;
88974
+ if (cached) updateHomeChats(state, cached.chats);
88975
+ } catch {
88976
+ if (!current()) return;
88977
+ }
88978
+ state.homeChatsLoading = homeChatItems(state).length === 0;
88979
+ render();
88980
+ }
88887
88981
  const work = [];
88888
88982
  if (typeof client2.getDailyInspirations === "function") work.push(client2.getDailyInspirations().then((list) => {
88889
88983
  if (current()) {
@@ -88904,17 +88998,30 @@ async function loadHomeData(state, client2, render) {
88904
88998
  }
88905
88999
  }).catch(() => {
88906
89000
  }));
88907
- if (signedIn && typeof client2.listChats === "function") work.push(client2.listChats(Number.MAX_SAFE_INTEGER, 1).then((page) => {
89001
+ if (signedIn && typeof client2.listChats === "function") work.push(client2.listChats(Number.MAX_SAFE_INTEGER, 1, { forceRefresh: true, signal: controller.signal, onSyncedChats: (page) => {
88908
89002
  if (current()) {
88909
- updateTuiChatSidebar(state, () => {
88910
- state.recentChats = page.chats;
88911
- });
89003
+ updateHomeChats(state, page.chats);
89004
+ state.homeChatsLoading = false;
89005
+ render();
89006
+ }
89007
+ } }).then((page) => {
89008
+ if (current()) {
89009
+ updateHomeChats(state, page.chats);
88912
89010
  if (page.pendingRecoveryOutputs) state.status = "Some saved AI outputs are pending recovery. Use /refresh to retry.";
88913
89011
  render();
88914
89012
  }
88915
89013
  }).catch(() => {
88916
- if (current()) state.homeError = "Saved chats could not be loaded. Use /refresh to retry.";
89014
+ if (current()) {
89015
+ state.homeError = "Saved chats could not be synced. Use /refresh to retry.";
89016
+ if (homeChatItems(state).length) state.status = "Showing cached chats. Sync failed; /refresh to retry.";
89017
+ }
89018
+ }).finally(() => {
89019
+ if (current()) {
89020
+ state.homeChatsLoading = false;
89021
+ render();
89022
+ }
88917
89023
  }));
89024
+ else state.homeChatsLoading = false;
88918
89025
  if (signedIn) work.push(refreshTuiChatSidebar(state, client2, render, true).catch(() => {
88919
89026
  if (current()) state.homeError = "Chat projects could not be loaded. Use /refresh to retry.";
88920
89027
  }));
@@ -88945,7 +89052,7 @@ function renderHomeChatCards(state, width, height) {
88945
89052
  const activeCount = runningTuiChatGroups(state).length;
88946
89053
  if (activeCount) result.push(centered2(`${activeCount} ${activeCount === 1 ? "chat" : "chats"} active\u2026 /active`, width), "");
88947
89054
  if (!chats.length) {
88948
- result.push(centered2(state.homeLoading ? "Loading your recent chats\u2026" : state.homeError || "Start a chat below. Your recent chats will appear here.", width), "");
89055
+ result.push(centered2(state.homeChatsLoading ? "Loading your recent chats\u2026" : state.homeError || "Start a chat below. Your recent chats will appear here.", width), "");
88949
89056
  return result;
88950
89057
  }
88951
89058
  result.push(centered2(state.signedIn ? "Continue where you left off" : "Explore example chats", width), "");
@@ -89698,6 +89805,8 @@ function createInitialTuiState() {
89698
89805
  inspirations: [],
89699
89806
  inspirationIndices: {},
89700
89807
  homeLoading: false,
89808
+ homeChatsLoading: false,
89809
+ homeAbortController: null,
89701
89810
  homeError: null,
89702
89811
  homeLoadVersion: 0,
89703
89812
  homeShowAll: false,
@@ -90431,7 +90540,7 @@ function newChat(state) {
90431
90540
  }
90432
90541
  async function recent(context) {
90433
90542
  const { state, client: client2, render } = context;
90434
- if (!state.signedIn || typeof client2.listChats !== "function") return;
90543
+ if (!state.signedIn || state.homeLoading || typeof client2.listChats !== "function") return;
90435
90544
  const request = state.routeVersion, homeRequest = state.homeLoadVersion;
90436
90545
  const chats = (await client2.listChats(Number.MAX_SAFE_INTEGER, 1)).chats;
90437
90546
  if (request !== state.routeVersion || homeRequest !== state.homeLoadVersion || !state.signedIn) return;
@@ -90917,12 +91026,22 @@ async function handleWorkspaceCommand(context, command) {
90917
91026
  state.sidebarOpen = !state.sidebarOpen;
90918
91027
  state.focus = state.sidebarOpen ? "sidebar" : state.workspace === "chats" && !["start", "chats"].includes(state.screen) ? "composer" : "content";
90919
91028
  render();
90920
- if (state.sidebarOpen) await recent(context);
91029
+ if (state.sidebarOpen) void recent(context).catch(() => {
91030
+ if (state.signedIn) {
91031
+ state.status = "Showing cached chats. Sync failed; /refresh to retry.";
91032
+ render();
91033
+ }
91034
+ });
90921
91035
  return true;
90922
91036
  case "/chats":
90923
91037
  route(state, "chats", "chats");
90924
- await recent(context);
90925
91038
  render();
91039
+ void recent(context).catch(() => {
91040
+ if (state.signedIn) {
91041
+ state.status = "Showing cached chats. Sync failed; /refresh to retry.";
91042
+ render();
91043
+ }
91044
+ });
90926
91045
  return true;
90927
91046
  case "/chat":
90928
91047
  if (!arg) return handleWorkspaceCommand(context, "/chats");
@@ -91798,6 +91917,7 @@ async function runTui(client2, terminal = new TuiTerminal(), startupServices) {
91798
91917
  if (closed) return;
91799
91918
  if (state.screen !== "chat") client2.clearInteractiveChatViewer();
91800
91919
  if (state.signedIn && typeof client2.hasSession === "function" && !client2.hasSession()) {
91920
+ state.homeAbortController?.abort();
91801
91921
  Object.values(state.chatContextAuthoringControls).forEach((control) => control.stop());
91802
91922
  Object.assign(state, createInitialTuiState(), { routeVersion: state.routeVersion + 1, homeLoadVersion: state.homeLoadVersion + 1, status: "Session ended. Sign in to reopen your work." });
91803
91923
  hydrateExamples(state);
@@ -91810,6 +91930,7 @@ async function runTui(client2, terminal = new TuiTerminal(), startupServices) {
91810
91930
  };
91811
91931
  const finish = (result2) => {
91812
91932
  closed = true;
91933
+ state.homeAbortController?.abort();
91813
91934
  Object.values(state.chatContextAuthoringControls).forEach((control) => control.stop());
91814
91935
  stopActivity?.();
91815
91936
  clearTimeout(activityTimer);
@@ -273,7 +273,7 @@ declare class OpenMatesWsClient {
273
273
  * Returns every frame received before the terminator, in order.
274
274
  * Used by ensureSynced to consume the full phased-sync event stream.
275
275
  */
276
- collectMessages(terminatorType: string, timeoutMs?: number): Promise<WsEnvelope[]>;
276
+ collectMessages(terminatorType: string, timeoutMs?: number, requireTerminator?: boolean): Promise<WsEnvelope[]>;
277
277
  /**
278
278
  * Collect the AI response for a sent message.
279
279
  *
@@ -2566,7 +2566,14 @@ declare class OpenMatesClient {
2566
2566
  getSidebarChats(ids: string[], options?: TeamContextOptions): Promise<ChatListItem[]>;
2567
2567
  /** Observe lifecycle metadata without claiming or collecting AI responses. */
2568
2568
  observeChatActivity(onChange: () => void): Promise<() => void>;
2569
- listChats(limit?: number, page?: number, options?: TeamContextOptions): Promise<ChatListPage>;
2569
+ /** Read encrypted local history without refreshing or opening a connection. */
2570
+ listCachedChats(limit?: number, page?: number, options?: TeamContextOptions): Promise<ChatListPage | null>;
2571
+ listChats(limit?: number, page?: number, options?: TeamContextOptions & {
2572
+ forceRefresh?: boolean;
2573
+ signal?: AbortSignal;
2574
+ onSyncedChats?: (page: ChatListPage) => void;
2575
+ }): Promise<ChatListPage>;
2576
+ private decryptChatListPage;
2570
2577
  saveDraft(params: {
2571
2578
  markdown: string;
2572
2579
  preview?: string | null;
@@ -2739,6 +2746,8 @@ declare class OpenMatesClient {
2739
2746
  getChatFollowUpSuggestions(chatId: string, options?: TeamContextOptions): Promise<string[]>;
2740
2747
  private persistAvailableRecoveryOutputs;
2741
2748
  private replayAvailableRecoveryOutputs;
2749
+ /** Refresh canonical history after recovery without removing offline browsing data. */
2750
+ private invalidateSyncedHistory;
2742
2751
  private replayRecoveryOutputPages;
2743
2752
  sendMessage(params: {
2744
2753
  message: string;
@@ -3587,7 +3596,7 @@ declare class OpenMatesClient {
3587
3596
  * The sync cache stores encrypted data — decryption is always on-demand.
3588
3597
  * SECURITY: decrypted user content is NEVER written to disk.
3589
3598
  */
3590
- ensureSynced(forceRefresh?: boolean, refreshChatIds?: string[], options?: TeamContextOptions, includeMessageContentRefresh?: boolean): Promise<SyncCache>;
3599
+ ensureSynced(forceRefresh?: boolean, refreshChatIds?: string[], options?: TeamContextOptions, includeMessageContentRefresh?: boolean, onSyncedCache?: (cache: SyncCache) => Promise<void>, signal?: AbortSignal): Promise<SyncCache>;
3591
3600
  /**
3592
3601
  * Claim and finish reconnect workflow deliveries using only normal client
3593
3602
  * ciphertext. A fenced server transaction persists chat/message/selected
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export { ai as ImagesAiDetectionClassification, aj as ImagesAiDetectionSummary, aJ as assertTrustedAccountCommandAllowed, aK as assertTrustedAccountGuardEnvironment, aB as buildImagesAiDetectionSummary, aL as buildProjectDefaultFocus, aM as buildTravelConnectionsRequest, aC as classifyImagesAiDetection, aN as extractExplicitProjectMentions, aE as formatImagesAiDetectionLabel, aF as getExtForLang, aO as handleWiki, aP as printWhoAmI, aQ as readRootProjectInstructions, aR as renderProjectReadApprovalRequest, aS as renderProjectWriteApprovalRequest, aT as renderRemoteCommandEvent, aU as requireExactConfirmation, aV as resolveProjectContext, aI as serializeToYaml, aW as shouldRequireTrustedAccountGuard, aX as waitForSafeE2ETotpWindow, aY as waitForWorkflowRun } from './cli-tQ_pwhDI.js';
2
+ export { ai as ImagesAiDetectionClassification, aj as ImagesAiDetectionSummary, aJ as assertTrustedAccountCommandAllowed, aK as assertTrustedAccountGuardEnvironment, aB as buildImagesAiDetectionSummary, aL as buildProjectDefaultFocus, aM as buildTravelConnectionsRequest, aC as classifyImagesAiDetection, aN as extractExplicitProjectMentions, aE as formatImagesAiDetectionLabel, aF as getExtForLang, aO as handleWiki, aP as printWhoAmI, aQ as readRootProjectInstructions, aR as renderProjectReadApprovalRequest, aS as renderProjectWriteApprovalRequest, aT as renderRemoteCommandEvent, aU as requireExactConfirmation, aV as resolveProjectContext, aI as serializeToYaml, aW as shouldRequireTrustedAccountGuard, aX as waitForSafeE2ETotpWindow, aY as waitForWorkflowRun } from './cli-CpMY_IwJ.js';
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  shouldRequireTrustedAccountGuard,
22
22
  waitForSafeE2ETotpWindow,
23
23
  waitForWorkflowRun
24
- } from "./chunk-O6W63GAS.js";
24
+ } from "./chunk-EPROIPGH.js";
25
25
  import "./chunk-USORGU2X.js";
26
26
  import "./chunk-IWKP55ZB.js";
27
27
  import "./chunk-4IRYB3YN.js";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { U as UserPlanStatus, a as UserPlanRecord, b as UserPlanVerificationStatus, c as UserPlanLearningType, d as UserPlanLearningTargetKind, e as UserPlanLearningStatus, f as UserPlanLearningLevel, g as UserPlanLearningRecord, h as UserPlanCriterionRecord, i as UserPlanVerificationRecord, j as UserPlanCreateInput, k as UserPlanUpdateInput, P as ProjectItemRecord, T as TaskCreateOptions, l as TaskUpdateOptions, m as UserPlanLearningCreateTasksInput, n as UserPlanLearningCreateTasksResult, o as UserTaskStatus, E as ExternalChatContext, p as TaskPriorityLevel, D as DecryptedUserTask, q as DecryptedTaskActivityEntry, r as TaskActivityCreateOptions, s as UserTaskReorderInput, W as WorkflowSummary, t as WorkflowCapability, u as WorkflowDetail, v as WorkflowAuthoringWarning, w as WorkflowInputStartParams, x as WorkflowInputSessionResult, y as WorkflowInputSessionDetail, z as WorkflowInputEvent, A as WorkflowGraph, B as WorkflowRunContentRetention, C as WorkflowRunOnceOptions, F as WorkflowRunDetail, G as WorkflowRunOptions, H as WorkflowNode, I as WorkflowRunCancellationResult, J as WorkflowTemplateProjectionUpsertParams, K as WorkflowTemplateProjectionResult, L as PublicWorkflowTemplateProjection, M as WorkflowTemplateProjectionRevocationResult, N as WorkflowTemplateBindingCompletionParams, O as WorkflowTemplateBindingCompletionResult, Q as WorkflowTemplateShortUrlParams, R as WorkflowTemplateShortUrlResult, S as ShortUrlRevokeResult, V as WorkflowTemplateImportPayload, X as ImportedWorkflowTemplate, Y as WikipediaLearningBundle } from './cli-tQ_pwhDI.js';
2
- export { Z as AuthMethodsStatus, _ as AuthoritativeChatReconciliation, $ as BackupCodesResult, a0 as BankTransferOrderDetails, a1 as BankTransferStatus, a2 as CachedChat, a3 as CachedNewChatSuggestion, a4 as ChatListPage, a5 as CliSignupResult, a6 as DecryptedDraft, a7 as DecryptedEmbed, a8 as DecryptedMemoryEntry, a9 as DecryptedMessage, aa as DecryptedNewChatSuggestion, ab as DocsFile, ac as DocsFolder, ad as DocsSearchResult, ae as DocsTree, af as EncryptedDraft, ag as GiftCardBankTransferStatus, ah as INTEREST_TAG_IDS, ai as ImagesAiDetectionClassification, aj as ImagesAiDetectionSummary, ak as InterestTagId, al as MATE_NAMES, am as MEMORY_TYPE_REGISTRY, an as MemoryFieldDef, ao as MemoryTypeDef, ap as OpenMatesClient, aq as OpenMatesClientOptions, ar as OpenMatesSession, as as SyncCache, at as TopicPreferencesPayload, au as TotpSetupStartResult, av as WorkflowEdge, aw as WorkflowNodeRun, ax as WorkflowNodeType, ay as WorkflowReturnedOutputReference, az as WorkflowRunContentStorage, aA as WorkflowRunRouting, aB as buildImagesAiDetectionSummary, aC as classifyImagesAiDetection, aD as deriveAppUrl, aE as formatImagesAiDetectionLabel, aF as getExtForLang, aG as normalizeInterestTagIds, aH as reconcileAuthoritativeChats, aI as serializeToYaml } from './cli-tQ_pwhDI.js';
1
+ import { U as UserPlanStatus, a as UserPlanRecord, b as UserPlanVerificationStatus, c as UserPlanLearningType, d as UserPlanLearningTargetKind, e as UserPlanLearningStatus, f as UserPlanLearningLevel, g as UserPlanLearningRecord, h as UserPlanCriterionRecord, i as UserPlanVerificationRecord, j as UserPlanCreateInput, k as UserPlanUpdateInput, P as ProjectItemRecord, T as TaskCreateOptions, l as TaskUpdateOptions, m as UserPlanLearningCreateTasksInput, n as UserPlanLearningCreateTasksResult, o as UserTaskStatus, E as ExternalChatContext, p as TaskPriorityLevel, D as DecryptedUserTask, q as DecryptedTaskActivityEntry, r as TaskActivityCreateOptions, s as UserTaskReorderInput, W as WorkflowSummary, t as WorkflowCapability, u as WorkflowDetail, v as WorkflowAuthoringWarning, w as WorkflowInputStartParams, x as WorkflowInputSessionResult, y as WorkflowInputSessionDetail, z as WorkflowInputEvent, A as WorkflowGraph, B as WorkflowRunContentRetention, C as WorkflowRunOnceOptions, F as WorkflowRunDetail, G as WorkflowRunOptions, H as WorkflowNode, I as WorkflowRunCancellationResult, J as WorkflowTemplateProjectionUpsertParams, K as WorkflowTemplateProjectionResult, L as PublicWorkflowTemplateProjection, M as WorkflowTemplateProjectionRevocationResult, N as WorkflowTemplateBindingCompletionParams, O as WorkflowTemplateBindingCompletionResult, Q as WorkflowTemplateShortUrlParams, R as WorkflowTemplateShortUrlResult, S as ShortUrlRevokeResult, V as WorkflowTemplateImportPayload, X as ImportedWorkflowTemplate, Y as WikipediaLearningBundle } from './cli-CpMY_IwJ.js';
2
+ export { Z as AuthMethodsStatus, _ as AuthoritativeChatReconciliation, $ as BackupCodesResult, a0 as BankTransferOrderDetails, a1 as BankTransferStatus, a2 as CachedChat, a3 as CachedNewChatSuggestion, a4 as ChatListPage, a5 as CliSignupResult, a6 as DecryptedDraft, a7 as DecryptedEmbed, a8 as DecryptedMemoryEntry, a9 as DecryptedMessage, aa as DecryptedNewChatSuggestion, ab as DocsFile, ac as DocsFolder, ad as DocsSearchResult, ae as DocsTree, af as EncryptedDraft, ag as GiftCardBankTransferStatus, ah as INTEREST_TAG_IDS, ai as ImagesAiDetectionClassification, aj as ImagesAiDetectionSummary, ak as InterestTagId, al as MATE_NAMES, am as MEMORY_TYPE_REGISTRY, an as MemoryFieldDef, ao as MemoryTypeDef, ap as OpenMatesClient, aq as OpenMatesClientOptions, ar as OpenMatesSession, as as SyncCache, at as TopicPreferencesPayload, au as TotpSetupStartResult, av as WorkflowEdge, aw as WorkflowNodeRun, ax as WorkflowNodeType, ay as WorkflowReturnedOutputReference, az as WorkflowRunContentStorage, aA as WorkflowRunRouting, aB as buildImagesAiDetectionSummary, aC as classifyImagesAiDetection, aD as deriveAppUrl, aE as formatImagesAiDetectionLabel, aF as getExtForLang, aG as normalizeInterestTagIds, aH as reconcileAuthoritativeChats, aI as serializeToYaml } from './cli-CpMY_IwJ.js';
3
3
 
4
4
  interface ProjectedAssistantSpeechSegment {
5
5
  sequence: number;
package/dist/index.js CHANGED
@@ -43,7 +43,7 @@ import {
43
43
  selectAssistantMessagesForSpeech,
44
44
  serializeToYaml,
45
45
  summarizeAssistantSpeech
46
- } from "./chunk-O6W63GAS.js";
46
+ } from "./chunk-EPROIPGH.js";
47
47
  import "./chunk-USORGU2X.js";
48
48
  import "./chunk-IWKP55ZB.js";
49
49
  import "./chunk-4IRYB3YN.js";
@@ -17,47 +17,47 @@
17
17
  {
18
18
  "name": "libggml-base.so",
19
19
  "bytes": 1110088,
20
- "sha256": "10c0078793c95cd34b27e95dcfdb53066be7419ccda64d377e3a456560b9a2b5"
20
+ "sha256": "97106b1dcf8fa2bbcd7f064014b51730a10c2d675ae8bf36abcde6d2a8a71f67"
21
21
  },
22
22
  {
23
23
  "name": "libggml-base.so.0",
24
24
  "bytes": 1110088,
25
- "sha256": "10c0078793c95cd34b27e95dcfdb53066be7419ccda64d377e3a456560b9a2b5"
25
+ "sha256": "97106b1dcf8fa2bbcd7f064014b51730a10c2d675ae8bf36abcde6d2a8a71f67"
26
26
  },
27
27
  {
28
28
  "name": "libggml-base.so.0.15.1",
29
29
  "bytes": 1110088,
30
- "sha256": "10c0078793c95cd34b27e95dcfdb53066be7419ccda64d377e3a456560b9a2b5"
30
+ "sha256": "97106b1dcf8fa2bbcd7f064014b51730a10c2d675ae8bf36abcde6d2a8a71f67"
31
31
  },
32
32
  {
33
33
  "name": "libggml-cpu.so",
34
34
  "bytes": 869800,
35
- "sha256": "baa43c9b620f3972a9177695c16a3e6c951a01162489039e722afecfaafe57df"
35
+ "sha256": "791fb22af968659c893f34a05b29c0e9323d6507695d4c9bb9e9e27a1ff2f7c8"
36
36
  },
37
37
  {
38
38
  "name": "libggml-cpu.so.0",
39
39
  "bytes": 869800,
40
- "sha256": "baa43c9b620f3972a9177695c16a3e6c951a01162489039e722afecfaafe57df"
40
+ "sha256": "791fb22af968659c893f34a05b29c0e9323d6507695d4c9bb9e9e27a1ff2f7c8"
41
41
  },
42
42
  {
43
43
  "name": "libggml-cpu.so.0.15.1",
44
44
  "bytes": 869800,
45
- "sha256": "baa43c9b620f3972a9177695c16a3e6c951a01162489039e722afecfaafe57df"
45
+ "sha256": "791fb22af968659c893f34a05b29c0e9323d6507695d4c9bb9e9e27a1ff2f7c8"
46
46
  },
47
47
  {
48
48
  "name": "libggml.so",
49
49
  "bytes": 1899104,
50
- "sha256": "01e410e671c40837154c6afc25208a08f5590fa4e3060ea0f9dfbc7ff9a65ad1"
50
+ "sha256": "b22bdedf9c5db7fe14bb75b529c74486b4b547f5aa573bbc5e645b033c42b977"
51
51
  },
52
52
  {
53
53
  "name": "libggml.so.0",
54
54
  "bytes": 1899104,
55
- "sha256": "01e410e671c40837154c6afc25208a08f5590fa4e3060ea0f9dfbc7ff9a65ad1"
55
+ "sha256": "b22bdedf9c5db7fe14bb75b529c74486b4b547f5aa573bbc5e645b033c42b977"
56
56
  },
57
57
  {
58
58
  "name": "libggml.so.0.15.1",
59
59
  "bytes": 1899104,
60
- "sha256": "01e410e671c40837154c6afc25208a08f5590fa4e3060ea0f9dfbc7ff9a65ad1"
60
+ "sha256": "b22bdedf9c5db7fe14bb75b529c74486b4b547f5aa573bbc5e645b033c42b977"
61
61
  },
62
62
  {
63
63
  "name": "openmates-pii-worker",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmates",
3
- "version": "0.27.0-alpha.26",
3
+ "version": "0.27.0-alpha.28",
4
4
  "description": "OpenMates CLI and SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",