@senguoyun/dsh-arkme 0.1.59 → 0.1.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/docs/dsh-input-system-topic.md +7 -3
  2. package/lib/client.js +3355 -1674
  3. package/lib/client.js.map +1 -1
  4. package/lib/harness-sidebar-client.js +214 -23
  5. package/lib/index.js +4 -1
  6. package/lib/persistent-extension.js +1 -1
  7. package/lib/{persistent-runtime-ByM3JB3z.js → persistent-runtime-DqPfc6RO.js} +396 -59
  8. package/lib/types/arkme-service.d.ts +3 -0
  9. package/lib/types/arkme-service.d.ts.map +1 -1
  10. package/lib/types/client/ArkmeCalendarSurface.d.ts +22 -1
  11. package/lib/types/client/ArkmeCalendarSurface.d.ts.map +1 -1
  12. package/lib/types/client/ArkmeDshMenu.d.ts +38 -0
  13. package/lib/types/client/ArkmeDshMenu.d.ts.map +1 -0
  14. package/lib/types/client/ArkmeGroupChatControls.d.ts +4 -0
  15. package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
  16. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  18. package/lib/types/client/ArkmeSourceBreadcrumb.d.ts +6 -2
  19. package/lib/types/client/ArkmeSourceBreadcrumb.d.ts.map +1 -1
  20. package/lib/types/client/ArkmeTopicDirectoryPopover.d.ts +4 -2
  21. package/lib/types/client/ArkmeTopicDirectoryPopover.d.ts.map +1 -1
  22. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  23. package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
  24. package/lib/types/client/PrivateChatActions.d.ts +15 -3
  25. package/lib/types/client/PrivateChatActions.d.ts.map +1 -1
  26. package/lib/types/client/calendar-invalidation-store.d.ts +10 -1
  27. package/lib/types/client/calendar-invalidation-store.d.ts.map +1 -1
  28. package/lib/types/client/calendar-month-cache.d.ts +37 -0
  29. package/lib/types/client/calendar-month-cache.d.ts.map +1 -0
  30. package/lib/types/client/conversation-export.d.ts +18 -0
  31. package/lib/types/client/conversation-export.d.ts.map +1 -0
  32. package/lib/types/client/conversation-menu-layer.d.ts +27 -0
  33. package/lib/types/client/conversation-menu-layer.d.ts.map +1 -0
  34. package/lib/types/client/conversation-menu-scrollbars.d.ts +3 -0
  35. package/lib/types/client/conversation-menu-scrollbars.d.ts.map +1 -0
  36. package/lib/types/client/conversation-selector-style.d.ts +34 -0
  37. package/lib/types/client/conversation-selector-style.d.ts.map +1 -0
  38. package/lib/types/client/harness-session-dropdown.d.ts.map +1 -1
  39. package/lib/types/client/harness-session-hover.d.ts +1 -1
  40. package/lib/types/client/harness-session-hover.d.ts.map +1 -1
  41. package/lib/types/client/harness-session-menu-bridge.d.ts +2 -0
  42. package/lib/types/client/harness-session-menu-bridge.d.ts.map +1 -1
  43. package/lib/types/client/harness-surface-viewport.d.ts +7 -0
  44. package/lib/types/client/harness-surface-viewport.d.ts.map +1 -0
  45. package/lib/types/client/navigation-cache.d.ts +6 -1
  46. package/lib/types/client/navigation-cache.d.ts.map +1 -1
  47. package/lib/types/client/provider-instance-runtime.d.ts.map +1 -1
  48. package/lib/types/client/realtime-client-events.d.ts.map +1 -1
  49. package/lib/types/client/self-topic-directory-cache.d.ts +42 -0
  50. package/lib/types/client/self-topic-directory-cache.d.ts.map +1 -0
  51. package/lib/types/client/self-topic-expansion-preference.d.ts +23 -0
  52. package/lib/types/client/self-topic-expansion-preference.d.ts.map +1 -0
  53. package/lib/types/client/self-topic-menu-bridge.d.ts +20 -0
  54. package/lib/types/client/self-topic-menu-bridge.d.ts.map +1 -0
  55. package/lib/types/client/self-topic-menu-hover.d.ts +3 -0
  56. package/lib/types/client/self-topic-menu-hover.d.ts.map +1 -0
  57. package/lib/types/client/use-calendar-month.d.ts +4 -0
  58. package/lib/types/client/use-calendar-month.d.ts.map +1 -0
  59. package/lib/types/dsh-agent-input-source.d.ts +2 -0
  60. package/lib/types/dsh-agent-input-source.d.ts.map +1 -1
  61. package/lib/types/host-api.d.ts.map +1 -1
  62. package/lib/types/services/calendar-service.d.ts +10 -0
  63. package/lib/types/services/calendar-service.d.ts.map +1 -1
  64. package/lib/types/services/chat-realtime-service.d.ts +3 -1
  65. package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
  66. package/lib/types/services/chat-service.d.ts +3 -1
  67. package/lib/types/services/chat-service.d.ts.map +1 -1
  68. package/lib/types/services/record-service.d.ts.map +1 -1
  69. package/lib/types/services/service.d.ts +5 -0
  70. package/lib/types/services/service.d.ts.map +1 -1
  71. package/lib/types/services/source-service.d.ts +3 -0
  72. package/lib/types/services/source-service.d.ts.map +1 -1
  73. package/lib/types/types.d.ts +3 -1
  74. package/lib/types/types.d.ts.map +1 -1
  75. package/package.json +5 -5
@@ -1153,6 +1153,51 @@ var ServiceRuntime = class {
1153
1153
  readRevision(scope) {
1154
1154
  return this.readRevisions.get(scope) ?? 0;
1155
1155
  }
1156
+ calendarRevisions = /* @__PURE__ */ new Map();
1157
+ calendarVersion(scope) {
1158
+ let value = this.calendarRevisions.get(scope);
1159
+ if (!value) {
1160
+ value = {
1161
+ epoch: 0,
1162
+ revision: 0,
1163
+ dates: /* @__PURE__ */ new Map()
1164
+ };
1165
+ this.calendarRevisions.set(scope, value);
1166
+ }
1167
+ return value;
1168
+ }
1169
+ calendarReadRevision(scope, start, end, timezone) {
1170
+ const value = this.calendarVersion(scope);
1171
+ const format = new Intl.DateTimeFormat("en-CA", {
1172
+ timeZone: timezone === "local" ? void 0 : timezone,
1173
+ year: "numeric",
1174
+ month: "2-digit",
1175
+ day: "2-digit"
1176
+ });
1177
+ let revision = 0;
1178
+ for (const [stamp, changed] of value.dates) {
1179
+ const day = format.format(new Date(stamp));
1180
+ if (day >= start && day <= end) revision = Math.max(revision, changed);
1181
+ }
1182
+ return `${value.epoch}:${revision}`;
1183
+ }
1184
+ /** Chat deltas invalidate only affected composite month results (in any timezone). */
1185
+ invalidateCalendarDates(scope, stamps) {
1186
+ const dates = [...new Set(stamps.filter((stamp) => Number.isFinite(stamp) && stamp > 0))];
1187
+ if (!dates.length) {
1188
+ this.invalidateKey(scope, "calendar:");
1189
+ return;
1190
+ }
1191
+ const value = this.calendarVersion(scope);
1192
+ if (value.dates.size + dates.length > 256) {
1193
+ value.epoch++;
1194
+ value.dates.clear();
1195
+ }
1196
+ for (const stamp of dates) value.dates.set(stamp, ++value.revision);
1197
+ this.readRevisions.set(scope, this.readRevision(scope) + 1);
1198
+ this.requestCoordinator.invalidateKey(scope, "calendar:");
1199
+ this.requestCoordinator.invalidateKey(scope, "owner-read:");
1200
+ }
1156
1201
  async withOwnerReadInvalidation(route, operation) {
1157
1202
  const session = await this.requireSession();
1158
1203
  try {
@@ -1194,16 +1239,25 @@ var ServiceRuntime = class {
1194
1239
  });
1195
1240
  }
1196
1241
  invalidateScope(scope) {
1242
+ const calendar = this.calendarVersion(scope);
1243
+ calendar.epoch++;
1244
+ calendar.dates.clear();
1197
1245
  this.readRevisions.set(scope, this.readRevision(scope) + 1);
1198
1246
  this.requestCoordinator.invalidateScope(scope);
1199
1247
  }
1200
1248
  invalidateKey(scope, key) {
1249
+ if (key.startsWith("calendar:")) {
1250
+ const calendar = this.calendarVersion(scope);
1251
+ calendar.epoch++;
1252
+ calendar.dates.clear();
1253
+ }
1201
1254
  this.readRevisions.set(scope, this.readRevision(scope) + 1);
1202
1255
  this.requestCoordinator.invalidateKey(scope, key);
1203
1256
  this.requestCoordinator.invalidateKey(scope, "owner-read:");
1204
1257
  }
1205
1258
  dispose() {
1206
1259
  this.refreshInFlightByUserId.clear();
1260
+ this.calendarRevisions.clear();
1207
1261
  this.requestCoordinator.dispose();
1208
1262
  }
1209
1263
  async requireSession() {
@@ -35608,6 +35662,8 @@ var SourceService = class {
35608
35662
  chatSourceCache = /* @__PURE__ */ new Map();
35609
35663
  sourceListCache = /* @__PURE__ */ new Map();
35610
35664
  sourceListInFlight = new SharedReadGroup();
35665
+ selfDirectoryContexts = /* @__PURE__ */ new Map();
35666
+ selfDirectoryContextReads = new SharedReadGroup();
35611
35667
  groupAvatarSnapshotCache = /* @__PURE__ */ new Map();
35612
35668
  topicDissolveProgress = /* @__PURE__ */ new Map();
35613
35669
  constructor(runtime, profile, recordReader, privacy = new ArkmePrivacyVisibilityService(runtime)) {
@@ -35886,6 +35942,8 @@ var SourceService = class {
35886
35942
  this.chatSourceCache.clear();
35887
35943
  this.sourceListCache.clear();
35888
35944
  this.sourceListInFlight.clear();
35945
+ this.selfDirectoryContexts.clear();
35946
+ this.selfDirectoryContextReads.clear();
35889
35947
  this.groupAvatarSnapshotCache.clear();
35890
35948
  this.topicDissolveProgress.clear();
35891
35949
  }
@@ -36224,7 +36282,7 @@ var SourceService = class {
36224
36282
  const movedParentUid = stringValue$2(moved.parent_topic_uid).trim();
36225
36283
  const siblingOrder = numberValue$26(moved.sibling_order);
36226
36284
  if (movedTopicUid !== topic.ownerRef || movedParentUid !== (nextParentUid ?? "") || siblingOrder <= 0) throw new ArkmePluginError("topic-hierarchy-move-contract-invalid", "主题移动响应不完整,请刷新后重试", true, 502);
36227
- this.sourceListCache.clear();
36285
+ this.invalidateSourceListCache(session.userId, "send_to_self");
36228
36286
  return {
36229
36287
  sourceRef,
36230
36288
  ...nextParentSourceRef === void 0 ? {} : { parentSourceRef: nextParentSourceRef },
@@ -36286,6 +36344,10 @@ var SourceService = class {
36286
36344
  options.signal?.throwIfAborted();
36287
36345
  const limit = Math.min(directory === "send_to_self" ? 100 : 50, Math.max(1, Math.trunc(options.limit ?? 30)));
36288
36346
  const cursor = options.cursor?.trim() ?? "";
36347
+ if (directory === "send_to_self" && options.refresh === true && cursor === "") {
36348
+ this.selfDirectoryContexts.delete(session.userId);
36349
+ this.selfDirectoryContextReads.invalidate((key) => key === String(session.userId));
36350
+ }
36289
36351
  const cacheKey = `${String(session.userId)}:${directory}:${String(limit)}:${cursor}:${options.firstPaint === true ? "first" : "full"}`;
36290
36352
  this.pruneSourceListCache();
36291
36353
  const cached = this.sourceListCache.get(cacheKey);
@@ -36464,19 +36526,34 @@ var SourceService = class {
36464
36526
  }
36465
36527
  throw new ArkmePluginError("directory-groups-pagination-limit", "群聊列表超过安全分页上限", false, 502);
36466
36528
  }
36529
+ async selfDirectoryContext(session, signal) {
36530
+ const cached = this.selfDirectoryContexts.get(session.userId);
36531
+ if (cached && cached.expiresAtMillis > Date.now()) return cached.value;
36532
+ return this.selfDirectoryContextReads.run(String(session.userId), async (inner, isCurrent) => {
36533
+ const [hierarchyData, [summaryResult, latestRecordsResult]] = await Promise.all([this.runtime.authenticatedPost("/api/v1/topics/hierarchy/relations/list", {}, session, inner).catch(() => void 0), Promise.allSettled([this.recordReader.summary(), this.runtime.authenticatedPost("/api/v1/records/uncategorized/query", { limit: 10 }, session, inner)])]);
36534
+ const value = {
36535
+ hierarchyData,
36536
+ summaryResult,
36537
+ latestRecordsResult
36538
+ };
36539
+ if (isCurrent()) this.selfDirectoryContexts.set(session.userId, {
36540
+ value,
36541
+ expiresAtMillis: Date.now() + SOURCE_LIST_CACHE_TTL_MS
36542
+ });
36543
+ return value;
36544
+ }, signal);
36545
+ }
36467
36546
  async listSourcesUncached(session, directory, options, limit, sessionKind) {
36468
36547
  if (directory === "send_to_self") {
36469
36548
  const lockedRecordUids = await this.privacy.lockedRecordUids(session, options.signal);
36470
36549
  const topicPage = options.cursor === void 0 || options.cursor.trim() === "" ? void 0 : this.decodeTopicDirectoryCursor(options.cursor);
36471
- const [data, hierarchyData] = await Promise.all([this.runtime.authenticatedPost("/api/v1/topics/display/list", {
36550
+ const [data, { hierarchyData, summaryResult, latestRecordsResult }] = await Promise.all([this.runtime.authenticatedPost("/api/v1/topics/display/list", {
36472
36551
  limit: Math.min(100, Math.max(1, limit)),
36473
36552
  keyword: "",
36474
36553
  privacy_state: 1,
36475
36554
  ...topicPage?.pageCursor === void 0 ? {} : { page_cursor: topicPage.pageCursor },
36476
36555
  ...topicPage?.offset === void 0 ? {} : { offset: topicPage.offset }
36477
- }, session, options.signal), this.runtime.authenticatedPost("/api/v1/topics/hierarchy/relations/list", {}, session, options.signal).catch(() => void 0)]);
36478
- options.signal?.throwIfAborted();
36479
- const [summaryResult, latestRecordsResult] = await Promise.allSettled([this.recordReader.summary(), this.runtime.authenticatedPost("/api/v1/records/uncategorized/query", { limit: 10 }, session, options.signal)]);
36556
+ }, session, options.signal), this.selfDirectoryContext(session, options.signal)]);
36480
36557
  options.signal?.throwIfAborted();
36481
36558
  const cached = summaryResult.status === "rejected" || latestRecordsResult.status === "rejected" ? await this.runtime.stateStore.cachedSnapshot(session.userId).catch(() => void 0) : void 0;
36482
36559
  const defaultRecordCount = summaryResult.status === "fulfilled" ? summaryResult.value.recordCount : cached?.summary?.recordCount;
@@ -36512,6 +36589,7 @@ var SourceService = class {
36512
36589
  const item = objectValue$2(raw);
36513
36590
  if (arkmePrivacyLockedTopic(item)) continue;
36514
36591
  const core = objectValue$2(item.topic_core);
36592
+ if (numberValue$26(core.kind) === 3) continue;
36515
36593
  const status = core.status ?? item.status;
36516
36594
  if (status !== void 0 && numberValue$26(status) !== 1) continue;
36517
36595
  const summary = objectValue$2(item.summary);
@@ -36690,6 +36768,10 @@ var SourceService = class {
36690
36768
  };
36691
36769
  }
36692
36770
  invalidateSourceListCache(userId, directory) {
36771
+ if (directory === void 0 || directory === "send_to_self") {
36772
+ this.selfDirectoryContexts.delete(userId);
36773
+ this.selfDirectoryContextReads.invalidate((key) => key === String(userId));
36774
+ }
36693
36775
  if (directory === void 0 || directory === "root") this.runtime.invalidateKey(this.runtime.requestScope(userId), "directory:root:");
36694
36776
  const prefix = `${String(userId)}:`;
36695
36777
  const matches = (key) => key.startsWith(prefix) && (directory === void 0 || key.startsWith(`${prefix}${directory}:`));
@@ -37879,10 +37961,29 @@ var BotConversationService = class {
37879
37961
  return context.reference.target.kind === "subject" ? this.subject : this.chatAdapter;
37880
37962
  }
37881
37963
  };
37964
+ /** Authoritative wire fields only: ordinary topics may have the same title. */
37965
+ function isDshAgentInputRawRecord(raw) {
37966
+ const object = (value) => value !== null && typeof value === "object" ? value : {};
37967
+ const item = object(raw);
37968
+ const core = object(item.record_core);
37969
+ const topic = object(item.topic_core);
37970
+ return Number(item.creation_source ?? item.creationSource ?? core.creation_source ?? core.creationSource) === 3 || Number(topic.kind ?? item.topicKind) === 3;
37971
+ }
37882
37972
  //#endregion
37883
37973
  //#region src/services/calendar-service.ts
37884
37974
  const MAX_CALENDAR_RANGE_DAYS = 62;
37885
37975
  const MAX_DAY_RECORD_LIMIT = 50;
37976
+ async function mapBounded(items, project) {
37977
+ const result = new Array(items.length);
37978
+ let index = 0;
37979
+ await Promise.all(Array.from({ length: Math.min(4, items.length) }, async () => {
37980
+ while (index < items.length) {
37981
+ const current = index++;
37982
+ result[current] = await project(items[current]);
37983
+ }
37984
+ }));
37985
+ return result;
37986
+ }
37886
37987
  function numberValue$23(value) {
37887
37988
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
37888
37989
  }
@@ -37943,6 +38044,12 @@ var CalendarService = class {
37943
38044
  media;
37944
38045
  record;
37945
38046
  source;
38047
+ months = /* @__PURE__ */ new Map();
38048
+ monthReads = new SharedReadGroup();
38049
+ dispose() {
38050
+ this.months.clear();
38051
+ this.monthReads.clear();
38052
+ }
37946
38053
  constructor(runtime, privacy, media, record, source) {
37947
38054
  this.runtime = runtime;
37948
38055
  this.privacy = privacy;
@@ -37951,6 +38058,44 @@ var CalendarService = class {
37951
38058
  this.source = source;
37952
38059
  }
37953
38060
  async bucketPage(options) {
38061
+ options.signal?.throwIfAborted();
38062
+ const session = await this.runtime.requireSession();
38063
+ const timezone = readTimezone(options.timezone);
38064
+ const key = JSON.stringify([
38065
+ session.userId,
38066
+ options.sourceRef ?? "global",
38067
+ options.startDate,
38068
+ options.endDate,
38069
+ timezone
38070
+ ]);
38071
+ const revision = () => this.runtime.calendarReadRevision?.(`user:${session.userId}`, options.startDate, options.endDate, timezone) ?? "0";
38072
+ const version = revision();
38073
+ const cached = this.months.get(key);
38074
+ if (cached && cached.revision === version && cached.expires > Date.now()) {
38075
+ this.months.delete(key);
38076
+ this.months.set(key, cached);
38077
+ return structuredClone(cached.value);
38078
+ }
38079
+ return structuredClone(await this.monthReads.run(`${key}:${version}`, async (signal, isCurrent) => {
38080
+ const value = await this.bucketPageUncached({
38081
+ ...options,
38082
+ timezone,
38083
+ signal
38084
+ });
38085
+ signal.throwIfAborted();
38086
+ if (isCurrent() && revision() === version) {
38087
+ this.months.delete(key);
38088
+ this.months.set(key, {
38089
+ revision: version,
38090
+ expires: Date.now() + 6e4,
38091
+ value
38092
+ });
38093
+ while (this.months.size > 48) this.months.delete(this.months.keys().next().value);
38094
+ }
38095
+ return value;
38096
+ }, options.signal));
38097
+ }
38098
+ async bucketPageUncached(options) {
37954
38099
  const startDate = readCalendarDate(options.startDate, "start_date");
37955
38100
  const endDate = readCalendarDate(options.endDate, "end_date");
37956
38101
  const startDay = calendarDayNumber(startDate);
@@ -37959,25 +38104,60 @@ var CalendarService = class {
37959
38104
  if (endDay - startDay + 1 > MAX_CALENDAR_RANGE_DAYS) throw new ArkmePluginError("calendar-range-too-wide", `日历范围最多查询 ${MAX_CALENDAR_RANGE_DAYS} 天`, false);
37960
38105
  const timezone = readTimezone(options.timezone);
37961
38106
  const session = await this.runtime.requireSession();
37962
- await this.privacy.lockedRecordUids(session, options.signal);
37963
- const data = await this.runtime.authenticatedCalendarPost("/api/v1/calendar/buckets/query", {
37964
- bucket_scope_kind: 1,
37965
- bucket_scope_uid: "",
37966
- start_date: startDate,
37967
- end_date: endDate,
37968
- timezone
37969
- }, session, options.signal, {
37970
- key: `calendar:buckets:self:${startDate}:${endDate}:${timezone}`,
37971
- cacheMs: 3e4,
37972
- failureCooldownMs: 2e3
38107
+ const locked = await this.privacy.lockedRecordUids(session, options.signal);
38108
+ const scope = await this.resolveScope(options.sourceRef, session, options.signal);
38109
+ const pages = await mapBounded(scope.buckets, async (bucket) => {
38110
+ return {
38111
+ bucket,
38112
+ days: listValue$20((await this.runtime.authenticatedCalendarPost("/api/v1/calendar/buckets/query", {
38113
+ bucket_scope_kind: bucket.kind,
38114
+ bucket_scope_uid: bucket.uid,
38115
+ start_date: startDate,
38116
+ end_date: endDate,
38117
+ timezone
38118
+ }, session, options.signal, {
38119
+ key: `calendar:buckets:${bucket.kind === 1 ? "self" : `topic:${bucket.uid}`}:${startDate}:${endDate}:${timezone}`,
38120
+ cacheMs: 3e4,
38121
+ lane: options.background ? "background-read" : "interactive-read",
38122
+ failureCooldownMs: 2e3
38123
+ })).daily_data).map((raw) => this.bucketDay(raw)).filter((day) => day !== void 0)
38124
+ };
38125
+ });
38126
+ const days = await mapBounded([...new Set(pages.flatMap((page) => page.days.map((day) => day.bucketDate)))].sort(), async (bucketDate) => {
38127
+ const contributors = pages.flatMap((page) => {
38128
+ const day = page.days.find((day) => day.bucketDate === bucketDate);
38129
+ return day?.hasRecords ? [{
38130
+ bucket: page.bucket,
38131
+ day
38132
+ }] : [];
38133
+ });
38134
+ if (scope.kind === "self") return contributors[0]?.day ?? {
38135
+ bucketDate,
38136
+ count: 0,
38137
+ protectedCount: 0,
38138
+ hasRecords: false
38139
+ };
38140
+ const records = /* @__PURE__ */ new Map();
38141
+ for (const { bucket } of contributors) {
38142
+ const rows = await this.scopedDayRows(bucket, scope.kind, bucketDate, timezone, Infinity, void 0, session, locked, options.signal, options.background);
38143
+ for (const { item } of rows) records.set(item.recordUid, item);
38144
+ }
38145
+ const first = Math.min(...[...records.values()].map((item) => item.sendAtMillis));
38146
+ return {
38147
+ bucketDate,
38148
+ count: records.size,
38149
+ protectedCount: 0,
38150
+ hasRecords: records.size > 0,
38151
+ ...Number.isFinite(first) ? { firstSendAtMillis: first } : {}
38152
+ };
37973
38153
  });
37974
38154
  return {
37975
- scope: "self",
38155
+ scope: scope.kind,
37976
38156
  startDate,
37977
38157
  endDate,
37978
- timezone: stringValue$2(data.timezone).trim() || timezone,
38158
+ timezone,
37979
38159
  refreshedAtMillis: Date.now(),
37980
- days: listValue$20(data.daily_data).map((raw) => this.bucketDay(raw)).filter((day) => day !== void 0)
38160
+ days
37981
38161
  };
37982
38162
  }
37983
38163
  async dayRecords(options) {
@@ -37988,7 +38168,21 @@ var CalendarService = class {
37988
38168
  const cursorRecordUid = options.cursor?.recordUid.trim() ?? "";
37989
38169
  const session = await this.runtime.requireSession();
37990
38170
  const lockedRecordUids = await this.privacy.lockedRecordUids(session, options.signal);
37991
- const data = await this.runtime.authenticatedCalendarPost("/api/v1/calendar/records/query", {
38171
+ const scope = await this.resolveScope(options.sourceRef, session, options.signal);
38172
+ let data;
38173
+ if (scope.kind !== "self") {
38174
+ const streams = await mapBounded(scope.buckets, (bucket) => this.scopedDayRows(bucket, scope.kind, bucketDate, timezone, limit + 1, options.cursor, session, lockedRecordUids, options.signal));
38175
+ const sorted = [...new Map(streams.flat().map((row) => [row.item.recordUid, row])).values()].sort((a, b) => b.item.sendAtMillis - a.item.sendAtMillis || b.item.recordUid.localeCompare(a.item.recordUid));
38176
+ const last = sorted[Math.min(limit, sorted.length) - 1]?.item;
38177
+ data = {
38178
+ items: sorted.slice(0, limit).map((row) => row.raw),
38179
+ has_more: sorted.length > limit,
38180
+ ...sorted.length > limit && last ? {
38181
+ next_cursor_send_at: last.sendAtMillis,
38182
+ next_cursor_record_uid: last.recordUid
38183
+ } : {}
38184
+ };
38185
+ } else data = await this.runtime.authenticatedCalendarPost("/api/v1/calendar/records/query", {
37992
38186
  bucket_scope_kind: 1,
37993
38187
  bucket_scope_uid: "",
37994
38188
  bucket_date: bucketDate,
@@ -38034,7 +38228,7 @@ var CalendarService = class {
38034
38228
  }
38035
38229
  const media = await this.media.hydrateRecordMediaPage(rows.filter((row) => row.item.accessState === "available").map((row) => row.raw), session, options.signal);
38036
38230
  return {
38037
- scope: "self",
38231
+ scope: scope.kind,
38038
38232
  bucketDate,
38039
38233
  timezone: stringValue$2(data.timezone).trim() || timezone,
38040
38234
  refreshedAtMillis: Date.now(),
@@ -38064,6 +38258,96 @@ var CalendarService = class {
38064
38258
  } } : {}
38065
38259
  };
38066
38260
  }
38261
+ async resolveScope(sourceRef, session, signal) {
38262
+ if (sourceRef === void 0) return {
38263
+ kind: "self",
38264
+ buckets: [{
38265
+ kind: 1,
38266
+ uid: ""
38267
+ }]
38268
+ };
38269
+ const source = await this.source.openSourceRef(sourceRef, session.userId);
38270
+ if (source.kind === "send_to_self") return {
38271
+ kind: "send_to_self",
38272
+ buckets: [{
38273
+ kind: 1,
38274
+ uid: ""
38275
+ }]
38276
+ };
38277
+ if (source.kind === "default_category") return {
38278
+ kind: "uncategorized",
38279
+ buckets: [{
38280
+ kind: 1,
38281
+ uid: ""
38282
+ }]
38283
+ };
38284
+ if (source.kind !== "topic") throw new ArkmePluginError("calendar-source-invalid", "此日历仅支持发给自己和主题", false, 400);
38285
+ const metadata = await readTopicMetadata(this.runtime, session, source.ownerRef, signal);
38286
+ if (metadata.privacyState === 2) throw new ArkmePluginError("topic-privacy-locked", "隐私锁主题不能在 Arkme 插件中查看", false, 403);
38287
+ if (metadata.topicKind === 3) return {
38288
+ kind: "topic",
38289
+ buckets: []
38290
+ };
38291
+ return {
38292
+ kind: "topic",
38293
+ buckets: (await mapBounded(await this.source.topicSubtreeSources(sourceRef, signal), async (topic) => {
38294
+ const opened = await this.source.openSourceRef(topic.sourceRef, session.userId);
38295
+ const meta = opened.ownerRef === source.ownerRef ? metadata : await readTopicMetadata(this.runtime, session, opened.ownerRef, signal);
38296
+ return meta.privacyState === 2 || meta.topicKind === 3 ? [] : [{
38297
+ kind: 2,
38298
+ uid: opened.ownerRef
38299
+ }];
38300
+ })).flat()
38301
+ };
38302
+ }
38303
+ /** Read only one calendar day; backfill filtered rows and reject stalled cursors. */
38304
+ async scopedDayRows(bucket, scope, bucketDate, timezone, count, cursor, session, locked, signal, background = false) {
38305
+ const rows = /* @__PURE__ */ new Map();
38306
+ const visited = /* @__PURE__ */ new Set();
38307
+ for (let page = 0; page < 1e3; page++) {
38308
+ signal?.throwIfAborted();
38309
+ const key = `${cursor?.sendAtMillis ?? 0}:${cursor?.recordUid ?? ""}`;
38310
+ if (visited.has(key)) throw new ArkmePluginError("calendar-cursor-invalid", "日历分页未推进,请重试", true, 502);
38311
+ visited.add(key);
38312
+ const data = await this.runtime.authenticatedCalendarPost("/api/v1/calendar/records/query", {
38313
+ bucket_scope_kind: bucket.kind,
38314
+ bucket_scope_uid: bucket.uid,
38315
+ bucket_date: bucketDate,
38316
+ timezone,
38317
+ limit: MAX_DAY_RECORD_LIMIT,
38318
+ ...cursor ? {
38319
+ cursor_send_at: cursor.sendAtMillis,
38320
+ cursor_record_uid: cursor.recordUid
38321
+ } : {}
38322
+ }, session, signal, {
38323
+ key: `calendar:records:${bucket.kind === 1 ? "self" : `topic:${bucket.uid}`}:${bucketDate}:${timezone}:50:${key}`,
38324
+ cacheMs: 1e4,
38325
+ failureCooldownMs: 2e3,
38326
+ lane: background ? "background-read" : "interactive-read"
38327
+ });
38328
+ for (const raw of listValue$20(data.items)) {
38329
+ const item = this.dayRecord(raw);
38330
+ if (!item || locked.has(item.recordUid)) continue;
38331
+ if (isDshAgentInputRawRecord(raw)) continue;
38332
+ if (scope === "uncategorized" && item.isUncategorized !== true) continue;
38333
+ if (scope === "send_to_self" && item.sourceKind !== "self" && item.sourceKind !== "topic") continue;
38334
+ rows.set(item.recordUid, {
38335
+ raw,
38336
+ item
38337
+ });
38338
+ if (rows.size >= count) return [...rows.values()];
38339
+ }
38340
+ if (data.has_more !== true) return [...rows.values()];
38341
+ const sendAtMillis = Math.trunc(numberValue$23(data.next_cursor_send_at));
38342
+ const recordUid = stringValue$2(data.next_cursor_record_uid).trim();
38343
+ if (sendAtMillis <= 0 || recordUid === "") throw new ArkmePluginError("calendar-cursor-invalid", "日历分页信息缺失,请重试", true, 502);
38344
+ cursor = {
38345
+ sendAtMillis,
38346
+ recordUid
38347
+ };
38348
+ }
38349
+ throw new ArkmePluginError("calendar-pagination-limit", "日历当日记录尚未加载完整,请重试", true, 502);
38350
+ }
38067
38351
  bucketDay(raw) {
38068
38352
  const item = objectValue$2(raw);
38069
38353
  const bucketDate = stringValue$2(item.bucket_date).trim();
@@ -38103,7 +38387,7 @@ var CalendarService = class {
38103
38387
  preview,
38104
38388
  ...stringValue$2(topic.title).trim() === "" ? {} : { topicTitle: stringValue$2(topic.title).trim() },
38105
38389
  sourceKind: sourceKind(item),
38106
- creationSource: Math.trunc(numberValue$23(core.creation_source)),
38390
+ creationSource: isDshAgentInputRawRecord(raw) ? 3 : Math.trunc(numberValue$23(core.creation_source)),
38107
38391
  templateKind: Math.trunc(numberValue$23(core.template_kind)),
38108
38392
  displayKind: Math.trunc(numberValue$23(core.display_kind)),
38109
38393
  protected: false,
@@ -40341,7 +40625,7 @@ var ChatRealtimeService = class {
40341
40625
  console.warn("dsh-arkme: Chat read receipt invalidation failed:", safeFailureMessage$5(error));
40342
40626
  }
40343
40627
  }
40344
- async invalidateRecordProjection() {
40628
+ async invalidateRecordProjection(options = {}) {
40345
40629
  try {
40346
40630
  const session = await this.runtime.sessionStore.read();
40347
40631
  if (session === void 0) return;
@@ -40350,7 +40634,8 @@ var ChatRealtimeService = class {
40350
40634
  this.emitChatClientEvent({
40351
40635
  type: "projection-invalidated",
40352
40636
  revision: this.nextChatClientRevision(),
40353
- projection: "record"
40637
+ projection: "record",
40638
+ ...options.contentOnly ? { retainTopicCounts: true } : {}
40354
40639
  });
40355
40640
  } catch (error) {
40356
40641
  console.warn("dsh-arkme: Record projection invalidation failed:", safeFailureMessage$5(error));
@@ -40975,7 +41260,7 @@ var ChatRealtimeService = class {
40975
41260
  if (ownerGeneration !== this.attentionOwnerGeneration) return [];
40976
41261
  if (updates.length > 0) {
40977
41262
  this.source.invalidateSourceListCache(session.userId, "root");
40978
- this.runtime.invalidateKey(this.runtime.requestScope(session.userId), "calendar:");
41263
+ this.runtime.invalidateCalendarDates(this.runtime.requestScope(session.userId), updates.flatMap((update) => [update.source.activeAtMillis, ...update.timelineItems.map((item) => item.sendAtMillis)]));
40979
41264
  this.emitChatClientEvent({
40980
41265
  type: "sessions-delta",
40981
41266
  revision: this.nextChatClientRevision(),
@@ -41864,7 +42149,6 @@ function numberValue$20(value) {
41864
42149
  function listValue$17(value) {
41865
42150
  return Array.isArray(value) ? value : [];
41866
42151
  }
41867
- const DSH_AGENT_INPUT_CREATION_SOURCE = 3;
41868
42152
  const MAX_DEFAULT_CATEGORY_FILTER_BACKFILL_PAGES = 5;
41869
42153
  const RECORD_REEDIT_SUPPORTED_TEMPLATE_KINDS = /* @__PURE__ */ new Set([
41870
42154
  1,
@@ -41886,7 +42170,7 @@ function recordCreationSource(raw) {
41886
42170
  return integerLikeValue$1(item.creation_source ?? item.creationSource ?? core.creation_source ?? core.creationSource);
41887
42171
  }
41888
42172
  function isDSHAgentInputRecord(raw) {
41889
- return recordCreationSource(raw) === DSH_AGENT_INPUT_CREATION_SOURCE;
42173
+ return isDshAgentInputRawRecord(raw);
41890
42174
  }
41891
42175
  function safeFailureMessage$4(error) {
41892
42176
  if (error instanceof ArkmePluginError) return error.message;
@@ -45030,9 +45314,13 @@ var ChatService = class {
45030
45314
  const opened = await this.source.openSourceRef(topic.sourceRef, session.userId);
45031
45315
  if (opened.kind !== "topic") throw new ArkmePluginError("topic-subtree-source-invalid", "下级主题信息无效,请刷新后重试", true, 409);
45032
45316
  const records = [];
45317
+ const visited = /* @__PURE__ */ new Set();
45033
45318
  let pageCursor = cursor;
45034
45319
  for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
45035
45320
  signal?.throwIfAborted();
45321
+ const key = JSON.stringify(pageCursor ?? {});
45322
+ if (visited.has(key)) throw new ArkmePluginError("topic-record-page-invalid", "主题快记分页未推进,请重试", true, 502);
45323
+ visited.add(key);
45036
45324
  const page = await readTopicRecordPage(this.runtime, session, opened.ownerRef, {
45037
45325
  limit,
45038
45326
  ...pageCursor === void 0 ? {} : { cursor: pageCursor },
@@ -45047,24 +45335,35 @@ var ChatService = class {
45047
45335
  hasMore: false
45048
45336
  };
45049
45337
  }
45338
+ let filtered = false;
45050
45339
  for (const raw of page.records) {
45051
- if (arkmePrivacyLockedRecord(raw) || lockedRecordUids.has(this.record.recordUid(raw))) continue;
45340
+ if (isDshAgentInputRawRecord(raw) || arkmePrivacyLockedRecord(raw) || lockedRecordUids.has(this.record.recordUid(raw))) {
45341
+ filtered = true;
45342
+ continue;
45343
+ }
45052
45344
  records.push(raw);
45053
45345
  }
45054
- if (!merged) return {
45346
+ if (!merged && !filtered && records.length <= limit) return {
45055
45347
  topic,
45056
45348
  topicUid: opened.ownerRef,
45057
45349
  records,
45058
45350
  hasMore: page.hasMore,
45059
45351
  ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor }
45060
45352
  };
45061
- if (records.length >= limit) return {
45062
- topic,
45063
- topicUid: opened.ownerRef,
45064
- records: records.slice(0, limit),
45065
- hasMore: records.length > limit || page.hasMore,
45066
- ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor }
45067
- };
45353
+ if (records.length >= limit) {
45354
+ const last = objectValue$2(records[limit - 1]);
45355
+ const nextCursor = records.length > limit ? {
45356
+ sendAtMillis: numberValue$19(last.send_at ?? objectValue$2(last.record_core).send_at),
45357
+ itemUid: this.record.recordUid(last)
45358
+ } : page.nextCursor;
45359
+ return {
45360
+ topic,
45361
+ topicUid: opened.ownerRef,
45362
+ records: records.slice(0, limit),
45363
+ hasMore: records.length > limit || page.hasMore,
45364
+ ...nextCursor === void 0 ? {} : { nextCursor }
45365
+ };
45366
+ }
45068
45367
  if (!page.hasMore) return {
45069
45368
  topic,
45070
45369
  topicUid: opened.ownerRef,
@@ -45103,13 +45402,55 @@ var ChatService = class {
45103
45402
  const limit = Math.min(100, Math.max(1, Math.trunc(options.limit ?? 30)));
45104
45403
  if (source.kind === "send_to_self") {
45105
45404
  const lockedRecordUids = await this.privacy.lockedRecordUids(session, options.signal);
45106
- const data = await this.runtime.authenticatedPost("/api/v1/home/feed/query", {
45107
- limit,
45108
- source_kinds: [1, 2],
45109
- ...options.cursor?.sendAtMillis === void 0 ? {} : { cursor_send_at: options.cursor.sendAtMillis },
45110
- ...options.cursor?.itemUid === void 0 ? {} : { cursor_record_uid: options.cursor.itemUid }
45111
- }, session, options.signal);
45112
- const rawRecords = listValue$16(data.items).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(this.record.recordUid(raw)));
45405
+ const rawRecords = [];
45406
+ const seen = /* @__PURE__ */ new Set();
45407
+ const visited = /* @__PURE__ */ new Set();
45408
+ let cursor = options.cursor;
45409
+ let hasMore = false;
45410
+ let nextCursor;
45411
+ for (let pageIndex = 0; pageIndex < 100; pageIndex++) {
45412
+ options.signal?.throwIfAborted();
45413
+ const key = `${cursor?.sendAtMillis ?? 0}:${cursor?.itemUid ?? ""}`;
45414
+ if (visited.has(key)) throw new ArkmePluginError("self-feed-cursor-invalid", "发给自己分页未推进,请重试", true, 502);
45415
+ visited.add(key);
45416
+ const data = await this.runtime.authenticatedPost("/api/v1/home/feed/query", {
45417
+ limit,
45418
+ source_kinds: [1, 2],
45419
+ ...cursor?.sendAtMillis === void 0 ? {} : { cursor_send_at: cursor.sendAtMillis },
45420
+ ...cursor?.itemUid === void 0 ? {} : { cursor_record_uid: cursor.itemUid }
45421
+ }, session, options.signal);
45422
+ const rows = listValue$16(data.items);
45423
+ let filtered = false;
45424
+ for (const raw of rows) {
45425
+ const uid = this.record.recordUid(raw);
45426
+ if (isDshAgentInputRawRecord(raw) || arkmePrivacyLockedRecord(raw) || lockedRecordUids.has(uid) || seen.has(uid)) {
45427
+ filtered = true;
45428
+ continue;
45429
+ }
45430
+ seen.add(uid);
45431
+ rawRecords.push(raw);
45432
+ }
45433
+ hasMore = data.has_more === true;
45434
+ const nextSendAt = numberValue$19(data.next_cursor_send_at);
45435
+ const nextUid = stringValue$2(data.next_cursor_record_uid).trim();
45436
+ nextCursor = nextSendAt > 0 && nextUid !== "" ? {
45437
+ sendAtMillis: nextSendAt,
45438
+ itemUid: nextUid
45439
+ } : void 0;
45440
+ if (rawRecords.length >= limit || !hasMore || !filtered) break;
45441
+ if (!nextCursor) throw new ArkmePluginError("self-feed-cursor-invalid", "发给自己分页信息缺失,请重试", true, 502);
45442
+ cursor = nextCursor;
45443
+ if (pageIndex === 99) throw new ArkmePluginError("self-feed-pagination-limit", "发给自己加载未完成,请重试", true, 502);
45444
+ }
45445
+ if (rawRecords.length > limit) {
45446
+ rawRecords.length = limit;
45447
+ const last = objectValue$2(rawRecords.at(-1));
45448
+ nextCursor = {
45449
+ sendAtMillis: numberValue$19(last.send_at ?? objectValue$2(last.record_core).send_at),
45450
+ itemUid: this.record.recordUid(last)
45451
+ };
45452
+ hasMore = true;
45453
+ }
45113
45454
  const media = await this.media.hydrateRecordMediaPage(rawRecords, session, options.signal);
45114
45455
  const signingKey = await this.runtime.stateStore.uniqueCode();
45115
45456
  const items = (await Promise.all(rawRecords.map(async (raw) => {
@@ -45140,16 +45481,11 @@ var ChatService = class {
45140
45481
  }))).filter((item) => item.itemUid !== "");
45141
45482
  this.hydrateTimelineExtensionParents(items);
45142
45483
  const projectedItems = items.map((item) => this.withRecordMessageActionRef(source, item, session.userId, signingKey));
45143
- const nextSendAt = numberValue$19(data.next_cursor_send_at);
45144
- const nextUid = stringValue$2(data.next_cursor_record_uid).trim();
45145
45484
  return {
45146
45485
  source: await this.source.sourceItem(source),
45147
45486
  items: projectedItems,
45148
- hasMore: data.has_more === true,
45149
- ...nextSendAt > 0 && nextUid !== "" ? { nextCursor: {
45150
- sendAtMillis: nextSendAt,
45151
- itemUid: nextUid
45152
- } } : {}
45487
+ hasMore,
45488
+ ...nextCursor === void 0 ? {} : { nextCursor }
45153
45489
  };
45154
45490
  }
45155
45491
  if (source.kind === "default_category") {
@@ -46255,7 +46591,7 @@ var ChatService = class {
46255
46591
  const publishedRecordUid = stringValue$2(data.record_uid).trim() || createdRecordUid;
46256
46592
  const senderDisplayName = profile.nickname.trim() || profile.displayName.trim() || "我";
46257
46593
  const senderAvatarUrl = profile.avatarRef.trim();
46258
- await this.realtime.invalidateRecordProjection();
46594
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46259
46595
  return {
46260
46596
  sid: detail.sid,
46261
46597
  recordUid: publishedRecordUid,
@@ -46350,7 +46686,7 @@ var ChatService = class {
46350
46686
  content_payload: contentPayload,
46351
46687
  send_at: Date.now()
46352
46688
  }, session, options.signal);
46353
- await this.realtime.invalidateRecordProjection();
46689
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46354
46690
  return await appendCommentWarning({
46355
46691
  sourceRef: targetSourceRef,
46356
46692
  itemUid: stringValue$2(data.record_uid).trim() || recordUid,
@@ -46366,7 +46702,7 @@ var ChatService = class {
46366
46702
  content_payload: contentPayload,
46367
46703
  send_at: Date.now()
46368
46704
  }, session, options.signal);
46369
- await this.realtime.invalidateRecordProjection();
46705
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46370
46706
  return await appendCommentWarning({
46371
46707
  sourceRef: targetSourceRef,
46372
46708
  itemUid: stringValue$2(data.record_uid).trim() || recordUid,
@@ -46389,7 +46725,7 @@ var ChatService = class {
46389
46725
  ...options.recordDurationMillis === void 0 ? {} : { recordDurationMillis: options.recordDurationMillis },
46390
46726
  ...options.captureContext === void 0 ? {} : { captureContext: options.captureContext }
46391
46727
  });
46392
- if (result.localState !== "failed") await this.realtime.invalidateRecordProjection();
46728
+ if (result.localState !== "failed") await this.realtime.invalidateRecordProjection({ contentOnly: true });
46393
46729
  return await this.withConfirmedSendReferences({
46394
46730
  sourceRef,
46395
46731
  itemUid: result.recordUid,
@@ -46422,7 +46758,7 @@ var ChatService = class {
46422
46758
  send_at: sendAtMillis
46423
46759
  }, session);
46424
46760
  await this.record.syncCreatedRecordTags?.(stringValue$2(result.record_uid).trim() || recordUid, text, session.userId);
46425
- await this.realtime.invalidateRecordProjection();
46761
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46426
46762
  return await this.withConfirmedSendReferences({
46427
46763
  sourceRef,
46428
46764
  itemUid: stringValue$2(result.record_uid).trim() || recordUid,
@@ -46793,7 +47129,7 @@ var ChatService = class {
46793
47129
  if (source.kind === "send_to_self" || source.kind === "default_category") {
46794
47130
  const result = await this.runtime.authenticatedPost("/api/v1/records/create", commonBody, session, options.signal, { trackWriteOutcome: true });
46795
47131
  await this.record.syncCreatedRecordTags?.(stringValue$2(result.record_uid).trim() || recordUid, textContent, session.userId, options.signal, input.textFormat);
46796
- await this.realtime.invalidateRecordProjection();
47132
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46797
47133
  return await this.withConfirmedSendReferences({
46798
47134
  sourceRef,
46799
47135
  itemUid: stringValue$2(result.record_uid).trim() || recordUid,
@@ -46818,7 +47154,7 @@ var ChatService = class {
46818
47154
  ...commonBody
46819
47155
  }, session, options.signal, { trackWriteOutcome: true });
46820
47156
  await this.record.syncCreatedRecordTags?.(stringValue$2(result.record_uid).trim() || recordUid, textContent, session.userId, options.signal, input.textFormat);
46821
- await this.realtime.invalidateRecordProjection();
47157
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46822
47158
  return await this.withConfirmedSendReferences({
46823
47159
  sourceRef,
46824
47160
  itemUid: stringValue$2(result.record_uid).trim() || recordUid,
@@ -89683,6 +90019,7 @@ var ArkmeService = class {
89683
90019
  return await this.backgroundSoundPreferenceOwner.update(enabled, signal, expectedUserId);
89684
90020
  }
89685
90021
  clearAccountState(userIds) {
90022
+ this.calendar.dispose();
89686
90023
  this.directory.reset();
89687
90024
  this.realtime.resetAttentionSummary();
89688
90025
  for (const userId of userIds) this.privacy.clear(userId);
@@ -90911,7 +91248,7 @@ var ArkmeService = class {
90911
91248
  }
90912
91249
  async createText(recordUid, textContent) {
90913
91250
  const result = await this.record.createText(recordUid, textContent);
90914
- await this.realtime.invalidateRecordProjection();
91251
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
90915
91252
  return result;
90916
91253
  }
90917
91254
  async listRecordTags(limit = 100, signal) {
@@ -90919,7 +91256,7 @@ var ArkmeService = class {
90919
91256
  }
90920
91257
  async createTextForConversation(recordUid, textContent) {
90921
91258
  const result = await this.record.createTextForConversation(recordUid, textContent);
90922
- if (result.localState !== "failed") await this.realtime.invalidateRecordProjection();
91259
+ if (result.localState !== "failed") await this.realtime.invalidateRecordProjection({ contentOnly: true });
90923
91260
  return result;
90924
91261
  }
90925
91262
  async createDSHAgentInputText(recordUid, textContent, sendAtMillis) {