@senguoyun/dsh-arkme 0.1.58 → 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 (84) hide show
  1. package/docs/dsh-input-system-topic.md +7 -3
  2. package/docs/plugin-release.md +1 -1
  3. package/lib/client.js +3479 -1686
  4. package/lib/client.js.map +1 -1
  5. package/lib/harness-sidebar-client.js +214 -23
  6. package/lib/index.js +51 -6
  7. package/lib/persistent-extension.js +1 -1
  8. package/lib/{persistent-runtime-BnDj8qcY.js → persistent-runtime-DqPfc6RO.js} +411 -66
  9. package/lib/types/arkme-service.d.ts +3 -0
  10. package/lib/types/arkme-service.d.ts.map +1 -1
  11. package/lib/types/client/ArkmeCalendarSurface.d.ts +22 -1
  12. package/lib/types/client/ArkmeCalendarSurface.d.ts.map +1 -1
  13. package/lib/types/client/ArkmeCallSurface.d.ts.map +1 -1
  14. package/lib/types/client/ArkmeDshMenu.d.ts +38 -0
  15. package/lib/types/client/ArkmeDshMenu.d.ts.map +1 -0
  16. package/lib/types/client/ArkmeGroupChatControls.d.ts +4 -0
  17. package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
  18. package/lib/types/client/ArkmeProductNavigation.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  20. package/lib/types/client/ArkmeSourceBreadcrumb.d.ts +6 -2
  21. package/lib/types/client/ArkmeSourceBreadcrumb.d.ts.map +1 -1
  22. package/lib/types/client/ArkmeTopicDirectoryPopover.d.ts +4 -2
  23. package/lib/types/client/ArkmeTopicDirectoryPopover.d.ts.map +1 -1
  24. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  25. package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
  26. package/lib/types/client/PrivateChatActions.d.ts +15 -3
  27. package/lib/types/client/PrivateChatActions.d.ts.map +1 -1
  28. package/lib/types/client/calendar-invalidation-store.d.ts +10 -1
  29. package/lib/types/client/calendar-invalidation-store.d.ts.map +1 -1
  30. package/lib/types/client/calendar-month-cache.d.ts +37 -0
  31. package/lib/types/client/calendar-month-cache.d.ts.map +1 -0
  32. package/lib/types/client/conversation-export.d.ts +18 -0
  33. package/lib/types/client/conversation-export.d.ts.map +1 -0
  34. package/lib/types/client/conversation-menu-layer.d.ts +27 -0
  35. package/lib/types/client/conversation-menu-layer.d.ts.map +1 -0
  36. package/lib/types/client/conversation-menu-scrollbars.d.ts +3 -0
  37. package/lib/types/client/conversation-menu-scrollbars.d.ts.map +1 -0
  38. package/lib/types/client/conversation-selector-style.d.ts +34 -0
  39. package/lib/types/client/conversation-selector-style.d.ts.map +1 -0
  40. package/lib/types/client/harness-session-dropdown.d.ts.map +1 -1
  41. package/lib/types/client/harness-session-hover.d.ts +1 -1
  42. package/lib/types/client/harness-session-hover.d.ts.map +1 -1
  43. package/lib/types/client/harness-session-menu-bridge.d.ts +2 -0
  44. package/lib/types/client/harness-session-menu-bridge.d.ts.map +1 -1
  45. package/lib/types/client/harness-surface-viewport.d.ts +7 -0
  46. package/lib/types/client/harness-surface-viewport.d.ts.map +1 -0
  47. package/lib/types/client/live-photo-playback.d.ts.map +1 -1
  48. package/lib/types/client/navigation-cache.d.ts +6 -1
  49. package/lib/types/client/navigation-cache.d.ts.map +1 -1
  50. package/lib/types/client/provider-instance-runtime.d.ts.map +1 -1
  51. package/lib/types/client/realtime-client-events.d.ts.map +1 -1
  52. package/lib/types/client/self-topic-directory-cache.d.ts +42 -0
  53. package/lib/types/client/self-topic-directory-cache.d.ts.map +1 -0
  54. package/lib/types/client/self-topic-expansion-preference.d.ts +23 -0
  55. package/lib/types/client/self-topic-expansion-preference.d.ts.map +1 -0
  56. package/lib/types/client/self-topic-menu-bridge.d.ts +20 -0
  57. package/lib/types/client/self-topic-menu-bridge.d.ts.map +1 -0
  58. package/lib/types/client/self-topic-menu-hover.d.ts +3 -0
  59. package/lib/types/client/self-topic-menu-hover.d.ts.map +1 -0
  60. package/lib/types/client/use-calendar-month.d.ts +4 -0
  61. package/lib/types/client/use-calendar-month.d.ts.map +1 -0
  62. package/lib/types/dsh-agent-input-source.d.ts +2 -0
  63. package/lib/types/dsh-agent-input-source.d.ts.map +1 -1
  64. package/lib/types/dsh-remote/session-persistence.d.ts +4 -0
  65. package/lib/types/dsh-remote/session-persistence.d.ts.map +1 -0
  66. package/lib/types/dsh-remote/transport-fragment.d.ts.map +1 -1
  67. package/lib/types/host-api.d.ts.map +1 -1
  68. package/lib/types/index.d.ts.map +1 -1
  69. package/lib/types/services/calendar-service.d.ts +10 -0
  70. package/lib/types/services/calendar-service.d.ts.map +1 -1
  71. package/lib/types/services/call-history-service.d.ts +4 -2
  72. package/lib/types/services/call-history-service.d.ts.map +1 -1
  73. package/lib/types/services/chat-realtime-service.d.ts +3 -1
  74. package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
  75. package/lib/types/services/chat-service.d.ts +3 -1
  76. package/lib/types/services/chat-service.d.ts.map +1 -1
  77. package/lib/types/services/record-service.d.ts.map +1 -1
  78. package/lib/types/services/service.d.ts +5 -0
  79. package/lib/types/services/service.d.ts.map +1 -1
  80. package/lib/types/services/source-service.d.ts +3 -0
  81. package/lib/types/services/source-service.d.ts.map +1 -1
  82. package/lib/types/types.d.ts +3 -1
  83. package/lib/types/types.d.ts.map +1 -1
  84. 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,
@@ -38669,10 +38953,12 @@ var CallHistoryService = class {
38669
38953
  runtime;
38670
38954
  profile;
38671
38955
  media;
38672
- constructor(runtime, profile, media) {
38956
+ source;
38957
+ constructor(runtime, profile, media, source) {
38673
38958
  this.runtime = runtime;
38674
38959
  this.profile = profile;
38675
38960
  this.media = media;
38961
+ this.source = source;
38676
38962
  }
38677
38963
  async listCallHistory(options = {}, signal) {
38678
38964
  const session = await this.runtime.requireSession();
@@ -38688,7 +38974,7 @@ var CallHistoryService = class {
38688
38974
  failureCooldownMs: 2e3
38689
38975
  });
38690
38976
  const validItems = (await Promise.all(this.historyItems(raw).map(async (item) => await this.normalizeHistoryItem(item, session.userId)))).filter((item) => item !== void 0);
38691
- await this.attachPeerAvatars(validItems, session, signal);
38977
+ await this.attachPeerPresentation(validItems, session, signal);
38692
38978
  const hasMore = booleanValue$9(raw.has_more ?? raw.hasMore);
38693
38979
  const nextCursor = firstString$1(raw, [
38694
38980
  "next_cursor",
@@ -38931,16 +39217,18 @@ var CallHistoryService = class {
38931
39217
  return [];
38932
39218
  }
38933
39219
  }
38934
- async attachPeerAvatars(items, session, signal) {
39220
+ async attachPeerPresentation(items, session, signal) {
38935
39221
  const ids = [...new Set(items.flatMap((item) => item.peerUserId === void 0 ? [] : [item.peerUserId]))];
38936
39222
  if (ids.length === 0) return;
38937
- const profiles = await this.profile.publicProfilesByUserIds(ids, session, signal).catch(() => /* @__PURE__ */ new Map());
39223
+ const [profiles, remarks] = await Promise.all([this.profile.publicProfileSummariesByUserIds(ids, session, signal).catch(() => /* @__PURE__ */ new Map()), this.source?.privateRemarksByUserIds(ids, signal === void 0 ? {} : { signal }).catch(() => /* @__PURE__ */ new Map()) ?? Promise.resolve(/* @__PURE__ */ new Map())]);
38938
39224
  await Promise.all(items.map(async (item) => {
38939
39225
  const peerUserId = item.peerUserId;
38940
39226
  if (peerUserId === void 0) return;
38941
39227
  const profile = profiles.get(peerUserId);
38942
39228
  const displayName = profile?.displayName?.trim() ?? "";
38943
- if (displayName !== "" && /^Arkme 用户 \d+$/.test(item.peerDisplayName.trim())) item.peerDisplayName = displayName;
39229
+ const remark = remarks.get(peerUserId)?.trim() ?? "";
39230
+ if (remark !== "") item.peerDisplayName = remark;
39231
+ else if (displayName !== "" && /^Arkme 用户 \d+$/.test(item.peerDisplayName.trim())) item.peerDisplayName = displayName;
38944
39232
  if (profile?.avatarUrl === void 0) return;
38945
39233
  item.peerAvatarRef = await this.profile.sealProfileImageRef(session.userId, peerUserId);
38946
39234
  }));
@@ -40337,7 +40625,7 @@ var ChatRealtimeService = class {
40337
40625
  console.warn("dsh-arkme: Chat read receipt invalidation failed:", safeFailureMessage$5(error));
40338
40626
  }
40339
40627
  }
40340
- async invalidateRecordProjection() {
40628
+ async invalidateRecordProjection(options = {}) {
40341
40629
  try {
40342
40630
  const session = await this.runtime.sessionStore.read();
40343
40631
  if (session === void 0) return;
@@ -40346,7 +40634,8 @@ var ChatRealtimeService = class {
40346
40634
  this.emitChatClientEvent({
40347
40635
  type: "projection-invalidated",
40348
40636
  revision: this.nextChatClientRevision(),
40349
- projection: "record"
40637
+ projection: "record",
40638
+ ...options.contentOnly ? { retainTopicCounts: true } : {}
40350
40639
  });
40351
40640
  } catch (error) {
40352
40641
  console.warn("dsh-arkme: Record projection invalidation failed:", safeFailureMessage$5(error));
@@ -40971,7 +41260,7 @@ var ChatRealtimeService = class {
40971
41260
  if (ownerGeneration !== this.attentionOwnerGeneration) return [];
40972
41261
  if (updates.length > 0) {
40973
41262
  this.source.invalidateSourceListCache(session.userId, "root");
40974
- 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)]));
40975
41264
  this.emitChatClientEvent({
40976
41265
  type: "sessions-delta",
40977
41266
  revision: this.nextChatClientRevision(),
@@ -41860,7 +42149,6 @@ function numberValue$20(value) {
41860
42149
  function listValue$17(value) {
41861
42150
  return Array.isArray(value) ? value : [];
41862
42151
  }
41863
- const DSH_AGENT_INPUT_CREATION_SOURCE = 3;
41864
42152
  const MAX_DEFAULT_CATEGORY_FILTER_BACKFILL_PAGES = 5;
41865
42153
  const RECORD_REEDIT_SUPPORTED_TEMPLATE_KINDS = /* @__PURE__ */ new Set([
41866
42154
  1,
@@ -41882,7 +42170,7 @@ function recordCreationSource(raw) {
41882
42170
  return integerLikeValue$1(item.creation_source ?? item.creationSource ?? core.creation_source ?? core.creationSource);
41883
42171
  }
41884
42172
  function isDSHAgentInputRecord(raw) {
41885
- return recordCreationSource(raw) === DSH_AGENT_INPUT_CREATION_SOURCE;
42173
+ return isDshAgentInputRawRecord(raw);
41886
42174
  }
41887
42175
  function safeFailureMessage$4(error) {
41888
42176
  if (error instanceof ArkmePluginError) return error.message;
@@ -43784,7 +44072,11 @@ function snapshotDetailFromChatRaw(raw, fallback) {
43784
44072
  ...position,
43785
44073
  ...objectValue$2(values.position)
43786
44074
  });
43787
- const locationLabel = firstSnapshotText(position.poi, position.poi_name, position.poiName, position.address, position.road, location.poi_name, location.poiName, location.address, location.name);
44075
+ const locationLabel = firstSnapshotText([
44076
+ [firstSnapshotText(position.city)?.replace(/市$/, ""), firstSnapshotText(position.county)?.replace(/(区|县|市)$/, "")].filter(Boolean).join("·"),
44077
+ firstSnapshotText(position.road),
44078
+ firstSnapshotText(position.poi, position.poi_name, position.poiName)
44079
+ ].join(""), position.address, location.poi_name, location.poiName, location.address, location.name);
43788
44080
  const weather = snapshotWeatherText(position.weather, position.weatherText, location.weather, values.weather);
43789
44081
  const altitudeMeters = nonNegativeNumberValue(location.altitude, location.alt, position.altitude, position.alt);
43790
44082
  const movement = snapshotMovement({
@@ -45022,9 +45314,13 @@ var ChatService = class {
45022
45314
  const opened = await this.source.openSourceRef(topic.sourceRef, session.userId);
45023
45315
  if (opened.kind !== "topic") throw new ArkmePluginError("topic-subtree-source-invalid", "下级主题信息无效,请刷新后重试", true, 409);
45024
45316
  const records = [];
45317
+ const visited = /* @__PURE__ */ new Set();
45025
45318
  let pageCursor = cursor;
45026
45319
  for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
45027
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);
45028
45324
  const page = await readTopicRecordPage(this.runtime, session, opened.ownerRef, {
45029
45325
  limit,
45030
45326
  ...pageCursor === void 0 ? {} : { cursor: pageCursor },
@@ -45039,24 +45335,35 @@ var ChatService = class {
45039
45335
  hasMore: false
45040
45336
  };
45041
45337
  }
45338
+ let filtered = false;
45042
45339
  for (const raw of page.records) {
45043
- 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
+ }
45044
45344
  records.push(raw);
45045
45345
  }
45046
- if (!merged) return {
45346
+ if (!merged && !filtered && records.length <= limit) return {
45047
45347
  topic,
45048
45348
  topicUid: opened.ownerRef,
45049
45349
  records,
45050
45350
  hasMore: page.hasMore,
45051
45351
  ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor }
45052
45352
  };
45053
- if (records.length >= limit) return {
45054
- topic,
45055
- topicUid: opened.ownerRef,
45056
- records: records.slice(0, limit),
45057
- hasMore: records.length > limit || page.hasMore,
45058
- ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor }
45059
- };
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
+ }
45060
45367
  if (!page.hasMore) return {
45061
45368
  topic,
45062
45369
  topicUid: opened.ownerRef,
@@ -45095,13 +45402,55 @@ var ChatService = class {
45095
45402
  const limit = Math.min(100, Math.max(1, Math.trunc(options.limit ?? 30)));
45096
45403
  if (source.kind === "send_to_self") {
45097
45404
  const lockedRecordUids = await this.privacy.lockedRecordUids(session, options.signal);
45098
- const data = await this.runtime.authenticatedPost("/api/v1/home/feed/query", {
45099
- limit,
45100
- source_kinds: [1, 2],
45101
- ...options.cursor?.sendAtMillis === void 0 ? {} : { cursor_send_at: options.cursor.sendAtMillis },
45102
- ...options.cursor?.itemUid === void 0 ? {} : { cursor_record_uid: options.cursor.itemUid }
45103
- }, session, options.signal);
45104
- 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
+ }
45105
45454
  const media = await this.media.hydrateRecordMediaPage(rawRecords, session, options.signal);
45106
45455
  const signingKey = await this.runtime.stateStore.uniqueCode();
45107
45456
  const items = (await Promise.all(rawRecords.map(async (raw) => {
@@ -45132,16 +45481,11 @@ var ChatService = class {
45132
45481
  }))).filter((item) => item.itemUid !== "");
45133
45482
  this.hydrateTimelineExtensionParents(items);
45134
45483
  const projectedItems = items.map((item) => this.withRecordMessageActionRef(source, item, session.userId, signingKey));
45135
- const nextSendAt = numberValue$19(data.next_cursor_send_at);
45136
- const nextUid = stringValue$2(data.next_cursor_record_uid).trim();
45137
45484
  return {
45138
45485
  source: await this.source.sourceItem(source),
45139
45486
  items: projectedItems,
45140
- hasMore: data.has_more === true,
45141
- ...nextSendAt > 0 && nextUid !== "" ? { nextCursor: {
45142
- sendAtMillis: nextSendAt,
45143
- itemUid: nextUid
45144
- } } : {}
45487
+ hasMore,
45488
+ ...nextCursor === void 0 ? {} : { nextCursor }
45145
45489
  };
45146
45490
  }
45147
45491
  if (source.kind === "default_category") {
@@ -46247,7 +46591,7 @@ var ChatService = class {
46247
46591
  const publishedRecordUid = stringValue$2(data.record_uid).trim() || createdRecordUid;
46248
46592
  const senderDisplayName = profile.nickname.trim() || profile.displayName.trim() || "我";
46249
46593
  const senderAvatarUrl = profile.avatarRef.trim();
46250
- await this.realtime.invalidateRecordProjection();
46594
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46251
46595
  return {
46252
46596
  sid: detail.sid,
46253
46597
  recordUid: publishedRecordUid,
@@ -46342,7 +46686,7 @@ var ChatService = class {
46342
46686
  content_payload: contentPayload,
46343
46687
  send_at: Date.now()
46344
46688
  }, session, options.signal);
46345
- await this.realtime.invalidateRecordProjection();
46689
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46346
46690
  return await appendCommentWarning({
46347
46691
  sourceRef: targetSourceRef,
46348
46692
  itemUid: stringValue$2(data.record_uid).trim() || recordUid,
@@ -46358,7 +46702,7 @@ var ChatService = class {
46358
46702
  content_payload: contentPayload,
46359
46703
  send_at: Date.now()
46360
46704
  }, session, options.signal);
46361
- await this.realtime.invalidateRecordProjection();
46705
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46362
46706
  return await appendCommentWarning({
46363
46707
  sourceRef: targetSourceRef,
46364
46708
  itemUid: stringValue$2(data.record_uid).trim() || recordUid,
@@ -46381,7 +46725,7 @@ var ChatService = class {
46381
46725
  ...options.recordDurationMillis === void 0 ? {} : { recordDurationMillis: options.recordDurationMillis },
46382
46726
  ...options.captureContext === void 0 ? {} : { captureContext: options.captureContext }
46383
46727
  });
46384
- if (result.localState !== "failed") await this.realtime.invalidateRecordProjection();
46728
+ if (result.localState !== "failed") await this.realtime.invalidateRecordProjection({ contentOnly: true });
46385
46729
  return await this.withConfirmedSendReferences({
46386
46730
  sourceRef,
46387
46731
  itemUid: result.recordUid,
@@ -46414,7 +46758,7 @@ var ChatService = class {
46414
46758
  send_at: sendAtMillis
46415
46759
  }, session);
46416
46760
  await this.record.syncCreatedRecordTags?.(stringValue$2(result.record_uid).trim() || recordUid, text, session.userId);
46417
- await this.realtime.invalidateRecordProjection();
46761
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46418
46762
  return await this.withConfirmedSendReferences({
46419
46763
  sourceRef,
46420
46764
  itemUid: stringValue$2(result.record_uid).trim() || recordUid,
@@ -46785,7 +47129,7 @@ var ChatService = class {
46785
47129
  if (source.kind === "send_to_self" || source.kind === "default_category") {
46786
47130
  const result = await this.runtime.authenticatedPost("/api/v1/records/create", commonBody, session, options.signal, { trackWriteOutcome: true });
46787
47131
  await this.record.syncCreatedRecordTags?.(stringValue$2(result.record_uid).trim() || recordUid, textContent, session.userId, options.signal, input.textFormat);
46788
- await this.realtime.invalidateRecordProjection();
47132
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46789
47133
  return await this.withConfirmedSendReferences({
46790
47134
  sourceRef,
46791
47135
  itemUid: stringValue$2(result.record_uid).trim() || recordUid,
@@ -46810,7 +47154,7 @@ var ChatService = class {
46810
47154
  ...commonBody
46811
47155
  }, session, options.signal, { trackWriteOutcome: true });
46812
47156
  await this.record.syncCreatedRecordTags?.(stringValue$2(result.record_uid).trim() || recordUid, textContent, session.userId, options.signal, input.textFormat);
46813
- await this.realtime.invalidateRecordProjection();
47157
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
46814
47158
  return await this.withConfirmedSendReferences({
46815
47159
  sourceRef,
46816
47160
  itemUid: stringValue$2(result.record_uid).trim() || recordUid,
@@ -89513,7 +89857,7 @@ var ArkmeService = class {
89513
89857
  this.arrangement = new ArrangementService(this.runtime);
89514
89858
  this.wechat = new WechatService(this.runtime);
89515
89859
  this.profile = new ProfileService(this.runtime);
89516
- this.callHistory = new CallHistoryService(this.runtime, this.profile, { forwardContentBlocks: (files, viewerUserId) => this.media.forwardContentBlocks(files, viewerUserId) });
89860
+ this.callHistory = new CallHistoryService(this.runtime, this.profile, { forwardContentBlocks: (files, viewerUserId) => this.media.forwardContentBlocks(files, viewerUserId) }, { privateRemarksByUserIds: (userIds, options) => this.source.privateRemarksByUserIds(userIds, options) });
89517
89861
  this.extensionReview = new ExtensionReviewService(this.runtime, this.profile, { createTextForConversation: async (recordUid, textContent) => {
89518
89862
  return await this.createTextForConversation(recordUid, textContent);
89519
89863
  } });
@@ -89675,6 +90019,7 @@ var ArkmeService = class {
89675
90019
  return await this.backgroundSoundPreferenceOwner.update(enabled, signal, expectedUserId);
89676
90020
  }
89677
90021
  clearAccountState(userIds) {
90022
+ this.calendar.dispose();
89678
90023
  this.directory.reset();
89679
90024
  this.realtime.resetAttentionSummary();
89680
90025
  for (const userId of userIds) this.privacy.clear(userId);
@@ -90903,7 +91248,7 @@ var ArkmeService = class {
90903
91248
  }
90904
91249
  async createText(recordUid, textContent) {
90905
91250
  const result = await this.record.createText(recordUid, textContent);
90906
- await this.realtime.invalidateRecordProjection();
91251
+ await this.realtime.invalidateRecordProjection({ contentOnly: true });
90907
91252
  return result;
90908
91253
  }
90909
91254
  async listRecordTags(limit = 100, signal) {
@@ -90911,7 +91256,7 @@ var ArkmeService = class {
90911
91256
  }
90912
91257
  async createTextForConversation(recordUid, textContent) {
90913
91258
  const result = await this.record.createTextForConversation(recordUid, textContent);
90914
- if (result.localState !== "failed") await this.realtime.invalidateRecordProjection();
91259
+ if (result.localState !== "failed") await this.realtime.invalidateRecordProjection({ contentOnly: true });
90915
91260
  return result;
90916
91261
  }
90917
91262
  async createDSHAgentInputText(recordUid, textContent, sendAtMillis) {