@senguoyun/dsh-arkme 0.1.34 → 0.1.35

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 (96) hide show
  1. package/README.md +1 -1
  2. package/docs/plugin-release.md +14 -1
  3. package/lib/client.js +33306 -30787
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +46 -4
  6. package/lib/persistent-extension.js +1 -1
  7. package/lib/{persistent-runtime-CZQTfyC1.js → persistent-runtime-e4gV6dqq.js} +1071 -609
  8. package/lib/sdk.js +19 -0
  9. package/lib/types/arkme-service.d.ts +5 -1
  10. package/lib/types/arkme-service.d.ts.map +1 -1
  11. package/lib/types/billing-gateway.d.ts.map +1 -1
  12. package/lib/types/client/ArkmeAvatar.d.ts +16 -10
  13. package/lib/types/client/ArkmeAvatar.d.ts.map +1 -1
  14. package/lib/types/client/ArkmeBillingSettings.d.ts +4 -0
  15. package/lib/types/client/ArkmeBillingSettings.d.ts.map +1 -1
  16. package/lib/types/client/ArkmeCalendarSurface.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeCallHistorySurface.d.ts.map +1 -1
  18. package/lib/types/client/ArkmeCallSurface.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeChatMemberActions.d.ts +4 -3
  20. package/lib/types/client/ArkmeChatMemberActions.d.ts.map +1 -1
  21. package/lib/types/client/ArkmeExtensionReviews.d.ts +2 -1
  22. package/lib/types/client/ArkmeExtensionReviews.d.ts.map +1 -1
  23. package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
  24. package/lib/types/client/ArkmeMarketplace.d.ts +4 -2
  25. package/lib/types/client/ArkmeMarketplace.d.ts.map +1 -1
  26. package/lib/types/client/ArkmeMessageReportDialog.d.ts +8 -0
  27. package/lib/types/client/ArkmeMessageReportDialog.d.ts.map +1 -0
  28. package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
  29. package/lib/types/client/ArkmeOutgoingCallHost.d.ts.map +1 -1
  30. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  31. package/lib/types/client/ArkmeRelatedQuickNotes.d.ts +47 -0
  32. package/lib/types/client/ArkmeRelatedQuickNotes.d.ts.map +1 -0
  33. package/lib/types/client/ArkmeRichComposerInput.d.ts.map +1 -1
  34. package/lib/types/client/ArkmeSearchSurface.d.ts.map +1 -1
  35. package/lib/types/client/ArkmeSidebar.d.ts +7 -4
  36. package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
  37. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  38. package/lib/types/client/ArkmeWorldSurface.d.ts +6 -1
  39. package/lib/types/client/ArkmeWorldSurface.d.ts.map +1 -1
  40. package/lib/types/client/api.d.ts +1 -1
  41. package/lib/types/client/api.d.ts.map +1 -1
  42. package/lib/types/client/arkme-billing.d.ts +7 -1
  43. package/lib/types/client/arkme-billing.d.ts.map +1 -1
  44. package/lib/types/client/avatar-image-runtime.d.ts +3 -0
  45. package/lib/types/client/avatar-image-runtime.d.ts.map +1 -0
  46. package/lib/types/client/avatar-image-store.d.ts +44 -0
  47. package/lib/types/client/avatar-image-store.d.ts.map +1 -0
  48. package/lib/types/client/calendar-invalidation-store.d.ts +26 -0
  49. package/lib/types/client/calendar-invalidation-store.d.ts.map +1 -0
  50. package/lib/types/client/chat-directory-store.d.ts +23 -13
  51. package/lib/types/client/chat-directory-store.d.ts.map +1 -1
  52. package/lib/types/client/composer-draft-store.d.ts +3 -3
  53. package/lib/types/client/composer-draft-store.d.ts.map +1 -1
  54. package/lib/types/client/composer-placeholder.d.ts +13 -0
  55. package/lib/types/client/composer-placeholder.d.ts.map +1 -0
  56. package/lib/types/client/conversation-memory-cache.d.ts +11 -14
  57. package/lib/types/client/conversation-memory-cache.d.ts.map +1 -1
  58. package/lib/types/client/file-send-tasks.d.ts +3 -1
  59. package/lib/types/client/file-send-tasks.d.ts.map +1 -1
  60. package/lib/types/client/interwoven-moments.d.ts +13 -2
  61. package/lib/types/client/interwoven-moments.d.ts.map +1 -1
  62. package/lib/types/client/navigation-cache.d.ts.map +1 -1
  63. package/lib/types/client/presentation-maintenance-runtime.d.ts +13 -0
  64. package/lib/types/client/presentation-maintenance-runtime.d.ts.map +1 -0
  65. package/lib/types/client/provider-instance-runtime.d.ts +5 -2
  66. package/lib/types/client/provider-instance-runtime.d.ts.map +1 -1
  67. package/lib/types/client/realtime-client-events.d.ts +6 -0
  68. package/lib/types/client/realtime-client-events.d.ts.map +1 -1
  69. package/lib/types/client/redesign/contacts/contacts-tab-store.d.ts.map +1 -1
  70. package/lib/types/client/related-recordings.d.ts +1 -1
  71. package/lib/types/client/source-identity.d.ts +5 -0
  72. package/lib/types/client/source-identity.d.ts.map +1 -0
  73. package/lib/types/client/ui-controller.d.ts +6 -0
  74. package/lib/types/client/ui-controller.d.ts.map +1 -1
  75. package/lib/types/client/use-arkme-avatar-image.d.ts +3 -0
  76. package/lib/types/client/use-arkme-avatar-image.d.ts.map +1 -0
  77. package/lib/types/extensions/manager.d.ts.map +1 -1
  78. package/lib/types/extensions/types.d.ts +1 -0
  79. package/lib/types/extensions/types.d.ts.map +1 -1
  80. package/lib/types/host-api.d.ts.map +1 -1
  81. package/lib/types/index.d.ts +1 -1
  82. package/lib/types/index.d.ts.map +1 -1
  83. package/lib/types/request-coordinator.d.ts.map +1 -1
  84. package/lib/types/sdk/index.d.ts +7 -2
  85. package/lib/types/sdk/index.d.ts.map +1 -1
  86. package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
  87. package/lib/types/services/chat-service.d.ts +7 -1
  88. package/lib/types/services/chat-service.d.ts.map +1 -1
  89. package/lib/types/services/interwoven-service.d.ts +3 -0
  90. package/lib/types/services/interwoven-service.d.ts.map +1 -1
  91. package/lib/types/services/related-quick-note-service.d.ts +38 -0
  92. package/lib/types/services/related-quick-note-service.d.ts.map +1 -0
  93. package/lib/types/services/world-service.d.ts.map +1 -1
  94. package/lib/types/types.d.ts +62 -5
  95. package/lib/types/types.d.ts.map +1 -1
  96. package/package.json +5 -5
@@ -525,6 +525,7 @@ var ArkmeRequestCoordinator = class {
525
525
  this.bump(request, "staleDropped");
526
526
  throw new ArkmeStaleRequestError();
527
527
  }
528
+ if (this.inFlight.get(fullKey)?.promise !== promise) return value;
528
529
  this.failures.delete(fullKey);
529
530
  if (cacheMs > 0) this.cache.set(fullKey, {
530
531
  value: clone(value),
@@ -533,7 +534,7 @@ var ArkmeRequestCoordinator = class {
533
534
  });
534
535
  return value;
535
536
  }).catch((error) => {
536
- if (this.epoch(scope) === epoch && failureCooldownMs > 0 && (request.shouldCooldown?.(error) ?? true)) {
537
+ if (this.epoch(scope) === epoch && this.inFlight.get(fullKey)?.promise === promise && failureCooldownMs > 0 && (request.shouldCooldown?.(error) ?? true)) {
537
538
  const jitter = .9 + this.random() * .2;
538
539
  this.failures.set(fullKey, {
539
540
  error,
@@ -570,6 +571,7 @@ var ArkmeRequestCoordinator = class {
570
571
  const prefix = `${scopeInput.trim() || "public"}\u0000${keyPrefix}`;
571
572
  for (const key of this.cache.keys()) if (key.startsWith(prefix)) this.cache.delete(key);
572
573
  for (const key of this.failures.keys()) if (key.startsWith(prefix)) this.failures.delete(key);
574
+ for (const key of this.inFlight.keys()) if (key.startsWith(prefix)) this.inFlight.delete(key);
573
575
  }
574
576
  snapshotStats() {
575
577
  return Object.fromEntries([...this.stats].map(([key, value]) => [key, { ...value }]));
@@ -1469,7 +1471,7 @@ var HttpArkmeBillingGateway = class {
1469
1471
  return new ArkmePluginError(error.code, error.message, retryable, error.httpStatus, { cause: error });
1470
1472
  }
1471
1473
  async quota(signal) {
1472
- const data = objectValue$1(await this.post("intelligent", "/api/v1/managed-ai/balance/query", {}, "interactive-read", signal, "managed-ai-balance"));
1474
+ const data = objectValue$1(await this.post("intelligent", "/api/v1/managed-ai/balance/query", {}, "interactive-read", signal));
1473
1475
  const currency = stringValue$1(data.currency).trim();
1474
1476
  if (currency !== "CNY") throw new ArkmePluginError("billing-contract-invalid", "余额接口币种无效", true, 502);
1475
1477
  const availableNanoCny = nanoCny(data.available_nano_cny, "available_nano_cny");
@@ -1843,13 +1845,13 @@ function bestMatchingCategory(text) {
1843
1845
  }
1844
1846
  //#endregion
1845
1847
  //#region src/services/ai-video-service.ts
1846
- function numberValue$25(value) {
1848
+ function numberValue$26(value) {
1847
1849
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
1848
1850
  }
1849
1851
  function booleanValue$15(value) {
1850
1852
  return value === true;
1851
1853
  }
1852
- function listValue$26(value) {
1854
+ function listValue$27(value) {
1853
1855
  return Array.isArray(value) ? value : [];
1854
1856
  }
1855
1857
  var AiVideoService = class {
@@ -1863,9 +1865,9 @@ var AiVideoService = class {
1863
1865
  return {
1864
1866
  allowed: booleanValue$15(data.allowed),
1865
1867
  message: stringValue$1(data.message).trim() || "AI 视频内容检查已完成",
1866
- selectedDurationMillis: Math.max(0, numberValue$25(data.selected_duration_millis)),
1867
- minimumDurationMillis: Math.max(0, numberValue$25(data.minimum_duration_millis)),
1868
- selectedSegmentCount: Math.max(0, numberValue$25(data.selected_segment_count)),
1868
+ selectedDurationMillis: Math.max(0, numberValue$26(data.selected_duration_millis)),
1869
+ minimumDurationMillis: Math.max(0, numberValue$26(data.minimum_duration_millis)),
1870
+ selectedSegmentCount: Math.max(0, numberValue$26(data.selected_segment_count)),
1869
1871
  retryable: booleanValue$15(data.retryable),
1870
1872
  ...stringValue$1(data.reason_code).trim() === "" ? {} : { reasonCode: stringValue$1(data.reason_code).trim() },
1871
1873
  ...stringValue$1(data.proof).trim() === "" ? {} : { proof: stringValue$1(data.proof).trim() }
@@ -1901,7 +1903,7 @@ var AiVideoService = class {
1901
1903
  ...options.statuses === void 0 || options.statuses.length === 0 ? {} : { statuses: options.statuses }
1902
1904
  }, session, options.signal);
1903
1905
  return {
1904
- items: listValue$26(data.items).map((raw) => this.aiVideoListItem(raw)).filter((item) => item !== void 0),
1906
+ items: listValue$27(data.items).map((raw) => this.aiVideoListItem(raw)).filter((item) => item !== void 0),
1905
1907
  hasMore: booleanValue$15(data.has_more),
1906
1908
  ...stringValue$1(data.next_cursor).trim() === "" ? {} : { nextCursor: stringValue$1(data.next_cursor).trim() }
1907
1909
  };
@@ -1915,10 +1917,10 @@ var AiVideoService = class {
1915
1917
  return {
1916
1918
  allowed: booleanValue$15(data.allowed),
1917
1919
  message: stringValue$1(data.message).trim() || "AI 视频内容检查已完成",
1918
- selectedDurationMillis: Math.max(0, numberValue$25(data.selected_duration_millis)),
1919
- minimumDurationMillis: Math.max(0, numberValue$25(data.minimum_duration_millis)),
1920
- selectedSegmentCount: Math.max(0, numberValue$25(data.selected_segment_count)),
1921
- selectedTextCount: Math.max(0, numberValue$25(data.selected_text_count)),
1920
+ selectedDurationMillis: Math.max(0, numberValue$26(data.selected_duration_millis)),
1921
+ minimumDurationMillis: Math.max(0, numberValue$26(data.minimum_duration_millis)),
1922
+ selectedSegmentCount: Math.max(0, numberValue$26(data.selected_segment_count)),
1923
+ selectedTextCount: Math.max(0, numberValue$26(data.selected_text_count)),
1922
1924
  retryable: booleanValue$15(data.retryable),
1923
1925
  ...stringValue$1(data.reason_code).trim() === "" ? {} : { reasonCode: stringValue$1(data.reason_code).trim() },
1924
1926
  ...stringValue$1(data.proof).trim() === "" ? {} : { proof: stringValue$1(data.proof).trim() }
@@ -1958,19 +1960,19 @@ var AiVideoService = class {
1958
1960
  "canceled"
1959
1961
  ])).has(status)) throw new ArkmePluginError("ai-video-contract-invalid", "AI 视频服务返回了无效任务信息", true, 502);
1960
1962
  const selection = objectValue$1(data.selection);
1961
- const segmentCount = listValue$26(selection.segments).length;
1962
- const textCount = listValue$26(selection.texts).length;
1963
+ const segmentCount = listValue$27(selection.segments).length;
1964
+ const textCount = listValue$27(selection.texts).length;
1963
1965
  return {
1964
1966
  jobId,
1965
1967
  status,
1966
1968
  stage: stringValue$1(data.stage).trim() || status,
1967
- progress: Math.min(100, Math.max(0, Math.trunc(numberValue$25(data.progress)))),
1969
+ progress: Math.min(100, Math.max(0, Math.trunc(numberValue$26(data.progress)))),
1968
1970
  selectedSegmentCount: segmentCount,
1969
1971
  ...textCount === 0 ? {} : { selectedTextCount: textCount },
1970
1972
  retryable: booleanValue$15(data.retryable),
1971
1973
  ...stringValue$1(data.video_asset_uid).trim() === "" ? {} : { videoAssetUid: stringValue$1(data.video_asset_uid).trim() },
1972
1974
  ...stringValue$1(data.cover_asset_uid).trim() === "" ? {} : { coverAssetUid: stringValue$1(data.cover_asset_uid).trim() },
1973
- ...numberValue$25(data.video_duration_millis) <= 0 ? {} : { videoDurationMillis: numberValue$25(data.video_duration_millis) },
1975
+ ...numberValue$26(data.video_duration_millis) <= 0 ? {} : { videoDurationMillis: numberValue$26(data.video_duration_millis) },
1974
1976
  ...stringValue$1(data.error_code).trim() === "" ? {} : { errorCode: stringValue$1(data.error_code).trim() },
1975
1977
  ...stringValue$1(data.error_message).trim() === "" ? {} : { errorMessage: stringValue$1(data.error_message).trim() },
1976
1978
  ...stringValue$1(data.failure_stage).trim() === "" ? {} : { failureStage: stringValue$1(data.failure_stage).trim() }
@@ -1993,17 +1995,17 @@ var AiVideoService = class {
1993
1995
  sessionId: stringValue$1(item.session_id).trim(),
1994
1996
  status,
1995
1997
  stage: stringValue$1(item.stage).trim() || status,
1996
- progress: Math.min(100, Math.max(0, Math.trunc(numberValue$25(item.progress)))),
1998
+ progress: Math.min(100, Math.max(0, Math.trunc(numberValue$26(item.progress)))),
1997
1999
  title: stringValue$1(source.title).trim() || "长录音 AI 视频",
1998
- sourceStartedAtMillis: numberValue$25(source.started_at),
1999
- selectedDurationMillis: Math.max(0, numberValue$25(source.selected_duration_millis)),
2000
- selectedSegmentCount: Math.max(0, Math.trunc(numberValue$25(item.selected_segment_count))),
2000
+ sourceStartedAtMillis: numberValue$26(source.started_at),
2001
+ selectedDurationMillis: Math.max(0, numberValue$26(source.selected_duration_millis)),
2002
+ selectedSegmentCount: Math.max(0, Math.trunc(numberValue$26(item.selected_segment_count))),
2001
2003
  retryable: booleanValue$15(item.retryable),
2002
- createdAtMillis: numberValue$25(item.created_at),
2003
- updatedAtMillis: numberValue$25(item.updated_at),
2004
+ createdAtMillis: numberValue$26(item.created_at),
2005
+ updatedAtMillis: numberValue$26(item.updated_at),
2004
2006
  ...stringValue$1(item.cover_asset_uid).trim() === "" ? {} : { coverAssetUid: stringValue$1(item.cover_asset_uid).trim() },
2005
2007
  ...stringValue$1(item.video_asset_uid).trim() === "" ? {} : { videoAssetUid: stringValue$1(item.video_asset_uid).trim() },
2006
- ...numberValue$25(item.video_duration_millis) <= 0 ? {} : { videoDurationMillis: numberValue$25(item.video_duration_millis) },
2008
+ ...numberValue$26(item.video_duration_millis) <= 0 ? {} : { videoDurationMillis: numberValue$26(item.video_duration_millis) },
2007
2009
  ...stringValue$1(item.error_code).trim() === "" ? {} : { errorCode: stringValue$1(item.error_code).trim() },
2008
2010
  ...stringValue$1(item.error_message).trim() === "" ? {} : { errorMessage: clippedText(item.error_message, 500) }
2009
2011
  };
@@ -2029,13 +2031,13 @@ const ARKO_RUN_STATUSES = /* @__PURE__ */ new Set([
2029
2031
  "expired"
2030
2032
  ]);
2031
2033
  const ARKO_PROFILE_DISPLAY_NAME_CACHE_TTL_MS = 10 * 6e4;
2032
- function numberValue$24(value) {
2034
+ function numberValue$25(value) {
2033
2035
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
2034
2036
  }
2035
2037
  function booleanValue$14(value) {
2036
2038
  return value === true;
2037
2039
  }
2038
- function listValue$25(value) {
2040
+ function listValue$26(value) {
2039
2041
  return Array.isArray(value) ? value : [];
2040
2042
  }
2041
2043
  function optionalString$3(value) {
@@ -2064,7 +2066,7 @@ function joinUrlWithQuery(baseUrl, path, query) {
2064
2066
  }
2065
2067
  function arkoProfileFromData(data) {
2066
2068
  const displayName = stringValue$1(data.display_name).trim();
2067
- const version = numberValue$24(data.version);
2069
+ const version = numberValue$25(data.version);
2068
2070
  if (displayName === "" || !Number.isSafeInteger(version) || version < 0) throw new ArkmePluginError("arko-profile-contract-invalid", "Arko 资料响应不完整", true, 502);
2069
2071
  return {
2070
2072
  displayName,
@@ -2096,7 +2098,7 @@ function arkoModelCatalogFromData(data) {
2096
2098
  const defaultRouteKey = arkoModelRouteKey(data.default_route_key);
2097
2099
  const effectiveRouteKey = arkoModelRouteKey(data.effective_route_key);
2098
2100
  const selectionSource = stringValue$1(data.selection_source);
2099
- const options = listValue$25(data.items).map(arkoModelOptionFromData);
2101
+ const options = listValue$26(data.items).map(arkoModelOptionFromData);
2100
2102
  const routeKeys = new Set(options.map((option) => option.routeKey));
2101
2103
  const selected = options.filter((option) => option.selected);
2102
2104
  if (selectionSource !== "default" && selectionSource !== "personal" || options.length === 0 || options.length > 16 || routeKeys.size !== options.length || !routeKeys.has(defaultRouteKey) || !routeKeys.has(effectiveRouteKey) || selected.length !== 1 || selected[0]?.routeKey !== effectiveRouteKey) throw new ArkmePluginError("arko-model-contract-invalid", "Arko 模型目录响应无效", true, 502);
@@ -2117,9 +2119,9 @@ function parsedJsonRecord(value) {
2117
2119
  }
2118
2120
  function arkoHistoryItemFromData(value) {
2119
2121
  const data = objectValue$1(value);
2120
- const messageId = numberValue$24(data.id ?? data.msg_id ?? data.msgId);
2121
- const sessionId = numberValue$24(data.session_id ?? data.sessionId);
2122
- const roleCode = numberValue$24(data.role ?? data.sender_role ?? data.senderRole);
2122
+ const messageId = numberValue$25(data.id ?? data.msg_id ?? data.msgId);
2123
+ const sessionId = numberValue$25(data.session_id ?? data.sessionId);
2124
+ const roleCode = numberValue$25(data.role ?? data.sender_role ?? data.senderRole);
2123
2125
  if (!Number.isSafeInteger(messageId) || messageId <= 0 || !Number.isSafeInteger(sessionId) || sessionId <= 0 || roleCode !== 2 && roleCode !== 3) return void 0;
2124
2126
  const extra = parsedJsonRecord(data.extra ?? data.metadata ?? data.meta);
2125
2127
  const extraData = parsedJsonRecord(extra.data);
@@ -2140,7 +2142,7 @@ function arkoHistoryItemFromData(value) {
2140
2142
  extraData.updateAt,
2141
2143
  data.updated_at,
2142
2144
  data.updatedAt
2143
- ].map(numberValue$24).find((candidate) => candidate > 0) ?? 0;
2145
+ ].map(numberValue$25).find((candidate) => candidate > 0) ?? 0;
2144
2146
  const runUid = optionalString$3(extra.agent_run_uid);
2145
2147
  const runStatus = optionalString$3(extra.agent_run_status);
2146
2148
  const retryable = optionalBooleanValue$1(extra.agent_run_retryable);
@@ -2153,7 +2155,7 @@ function arkoHistoryItemFromData(value) {
2153
2155
  text: stringValue$1(data.content ?? data.text_content ?? data.textContent),
2154
2156
  reasoning: stringValue$1(data.reason_content ?? data.reason_text ?? data.reasonText),
2155
2157
  createdAtMillis: activityAt > 0 && activityAt < 1e11 ? activityAt * 1e3 : activityAt,
2156
- status: numberValue$24(data.status ?? data.msg_status),
2158
+ status: numberValue$25(data.status ?? data.msg_status),
2157
2159
  ...runUid === void 0 ? {} : { runUid },
2158
2160
  ...runStatus === void 0 ? {} : { runStatus },
2159
2161
  ...retryable === void 0 ? {} : { retryable },
@@ -2179,7 +2181,7 @@ function arkoRunProjectionFromData(data) {
2179
2181
  };
2180
2182
  }
2181
2183
  function arkoCreatedRecordUids(value) {
2182
- return listValue$25(value).map((item) => stringValue$1(item).trim()).filter((item) => item !== "");
2184
+ return listValue$26(value).map((item) => stringValue$1(item).trim()).filter((item) => item !== "");
2183
2185
  }
2184
2186
  function arkoStreamFrame(raw) {
2185
2187
  try {
@@ -2208,7 +2210,7 @@ var ArkoService = class {
2208
2210
  async arkoEnsureSession(signal) {
2209
2211
  const session = await this.runtime.requireSession();
2210
2212
  const latest = await this.runtime.authenticatedIntelligentPost("/api/v1/qa/latest-session", { type: ARKO_AGENT_DIRECT_SESSION_TYPE }, session, signal);
2211
- const latestSessionId = numberValue$24(latest.id);
2213
+ const latestSessionId = numberValue$25(latest.id);
2212
2214
  if (Number.isSafeInteger(latestSessionId) && latestSessionId > 0) return {
2213
2215
  sessionId: latestSessionId,
2214
2216
  created: false,
@@ -2218,7 +2220,7 @@ var ArkoService = class {
2218
2220
  }
2219
2221
  async arkoCreateSession(signal) {
2220
2222
  const session = await this.runtime.requireSession();
2221
- const createdSessionId = numberValue$24((await this.runtime.authenticatedIntelligentPost("/api/v1/qa/new-session", {
2223
+ const createdSessionId = numberValue$25((await this.runtime.authenticatedIntelligentPost("/api/v1/qa/new-session", {
2222
2224
  name: ARKO_DEFAULT_SESSION_NAME,
2223
2225
  type: ARKO_AGENT_DIRECT_SESSION_TYPE
2224
2226
  }, session, signal)).session_id);
@@ -2243,7 +2245,7 @@ var ArkoService = class {
2243
2245
  const normalizedLimit = Math.min(ARKO_HISTORY_PAGE_LIMIT, Math.max(1, Math.trunc(limit)));
2244
2246
  const normalizedOffset = Math.max(0, Math.trunc(offset));
2245
2247
  const session = await this.runtime.requireSession();
2246
- const rawItems = listValue$25((await this.runtime.authenticatedIntelligentPost("/api/v1/qa/message-list", {
2248
+ const rawItems = listValue$26((await this.runtime.authenticatedIntelligentPost("/api/v1/qa/message-list", {
2247
2249
  limit: normalizedLimit,
2248
2250
  offset: normalizedOffset,
2249
2251
  session_type: ARKO_AGENT_DIRECT_SESSION_TYPE
@@ -2286,9 +2288,9 @@ var ArkoService = class {
2286
2288
  };
2287
2289
  if (modelRouteKey !== "") body.model_route_key = modelRouteKey;
2288
2290
  const accepted = await this.runtime.authenticatedIntelligentPost("/api/v1/qa/new-msg-v2", body, session, options.signal);
2289
- const userMsgId = numberValue$24(accepted.user_msg_id);
2290
- const assistantMsgId = numberValue$24(accepted.assistant_msg_id);
2291
- const acceptedSessionId = numberValue$24(accepted.session_id) || sessionId;
2291
+ const userMsgId = numberValue$25(accepted.user_msg_id);
2292
+ const assistantMsgId = numberValue$25(accepted.assistant_msg_id);
2293
+ const acceptedSessionId = numberValue$25(accepted.session_id) || sessionId;
2292
2294
  if (!Number.isSafeInteger(acceptedSessionId) || acceptedSessionId <= 0 || !Number.isSafeInteger(userMsgId) || userMsgId <= 0 || !Number.isSafeInteger(assistantMsgId) || assistantMsgId <= 0) throw new ArkmePluginError("arko-ask-contract-invalid", "Arko 消息响应不完整", true, 502);
2293
2295
  const runUid = stringValue$1(accepted.run_uid).trim();
2294
2296
  if (runUid === "") throw new ArkmePluginError("arko-ask-contract-invalid", "Arko 消息响应缺少运行标识", true, 502);
@@ -2324,8 +2326,8 @@ var ArkoService = class {
2324
2326
  }, session, signal);
2325
2327
  const status = stringValue$1(data.status).trim();
2326
2328
  const normalizedRunUid = stringValue$1(data.run_uid).trim() || runUid.trim();
2327
- const sequence = numberValue$24(data.sequence);
2328
- const surfaceAssistantMsgId = numberValue$24(data.surface_assistant_msg_id);
2329
+ const sequence = numberValue$25(data.sequence);
2330
+ const surfaceAssistantMsgId = numberValue$25(data.surface_assistant_msg_id);
2329
2331
  const clientAction = optionalRecord(data.client_action);
2330
2332
  if (normalizedRunUid !== runUid.trim() || !ARKO_RUN_STATUSES.has(status) || !Number.isSafeInteger(sequence) || sequence < 0 || !Number.isSafeInteger(surfaceAssistantMsgId) || surfaceAssistantMsgId <= 0) throw new ArkmePluginError("arko-run-status-contract-invalid", "Arko 运行状态响应不完整", true, 502);
2331
2333
  return {
@@ -2563,13 +2565,13 @@ const ARKME_ARRANGEMENT_REF_TTL_MILLIS = 900 * 1e3;
2563
2565
  const MAX_ARKME_ARRANGEMENT_REFS = 4096;
2564
2566
  const ARKME_ARRANGEMENT_REMINDER_REF_TTL_MILLIS = 900 * 1e3;
2565
2567
  const MAX_ARKME_ARRANGEMENT_REMINDER_REFS = 4096;
2566
- function numberValue$23(value) {
2568
+ function numberValue$24(value) {
2567
2569
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
2568
2570
  }
2569
2571
  function booleanValue$13(value) {
2570
2572
  return value === true;
2571
2573
  }
2572
- function listValue$24(value) {
2574
+ function listValue$25(value) {
2573
2575
  return Array.isArray(value) ? value : [];
2574
2576
  }
2575
2577
  function arrangementStatusCode(status) {
@@ -2579,7 +2581,7 @@ function arrangementStatusCode(status) {
2579
2581
  return -1;
2580
2582
  }
2581
2583
  function arrangementStatus(value) {
2582
- const code = Math.trunc(numberValue$23(value));
2584
+ const code = Math.trunc(numberValue$24(value));
2583
2585
  if (code === 1) return "identified";
2584
2586
  if (code === 2) return "following";
2585
2587
  if (code === 3) return "completed";
@@ -2628,9 +2630,9 @@ var ArrangementService = class {
2628
2630
  limit,
2629
2631
  offset
2630
2632
  }, session, options.signal, { lane: "interactive-read" });
2631
- const rawItems = listValue$24(data.list);
2633
+ const rawItems = listValue$25(data.list);
2632
2634
  const items = await Promise.all(rawItems.map(async (raw) => await this.arrangementItem(raw, session.userId)));
2633
- const total = Math.max(0, Math.trunc(numberValue$23(data.total)));
2635
+ const total = Math.max(0, Math.trunc(numberValue$24(data.total)));
2634
2636
  const nextOffset = offset + rawItems.length;
2635
2637
  const hasMore = rawItems.length > 0 && nextOffset < total;
2636
2638
  return {
@@ -2654,9 +2656,9 @@ var ArrangementService = class {
2654
2656
  offset,
2655
2657
  unread_only: options.unreadOnly === true
2656
2658
  }, session, options.signal, { lane: "interactive-read" });
2657
- const rawItems = listValue$24(data.list);
2659
+ const rawItems = listValue$25(data.list);
2658
2660
  const items = await Promise.all(rawItems.map(async (raw) => await this.arrangementReminderEvent(raw, session.userId)));
2659
- const total = Math.max(0, Math.trunc(numberValue$23(data.total)));
2661
+ const total = Math.max(0, Math.trunc(numberValue$24(data.total)));
2660
2662
  const nextOffset = offset + rawItems.length;
2661
2663
  const hasMore = rawItems.length > 0 && nextOffset < total;
2662
2664
  return {
@@ -2679,7 +2681,7 @@ var ArrangementService = class {
2679
2681
  project(data.next_reminder)
2680
2682
  ]);
2681
2683
  return {
2682
- unreadCount: Math.max(0, Math.trunc(numberValue$23(data.unread_count))),
2684
+ unreadCount: Math.max(0, Math.trunc(numberValue$24(data.unread_count))),
2683
2685
  ...latestUnread === void 0 ? {} : { latestUnread },
2684
2686
  ...latestEvent === void 0 ? {} : { latestEvent },
2685
2687
  ...nextReminder === void 0 ? {} : { nextReminder }
@@ -2783,7 +2785,7 @@ var ArrangementService = class {
2783
2785
  }, session, signal);
2784
2786
  return {
2785
2787
  outcome: "confirmed",
2786
- updatedCount: Math.max(0, Math.trunc(numberValue$23(data.updated_count)))
2788
+ updatedCount: Math.max(0, Math.trunc(numberValue$24(data.updated_count)))
2787
2789
  };
2788
2790
  } catch (error) {
2789
2791
  if (!isAmbiguousArrangementWriteError(error)) throw error;
@@ -2801,7 +2803,7 @@ var ArrangementService = class {
2801
2803
  }, session, signal);
2802
2804
  return {
2803
2805
  outcome: "confirmed",
2804
- updatedCount: Math.max(0, Math.trunc(numberValue$23(data.updated_count)))
2806
+ updatedCount: Math.max(0, Math.trunc(numberValue$24(data.updated_count)))
2805
2807
  };
2806
2808
  } catch (error) {
2807
2809
  if (!isAmbiguousArrangementWriteError(error)) throw error;
@@ -2816,7 +2818,7 @@ var ArrangementService = class {
2816
2818
  const data = await this.runtime.authenticatedIntelligentPost("/api/v1/arrangements/reminders/clear", {}, session, signal);
2817
2819
  return {
2818
2820
  outcome: "confirmed",
2819
- updatedCount: Math.max(0, Math.trunc(numberValue$23(data.updated_count)))
2821
+ updatedCount: Math.max(0, Math.trunc(numberValue$24(data.updated_count)))
2820
2822
  };
2821
2823
  } catch (error) {
2822
2824
  if (!isAmbiguousArrangementWriteError(error)) throw error;
@@ -2849,7 +2851,7 @@ var ArrangementService = class {
2849
2851
  }
2850
2852
  async reconcileArrangementDelete(arrangementRef, arrangementUid, session) {
2851
2853
  try {
2852
- const visible = listValue$24((await this.runtime.authenticatedIntelligentPost("/api/v1/arrangements/list", {
2854
+ const visible = listValue$25((await this.runtime.authenticatedIntelligentPost("/api/v1/arrangements/list", {
2853
2855
  status: -1,
2854
2856
  uids: [arrangementUid],
2855
2857
  limit: 1,
@@ -2880,8 +2882,8 @@ var ArrangementService = class {
2880
2882
  const item = objectValue$1(raw);
2881
2883
  const arrangementUid = stringValue$1(item.uid).trim();
2882
2884
  if (arrangementUid === "") throw new ArkmePluginError("arrangement-contract-invalid", "安排响应缺少业务身份", true, 502);
2883
- const dueAtMillis = Math.trunc(numberValue$23(item.due_at));
2884
- const remindAtMillis = Math.trunc(numberValue$23(item.remind_at));
2885
+ const dueAtMillis = Math.trunc(numberValue$24(item.due_at));
2886
+ const remindAtMillis = Math.trunc(numberValue$24(item.remind_at));
2885
2887
  return {
2886
2888
  arrangementRef: await this.arrangementRef(viewerUserId, arrangementUid),
2887
2889
  title: stringValue$1(item.title),
@@ -2889,8 +2891,8 @@ var ArrangementService = class {
2889
2891
  status: arrangementStatus(item.status),
2890
2892
  reminderEnabled: booleanValue$13(item.reminder_enabled),
2891
2893
  reminderState: stringValue$1(item.reminder_state),
2892
- createdAtMillis: Math.trunc(numberValue$23(item.create_at)),
2893
- updatedAtMillis: Math.trunc(numberValue$23(item.update_at)),
2894
+ createdAtMillis: Math.trunc(numberValue$24(item.create_at)),
2895
+ updatedAtMillis: Math.trunc(numberValue$24(item.update_at)),
2894
2896
  ...dueAtMillis > 0 ? { dueAtMillis } : {},
2895
2897
  ...remindAtMillis > 0 ? { remindAtMillis } : {}
2896
2898
  };
@@ -2911,20 +2913,20 @@ var ArrangementService = class {
2911
2913
  const eventUid = stringValue$1(item.uid).trim();
2912
2914
  const arrangementUid = stringValue$1(item.arrangement_uid).trim();
2913
2915
  if (eventUid === "" || arrangementUid === "") throw new ArkmePluginError("arrangement-reminder-contract-invalid", "安排提醒响应缺少有效身份", true, 502);
2914
- const dueAtMillis = Math.trunc(numberValue$23(item.due_at));
2915
- const remindAtMillis = Math.trunc(numberValue$23(item.remind_at));
2916
- const readAtMillis = Math.trunc(numberValue$23(item.read_at));
2916
+ const dueAtMillis = Math.trunc(numberValue$24(item.due_at));
2917
+ const remindAtMillis = Math.trunc(numberValue$24(item.remind_at));
2918
+ const readAtMillis = Math.trunc(numberValue$24(item.read_at));
2917
2919
  return {
2918
2920
  eventRef: await this.arrangementReminderRef(viewerUserId, eventUid),
2919
2921
  arrangementRef: await this.arrangementRef(viewerUserId, arrangementUid),
2920
2922
  title: stringValue$1(item.title),
2921
2923
  description: stringValue$1(item.description),
2922
2924
  eventKind: stringValue$1(item.event_kind),
2923
- eventAtMillis: Math.trunc(numberValue$23(item.event_at)),
2925
+ eventAtMillis: Math.trunc(numberValue$24(item.event_at)),
2924
2926
  read: readAtMillis > 0,
2925
2927
  reminderState: stringValue$1(item.reminder_state),
2926
- createdAtMillis: Math.trunc(numberValue$23(item.create_at)),
2927
- updatedAtMillis: Math.trunc(numberValue$23(item.update_at)),
2928
+ createdAtMillis: Math.trunc(numberValue$24(item.create_at)),
2929
+ updatedAtMillis: Math.trunc(numberValue$24(item.update_at)),
2928
2930
  ...dueAtMillis > 0 ? { dueAtMillis } : {},
2929
2931
  ...remindAtMillis > 0 ? { remindAtMillis } : {},
2930
2932
  ...readAtMillis > 0 ? { readAtMillis } : {}
@@ -2981,7 +2983,7 @@ var ArrangementService = class {
2981
2983
  const ARKME_PHONE_BIND_SUCCESS = 1;
2982
2984
  const ARKME_PHONE_BIND_REPEAT = 2;
2983
2985
  const ARKME_PHONE_BIND_CODE_ERR = 3;
2984
- function numberValue$22(value) {
2986
+ function numberValue$23(value) {
2985
2987
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
2986
2988
  }
2987
2989
  function jiwoScanLoginAvailable(config) {
@@ -3066,7 +3068,7 @@ var AuthService = class {
3066
3068
  const sceneStr = stringValue$1(data.scene_str).trim();
3067
3069
  const pollToken = stringValue$1(data.poll_token).trim();
3068
3070
  const qrContent = stringValue$1(data.url).trim();
3069
- const expireSeconds = Math.max(30, numberValue$22(data.expire_seconds) || 300);
3071
+ const expireSeconds = Math.max(30, numberValue$23(data.expire_seconds) || 300);
3070
3072
  if (qrContent === "" && sceneStr !== "") throw new ArkmePluginError("wechat-qr-unavailable", "测试环境当前未返回可用微信二维码,请使用手机号登录", true, 503);
3071
3073
  if (sceneStr === "" || pollToken === "" || qrContent === "") throw new ArkmePluginError("login-contract-invalid", "Arkme 登录二维码响应不完整", true, 502);
3072
3074
  const attemptId = crypto.randomUUID();
@@ -3105,7 +3107,7 @@ var AuthService = class {
3105
3107
  ref: 0,
3106
3108
  keep_cancel: true
3107
3109
  }, void 0, [200]);
3108
- const userId = numberValue$22(data.user_id);
3110
+ const userId = numberValue$23(data.user_id);
3109
3111
  if (userId <= 0) return {
3110
3112
  status: "pending",
3111
3113
  environment: this.runtime.config.environment,
@@ -3135,7 +3137,7 @@ var AuthService = class {
3135
3137
  }, void 0, [200]);
3136
3138
  const ticket = stringValue$1(data.ticket).trim();
3137
3139
  const pollSecret = stringValue$1(data.poll_secret).trim();
3138
- const expiresAtMillis = numberValue$22(data.expires_at);
3140
+ const expiresAtMillis = numberValue$23(data.expires_at);
3139
3141
  if (!/^[A-Za-z0-9_-]{43}$/.test(ticket) || !/^[A-Za-z0-9_-]{43}$/.test(pollSecret) || expiresAtMillis <= Date.now()) throw new ArkmePluginError("jiwo-login-contract-invalid", "即我登录二维码响应不完整", true, 502);
3140
3142
  if (attemptGeneration !== this.jiwoAttemptGeneration) {
3141
3143
  await this.cancelJiwoCredentials(ticket, pollSecret);
@@ -3185,7 +3187,7 @@ var AuthService = class {
3185
3187
  };
3186
3188
  }
3187
3189
  if (status !== "authenticated") throw new ArkmePluginError("jiwo-login-contract-invalid", "即我扫码登录状态无效", true, 502);
3188
- const userId = numberValue$22(data.user_id);
3190
+ const userId = numberValue$23(data.user_id);
3189
3191
  const accessToken = stringValue$1(data.access_token);
3190
3192
  const refreshToken = stringValue$1(data.refresh_token);
3191
3193
  if (userId <= 0 || accessToken === "" || refreshToken === "") throw new ArkmePluginError("login-contract-invalid", "Arkme 登录成功响应缺少凭据", false, 502);
@@ -3254,7 +3256,7 @@ var AuthService = class {
3254
3256
  const normalizedCaptcha = this.normalizedCaptcha(captcha);
3255
3257
  const session = await this.runtime.sessionStore.read() ?? await this.runtime.readPendingBindingSession();
3256
3258
  if (session !== void 0) {
3257
- if (numberValue$22((await this.runtime.post(this.runtime.config.authBaseUrl, "/api/v1/auth/bind-phone-send-code", {
3259
+ if (numberValue$23((await this.runtime.post(this.runtime.config.authBaseUrl, "/api/v1/auth/bind-phone-send-code", {
3258
3260
  phone: normalizedPhone,
3259
3261
  pre: "86",
3260
3262
  is_test: this.runtime.config.environment === "test",
@@ -3276,7 +3278,7 @@ var AuthService = class {
3276
3278
  if (!/^[0-9]{6}$/.test(normalizedCode)) throw new ArkmePluginError("phone-code-invalid", "请输入有效的短信验证码", false);
3277
3279
  const session = await this.runtime.sessionStore.read() ?? await this.runtime.readPendingBindingSession();
3278
3280
  if (session !== void 0) {
3279
- const result = numberValue$22((await this.runtime.post(this.runtime.config.authBaseUrl, "/api/v1/auth/verify-bind-phone", {
3281
+ const result = numberValue$23((await this.runtime.post(this.runtime.config.authBaseUrl, "/api/v1/auth/verify-bind-phone", {
3280
3282
  phone: normalizedPhone,
3281
3283
  pre: "86",
3282
3284
  code: normalizedCode,
@@ -3297,7 +3299,7 @@ var AuthService = class {
3297
3299
  keep_cancel: true
3298
3300
  }, void 0, [200]);
3299
3301
  if (data.ok === false) throw new ArkmePluginError("phone-code-rejected", "手机号或验证码错误", false, 401);
3300
- const userId = numberValue$22(data.user_id);
3302
+ const userId = numberValue$23(data.user_id);
3301
3303
  const accessToken = stringValue$1(data.access_token);
3302
3304
  const refreshToken = stringValue$1(data.refresh_token);
3303
3305
  if (userId <= 0 || accessToken === "" || refreshToken === "") throw new ArkmePluginError("login-contract-invalid", "Arkme手机号登录响应不完整", false, 502);
@@ -3447,7 +3449,7 @@ var ArkmePrivacyVisibilityService = class {
3447
3449
  };
3448
3450
  //#endregion
3449
3451
  //#region src/mention-metadata.ts
3450
- function listValue$23(value) {
3452
+ function listValue$24(value) {
3451
3453
  return Array.isArray(value) ? value : [];
3452
3454
  }
3453
3455
  function integerLikeValue$3(value) {
@@ -3468,7 +3470,7 @@ function arkmeMentionMetadataFromRecord(record, payload) {
3468
3470
  function arkmeMentionMetadataMentionsViewer(record, payload, viewerUserId) {
3469
3471
  if (viewerUserId <= 0) return false;
3470
3472
  const metadata = arkmeMentionMetadataFromRecord(record, payload);
3471
- for (const rawMention of listValue$23(metadata.human_mentions ?? metadata.humanMentions)) {
3473
+ for (const rawMention of listValue$24(metadata.human_mentions ?? metadata.humanMentions)) {
3472
3474
  const mention = objectValue$1(rawMention);
3473
3475
  const mentionedUserId = integerLikeValue$3(mention.user_id ?? mention.userId);
3474
3476
  if (mentionedUserId === 0 || mentionedUserId === viewerUserId) return true;
@@ -3736,7 +3738,7 @@ const GROUP_AVATAR_NEGATIVE_CACHE_TTL_MS = 6e4;
3736
3738
  const TOPIC_DISSOLVE_RECORD_CONCURRENCY = 6;
3737
3739
  const PRIVATE_REMARK_PAGE_LIMIT = 50;
3738
3740
  const PRIVATE_REMARK_MAX_PAGES = 20;
3739
- function numberValue$21(value) {
3741
+ function numberValue$22(value) {
3740
3742
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
3741
3743
  }
3742
3744
  function integerIdentifierValue(value) {
@@ -3748,7 +3750,7 @@ function integerIdentifierValue(value) {
3748
3750
  function booleanValue$12(value) {
3749
3751
  return value === true;
3750
3752
  }
3751
- function listValue$22(value) {
3753
+ function listValue$23(value) {
3752
3754
  return Array.isArray(value) ? value : [];
3753
3755
  }
3754
3756
  function integerLikeValue$2(value) {
@@ -3788,7 +3790,7 @@ function arkmeChatSidebarTargetFromBundle(bundle, fallbackSubjectUid = "") {
3788
3790
  if (subjectUid === "") return void 0;
3789
3791
  return {
3790
3792
  subjectUid,
3791
- hiddenAnchorTimestamp: Math.max(0, numberValue$21(bundle.sort_active_at ?? bundle.last_active_at ?? chatSession.last_active_at))
3793
+ hiddenAnchorTimestamp: Math.max(0, numberValue$22(bundle.sort_active_at ?? bundle.last_active_at ?? chatSession.last_active_at))
3792
3794
  };
3793
3795
  }
3794
3796
  function backendBooleanValue(value) {
@@ -3820,7 +3822,7 @@ function isSourceKind(value) {
3820
3822
  function chatMessageDnd$1(value) {
3821
3823
  if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
3822
3824
  const policy = value;
3823
- return numberValue$21(policy.mute_state) === 2 || numberValue$21(policy.notify_state) === 2;
3825
+ return numberValue$22(policy.mute_state) === 2 || numberValue$22(policy.notify_state) === 2;
3824
3826
  }
3825
3827
  function attachmentPreviewKind(item) {
3826
3828
  const fileName = stringValue$1(item.file_name ?? item.fileName).trim();
@@ -3839,13 +3841,13 @@ function arkmeChatConversationPreview(raw) {
3839
3841
  const nested = stringValue$1(content.text_content ?? content.title ?? content.summary).trim();
3840
3842
  if (nested !== "") return nested.slice(0, 300);
3841
3843
  if (objectValue$1(content.voice).duration !== void 0) return "[语音]";
3842
- const displayItems = listValue$22(raw.media_display_items ?? raw.mediaDisplayItems).map(objectValue$1);
3844
+ const displayItems = listValue$23(raw.media_display_items ?? raw.mediaDisplayItems).map(objectValue$1);
3843
3845
  const displayByAsset = /* @__PURE__ */ new Map();
3844
3846
  for (const item of displayItems) {
3845
3847
  const fileAssetUid = stringValue$1(item.file_asset_uid ?? item.fileAssetUid).trim();
3846
3848
  if (fileAssetUid !== "") displayByAsset.set(fileAssetUid, item);
3847
3849
  }
3848
- const mediaRefs = listValue$22(content.media_refs ?? content.mediaRefs).map(objectValue$1);
3850
+ const mediaRefs = listValue$23(content.media_refs ?? content.mediaRefs).map(objectValue$1);
3849
3851
  const firstAttachment = (mediaRefs.length > 0 ? mediaRefs.map((ref) => ({
3850
3852
  ...displayByAsset.get(stringValue$1(ref.file_asset_uid ?? ref.fileAssetUid).trim()) ?? {},
3851
3853
  ...ref
@@ -3997,11 +3999,11 @@ var SourceService = class {
3997
3999
  lane: "background-read",
3998
4000
  key: `world-author-labels:${pageCursor === void 0 ? "first" : String(page)}`
3999
4001
  });
4000
- for (const raw of listValue$22(data.items)) {
4002
+ for (const raw of listValue$23(data.items)) {
4001
4003
  const bundle = objectValue$1(raw);
4002
- const sessionKind = numberValue$21(objectValue$1(bundle.session).session_kind);
4004
+ const sessionKind = numberValue$22(objectValue$1(bundle.session).session_kind);
4003
4005
  if (sessionKind !== 1 && sessionKind !== 3) continue;
4004
- const targetUserId = numberValue$21(objectValue$1(bundle.private_counterpart).user_id);
4006
+ const targetUserId = numberValue$22(objectValue$1(bundle.private_counterpart).user_id);
4005
4007
  if (!remaining.has(targetUserId)) continue;
4006
4008
  const supplement = objectValue$1(bundle.private_supplement);
4007
4009
  const counterpart = objectValue$1(bundle.private_counterpart);
@@ -4035,7 +4037,7 @@ var SourceService = class {
4035
4037
  key: `private-remarks:${String(offset)}`,
4036
4038
  failureCooldownMs: 2e3
4037
4039
  });
4038
- const rawItems = listValue$22(data.items);
4040
+ const rawItems = listValue$23(data.items);
4039
4041
  for (const value of rawItems) {
4040
4042
  const raw = objectValue$1(value);
4041
4043
  const targetUserId = integerIdentifierValue(raw.user_id);
@@ -4060,9 +4062,9 @@ var SourceService = class {
4060
4062
  key: `private-remarks:direct:${pageCursor === void 0 ? "first" : String(page)}`,
4061
4063
  failureCooldownMs: 2e3
4062
4064
  });
4063
- for (const value of listValue$22(data.items)) {
4065
+ for (const value of listValue$23(data.items)) {
4064
4066
  const bundle = objectValue$1(value);
4065
- const sessionKind = numberValue$21(objectValue$1(bundle.session).session_kind);
4067
+ const sessionKind = numberValue$22(objectValue$1(bundle.session).session_kind);
4066
4068
  if (sessionKind !== 1 && sessionKind !== 3) continue;
4067
4069
  const targetUserId = integerIdentifierValue(objectValue$1(bundle.private_counterpart).user_id);
4068
4070
  if (!remaining.has(targetUserId)) continue;
@@ -4105,10 +4107,10 @@ var SourceService = class {
4105
4107
  extra: { source: "dsh-arkme" }
4106
4108
  }, session);
4107
4109
  const topicUid = stringValue$1(created.topic_uid).trim();
4108
- if (topicUid === "" || numberValue$21(created.status) !== 1) throw new ArkmePluginError("topic-create-contract-invalid", "主题创建响应不完整", true, 502);
4110
+ if (topicUid === "" || numberValue$22(created.status) !== 1) throw new ArkmePluginError("topic-create-contract-invalid", "主题创建响应不完整", true, 502);
4109
4111
  const sourceRef = await this.sealSourceRef(session.userId, "topic", topicUid, title);
4110
4112
  if (parentTopicUid !== void 0) try {
4111
- if (numberValue$21(objectValue$1((await this.runtime.authenticatedPost("/api/v1/topics/hierarchy/bind", {
4113
+ if (numberValue$22(objectValue$1((await this.runtime.authenticatedPost("/api/v1/topics/hierarchy/bind", {
4112
4114
  parent_topic_uid: parentTopicUid,
4113
4115
  child_topic_uid: topicUid
4114
4116
  }, session)).relation).status) !== 1) throw new ArkmePluginError("topic-hierarchy-bind-contract-invalid", "子主题层级响应不完整", true, 502);
@@ -4277,14 +4279,14 @@ var SourceService = class {
4277
4279
  ...cursorSendAt === void 0 ? {} : { cursor_send_at: cursorSendAt },
4278
4280
  ...cursorRecordUid === void 0 ? {} : { cursor_record_uid: cursorRecordUid }
4279
4281
  }, session);
4280
- for (const raw of listValue$22(data.records)) {
4282
+ for (const raw of listValue$23(data.records)) {
4281
4283
  const item = objectValue$1(raw);
4282
4284
  const uid = stringValue$1(item.record_uid ?? objectValue$1(item.record_core).record_uid).trim();
4283
4285
  if (uid !== "") recordUids.add(uid);
4284
4286
  }
4285
4287
  onProgress?.(recordUids.size);
4286
4288
  if (data.has_more !== true) return [...recordUids];
4287
- const nextSendAt = numberValue$21(data.next_cursor_send_at);
4289
+ const nextSendAt = numberValue$22(data.next_cursor_send_at);
4288
4290
  const nextRecordUid = stringValue$1(data.next_cursor_record_uid).trim();
4289
4291
  const cursorKey = `${String(nextSendAt)}:${nextRecordUid}`;
4290
4292
  if (nextSendAt <= 0 || nextRecordUid === "" || seenCursors.has(cursorKey)) throw new ArkmePluginError("topic-dissolve-record-page-invalid", "主题快记加载不完整,未解散主题,请刷新后重试", true, 502);
@@ -4367,7 +4369,7 @@ var SourceService = class {
4367
4369
  parent_topic_uid: nextParentTopicUid ?? "",
4368
4370
  insert_before_topic_uid: ""
4369
4371
  }, session);
4370
- return stringValue$1(moved.topic_uid).trim() === topicUid && stringValue$1(moved.parent_topic_uid).trim() === (nextParentTopicUid ?? "") && numberValue$21(moved.sibling_order) > 0;
4372
+ return stringValue$1(moved.topic_uid).trim() === topicUid && stringValue$1(moved.parent_topic_uid).trim() === (nextParentTopicUid ?? "") && numberValue$22(moved.sibling_order) > 0;
4371
4373
  }
4372
4374
  async rollbackDissolvedTopicChildren(children, topicUid, parentTopicUid, session) {
4373
4375
  for (const child of [...children].reverse()) await this.moveTopicHierarchyForDissolve(child.topic.ownerRef, parentTopicUid ?? "", topicUid, session).catch(() => false);
@@ -4396,7 +4398,7 @@ var SourceService = class {
4396
4398
  }, session);
4397
4399
  const movedTopicUid = stringValue$1(moved.topic_uid).trim();
4398
4400
  const movedParentUid = stringValue$1(moved.parent_topic_uid).trim();
4399
- const siblingOrder = numberValue$21(moved.sibling_order);
4401
+ const siblingOrder = numberValue$22(moved.sibling_order);
4400
4402
  if (movedTopicUid !== topic.ownerRef || movedParentUid !== (nextParentUid ?? "") || siblingOrder <= 0) throw new ArkmePluginError("topic-hierarchy-move-contract-invalid", "主题移动响应不完整,请刷新后重试", true, 502);
4401
4403
  this.sourceListCache.clear();
4402
4404
  return {
@@ -4441,17 +4443,17 @@ var SourceService = class {
4441
4443
  if (source.kind !== "private_chat" && source.kind !== "group_chat") throw new ArkmePluginError("chat-directory-policy-invalid", "仅支持更新私聊或群聊的会话列表状态", false);
4442
4444
  const sidebarTarget = await this.resolveChatSidebarTarget(source, session, options.signal);
4443
4445
  const current = await this.runtime.authenticatedChatPost("/api/v1/chats/policy/get", { chat_session_uid: source.ownerRef }, session, options.signal);
4444
- const pinned = options.pinned ?? numberValue$21(current.pin_state) === 2;
4445
- const hidden = options.hidden ?? numberValue$21(current.show_in_home_state) === 2;
4446
+ const pinned = options.pinned ?? numberValue$22(current.pin_state) === 2;
4447
+ const hidden = options.hidden ?? numberValue$22(current.show_in_home_state) === 2;
4446
4448
  const updatedAt = Date.now();
4447
4449
  const writes = [this.runtime.authenticatedChatPost("/api/v1/chats/policy/update", {
4448
4450
  chat_session_uid: source.ownerRef,
4449
4451
  show_in_home_state: hidden ? 2 : 1,
4450
- privacy_state: numberValue$21(current.privacy_state) || 1,
4451
- mute_state: numberValue$21(current.mute_state) || 1,
4452
+ privacy_state: numberValue$22(current.privacy_state) || 1,
4453
+ mute_state: numberValue$22(current.mute_state) || 1,
4452
4454
  pin_state: pinned ? 2 : 1,
4453
- notify_state: numberValue$21(current.notify_state) || 1,
4454
- status: numberValue$21(current.status) || 1,
4455
+ notify_state: numberValue$22(current.notify_state) || 1,
4456
+ status: numberValue$22(current.status) || 1,
4455
4457
  update_at: updatedAt
4456
4458
  }, session, options.signal)];
4457
4459
  if (options.hidden !== void 0) writes.push(this.runtime.authenticatedSubjectPost("/api/v1/subject/batch-set-sidebar-hidden-status", { items: [{
@@ -4510,7 +4512,7 @@ var SourceService = class {
4510
4512
  key: "directory:groups:count",
4511
4513
  failureCooldownMs: 2e3
4512
4514
  });
4513
- return Math.max(0, numberValue$21(data.total ?? data.total_count));
4515
+ return Math.max(0, numberValue$22(data.total ?? data.total_count));
4514
4516
  }
4515
4517
  async listSourcesUncached(session, directory, options, limit, sessionKind) {
4516
4518
  if (directory === "send_to_self") {
@@ -4528,7 +4530,7 @@ var SourceService = class {
4528
4530
  options.signal?.throwIfAborted();
4529
4531
  const cached = summaryResult.status === "rejected" || latestRecordsResult.status === "rejected" ? await this.runtime.stateStore.cachedSnapshot(session.userId).catch(() => void 0) : void 0;
4530
4532
  const defaultRecordCount = summaryResult.status === "fulfilled" ? summaryResult.value.recordCount : cached?.summary?.recordCount;
4531
- const defaultLatestRecord = latestRecordsResult.status === "fulfilled" ? listValue$22(latestRecordsResult.value.items).map((raw) => this.recordReader.isDSHAgentInput?.(raw) === true || this.recordReader.isPrivacyLocked?.(raw) === true || arkmePrivacyLockedRecord(raw) || lockedRecordUids.has(stringValue$1(objectValue$1(raw).record_uid ?? objectValue$1(objectValue$1(raw).record_core).record_uid).trim()) ? void 0 : this.recordReader.recordItem(raw)).find((item) => item !== void 0) : cached?.items.reduce((latest, item) => item.creationSource === 3 ? latest : latest === void 0 || item.sendAtMillis > latest.sendAtMillis ? item : latest, void 0);
4533
+ const defaultLatestRecord = latestRecordsResult.status === "fulfilled" ? listValue$23(latestRecordsResult.value.items).map((raw) => this.recordReader.isDSHAgentInput?.(raw) === true || this.recordReader.isPrivacyLocked?.(raw) === true || arkmePrivacyLockedRecord(raw) || lockedRecordUids.has(stringValue$1(objectValue$1(raw).record_uid ?? objectValue$1(objectValue$1(raw).record_core).record_uid).trim()) ? void 0 : this.recordReader.recordItem(raw)).find((item) => item !== void 0) : cached?.items.reduce((latest, item) => item.creationSource === 3 ? latest : latest === void 0 || item.sendAtMillis > latest.sendAtMillis ? item : latest, void 0);
4532
4534
  const defaultLatestPreview = defaultLatestRecord === void 0 ? "" : defaultLatestRecord.textContent.trim() || defaultLatestRecord.title.trim();
4533
4535
  const defaultCategory = {
4534
4536
  sourceRef: await this.sealSourceRef(session.userId, "default_category", "uncategorized", "未分类"),
@@ -4544,24 +4546,24 @@ var SourceService = class {
4544
4546
  const parentTopicUidByChild = /* @__PURE__ */ new Map();
4545
4547
  const siblingOrderByChild = /* @__PURE__ */ new Map();
4546
4548
  const childTopicUidsByParent = /* @__PURE__ */ new Map();
4547
- for (const raw of listValue$22(hierarchyData?.relations)) {
4549
+ for (const raw of listValue$23(hierarchyData?.relations)) {
4548
4550
  const relation = objectValue$1(raw);
4549
- if (numberValue$21(relation.rel_kind) !== 1 || numberValue$21(relation.status) !== 1) continue;
4551
+ if (numberValue$22(relation.rel_kind) !== 1 || numberValue$22(relation.status) !== 1) continue;
4550
4552
  const parentTopicUid = stringValue$1(relation.parent_topic_uid).trim();
4551
4553
  const childTopicUid = stringValue$1(relation.child_topic_uid).trim();
4552
4554
  if (parentTopicUid === "" || childTopicUid === "" || parentTopicUid === childTopicUid) continue;
4553
4555
  parentTopicUidByChild.set(childTopicUid, parentTopicUid);
4554
- siblingOrderByChild.set(childTopicUid, numberValue$21(relation.sibling_order));
4556
+ siblingOrderByChild.set(childTopicUid, numberValue$22(relation.sibling_order));
4555
4557
  const children = childTopicUidsByParent.get(parentTopicUid) ?? /* @__PURE__ */ new Set();
4556
4558
  children.add(childTopicUid);
4557
4559
  childTopicUidsByParent.set(parentTopicUid, children);
4558
4560
  }
4559
- for (const raw of listValue$22(data.items)) {
4561
+ for (const raw of listValue$23(data.items)) {
4560
4562
  const item = objectValue$1(raw);
4561
4563
  if (arkmePrivacyLockedTopic(item)) continue;
4562
4564
  const core = objectValue$1(item.topic_core);
4563
4565
  const status = core.status ?? item.status;
4564
- if (status !== void 0 && numberValue$21(status) !== 1) continue;
4566
+ if (status !== void 0 && numberValue$22(status) !== 1) continue;
4565
4567
  const summary = objectValue$1(item.summary);
4566
4568
  const latest = objectValue$1(item.latest_record_core);
4567
4569
  const parent = objectValue$1(core.parent_topic_core ?? core.parent_topic ?? item.parent_topic_core ?? item.parent_topic);
@@ -4573,12 +4575,12 @@ var SourceService = class {
4573
4575
  topicDescriptors.push({
4574
4576
  topicUid,
4575
4577
  ...parentTopicUid === "" || parentTopicUid === topicUid ? {} : { parentTopicUid },
4576
- siblingOrder: numberValue$21(siblingOrderByChild.get(topicUid) ?? core.sibling_order ?? item.sibling_order),
4578
+ siblingOrder: numberValue$22(siblingOrderByChild.get(topicUid) ?? core.sibling_order ?? item.sibling_order),
4577
4579
  title,
4578
4580
  latestPreview: arkmeChatConversationPreview(latest),
4579
- latestMessageAtMillis: numberValue$21(latest.send_at ?? summary.latest_send_at),
4580
- activeAtMillis: numberValue$21(latest.send_at ?? summary.latest_send_at ?? core.update_at),
4581
- recordCount: numberValue$21(summary.record_count)
4581
+ latestMessageAtMillis: numberValue$22(latest.send_at ?? summary.latest_send_at),
4582
+ activeAtMillis: numberValue$22(latest.send_at ?? summary.latest_send_at ?? core.update_at),
4583
+ recordCount: numberValue$22(summary.record_count)
4582
4584
  });
4583
4585
  }
4584
4586
  const sourceRefByTopicUid = /* @__PURE__ */ new Map();
@@ -4620,12 +4622,12 @@ var SourceService = class {
4620
4622
  unreadCount: 0
4621
4623
  };
4622
4624
  const nextPageCursor = objectValue$1(data.next_page_cursor ?? data.next_cursor);
4623
- const nextOffset = numberValue$21(data.next_offset);
4625
+ const nextOffset = numberValue$22(data.next_offset);
4624
4626
  const preferredPageCursor = Object.keys(nextPageCursor).length > 0 ? nextPageCursor : void 0;
4625
4627
  const preferredOffset = preferredPageCursor === void 0 && nextOffset > 0 ? nextOffset : void 0;
4626
4628
  const hasMore = data.has_more === true && (preferredPageCursor !== void 0 || preferredOffset !== void 0);
4627
4629
  const totalValue = data.total ?? data.total_count;
4628
- const total = totalValue === void 0 ? void 0 : Math.max(0, numberValue$21(totalValue));
4630
+ const total = totalValue === void 0 ? void 0 : Math.max(0, numberValue$22(totalValue));
4629
4631
  return {
4630
4632
  directory,
4631
4633
  items: [
@@ -4656,7 +4658,7 @@ var SourceService = class {
4656
4658
  const items = [];
4657
4659
  const privateUserIdByIndex = /* @__PURE__ */ new Map();
4658
4660
  const groupSessionUidByIndex = /* @__PURE__ */ new Map();
4659
- for (const raw of listValue$22(data.items)) {
4661
+ for (const raw of listValue$23(data.items)) {
4660
4662
  const bundle = objectValue$1(raw);
4661
4663
  const chatSession = objectValue$1(bundle.session);
4662
4664
  const counterpart = objectValue$1(bundle.private_counterpart);
@@ -4667,14 +4669,14 @@ var SourceService = class {
4667
4669
  const unread = objectValue$1(bundle.unread_snapshot);
4668
4670
  const currentPolicy = objectValue$1(bundle.current_policy);
4669
4671
  const isMuted = chatMessageDnd$1(currentPolicy) ?? false;
4670
- const isPinned = numberValue$21(currentPolicy.pin_state) === 2;
4672
+ const isPinned = numberValue$22(currentPolicy.pin_state) === 2;
4671
4673
  const uid = stringValue$1(chatSession.chat_session_uid).trim();
4672
- const sessionKind = numberValue$21(chatSession.session_kind);
4674
+ const sessionKind = numberValue$22(chatSession.session_kind);
4673
4675
  const kind = sessionKind === 2 ? "group_chat" : sessionKind === 1 || sessionKind === 3 ? "private_chat" : void 0;
4674
4676
  if (uid === "" || kind === void 0) continue;
4675
4677
  const displayName = (kind === "private_chat" ? stringValue$1(supplement.remark ?? supplement.counterpart_name_snapshot ?? counterpart.display_name_snapshot ?? supplement.pending_name ?? counterpart.visible_phone) : stringValue$1(chatSession.title)).trim() || "未命名会话";
4676
4678
  const preview = arkmeChatConversationPreview(latestPayload);
4677
- const unreadCount = Math.max(0, Math.trunc(numberValue$21(unread.unread_count)));
4679
+ const unreadCount = Math.max(0, Math.trunc(numberValue$22(unread.unread_count)));
4678
4680
  const latestRelation = objectValue$1(latestPreview.relation);
4679
4681
  const latestPreviewMentionsViewer = integerLikeValue$2(latestRelation.sender_user_id ?? latestRelation.senderUserId) !== session.userId && arkmeMentionMetadataMentionsViewer(latestRecord, latestPayload, session.userId);
4680
4682
  const backendMentionState = chatUnreadMentionState(unread);
@@ -4690,17 +4692,17 @@ var SourceService = class {
4690
4692
  kind,
4691
4693
  displayName,
4692
4694
  ...preview === "" ? {} : { latestPreview: preview },
4693
- activeAtMillis: numberValue$21(bundle.sort_active_at ?? chatSession.last_active_at),
4695
+ activeAtMillis: numberValue$22(bundle.sort_active_at ?? chatSession.last_active_at),
4694
4696
  unreadCount,
4695
4697
  ...hasUnreadMention === void 0 ? {} : { hasUnreadMention },
4696
4698
  isMuted,
4697
4699
  isPinned,
4698
- ...numberValue$21(unread.session_last_seq ?? chatSession.last_seq) > 0 ? { latestSequence: numberValue$21(unread.session_last_seq ?? chatSession.last_seq) } : {}
4700
+ ...numberValue$22(unread.session_last_seq ?? chatSession.last_seq) > 0 ? { latestSequence: numberValue$22(unread.session_last_seq ?? chatSession.last_seq) } : {}
4699
4701
  };
4700
4702
  const itemIndex = items.push(item) - 1;
4701
4703
  this.chatSourceCache.set(`${String(session.userId)}:${uid}`, item);
4702
4704
  if (kind === "private_chat") {
4703
- const counterpartUserId = numberValue$21(counterpart.user_id);
4705
+ const counterpartUserId = numberValue$22(counterpart.user_id);
4704
4706
  if (Number.isSafeInteger(counterpartUserId) && counterpartUserId > 0) {
4705
4707
  item.peerUserId = counterpartUserId;
4706
4708
  privateUserIdByIndex.set(itemIndex, counterpartUserId);
@@ -4714,7 +4716,7 @@ var SourceService = class {
4714
4716
  }
4715
4717
  const hasMore = data.has_more === true;
4716
4718
  const totalValue = data.total ?? data.total_count;
4717
- const total = totalValue === void 0 ? void 0 : Math.max(0, numberValue$21(totalValue));
4719
+ const total = totalValue === void 0 ? void 0 : Math.max(0, numberValue$22(totalValue));
4718
4720
  const nextPageCursor = objectValue$1(data.next_page_cursor);
4719
4721
  return {
4720
4722
  directory,
@@ -4770,16 +4772,16 @@ var SourceService = class {
4770
4772
  continue;
4771
4773
  }
4772
4774
  const snapshotsByUid = /* @__PURE__ */ new Map();
4773
- for (const raw of listValue$22(data.items)) {
4775
+ for (const raw of listValue$23(data.items)) {
4774
4776
  const snapshot = objectValue$1(raw);
4775
4777
  const uid = stringValue$1(snapshot.chat_session_uid).trim();
4776
4778
  const index = indexByGroupUid.get(uid);
4777
4779
  if (index === void 0 || !groupUids.includes(uid)) continue;
4778
- const memberIds = listValue$22(snapshot.members).map((member) => numberValue$21(objectValue$1(member).user_id)).filter((userId) => Number.isSafeInteger(userId) && userId > 0).slice(0, 5);
4780
+ const memberIds = listValue$23(snapshot.members).map((member) => numberValue$22(objectValue$1(member).user_id)).filter((userId) => Number.isSafeInteger(userId) && userId > 0).slice(0, 5);
4779
4781
  const projection = {
4780
- memberCount: Math.max(0, Math.trunc(numberValue$21(snapshot.member_count))),
4782
+ memberCount: Math.max(0, Math.trunc(numberValue$22(snapshot.member_count))),
4781
4783
  strategy: stringValue$1(snapshot.strategy).trim(),
4782
- computedAtMillis: numberValue$21(snapshot.computed_at),
4784
+ computedAtMillis: numberValue$22(snapshot.computed_at),
4783
4785
  memberIds
4784
4786
  };
4785
4787
  snapshotsByUid.set(uid, projection);
@@ -4858,7 +4860,7 @@ var SourceService = class {
4858
4860
  const kind = parsed.kind;
4859
4861
  const result = {
4860
4862
  version: 1,
4861
- userId: numberValue$21(parsed.userId),
4863
+ userId: numberValue$22(parsed.userId),
4862
4864
  kind: isSourceKind(kind) ? kind : "default_category",
4863
4865
  ownerRef: stringValue$1(parsed.ownerRef).trim(),
4864
4866
  displayName: isSourceKind(kind) && kind === "default_category" ? "未分类" : stringValue$1(parsed.displayName).trim(),
@@ -4868,7 +4870,7 @@ var SourceService = class {
4868
4870
  })() : {},
4869
4871
  ...kind === "private_chat" || kind === "group_chat" ? (() => {
4870
4872
  const sidebarSubjectUid = stringValue$1(parsed.sidebarSubjectUid).trim();
4871
- const sidebarHiddenAnchorTimestamp = Math.max(0, numberValue$21(parsed.sidebarHiddenAnchorTimestamp));
4873
+ const sidebarHiddenAnchorTimestamp = Math.max(0, numberValue$22(parsed.sidebarHiddenAnchorTimestamp));
4872
4874
  return sidebarSubjectUid === "" ? {} : {
4873
4875
  sidebarSubjectUid,
4874
4876
  sidebarHiddenAnchorTimestamp
@@ -4902,16 +4904,16 @@ var SourceService = class {
4902
4904
  const unread = objectValue$1(bundle.unread_snapshot);
4903
4905
  const currentPolicy = objectValue$1(bundle.current_policy);
4904
4906
  const isMuted = chatMessageDnd$1(currentPolicy) ?? cached?.isMuted ?? false;
4905
- const isPinned = numberValue$21(currentPolicy.pin_state) === 2;
4907
+ const isPinned = numberValue$22(currentPolicy.pin_state) === 2;
4906
4908
  const uid = stringValue$1(chatSession.chat_session_uid).trim();
4907
- const sessionKind = numberValue$21(chatSession.session_kind);
4909
+ const sessionKind = numberValue$22(chatSession.session_kind);
4908
4910
  const kind = sessionKind === 2 ? "group_chat" : sessionKind === 1 || sessionKind === 3 ? "private_chat" : void 0;
4909
4911
  if (uid === "" || kind === void 0) throw new Error("invalid chat display snapshot");
4910
4912
  const displayName = (kind === "private_chat" ? stringValue$1(supplement.remark ?? supplement.counterpart_name_snapshot ?? counterpart.display_name_snapshot ?? supplement.pending_name ?? counterpart.visible_phone) : stringValue$1(chatSession.title)).trim() || cached?.displayName || "未命名会话";
4911
4913
  const latestItem = [...timelineItems].sort((left, right) => (right.sequence ?? 0) - (left.sequence ?? 0))[0];
4912
4914
  const latestPreview = latestItem === void 0 ? cached?.latestPreview : latestItem.forwardRecords === void 0 ? arkmeTimelineConversationPreview(latestItem) : `[转发] ${latestItem.forwardRecords.title}`;
4913
- const latestSequence = Math.max(numberValue$21(unread.session_last_seq ?? chatSession.last_seq), latestItem?.sequence ?? 0, cached?.latestSequence ?? 0);
4914
- const unreadCount = Math.max(0, Math.trunc(numberValue$21(unread.unread_count)));
4915
+ const latestSequence = Math.max(numberValue$22(unread.session_last_seq ?? chatSession.last_seq), latestItem?.sequence ?? 0, cached?.latestSequence ?? 0);
4916
+ const unreadCount = Math.max(0, Math.trunc(numberValue$22(unread.unread_count)));
4915
4917
  const latestMentionsViewer = latestItem?.isMe === false && latestItem.mentionsViewer === true;
4916
4918
  const backendMentionState = chatUnreadMentionState(unread);
4917
4919
  const hasUnreadMention = kind !== "group_chat" ? void 0 : unreadCount <= 0 ? false : backendMentionState === void 0 && !latestMentionsViewer ? cached?.hasUnreadMention : backendMentionState === true || latestMentionsViewer;
@@ -4929,7 +4931,7 @@ var SourceService = class {
4929
4931
  ...cached?.avatarRefs === void 0 ? {} : { avatarRefs: cached.avatarRefs },
4930
4932
  ...cached?.groupAvatar === void 0 ? {} : { groupAvatar: cached.groupAvatar },
4931
4933
  ...latestPreview === void 0 || latestPreview === "" ? {} : { latestPreview },
4932
- activeAtMillis: Math.max(numberValue$21(bundle.sort_active_at ?? chatSession.last_active_at), latestItem?.sendAtMillis ?? 0),
4934
+ activeAtMillis: Math.max(numberValue$22(bundle.sort_active_at ?? chatSession.last_active_at), latestItem?.sendAtMillis ?? 0),
4933
4935
  unreadCount,
4934
4936
  ...hasUnreadMention === void 0 ? {} : { hasUnreadMention },
4935
4937
  isMuted,
@@ -4961,7 +4963,7 @@ var SourceService = class {
4961
4963
  const value = this.decodeCursor(cursor);
4962
4964
  if (value.directory !== "send_to_self") throw new ArkmePluginError("source-cursor-invalid", "发给自己的主题分页游标无效", false);
4963
4965
  const pageCursor = objectValue$1(value.pageCursor);
4964
- const offset = numberValue$21(value.offset);
4966
+ const offset = numberValue$22(value.offset);
4965
4967
  if (Object.keys(pageCursor).length === 0 && offset <= 0) throw new ArkmePluginError("source-cursor-invalid", "发给自己的主题分页游标无效", false);
4966
4968
  return {
4967
4969
  ...Object.keys(pageCursor).length === 0 ? {} : { pageCursor },
@@ -4979,13 +4981,13 @@ const BOT_REF_TTL_MILLIS = 30 * 6e4;
4979
4981
  const BOT_REF_CAP = 2e3;
4980
4982
  const BOT_REF_PATTERN = /^arkme-bot-v2\.[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
4981
4983
  const BOT_IMAGE_REF_PATTERN = /^arkme-bot-image-v1\.[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
4982
- function numberValue$20(value) {
4984
+ function numberValue$21(value) {
4983
4985
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
4984
4986
  }
4985
4987
  function booleanValue$11(value) {
4986
4988
  return value === true;
4987
4989
  }
4988
- function listValue$21(value) {
4990
+ function listValue$22(value) {
4989
4991
  return Array.isArray(value) ? value : [];
4990
4992
  }
4991
4993
  function arkmeNormalizeBotProvider(value) {
@@ -4993,7 +4995,7 @@ function arkmeNormalizeBotProvider(value) {
4993
4995
  if (normalized === "openclaw" || normalized === "webhook") return normalized;
4994
4996
  }
4995
4997
  function botPrivateChatTimestamp(value) {
4996
- const numeric = numberValue$20(value);
4998
+ const numeric = numberValue$21(value);
4997
4999
  if (numeric <= 0) return 0;
4998
5000
  if (numeric < 1e10) return numeric * 1e3;
4999
5001
  let millis = numeric;
@@ -5007,7 +5009,7 @@ function botWebhookSecurity(value) {
5007
5009
  keyword: stringValue$1(raw.keyword).trim(),
5008
5010
  tokenEnabled: booleanValue$11(raw.token_enabled ?? raw.signature_enabled),
5009
5011
  ipWhitelistEnabled: booleanValue$11(raw.ip_whitelist_enabled),
5010
- ipWhitelist: listValue$21(raw.ip_whitelist).map((item) => stringValue$1(item).trim()).filter((item) => item !== "").slice(0, 100)
5012
+ ipWhitelist: listValue$22(raw.ip_whitelist).map((item) => stringValue$1(item).trim()).filter((item) => item !== "").slice(0, 100)
5011
5013
  };
5012
5014
  }
5013
5015
  function botProfileSource(data) {
@@ -5086,7 +5088,7 @@ var BotService = class {
5086
5088
  }
5087
5089
  async listBots(options = {}) {
5088
5090
  const session = await this.runtime.requireSession();
5089
- const rawBots = listValue$21((await this.runtime.authenticatedBotPost("/api/v1/bot/list", {}, session, options.signal)).bots).map(objectValue$1);
5091
+ const rawBots = listValue$22((await this.runtime.authenticatedBotPost("/api/v1/bot/list", {}, session, options.signal)).bots).map(objectValue$1);
5090
5092
  const candidates = rawBots.filter((raw) => stringValue$1(raw.bot_id).trim() !== "" && stringValue$1(raw.name).trim() !== "" && arkmeNormalizeBotProvider(raw.provider) !== void 0);
5091
5093
  const chatTargetCounts = /* @__PURE__ */ new Map();
5092
5094
  for (const raw of rawBots) {
@@ -5112,7 +5114,7 @@ var BotService = class {
5112
5114
  async countBots(options = {}) {
5113
5115
  const session = await this.runtime.requireSession();
5114
5116
  const data = await this.runtime.authenticatedBotPost("/api/v1/bot/list", { limit: 0 }, session, options.signal);
5115
- return Math.max(0, numberValue$20(data.total ?? data.total_count));
5117
+ return Math.max(0, numberValue$21(data.total ?? data.total_count));
5116
5118
  }
5117
5119
  async createBot(input, options = {}) {
5118
5120
  const name = input.name.trim();
@@ -5248,7 +5250,7 @@ var BotService = class {
5248
5250
  const groupTargetBody = await this.resolveGroupBotBindingBody(group, session, options.signal);
5249
5251
  const data = await this.runtime.authenticatedBotPost("/api/v1/bot/group/list", groupTargetBody, session, options.signal);
5250
5252
  const items = [];
5251
- for (const value of listValue$21(data.bots)) {
5253
+ for (const value of listValue$22(data.bots)) {
5252
5254
  const raw = objectValue$1(value);
5253
5255
  if (arkmeNormalizeBotProvider(raw.provider) !== "openclaw") continue;
5254
5256
  const summary = await this.groupBotSummaryFromData(raw, session.userId);
@@ -5300,7 +5302,7 @@ var BotService = class {
5300
5302
  }
5301
5303
  async listMentionableGroupBots(group, session, signal) {
5302
5304
  const groupTargetBody = await this.resolveGroupBotBindingBody(group, session, signal);
5303
- return listValue$21((await this.runtime.authenticatedBotPost("/api/v1/bot/group/list", groupTargetBody, session, signal)).bots).flatMap((value) => {
5305
+ return listValue$22((await this.runtime.authenticatedBotPost("/api/v1/bot/group/list", groupTargetBody, session, signal)).bots).flatMap((value) => {
5304
5306
  const raw = objectValue$1(value);
5305
5307
  if (arkmeNormalizeBotProvider(raw.provider) !== "openclaw" || !booleanValue$11(raw.installed)) return [];
5306
5308
  const botId = stringValue$1(raw.bot_id).trim();
@@ -5360,7 +5362,7 @@ var BotService = class {
5360
5362
  const raw = botProfileSource(data);
5361
5363
  if (stringValue$1(raw.bot_id).trim() !== reference.botId || arkmeNormalizeBotProvider(raw.provider) !== reference.provider) throw new ArkmePluginError("bot-contract-invalid", "Bot 配置响应与当前 Bot 不一致", true, 502);
5362
5364
  const summary = await this.botSummaryFromData(raw, userId, reference.target);
5363
- const joinedGroups = listValue$21(data.joined_groups).map((value) => objectValue$1(value)).map((group) => ({
5365
+ const joinedGroups = listValue$22(data.joined_groups).map((value) => objectValue$1(value)).map((group) => ({
5364
5366
  title: stringValue$1(group.subject_title).trim() || stringValue$1(group.subject_uid).trim() || "未命名群聊",
5365
5367
  installedAtMillis: botPrivateChatTimestamp(group.installed_at)
5366
5368
  }));
@@ -5372,7 +5374,7 @@ var BotService = class {
5372
5374
  tokenRevealEnabled: data.token_reveal_enabled !== false,
5373
5375
  gatewayUrl: stringValue$1(data.gateway_url).trim(),
5374
5376
  webhookUrl: stringValue$1(data.webhook_url).trim(),
5375
- recordCount: Math.max(-1, Math.trunc(numberValue$20(raw.record_count))),
5377
+ recordCount: Math.max(-1, Math.trunc(numberValue$21(raw.record_count))),
5376
5378
  webhookSecurity: botWebhookSecurity(data.webhook_security ?? raw.webhook_security),
5377
5379
  joinedGroups
5378
5380
  };
@@ -5499,14 +5501,14 @@ var BotService = class {
5499
5501
  };
5500
5502
  //#endregion
5501
5503
  //#region src/services/bot-conversation-service.ts
5502
- function numberValue$19(value) {
5504
+ function numberValue$20(value) {
5503
5505
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
5504
5506
  }
5505
- function listValue$20(value) {
5507
+ function listValue$21(value) {
5506
5508
  return Array.isArray(value) ? value : [];
5507
5509
  }
5508
5510
  function botConversationTimestamp(value) {
5509
- const numeric = numberValue$19(value);
5511
+ const numeric = numberValue$20(value);
5510
5512
  if (numeric <= 0) return 0;
5511
5513
  if (numeric < 1e10) return numeric * 1e3;
5512
5514
  let millis = numeric;
@@ -5514,7 +5516,7 @@ function botConversationTimestamp(value) {
5514
5516
  return millis;
5515
5517
  }
5516
5518
  function subjectAttachments(value) {
5517
- return listValue$20(value).map(objectValue$1).flatMap((raw) => {
5519
+ return listValue$21(value).map(objectValue$1).flatMap((raw) => {
5518
5520
  const kind = stringValue$1(raw.kind).trim();
5519
5521
  const fileName = stringValue$1(raw.file_name ?? raw.fileName).trim();
5520
5522
  const mimeType = stringValue$1(raw.mime_type ?? raw.mimeType).trim();
@@ -5524,11 +5526,11 @@ function subjectAttachments(value) {
5524
5526
  kind: kind || "file",
5525
5527
  fileName,
5526
5528
  mimeType,
5527
- size: Math.max(0, Math.trunc(numberValue$19(raw.size))),
5528
- durationMillis: Math.max(0, Math.trunc(numberValue$19(raw.duration_ms ?? raw.durationMillis))),
5529
- width: Math.max(0, Math.trunc(numberValue$19(raw.width))),
5530
- height: Math.max(0, Math.trunc(numberValue$19(raw.height))),
5531
- sortOrder: Math.max(0, Math.trunc(numberValue$19(raw.order ?? raw.sort_order ?? raw.sortOrder)))
5529
+ size: Math.max(0, Math.trunc(numberValue$20(raw.size))),
5530
+ durationMillis: Math.max(0, Math.trunc(numberValue$20(raw.duration_ms ?? raw.durationMillis))),
5531
+ width: Math.max(0, Math.trunc(numberValue$20(raw.width))),
5532
+ height: Math.max(0, Math.trunc(numberValue$20(raw.height))),
5533
+ sortOrder: Math.max(0, Math.trunc(numberValue$20(raw.order ?? raw.sort_order ?? raw.sortOrder)))
5532
5534
  }];
5533
5535
  });
5534
5536
  }
@@ -5597,7 +5599,7 @@ var SubjectBotConversationAdapter = class {
5597
5599
  if (stringValue$1(objectValue$1(data.user_message).message_id).trim() === "") throw new ArkmePluginError("bot-conversation-send-outcome-unknown", "消息可能已发送,但响应缺少确认标识;请刷新会话确认", false, 409);
5598
5600
  return {
5599
5601
  userMessage: subjectMessage(data.user_message, content),
5600
- botMessages: dedupeMessages([...listValue$20(data.bot_messages).map((message) => subjectMessage(message)), ...Object.keys(objectValue$1(data.bot_message)).length === 0 ? [] : [subjectMessage(data.bot_message)]]),
5602
+ botMessages: dedupeMessages([...listValue$21(data.bot_messages).map((message) => subjectMessage(message)), ...Object.keys(objectValue$1(data.bot_message)).length === 0 ? [] : [subjectMessage(data.bot_message)]]),
5601
5603
  status: stringValue$1(data.status).trim() || "ok"
5602
5604
  };
5603
5605
  }
@@ -5615,7 +5617,7 @@ var SubjectBotConversationAdapter = class {
5615
5617
  }
5616
5618
  async read(context, signal) {
5617
5619
  this.subjectTarget(context);
5618
- return { messages: dedupeMessages(listValue$20((await this.runtime.authenticatedBotPost("/api/v1/bot/private-chat/open", { bot_id: context.reference.botId }, context.session, signal)).messages).map((message) => subjectMessage(message))) };
5620
+ return { messages: dedupeMessages(listValue$21((await this.runtime.authenticatedBotPost("/api/v1/bot/private-chat/open", { bot_id: context.reference.botId }, context.session, signal)).messages).map((message) => subjectMessage(message))) };
5619
5621
  }
5620
5622
  subjectTarget(context) {
5621
5623
  if (context.reference.target.kind !== "subject") throw new ArkmePluginError("bot-conversation-owner-mismatch", "Bot 会话 owner 不匹配", false, 409);
@@ -5739,14 +5741,14 @@ var BotConversationService = class {
5739
5741
  //#region src/services/calendar-service.ts
5740
5742
  const MAX_CALENDAR_RANGE_DAYS = 62;
5741
5743
  const MAX_DAY_RECORD_LIMIT = 50;
5742
- function numberValue$18(value) {
5744
+ function numberValue$19(value) {
5743
5745
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
5744
5746
  }
5745
5747
  function booleanValue$10(value) {
5746
5748
  if (typeof value === "boolean") return value;
5747
5749
  if (typeof value === "number") return value !== 0;
5748
5750
  }
5749
- function listValue$19(value) {
5751
+ function listValue$20(value) {
5750
5752
  return Array.isArray(value) ? value : [];
5751
5753
  }
5752
5754
  function readCalendarDate(value, field) {
@@ -5779,7 +5781,7 @@ function boundedLimit$2(value) {
5779
5781
  return limit;
5780
5782
  }
5781
5783
  function contentAccessState(value) {
5782
- const code = Math.trunc(numberValue$18(value));
5784
+ const code = Math.trunc(numberValue$19(value));
5783
5785
  if (code === 1) return "available";
5784
5786
  if (code === 2) return "protected";
5785
5787
  return "unknown";
@@ -5826,7 +5828,7 @@ var CalendarService = class {
5826
5828
  endDate,
5827
5829
  timezone: stringValue$1(data.timezone).trim() || timezone,
5828
5830
  refreshedAtMillis: Date.now(),
5829
- days: listValue$19(data.daily_data).map((raw) => this.bucketDay(raw)).filter((day) => day !== void 0)
5831
+ days: listValue$20(data.daily_data).map((raw) => this.bucketDay(raw)).filter((day) => day !== void 0)
5830
5832
  };
5831
5833
  }
5832
5834
  async dayRecords(options) {
@@ -5852,14 +5854,14 @@ var CalendarService = class {
5852
5854
  cacheMs: 1e4,
5853
5855
  failureCooldownMs: 2e3
5854
5856
  });
5855
- const nextSendAt = Math.trunc(numberValue$18(data.next_cursor_send_at));
5857
+ const nextSendAt = Math.trunc(numberValue$19(data.next_cursor_send_at));
5856
5858
  const nextUid = stringValue$1(data.next_cursor_record_uid).trim();
5857
5859
  return {
5858
5860
  scope: "self",
5859
5861
  bucketDate,
5860
5862
  timezone: stringValue$1(data.timezone).trim() || timezone,
5861
5863
  refreshedAtMillis: Date.now(),
5862
- items: listValue$19(data.items).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(stringValue$1(objectValue$1(raw).record_uid ?? objectValue$1(objectValue$1(raw).record_core).record_uid).trim())).map((raw) => this.dayRecord(raw)).filter((item) => item !== void 0),
5864
+ items: listValue$20(data.items).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(stringValue$1(objectValue$1(raw).record_uid ?? objectValue$1(objectValue$1(raw).record_core).record_uid).trim())).map((raw) => this.dayRecord(raw)).filter((item) => item !== void 0),
5863
5865
  hasMore: data.has_more === true,
5864
5866
  ...nextSendAt > 0 && nextUid !== "" ? { nextCursor: {
5865
5867
  sendAtMillis: nextSendAt,
@@ -5871,21 +5873,21 @@ var CalendarService = class {
5871
5873
  const item = objectValue$1(raw);
5872
5874
  const bucketDate = stringValue$1(item.bucket_date).trim();
5873
5875
  if (bucketDate === "") return void 0;
5874
- const protectedCount = Math.max(0, Math.trunc(numberValue$18(item.protected_count)));
5875
- const count = Math.max(0, Math.trunc(numberValue$18(item.count)) - protectedCount);
5876
+ const protectedCount = Math.max(0, Math.trunc(numberValue$19(item.protected_count)));
5877
+ const count = Math.max(0, Math.trunc(numberValue$19(item.count)) - protectedCount);
5876
5878
  return {
5877
5879
  bucketDate,
5878
5880
  count,
5879
5881
  protectedCount: 0,
5880
5882
  hasRecords: count > 0,
5881
- ...numberValue$18(item.first_send_at) > 0 ? { firstSendAtMillis: Math.trunc(numberValue$18(item.first_send_at)) } : {}
5883
+ ...numberValue$19(item.first_send_at) > 0 ? { firstSendAtMillis: Math.trunc(numberValue$19(item.first_send_at)) } : {}
5882
5884
  };
5883
5885
  }
5884
5886
  dayRecord(raw) {
5885
5887
  const item = objectValue$1(raw);
5886
5888
  const core = objectValue$1(item.record_core);
5887
5889
  const recordUid = stringValue$1(item.record_uid ?? core.record_uid).trim();
5888
- const sendAtMillis = Math.trunc(numberValue$18(item.send_at ?? core.send_at));
5890
+ const sendAtMillis = Math.trunc(numberValue$19(item.send_at ?? core.send_at));
5889
5891
  if (recordUid === "" || sendAtMillis <= 0) return void 0;
5890
5892
  const accessState = contentAccessState(core.content_access_state);
5891
5893
  if (accessState === "protected" || arkmePrivacyLockedRecord(item)) return void 0;
@@ -5906,9 +5908,9 @@ var CalendarService = class {
5906
5908
  preview,
5907
5909
  ...stringValue$1(topic.title).trim() === "" ? {} : { topicTitle: stringValue$1(topic.title).trim() },
5908
5910
  sourceKind: sourceKind(item),
5909
- creationSource: Math.trunc(numberValue$18(core.creation_source)),
5910
- templateKind: Math.trunc(numberValue$18(core.template_kind)),
5911
- displayKind: Math.trunc(numberValue$18(core.display_kind)),
5911
+ creationSource: Math.trunc(numberValue$19(core.creation_source)),
5912
+ templateKind: Math.trunc(numberValue$19(core.template_kind)),
5913
+ displayKind: Math.trunc(numberValue$19(core.display_kind)),
5912
5914
  protected: false,
5913
5915
  ...isUncategorized === void 0 ? {} : { isUncategorized },
5914
5916
  ...hasManualEdit === void 0 ? {} : { hasManualEdit },
@@ -5918,10 +5920,10 @@ var CalendarService = class {
5918
5920
  };
5919
5921
  //#endregion
5920
5922
  //#region src/services/call-history-service.ts
5921
- function listValue$18(value) {
5923
+ function listValue$19(value) {
5922
5924
  return Array.isArray(value) ? value : [];
5923
5925
  }
5924
- function numberValue$17(value) {
5926
+ function numberValue$18(value) {
5925
5927
  if (typeof value === "number" && Number.isFinite(value)) return value;
5926
5928
  if (typeof value === "string" && value.trim() !== "") {
5927
5929
  const parsed = Number(value.trim());
@@ -5935,7 +5937,7 @@ function booleanValue$9(value) {
5935
5937
  function firstValue(source, keys) {
5936
5938
  for (const key of keys) if (source[key] !== void 0 && source[key] !== null) return source[key];
5937
5939
  }
5938
- function firstString(source, keys) {
5940
+ function firstString$1(source, keys) {
5939
5941
  for (const key of keys) {
5940
5942
  const value = stringValue$1(source[key]).trim();
5941
5943
  if (value !== "") return value;
@@ -5944,13 +5946,13 @@ function firstString(source, keys) {
5944
5946
  }
5945
5947
  function firstNumber(source, keys) {
5946
5948
  for (const key of keys) {
5947
- const value = numberValue$17(source[key]);
5949
+ const value = numberValue$18(source[key]);
5948
5950
  if (value !== 0) return value;
5949
5951
  }
5950
5952
  return 0;
5951
5953
  }
5952
5954
  function epochMillis$1(value) {
5953
- const raw = numberValue$17(value);
5955
+ const raw = numberValue$18(value);
5954
5956
  if (raw <= 0) return 0;
5955
5957
  return raw < 1e10 ? Math.trunc(raw * 1e3) : Math.trunc(raw);
5956
5958
  }
@@ -5962,7 +5964,7 @@ function firstEpochMillis(source, keys) {
5962
5964
  return 0;
5963
5965
  }
5964
5966
  function numberList(value) {
5965
- return listValue$18(value).map((item) => Math.trunc(numberValue$17(item))).filter((item) => Number.isSafeInteger(item) && item > 0);
5967
+ return listValue$19(value).map((item) => Math.trunc(numberValue$18(item))).filter((item) => Number.isSafeInteger(item) && item > 0);
5966
5968
  }
5967
5969
  function callMediaType(value) {
5968
5970
  if (value === 0 || value === "0") return "audio";
@@ -6061,7 +6063,7 @@ function videoRecordCandidateObjects(raw) {
6061
6063
  const item = objectValue$1(raw[key]);
6062
6064
  if (Object.keys(item).length > 0) values.push(item);
6063
6065
  }
6064
- for (const key of arrayKeys) for (const item of listValue$18(raw[key])) {
6066
+ for (const key of arrayKeys) for (const item of listValue$19(raw[key])) {
6065
6067
  const value = objectValue$1(item);
6066
6068
  if (Object.keys(value).length > 0) values.push(value);
6067
6069
  }
@@ -6103,7 +6105,7 @@ function perspectiveFromItem(item, viewerUserId) {
6103
6105
  "viewUserId"
6104
6106
  ]));
6105
6107
  if (userId > 0) return userId === viewerUserId ? "self" : "peer";
6106
- return perspectiveFromText(firstString(item, [
6108
+ return perspectiveFromText(firstString$1(item, [
6107
6109
  "perspective",
6108
6110
  "view",
6109
6111
  "viewType",
@@ -6157,12 +6159,12 @@ function videoPerspectiveFromItem(item, viewerUserId) {
6157
6159
  if (videoUrl === "" && posterUrl === "") return void 0;
6158
6160
  return {
6159
6161
  perspective: perspectiveFromItem(item, viewerUserId),
6160
- ...firstString(item, [
6162
+ ...firstString$1(item, [
6161
6163
  "label",
6162
6164
  "display_name",
6163
6165
  "displayName",
6164
6166
  "name"
6165
- ]) === "" ? {} : { label: firstString(item, [
6167
+ ]) === "" ? {} : { label: firstString$1(item, [
6166
6168
  "label",
6167
6169
  "display_name",
6168
6170
  "displayName",
@@ -6275,7 +6277,7 @@ var CallHistoryService = class {
6275
6277
  const validItems = (await Promise.all(this.historyItems(raw).map(async (item) => await this.normalizeHistoryItem(item, session.userId)))).filter((item) => item !== void 0);
6276
6278
  await this.attachPeerAvatars(validItems, session, signal);
6277
6279
  const hasMore = booleanValue$9(raw.has_more ?? raw.hasMore);
6278
- const nextCursor = firstString(raw, [
6280
+ const nextCursor = firstString$1(raw, [
6279
6281
  "next_cursor",
6280
6282
  "nextCursor",
6281
6283
  "cursor"
@@ -6313,7 +6315,7 @@ var CallHistoryService = class {
6313
6315
  return Math.min(50, raw);
6314
6316
  }
6315
6317
  historyItems(raw) {
6316
- return listValue$18(firstValue(raw, [
6318
+ return listValue$19(firstValue(raw, [
6317
6319
  "items",
6318
6320
  "records",
6319
6321
  "histories",
@@ -6326,9 +6328,9 @@ var CallHistoryService = class {
6326
6328
  ...raw,
6327
6329
  ...trtc
6328
6330
  };
6329
- const roomId = firstString(item, ["room_id", "roomId"]);
6331
+ const roomId = firstString$1(item, ["room_id", "roomId"]);
6330
6332
  if (roomId === "") return void 0;
6331
- const stableId = firstString(raw, ["stable_id", "stableId"]) || `trtc:${roomId}`;
6333
+ const stableId = firstString$1(raw, ["stable_id", "stableId"]) || `trtc:${roomId}`;
6332
6334
  const startedAtMillis = firstEpochMillis(item, [
6333
6335
  "started_at_millis",
6334
6336
  "startedAtMillis",
@@ -6350,7 +6352,7 @@ var CallHistoryService = class {
6350
6352
  "endTime"
6351
6353
  ]);
6352
6354
  const duration = durationSeconds(acceptedAtMillis, startedAtMillis, endedAtMillis);
6353
- const callResult = firstString(item, [
6355
+ const callResult = firstString$1(item, [
6354
6356
  "call_result",
6355
6357
  "callResult",
6356
6358
  "result"
@@ -6359,7 +6361,7 @@ var CallHistoryService = class {
6359
6361
  const calleeUserIds = numberList(item.callee_user_ids ?? item.calleeUserIds);
6360
6362
  const connectedUserIds = numberList(item.connected_user_ids ?? item.connectedUserIds);
6361
6363
  const peerUserId = this.resolvePeerUserId(viewerUserId, callerUserId, calleeUserIds, connectedUserIds, item);
6362
- const summaryText = safeSummary(firstString(item, [
6364
+ const summaryText = safeSummary(firstString$1(item, [
6363
6365
  "call_summary",
6364
6366
  "callSummary",
6365
6367
  "summary_text",
@@ -6372,7 +6374,7 @@ var CallHistoryService = class {
6372
6374
  "summary_updated_at",
6373
6375
  "summaryUpdatedAt"
6374
6376
  ]);
6375
- const displayName = firstString(item, [
6377
+ const displayName = firstString$1(item, [
6376
6378
  "peer_display_name",
6377
6379
  "peerDisplayName",
6378
6380
  "display_name",
@@ -6413,12 +6415,12 @@ var CallHistoryService = class {
6413
6415
  ...summaryUpdatedAtMillis > 0 ? { summaryUpdatedAtMillis } : {},
6414
6416
  canOpenDetail: true,
6415
6417
  canRedial: peerUserId > 0,
6416
- ...firstString(item, [
6418
+ ...firstString$1(item, [
6417
6419
  "chat_session_uid",
6418
6420
  "chatSessionUid",
6419
6421
  "resolved_chat_session_uid",
6420
6422
  "resolvedChatSessionUid"
6421
- ]) === "" ? {} : { chatSessionUid: firstString(item, [
6423
+ ]) === "" ? {} : { chatSessionUid: firstString$1(item, [
6422
6424
  "chat_session_uid",
6423
6425
  "chatSessionUid",
6424
6426
  "resolved_chat_session_uid",
@@ -6449,7 +6451,7 @@ var CallHistoryService = class {
6449
6451
  return [...calleeUserIds, ...connectedUserIds].find((userId) => userId > 0 && userId !== viewerUserId) ?? 0;
6450
6452
  }
6451
6453
  async recentContacts(raw, session, signal) {
6452
- const rawContacts = listValue$18(firstValue(raw, ["recent_contacts", "recentContacts"]));
6454
+ const rawContacts = listValue$19(firstValue(raw, ["recent_contacts", "recentContacts"]));
6453
6455
  if (rawContacts.length > 0) {
6454
6456
  const contacts = rawContacts.map((item) => this.recentContactFromRaw(objectValue$1(item))).filter((item) => item !== void 0);
6455
6457
  if (contacts.length > 0) return await this.attachRecentContactAvatars(contacts, session, signal);
@@ -6511,7 +6513,7 @@ var CallHistoryService = class {
6511
6513
  "peerUserId"
6512
6514
  ]));
6513
6515
  if (userId <= 0) return void 0;
6514
- const displayName = firstString(raw, [
6516
+ const displayName = firstString$1(raw, [
6515
6517
  "display_name",
6516
6518
  "displayName",
6517
6519
  "peer_display_name",
@@ -6556,12 +6558,12 @@ var CallHistoryService = class {
6556
6558
  "endTime"
6557
6559
  ]);
6558
6560
  const duration = durationSeconds(acceptedAtMillis, startedAtMillis, endedAtMillis);
6559
- const callResult = firstString(raw, [
6561
+ const callResult = firstString$1(raw, [
6560
6562
  "call_result",
6561
6563
  "callResult",
6562
6564
  "result"
6563
6565
  ]);
6564
- const summaryText = safeSummary(firstString(raw, [
6566
+ const summaryText = safeSummary(firstString$1(raw, [
6565
6567
  "call_summary",
6566
6568
  "callSummary",
6567
6569
  "summary_text",
@@ -6587,7 +6589,7 @@ var CallHistoryService = class {
6587
6589
  ...payload,
6588
6590
  issuedAtMillis: Date.now()
6589
6591
  }),
6590
- title: firstString(raw, [
6592
+ title: firstString$1(raw, [
6591
6593
  "title",
6592
6594
  "display_name",
6593
6595
  "displayName",
@@ -6630,14 +6632,14 @@ var CallHistoryService = class {
6630
6632
  }));
6631
6633
  }
6632
6634
  participants(raw, viewerUserId) {
6633
- return listValue$18(firstValue(raw, [
6635
+ return listValue$19(firstValue(raw, [
6634
6636
  "participants",
6635
6637
  "participant_profiles",
6636
6638
  "participantProfiles"
6637
6639
  ])).map((item) => {
6638
6640
  const rawItem = objectValue$1(item);
6639
6641
  const userId = Math.trunc(firstNumber(rawItem, ["user_id", "userId"]));
6640
- const displayName = firstString(rawItem, [
6642
+ const displayName = firstString$1(rawItem, [
6641
6643
  "display_name",
6642
6644
  "displayName",
6643
6645
  "name",
@@ -6653,19 +6655,19 @@ var CallHistoryService = class {
6653
6655
  }).filter((item) => item !== void 0).slice(0, 50);
6654
6656
  }
6655
6657
  transcriptSegments(raw) {
6656
- return listValue$18(firstValue(raw, [
6658
+ return listValue$19(firstValue(raw, [
6657
6659
  "segments",
6658
6660
  "transcript_segments",
6659
6661
  "room_transcript_segments"
6660
6662
  ])).map((item, index) => {
6661
6663
  const rawItem = objectValue$1(item);
6662
- const text = clippedText(firstString(rawItem, [
6664
+ const text = clippedText(firstString$1(rawItem, [
6663
6665
  "text",
6664
6666
  "transcript",
6665
6667
  "content"
6666
6668
  ]), 2e3);
6667
6669
  if (text === "") return void 0;
6668
- const segmentId = firstString(rawItem, [
6670
+ const segmentId = firstString$1(rawItem, [
6669
6671
  "segment_id",
6670
6672
  "segmentId",
6671
6673
  "id"
@@ -6678,7 +6680,7 @@ var CallHistoryService = class {
6678
6680
  ]));
6679
6681
  return {
6680
6682
  segmentId,
6681
- speakerDisplayName: firstString(rawItem, [
6683
+ speakerDisplayName: firstString$1(rawItem, [
6682
6684
  "speaker_display_name",
6683
6685
  "speakerDisplayName",
6684
6686
  "speaker_name",
@@ -6727,11 +6729,11 @@ var CallHistoryService = class {
6727
6729
  }
6728
6730
  const value = objectValue$1(payload);
6729
6731
  const parsed = {
6730
- version: numberValue$17(value.version),
6731
- userId: numberValue$17(value.userId),
6732
+ version: numberValue$18(value.version),
6733
+ userId: numberValue$18(value.userId),
6732
6734
  roomId: stringValue$1(value.roomId).trim(),
6733
6735
  stableId: stringValue$1(value.stableId).trim(),
6734
- issuedAtMillis: numberValue$17(value.issuedAtMillis)
6736
+ issuedAtMillis: numberValue$18(value.issuedAtMillis)
6735
6737
  };
6736
6738
  if (parsed.version !== 1 || parsed.userId !== expectedUserId || parsed.roomId === "" || parsed.stableId === "") throw new ArkmePluginError("call-ref-invalid", "通话记录引用无效,请重新查询通话记录", false, 400);
6737
6739
  return parsed;
@@ -7188,7 +7190,7 @@ var ArkmeChatRealtimeRuntime = class {
7188
7190
  //#endregion
7189
7191
  //#region src/services/chat-realtime-service.ts
7190
7192
  const MAX_PROJECTION_RETRIES = 5;
7191
- function listValue$17(value) {
7193
+ function listValue$18(value) {
7192
7194
  return Array.isArray(value) ? value : [];
7193
7195
  }
7194
7196
  function safeFailureMessage$5(error) {
@@ -7340,6 +7342,7 @@ var ChatRealtimeService = class {
7340
7342
  const session = await this.runtime.sessionStore.read();
7341
7343
  if (session === void 0) return;
7342
7344
  this.source.invalidateSourceListCache(session.userId, "send_to_self");
7345
+ this.runtime.invalidateKey(this.runtime.requestScope(session.userId), "calendar:");
7343
7346
  this.emitChatClientEvent({
7344
7347
  type: "projection-invalidated",
7345
7348
  revision: this.nextChatClientRevision(),
@@ -7480,7 +7483,7 @@ var ChatRealtimeService = class {
7480
7483
  lane: "background-read",
7481
7484
  key: `projection:display:${projectionBatchKey}`
7482
7485
  });
7483
- const bundles = new Map(listValue$17(displayData.items).map((raw) => {
7486
+ const bundles = new Map(listValue$18(displayData.items).map((raw) => {
7484
7487
  const bundle = objectValue$1(raw);
7485
7488
  return [stringValue$1(objectValue$1(bundle.session).chat_session_uid).trim(), bundle];
7486
7489
  }).filter(([uid]) => uid !== ""));
@@ -7570,6 +7573,7 @@ var ChatRealtimeService = class {
7570
7573
  }
7571
7574
  if (updates.length > 0) {
7572
7575
  this.source.invalidateSourceListCache(session.userId, "root");
7576
+ this.runtime.invalidateKey(this.runtime.requestScope(session.userId), "calendar:");
7573
7577
  this.emitChatClientEvent({
7574
7578
  type: "sessions-delta",
7575
7579
  revision: this.nextChatClientRevision(),
@@ -7602,10 +7606,10 @@ const ARKME_WORLD_PUBLISH_MAX_IMAGES = 27;
7602
7606
  const ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES = 20 * 1024 * 1024;
7603
7607
  //#endregion
7604
7608
  //#region src/services/record-service.ts
7605
- function numberValue$16(value) {
7609
+ function numberValue$17(value) {
7606
7610
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
7607
7611
  }
7608
- function listValue$16(value) {
7612
+ function listValue$17(value) {
7609
7613
  return Array.isArray(value) ? value : [];
7610
7614
  }
7611
7615
  const DSH_AGENT_INPUT_CREATION_SOURCE = 3;
@@ -7648,8 +7652,8 @@ function normalizedRecordCaptureContext(input) {
7648
7652
  const payload = arkmeRecordCaptureContextPayload(input);
7649
7653
  const clientName = stringValue$1(payload.client_name);
7650
7654
  const networkName = stringValue$1(payload.network_name);
7651
- const electric = numberValue$16(payload.electric);
7652
- const charge = numberValue$16(payload.charge);
7655
+ const electric = numberValue$17(payload.electric);
7656
+ const charge = numberValue$17(payload.charge);
7653
7657
  const normalized = {
7654
7658
  ...clientName === "" ? {} : { clientName },
7655
7659
  ...networkName === "" ? {} : { networkName },
@@ -7677,29 +7681,29 @@ var RecordService = class {
7677
7681
  const data = await this.runtime.authenticatedPost("/api/v1/records/detail", { record_uid: uid }, session, signal);
7678
7682
  const core = objectValue$1(data.record_core);
7679
7683
  const recordUid = stringValue$1(core.record_uid).trim();
7680
- const templateKind = numberValue$16(core.template_kind);
7681
- const displayKind = numberValue$16(core.display_kind);
7684
+ const templateKind = numberValue$17(core.template_kind);
7685
+ const displayKind = numberValue$17(core.display_kind);
7682
7686
  if (recordUid !== uid || templateKind !== 8 && displayKind !== 1) throw new ArkmePluginError("long-article-not-found", "未找到可用的长文详情", false, 404);
7683
7687
  const originContainerRef = stringValue$1(core.origin_container_ref).trim();
7684
7688
  if (source.kind === "topic") {
7685
7689
  if (stringValue$1(objectValue$1(data.topic_core).topic_uid).trim() !== source.ownerRef) throw new ArkmePluginError("long-article-source-mismatch", "长文不属于当前会话", false, 403);
7686
7690
  } else if (source.kind === "private_chat" || source.kind === "group_chat") {
7687
7691
  if (originContainerRef !== source.ownerRef) throw new ArkmePluginError("long-article-source-mismatch", "长文不属于当前会话", false, 403);
7688
- } else if (numberValue$16(core.owner_user_id) !== session.userId) throw new ArkmePluginError("long-article-source-mismatch", "长文不属于当前会话", false, 403);
7689
- const recordDurationMillis = Math.max(0, Math.trunc(numberValue$16(core.record_duration_millis)));
7690
- const editDurationMillis = Math.max(0, Math.trunc(numberValue$16(core.edit_duration_millis)));
7692
+ } else if (numberValue$17(core.owner_user_id) !== session.userId) throw new ArkmePluginError("long-article-source-mismatch", "长文不属于当前会话", false, 403);
7693
+ const recordDurationMillis = Math.max(0, Math.trunc(numberValue$17(core.record_duration_millis)));
7694
+ const editDurationMillis = Math.max(0, Math.trunc(numberValue$17(core.edit_duration_millis)));
7691
7695
  return {
7692
7696
  sourceRef,
7693
7697
  itemUid: recordUid,
7694
7698
  title: stringValue$1(core.title),
7695
7699
  textContent: stringValue$1(core.text_content),
7696
- sendAtMillis: Math.trunc(numberValue$16(core.send_at)),
7697
- updateAtMillis: Math.trunc(numberValue$16(core.update_at)),
7700
+ sendAtMillis: Math.trunc(numberValue$17(core.send_at)),
7701
+ updateAtMillis: Math.trunc(numberValue$17(core.update_at)),
7698
7702
  recordDurationMillis,
7699
7703
  editDurationMillis,
7700
7704
  thinkingDurationMillis: recordDurationMillis + editDurationMillis,
7701
- version: Math.trunc(numberValue$16(core.version)),
7702
- editable: numberValue$16(core.owner_user_id) === session.userId && numberValue$16(core.creator_user_id) === session.userId
7705
+ version: Math.trunc(numberValue$17(core.version)),
7706
+ editable: numberValue$17(core.owner_user_id) === session.userId && numberValue$17(core.creator_user_id) === session.userId
7703
7707
  };
7704
7708
  }
7705
7709
  async updateLongArticle(sourceRef, itemUid, input) {
@@ -7766,9 +7770,9 @@ var RecordService = class {
7766
7770
  const session = await this.runtime.requireSession();
7767
7771
  const data = await this.runtime.authenticatedPost("/api/v1/records/uncategorized/summary", {}, session);
7768
7772
  const summary = {
7769
- recordCount: numberValue$16(data.record_count),
7770
- wordsCount: numberValue$16(data.words_count ?? data.available_text_rune_count),
7771
- totalSec: numberValue$16(data.total_sec ?? data.available_voice_duration_sec)
7773
+ recordCount: numberValue$17(data.record_count),
7774
+ wordsCount: numberValue$17(data.words_count ?? data.available_text_rune_count),
7775
+ totalSec: numberValue$17(data.total_sec ?? data.available_voice_duration_sec)
7772
7776
  };
7773
7777
  await this.runtime.stateStore.cacheSummary(session.userId, summary);
7774
7778
  return summary;
@@ -7789,11 +7793,11 @@ var RecordService = class {
7789
7793
  cursor_record_uid: requestCursor.recordUid
7790
7794
  }
7791
7795
  }, session);
7792
- const rawPageItems = listValue$16(data.items);
7796
+ const rawPageItems = listValue$17(data.items);
7793
7797
  const visiblePageItems = rawPageItems.filter((raw) => !isDSHAgentInputRecord(raw) && !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(this.recordUid(raw)));
7794
7798
  visibleRawItems.push(...visiblePageItems);
7795
7799
  hasMore = data.has_more === true;
7796
- const nextSendAt = numberValue$16(data.next_cursor_send_at);
7800
+ const nextSendAt = numberValue$17(data.next_cursor_send_at);
7797
7801
  const nextUid = stringValue$1(data.next_cursor_record_uid);
7798
7802
  backendNextCursor = nextSendAt > 0 && nextUid !== "" ? {
7799
7803
  sendAtMillis: nextSendAt,
@@ -7816,7 +7820,7 @@ var RecordService = class {
7816
7820
  }).filter((item) => item !== void 0);
7817
7821
  const lastVisibleRaw = rawItems.at(-1);
7818
7822
  const lastVisibleCursor = lastVisibleRaw === void 0 ? void 0 : {
7819
- sendAtMillis: numberValue$16(objectValue$1(lastVisibleRaw).send_at ?? objectValue$1(objectValue$1(lastVisibleRaw).record_core).send_at),
7823
+ sendAtMillis: numberValue$17(objectValue$1(lastVisibleRaw).send_at ?? objectValue$1(objectValue$1(lastVisibleRaw).record_core).send_at),
7820
7824
  recordUid: this.recordUid(lastVisibleRaw)
7821
7825
  };
7822
7826
  const filledRequestedPage = rawItems.length >= normalizedLimit;
@@ -7890,7 +7894,7 @@ var RecordService = class {
7890
7894
  this.runtime.invalidateScope(this.runtime.requestScope(session.userId));
7891
7895
  return {
7892
7896
  recordUid: stringValue$1(data.record_uid) || normalizedUid,
7893
- status: numberValue$16(data.status)
7897
+ status: numberValue$17(data.status)
7894
7898
  };
7895
7899
  }
7896
7900
  async createFileAssetsForConversation(recordUid, textContent, assets) {
@@ -7927,7 +7931,7 @@ var RecordService = class {
7927
7931
  }, session);
7928
7932
  return {
7929
7933
  recordUid: stringValue$1(data.record_uid).trim() || normalizedUid,
7930
- status: numberValue$16(data.status)
7934
+ status: numberValue$17(data.status)
7931
7935
  };
7932
7936
  }
7933
7937
  async pendingWrites() {
@@ -7954,7 +7958,7 @@ var RecordService = class {
7954
7958
  }, session);
7955
7959
  const result = {
7956
7960
  recordUid: stringValue$1(data.record_uid) || pending.recordUid,
7957
- status: numberValue$16(data.status)
7961
+ status: numberValue$17(data.status)
7958
7962
  };
7959
7963
  await this.runtime.stateStore.markSynced(session.userId, pending.recordUid, result.status);
7960
7964
  return result;
@@ -7989,17 +7993,17 @@ var RecordService = class {
7989
7993
  return {
7990
7994
  itemUid: stringValue$1(item.record_uid ?? core.record_uid).trim(),
7991
7995
  senderName: stringValue$1(item.nickname).trim() || "我",
7992
- isMe: options.isMe ?? numberValue$16(item.creator_user_id ?? item.owner_user_id ?? core.creator_user_id ?? core.owner_user_id) === userId,
7993
- sendAtMillis: numberValue$16(item.send_at ?? core.send_at),
7996
+ isMe: options.isMe ?? numberValue$17(item.creator_user_id ?? item.owner_user_id ?? core.creator_user_id ?? core.owner_user_id) === userId,
7997
+ sendAtMillis: numberValue$17(item.send_at ?? core.send_at),
7994
7998
  title: stringValue$1(item.title ?? core.title),
7995
7999
  textContent: stringValue$1(item.text_content ?? core.text_content),
7996
- status: numberValue$16(item.status ?? core.status),
7997
- templateKind: numberValue$16(item.template_kind ?? core.template_kind),
7998
- displayKind: numberValue$16(item.display_kind ?? core.display_kind),
7999
- version: numberValue$16(item.version ?? core.version),
8000
- updateAtMillis: numberValue$16(item.update_at ?? core.update_at),
8001
- recordDurationMillis: numberValue$16(item.record_duration_millis ?? core.record_duration_millis),
8002
- editDurationMillis: numberValue$16(item.edit_duration_millis ?? core.edit_duration_millis),
8000
+ status: numberValue$17(item.status ?? core.status),
8001
+ templateKind: numberValue$17(item.template_kind ?? core.template_kind),
8002
+ displayKind: numberValue$17(item.display_kind ?? core.display_kind),
8003
+ version: numberValue$17(item.version ?? core.version),
8004
+ updateAtMillis: numberValue$17(item.update_at ?? core.update_at),
8005
+ recordDurationMillis: numberValue$17(item.record_duration_millis ?? core.record_duration_millis),
8006
+ editDurationMillis: numberValue$17(item.edit_duration_millis ?? core.edit_duration_millis),
8003
8007
  contentBlocks: this.media.richContentBlocks(raw, userId, options.displayItems),
8004
8008
  ...options.selfTopic === void 0 ? {} : { selfTopic: options.selfTopic },
8005
8009
  ...options.mediaUnavailable === true ? { mediaUnavailable: true } : {}
@@ -8012,14 +8016,14 @@ var RecordService = class {
8012
8016
  if (recordUid === "") return void 0;
8013
8017
  return {
8014
8018
  recordUid,
8015
- sendAtMillis: numberValue$16(item.send_at ?? core.send_at),
8019
+ sendAtMillis: numberValue$17(item.send_at ?? core.send_at),
8016
8020
  title: stringValue$1(core.title),
8017
8021
  textContent: stringValue$1(core.text_content),
8018
- templateKind: numberValue$16(core.template_kind),
8019
- status: numberValue$16(core.status),
8020
- version: numberValue$16(core.version),
8022
+ templateKind: numberValue$17(core.template_kind),
8023
+ status: numberValue$17(core.status),
8024
+ version: numberValue$17(core.version),
8021
8025
  creationSource: recordCreationSource(raw),
8022
- displayKind: numberValue$16(item.display_kind ?? core.display_kind),
8026
+ displayKind: numberValue$17(item.display_kind ?? core.display_kind),
8023
8027
  ...userId === void 0 ? {} : { contentBlocks: this.media.richContentBlocks(raw, userId, options.displayItems) },
8024
8028
  ...options.mediaUnavailable === true ? { mediaUnavailable: true } : {}
8025
8029
  };
@@ -8053,7 +8057,9 @@ const OFFICIAL_AUTHOR_USER_ID = 11;
8053
8057
  const OFFICIAL_AUTHOR_FALLBACK_DISPLAY_NAME = "即我作者";
8054
8058
  const MAX_MESSAGE_COPY_LINK_ITEMS = 100;
8055
8059
  const RECORD_UID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
8056
- function numberValue$15(value) {
8060
+ const CHAT_MEMBER_REF_PREFIX = "arkme-chat-member-v1";
8061
+ const CHAT_HUMAN_MENTION_REF_PREFIX = "arkme-chat-human-mention-v1";
8062
+ function numberValue$16(value) {
8057
8063
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
8058
8064
  }
8059
8065
  function finiteNumberValue(value) {
@@ -8085,7 +8091,7 @@ function timelineCaptureContext(payload) {
8085
8091
  const electricity = objectValue$1(payload.electricity);
8086
8092
  const electricValue = finiteNumberValue(raw.electric ?? payload.electric ?? electricity.electric);
8087
8093
  const electric = electricValue === void 0 ? void 0 : Math.trunc(electricValue);
8088
- const charge = Math.trunc(numberValue$15(raw.charge ?? payload.charge ?? electricity.charge));
8094
+ const charge = Math.trunc(numberValue$16(raw.charge ?? payload.charge ?? electricity.charge));
8089
8095
  const context = {
8090
8096
  ...clientName === "" ? {} : { clientName },
8091
8097
  ...networkName === "" ? {} : { networkName },
@@ -8129,7 +8135,7 @@ function snapshotDateLabel(value) {
8129
8135
  }
8130
8136
  function snapshotMovement(value) {
8131
8137
  const rawSpeed = value.speed ?? value.speed_mps ?? value.speedMetersPerSecond;
8132
- const speed = numberValue$15(rawSpeed);
8138
+ const speed = numberValue$16(rawSpeed);
8133
8139
  if (typeof rawSpeed === "number" && Number.isFinite(rawSpeed)) {
8134
8140
  const kmh = Math.round(speed * 3.6);
8135
8141
  return kmh <= 2 ? "静止" : kmh <= 5 ? `缓慢移动(${String(kmh)}km/h)` : kmh <= 60 ? `移动(${String(kmh)}km/h)` : `高速移动(${String(kmh)}km/h)`;
@@ -8351,19 +8357,19 @@ function snapshotDetailFromChatRaw(raw, fallback) {
8351
8357
  ...position
8352
8358
  });
8353
8359
  const backgroundFiles = [
8354
- ...listValue$15(values.file_assets),
8355
- ...listValue$15(values.fileAssets),
8356
- ...listValue$15(values.media_items),
8357
- ...listValue$15(values.mediaItems),
8358
- ...listValue$15(values.media_refs),
8359
- ...listValue$15(values.mediaRefs),
8360
- ...listValue$15(values.media_display_items),
8361
- ...listValue$15(values.mediaDisplayItems),
8362
- ...listValue$15(values.files),
8363
- ...listValue$15(values.attachments)
8360
+ ...listValue$16(values.file_assets),
8361
+ ...listValue$16(values.fileAssets),
8362
+ ...listValue$16(values.media_items),
8363
+ ...listValue$16(values.mediaItems),
8364
+ ...listValue$16(values.media_refs),
8365
+ ...listValue$16(values.mediaRefs),
8366
+ ...listValue$16(values.media_display_items),
8367
+ ...listValue$16(values.mediaDisplayItems),
8368
+ ...listValue$16(values.files),
8369
+ ...listValue$16(values.attachments)
8364
8370
  ].map(objectValue$1);
8365
- const backgroundSound = backgroundFiles.some((file) => Math.trunc(numberValue$15(file.content_file_role ?? file.contentFileRole)) === 4) || backgroundFiles.some((file) => Math.trunc(numberValue$15(file.binding_type ?? file.bindingType)) === 4) || listValue$15(values.background_sound_amplitudes ?? values.backgroundSoundAmplitudes).length > 0 || listValue$15(parsedObject(values.background_sound ?? values.backgroundSound).amplitudes).length > 0 ? "available" : values.background_sound_enabled === false || values.backgroundSoundEnabled === false ? "disabled" : "not-recorded";
8366
- const status = numberValue$15(values.status);
8371
+ const backgroundSound = backgroundFiles.some((file) => Math.trunc(numberValue$16(file.content_file_role ?? file.contentFileRole)) === 4) || backgroundFiles.some((file) => Math.trunc(numberValue$16(file.binding_type ?? file.bindingType)) === 4) || listValue$16(values.background_sound_amplitudes ?? values.backgroundSoundAmplitudes).length > 0 || listValue$16(parsedObject(values.background_sound ?? values.backgroundSound).amplitudes).length > 0 ? "available" : values.background_sound_enabled === false || values.backgroundSoundEnabled === false ? "disabled" : "not-recorded";
8372
+ const status = numberValue$16(values.status);
8367
8373
  const syncState = status === 2 ? "syncing" : status < 0 ? "failed" : syncedAtMillis !== void 0 || status === 1 ? "synced" : "not-synced";
8368
8374
  const belongDate = snapshotDateLabel(startAtMillis ?? completeAtMillis);
8369
8375
  return {
@@ -8371,8 +8377,8 @@ function snapshotDetailFromChatRaw(raw, fallback) {
8371
8377
  textContent: firstSnapshotText(record.text_content, record.textContent, values.text_content, values.textContent) ?? fallback.textContent,
8372
8378
  ...recordDurationMillis === void 0 ? {} : { recordDurationMillis },
8373
8379
  ...editDurationMillis === void 0 ? {} : { editDurationMillis },
8374
- ...numberValue$15(values.view_times ?? values.viewTimes) > 0 ? { viewTimes: Math.trunc(numberValue$15(values.view_times ?? values.viewTimes)) } : {},
8375
- ...numberValue$15(values.share_times ?? values.shareTimes) > 0 ? { shareTimes: Math.trunc(numberValue$15(values.share_times ?? values.shareTimes)) } : {},
8380
+ ...numberValue$16(values.view_times ?? values.viewTimes) > 0 ? { viewTimes: Math.trunc(numberValue$16(values.view_times ?? values.viewTimes)) } : {},
8381
+ ...numberValue$16(values.share_times ?? values.shareTimes) > 0 ? { shareTimes: Math.trunc(numberValue$16(values.share_times ?? values.shareTimes)) } : {},
8376
8382
  ...captureContext === void 0 ? {} : { captureContext },
8377
8383
  backgroundSound,
8378
8384
  ...locationLabel === void 0 ? {} : { locationLabel },
@@ -8394,15 +8400,15 @@ function favoriteStickerPersistenceItem(item) {
8394
8400
  file_hash: stringValue$1(item.file_hash).trim(),
8395
8401
  file_name: stringValue$1(item.file_name).trim(),
8396
8402
  mime_type: stringValue$1(item.mime_type).trim(),
8397
- file_kind: Math.trunc(numberValue$15(item.file_kind)),
8398
- file_type: Math.trunc(numberValue$15(item.file_type)),
8403
+ file_kind: Math.trunc(numberValue$16(item.file_kind)),
8404
+ file_type: Math.trunc(numberValue$16(item.file_type)),
8399
8405
  is_animated: booleanValue$8(item.is_animated),
8400
- file_size: Math.max(0, Math.trunc(numberValue$15(item.file_size))),
8401
- file_create_at: Math.max(0, Math.trunc(numberValue$15(item.file_create_at))),
8402
- file_push_at: Math.max(0, Math.trunc(numberValue$15(item.file_push_at)))
8406
+ file_size: Math.max(0, Math.trunc(numberValue$16(item.file_size))),
8407
+ file_create_at: Math.max(0, Math.trunc(numberValue$16(item.file_create_at))),
8408
+ file_push_at: Math.max(0, Math.trunc(numberValue$16(item.file_push_at)))
8403
8409
  };
8404
8410
  }
8405
- function listValue$15(value) {
8411
+ function listValue$16(value) {
8406
8412
  return Array.isArray(value) ? value : [];
8407
8413
  }
8408
8414
  function chatMemberRole$1(value) {
@@ -8486,7 +8492,7 @@ function messageCopyLinkSnapshotItemFromData(value) {
8486
8492
  templateKind: integerLikeValue(data.template_kind),
8487
8493
  displayKind: integerLikeValue(data.display_kind),
8488
8494
  officialMark: integerLikeValue(data.official_mark),
8489
- mediaItems: listValue$15(data.media_items).map(messageCopyLinkMediaItemFromData),
8495
+ mediaItems: listValue$16(data.media_items).map(messageCopyLinkMediaItemFromData),
8490
8496
  ...structuredContent === void 0 ? {} : { structuredContent }
8491
8497
  };
8492
8498
  if (item.sendAtMillis <= 0 || item.senderDisplayName.trim() === "" && item.sourceKind !== "agent_message") throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
@@ -8494,7 +8500,7 @@ function messageCopyLinkSnapshotItemFromData(value) {
8494
8500
  return item;
8495
8501
  }
8496
8502
  function messageCopyLinkPresentationNodesFromData(value, itemCount, depth = 1, seenItemIndexes = /* @__PURE__ */ new Set()) {
8497
- const rawNodes = listValue$15(value);
8503
+ const rawNodes = listValue$16(value);
8498
8504
  if (rawNodes.length === 0) return [];
8499
8505
  if (depth > 8) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
8500
8506
  return rawNodes.map((rawNode) => {
@@ -8568,7 +8574,7 @@ function messageCopyLinkExtensionItemFromData(value) {
8568
8574
  const sendAtMillis = epochMillisValue(data.send_at ?? data.sendAt ?? data.created_at ?? data.createdAt ?? data.published_at ?? data.publishedAt ?? core.send_at ?? core.sendAt ?? core.created_at ?? core.createdAt ?? core.published_at ?? core.publishedAt);
8569
8575
  const title = stringValue$1(data.title ?? core.title);
8570
8576
  const textContent = stringValue$1(data.text_content ?? data.textContent ?? data.content ?? core.text_content ?? core.textContent ?? core.content);
8571
- const mediaItems = listValue$15(data.media_items ?? data.mediaItems ?? core.media_items ?? core.mediaItems).map(messageCopyLinkMediaItemFromData);
8577
+ const mediaItems = listValue$16(data.media_items ?? data.mediaItems ?? core.media_items ?? core.mediaItems).map(messageCopyLinkMediaItemFromData);
8572
8578
  const structuredContent = messageCopyLinkStructuredContentFromData(data.structured_content ?? data.structuredContent ?? core.structured_content ?? core.structuredContent);
8573
8579
  if (recordUid === "" || sendAtMillis <= 0 || title.trim() === "" && textContent.trim() === "" && mediaItems.length === 0 && structuredContent === void 0) return;
8574
8580
  return {
@@ -8597,7 +8603,7 @@ function messageCopyLinkExtensionItemFromData(value) {
8597
8603
  }
8598
8604
  function messageCopyLinkRecordContextFromData(data) {
8599
8605
  const context = objectValue$1(data.record_context ?? data.recordContext);
8600
- const extensions = listValue$15(context.extensions ?? context.extend_records ?? context.extendRecords ?? context.list ?? data.extensions ?? data.extend_records ?? data.extendRecords ?? data.list).map(messageCopyLinkExtensionItemFromData).filter((item) => item !== void 0);
8606
+ const extensions = listValue$16(context.extensions ?? context.extend_records ?? context.extendRecords ?? context.list ?? data.extensions ?? data.extend_records ?? data.extendRecords ?? data.list).map(messageCopyLinkExtensionItemFromData).filter((item) => item !== void 0);
8601
8607
  const extensionCount = Math.max(integerLikeValue(context.extension_count ?? context.extensionCount ?? context.total ?? data.extension_count ?? data.extensionCount ?? data.total), extensions.length);
8602
8608
  if (extensionCount <= 0 && extensions.length === 0) return void 0;
8603
8609
  extensions.sort((left, right) => right.sendAtMillis - left.sendAtMillis);
@@ -8683,6 +8689,18 @@ function resolveChatMemberDisplayNames(input) {
8683
8689
  ].find((value) => isUsable(value) && value !== displayName) ?? ""
8684
8690
  };
8685
8691
  }
8692
+ function projectChatMemberDisplayNames(item, userId, viewerLabel, publicDisplayName) {
8693
+ return resolveChatMemberDisplayNames({
8694
+ userId,
8695
+ remarkCandidates: [],
8696
+ memberNameCandidates: [item.display_name_snapshot],
8697
+ userNameCandidates: [
8698
+ viewerLabel,
8699
+ item.remark,
8700
+ publicDisplayName
8701
+ ]
8702
+ });
8703
+ }
8686
8704
  function normalizedJoinTimestamp(value) {
8687
8705
  const raw = integerLikeValue(value);
8688
8706
  if (raw <= 0) return 0;
@@ -8891,7 +8909,7 @@ var ChatService = class {
8891
8909
  if (stringValue$1(data.chat_session_uid).trim() !== normalizedSessionUid) throw new ArkmePluginError("bot-chat-timeline-contract-invalid", "Bot Chat 时间线返回了不匹配的会话", true, 502);
8892
8910
  const projected = [];
8893
8911
  let latestSequence = 0;
8894
- for (const raw of listValue$15(data.items)) {
8912
+ for (const raw of listValue$16(data.items)) {
8895
8913
  const item = objectValue$1(raw);
8896
8914
  const relation = objectValue$1(item.relation);
8897
8915
  const record = objectValue$1(item.record);
@@ -8899,15 +8917,15 @@ var ChatService = class {
8899
8917
  const relationUid = stringValue$1(relation.rel_uid).trim();
8900
8918
  const recordUid = stringValue$1(relation.record_uid).trim();
8901
8919
  const hydratedRecordUid = stringValue$1(payload.record_uid).trim();
8902
- const sequence = Math.trunc(numberValue$15(relation.seq));
8903
- const actorKind = Math.trunc(numberValue$15(relation.sender_actor_kind));
8904
- const senderUserId = Math.trunc(numberValue$15(relation.sender_user_id));
8920
+ const sequence = Math.trunc(numberValue$16(relation.seq));
8921
+ const actorKind = Math.trunc(numberValue$16(relation.sender_actor_kind));
8922
+ const senderUserId = Math.trunc(numberValue$16(relation.sender_user_id));
8905
8923
  const senderBotUid = stringValue$1(relation.sender_bot_uid).trim();
8906
8924
  const userActor = actorKind === 1 && senderUserId === session.userId && senderBotUid === "";
8907
8925
  const botActor = actorKind === 2 && senderBotUid === normalizedBotId;
8908
8926
  if (relationUid === "" || recordUid === "" || hydratedRecordUid !== "" && hydratedRecordUid !== recordUid || !Number.isSafeInteger(sequence) || sequence <= 0 || !userActor && !botActor) throw new ArkmePluginError("bot-chat-timeline-contract-invalid", "Bot Chat 时间线包含无效消息身份", true, 502);
8909
8927
  latestSequence = Math.max(latestSequence, sequence);
8910
- if (numberValue$15(record.status) !== 1) continue;
8928
+ if (numberValue$16(record.status) !== 1) continue;
8911
8929
  const contentBlocks = this.media.richContentBlocks(item, session.userId);
8912
8930
  projected.push({
8913
8931
  messageId: relationUid,
@@ -8915,7 +8933,7 @@ var ChatService = class {
8915
8933
  role: userActor ? "user" : "assistant",
8916
8934
  content: stringValue$1(payload.text_content),
8917
8935
  status: "sent",
8918
- createdAtMillis: Math.max(0, Math.trunc(numberValue$15(relation.attach_at ?? payload.send_at))),
8936
+ createdAtMillis: Math.max(0, Math.trunc(numberValue$16(relation.attach_at ?? payload.send_at))),
8919
8937
  attachments: contentBlocks.map((block) => ({
8920
8938
  kind: block.kind,
8921
8939
  fileName: block.fileName,
@@ -8965,7 +8983,7 @@ var ChatService = class {
8965
8983
  const responseSessionUid = stringValue$1(result.chat_session_uid).trim();
8966
8984
  const responseRecordUid = stringValue$1(result.record_uid).trim();
8967
8985
  const responseRelationUid = stringValue$1(result.rel_uid).trim();
8968
- const sequence = Math.trunc(numberValue$15(result.seq));
8986
+ const sequence = Math.trunc(numberValue$16(result.seq));
8969
8987
  if (responseSessionUid !== chatSessionUidValue || responseRecordUid !== recordUid || responseRelationUid !== relationUid || !Number.isSafeInteger(sequence) || sequence <= 0) throw new ArkmePluginError("bot-conversation-send-outcome-unknown", "消息可能已发送,但服务端确认不完整;请刷新会话确认", false, 409);
8970
8988
  this.realtime.scheduleChatSessionProjection(chatSessionUidValue, sequence);
8971
8989
  return {
@@ -8984,22 +9002,22 @@ var ChatService = class {
8984
9002
  }
8985
9003
  async directBotNotificationPreference(chatSessionUid, options = {}) {
8986
9004
  const { policy } = await this.directBotPolicy(chatSessionUid, options.signal);
8987
- return { muted: numberValue$15(policy.mute_state) === 2 || numberValue$15(policy.notify_state) === 2 };
9005
+ return { muted: numberValue$16(policy.mute_state) === 2 || numberValue$16(policy.notify_state) === 2 };
8988
9006
  }
8989
9007
  async updateDirectBotNotificationPreference(chatSessionUid, muted, options = {}) {
8990
9008
  const { session, sessionUid, policy } = await this.directBotPolicy(chatSessionUid, options.signal);
8991
- const updateAt = Math.max(Date.now(), numberValue$15(policy.update_at) + 1);
9009
+ const updateAt = Math.max(Date.now(), numberValue$16(policy.update_at) + 1);
8992
9010
  const updated = await this.runtime.authenticatedChatPost("/api/v1/chats/policy/update", {
8993
9011
  chat_session_uid: sessionUid,
8994
- show_in_home_state: numberValue$15(policy.show_in_home_state),
8995
- privacy_state: numberValue$15(policy.privacy_state),
9012
+ show_in_home_state: numberValue$16(policy.show_in_home_state),
9013
+ privacy_state: numberValue$16(policy.privacy_state),
8996
9014
  mute_state: muted ? 2 : 1,
8997
- pin_state: numberValue$15(policy.pin_state),
9015
+ pin_state: numberValue$16(policy.pin_state),
8998
9016
  notify_state: muted ? 2 : 1,
8999
- status: numberValue$15(policy.status),
9017
+ status: numberValue$16(policy.status),
9000
9018
  update_at: updateAt
9001
9019
  }, session, options.signal);
9002
- if (stringValue$1(updated.chat_session_uid).trim() !== sessionUid || numberValue$15(updated.user_id) !== session.userId || numberValue$15(updated.show_in_home_state) !== numberValue$15(policy.show_in_home_state) || numberValue$15(updated.privacy_state) !== numberValue$15(policy.privacy_state) || numberValue$15(updated.mute_state) !== (muted ? 2 : 1) || numberValue$15(updated.pin_state) !== numberValue$15(policy.pin_state) || numberValue$15(updated.notify_state) !== (muted ? 2 : 1) || numberValue$15(updated.status) !== numberValue$15(policy.status) || !Number.isSafeInteger(numberValue$15(updated.update_at)) || numberValue$15(updated.update_at) < updateAt) throw new ArkmePluginError("bot-chat-policy-contract-invalid", "Bot Chat 通知策略更新确认不完整", true, 502);
9020
+ if (stringValue$1(updated.chat_session_uid).trim() !== sessionUid || numberValue$16(updated.user_id) !== session.userId || numberValue$16(updated.show_in_home_state) !== numberValue$16(policy.show_in_home_state) || numberValue$16(updated.privacy_state) !== numberValue$16(policy.privacy_state) || numberValue$16(updated.mute_state) !== (muted ? 2 : 1) || numberValue$16(updated.pin_state) !== numberValue$16(policy.pin_state) || numberValue$16(updated.notify_state) !== (muted ? 2 : 1) || numberValue$16(updated.status) !== numberValue$16(policy.status) || !Number.isSafeInteger(numberValue$16(updated.update_at)) || numberValue$16(updated.update_at) < updateAt) throw new ArkmePluginError("bot-chat-policy-contract-invalid", "Bot Chat 通知策略更新确认不完整", true, 502);
9003
9021
  const cacheKey = `${String(session.userId)}:${sessionUid}`;
9004
9022
  const cached = this.source.cachedChatSourceByKey(cacheKey);
9005
9023
  if (cached !== void 0) this.source.setChatSourceByKey(cacheKey, {
@@ -9024,15 +9042,15 @@ var ChatService = class {
9024
9042
  if (sessionUid === "") throw new ArkmePluginError("bot-chat-target-invalid", "Bot Chat 会话目标无效", false, 400);
9025
9043
  const session = await this.runtime.requireSession();
9026
9044
  const policy = await this.runtime.authenticatedChatPost("/api/v1/chats/policy/get", { chat_session_uid: sessionUid }, session, signal);
9027
- const updateAt = numberValue$15(policy.update_at);
9028
- if (stringValue$1(policy.chat_session_uid).trim() !== sessionUid || numberValue$15(policy.user_id) !== session.userId || ![
9045
+ const updateAt = numberValue$16(policy.update_at);
9046
+ if (stringValue$1(policy.chat_session_uid).trim() !== sessionUid || numberValue$16(policy.user_id) !== session.userId || ![
9029
9047
  "show_in_home_state",
9030
9048
  "privacy_state",
9031
9049
  "mute_state",
9032
9050
  "pin_state",
9033
9051
  "notify_state",
9034
9052
  "status"
9035
- ].every((key) => Number.isSafeInteger(numberValue$15(policy[key])) && numberValue$15(policy[key]) > 0) || !Number.isSafeInteger(updateAt) || updateAt <= 0 || updateAt >= Number.MAX_SAFE_INTEGER) throw new ArkmePluginError("bot-chat-policy-contract-invalid", "Bot Chat 通知策略响应不完整", true, 502);
9053
+ ].every((key) => Number.isSafeInteger(numberValue$16(policy[key])) && numberValue$16(policy[key]) > 0) || !Number.isSafeInteger(updateAt) || updateAt <= 0 || updateAt >= Number.MAX_SAFE_INTEGER) throw new ArkmePluginError("bot-chat-policy-contract-invalid", "Bot Chat 通知策略响应不完整", true, 502);
9036
9054
  return {
9037
9055
  session,
9038
9056
  sessionUid,
@@ -9052,7 +9070,11 @@ var ChatService = class {
9052
9070
  const joinEventsEnabled = source.kind === "group_chat" && this.runtime.config.chatMemberJoinEventsEnabled !== false;
9053
9071
  const rawItems = await this.rawChatMembers(source.ownerRef, joinEventsEnabled ? false : activeOnly, session, options.signal);
9054
9072
  const visibleRawItems = activeOnly ? rawItems.filter((item) => chatMemberStatus$1(item.status) === "active") : rawItems;
9055
- const members = await this.projectChatMembers(source.ownerRef, visibleRawItems, session, source.kind === "group_chat", options.signal);
9073
+ const members = await this.projectChatMembers(source.ownerRef, visibleRawItems, session, {
9074
+ includeViewerLabels: source.kind === "group_chat",
9075
+ includeHumanMentionRefs: source.kind === "group_chat",
9076
+ ...options.signal === void 0 ? {} : { signal: options.signal }
9077
+ });
9056
9078
  const signingKey = joinEventsEnabled ? await this.runtime.stateStore.uniqueCode() : "";
9057
9079
  const joinEvents = joinEventsEnabled ? await projectArkmeConversationMemberJoinEvents(rawItems, {
9058
9080
  viewerUserId: session.userId,
@@ -9074,7 +9096,11 @@ var ChatService = class {
9074
9096
  if (mode !== "owner" && mode !== "mentioned") throw new ArkmePluginError("chat-member-record-mode-invalid", "成员快记模式无效", false);
9075
9097
  const reference = await this.openChatMemberRef(memberRef, session.userId, source.ownerRef);
9076
9098
  const rawMembers = await this.rawChatMembers(source.ownerRef, false, session, options.signal);
9077
- const member = (await this.projectChatMembers(source.ownerRef, rawMembers, session, source.kind === "group_chat", options.signal)).find((item) => item.memberRef === memberRef);
9099
+ const member = (await this.projectChatMembers(source.ownerRef, rawMembers, session, {
9100
+ includeViewerLabels: source.kind === "group_chat",
9101
+ includeHumanMentionRefs: source.kind === "group_chat",
9102
+ ...options.signal === void 0 ? {} : { signal: options.signal }
9103
+ })).find((item) => item.memberRef === memberRef);
9078
9104
  if (member === void 0) throw new ArkmePluginError("chat-member-ref-stale", "该成员已不属于当前会话,请刷新后重试", false, 409);
9079
9105
  const limit = Math.min(50, Math.max(1, Math.trunc(options.limit ?? 30)));
9080
9106
  const data = await this.runtime.authenticatedChatPost("/api/v1/chats/members/records/page", {
@@ -9088,8 +9114,8 @@ var ChatService = class {
9088
9114
  key: `member-records:${source.ownerRef}:${String(reference.targetUserId)}:${mode}:${String(Math.max(0, Math.trunc(options.beforeSequence ?? 0)))}`,
9089
9115
  failureCooldownMs: 2e3
9090
9116
  });
9091
- const items = await this.projectChatTimelineItems(listValue$15(data.items), source, session, options.signal);
9092
- const nextBeforeSequence = Math.max(0, Math.trunc(numberValue$15(data.next_before_seq)));
9117
+ const items = await this.projectChatTimelineItems(listValue$16(data.items), source, session, options.signal);
9118
+ const nextBeforeSequence = Math.max(0, Math.trunc(numberValue$16(data.next_before_seq)));
9093
9119
  return {
9094
9120
  source: await this.source.sourceItem(source),
9095
9121
  member,
@@ -9105,7 +9131,7 @@ var ChatService = class {
9105
9131
  if (source.kind !== "group_chat" && source.kind !== "private_chat") throw new ArkmePluginError("chat-members-source-invalid", "仅支持从聊天成员发起私聊", false);
9106
9132
  const reference = await this.openChatMemberRef(memberRef, session.userId, source.ownerRef);
9107
9133
  if (reference.targetUserId === session.userId) throw new ArkmePluginError("private-chat-self-invalid", "不能给自己发起私聊", false, 409);
9108
- const rawMember = (await this.rawChatMembers(source.ownerRef, true, session, options.signal)).find((item) => Math.trunc(numberValue$15(item.user_id)) === reference.targetUserId);
9134
+ const rawMember = (await this.rawChatMembers(source.ownerRef, true, session, options.signal)).find((item) => Math.trunc(numberValue$16(item.user_id)) === reference.targetUserId);
9109
9135
  if (rawMember === void 0) throw new ArkmePluginError("chat-member-ref-stale", "该成员已不属于当前会话,请刷新后重试", false, 409);
9110
9136
  const displayName = stringValue$1(rawMember.remark).trim() || stringValue$1(rawMember.display_name_snapshot).trim() || "群成员";
9111
9137
  return await this.openPrivateChatFromUser(reference.targetUserId, {
@@ -9136,7 +9162,7 @@ var ChatService = class {
9136
9162
  const uid = stringValue$1(chatSession.chat_session_uid).trim();
9137
9163
  if (uid === "") throw new ArkmePluginError("private-chat-contract-invalid", "私聊会话响应不完整", true, 502);
9138
9164
  const unread = objectValue$1(data.unread_snapshot);
9139
- const latestSequence = numberValue$15(unread.session_last_seq ?? chatSession.last_seq);
9165
+ const latestSequence = numberValue$16(unread.session_last_seq ?? chatSession.last_seq);
9140
9166
  const source = {
9141
9167
  sourceRef: await this.source.sealSourceRef(session.userId, "private_chat", uid, displayName),
9142
9168
  sourceKey: await this.source.chatDirectorySourceKey(session.userId, uid),
@@ -9144,8 +9170,8 @@ var ChatService = class {
9144
9170
  kind: "private_chat",
9145
9171
  displayName,
9146
9172
  ...profile?.avatarUrl === void 0 ? {} : { avatarRef: await this.profile.sealProfileImageRef(session.userId, peerUserId) },
9147
- activeAtMillis: numberValue$15(chatSession.last_active_at) || Date.now(),
9148
- unreadCount: numberValue$15(unread.unread_count),
9173
+ activeAtMillis: numberValue$16(chatSession.last_active_at) || Date.now(),
9174
+ unreadCount: numberValue$16(unread.unread_count),
9149
9175
  ...latestSequence > 0 ? { latestSequence } : {}
9150
9176
  };
9151
9177
  this.source.setChatSourceByKey(`${String(session.userId)}:${uid}`, source);
@@ -9161,14 +9187,14 @@ var ChatService = class {
9161
9187
  client_name: "DSH",
9162
9188
  locs: []
9163
9189
  }, session, options.signal);
9164
- const rmSubjectId = Math.trunc(numberValue$15(created.rm_subject_id));
9190
+ const rmSubjectId = Math.trunc(numberValue$16(created.rm_subject_id));
9165
9191
  if (!Number.isSafeInteger(rmSubjectId) || rmSubjectId <= 0) {
9166
9192
  if (booleanValue$8(created.already_exist)) throw new ArkmePluginError("private-chat-self-invalid", "不能给自己发起私聊", false, 409);
9167
9193
  throw new ArkmePluginError("official-author-chat-contract-invalid", "联系作者私聊响应不完整", true, 502);
9168
9194
  }
9169
- const partner = listValue$15((await this.runtime.authenticatedSubjectPost("/api/v1/private/get-partner-info-v2", { rm_subject_ids: [rmSubjectId] }, session, options.signal)).item_ls).map((item) => objectValue$1(item)).find((item) => Math.trunc(numberValue$15(item.rm_subject_id)) === rmSubjectId);
9195
+ const partner = listValue$16((await this.runtime.authenticatedSubjectPost("/api/v1/private/get-partner-info-v2", { rm_subject_ids: [rmSubjectId] }, session, options.signal)).item_ls).map((item) => objectValue$1(item)).find((item) => Math.trunc(numberValue$16(item.rm_subject_id)) === rmSubjectId);
9170
9196
  if (partner === void 0) throw new ArkmePluginError("official-author-chat-contract-invalid", "联系作者私聊资料缺失,请稍后重试", true, 502);
9171
- const peerUserId = Math.trunc(numberValue$15(partner.user_id));
9197
+ const peerUserId = Math.trunc(numberValue$16(partner.user_id));
9172
9198
  if (!Number.isSafeInteger(peerUserId) || peerUserId <= 0) throw new ArkmePluginError("official-author-chat-contract-invalid", "联系作者账号资料缺失,请稍后重试", true, 502);
9173
9199
  const displayName = stringValue$1(partner.mark).trim() || stringValue$1(partner.nick_name).trim() || "作者";
9174
9200
  return await this.openPrivateChatFromUser(peerUserId, {
@@ -9221,7 +9247,7 @@ var ChatService = class {
9221
9247
  ...options.cursor?.sendAtMillis === void 0 ? {} : { cursor_send_at: options.cursor.sendAtMillis },
9222
9248
  ...options.cursor?.itemUid === void 0 ? {} : { cursor_record_uid: options.cursor.itemUid }
9223
9249
  }, session, options.signal);
9224
- const rawRecords = listValue$15(data.items).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(this.record.recordUid(raw)));
9250
+ const rawRecords = listValue$16(data.items).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(this.record.recordUid(raw)));
9225
9251
  const media = await this.media.hydrateRecordMediaPage(rawRecords, session, options.signal);
9226
9252
  const signingKey = await this.runtime.stateStore.uniqueCode();
9227
9253
  const items = (await Promise.all(rawRecords.map(async (raw) => {
@@ -9241,7 +9267,7 @@ var ChatService = class {
9241
9267
  mediaUnavailable: media.unavailableRecordUids.has(recordUid)
9242
9268
  });
9243
9269
  }))).filter((item) => item.itemUid !== "").map((item) => this.withRecordMessageActionRef(source, item, session.userId, signingKey));
9244
- const nextSendAt = numberValue$15(data.next_cursor_send_at);
9270
+ const nextSendAt = numberValue$16(data.next_cursor_send_at);
9245
9271
  const nextUid = stringValue$1(data.next_cursor_record_uid).trim();
9246
9272
  return {
9247
9273
  source: await this.source.sourceItem(source),
@@ -9278,7 +9304,7 @@ var ChatService = class {
9278
9304
  ...options.cursor?.itemUid === void 0 ? {} : { cursor_record_uid: options.cursor.itemUid }
9279
9305
  }, session, options.signal);
9280
9306
  if (arkmePrivacyLockedTopic(data.topic_core)) throw new ArkmePluginError("topic-privacy-locked", "隐私锁主题不能在 Arkme 插件中查看", false, 403);
9281
- const rawRecords = listValue$15(data.records).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(this.record.recordUid(raw)));
9307
+ const rawRecords = listValue$16(data.records).filter((raw) => !arkmePrivacyLockedRecord(raw) && !lockedRecordUids.has(this.record.recordUid(raw)));
9282
9308
  const media = await this.media.hydrateRecordMediaPage(rawRecords, session, options.signal);
9283
9309
  const signingKey = await this.runtime.stateStore.uniqueCode();
9284
9310
  const records = rawRecords.map((raw) => {
@@ -9289,7 +9315,7 @@ var ChatService = class {
9289
9315
  mediaUnavailable: media.unavailableRecordUids.has(recordUid)
9290
9316
  });
9291
9317
  });
9292
- const nextSendAt = numberValue$15(data.next_cursor_send_at);
9318
+ const nextSendAt = numberValue$16(data.next_cursor_send_at);
9293
9319
  const nextUid = stringValue$1(data.next_cursor_record_uid).trim();
9294
9320
  return {
9295
9321
  source: await this.source.sourceItem(source),
@@ -9311,8 +9337,8 @@ var ChatService = class {
9311
9337
  key: `timeline:${source.ownerRef}:${String(Math.max(0, Math.trunc(options.cursor?.beforeSequence ?? 0)))}:${String(limit)}`,
9312
9338
  failureCooldownMs: 2e3
9313
9339
  });
9314
- const items = await this.projectChatTimelineItems(listValue$15(data.items), source, session, options.signal);
9315
- const beforeSequence = numberValue$15(data.next_before_seq);
9340
+ const items = await this.projectChatTimelineItems(listValue$16(data.items), source, session, options.signal);
9341
+ const beforeSequence = numberValue$16(data.next_before_seq);
9316
9342
  let aiPolishSettings;
9317
9343
  let aiPolishNotices;
9318
9344
  if (aiPolishDecorations !== void 0) {
@@ -9351,7 +9377,7 @@ var ChatService = class {
9351
9377
  return {
9352
9378
  messageRef,
9353
9379
  reportUid,
9354
- status: numberValue$15(report.status)
9380
+ status: numberValue$16(report.status)
9355
9381
  };
9356
9382
  }
9357
9383
  async messageReadReceiptSummaries(sourceRef, rawItems, options = {}) {
@@ -9368,15 +9394,15 @@ var ChatService = class {
9368
9394
  if (stringValue$1(data.chat_session_uid).trim() !== source.ownerRef) throw new ArkmePluginError("message-read-receipt-invalid-response", "消息已读状态服务返回了不匹配的会话", true, 502);
9369
9395
  const requestedKeys = new Set(items.map((item) => this.readReceiptItemKey(item.itemUid, item.sequence)));
9370
9396
  const summaries = /* @__PURE__ */ new Map();
9371
- for (const raw of listValue$15(data.items)) {
9397
+ for (const raw of listValue$16(data.items)) {
9372
9398
  const item = objectValue$1(raw);
9373
9399
  const itemUid = stringValue$1(item.record_uid).trim();
9374
- const sequence = Math.trunc(numberValue$15(item.seq));
9400
+ const sequence = Math.trunc(numberValue$16(item.seq));
9375
9401
  const key = this.readReceiptItemKey(itemUid, sequence);
9376
9402
  const rowSessionUid = stringValue$1(item.chat_session_uid).trim();
9377
- const readCount = Math.trunc(numberValue$15(item.read_count));
9378
- const unreadCount = Math.trunc(numberValue$15(item.unread_count));
9379
- const totalMemberCount = Math.trunc(numberValue$15(item.total_member_count));
9403
+ const readCount = Math.trunc(numberValue$16(item.read_count));
9404
+ const unreadCount = Math.trunc(numberValue$16(item.unread_count));
9405
+ const totalMemberCount = Math.trunc(numberValue$16(item.total_member_count));
9380
9406
  if (!requestedKeys.has(key) || summaries.has(key) || rowSessionUid !== source.ownerRef || typeof item.seq !== "number" || !Number.isSafeInteger(item.seq) || typeof item.read_count !== "number" || !Number.isSafeInteger(item.read_count) || typeof item.unread_count !== "number" || !Number.isSafeInteger(item.unread_count) || typeof item.total_member_count !== "number" || !Number.isSafeInteger(item.total_member_count) || readCount < 0 || unreadCount < 0 || totalMemberCount < 0 || readCount + unreadCount !== totalMemberCount) throw new ArkmePluginError("message-read-receipt-invalid-response", "消息已读状态服务返回无效", true, 502);
9381
9407
  summaries.set(key, {
9382
9408
  itemUid,
@@ -9407,13 +9433,13 @@ var ChatService = class {
9407
9433
  record_uid: message.itemUid,
9408
9434
  seq: message.sequence
9409
9435
  }, session, options.signal);
9410
- if (stringValue$1(data.chat_session_uid).trim() !== source.ownerRef || stringValue$1(data.record_uid).trim() !== message.itemUid || typeof data.seq !== "number" || !Number.isSafeInteger(data.seq) || Math.trunc(numberValue$15(data.seq)) !== message.sequence) throw new ArkmePluginError("message-read-receipt-invalid-response", "成员已读详情服务返回了不匹配的消息", true, 502);
9411
- const rawMembers = listValue$15(data.items).map(objectValue$1);
9436
+ if (stringValue$1(data.chat_session_uid).trim() !== source.ownerRef || stringValue$1(data.record_uid).trim() !== message.itemUid || typeof data.seq !== "number" || !Number.isSafeInteger(data.seq) || Math.trunc(numberValue$16(data.seq)) !== message.sequence) throw new ArkmePluginError("message-read-receipt-invalid-response", "成员已读详情服务返回了不匹配的消息", true, 502);
9437
+ const rawMembers = listValue$16(data.items).map(objectValue$1);
9412
9438
  const seenUserIds = /* @__PURE__ */ new Set();
9413
9439
  const receiptMembers = rawMembers.map((item) => {
9414
- const userId = Math.trunc(numberValue$15(item.user_id));
9440
+ const userId = Math.trunc(numberValue$16(item.user_id));
9415
9441
  const readStatus = stringValue$1(item.read_status).trim();
9416
- const readAtMillis = Math.trunc(numberValue$15(item.read_at));
9442
+ const readAtMillis = Math.trunc(numberValue$16(item.read_at));
9417
9443
  if (typeof item.user_id !== "number" || !Number.isSafeInteger(item.user_id) || typeof item.read_at !== "number" || !Number.isSafeInteger(item.read_at) || !Number.isSafeInteger(userId) || userId <= 0 || userId === session.userId || seenUserIds.has(userId) || readStatus !== "read" && readStatus !== "unread" || readAtMillis < 0 || readStatus === "unread" && readAtMillis !== 0) throw new ArkmePluginError("message-read-receipt-invalid-response", "成员已读详情服务返回无效", true, 502);
9418
9444
  seenUserIds.add(userId);
9419
9445
  return {
@@ -9527,7 +9553,7 @@ var ChatService = class {
9527
9553
  if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid)) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
9528
9554
  const session = await this.runtime.requireSession();
9529
9555
  const data = await this.runtime.authenticatedChatPost("/api/v1/chats/messages/copy-link/resolve", { sid: normalizedSid }, session, options.signal);
9530
- const rawItems = listValue$15(data.items);
9556
+ const rawItems = listValue$16(data.items);
9531
9557
  if (rawItems.length === 0 && stringValue$1(data.audit_status) === "auditing") throw new ArkmePluginError("message-copy-link-auditing", "审核中", true, 409);
9532
9558
  const items = rawItems.map(messageCopyLinkSnapshotItemFromData);
9533
9559
  if (items.length === 0 || items.length > MAX_MESSAGE_COPY_LINK_ITEMS) throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
@@ -9535,7 +9561,7 @@ var ChatService = class {
9535
9561
  const sourceContext = objectValue$1(data.source_context);
9536
9562
  const accessMode = stringValue$1(data.access_mode);
9537
9563
  if (accessMode !== "normal" && accessMode !== "link_read_only") throw new ArkmePluginError("message-copy-link-unavailable", "链接暂不可用", false, 404);
9538
- const anchors = accessMode === "normal" ? listValue$15(sourceContext.anchors).map(messageCopyLinkSourceAnchorFromData).filter((anchor) => anchor.relationUid.trim() !== "" && anchor.recordUid.trim() !== "" && anchor.recordOwnerUserId > 0) : [];
9564
+ const anchors = accessMode === "normal" ? listValue$16(sourceContext.anchors).map(messageCopyLinkSourceAnchorFromData).filter((anchor) => anchor.relationUid.trim() !== "" && anchor.recordUid.trim() !== "" && anchor.recordOwnerUserId > 0) : [];
9539
9565
  const recordContext = mergeMessageCopyLinkRecordContexts(messageCopyLinkRecordContextFromData(data), accessMode === "normal" ? await this.loadMessageCopyLinkPublicExtensions(messageCopyLinkRecordUidCandidates(items, anchors), session, options.signal) : void 0);
9540
9566
  const result = {
9541
9567
  sid: stringValue$1(data.sid),
@@ -9572,7 +9598,7 @@ var ChatService = class {
9572
9598
  lane: "interactive-read",
9573
9599
  bypassCache: true
9574
9600
  });
9575
- const rawItems = listValue$15(data.list ?? data.items);
9601
+ const rawItems = listValue$16(data.list ?? data.items);
9576
9602
  if (offset === 0) total = Math.max(0, integerLikeValue(data.total));
9577
9603
  const pageItems = rawItems.map((raw) => messageCopyLinkExtensionItemFromData(raw)).filter((item) => item !== void 0);
9578
9604
  extensions.push(...pageItems);
@@ -9632,17 +9658,12 @@ var ChatService = class {
9632
9658
  const publishedRecordUid = stringValue$1(data.record_uid).trim() || createdRecordUid;
9633
9659
  const senderDisplayName = profile.nickname.trim() || profile.displayName.trim() || "我";
9634
9660
  const senderAvatarUrl = profile.avatarRef.trim();
9635
- this.source.invalidateSourceListCache(session.userId, "send_to_self");
9636
- this.realtime.emitChatClientEvent({
9637
- type: "projection-invalidated",
9638
- revision: this.realtime.nextChatClientRevision(),
9639
- projection: "record"
9640
- });
9661
+ await this.realtime.invalidateRecordProjection();
9641
9662
  return {
9642
9663
  sid: detail.sid,
9643
9664
  recordUid: publishedRecordUid,
9644
9665
  parentRecordUid,
9645
- status: numberValue$15(data.status ?? data.check_status ?? recordResult.status),
9666
+ status: numberValue$16(data.status ?? data.check_status ?? recordResult.status),
9646
9667
  localState: "synced",
9647
9668
  extension: {
9648
9669
  recordUid: publishedRecordUid,
@@ -9712,12 +9733,12 @@ var ChatService = class {
9712
9733
  ...commentText === "" ? {} : { comment_text: commentText },
9713
9734
  send_at: sendAtMillis
9714
9735
  }, session, options.signal);
9715
- const sequence = numberValue$15(data.seq ?? data.sequence);
9736
+ const sequence = numberValue$16(data.seq ?? data.sequence);
9716
9737
  this.realtime.scheduleChatSessionProjection(targetSource.ownerRef, sequence);
9717
9738
  return {
9718
9739
  sourceRef: targetSourceRef,
9719
9740
  itemUid: stringValue$1(data.record_uid).trim() || recordUid,
9720
- status: numberValue$15(data.audit_status ?? data.status) || 1,
9741
+ status: numberValue$16(data.audit_status ?? data.status) || 1,
9721
9742
  ...sequence > 0 ? { sequence } : {},
9722
9743
  localState: "synced"
9723
9744
  };
@@ -9732,11 +9753,11 @@ var ChatService = class {
9732
9753
  content_payload: contentPayload,
9733
9754
  send_at: Date.now()
9734
9755
  }, session, options.signal);
9735
- this.source.invalidateSourceListCache(session.userId, "send_to_self");
9756
+ await this.realtime.invalidateRecordProjection();
9736
9757
  return await appendCommentWarning({
9737
9758
  sourceRef: targetSourceRef,
9738
9759
  itemUid: stringValue$1(data.record_uid).trim() || recordUid,
9739
- status: numberValue$15(data.status),
9760
+ status: numberValue$16(data.status),
9740
9761
  localState: "synced"
9741
9762
  });
9742
9763
  }
@@ -9748,11 +9769,11 @@ var ChatService = class {
9748
9769
  content_payload: contentPayload,
9749
9770
  send_at: Date.now()
9750
9771
  }, session, options.signal);
9751
- this.source.invalidateSourceListCache(session.userId, "send_to_self");
9772
+ await this.realtime.invalidateRecordProjection();
9752
9773
  return await appendCommentWarning({
9753
9774
  sourceRef: targetSourceRef,
9754
9775
  itemUid: stringValue$1(data.record_uid).trim() || recordUid,
9755
- status: numberValue$15(data.status),
9776
+ status: numberValue$16(data.status),
9756
9777
  localState: "synced"
9757
9778
  });
9758
9779
  }
@@ -9767,7 +9788,7 @@ var ChatService = class {
9767
9788
  ...options.recordDurationMillis === void 0 ? {} : { recordDurationMillis: options.recordDurationMillis },
9768
9789
  ...options.captureContext === void 0 ? {} : { captureContext: options.captureContext }
9769
9790
  });
9770
- if (result.localState !== "failed") this.source.invalidateSourceListCache(session.userId, "send_to_self");
9791
+ if (result.localState !== "failed") await this.realtime.invalidateRecordProjection();
9771
9792
  return {
9772
9793
  sourceRef,
9773
9794
  itemUid: result.recordUid,
@@ -9788,11 +9809,11 @@ var ChatService = class {
9788
9809
  ...captureContext === void 0 || Object.keys(captureContext).length === 0 ? {} : { capture_context: captureContext },
9789
9810
  send_at: Date.now()
9790
9811
  }, session);
9791
- this.source.invalidateSourceListCache(session.userId, "send_to_self");
9812
+ await this.realtime.invalidateRecordProjection();
9792
9813
  return {
9793
9814
  sourceRef,
9794
9815
  itemUid: stringValue$1(result.record_uid).trim() || recordUid,
9795
- status: numberValue$15(result.status),
9816
+ status: numberValue$16(result.status),
9796
9817
  localState: "synced"
9797
9818
  };
9798
9819
  }
@@ -9807,7 +9828,7 @@ var ChatService = class {
9807
9828
  ...options.captureContext === void 0 ? {} : { captureContext: options.captureContext }
9808
9829
  });
9809
9830
  else if ((options.humanMentions?.length ?? 0) > 0 || (options.botMentions?.length ?? 0) > 0) {
9810
- const contentPayload = await this.humanMentionContentPayload(source, textContent, text, options.humanMentions ?? [], session, options.signal, options.botMentions ?? []);
9831
+ const contentPayload = await this.mentionContentPayload(source, textContent, text, options.humanMentions ?? [], options.botMentions ?? [], session, options.signal);
9811
9832
  sent = await this.sendChatSourceTextRaw(sourceRef, source.ownerRef, text, recordUid, relationUid, session, void 0, contentPayload, options.signal, {
9812
9833
  agentAuthored,
9813
9834
  ...options.recordDurationMillis === void 0 ? {} : { recordDurationMillis: options.recordDurationMillis },
@@ -9822,7 +9843,7 @@ var ChatService = class {
9822
9843
  } else if ((options.humanMentions?.length ?? 0) > 0) throw new ArkmePluginError("mention-group-required", "真人 mention 只能发送到群聊", false);
9823
9844
  else if ((options.botRefs?.length ?? 0) > 0) throw new ArkmePluginError("mention-group-required", "bot_refs 只能发送到群聊", false);
9824
9845
  else if ((options.botMentions?.length ?? 0) > 0) {
9825
- const contentPayload = await this.humanMentionContentPayload(source, textContent, text, [], session, options.signal, options.botMentions ?? []);
9846
+ const contentPayload = await this.mentionContentPayload(source, textContent, text, [], options.botMentions ?? [], session, options.signal);
9826
9847
  sent = await this.sendChatSourceTextRaw(sourceRef, source.ownerRef, text, recordUid, relationUid, session, void 0, contentPayload, options.signal, {
9827
9848
  agentAuthored,
9828
9849
  ...options.recordDurationMillis === void 0 ? {} : { recordDurationMillis: options.recordDurationMillis },
@@ -9836,44 +9857,10 @@ var ChatService = class {
9836
9857
  if (agentAuthored && sent.localState === "synced") this.arko.agentSourceDisplayName(session);
9837
9858
  return sent;
9838
9859
  }
9839
- async humanMentionContentPayload(source, rawText, normalizedText, inputs, session, signal, botInputs = []) {
9840
- if (inputs.length > 50) throw new ArkmePluginError("human-mention-invalid", "单条消息 mention 数量过多", false);
9860
+ async mentionContentPayload(source, rawText, normalizedText, humanInputs, botInputs, session, signal) {
9861
+ if (humanInputs.length > 50) throw new ArkmePluginError("human-mention-invalid", "单条消息 mention 数量过多", false);
9841
9862
  if (botInputs.length > 50) throw new ArkmePluginError("bot-mention-invalid", "单条消息 Bot mention 数量过多", false);
9842
- const leadingTrim = rawText.length - rawText.trimStart().length;
9843
- const rawMembers = inputs.length === 0 ? [] : await this.rawChatMembers(source.ownerRef, true, session, signal);
9844
- const membersByUserId = new Map(rawMembers.map((item) => [Math.trunc(numberValue$15(item.user_id)), item]));
9845
- const mentions = [];
9846
- for (const input of [...inputs].sort((left, right) => left.startIndex - right.startIndex)) {
9847
- const memberRef = input.memberRef?.trim() ?? "";
9848
- const startIndex = Math.trunc(input.startIndex) - leadingTrim;
9849
- const length = Math.trunc(input.length);
9850
- if (startIndex < 0 || length < 2 || startIndex + length > normalizedText.length) throw new ArkmePluginError("human-mention-invalid", "真人 mention 引用或文本区间无效", false);
9851
- const visible = normalizedText.slice(startIndex, startIndex + length);
9852
- const displayName = visible.startsWith("@") ? visible.slice(1).trim() : "";
9853
- let userId;
9854
- if (input.all === true) {
9855
- if (displayName !== "所有人") throw new ArkmePluginError("human-mention-text-mismatch", "真人 mention 文本已变化,请重新选择成员", false, 409);
9856
- userId = 0;
9857
- } else {
9858
- if (memberRef === "") throw new ArkmePluginError("human-mention-invalid", "真人 mention 引用或文本区间无效", false);
9859
- const reference = await this.openChatMemberRef(memberRef, session.userId, source.ownerRef);
9860
- if (reference.targetUserId === session.userId) throw new ArkmePluginError("human-mention-self-invalid", "不能 @ 自己", false);
9861
- const rawMember = membersByUserId.get(reference.targetUserId);
9862
- if (rawMember === void 0) throw new ArkmePluginError("chat-member-ref-stale", "被 @ 成员已不在当前群聊", false, 409);
9863
- const expectedNames = [stringValue$1(rawMember.remark).trim(), stringValue$1(rawMember.display_name_snapshot).trim()].filter((value) => value !== "");
9864
- if (displayName === "" || !expectedNames.includes(displayName)) throw new ArkmePluginError("human-mention-text-mismatch", "真人 mention 文本已变化,请重新选择成员", false, 409);
9865
- userId = reference.targetUserId;
9866
- }
9867
- const previous = mentions.at(-1);
9868
- if (previous !== void 0 && previous.start_index + previous.length > startIndex) throw new ArkmePluginError("human-mention-overlap", "真人 mention 文本区间重叠", false);
9869
- mentions.push({
9870
- user_id: userId,
9871
- display_name_snapshot: displayName,
9872
- start_index: startIndex,
9873
- length
9874
- });
9875
- }
9876
- const botMentions = await this.botMentionMetadata(source, rawText, normalizedText, botInputs, session, signal);
9863
+ const [mentions, botMentions] = await Promise.all([this.humanMentionMetadata(source, rawText, normalizedText, humanInputs, session, signal), this.botMentionMetadata(source, rawText, normalizedText, botInputs, session, signal)]);
9877
9864
  const orderedRanges = [...mentions.map((mention) => ({
9878
9865
  startIndex: mention.start_index,
9879
9866
  length: mention.length
@@ -9911,6 +9898,45 @@ var ChatService = class {
9911
9898
  }
9912
9899
  };
9913
9900
  }
9901
+ async humanMentionMetadata(source, rawText, normalizedText, inputs, session, signal) {
9902
+ const leadingTrim = rawText.length - rawText.trimStart().length;
9903
+ const rawMembers = inputs.some((input) => input.all !== true) ? await this.rawChatMembers(source.ownerRef, true, session, signal) : [];
9904
+ const membersByUserId = new Map(rawMembers.map((item) => [Math.trunc(numberValue$16(item.user_id)), item]));
9905
+ const mentions = [];
9906
+ for (const input of [...inputs].sort((left, right) => left.startIndex - right.startIndex)) {
9907
+ const rawInput = input;
9908
+ const mentionRef = input.mentionRef?.trim() ?? "";
9909
+ const memberRef = stringValue$1(rawInput.memberRef).trim();
9910
+ const startIndex = Math.trunc(input.startIndex) - leadingTrim;
9911
+ const length = Math.trunc(input.length);
9912
+ if (startIndex < 0 || length < 2 || startIndex + length > normalizedText.length) throw new ArkmePluginError("human-mention-invalid", "真人 mention 引用或文本区间无效", false);
9913
+ const visible = normalizedText.slice(startIndex, startIndex + length);
9914
+ const displayName = visible.startsWith("@") ? visible.slice(1) : "";
9915
+ let userId;
9916
+ if (input.all === true) {
9917
+ if (mentionRef !== "" || memberRef !== "") throw new ArkmePluginError("human-mention-invalid", "@所有人不能携带成员引用", false);
9918
+ if (displayName !== "所有人") throw new ArkmePluginError("human-mention-text-mismatch", "真人 mention 文本已变化,请重新选择成员", false, 409);
9919
+ userId = 0;
9920
+ } else {
9921
+ if (mentionRef === "" || memberRef !== "") throw new ArkmePluginError("human-mention-invalid", "真人 mention 必须且只能携带 mention 引用", false);
9922
+ const reference = await this.openChatHumanMentionRef(mentionRef, session.userId, source.ownerRef);
9923
+ const targetUserId = reference.targetUserId;
9924
+ if (targetUserId === session.userId) throw new ArkmePluginError("human-mention-self-invalid", "不能 @ 自己", false);
9925
+ if (membersByUserId.get(targetUserId) === void 0) throw new ArkmePluginError("chat-member-ref-stale", "被 @ 成员已不在当前群聊", false, 409);
9926
+ if (displayName === "" || displayName !== reference.displayNameSnapshot) throw new ArkmePluginError("human-mention-text-mismatch", "真人 mention 文本已变化,请重新选择成员", false, 409);
9927
+ userId = targetUserId;
9928
+ }
9929
+ const previous = mentions.at(-1);
9930
+ if (previous !== void 0 && previous.start_index + previous.length > startIndex) throw new ArkmePluginError("human-mention-overlap", "真人 mention 文本区间重叠", false);
9931
+ mentions.push({
9932
+ user_id: userId,
9933
+ display_name_snapshot: displayName,
9934
+ start_index: startIndex,
9935
+ length
9936
+ });
9937
+ }
9938
+ return mentions;
9939
+ }
9914
9940
  async botMentionMetadata(source, rawText, normalizedText, inputs, session, signal) {
9915
9941
  if (inputs.length === 0) return [];
9916
9942
  if (source.kind !== "group_chat" && source.kind !== "private_chat") throw new ArkmePluginError("mention-chat-required", "Bot mention 只能发送到聊天", false);
@@ -10017,12 +10043,12 @@ var ChatService = class {
10017
10043
  ...initialAiPolish === void 0 ? {} : { initialAiPolish },
10018
10044
  ...contentPayload === void 0 ? {} : { contentPayload }
10019
10045
  });
10020
- const sequence = numberValue$15(result.seq);
10046
+ const sequence = numberValue$16(result.seq);
10021
10047
  this.realtime.scheduleChatSessionProjection(chatSessionUid, sequence);
10022
10048
  return {
10023
10049
  sourceRef,
10024
10050
  itemUid: stringValue$1(result.record_uid).trim() || recordUid,
10025
- status: numberValue$15(result.audit_status),
10051
+ status: numberValue$16(result.audit_status),
10026
10052
  sequence,
10027
10053
  localState: "synced"
10028
10054
  };
@@ -10088,7 +10114,7 @@ var ChatService = class {
10088
10114
  if ((input.humanMentions?.length ?? 0) > 0 && source.kind !== "group_chat") throw new ArkmePluginError("mention-group-required", "真人 mention 只能发送到群聊", false);
10089
10115
  if ((input.botMentions?.length ?? 0) > 0 && source.kind !== "group_chat" && source.kind !== "private_chat") throw new ArkmePluginError("mention-chat-required", "Bot mention 只能发送到聊天", false);
10090
10116
  if (longArticle) throw new ArkmePluginError("mention-rich-invalid", "长文暂不支持 mention", false);
10091
- const mentionPayload = await this.humanMentionContentPayload(source, input.textContent ?? "", textContent, input.humanMentions ?? [], session, options.signal, input.botMentions ?? []);
10117
+ const mentionPayload = await this.mentionContentPayload(source, input.textContent ?? "", textContent, input.humanMentions ?? [], input.botMentions ?? [], session, options.signal);
10092
10118
  contentPayload = {
10093
10119
  ...mediaContentPayload ?? {},
10094
10120
  ...mentionPayload
@@ -10107,11 +10133,11 @@ var ChatService = class {
10107
10133
  };
10108
10134
  if (source.kind === "send_to_self" || source.kind === "default_category") {
10109
10135
  const result = await this.runtime.authenticatedPost("/api/v1/records/create", commonBody, session, options.signal);
10110
- this.source.invalidateSourceListCache(session.userId, "send_to_self");
10136
+ await this.realtime.invalidateRecordProjection();
10111
10137
  return {
10112
10138
  sourceRef,
10113
10139
  itemUid: stringValue$1(result.record_uid).trim() || recordUid,
10114
- status: numberValue$15(result.status),
10140
+ status: numberValue$16(result.status),
10115
10141
  localState: "synced"
10116
10142
  };
10117
10143
  }
@@ -10120,11 +10146,11 @@ var ChatService = class {
10120
10146
  topic_uid: source.ownerRef,
10121
10147
  ...commonBody
10122
10148
  }, session, options.signal);
10123
- this.source.invalidateSourceListCache(session.userId, "send_to_self");
10149
+ await this.realtime.invalidateRecordProjection();
10124
10150
  return {
10125
10151
  sourceRef,
10126
10152
  itemUid: stringValue$1(result.record_uid).trim() || recordUid,
10127
- status: numberValue$15(result.status),
10153
+ status: numberValue$16(result.status),
10128
10154
  localState: "synced"
10129
10155
  };
10130
10156
  }
@@ -10133,12 +10159,12 @@ var ChatService = class {
10133
10159
  rel_uid: relationUid,
10134
10160
  ...commonBody
10135
10161
  }, session, options.signal);
10136
- const sequence = numberValue$15(result.seq);
10162
+ const sequence = numberValue$16(result.seq);
10137
10163
  this.realtime.scheduleChatSessionProjection(source.ownerRef, sequence);
10138
10164
  return {
10139
10165
  sourceRef,
10140
10166
  itemUid: stringValue$1(result.record_uid).trim() || recordUid,
10141
- status: numberValue$15(result.audit_status ?? result.status),
10167
+ status: numberValue$16(result.audit_status ?? result.status),
10142
10168
  sequence,
10143
10169
  localState: "synced"
10144
10170
  };
@@ -10147,7 +10173,7 @@ var ChatService = class {
10147
10173
  const root = await this.runtime.authenticatedChatPost("/api/v1/chats/favorite-stickers/get", {}, session, signal);
10148
10174
  return {
10149
10175
  root,
10150
- items: listValue$15(root.items).map(objectValue$1)
10176
+ items: listValue$16(root.items).map(objectValue$1)
10151
10177
  };
10152
10178
  }
10153
10179
  favoriteStickerView(raw, viewerUserId) {
@@ -10161,7 +10187,7 @@ var ChatService = class {
10161
10187
  fileAssetUid,
10162
10188
  fileName,
10163
10189
  mimeType,
10164
- size: Math.max(0, Math.trunc(numberValue$15(raw.file_size ?? raw.size))),
10190
+ size: Math.max(0, Math.trunc(numberValue$16(raw.file_size ?? raw.size))),
10165
10191
  fileKind: 1,
10166
10192
  isAnimated: booleanValue$8(raw.is_animated) || mimeType.toLowerCase() === "image/gif",
10167
10193
  isAvailable: serverAvailable && mediaRef !== void 0,
@@ -10179,7 +10205,7 @@ var ChatService = class {
10179
10205
  return {
10180
10206
  items: projected,
10181
10207
  itemCount: projected.length,
10182
- updatedAtMillis: Math.max(0, Math.trunc(numberValue$15(root.updated_at_millis ?? root.updated_at)))
10208
+ updatedAtMillis: Math.max(0, Math.trunc(numberValue$16(root.updated_at_millis ?? root.updated_at)))
10183
10209
  };
10184
10210
  }
10185
10211
  async addFavoriteSticker(item, signal) {
@@ -10243,7 +10269,7 @@ var ChatService = class {
10243
10269
  render_role: 3,
10244
10270
  sort_order: 0,
10245
10271
  file_kind: 1,
10246
- file_type: Math.trunc(numberValue$15(sticker.file_type)),
10272
+ file_type: Math.trunc(numberValue$16(sticker.file_type)),
10247
10273
  mime_type: stringValue$1(sticker.mime_type).trim(),
10248
10274
  file_name: stringValue$1(sticker.file_name).trim()
10249
10275
  }]
@@ -10253,8 +10279,8 @@ var ChatService = class {
10253
10279
  return {
10254
10280
  sourceRef,
10255
10281
  itemUid: stringValue$1(result.record_uid).trim() || recordUid,
10256
- status: numberValue$15(result.audit_status ?? result.status),
10257
- sequence: numberValue$15(result.seq),
10282
+ status: numberValue$16(result.audit_status ?? result.status),
10283
+ sequence: numberValue$16(result.seq),
10258
10284
  localState: "synced"
10259
10285
  };
10260
10286
  }
@@ -10298,7 +10324,7 @@ var ChatService = class {
10298
10324
  send_at: sendAtMillis
10299
10325
  }, session, options.signal);
10300
10326
  const chatSessionUid = stringValue$1(result.chat_session_uid).trim();
10301
- const sequence = numberValue$15(result.seq);
10327
+ const sequence = numberValue$16(result.seq);
10302
10328
  const targetKind = stringValue$1(result.target_kind).trim();
10303
10329
  if (chatSessionUid === "" || !Number.isSafeInteger(sequence) || sequence <= 0 || targetKind !== "direct") throw new ArkmePluginError("direct-send-response-invalid", "Chat Agent 发送返回了无效响应", true, 502);
10304
10330
  const responseRecordUid = stringValue$1(result.record_uid).trim() || recordUid;
@@ -10330,11 +10356,11 @@ var ChatService = class {
10330
10356
  reason: "arkme_dsh_open_chat"
10331
10357
  }, session, signal);
10332
10358
  const responseSessionUid = stringValue$1(data.chat_session_uid).trim();
10333
- const responseUserId = numberValue$15(data.user_id);
10334
- const effectiveReadSequence = numberValue$15(data.effective_read_seq);
10335
- const readAt = numberValue$15(data.read_at);
10336
- const sessionLastSequence = numberValue$15(data.session_last_seq);
10337
- const unreadCount = numberValue$15(data.unread_count);
10359
+ const responseUserId = numberValue$16(data.user_id);
10360
+ const effectiveReadSequence = numberValue$16(data.effective_read_seq);
10361
+ const readAt = numberValue$16(data.read_at);
10362
+ const sessionLastSequence = numberValue$16(data.session_last_seq);
10363
+ const unreadCount = numberValue$16(data.unread_count);
10338
10364
  if (responseSessionUid !== chatSessionUid || responseUserId !== session.userId || !Number.isSafeInteger(effectiveReadSequence) || effectiveReadSequence < readSequence || !Number.isSafeInteger(readAt) || readAt <= 0 || !Number.isSafeInteger(sessionLastSequence) || sessionLastSequence < effectiveReadSequence || !Number.isSafeInteger(unreadCount) || unreadCount < 0) throw new ArkmePluginError("source-read-ack-invalid", "聊天已读响应不完整", true, 502);
10339
10365
  const cacheKey = `${String(session.userId)}:${chatSessionUid}`;
10340
10366
  const cached = this.source.cachedChatSourceByKey(cacheKey);
@@ -10364,7 +10390,7 @@ var ChatService = class {
10364
10390
  async chatTimelineItems(data, session, chatSessionUid) {
10365
10391
  const items = [];
10366
10392
  const signingKey = await this.runtime.stateStore.uniqueCode();
10367
- for (const raw of listValue$15(data.items)) {
10393
+ for (const raw of listValue$16(data.items)) {
10368
10394
  const item = objectValue$1(raw);
10369
10395
  const relation = objectValue$1(item.relation);
10370
10396
  const record = objectValue$1(item.record);
@@ -10375,7 +10401,7 @@ var ChatService = class {
10375
10401
  const relationUid = stringValue$1(relation.rel_uid ?? relation.relUid).trim();
10376
10402
  const recordOwnerUserId = chatRecordOwnerUserId(relation, record, payload, senderUserId);
10377
10403
  const aiPolish = this.aiPolish.timelineAiPolish(record, payload);
10378
- const sendAtMillis = numberValue$15(relation.attach_at ?? payload.send_at);
10404
+ const sendAtMillis = numberValue$16(relation.attach_at ?? payload.send_at);
10379
10405
  const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
10380
10406
  const sharedRecording = this.withSharedRecordingDetailRef(this.chatSharedRecordingPreview(item), {
10381
10407
  viewerUserId: session.userId,
@@ -10407,9 +10433,9 @@ var ChatService = class {
10407
10433
  title: stringValue$1(payload.title),
10408
10434
  textContent: stringValue$1(payload.text_content),
10409
10435
  sendAtMillis,
10410
- sourceSequence: numberValue$15(relation.seq),
10411
- templateKind: numberValue$15(payload.template_kind),
10412
- displayKind: numberValue$15(payload.display_kind),
10436
+ sourceSequence: numberValue$16(relation.seq),
10437
+ templateKind: numberValue$16(payload.template_kind),
10438
+ displayKind: numberValue$16(payload.display_kind),
10413
10439
  contentBlocks
10414
10440
  }), signingKey) },
10415
10441
  ...senderUserId > 0 ? { memberRef: await this.sealChatMemberRef(session.userId, chatSessionUid, senderUserId) } : {},
@@ -10421,18 +10447,35 @@ var ChatService = class {
10421
10447
  sendAtMillis,
10422
10448
  title: stringValue$1(payload.title),
10423
10449
  textContent: stringValue$1(payload.text_content),
10424
- status: numberValue$15(record.status),
10425
- sequence: numberValue$15(relation.seq),
10426
- ...numberValue$15(record.version ?? payload.version) > 0 ? { recordVersion: numberValue$15(record.version ?? payload.version) } : {},
10450
+ status: numberValue$16(record.status),
10451
+ sequence: numberValue$16(relation.seq),
10452
+ ...numberValue$16(record.version ?? payload.version) > 0 ? { recordVersion: numberValue$16(record.version ?? payload.version) } : {},
10427
10453
  ...aiPolish === void 0 ? {} : { aiPolish },
10428
10454
  ...forwardRecords === void 0 ? {} : { forwardRecords },
10429
10455
  ...sharedRecording === void 0 ? {} : { sharedRecording },
10430
- displayKind: numberValue$15(payload.display_kind),
10456
+ displayKind: numberValue$16(payload.display_kind),
10431
10457
  contentBlocks
10432
10458
  });
10433
10459
  }
10434
10460
  return items;
10435
10461
  }
10462
+ async relatedQuickNoteLocator(sourceRef, messageActionRef) {
10463
+ const session = await this.runtime.requireSession();
10464
+ const normalizedSourceRef = sourceRef.trim();
10465
+ const source = await this.source.openSourceRef(normalizedSourceRef, session.userId);
10466
+ const reference = await this.openMessageActionRef(messageActionRef, session.userId, source);
10467
+ const ownerUserId = reference.recordOwnerUserId > 0 ? reference.recordOwnerUserId : reference.senderUserId;
10468
+ if (!Number.isSafeInteger(ownerUserId) || ownerUserId <= 0) throw new ArkmePluginError("related-quick-note-source-invalid", "当前快记缺少作者定位,请刷新后重试", false, 409);
10469
+ return {
10470
+ viewerUserId: session.userId,
10471
+ sourceRef: normalizedSourceRef,
10472
+ sourceOwnerRef: source.ownerRef,
10473
+ contextType: reference.sourceKind === "chat_relation" ? "chat" : "record",
10474
+ recordUid: reference.recordUid,
10475
+ recordOwnerUserId: ownerUserId,
10476
+ chatSessionUid: reference.sourceKind === "chat_relation" ? reference.chatSessionUid : ""
10477
+ };
10478
+ }
10436
10479
  async chatForwardRecordsPreview(raw, viewerUserId, fallbackCreatedAtMillis) {
10437
10480
  const contentPayload = this.media.recordContentPayload(raw);
10438
10481
  const nested = objectValue$1(contentPayload.forward_records ?? contentPayload.forwardRecords);
@@ -10442,7 +10485,7 @@ var ChatService = class {
10442
10485
  let truncated = false;
10443
10486
  let remainingSegments = 2e3;
10444
10487
  const epochMillis = (value) => {
10445
- const time = numberValue$15(value);
10488
+ const time = numberValue$16(value);
10446
10489
  return Number.isFinite(time) && time > 0 && time < 864e13 ? Math.trunc(time < 0xe8d4a51000 ? time * 1e3 : time) : 0;
10447
10490
  };
10448
10491
  const appendItems = async (values, depth) => {
@@ -10450,7 +10493,7 @@ var ChatService = class {
10450
10493
  const sorted = values.slice(0, 100).map((value, index) => ({
10451
10494
  value,
10452
10495
  index,
10453
- order: numberValue$15(objectValue$1(value).item_order)
10496
+ order: numberValue$16(objectValue$1(value).item_order)
10454
10497
  })).sort((left, right) => (left.order || left.index) - (right.order || right.index));
10455
10498
  for (const entry of sorted) {
10456
10499
  if (projectedItems.length >= 100) {
@@ -10460,26 +10503,26 @@ var ChatService = class {
10460
10503
  const item = objectValue$1(entry.value);
10461
10504
  const nestedForward = objectValue$1(item.forward_records ?? item.forwardRecords);
10462
10505
  if (depth < 4 && stringValue$1(nestedForward.render_kind ?? nestedForward.renderKind).trim() === "forward_records") {
10463
- await appendItems(listValue$15(nestedForward.items), depth + 1);
10506
+ await appendItems(listValue$16(nestedForward.items), depth + 1);
10464
10507
  continue;
10465
10508
  }
10466
10509
  if (depth >= 4 && Object.keys(nestedForward).length > 0) truncated = true;
10467
- const senderUserId = numberValue$15(item.source_sender_user_id ?? item.sourceSenderUserId ?? item.owner_id ?? item.ownerId);
10510
+ const senderUserId = numberValue$16(item.source_sender_user_id ?? item.sourceSenderUserId ?? item.owner_id ?? item.ownerId);
10468
10511
  const senderName = stringValue$1(item.owner_name ?? item.ownerName ?? item.source_display_name ?? item.sourceDisplayName).trim() || "Arkme用户";
10469
10512
  const textContent = stringValue$1(item.text ?? item.text_preview ?? item.textPreview).trim();
10470
10513
  const title = stringValue$1(item.title).trim();
10471
10514
  const rawType = stringValue$1(item.source_type ?? item.sourceType ?? payload.source_type ?? payload.sourceType);
10472
10515
  const sourceType = rawType === "record" || rawType === "chat_record" || rawType === "long_recording_segments" || rawType === "agent" || rawType === "ai_letter" ? rawType : "unknown";
10473
- const files = listValue$15(item.files);
10516
+ const files = listValue$16(item.files);
10474
10517
  const contentBlocks = this.media.forwardContentBlocks(files, viewerUserId);
10475
10518
  const call = objectValue$1(item.call_record_snapshot ?? item.callRecordSnapshot);
10476
- const recordingSegments = listValue$15(item.long_recording_segments ?? item.longRecordingSegments);
10477
- const rawSegments = recordingSegments.length > 0 ? recordingSegments : listValue$15(call.transcript_segments ?? call.transcriptSegments);
10519
+ const recordingSegments = listValue$16(item.long_recording_segments ?? item.longRecordingSegments);
10520
+ const rawSegments = recordingSegments.length > 0 ? recordingSegments : listValue$16(call.transcript_segments ?? call.transcriptSegments);
10478
10521
  const segmentValues = rawSegments.slice(0, Math.min(500, remainingSegments));
10479
10522
  remainingSegments -= segmentValues.length;
10480
10523
  const segments = segmentValues.map((value) => {
10481
10524
  const segment = objectValue$1(value);
10482
- const offset = (value) => Math.max(0, Math.trunc(numberValue$15(value)));
10525
+ const offset = (value) => Math.max(0, Math.trunc(numberValue$16(value)));
10483
10526
  const startMillis = offset(segment.start_millis ?? segment.startMillis ?? segment.start_ms ?? segment.startMs);
10484
10527
  const endMillis = Math.max(startMillis, offset(segment.end_millis ?? segment.endMillis ?? segment.end_ms ?? segment.endMs));
10485
10528
  const speakerNumber = offset(segment.speaker_number ?? segment.speakerNumber);
@@ -10501,10 +10544,10 @@ var ChatService = class {
10501
10544
  ...audioUrl !== "" && segmentMedia.length === 0 ? { mediaUnavailable: true } : {}
10502
10545
  };
10503
10546
  });
10504
- const imageCount = Math.max(0, Math.trunc(numberValue$15(item.image_count ?? item.imageCount)));
10505
- const voiceCount = Math.max(0, Math.trunc(numberValue$15(item.voice_count ?? item.voiceCount)));
10506
- const fileCount = Math.max(0, Math.trunc(numberValue$15(item.file_count ?? item.fileCount)));
10507
- const fileName = listValue$15(item.file_names ?? item.fileNames).map((value) => stringValue$1(value).trim()).find((value) => value !== "");
10547
+ const imageCount = Math.max(0, Math.trunc(numberValue$16(item.image_count ?? item.imageCount)));
10548
+ const voiceCount = Math.max(0, Math.trunc(numberValue$16(item.voice_count ?? item.voiceCount)));
10549
+ const fileCount = Math.max(0, Math.trunc(numberValue$16(item.file_count ?? item.fileCount)));
10550
+ const fileName = listValue$16(item.file_names ?? item.fileNames).map((value) => stringValue$1(value).trim()).find((value) => value !== "");
10508
10551
  const contentLabel = imageCount > 0 ? imageCount > 1 ? `[${String(imageCount)}张图片]` : "[图片]" : voiceCount > 0 ? "[语音]" : fileCount > 0 ? fileName === void 0 ? "[文件]" : `[文件] ${fileName}` : stringValue$1(item.availability).trim() !== "" ? "原快记暂不可查看" : void 0;
10509
10552
  projectedItems.push({
10510
10553
  senderName,
@@ -10521,8 +10564,8 @@ var ChatService = class {
10521
10564
  });
10522
10565
  }
10523
10566
  };
10524
- await appendItems(listValue$15(payload.items), 0);
10525
- const summaryLines = listValue$15(payload.summary_lines ?? payload.summaryLines).map((value) => stringValue$1(value).trim()).filter((value) => value !== "");
10567
+ await appendItems(listValue$16(payload.items), 0);
10568
+ const summaryLines = listValue$16(payload.summary_lines ?? payload.summaryLines).map((value) => stringValue$1(value).trim()).filter((value) => value !== "");
10526
10569
  const createdAtMillis = epochMillis(payload.created_at ?? payload.createdAt) || epochMillis(fallbackCreatedAtMillis);
10527
10570
  return {
10528
10571
  title: stringValue$1(payload.title).trim() || "转发快记",
@@ -10565,9 +10608,9 @@ var ChatService = class {
10565
10608
  }
10566
10609
  }
10567
10610
  sharedRecordingParticipantsFromPayload(payload) {
10568
- const participants = listValue$15(payload.participants);
10569
- const mobileParticipants = listValue$15(payload.participant_ls ?? payload.participantLs);
10570
- const speakers = listValue$15(payload.speaker_ls ?? payload.speakerLs ?? payload.speakers);
10611
+ const participants = listValue$16(payload.participants);
10612
+ const mobileParticipants = listValue$16(payload.participant_ls ?? payload.participantLs);
10613
+ const speakers = listValue$16(payload.speaker_ls ?? payload.speakerLs ?? payload.speakers);
10571
10614
  return (participants.length > 0 ? participants : mobileParticipants.length > 0 ? mobileParticipants : speakers).flatMap((value) => {
10572
10615
  if (typeof value === "string") {
10573
10616
  const displayName = value.trim();
@@ -10598,7 +10641,7 @@ var ChatService = class {
10598
10641
  "asr_ls",
10599
10642
  "asrLs"
10600
10643
  ]) {
10601
- const lines = listValue$15(payload[key]).flatMap((value) => {
10644
+ const lines = listValue$16(payload[key]).flatMap((value) => {
10602
10645
  if (typeof value === "string") {
10603
10646
  const line = value.trim();
10604
10647
  return line === "" ? [] : [line];
@@ -10785,7 +10828,7 @@ var ChatService = class {
10785
10828
  recordRaw.captureContext,
10786
10829
  recordRaw.background_sound,
10787
10830
  recordRaw.backgroundSound
10788
- ].some((value) => Object.keys(parsedObject(value)).length > 0 || listValue$15(value).length > 0);
10831
+ ].some((value) => Object.keys(parsedObject(value)).length > 0 || listValue$16(value).length > 0);
10789
10832
  if (!Object.keys(recordCore).some((key) => ![
10790
10833
  "record_uid",
10791
10834
  "recordUid",
@@ -10880,34 +10923,26 @@ var ChatService = class {
10880
10923
  }, session, options.signal);
10881
10924
  }
10882
10925
  async rawChatMembers(chatSessionUid, activeOnly, session, signal) {
10883
- return listValue$15((await this.runtime.authenticatedChatPost("/api/v1/chats/members/list", {
10926
+ return listValue$16((await this.runtime.authenticatedChatPost("/api/v1/chats/members/list", {
10884
10927
  chat_session_uid: chatSessionUid,
10885
10928
  active_only: activeOnly
10886
10929
  }, session, signal)).items).map(objectValue$1);
10887
10930
  }
10888
- async projectChatMembers(chatSessionUid, rawItems, session, includeViewerLabels, signal) {
10889
- const userIds = rawItems.map((item) => Math.trunc(numberValue$15(item.user_id))).filter((userId) => Number.isSafeInteger(userId) && userId > 0);
10890
- const [profiles, viewerLabels] = await Promise.all([this.profile.publicProfileSummariesByUserIds(userIds, session, signal).catch(() => /* @__PURE__ */ new Map()), includeViewerLabels ? this.source.privateDisplayNamesByUserIds(userIds, { ...signal === void 0 ? {} : { signal } }).catch(() => /* @__PURE__ */ new Map()) : Promise.resolve(/* @__PURE__ */ new Map())]);
10931
+ async projectChatMembers(chatSessionUid, rawItems, session, options) {
10932
+ const userIds = rawItems.map((item) => Math.trunc(numberValue$16(item.user_id))).filter((userId) => Number.isSafeInteger(userId) && userId > 0);
10933
+ const [profiles, viewerLabels] = await Promise.all([this.profile.publicProfileSummariesByUserIds(userIds, session, options.signal).catch(() => /* @__PURE__ */ new Map()), options.includeViewerLabels ? this.source.privateDisplayNamesByUserIds(userIds, { ...options.signal === void 0 ? {} : { signal: options.signal } }).catch(() => /* @__PURE__ */ new Map()) : Promise.resolve(/* @__PURE__ */ new Map())]);
10891
10934
  const members = [];
10892
10935
  for (const item of rawItems) {
10893
- const userId = Math.trunc(numberValue$15(item.user_id));
10936
+ const userId = Math.trunc(numberValue$16(item.user_id));
10894
10937
  if (!Number.isSafeInteger(userId) || userId <= 0) continue;
10895
10938
  const profile = profiles.get(userId);
10896
- const { displayName, memberName, secondaryName } = resolveChatMemberDisplayNames({
10897
- userId,
10898
- remarkCandidates: [],
10899
- memberNameCandidates: [item.display_name_snapshot],
10900
- userNameCandidates: [
10901
- viewerLabels.get(userId),
10902
- item.remark,
10903
- profile?.displayName
10904
- ]
10905
- });
10939
+ const { displayName, memberName, secondaryName } = projectChatMemberDisplayNames(item, userId, viewerLabels.get(userId), profile?.displayName);
10906
10940
  const role = chatMemberRole$1(item.role);
10907
10941
  const status = chatMemberStatus$1(item.status);
10908
10942
  const extra = parsedObject(item.extra);
10909
10943
  members.push({
10910
10944
  memberRef: await this.sealChatMemberRef(session.userId, chatSessionUid, userId),
10945
+ ...options.includeHumanMentionRefs && status === "active" && userId !== session.userId ? { mentionRef: await this.sealChatHumanMentionRef(session.userId, chatSessionUid, userId, displayName) } : {},
10911
10946
  displayName,
10912
10947
  ...memberName === "" ? {} : { memberName },
10913
10948
  ...secondaryName === "" ? {} : { secondaryName },
@@ -10916,9 +10951,9 @@ var ChatService = class {
10916
10951
  status,
10917
10952
  isSelf: userId === session.userId,
10918
10953
  isOwner: role === "owner",
10919
- joinedAtMillis: Math.max(0, Math.trunc(numberValue$15(item.join_at))),
10920
- recordCount: Math.max(0, Math.trunc(numberValue$15(extra.record_count))),
10921
- mentionCount: Math.max(0, Math.trunc(numberValue$15(extra.mention_count)))
10954
+ joinedAtMillis: Math.max(0, Math.trunc(numberValue$16(item.join_at))),
10955
+ recordCount: Math.max(0, Math.trunc(numberValue$16(extra.record_count))),
10956
+ mentionCount: Math.max(0, Math.trunc(numberValue$16(extra.mention_count)))
10922
10957
  });
10923
10958
  }
10924
10959
  const roleRank = (role) => role === "owner" ? 0 : role === "admin" ? 1 : role === "member" ? 2 : 3;
@@ -10934,15 +10969,15 @@ var ChatService = class {
10934
10969
  const relation = objectValue$1(item.relation);
10935
10970
  const record = objectValue$1(item.record);
10936
10971
  const payload = objectValue$1(record.payload);
10937
- const recordStatus = numberValue$15(record.status);
10972
+ const recordStatus = numberValue$16(record.status);
10938
10973
  if (recordStatus !== 1) continue;
10939
10974
  const uid = stringValue$1(relation.record_uid ?? payload.record_uid).trim();
10940
10975
  if (uid === "") continue;
10941
10976
  const relationUid = stringValue$1(relation.rel_uid).trim();
10942
- const senderUserId = Math.trunc(numberValue$15(relation.sender_user_id));
10977
+ const senderUserId = Math.trunc(numberValue$16(relation.sender_user_id));
10943
10978
  const recordOwnerUserId = chatRecordOwnerUserId(relation, record, payload, senderUserId);
10944
10979
  const aiPolish = this.aiPolish.timelineAiPolish(record, payload);
10945
- const sendAtMillis = numberValue$15(relation.attach_at ?? payload.send_at);
10980
+ const sendAtMillis = numberValue$16(relation.attach_at ?? payload.send_at);
10946
10981
  const forwardRecords = await this.chatForwardRecordsPreview(item, session.userId, sendAtMillis);
10947
10982
  const sharedRecording = this.withSharedRecordingDetailRef(this.chatSharedRecordingPreview(item), {
10948
10983
  viewerUserId: session.userId,
@@ -10980,9 +11015,9 @@ var ChatService = class {
10980
11015
  title: stringValue$1(payload.title),
10981
11016
  textContent: stringValue$1(payload.text_content),
10982
11017
  sendAtMillis,
10983
- sourceSequence: numberValue$15(relation.seq),
10984
- templateKind: numberValue$15(payload.template_kind),
10985
- displayKind: numberValue$15(payload.display_kind),
11018
+ sourceSequence: numberValue$16(relation.seq),
11019
+ templateKind: numberValue$16(payload.template_kind),
11020
+ displayKind: numberValue$16(payload.display_kind),
10986
11021
  contentBlocks
10987
11022
  }), signingKey) },
10988
11023
  ...senderUserId > 0 ? { memberRef: await this.sealChatMemberRef(session.userId, source.ownerRef, senderUserId) } : {},
@@ -10993,13 +11028,13 @@ var ChatService = class {
10993
11028
  title: stringValue$1(payload.title),
10994
11029
  textContent: stringValue$1(payload.text_content),
10995
11030
  status: recordStatus,
10996
- sequence: numberValue$15(relation.seq),
10997
- ...numberValue$15(record.version ?? payload.version) > 0 ? { recordVersion: numberValue$15(record.version ?? payload.version) } : {},
11031
+ sequence: numberValue$16(relation.seq),
11032
+ ...numberValue$16(record.version ?? payload.version) > 0 ? { recordVersion: numberValue$16(record.version ?? payload.version) } : {},
10998
11033
  ...aiPolish === void 0 ? {} : { aiPolish },
10999
- templateKind: numberValue$15(payload.template_kind),
11000
- displayKind: numberValue$15(payload.display_kind),
11001
- version: numberValue$15(payload.version ?? record.version),
11002
- updateAtMillis: numberValue$15(payload.update_at ?? record.update_at),
11034
+ templateKind: numberValue$16(payload.template_kind),
11035
+ displayKind: numberValue$16(payload.display_kind),
11036
+ version: numberValue$16(payload.version ?? record.version),
11037
+ updateAtMillis: numberValue$16(payload.update_at ?? record.update_at),
11003
11038
  ...recordDurationMillis === void 0 ? {} : { recordDurationMillis },
11004
11039
  ...editDurationMillis === void 0 ? {} : { editDurationMillis },
11005
11040
  ...captureContext === void 0 ? {} : { captureContext },
@@ -11025,11 +11060,12 @@ var ChatService = class {
11025
11060
  chatSessionUid,
11026
11061
  targetUserId
11027
11062
  });
11028
- return `arkme-chat-member-v1.${encoded}.${createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(encoded).digest("base64url")}`;
11063
+ const signature = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(encoded).digest("base64url");
11064
+ return `${CHAT_MEMBER_REF_PREFIX}.${encoded}.${signature}`;
11029
11065
  }
11030
11066
  async openChatMemberRef(memberRef, expectedViewerUserId, expectedChatSessionUid) {
11031
11067
  const parts = memberRef.trim().split(".");
11032
- if (parts.length !== 3 || parts[0] !== "arkme-chat-member-v1") throw new ArkmePluginError("chat-member-ref-invalid", "聊天成员引用无效", false);
11068
+ if (parts.length !== 3 || parts[0] !== CHAT_MEMBER_REF_PREFIX) throw new ArkmePluginError("chat-member-ref-invalid", "聊天成员引用无效", false);
11033
11069
  const encoded = parts[1] ?? "";
11034
11070
  const supplied = Buffer.from(parts[2] ?? "", "base64url");
11035
11071
  const expected = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(encoded).digest();
@@ -11042,13 +11078,49 @@ var ChatService = class {
11042
11078
  }
11043
11079
  const result = {
11044
11080
  version: 1,
11045
- viewerUserId: Math.trunc(numberValue$15(raw.viewerUserId)),
11081
+ viewerUserId: Math.trunc(numberValue$16(raw.viewerUserId)),
11046
11082
  chatSessionUid: stringValue$1(raw.chatSessionUid).trim(),
11047
- targetUserId: Math.trunc(numberValue$15(raw.targetUserId))
11083
+ targetUserId: Math.trunc(numberValue$16(raw.targetUserId))
11048
11084
  };
11049
11085
  if (raw.version !== 1 || result.viewerUserId !== expectedViewerUserId || result.chatSessionUid !== expectedChatSessionUid || !Number.isSafeInteger(result.targetUserId) || result.targetUserId <= 0) throw new ArkmePluginError("chat-member-ref-invalid", "聊天成员引用与当前账号或会话不匹配", false, 403);
11050
11086
  return result;
11051
11087
  }
11088
+ async sealChatHumanMentionRef(viewerUserId, chatSessionUid, targetUserId, displayNameSnapshot) {
11089
+ const normalizedDisplayNameSnapshot = normalizedJoinDisplayName(displayNameSnapshot);
11090
+ if (normalizedDisplayNameSnapshot === "") throw new ArkmePluginError("chat-mention-ref-invalid", "真人 mention 展示名无效", false);
11091
+ const encoded = encodeOpaqueJson$6({
11092
+ version: 1,
11093
+ viewerUserId,
11094
+ chatSessionUid,
11095
+ targetUserId,
11096
+ displayNameSnapshot: normalizedDisplayNameSnapshot
11097
+ });
11098
+ const signature = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(`${CHAT_HUMAN_MENTION_REF_PREFIX}.${encoded}`).digest("base64url");
11099
+ return `${CHAT_HUMAN_MENTION_REF_PREFIX}.${encoded}.${signature}`;
11100
+ }
11101
+ async openChatHumanMentionRef(mentionRef, expectedViewerUserId, expectedChatSessionUid) {
11102
+ const parts = mentionRef.trim().split(".");
11103
+ if (parts.length !== 3 || parts[0] !== CHAT_HUMAN_MENTION_REF_PREFIX) throw new ArkmePluginError("chat-mention-ref-invalid", "真人 mention 引用无效", false);
11104
+ const encoded = parts[1] ?? "";
11105
+ const supplied = Buffer.from(parts[2] ?? "", "base64url");
11106
+ const expected = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(`${CHAT_HUMAN_MENTION_REF_PREFIX}.${encoded}`).digest();
11107
+ if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) throw new ArkmePluginError("chat-mention-ref-invalid", "真人 mention 引用无效", false);
11108
+ let raw;
11109
+ try {
11110
+ raw = objectValue$1(decodeOpaqueJson$5(encoded));
11111
+ } catch (error) {
11112
+ throw new ArkmePluginError("chat-mention-ref-invalid", "真人 mention 引用无效", false, 400, { cause: error });
11113
+ }
11114
+ const result = {
11115
+ version: 1,
11116
+ viewerUserId: Math.trunc(numberValue$16(raw.viewerUserId)),
11117
+ chatSessionUid: stringValue$1(raw.chatSessionUid).trim(),
11118
+ targetUserId: Math.trunc(numberValue$16(raw.targetUserId)),
11119
+ displayNameSnapshot: normalizedJoinDisplayName(raw.displayNameSnapshot)
11120
+ };
11121
+ if (raw.version !== 1 || result.viewerUserId !== expectedViewerUserId || result.chatSessionUid !== expectedChatSessionUid || !Number.isSafeInteger(result.targetUserId) || result.targetUserId <= 0 || result.displayNameSnapshot === "") throw new ArkmePluginError("chat-mention-ref-invalid", "真人 mention 引用与当前账号或会话不匹配", false, 403);
11122
+ return result;
11123
+ }
11052
11124
  messageActionRefPayload(input) {
11053
11125
  const contentBlocks = input.contentBlocks ?? [];
11054
11126
  const fileNames = contentBlocks.filter((block) => block.kind === "file" && block.fileName.trim() !== "").slice(0, 5).map((block) => block.fileName.trim().slice(0, 256));
@@ -11066,7 +11138,7 @@ var ChatService = class {
11066
11138
  title: input.title.slice(0, 500),
11067
11139
  textContent: input.textContent.slice(0, this.runtime.config.maxTextLength),
11068
11140
  sendAtMillis: Math.trunc(input.sendAtMillis),
11069
- sourceSequence: Math.max(0, Math.trunc(numberValue$15(input.sourceSequence))),
11141
+ sourceSequence: Math.max(0, Math.trunc(numberValue$16(input.sourceSequence))),
11070
11142
  templateKind: Math.trunc(input.templateKind),
11071
11143
  displayKind: Math.trunc(input.displayKind),
11072
11144
  imageCount: contentBlocks.filter((block) => block.kind === "image").length,
@@ -11129,27 +11201,27 @@ var ChatService = class {
11129
11201
  } catch (error) {
11130
11202
  throw new ArkmePluginError("message-action-ref-invalid", "消息操作引用无效,请刷新后重试", false, 400, { cause: error });
11131
11203
  }
11132
- const fileNames = listValue$15(parsed.fileNames).map((value) => stringValue$1(value).trim()).filter((value) => value !== "").slice(0, 5);
11204
+ const fileNames = listValue$16(parsed.fileNames).map((value) => stringValue$1(value).trim()).filter((value) => value !== "").slice(0, 5);
11133
11205
  const result = {
11134
11206
  version: 1,
11135
11207
  sourceKind: stringValue$1(parsed.sourceKind).trim() === "record" ? "record" : "chat_relation",
11136
- userId: Math.trunc(numberValue$15(parsed.userId)),
11208
+ userId: Math.trunc(numberValue$16(parsed.userId)),
11137
11209
  sourceOwnerRef: stringValue$1(parsed.sourceOwnerRef).trim(),
11138
11210
  chatSessionUid: stringValue$1(parsed.chatSessionUid).trim(),
11139
11211
  relationUid: stringValue$1(parsed.relationUid).trim(),
11140
- recordOwnerUserId: Math.trunc(numberValue$15(parsed.recordOwnerUserId)),
11212
+ recordOwnerUserId: Math.trunc(numberValue$16(parsed.recordOwnerUserId)),
11141
11213
  recordUid: stringValue$1(parsed.recordUid).trim(),
11142
- senderUserId: Math.trunc(numberValue$15(parsed.senderUserId)),
11214
+ senderUserId: Math.trunc(numberValue$16(parsed.senderUserId)),
11143
11215
  senderName: stringValue$1(parsed.senderName).trim() || "Arkme用户",
11144
11216
  title: stringValue$1(parsed.title),
11145
11217
  textContent: stringValue$1(parsed.textContent),
11146
- sendAtMillis: Math.trunc(numberValue$15(parsed.sendAtMillis)),
11147
- sourceSequence: Math.max(0, Math.trunc(numberValue$15(parsed.sourceSequence))),
11148
- templateKind: Math.trunc(numberValue$15(parsed.templateKind)),
11149
- displayKind: Math.trunc(numberValue$15(parsed.displayKind)),
11150
- imageCount: Math.max(0, Math.trunc(numberValue$15(parsed.imageCount))),
11151
- voiceCount: Math.max(0, Math.trunc(numberValue$15(parsed.voiceCount))),
11152
- fileCount: Math.max(0, Math.trunc(numberValue$15(parsed.fileCount))),
11218
+ sendAtMillis: Math.trunc(numberValue$16(parsed.sendAtMillis)),
11219
+ sourceSequence: Math.max(0, Math.trunc(numberValue$16(parsed.sourceSequence))),
11220
+ templateKind: Math.trunc(numberValue$16(parsed.templateKind)),
11221
+ displayKind: Math.trunc(numberValue$16(parsed.displayKind)),
11222
+ imageCount: Math.max(0, Math.trunc(numberValue$16(parsed.imageCount))),
11223
+ voiceCount: Math.max(0, Math.trunc(numberValue$16(parsed.voiceCount))),
11224
+ fileCount: Math.max(0, Math.trunc(numberValue$16(parsed.fileCount))),
11153
11225
  fileNames
11154
11226
  };
11155
11227
  const matchesChatSource = result.sourceKind === "chat_relation" && (source.kind === "private_chat" || source.kind === "group_chat") && result.sourceOwnerRef === source.ownerRef && result.chatSessionUid === source.ownerRef && result.relationUid !== "" && result.recordUid !== "";
@@ -11243,7 +11315,7 @@ var ChatService = class {
11243
11315
  }
11244
11316
  const result = {
11245
11317
  version: 1,
11246
- userId: numberValue$15(parsed.userId),
11318
+ userId: numberValue$16(parsed.userId),
11247
11319
  chatSessionUid: stringValue$1(parsed.chatSessionUid).trim(),
11248
11320
  relationUid: stringValue$1(parsed.relationUid).trim()
11249
11321
  };
@@ -11256,7 +11328,7 @@ var ChatService = class {
11256
11328
  const CONTACT_REF_TTL_MS = 10 * 6e4;
11257
11329
  const MAX_CONTACT_REFS = 512;
11258
11330
  const CONTACT_REF_PATTERN = /^arkme-contact-v1\.[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
11259
- function numberValue$14(value) {
11331
+ function numberValue$15(value) {
11260
11332
  const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
11261
11333
  return Number.isFinite(parsed) ? parsed : 0;
11262
11334
  }
@@ -11390,7 +11462,7 @@ var ContactService = class {
11390
11462
  key: `contact:search:${normalized.identifierType}:${normalized.identifier}`,
11391
11463
  bypassCache: true
11392
11464
  });
11393
- const targetUserId = Math.trunc(numberValue$14(data.user_id));
11465
+ const targetUserId = Math.trunc(numberValue$15(data.user_id));
11394
11466
  const registered = booleanValue$7(data.is_registered) && targetUserId > 0;
11395
11467
  const isSelf = booleanValue$7(data.is_self);
11396
11468
  const canAdd = booleanValue$7(data.can_add) && !isSelf && (normalized.kind === "phone" || registered);
@@ -39960,7 +40032,7 @@ const CONTACT_DIRECTORY_PAGE_LIMIT = 50;
39960
40032
  const CONTACT_DIRECTORY_MAX_SOURCE_PAGES = 20;
39961
40033
  const CONTACT_DIRECTORY_REF_PATTERN = /^arkme-directory-contact-v1\.[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
39962
40034
  const OFFSET_CURSOR_PATTERN = /^arkme-directory-offset-v1\.(bots|teams|contacts)\.([0-9]+)\.([A-Za-z0-9_-]+)$/;
39963
- function numberValue$13(value) {
40035
+ function numberValue$14(value) {
39964
40036
  if (typeof value === "number" && Number.isFinite(value)) return Math.trunc(value);
39965
40037
  if (typeof value === "string" && value.trim() !== "") {
39966
40038
  const parsed = Number(value);
@@ -39968,7 +40040,7 @@ function numberValue$13(value) {
39968
40040
  }
39969
40041
  return 0;
39970
40042
  }
39971
- function listValue$14(value) {
40043
+ function listValue$15(value) {
39972
40044
  return Array.isArray(value) ? value : [];
39973
40045
  }
39974
40046
  function boundedLimit$1(value, fallback = 30) {
@@ -40026,7 +40098,7 @@ var ContactDirectoryService = class {
40026
40098
  key: "directory:teams:count",
40027
40099
  failureCooldownMs: 2e3
40028
40100
  });
40029
- total = Math.max(0, numberValue$13(data.total ?? data.total_count));
40101
+ total = Math.max(0, numberValue$14(data.total ?? data.total_count));
40030
40102
  break;
40031
40103
  }
40032
40104
  case "contacts":
@@ -40126,9 +40198,9 @@ var ContactDirectoryService = class {
40126
40198
  bypassCache: options.refresh === true
40127
40199
  });
40128
40200
  const projected = [];
40129
- for (const value of listValue$14(data.teams ?? data.items ?? data.list)) {
40201
+ for (const value of listValue$15(data.teams ?? data.items ?? data.list)) {
40130
40202
  const raw = objectValue$1(value);
40131
- const teamId = numberValue$13(raw.team_id ?? raw.teamId ?? raw.id);
40203
+ const teamId = numberValue$14(raw.team_id ?? raw.teamId ?? raw.id);
40132
40204
  const displayName = stringValue$1(raw.name ?? raw.team_name ?? raw.teamName).trim();
40133
40205
  if (teamId <= 0 || displayName === "") continue;
40134
40206
  const publicId = stringValue$1(raw.jotmo_id ?? raw.jotmoId).trim();
@@ -40207,10 +40279,10 @@ var ContactDirectoryService = class {
40207
40279
  failureCooldownMs: 2e3,
40208
40280
  bypassCache: options.refresh === true
40209
40281
  });
40210
- const rawItems = listValue$14(data.items);
40282
+ const rawItems = listValue$15(data.items);
40211
40283
  for (const value of rawItems) {
40212
40284
  const raw = objectValue$1(value);
40213
- const targetUserId = numberValue$13(raw.user_id);
40285
+ const targetUserId = numberValue$14(raw.user_id);
40214
40286
  if (targetUserId <= 0 || !Number.isSafeInteger(targetUserId) || descriptorsByUserId.has(targetUserId)) continue;
40215
40287
  const chatSessionUid = stringValue$1(raw.chat_session_uid).trim();
40216
40288
  descriptorsByUserId.set(targetUserId, {
@@ -40241,12 +40313,12 @@ var ContactDirectoryService = class {
40241
40313
  failureCooldownMs: 2e3,
40242
40314
  bypassCache: options.refresh === true
40243
40315
  });
40244
- for (const value of listValue$14(data.items)) {
40316
+ for (const value of listValue$15(data.items)) {
40245
40317
  const bundle = objectValue$1(value);
40246
40318
  const chatSession = objectValue$1(bundle.session);
40247
- if (numberValue$13(chatSession.session_kind) !== 1 || listValue$14(bundle.bot_participants).length > 0) continue;
40319
+ if (numberValue$14(chatSession.session_kind) !== 1 || listValue$15(bundle.bot_participants).length > 0) continue;
40248
40320
  const counterpart = objectValue$1(bundle.private_counterpart);
40249
- const targetUserId = numberValue$13(counterpart.user_id);
40321
+ const targetUserId = numberValue$14(counterpart.user_id);
40250
40322
  if (targetUserId <= 0 || !Number.isSafeInteger(targetUserId)) continue;
40251
40323
  const chatSessionUid = stringValue$1(chatSession.chat_session_uid).trim();
40252
40324
  const existing = descriptorsByUserId.get(targetUserId);
@@ -40328,7 +40400,7 @@ var ContactDirectoryService = class {
40328
40400
  if (normalized === "") return 0;
40329
40401
  const match = OFFSET_CURSOR_PATTERN.exec(normalized);
40330
40402
  if (match === null || match[1] !== section) throw new ArkmePluginError("directory-cursor-invalid", "联系人目录分页游标无效", false);
40331
- const offset = numberValue$13(match[2]);
40403
+ const offset = numberValue$14(match[2]);
40332
40404
  if (offset < 0 || !Number.isSafeInteger(offset)) throw new ArkmePluginError("directory-cursor-invalid", "联系人目录分页游标无效", false);
40333
40405
  const payload = `${section}:${String(viewerUserId)}:${String(offset)}`;
40334
40406
  const supplied = Buffer.from(match[3] ?? "", "base64url");
@@ -40339,13 +40411,13 @@ var ContactDirectoryService = class {
40339
40411
  };
40340
40412
  //#endregion
40341
40413
  //#region src/services/community-service.ts
40342
- function numberValue$12(value) {
40414
+ function numberValue$13(value) {
40343
40415
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
40344
40416
  }
40345
40417
  function booleanValue$6(value) {
40346
40418
  return value === true;
40347
40419
  }
40348
- function listValue$13(value) {
40420
+ function listValue$14(value) {
40349
40421
  return Array.isArray(value) ? value : [];
40350
40422
  }
40351
40423
  var CommunityService = class {
@@ -40364,8 +40436,8 @@ var CommunityService = class {
40364
40436
  const visible = booleanValue$6(data.visible);
40365
40437
  const groupTitle = stringValue$1(data.group_title).trim();
40366
40438
  const snapshot = objectValue$1(data.group_avatar_snapshot);
40367
- const memberCount = Math.max(0, Math.trunc(numberValue$12(snapshot.member_count)));
40368
- const memberIds = listValue$13(snapshot.members).map((member) => numberValue$12(objectValue$1(member).user_id)).filter((userId) => Number.isSafeInteger(userId) && userId > 0).slice(0, 5);
40439
+ const memberCount = Math.max(0, Math.trunc(numberValue$13(snapshot.member_count)));
40440
+ const memberIds = listValue$14(snapshot.members).map((member) => numberValue$13(objectValue$1(member).user_id)).filter((userId) => Number.isSafeInteger(userId) && userId > 0).slice(0, 5);
40369
40441
  let avatarRefs = [];
40370
40442
  let groupAvatar;
40371
40443
  if (visible && status === "ready" && memberIds.length > 0) try {
@@ -40373,7 +40445,7 @@ var CommunityService = class {
40373
40445
  groupAvatar = await this.source.groupAvatarPresentation({
40374
40446
  memberCount,
40375
40447
  strategy: stringValue$1(snapshot.strategy).trim(),
40376
- computedAtMillis: numberValue$12(snapshot.computed_at),
40448
+ computedAtMillis: numberValue$13(snapshot.computed_at),
40377
40449
  memberIds
40378
40450
  }, profiles, session.userId);
40379
40451
  avatarRefs = groupAvatar.slots.flatMap((slot) => slot.avatarRef === void 0 ? [] : [slot.avatarRef]);
@@ -40396,7 +40468,7 @@ var CommunityService = class {
40396
40468
  let groupTitle = stringValue$1(data.group_title).trim();
40397
40469
  if (groupTitle === "") try {
40398
40470
  const chatSession = objectValue$1((await this.runtime.authenticatedChatPost("/api/v1/chats/detail", { chat_session_uid: chatSessionUid }, session, signal)).session);
40399
- if (stringValue$1(chatSession.chat_session_uid).trim() === chatSessionUid && numberValue$12(chatSession.session_kind) === 2) groupTitle = stringValue$1(chatSession.title).trim();
40471
+ if (stringValue$1(chatSession.chat_session_uid).trim() === chatSessionUid && numberValue$13(chatSession.session_kind) === 2) groupTitle = stringValue$1(chatSession.title).trim();
40400
40472
  } catch {}
40401
40473
  if (groupTitle === "") groupTitle = "DSH 内测群";
40402
40474
  this.source.invalidateGroupAvatar(session.userId, chatSessionUid);
@@ -40653,19 +40725,19 @@ var ExtensionReviewService = class {
40653
40725
  };
40654
40726
  //#endregion
40655
40727
  //#region src/services/group-ai-polish-service.ts
40656
- function numberValue$11(value) {
40728
+ function numberValue$12(value) {
40657
40729
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
40658
40730
  }
40659
40731
  function booleanValue$5(value) {
40660
40732
  return value === true;
40661
40733
  }
40662
- function listValue$12(value) {
40734
+ function listValue$13(value) {
40663
40735
  return Array.isArray(value) ? value : [];
40664
40736
  }
40665
40737
  function aiPolishThreadMessages(value) {
40666
40738
  let totalCharacters = 0;
40667
40739
  const messages = [];
40668
- for (const raw of listValue$12(value).slice(-40)) {
40740
+ for (const raw of listValue$13(value).slice(-40)) {
40669
40741
  const item = objectValue$1(raw);
40670
40742
  const id = stringValue$1(item.id).trim();
40671
40743
  const role = stringValue$1(item.role).trim();
@@ -41130,23 +41202,23 @@ var GroupAiPolishService = class {
41130
41202
  });
41131
41203
  const config = objectValue$1(data.config ?? data.setting ?? data.settings ?? data);
41132
41204
  const activeRuleUid = stringValue$1(config.active_rule_uid).trim();
41133
- const rules = listValue$12(data.rules).map((raw) => objectValue$1(raw)).map((rule) => {
41205
+ const rules = listValue$13(data.rules).map((raw) => objectValue$1(raw)).map((rule) => {
41134
41206
  const extra = objectValue$1(rule.extra);
41135
41207
  return {
41136
41208
  ruleUid: stringValue$1(rule.rule_uid).trim(),
41137
41209
  name: stringValue$1(rule.name).trim() || "未命名规则",
41138
41210
  ruleText: stringValue$1(rule.rule_text).trim(),
41139
- ruleVersion: numberValue$11(rule.rule_version),
41211
+ ruleVersion: numberValue$12(rule.rule_version),
41140
41212
  threadMessages: aiPolishThreadMessages(extra.rule_thread_messages)
41141
41213
  };
41142
41214
  }).filter((rule) => rule.ruleUid !== "" && rule.ruleText !== "");
41143
41215
  return {
41144
41216
  enabled: booleanValue$5(config.enabled ?? config.is_enabled),
41145
41217
  canManage: booleanValue$5(data.can_manage),
41146
- viewerRole: numberValue$11(data.viewer_role),
41218
+ viewerRole: numberValue$12(data.viewer_role),
41147
41219
  activeRuleUid,
41148
41220
  activeRuleName: rules.find((rule) => rule.ruleUid === activeRuleUid)?.name ?? "",
41149
- updatedAtMillis: numberValue$11(config.update_at),
41221
+ updatedAtMillis: numberValue$12(config.update_at),
41150
41222
  rules
41151
41223
  };
41152
41224
  }
@@ -41169,7 +41241,7 @@ var GroupAiPolishService = class {
41169
41241
  };
41170
41242
  }
41171
41243
  async queryGroupAiPolishNotices(chatSessionUid, session, signal) {
41172
- return listValue$12((await this.runtime.authenticatedChatPost("/api/v1/chats/ai-polish/notices/query", {
41244
+ return listValue$13((await this.runtime.authenticatedChatPost("/api/v1/chats/ai-polish/notices/query", {
41173
41245
  chat_session_uid: chatSessionUid,
41174
41246
  limit: 100
41175
41247
  }, session, signal, {
@@ -41178,15 +41250,15 @@ var GroupAiPolishService = class {
41178
41250
  cacheMs: 15e3,
41179
41251
  failureCooldownMs: 5e3
41180
41252
  })).notices).map((raw) => objectValue$1(raw)).map((notice) => {
41181
- const kind = numberValue$11(notice.notice_kind);
41253
+ const kind = numberValue$12(notice.notice_kind);
41182
41254
  const rule = stringValue$1(notice.rule_name).trim() || stringValue$1(notice.rule_text).trim();
41183
41255
  const actor = compactAiPolishActorLabel(notice.actor_display_name_snapshot);
41184
41256
  return {
41185
41257
  noticeUid: stringValue$1(notice.notice_uid).trim(),
41186
41258
  sourceKey: stringValue$1(notice.source_key).trim(),
41187
41259
  message: kind === 1 ? actor === "" ? `AI润色已开启:${rule}` : `${actor}开启了 AI 润色:${rule}` : kind === 2 ? actor === "" ? `AI润色规则已修改:${rule}` : `${actor}修改了 AI 润色规则:${rule}` : "",
41188
- createdAtMillis: numberValue$11(notice.created_at),
41189
- status: numberValue$11(notice.status)
41260
+ createdAtMillis: numberValue$12(notice.created_at),
41261
+ status: numberValue$12(notice.status)
41190
41262
  };
41191
41263
  }).filter((notice) => notice.noticeUid !== "" && notice.message !== "" && notice.createdAtMillis > 0 && (notice.status === 0 || notice.status === 1)).map(({ status: _status, ...notice }) => notice);
41192
41264
  }
@@ -41227,9 +41299,9 @@ var GroupAiPolishService = class {
41227
41299
  aiPolishTextResult(data) {
41228
41300
  return {
41229
41301
  taskUid: stringValue$1(data.task_uid).trim(),
41230
- attempt: numberValue$11(data.attempt),
41231
- state: numberValue$11(data.state),
41232
- action: numberValue$11(data.action),
41302
+ attempt: numberValue$12(data.attempt),
41303
+ state: numberValue$12(data.state),
41304
+ action: numberValue$12(data.action),
41233
41305
  polishedText: stringValue$1(data.polished_text),
41234
41306
  recordUid: stringValue$1(data.record_uid ?? data.recordUid).trim(),
41235
41307
  revisionUid: stringValue$1(data.revision_uid ?? data.revisionUid).trim(),
@@ -41244,10 +41316,10 @@ var GroupAiPolishService = class {
41244
41316
  //#endregion
41245
41317
  //#region src/services/group-service.ts
41246
41318
  const UUID$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
41247
- function numberValue$10(value) {
41319
+ function numberValue$11(value) {
41248
41320
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
41249
41321
  }
41250
- function listValue$11(value) {
41322
+ function listValue$12(value) {
41251
41323
  return Array.isArray(value) ? value : [];
41252
41324
  }
41253
41325
  function encodeOpaqueJson$5(value) {
@@ -41259,7 +41331,7 @@ function decodeOpaqueJson$4(value) {
41259
41331
  function chatMessageDnd(value) {
41260
41332
  if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
41261
41333
  const policy = value;
41262
- return numberValue$10(policy.mute_state) === 2 || numberValue$10(policy.notify_state) === 2;
41334
+ return numberValue$11(policy.mute_state) === 2 || numberValue$11(policy.notify_state) === 2;
41263
41335
  }
41264
41336
  function chatMemberRole(value) {
41265
41337
  if (value === "owner" || value === 1) return "owner";
@@ -41296,8 +41368,8 @@ var GroupService = class {
41296
41368
  title: stringValue$1(preview.title).trim() || source.displayName,
41297
41369
  inviterDisplayName: stringValue$1(preview.creator_display_name).trim() || "Arkme",
41298
41370
  inviteLink,
41299
- expireAtMillis: numberValue$10(preview.expire_at),
41300
- mode: numberValue$10(preview.join_mode) === 2 ? "approval_invite" : "direct_add"
41371
+ expireAtMillis: numberValue$11(preview.expire_at),
41372
+ mode: numberValue$11(preview.join_mode) === 2 ? "approval_invite" : "direct_add"
41301
41373
  };
41302
41374
  }
41303
41375
  async listGroupMemberCandidates(sourceRef, options = {}) {
@@ -41315,35 +41387,35 @@ var GroupService = class {
41315
41387
  this.runtime.authenticatedChatPost("/api/v1/chats/list", { limit: 50 }, session, options.signal),
41316
41388
  this.runtime.authenticatedChatPost("/api/v1/chats/governance/invite-preview", { chat_session_uid: source.ownerRef }, session, options.signal).catch(() => ({}))
41317
41389
  ]);
41318
- const directoryBundles = listValue$11(directoryData.items);
41390
+ const directoryBundles = listValue$12(directoryData.items);
41319
41391
  const privateBundles = directoryBundles.filter((raw) => {
41320
- const kind = numberValue$10(objectValue$1(objectValue$1(raw).session).session_kind);
41392
+ const kind = numberValue$11(objectValue$1(objectValue$1(raw).session).session_kind);
41321
41393
  return kind === 1 || kind === 3;
41322
41394
  });
41323
41395
  const privateTopicIds = privateBundles.map((raw) => {
41324
41396
  const chatSession = objectValue$1(objectValue$1(raw).session);
41325
- return numberValue$10(chatSession.shared_topic_id ?? chatSession.subject_id);
41397
+ return numberValue$11(chatSession.shared_topic_id ?? chatSession.subject_id);
41326
41398
  }).filter((value) => Number.isSafeInteger(value) && value > 0);
41327
41399
  const partnerData = privateTopicIds.length === 0 ? {} : await this.runtime.authenticatedSubjectPost("/api/v1/private/get-partner-info-v2", { rm_subject_ids: [...new Set(privateTopicIds)] }, session, options.signal).catch(() => ({}));
41328
41400
  const contactByTopicId = /* @__PURE__ */ new Map();
41329
- for (const raw of listValue$11(partnerData.item_ls)) {
41401
+ for (const raw of listValue$12(partnerData.item_ls)) {
41330
41402
  const item = objectValue$1(raw);
41331
- const topicId = numberValue$10(item.rm_subject_id ?? item.shared_topic_id);
41403
+ const topicId = numberValue$11(item.rm_subject_id ?? item.shared_topic_id);
41332
41404
  if (topicId > 0 && typeof item.is_contact === "boolean") contactByTopicId.set(topicId, item.is_contact);
41333
41405
  }
41334
- const existingMemberUserIds = new Set(listValue$11(objectValue$1(membersData).items).map((item) => numberValue$10(objectValue$1(item).user_id)));
41406
+ const existingMemberUserIds = new Set(listValue$12(objectValue$1(membersData).items).map((item) => numberValue$11(objectValue$1(item).user_id)));
41335
41407
  const rawCandidates = /* @__PURE__ */ new Map();
41336
41408
  for (const raw of privateBundles) {
41337
41409
  const bundle = objectValue$1(raw);
41338
41410
  const chatSession = objectValue$1(bundle.session);
41339
- if (numberValue$10(chatSession.session_kind) !== 1 && numberValue$10(chatSession.session_kind) !== 3) continue;
41411
+ if (numberValue$11(chatSession.session_kind) !== 1 && numberValue$11(chatSession.session_kind) !== 3) continue;
41340
41412
  const counterpart = objectValue$1(bundle.private_counterpart);
41341
41413
  const supplement = objectValue$1(bundle.private_supplement);
41342
- const targetUserId = numberValue$10(counterpart.user_id);
41414
+ const targetUserId = numberValue$11(counterpart.user_id);
41343
41415
  const privateChatSessionUid = stringValue$1(chatSession.chat_session_uid).trim();
41344
41416
  const displayName = stringValue$1(supplement.remark ?? supplement.counterpart_name_snapshot ?? counterpart.display_name_snapshot ?? supplement.pending_name ?? counterpart.visible_phone).trim();
41345
- const privateTopicId = numberValue$10(chatSession.shared_topic_id ?? chatSession.subject_id);
41346
- const contactState = numberValue$10(supplement.contact_state);
41417
+ const privateTopicId = numberValue$11(chatSession.shared_topic_id ?? chatSession.subject_id);
41418
+ const contactState = numberValue$11(supplement.contact_state);
41347
41419
  const relation = contactByTopicId.get(privateTopicId) === true || contactState === 1 || contactState === 3 ? "contact" : "stranger";
41348
41420
  if (!Number.isSafeInteger(targetUserId) || targetUserId <= 0 || targetUserId === session.userId || privateChatSessionUid === "" || displayName === "" || query !== "" && !displayName.toLocaleLowerCase().includes(query)) continue;
41349
41421
  rawCandidates.set(targetUserId, {
@@ -41361,7 +41433,7 @@ var GroupService = class {
41361
41433
  for (const raw of directoryBundles) {
41362
41434
  const bundle = objectValue$1(raw);
41363
41435
  const chatSession = objectValue$1(bundle.session);
41364
- if (numberValue$10(chatSession.session_kind) !== 2) continue;
41436
+ if (numberValue$11(chatSession.session_kind) !== 2) continue;
41365
41437
  const groupSessionUid = stringValue$1(chatSession.chat_session_uid).trim();
41366
41438
  if (groupSessionUid === "" || groupSessionUid === source.ownerRef) continue;
41367
41439
  const displayName = stringValue$1(chatSession.title ?? chatSession.subject_title ?? objectValue$1(bundle.participant_summary).display_name).trim();
@@ -41370,7 +41442,7 @@ var GroupService = class {
41370
41442
  sourceKey: await this.source.chatDirectorySourceKey(session.userId, groupSessionUid),
41371
41443
  kind: "group_chat",
41372
41444
  displayName: displayName || "群聊",
41373
- activeAtMillis: numberValue$10(chatSession.updated_at),
41445
+ activeAtMillis: numberValue$11(chatSession.updated_at),
41374
41446
  unreadCount: 0
41375
41447
  };
41376
41448
  const groupIndex = groups.push(groupItem) - 1;
@@ -41398,9 +41470,9 @@ var GroupService = class {
41398
41470
  }, session, options.signal);
41399
41471
  const raws = [];
41400
41472
  const seenUserIds = /* @__PURE__ */ new Set();
41401
- for (const rawMember of listValue$11(groupMembersData.items)) {
41473
+ for (const rawMember of listValue$12(groupMembersData.items)) {
41402
41474
  const member = objectValue$1(rawMember);
41403
- const targetUserId = numberValue$10(member.user_id);
41475
+ const targetUserId = numberValue$11(member.user_id);
41404
41476
  if (!Number.isSafeInteger(targetUserId) || targetUserId <= 0 || targetUserId === session.userId || seenUserIds.has(targetUserId)) continue;
41405
41477
  if (chatMemberStatus(member.status) !== "active") continue;
41406
41478
  const privateCandidate = rawCandidates.get(targetUserId);
@@ -41446,7 +41518,7 @@ var GroupService = class {
41446
41518
  }
41447
41519
  const profiles = await this.profile.publicProfileSummariesByUserIds([.../* @__PURE__ */ new Set([...selected.map(([userId]) => userId), ...groupTargetUserIds])], session, options.signal).catch(() => /* @__PURE__ */ new Map());
41448
41520
  const preview = objectValue$1(objectValue$1(inviteData).preview);
41449
- const approvalRequired = numberValue$10(preview.join_mode) === 2;
41521
+ const approvalRequired = numberValue$11(preview.join_mode) === 2;
41450
41522
  const candidateItem = async (targetUserId, candidate) => {
41451
41523
  const alreadyMember = existingMemberUserIds.has(targetUserId);
41452
41524
  const cannotInvite = approvalRequired && candidate.privateChatSessionUid === void 0;
@@ -41486,7 +41558,7 @@ var GroupService = class {
41486
41558
  items,
41487
41559
  total: rawCandidates.size,
41488
41560
  hasMore: rawCandidates.size > items.length,
41489
- mode: numberValue$10(preview.join_mode) === 2 ? "approval_invite" : "direct_add",
41561
+ mode: numberValue$11(preview.join_mode) === 2 ? "approval_invite" : "direct_add",
41490
41562
  groups,
41491
41563
  groupCandidates,
41492
41564
  contactCount: [...rawCandidates.values()].filter((item) => item.relation === "contact").length,
@@ -41500,7 +41572,7 @@ var GroupService = class {
41500
41572
  const refs = candidateRefs.map((value) => value.trim());
41501
41573
  if (refs.length === 0 || refs.length > 20 || refs.some((value) => value === "") || new Set(refs).size !== refs.length) throw new ArkmePluginError("group-member-candidates-invalid", "请选择 1-20 个不重复的成员候选项", false);
41502
41574
  const preview = objectValue$1(objectValue$1(await this.runtime.authenticatedChatPost("/api/v1/chats/governance/invite-preview", { chat_session_uid: source.ownerRef }, session, signal)).preview);
41503
- const approvalRequired = numberValue$10(preview.join_mode) === 2;
41575
+ const approvalRequired = numberValue$11(preview.join_mode) === 2;
41504
41576
  const inviteLink = stringValue$1(preview.invite_link).trim();
41505
41577
  const results = [];
41506
41578
  for (const candidateRef of refs) {
@@ -41566,9 +41638,9 @@ var GroupService = class {
41566
41638
  }
41567
41639
  const result = {
41568
41640
  version: 1,
41569
- userId: numberValue$10(raw.userId),
41641
+ userId: numberValue$11(raw.userId),
41570
41642
  groupChatSessionUid: stringValue$1(raw.groupChatSessionUid).trim(),
41571
- targetUserId: numberValue$10(raw.targetUserId),
41643
+ targetUserId: numberValue$11(raw.targetUserId),
41572
41644
  displayName: stringValue$1(raw.displayName).trim(),
41573
41645
  ...stringValue$1(raw.privateChatSessionUid).trim() === "" ? {} : { privateChatSessionUid: stringValue$1(raw.privateChatSessionUid).trim() },
41574
41646
  origin: raw.origin === "group_chat" ? "group_chat" : "private_chat"
@@ -41601,15 +41673,15 @@ var GroupService = class {
41601
41673
  const session = await this.runtime.requireSession();
41602
41674
  const source = await this.source.openSourceRef(sourceRef, session.userId);
41603
41675
  if (source.kind !== "group_chat") throw new ArkmePluginError("group-source-invalid", "仅支持查看群聊成员", false);
41604
- const rawItems = listValue$11((await this.runtime.authenticatedChatPost("/api/v1/chats/members/list", {
41676
+ const rawItems = listValue$12((await this.runtime.authenticatedChatPost("/api/v1/chats/members/list", {
41605
41677
  chat_session_uid: source.ownerRef,
41606
41678
  active_only: options.activeOnly !== false
41607
41679
  }, session, options.signal)).items).map(objectValue$1);
41608
- const userIds = rawItems.map((item) => numberValue$10(item.user_id)).filter((userId) => Number.isSafeInteger(userId) && userId > 0);
41680
+ const userIds = rawItems.map((item) => numberValue$11(item.user_id)).filter((userId) => Number.isSafeInteger(userId) && userId > 0);
41609
41681
  const profiles = await this.profile.publicProfileSummariesByUserIds(userIds, session, options.signal).catch(() => /* @__PURE__ */ new Map());
41610
41682
  const members = [];
41611
41683
  for (const item of rawItems) {
41612
- const userId = numberValue$10(item.user_id);
41684
+ const userId = numberValue$11(item.user_id);
41613
41685
  if (!Number.isSafeInteger(userId) || userId <= 0) continue;
41614
41686
  const profile = profiles.get(userId);
41615
41687
  const remarkName = stringValue$1(item.remark).trim();
@@ -41638,9 +41710,9 @@ var GroupService = class {
41638
41710
  status,
41639
41711
  isSelf: userId === session.userId,
41640
41712
  isOwner: role === "owner",
41641
- joinedAtMillis: numberValue$10(item.join_at),
41642
- recordCount: Math.max(0, Math.trunc(numberValue$10(objectValue$1(item.extra).record_count))),
41643
- mentionCount: Math.max(0, Math.trunc(numberValue$10(objectValue$1(item.extra).mention_count)))
41713
+ joinedAtMillis: numberValue$11(item.join_at),
41714
+ recordCount: Math.max(0, Math.trunc(numberValue$11(objectValue$1(item.extra).record_count))),
41715
+ mentionCount: Math.max(0, Math.trunc(numberValue$11(objectValue$1(item.extra).mention_count)))
41644
41716
  });
41645
41717
  }
41646
41718
  const roleRank = (role) => role === "owner" ? 0 : role === "admin" ? 1 : role === "member" ? 2 : 3;
@@ -41671,10 +41743,10 @@ var GroupService = class {
41671
41743
  sourceKey: await this.source.chatDirectorySourceKey(session.userId, source.ownerRef),
41672
41744
  kind: "group_chat",
41673
41745
  displayName: title,
41674
- activeAtMillis: numberValue$10(chatSession.last_active_at),
41675
- unreadCount: numberValue$10(objectValue$1(data.unread_snapshot).unread_count),
41746
+ activeAtMillis: numberValue$11(chatSession.last_active_at),
41747
+ unreadCount: numberValue$11(objectValue$1(data.unread_snapshot).unread_count),
41676
41748
  isMuted: messageDnd,
41677
- ...numberValue$10(chatSession.last_seq) > 0 ? { latestSequence: numberValue$10(chatSession.last_seq) } : {}
41749
+ ...numberValue$11(chatSession.last_seq) > 0 ? { latestSequence: numberValue$11(chatSession.last_seq) } : {}
41678
41750
  };
41679
41751
  try {
41680
41752
  await this.source.hydrateSourceAvatars([nextSource], /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map([[0, source.ownerRef]]), session, signal);
@@ -41700,12 +41772,12 @@ var GroupService = class {
41700
41772
  const current = await this.runtime.authenticatedChatPost("/api/v1/chats/policy/get", { chat_session_uid: source.ownerRef }, session, signal);
41701
41773
  await this.runtime.authenticatedChatPost("/api/v1/chats/policy/update", {
41702
41774
  chat_session_uid: source.ownerRef,
41703
- show_in_home_state: numberValue$10(current.show_in_home_state) || 1,
41704
- privacy_state: numberValue$10(current.privacy_state) || 1,
41775
+ show_in_home_state: numberValue$11(current.show_in_home_state) || 1,
41776
+ privacy_state: numberValue$11(current.privacy_state) || 1,
41705
41777
  mute_state: enabled ? 2 : 1,
41706
- pin_state: numberValue$10(current.pin_state) || 1,
41778
+ pin_state: numberValue$11(current.pin_state) || 1,
41707
41779
  notify_state: enabled ? 2 : 1,
41708
- status: numberValue$10(current.status) || 1,
41780
+ status: numberValue$11(current.status) || 1,
41709
41781
  update_at: Date.now()
41710
41782
  }, session, signal);
41711
41783
  const cacheKey = `${String(session.userId)}:${source.ownerRef}`;
@@ -41787,13 +41859,13 @@ var GroupService = class {
41787
41859
  };
41788
41860
  //#endregion
41789
41861
  //#region src/services/interwoven-service.ts
41790
- function numberValue$9(value) {
41862
+ function numberValue$10(value) {
41791
41863
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
41792
41864
  }
41793
41865
  function booleanValue$4(value) {
41794
41866
  return value === true;
41795
41867
  }
41796
- function listValue$10(value) {
41868
+ function listValue$11(value) {
41797
41869
  return Array.isArray(value) ? value : [];
41798
41870
  }
41799
41871
  var InterwovenService = class {
@@ -41839,27 +41911,27 @@ var InterwovenService = class {
41839
41911
  rm_subject_id: legacyRmSubjectId,
41840
41912
  limit: 100
41841
41913
  }, session, signal);
41842
- const preparedAtMillis = Math.max(0, Math.trunc(numberValue$9(data.prepared_at))) || Date.now();
41914
+ const preparedAtMillis = Math.max(0, Math.trunc(numberValue$10(data.prepared_at))) || Date.now();
41843
41915
  const descriptors = [];
41844
41916
  let invalidItemCount = 0;
41845
- for (const rawGroup of listValue$10(data.groups)) {
41917
+ for (const rawGroup of listValue$11(data.groups)) {
41846
41918
  const group = objectValue$1(rawGroup);
41847
- if (numberValue$9(group.moment_type) !== 1) continue;
41919
+ if (numberValue$10(group.moment_type) !== 1) continue;
41848
41920
  const groupTitle = stringValue$1(group.group_title).trim() || "群聊";
41849
- for (const rawItem of listValue$10(group.group_preview_items)) {
41921
+ for (const rawItem of listValue$11(group.group_preview_items)) {
41850
41922
  const item = objectValue$1(rawItem);
41851
- if (numberValue$9(item.moment_type) !== 1) continue;
41923
+ if (numberValue$10(item.moment_type) !== 1) continue;
41852
41924
  const jumpTarget = objectValue$1(item.jump_target);
41853
41925
  const renderPayload = objectValue$1(item.render_payload);
41854
41926
  const groupName = stringValue$1(renderPayload.group_name ?? item.title).trim() || groupTitle;
41855
41927
  const rawMomentId = stringValue$1(item.moment_id).trim();
41856
- const occurredAtMillis = Math.trunc(numberValue$9(item.occurred_at));
41928
+ const occurredAtMillis = Math.trunc(numberValue$10(item.occurred_at));
41857
41929
  const sourceChatSessionUid = stringValue$1(jumpTarget.chat_session_uid).trim();
41858
- const recordOwnerUserId = Math.trunc(numberValue$9(jumpTarget.record_owner_user_id ?? renderPayload.record_owner_user_id ?? renderPayload.sender_user_id));
41930
+ const recordOwnerUserId = Math.trunc(numberValue$10(jumpTarget.record_owner_user_id ?? renderPayload.record_owner_user_id ?? renderPayload.sender_user_id));
41859
41931
  const recordUid = stringValue$1(jumpTarget.record_uid ?? renderPayload.record_uid).trim();
41860
41932
  const relationUid = stringValue$1(jumpTarget.rel_uid).trim();
41861
- const sequence = Math.trunc(numberValue$9(jumpTarget.seq));
41862
- const senderUserId = Math.trunc(numberValue$9(renderPayload.sender_user_id));
41933
+ const sequence = Math.trunc(numberValue$10(jumpTarget.seq));
41934
+ const senderUserId = Math.trunc(numberValue$10(renderPayload.sender_user_id));
41863
41935
  const hasChatDetailLocator = sourceChatSessionUid !== "" && recordOwnerUserId > 0 && recordUid !== "" && relationUid !== "" && sequence > 0;
41864
41936
  const fallbackTextContent = stringValue$1(renderPayload.content ?? renderPayload.mention_text ?? item.summary).trim().slice(0, 2e4);
41865
41937
  const fallbackTitle = stringValue$1(item.title ?? renderPayload.group_name).trim().slice(0, 500);
@@ -41926,9 +41998,9 @@ var InterwovenService = class {
41926
41998
  });
41927
41999
  }
41928
42000
  moments.sort((left, right) => left.occurredAtMillis - right.occurredAtMillis || left.momentId.localeCompare(right.momentId));
41929
- const sourceStatusPartial = listValue$10(data.source_status).some((raw) => {
42001
+ const sourceStatusPartial = listValue$11(data.source_status).some((raw) => {
41930
42002
  const status = objectValue$1(raw);
41931
- return numberValue$9(status.moment_type) === 1 && numberValue$9(status.status) !== 1;
42003
+ return numberValue$10(status.moment_type) === 1 && numberValue$10(status.status) !== 1;
41932
42004
  });
41933
42005
  if (moments.length === 0 && invalidItemCount === 0 && !sourceStatusPartial) return {
41934
42006
  state: "empty",
@@ -41948,9 +42020,7 @@ var InterwovenService = class {
41948
42020
  const source = await this.source.openSourceRef(sourceRef, session.userId);
41949
42021
  if (source.kind !== "private_chat") throw new ArkmePluginError("interwoven-source-invalid", "交织瞬间仅支持普通私聊", false, 400);
41950
42022
  const reference = await this.openInterwovenMomentRef(momentRef, session.userId, source.ownerRef);
41951
- if (!this.runtime.config.interwovenMomentsEnabled) throw new ArkmePluginError("interwoven-disabled", "交织瞬间能力当前未开启", false, 403);
41952
- if (!booleanValue$4((await this.runtime.authenticatedAuthPost("/api/v1/auth/able-func", { func_type: 12 }, session, signal)).able)) throw new ArkmePluginError("interwoven-disabled", "交织瞬间能力当前未开放", false, 403);
41953
- await this.assertHumanPrivateSource(source, session, signal);
42023
+ await this.assertInterwovenOperationAllowed(source, session, signal);
41954
42024
  if (reference.detailMode === "owner_payload") return {
41955
42025
  momentId: reference.momentId,
41956
42026
  groupName: reference.groupName,
@@ -41974,8 +42044,8 @@ var InterwovenService = class {
41974
42044
  const relation = objectValue$1(item.relation);
41975
42045
  const record = objectValue$1(item.record);
41976
42046
  const payload = objectValue$1(record.payload);
41977
- if (stringValue$1(data.chat_session_uid).trim() !== reference.sourceChatSessionUid || stringValue$1(relation.chat_session_uid).trim() !== reference.sourceChatSessionUid || numberValue$9(relation.record_owner_user_id) !== reference.recordOwnerUserId || stringValue$1(relation.record_uid).trim() !== reference.recordUid || stringValue$1(relation.rel_uid).trim() !== reference.relationUid || numberValue$9(relation.seq) !== reference.sequence) throw new ArkmePluginError("interwoven-detail-contract-invalid", "快记详情响应与所选交织瞬间不一致", true, 502);
41978
- const status = Math.trunc(numberValue$9(record.status));
42047
+ if (stringValue$1(data.chat_session_uid).trim() !== reference.sourceChatSessionUid || stringValue$1(relation.chat_session_uid).trim() !== reference.sourceChatSessionUid || numberValue$10(relation.record_owner_user_id) !== reference.recordOwnerUserId || stringValue$1(relation.record_uid).trim() !== reference.recordUid || stringValue$1(relation.rel_uid).trim() !== reference.relationUid || numberValue$10(relation.seq) !== reference.sequence) throw new ArkmePluginError("interwoven-detail-contract-invalid", "快记详情响应与所选交织瞬间不一致", true, 502);
42048
+ const status = Math.trunc(numberValue$10(record.status));
41979
42049
  const title = stringValue$1(payload.title).trim() || reference.groupName;
41980
42050
  const textContent = stringValue$1(payload.text_content).trim();
41981
42051
  return {
@@ -41991,18 +42061,41 @@ var InterwovenService = class {
41991
42061
  degraded: status !== 1 || textContent === ""
41992
42062
  };
41993
42063
  }
42064
+ async relatedQuickNoteLocator(sourceRef, momentRef, signal) {
42065
+ const session = await this.runtime.requireSession();
42066
+ const normalizedSourceRef = sourceRef.trim();
42067
+ const source = await this.source.openSourceRef(normalizedSourceRef, session.userId);
42068
+ if (source.kind !== "private_chat") throw new ArkmePluginError("interwoven-source-invalid", "交织瞬间仅支持普通私聊", false, 400);
42069
+ const reference = await this.openInterwovenMomentRef(momentRef, session.userId, source.ownerRef);
42070
+ await this.assertInterwovenOperationAllowed(source, session, signal);
42071
+ return {
42072
+ viewerUserId: session.userId,
42073
+ sourceRef: normalizedSourceRef,
42074
+ sourceOwnerRef: source.ownerRef,
42075
+ contextType: reference.sourceChatSessionUid === "" ? "record" : "chat",
42076
+ recordUid: reference.recordUid,
42077
+ recordOwnerUserId: reference.recordOwnerUserId,
42078
+ chatSessionUid: reference.sourceChatSessionUid
42079
+ };
42080
+ }
42081
+ async assertInterwovenOperationAllowed(source, session, signal) {
42082
+ if (source.kind !== "private_chat") throw new ArkmePluginError("interwoven-source-invalid", "交织瞬间仅支持普通私聊", false, 400);
42083
+ if (!this.runtime.config.interwovenMomentsEnabled) throw new ArkmePluginError("interwoven-disabled", "交织瞬间能力当前未开启", false, 403);
42084
+ if (!booleanValue$4((await this.runtime.authenticatedAuthPost("/api/v1/auth/able-func", { func_type: 12 }, session, signal)).able)) throw new ArkmePluginError("interwoven-disabled", "交织瞬间能力当前未开放", false, 403);
42085
+ await this.assertHumanPrivateSource(source, session, signal);
42086
+ }
41994
42087
  async assertHumanPrivateSource(source, session, signal) {
41995
42088
  const detail = await this.runtime.authenticatedChatPost("/api/v1/chats/detail", { chat_session_uid: source.ownerRef }, session, signal);
41996
42089
  const chatSession = objectValue$1(detail.session);
41997
42090
  const counterpart = objectValue$1(detail.private_counterpart);
41998
- const counterpartUserId = Math.trunc(numberValue$9(counterpart.user_id));
41999
- if (stringValue$1(chatSession.chat_session_uid).trim() !== source.ownerRef || numberValue$9(chatSession.session_kind) !== 1 || !Number.isSafeInteger(counterpartUserId) || counterpartUserId <= 0 || counterpartUserId === session.userId) throw new ArkmePluginError("interwoven-source-invalid", "交织瞬间仅支持有效的双人私聊", false, 409);
42091
+ const counterpartUserId = Math.trunc(numberValue$10(counterpart.user_id));
42092
+ if (stringValue$1(chatSession.chat_session_uid).trim() !== source.ownerRef || numberValue$10(chatSession.session_kind) !== 1 || !Number.isSafeInteger(counterpartUserId) || counterpartUserId <= 0 || counterpartUserId === session.userId) throw new ArkmePluginError("interwoven-source-invalid", "交织瞬间仅支持有效的双人私聊", false, 409);
42000
42093
  return counterpartUserId;
42001
42094
  }
42002
42095
  async resolveLegacyPrivateSubjectId(counterpartUserId, session, signal) {
42003
42096
  const data = await this.runtime.authenticatedSubjectPost("/api/v1/private/check-contact-chat", { target_user_id: counterpartUserId }, session, signal);
42004
42097
  if (!booleanValue$4(data.exist)) return 0;
42005
- const rmSubjectId = Math.trunc(numberValue$9(data.rm_subject_id));
42098
+ const rmSubjectId = Math.trunc(numberValue$10(data.rm_subject_id));
42006
42099
  if (!Number.isSafeInteger(rmSubjectId) || rmSubjectId <= 0) throw new ArkmePluginError("interwoven-subject-contract-invalid", "私聊交织主题定位响应不完整", true, 502);
42007
42100
  return rmSubjectId;
42008
42101
  }
@@ -42412,10 +42505,10 @@ const IMAGE_CACHE_MAX_BYTES = 32 * 1024 * 1024;
42412
42505
  const IMAGE_CACHE_MAX_ENTRIES = 64;
42413
42506
  const IMAGE_DOWNLOAD_CONCURRENCY = 4;
42414
42507
  const RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND = 4;
42415
- function numberValue$8(value) {
42508
+ function numberValue$9(value) {
42416
42509
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
42417
42510
  }
42418
- function listValue$9(value) {
42511
+ function listValue$10(value) {
42419
42512
  return Array.isArray(value) ? value : [];
42420
42513
  }
42421
42514
  function jsonObjectValue(value) {
@@ -42580,7 +42673,7 @@ var MediaService = class {
42580
42673
  const unique = [...new Set(fileAssetUids.map((uid) => uid.trim()).filter((uid) => uid !== ""))].slice(0, 50);
42581
42674
  if (unique.length === 0) return [];
42582
42675
  const session = await this.runtime.requireSession();
42583
- return listValue$9((await this.runtime.authenticatedPost("/api/v1/files/assets/query", { file_asset_uids: unique }, session, signal)).items).map((raw) => {
42676
+ return listValue$10((await this.runtime.authenticatedPost("/api/v1/files/assets/query", { file_asset_uids: unique }, session, signal)).items).map((raw) => {
42584
42677
  const item = objectValue$1(raw);
42585
42678
  const previewUrl = safeHttpsUrl(item.preview_url);
42586
42679
  const downloadUrl = safeHttpsUrl(item.download_url);
@@ -42645,14 +42738,14 @@ var MediaService = class {
42645
42738
  if (!response.ok) throw new ArkmePluginError("upload-storage-failed", `对象存储上传失败(${String(response.status)})`, true, 502);
42646
42739
  storageETag = response.headers.get("etag") ?? "";
42647
42740
  } else {
42648
- const partSize = Math.trunc(numberValue$8(prepared.multipart_part_size));
42649
- const parts = listValue$9(prepared.multipart_parts).map(objectValue$1);
42741
+ const partSize = Math.trunc(numberValue$9(prepared.multipart_part_size));
42742
+ const parts = listValue$10(prepared.multipart_parts).map(objectValue$1);
42650
42743
  if (partSize <= 0 || parts.length === 0) throw new ArkmePluginError("upload-parts-missing", "分片上传参数缺失", true, 502);
42651
42744
  const handle = await open(filePath, "r");
42652
42745
  try {
42653
42746
  for (const part of parts) {
42654
42747
  options.signal?.throwIfAborted();
42655
- const partNumber = Math.trunc(numberValue$8(part.part_number));
42748
+ const partNumber = Math.trunc(numberValue$9(part.part_number));
42656
42749
  const uploadUrl = stringValue$1(part.upload_url).trim();
42657
42750
  const offset = (partNumber - 1) * partSize;
42658
42751
  const length = Math.min(partSize, metadata.size - offset);
@@ -42691,7 +42784,7 @@ var MediaService = class {
42691
42784
  fileAssetUid,
42692
42785
  fileName: metadata.fileName,
42693
42786
  mimeType: stringValue$1(completed.mime_type).trim() || metadata.mimeType,
42694
- size: numberValue$8(completed.size) || metadata.size,
42787
+ size: numberValue$9(completed.size) || metadata.size,
42695
42788
  fileKind: metadata.fileKind
42696
42789
  };
42697
42790
  } catch (error) {
@@ -43000,8 +43093,8 @@ var MediaService = class {
43000
43093
  voice.uid
43001
43094
  ].map((value) => stringValue$1(value).trim()).find((value) => value !== "");
43002
43095
  if (fileAssetUid === void 0) continue;
43003
- const durationSeconds = numberValue$8(voice.duration_sec ?? voice.durationSec ?? voice.duration);
43004
- const durationMillis = numberValue$8(voice.duration_millis ?? voice.durationMillis ?? voice.duration_ms);
43096
+ const durationSeconds = numberValue$9(voice.duration_sec ?? voice.durationSec ?? voice.duration);
43097
+ const durationMillis = numberValue$9(voice.duration_millis ?? voice.durationMillis ?? voice.duration_ms);
43005
43098
  return {
43006
43099
  ...voice,
43007
43100
  file_asset_uid: fileAssetUid,
@@ -43014,7 +43107,7 @@ var MediaService = class {
43014
43107
  recordMediaRefs(raw) {
43015
43108
  const contentPayload = this.recordContentPayload(raw);
43016
43109
  const voiceRef = this.recordVoiceMediaRef(raw);
43017
- const refs = [...listValue$9(contentPayload.media_refs ?? contentPayload.mediaRefs).map(objectValue$1), ...voiceRef === void 0 ? [] : [voiceRef]].filter((item) => Math.trunc(numberValue$8(item.content_file_role)) !== RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND && stringValue$1(item.file_asset_uid).trim() !== "");
43110
+ const refs = [...listValue$10(contentPayload.media_refs ?? contentPayload.mediaRefs).map(objectValue$1), ...voiceRef === void 0 ? [] : [voiceRef]].filter((item) => Math.trunc(numberValue$9(item.content_file_role)) !== RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND && stringValue$1(item.file_asset_uid).trim() !== "");
43018
43111
  return [...new Map(refs.map((item) => [stringValue$1(item.file_asset_uid).trim(), item])).values()];
43019
43112
  }
43020
43113
  async queryRecordMediaDisplayItems(recordUids, session, signal) {
@@ -43027,11 +43120,11 @@ var MediaService = class {
43027
43120
  key: expectedRecordUids.join(","),
43028
43121
  cacheMs: 1e3
43029
43122
  });
43030
- for (const rawResult of listValue$9(data.items ?? data.results)) {
43123
+ for (const rawResult of listValue$10(data.items ?? data.results)) {
43031
43124
  const result = objectValue$1(rawResult);
43032
43125
  const recordUid = stringValue$1(result.record_uid).trim();
43033
43126
  if (recordUid === "" || !expectedRecordUids.includes(recordUid)) continue;
43034
- displayItemsByRecordUid.set(recordUid, listValue$9(result.items));
43127
+ displayItemsByRecordUid.set(recordUid, listValue$10(result.items));
43035
43128
  }
43036
43129
  return displayItemsByRecordUid;
43037
43130
  }
@@ -43062,7 +43155,7 @@ var MediaService = class {
43062
43155
  remoteUrl,
43063
43156
  mimeType,
43064
43157
  fileName: stringValue$1(item.file_name).trim() || "语音",
43065
- size: Math.max(0, numberValue$8(item.size))
43158
+ size: Math.max(0, numberValue$9(item.size))
43066
43159
  }, `search-audio\0${key}`));
43067
43160
  }
43068
43161
  return mediaRefs;
@@ -43085,7 +43178,7 @@ var MediaService = class {
43085
43178
  purpose: "original",
43086
43179
  mimeType: stringValue$1(item.mime_type) || "application/octet-stream",
43087
43180
  fileName: stringValue$1(item.file_name) || "文件",
43088
- size: Math.max(0, numberValue$8(item.size))
43181
+ size: Math.max(0, numberValue$9(item.size))
43089
43182
  }, `search-file\0${key}`));
43090
43183
  }
43091
43184
  return refs;
@@ -43124,7 +43217,7 @@ var MediaService = class {
43124
43217
  forwardContentBlocks(files, viewerUserId) {
43125
43218
  if (this.runtime.config.richMediaRenderEnabled === false) return [];
43126
43219
  const displayItems = files.slice(0, 32).map(objectValue$1).flatMap((file, index) => {
43127
- if (numberValue$8(file.content_file_role) === RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND) return [];
43220
+ if (numberValue$9(file.content_file_role) === RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND) return [];
43128
43221
  const trustedUrl = (raw) => {
43129
43222
  const value = safeHttpsUrl(raw);
43130
43223
  if (value === void 0) return void 0;
@@ -43135,11 +43228,11 @@ var MediaService = class {
43135
43228
  const previewUrl = trustedUrl(file.preview_url ?? file.previewUrl);
43136
43229
  return [{
43137
43230
  file_name: stringValue$1(file.name ?? file.file_name ?? file.fileName),
43138
- file_kind: numberValue$8(file.type ?? file.file_kind ?? file.fileKind),
43231
+ file_kind: numberValue$9(file.type ?? file.file_kind ?? file.fileKind),
43139
43232
  mime_type: stringValue$1(file.mime_type ?? file.mimeType),
43140
- size: numberValue$8(file.size),
43141
- sort_order: numberValue$8(file.order ?? file.sort_order ?? index),
43142
- duration_sec: numberValue$8(file.duration_sec ?? file.durationSec),
43233
+ size: numberValue$9(file.size),
43234
+ sort_order: numberValue$9(file.order ?? file.sort_order ?? index),
43235
+ duration_sec: numberValue$9(file.duration_sec ?? file.durationSec),
43143
43236
  ...downloadUrl === void 0 ? {} : { download_url: downloadUrl },
43144
43237
  ...previewUrl === void 0 ? {} : { preview_url: previewUrl }
43145
43238
  }];
@@ -43160,7 +43253,7 @@ var MediaService = class {
43160
43253
  remoteUrl,
43161
43254
  mimeType,
43162
43255
  fileName,
43163
- size: Math.max(0, Math.trunc(numberValue$8(item.file_size ?? item.size)))
43256
+ size: Math.max(0, Math.trunc(numberValue$9(item.file_size ?? item.size)))
43164
43257
  }, fileAssetUid === "" ? void 0 : `favorite-sticker:${fileAssetUid}`);
43165
43258
  }
43166
43259
  issueMediaRef(viewerUserId, descriptor, stableIdentity, lifetimeMillis) {
@@ -43197,10 +43290,10 @@ var MediaService = class {
43197
43290
  const core = objectValue$1(root.record_core);
43198
43291
  this.recordContentPayload(raw);
43199
43292
  const displayItems = [
43200
- ...listValue$9(root.media_display_items),
43201
- ...listValue$9(record.media_display_items),
43202
- ...listValue$9(payload.media_display_items),
43203
- ...listValue$9(core.media_display_items),
43293
+ ...listValue$10(root.media_display_items),
43294
+ ...listValue$10(record.media_display_items),
43295
+ ...listValue$10(payload.media_display_items),
43296
+ ...listValue$10(core.media_display_items),
43204
43297
  ...hydratedDisplayItems
43205
43298
  ].map(objectValue$1);
43206
43299
  const displayByAsset = /* @__PURE__ */ new Map();
@@ -43213,11 +43306,11 @@ var MediaService = class {
43213
43306
  ...displayByAsset.get(stringValue$1(ref.file_asset_uid).trim()) ?? {},
43214
43307
  ...ref
43215
43308
  })) : displayItems).filter((item) => {
43216
- return Math.trunc(numberValue$8(item.content_file_role)) !== RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND;
43309
+ return Math.trunc(numberValue$9(item.content_file_role)) !== RECORD_CONTENT_FILE_ROLE_BACKGROUND_SOUND;
43217
43310
  }).flatMap((item, index) => {
43218
43311
  const fileAssetUid = stringValue$1(item.file_asset_uid).trim();
43219
43312
  const fileName = stringValue$1(item.file_name).trim() || `附件-${String(index + 1)}`;
43220
- const fileKind = Math.trunc(numberValue$8(item.file_kind));
43313
+ const fileKind = Math.trunc(numberValue$9(item.file_kind));
43221
43314
  const mimeType = arkmeNormalizedFileMimeType(stringValue$1(item.mime_type).trim().toLowerCase(), fileName);
43222
43315
  const kind = this.mediaKind(fileKind, mimeType, fileName);
43223
43316
  const remoteUrl = stringValue$1(kind === "image" ? item.preview_url ?? item.download_url : item.download_url).trim();
@@ -43228,7 +43321,7 @@ var MediaService = class {
43228
43321
  purpose: "original",
43229
43322
  mimeType,
43230
43323
  fileName,
43231
- size: Math.max(0, Math.trunc(numberValue$8(item.size)))
43324
+ size: Math.max(0, Math.trunc(numberValue$9(item.size)))
43232
43325
  }, fileAssetUid === "" ? void 0 : `original\0${fileAssetUid}`);
43233
43326
  return [{
43234
43327
  kind,
@@ -43237,15 +43330,15 @@ var MediaService = class {
43237
43330
  remoteUrl,
43238
43331
  mimeType,
43239
43332
  fileName,
43240
- size: Math.max(0, Math.trunc(numberValue$8(item.size)))
43333
+ size: Math.max(0, Math.trunc(numberValue$9(item.size)))
43241
43334
  }, kind === "image" && fileAssetUid !== "" ? fileAssetUid : void 0),
43242
43335
  ...fileAssetUid === "" ? {} : { fileAssetUid },
43243
43336
  fileName,
43244
43337
  mimeType,
43245
- size: Math.max(0, Math.trunc(numberValue$8(item.size))),
43246
- ...numberValue$8(item.duration_sec) > 0 ? { durationSec: numberValue$8(item.duration_sec) } : {},
43247
- sortOrder: Math.trunc(numberValue$8(item.sort_order ?? index)),
43248
- ...[1, 3].includes(Math.trunc(numberValue$8(item.render_role))) ? { renderRole: Math.trunc(numberValue$8(item.render_role)) } : {}
43338
+ size: Math.max(0, Math.trunc(numberValue$9(item.size))),
43339
+ ...numberValue$9(item.duration_sec) > 0 ? { durationSec: numberValue$9(item.duration_sec) } : {},
43340
+ sortOrder: Math.trunc(numberValue$9(item.sort_order ?? index)),
43341
+ ...[1, 3].includes(Math.trunc(numberValue$9(item.render_role))) ? { renderRole: Math.trunc(numberValue$9(item.render_role)) } : {}
43249
43342
  }];
43250
43343
  }).sort((left, right) => left.sortOrder - right.sortOrder);
43251
43344
  }
@@ -43342,10 +43435,10 @@ var MediaService = class {
43342
43435
  };
43343
43436
  //#endregion
43344
43437
  //#region src/services/outgoing-call-service.ts
43345
- function numberValue$7(value) {
43438
+ function numberValue$8(value) {
43346
43439
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
43347
43440
  }
43348
- function listValue$8(value) {
43441
+ function listValue$9(value) {
43349
43442
  return Array.isArray(value) ? value : [];
43350
43443
  }
43351
43444
  function callDiag(label, detail) {
@@ -43435,11 +43528,11 @@ var OutgoingCallService = class {
43435
43528
  const detail = await this.runtime.authenticatedChatPost("/api/v1/chats/detail", { chat_session_uid: source.ownerRef }, session, input.signal);
43436
43529
  const chatSession = objectValue$1(detail.session);
43437
43530
  const sessionUid = stringValue$1(chatSession.chat_session_uid).trim();
43438
- const sessionKind = numberValue$7(chatSession.session_kind);
43531
+ const sessionKind = numberValue$8(chatSession.session_kind);
43439
43532
  if (sessionUid !== source.ownerRef || sessionKind !== 1 && sessionKind !== 3) throw new ArkmePluginError("call-source-invalid", "当前私聊会话不可用,请刷新后重试", false, 409);
43440
43533
  const counterpart = objectValue$1(detail.private_counterpart);
43441
43534
  const supplement = objectValue$1(detail.private_supplement);
43442
- const counterpartUserId = numberValue$7(counterpart.user_id);
43535
+ const counterpartUserId = numberValue$8(counterpart.user_id);
43443
43536
  if (!Number.isSafeInteger(counterpartUserId) || counterpartUserId <= 0 || counterpartUserId === session.userId) throw new ArkmePluginError("call-peer-unavailable", "当前私聊用户不可用,请刷新后重试", false, 409);
43444
43537
  callDiag("prepare_peer_resolved", {
43445
43538
  userId: session.userId,
@@ -43453,7 +43546,7 @@ var OutgoingCallService = class {
43453
43546
  const peerProfile = (await this.profile.publicProfilesByUserIds([counterpartUserId], session, input.signal)).get(counterpartUserId);
43454
43547
  const displayName = detailDisplayName || peerProfile?.displayName || source.displayName || "Arkme 用户";
43455
43548
  const credentials = await this.runtime.authenticatedWebrtcPost("/api/v1/trtc/credentials", {}, session, input.signal);
43456
- const sdkAppId = numberValue$7(credentials.sdk_app_id);
43549
+ const sdkAppId = numberValue$8(credentials.sdk_app_id);
43457
43550
  const trtcUserId = stringValue$1(credentials.user_id).trim();
43458
43551
  const userSig = stringValue$1(credentials.user_sig).trim();
43459
43552
  if (!Number.isSafeInteger(sdkAppId) || sdkAppId <= 0 || trtcUserId === "" || userSig === "") throw new ArkmePluginError("call-credentials-invalid", "桌面通话初始化失败", true, 502);
@@ -43471,7 +43564,7 @@ var OutgoingCallService = class {
43471
43564
  }
43472
43565
  }, session, input.signal);
43473
43566
  const roomId = stringValue$1(room.room_id).trim();
43474
- const calleeAccounts = [...new Set(listValue$8(room.callee_accounts).map((value) => stringValue$1(value).trim()).filter((value) => value !== ""))];
43567
+ const calleeAccounts = [...new Set(listValue$9(room.callee_accounts).map((value) => stringValue$1(value).trim()).filter((value) => value !== ""))];
43475
43568
  if (roomId === "") throw new ArkmePluginError("call-room-invalid", "呼叫房间创建失败,请重试", true, 502);
43476
43569
  if (calleeAccounts.length === 0) throw new ArkmePluginError("call-peer-unavailable", "对方未开通通话,请对方先登录后再试", false, 409);
43477
43570
  callDiag("prepare_room_created", {
@@ -43481,7 +43574,7 @@ var OutgoingCallService = class {
43481
43574
  mediaType: input.mediaType,
43482
43575
  calleeCount: calleeAccounts.length
43483
43576
  });
43484
- const sharedTopicId = numberValue$7(room.shared_topic_id);
43577
+ const sharedTopicId = numberValue$8(room.shared_topic_id);
43485
43578
  const userData = JSON.stringify({
43486
43579
  sharedTopicId: sharedTopicId > 0 ? sharedTopicId : 0,
43487
43580
  sourceTag: "arkme-private-chat-header",
@@ -43560,7 +43653,7 @@ const ARKME_ID_MIN_LENGTH_DEFAULT = 6;
43560
43653
  const ARKME_ID_MIN_LENGTH_STAFF = 5;
43561
43654
  const ARKME_ID_MAX_LENGTH = 20;
43562
43655
  const ARKME_STAFF_ACCOUNT_TYPE = 2;
43563
- function numberValue$6(value) {
43656
+ function numberValue$7(value) {
43564
43657
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
43565
43658
  }
43566
43659
  function booleanValue$3(value) {
@@ -43569,7 +43662,7 @@ function booleanValue$3(value) {
43569
43662
  function optionalBooleanValue(value) {
43570
43663
  return typeof value === "boolean" ? value : void 0;
43571
43664
  }
43572
- function listValue$7(value) {
43665
+ function listValue$8(value) {
43573
43666
  return Array.isArray(value) ? value : [];
43574
43667
  }
43575
43668
  function chunksOf(values, size) {
@@ -43789,7 +43882,7 @@ var ProfileService = class {
43789
43882
  key: "profile:self",
43790
43883
  failureCooldownMs: 2e3
43791
43884
  });
43792
- const userId = numberValue$6(data.user_id);
43885
+ const userId = numberValue$7(data.user_id);
43793
43886
  if (userId <= 0 || userId !== session.userId) throw new ArkmePluginError("profile-contract-invalid", "Arkme 个人资料响应缺少有效用户标识", false, 502);
43794
43887
  const nickname = stringValue$1(data.nick_name).trim();
43795
43888
  const displayName = nickname || stringValue$1(data.apple_nick_name).trim() || stringValue$1(data.wechat_nick_name).trim() || stringValue$1(data.google_given_name).trim() || stringValue$1(data.name_slug).trim() || "Arkme用户";
@@ -43810,8 +43903,8 @@ var ProfileService = class {
43810
43903
  ...avatarUrl === void 0 ? {} : { avatarUrl },
43811
43904
  arkmeId: stringValue$1(data.jotmo_id).trim() || stringValue$1(data.name_slug).trim(),
43812
43905
  ...canUpdateArkmeId === void 0 ? {} : { canUpdateArkmeId },
43813
- accountType: numberValue$6(data.type),
43814
- createdAt: numberValue$6(data.create_at),
43906
+ accountType: numberValue$7(data.type),
43907
+ createdAt: numberValue$7(data.create_at),
43815
43908
  bindings: {
43816
43909
  apple: booleanValue$3(data.has_bind_apple),
43817
43910
  wechat: booleanValue$3(data.has_bind_wechat),
@@ -43864,9 +43957,9 @@ var ProfileService = class {
43864
43957
  key: `public-profiles:${batch.join("|")}`,
43865
43958
  failureCooldownMs: 5e3
43866
43959
  });
43867
- for (const raw of listValue$7(data.items)) {
43960
+ for (const raw of listValue$8(data.items)) {
43868
43961
  const item = objectValue$1(raw);
43869
- const userId = numberValue$6(item.user_id);
43962
+ const userId = numberValue$7(item.user_id);
43870
43963
  if (!batch.includes(userId)) continue;
43871
43964
  const displayName = stringValue$1(item.nick_name ?? item.display_name ?? item.name_slug).trim();
43872
43965
  const nickname = stringValue$1(item.nick_name ?? item.nickname).trim();
@@ -43927,9 +44020,9 @@ var ProfileService = class {
43927
44020
  for (const batch of chunksOf(normalized, 100)) {
43928
44021
  if (batch.length === 0) continue;
43929
44022
  const data = await this.runtime.authenticatedAuthPost("/api/v1/auth/get-public-users-by-ids", { user_ids: batch }, session, signal);
43930
- for (const raw of listValue$7(data.items)) {
44023
+ for (const raw of listValue$8(data.items)) {
43931
44024
  const item = objectValue$1(raw);
43932
- const userId = Math.trunc(numberValue$6(item.user_id));
44025
+ const userId = Math.trunc(numberValue$7(item.user_id));
43933
44026
  if (!batch.includes(userId)) continue;
43934
44027
  let hasAvatar = false;
43935
44028
  const avatarUrl = stringValue$1(item.head_img).trim();
@@ -43968,8 +44061,8 @@ var ProfileService = class {
43968
44061
  }
43969
44062
  const result = {
43970
44063
  version: 1,
43971
- viewerUserId: numberValue$6(parsed.viewerUserId),
43972
- targetUserId: numberValue$6(parsed.targetUserId)
44064
+ viewerUserId: numberValue$7(parsed.viewerUserId),
44065
+ targetUserId: numberValue$7(parsed.targetUserId)
43973
44066
  };
43974
44067
  if (parsed.version !== 1 || result.viewerUserId !== expectedViewerUserId || !Number.isSafeInteger(result.targetUserId) || result.targetUserId <= 0) throw new ArkmePluginError("image-ref-invalid", "Arkme头像引用与当前账号不匹配", false, 403);
43975
44068
  return result;
@@ -44004,6 +44097,330 @@ var ProfileService = class {
44004
44097
  }
44005
44098
  };
44006
44099
  //#endregion
44100
+ //#region src/services/related-quick-note-service.ts
44101
+ const RELATED_QUICK_NOTE_LIMIT = 20;
44102
+ const RELATED_QUICK_NOTE_REF_TTL_MS = 720 * 60 * 1e3;
44103
+ const RELATED_QUICK_NOTE_REF_CAP = 1e3;
44104
+ function numberValue$6(value) {
44105
+ if (typeof value === "number" && Number.isFinite(value)) return value;
44106
+ if (typeof value === "string" && value.trim() !== "") {
44107
+ const parsed = Number(value.trim());
44108
+ if (Number.isFinite(parsed)) return parsed;
44109
+ }
44110
+ return 0;
44111
+ }
44112
+ function firstString(item, keys) {
44113
+ for (const key of keys) {
44114
+ const value = stringValue$1(item[key]).trim();
44115
+ if (value !== "") return value;
44116
+ }
44117
+ return "";
44118
+ }
44119
+ function firstPositiveInteger(item, keys) {
44120
+ for (const key of keys) {
44121
+ const value = Math.trunc(numberValue$6(item[key]));
44122
+ if (Number.isSafeInteger(value) && value > 0) return value;
44123
+ }
44124
+ return 0;
44125
+ }
44126
+ function recordCore(raw) {
44127
+ const item = objectValue$1(raw);
44128
+ return objectValue$1(item.record_core ?? item.recordCore);
44129
+ }
44130
+ function recordUid(raw) {
44131
+ const item = objectValue$1(raw);
44132
+ const core = recordCore(raw);
44133
+ return firstString(item, [
44134
+ "record_uid",
44135
+ "recordUid",
44136
+ "uid",
44137
+ "target_record_uid",
44138
+ "targetRecordUid"
44139
+ ]) || firstString(core, [
44140
+ "record_uid",
44141
+ "recordUid",
44142
+ "uid"
44143
+ ]);
44144
+ }
44145
+ function recordOwnerUserId(raw) {
44146
+ const item = objectValue$1(raw);
44147
+ const core = recordCore(raw);
44148
+ return firstPositiveInteger(item, [
44149
+ "record_owner_user_id",
44150
+ "recordOwnerUserId",
44151
+ "owner_user_id",
44152
+ "ownerUserId"
44153
+ ]) || firstPositiveInteger(core, [
44154
+ "record_owner_user_id",
44155
+ "recordOwnerUserId",
44156
+ "owner_user_id",
44157
+ "ownerUserId"
44158
+ ]);
44159
+ }
44160
+ function matchesRecordIdentity(raw, expectedUid, expectedOwnerUserId) {
44161
+ const uidKeys = [
44162
+ "record_uid",
44163
+ "recordUid",
44164
+ "uid",
44165
+ "target_record_uid",
44166
+ "targetRecordUid"
44167
+ ];
44168
+ const ownerKeys = [
44169
+ "record_owner_user_id",
44170
+ "recordOwnerUserId",
44171
+ "owner_user_id",
44172
+ "ownerUserId"
44173
+ ];
44174
+ const identities = [objectValue$1(raw), recordCore(raw)];
44175
+ const uids = identities.flatMap((item) => uidKeys.map((key) => stringValue$1(item[key]).trim()).filter((value) => value !== ""));
44176
+ const ownerUserIds = identities.flatMap((item) => ownerKeys.map((key) => Math.trunc(numberValue$6(item[key]))).filter((value) => Number.isSafeInteger(value) && value > 0));
44177
+ return uids.length > 0 && ownerUserIds.length > 0 && uids.every((value) => value === expectedUid) && ownerUserIds.every((value) => value === expectedOwnerUserId);
44178
+ }
44179
+ function millisValue(value) {
44180
+ const parsed = Math.trunc(numberValue$6(value));
44181
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) return 0;
44182
+ return parsed > 1e11 ? parsed : parsed * 1e3;
44183
+ }
44184
+ function listValue$7(value) {
44185
+ return Array.isArray(value) ? value : [];
44186
+ }
44187
+ function descriptorFromRaw(raw) {
44188
+ const item = objectValue$1(raw);
44189
+ const core = recordCore(raw);
44190
+ const uid = recordUid(raw);
44191
+ const ownerUserId = recordOwnerUserId(raw);
44192
+ if (uid === "" || ownerUserId <= 0) return void 0;
44193
+ const authorUserId = firstPositiveInteger(item, [
44194
+ "author_user_id",
44195
+ "authorUserId",
44196
+ "creator_user_id",
44197
+ "creatorUserId"
44198
+ ]) || firstPositiveInteger(core, ["creator_user_id", "creatorUserId"]) || ownerUserId;
44199
+ const senderName = firstString(item, [
44200
+ "author_name",
44201
+ "authorName",
44202
+ "author_nickname",
44203
+ "authorNickname",
44204
+ "sender_name",
44205
+ "senderName",
44206
+ "nickname",
44207
+ "nick_name"
44208
+ ]);
44209
+ const title = firstString(item, ["title"]) || firstString(core, ["title"]);
44210
+ const textPreview = firstString(item, [
44211
+ "text_preview",
44212
+ "textPreview",
44213
+ "text",
44214
+ "content",
44215
+ "text_content",
44216
+ "textContent"
44217
+ ]) || firstString(core, ["text_content", "textContent"]);
44218
+ const sendAtMillis = millisValue(item.send_at ?? item.sendAt ?? core.send_at ?? core.sendAt);
44219
+ const sourceLabel = firstString(item, ["source_label", "sourceLabel"]);
44220
+ return {
44221
+ recordUid: uid,
44222
+ recordOwnerUserId: ownerUserId,
44223
+ authorUserId,
44224
+ senderName: senderName.slice(0, 200),
44225
+ sendAtMillis,
44226
+ title: title.slice(0, 500),
44227
+ textPreview: textPreview.slice(0, 2e3),
44228
+ ...sourceLabel === "" ? {} : { sourceLabel: sourceLabel.slice(0, 200) }
44229
+ };
44230
+ }
44231
+ var RelatedQuickNoteService = class {
44232
+ runtime;
44233
+ record;
44234
+ media;
44235
+ profile;
44236
+ privacy;
44237
+ references = /* @__PURE__ */ new Map();
44238
+ constructor(runtime, record, media, profile, privacy = new ArkmePrivacyVisibilityService(runtime)) {
44239
+ this.runtime = runtime;
44240
+ this.record = record;
44241
+ this.media = media;
44242
+ this.profile = profile;
44243
+ this.privacy = privacy;
44244
+ }
44245
+ dispose() {
44246
+ this.references.clear();
44247
+ }
44248
+ async list(locator, signal) {
44249
+ const session = await this.runtime.requireSession();
44250
+ const source = this.validLocator(locator, session.userId);
44251
+ const response = await this.runtime.authenticatedPost("/api/v1/records/related/query", {
44252
+ record_uid: source.recordUid,
44253
+ record_owner_user_id: source.recordOwnerUserId,
44254
+ context_type: source.contextType,
44255
+ ...source.contextType === "chat" ? { chat_session_uid: source.chatSessionUid } : {},
44256
+ limit: RELATED_QUICK_NOTE_LIMIT
44257
+ }, session, signal);
44258
+ const excluded = /* @__PURE__ */ new Set([source.recordUid]);
44259
+ const lockedRecordUids = await this.privacy.lockedRecordUids(session, signal);
44260
+ const directItems = listValue$7(response.items ?? response.records);
44261
+ const candidates = this.relatedCandidates(response, directItems, excluded, lockedRecordUids);
44262
+ const missingUids = candidates.filter((candidate) => descriptorFromRaw(candidate.raw) === void 0).map((candidate) => candidate.uid);
44263
+ const hydratedByUid = /* @__PURE__ */ new Map();
44264
+ if (missingUids.length > 0) {
44265
+ const batch = await this.runtime.authenticatedDataPost("/api/v1/memo/batch-get-records", { record_uids: missingUids }, session, signal);
44266
+ for (const raw of listValue$7(batch.items)) {
44267
+ const uid = recordUid(raw);
44268
+ if (uid !== "" && !hydratedByUid.has(uid)) hydratedByUid.set(uid, raw);
44269
+ }
44270
+ }
44271
+ const descriptors = this.descriptorsFromItems(candidates.flatMap((candidate) => descriptorFromRaw(candidate.raw) === void 0 ? hydratedByUid.has(candidate.uid) ? [hydratedByUid.get(candidate.uid)] : [] : [candidate.raw]), excluded, lockedRecordUids);
44272
+ const profiles = await this.profile.publicProfileSummariesByUserIds(descriptors.map((item) => item.authorUserId), session, signal).catch(() => /* @__PURE__ */ new Map());
44273
+ const items = [];
44274
+ for (const descriptor of descriptors) {
44275
+ const profile = profiles.get(descriptor.authorUserId);
44276
+ const senderName = descriptor.senderName || profile?.displayName || (descriptor.authorUserId === session.userId ? "我" : "Arkme 用户");
44277
+ const senderAvatarRef = profile?.avatarUrl === void 0 ? void 0 : await this.profile.sealProfileImageRef(session.userId, descriptor.authorUserId);
44278
+ const relatedRef = await this.sealReference({
44279
+ viewerUserId: session.userId,
44280
+ sourceRef: source.sourceRef,
44281
+ sourceOwnerRef: source.sourceOwnerRef,
44282
+ recordUid: descriptor.recordUid,
44283
+ recordOwnerUserId: descriptor.recordOwnerUserId,
44284
+ senderName,
44285
+ ...senderAvatarRef === void 0 ? {} : { senderAvatarRef }
44286
+ });
44287
+ items.push({
44288
+ relatedRef,
44289
+ senderName,
44290
+ ...senderAvatarRef === void 0 ? {} : { senderAvatarRef },
44291
+ sendAtMillis: descriptor.sendAtMillis,
44292
+ title: descriptor.title,
44293
+ textPreview: descriptor.textPreview,
44294
+ ...descriptor.sourceLabel === void 0 ? {} : { sourceLabel: descriptor.sourceLabel }
44295
+ });
44296
+ }
44297
+ return {
44298
+ items,
44299
+ total: items.length
44300
+ };
44301
+ }
44302
+ async detail(sourceRef, relatedRef, signal) {
44303
+ const session = await this.runtime.requireSession();
44304
+ const reference = await this.openReference(relatedRef, session.userId, sourceRef.trim());
44305
+ const raw = await this.runtime.authenticatedPost("/api/v1/records/detail", { record_uid: reference.recordUid }, session, signal);
44306
+ if (!matchesRecordIdentity(raw, reference.recordUid, reference.recordOwnerUserId)) throw new ArkmePluginError("related-quick-note-detail-contract-invalid", "相关快记详情响应与所选快记不一致", true, 502);
44307
+ const lockedRecordUids = await this.privacy.lockedRecordUids(session, signal);
44308
+ if (arkmePrivacyLockedRecord(raw) || lockedRecordUids.has(reference.recordUid)) throw new ArkmePluginError("related-quick-note-private", "该相关快记不可查看", false, 403);
44309
+ const hydrated = await this.media.hydrateRecordMediaPage([raw], session, signal);
44310
+ const displayItems = hydrated.displayItemsByRecordUid.get(reference.recordUid);
44311
+ const mediaUnavailable = hydrated.unavailableRecordUids.has(reference.recordUid);
44312
+ const detail = this.record.recordTimelineItemFromRaw(raw, session.userId, {
44313
+ ...displayItems === void 0 ? {} : { displayItems },
44314
+ ...mediaUnavailable ? { mediaUnavailable: true } : {},
44315
+ isMe: reference.recordOwnerUserId === session.userId
44316
+ });
44317
+ if (detail.itemUid !== reference.recordUid) throw new ArkmePluginError("related-quick-note-detail-contract-invalid", "相关快记详情标识无效", true, 502);
44318
+ return {
44319
+ relatedRef,
44320
+ senderName: reference.senderName,
44321
+ ...reference.senderAvatarRef === void 0 ? {} : { avatarRef: reference.senderAvatarRef },
44322
+ isMe: reference.recordOwnerUserId === session.userId,
44323
+ sendAtMillis: detail.sendAtMillis,
44324
+ title: detail.title,
44325
+ textContent: detail.textContent,
44326
+ status: detail.status,
44327
+ ...detail.recordVersion === void 0 ? {} : { recordVersion: detail.recordVersion },
44328
+ ...detail.aiPolish === void 0 ? {} : { aiPolish: detail.aiPolish },
44329
+ ...detail.templateKind === void 0 ? {} : { templateKind: detail.templateKind },
44330
+ ...detail.displayKind === void 0 ? {} : { displayKind: detail.displayKind },
44331
+ ...detail.version === void 0 ? {} : { version: detail.version },
44332
+ ...detail.updateAtMillis === void 0 ? {} : { updateAtMillis: detail.updateAtMillis },
44333
+ ...detail.recordDurationMillis === void 0 ? {} : { recordDurationMillis: detail.recordDurationMillis },
44334
+ ...detail.editDurationMillis === void 0 ? {} : { editDurationMillis: detail.editDurationMillis },
44335
+ ...detail.contentBlocks === void 0 ? {} : { contentBlocks: detail.contentBlocks },
44336
+ ...detail.mediaUnavailable === void 0 ? {} : { mediaUnavailable: detail.mediaUnavailable },
44337
+ ...detail.forwardRecords === void 0 ? {} : { forwardRecords: detail.forwardRecords }
44338
+ };
44339
+ }
44340
+ validLocator(locator, expectedViewerUserId) {
44341
+ const sourceRef = locator.sourceRef.trim();
44342
+ const sourceOwnerRef = locator.sourceOwnerRef.trim();
44343
+ const recordUidValue = locator.recordUid.trim();
44344
+ const chatSessionUid = locator.chatSessionUid.trim();
44345
+ if (locator.viewerUserId !== expectedViewerUserId || sourceRef === "" || sourceOwnerRef === "" || recordUidValue === "" || !Number.isSafeInteger(locator.recordOwnerUserId) || locator.recordOwnerUserId <= 0 || locator.contextType !== "record" && locator.contextType !== "chat" || locator.contextType === "chat" && chatSessionUid === "") throw new ArkmePluginError("related-quick-note-source-invalid", "当前快记来源无效,请刷新后重试", false, 400);
44346
+ return {
44347
+ ...locator,
44348
+ sourceRef,
44349
+ sourceOwnerRef,
44350
+ recordUid: recordUidValue,
44351
+ chatSessionUid: locator.contextType === "chat" ? chatSessionUid : ""
44352
+ };
44353
+ }
44354
+ descriptorsFromItems(rawItems, excluded, lockedRecordUids) {
44355
+ const seen = /* @__PURE__ */ new Set();
44356
+ const result = [];
44357
+ for (const raw of rawItems) {
44358
+ if (result.length >= RELATED_QUICK_NOTE_LIMIT || arkmePrivacyLockedRecord(raw)) continue;
44359
+ const descriptor = descriptorFromRaw(raw);
44360
+ if (descriptor === void 0 || excluded.has(descriptor.recordUid) || lockedRecordUids.has(descriptor.recordUid) || seen.has(descriptor.recordUid)) continue;
44361
+ seen.add(descriptor.recordUid);
44362
+ result.push(descriptor);
44363
+ }
44364
+ return result;
44365
+ }
44366
+ relatedCandidates(response, directItems, excluded, lockedRecordUids) {
44367
+ const rawItems = [...directItems, ...listValue$7(response.similarLs ?? response.similar_ls ?? response.similarRecords ?? response.similar_records)];
44368
+ const blocked = new Set(lockedRecordUids);
44369
+ for (const raw of rawItems) {
44370
+ if (!arkmePrivacyLockedRecord(raw)) continue;
44371
+ const uid = typeof raw === "string" ? raw.trim() : recordUid(raw);
44372
+ if (uid !== "") blocked.add(uid);
44373
+ }
44374
+ const result = [];
44375
+ const seen = /* @__PURE__ */ new Set();
44376
+ for (const raw of rawItems) {
44377
+ const uid = typeof raw === "string" ? raw.trim() : recordUid(raw);
44378
+ if (uid === "" || excluded.has(uid) || blocked.has(uid) || seen.has(uid) || arkmePrivacyLockedRecord(raw)) continue;
44379
+ seen.add(uid);
44380
+ result.push({
44381
+ uid,
44382
+ raw
44383
+ });
44384
+ if (result.length >= RELATED_QUICK_NOTE_LIMIT) break;
44385
+ }
44386
+ return result;
44387
+ }
44388
+ async sealReference(reference) {
44389
+ const now = Date.now();
44390
+ for (const [key, value] of this.references) if (value.expiresAtMillis <= now) this.references.delete(key);
44391
+ while (this.references.size >= RELATED_QUICK_NOTE_REF_CAP) {
44392
+ const oldest = this.references.keys().next().value;
44393
+ if (oldest === void 0) break;
44394
+ this.references.delete(oldest);
44395
+ }
44396
+ const nonce = randomUUID();
44397
+ const signature = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(nonce).digest("base64url");
44398
+ this.references.set(nonce, {
44399
+ ...reference,
44400
+ expiresAtMillis: now + RELATED_QUICK_NOTE_REF_TTL_MS
44401
+ });
44402
+ return `arkme-related-quick-note-v1.${nonce}.${signature}`;
44403
+ }
44404
+ async openReference(relatedRef, expectedViewerUserId, expectedSourceRef) {
44405
+ const parts = relatedRef.trim().split(".");
44406
+ if (parts.length !== 3 || parts[0] !== "arkme-related-quick-note-v1") throw this.invalidReference();
44407
+ const nonce = parts[1] ?? "";
44408
+ const supplied = Buffer.from(parts[2] ?? "", "base64url");
44409
+ const expected = createHmac("sha256", await this.runtime.stateStore.uniqueCode()).update(nonce).digest();
44410
+ if (nonce === "" || supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) throw this.invalidReference();
44411
+ const reference = this.references.get(nonce);
44412
+ if (reference === void 0 || reference.expiresAtMillis <= Date.now()) {
44413
+ this.references.delete(nonce);
44414
+ throw new ArkmePluginError("related-quick-note-ref-expired", "相关快记引用已过期,请刷新后重试", true, 410);
44415
+ }
44416
+ if (reference.viewerUserId !== expectedViewerUserId || reference.sourceRef !== expectedSourceRef) throw this.invalidReference();
44417
+ return reference;
44418
+ }
44419
+ invalidReference() {
44420
+ return new ArkmePluginError("related-quick-note-ref-invalid", "相关快记引用无效,请刷新后重试", false, 403);
44421
+ }
44422
+ };
44423
+ //#endregion
44007
44424
  //#region src/recording-presentation.ts
44008
44425
  function objectValue(value) {
44009
44426
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -47304,6 +47721,31 @@ function integerValue(value) {
47304
47721
  function listValue(value) {
47305
47722
  return Array.isArray(value) ? value : [];
47306
47723
  }
47724
+ function extensionShareRefFromUrl(rawUrl) {
47725
+ try {
47726
+ return new URL(rawUrl).pathname.split("/").at(-1) ?? "";
47727
+ } catch {
47728
+ return "";
47729
+ }
47730
+ }
47731
+ function worldExtensionPublicationShare(rawPublication, rawItem) {
47732
+ const share = objectValue$1(rawPublication.share ?? rawItem.share);
47733
+ const url = stringValue$1(share.url ?? rawPublication.share_url ?? rawItem.share_url).trim();
47734
+ if (url === "" || url.length > 4096) return void 0;
47735
+ const ref = stringValue$1(share.ref ?? rawPublication.share_ref ?? rawItem.share_ref).trim() || extensionShareRefFromUrl(url);
47736
+ if (!/^extshare_[0-9a-f]{32}$/.test(ref)) return void 0;
47737
+ let parsed;
47738
+ try {
47739
+ parsed = new URL(url);
47740
+ } catch {
47741
+ return;
47742
+ }
47743
+ if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || !parsed.pathname.endsWith(`/${ref}`)) return;
47744
+ return {
47745
+ ref,
47746
+ url: parsed.href
47747
+ };
47748
+ }
47307
47749
  function isTopLevelWorldRecord(raw) {
47308
47750
  return stringValue$1(objectValue$1(raw).parent_record_uid).trim() === "";
47309
47751
  }
@@ -48150,6 +48592,10 @@ var WorldService = class {
48150
48592
  version: stringValue$1(rawExtensionPublication.version).trim(),
48151
48593
  name: stringValue$1(rawExtensionPublication.name).trim() || "未命名插件",
48152
48594
  description: stringValue$1(rawExtensionPublication.description).trim(),
48595
+ ...(() => {
48596
+ const share = worldExtensionPublicationShare(rawExtensionPublication, item);
48597
+ return share === void 0 ? {} : { share };
48598
+ })(),
48153
48599
  ...(() => {
48154
48600
  const iconRef = stringValue$1(rawExtensionPublication.icon_ref).trim();
48155
48601
  return iconRef === "" ? {} : { iconRef };
@@ -48431,6 +48877,7 @@ var ArkmeService = class {
48431
48877
  linkMetadata;
48432
48878
  aiPolish;
48433
48879
  chat;
48880
+ relatedQuickNote;
48434
48881
  contact;
48435
48882
  contactDirectory;
48436
48883
  unmarkedSpeaker;
@@ -48483,6 +48930,7 @@ var ArkmeService = class {
48483
48930
  this.botConversation = new BotConversationService(this.runtime, this.bot, this.chat, async () => {
48484
48931
  await this.realtime.invalidateRecordProjection();
48485
48932
  });
48933
+ this.relatedQuickNote = new RelatedQuickNoteService(this.runtime, this.record, this.media, this.profile, this.privacy);
48486
48934
  this.contactDirectory = new ContactDirectoryService(this.runtime, this.source, this.bot, this.profile, this.world, this.chat);
48487
48935
  this.unmarkedSpeaker = new UnmarkedSpeakerService(this.runtime, this.media);
48488
48936
  this.contact = new ContactService(this.runtime, this.source, this.profile, this.realtime);
@@ -48568,6 +49016,7 @@ var ArkmeService = class {
48568
49016
  this.media.dispose();
48569
49017
  this.aiPolish.dispose();
48570
49018
  this.interwoven.dispose();
49019
+ this.relatedQuickNote.dispose();
48571
49020
  this.world.dispose();
48572
49021
  this.arrangement.dispose();
48573
49022
  this.contact.dispose();
@@ -48729,6 +49178,7 @@ var ArkmeService = class {
48729
49178
  forwardContent: true,
48730
49179
  sourceTextSend: true,
48731
49180
  messageReadReceipts: true,
49181
+ messageReport: true,
48732
49182
  richContentRead: this.config.richMediaRenderEnabled !== false,
48733
49183
  richContentSend: this.config.richMediaSendEnabled !== false,
48734
49184
  fileUpload: this.config.richMediaSendEnabled !== false,
@@ -48827,6 +49277,7 @@ var ArkmeService = class {
48827
49277
  this.runtime.dispose();
48828
49278
  this.outgoingCall.dispose();
48829
49279
  this.interwoven.dispose();
49280
+ this.relatedQuickNote.dispose();
48830
49281
  this.world.dispose();
48831
49282
  this.linkMetadata.dispose();
48832
49283
  }
@@ -48995,6 +49446,15 @@ var ArkmeService = class {
48995
49446
  async interwovenMomentDetail(sourceRef, momentRef, signal) {
48996
49447
  return await this.interwoven.interwovenMomentDetail(sourceRef, momentRef, signal);
48997
49448
  }
49449
+ async relatedQuickNotesFromMessage(sourceRef, messageActionRef, signal) {
49450
+ return await this.relatedQuickNote.list(await this.chat.relatedQuickNoteLocator(sourceRef, messageActionRef), signal);
49451
+ }
49452
+ async relatedQuickNotesFromMoment(sourceRef, momentRef, signal) {
49453
+ return await this.relatedQuickNote.list(await this.interwoven.relatedQuickNoteLocator(sourceRef, momentRef, signal), signal);
49454
+ }
49455
+ async relatedQuickNoteDetail(sourceRef, relatedRef, signal) {
49456
+ return await this.relatedQuickNote.detail(sourceRef, relatedRef, signal);
49457
+ }
48998
49458
  async joinDSHBetaCommunity(signal) {
48999
49459
  return await this.community.joinDSHBetaCommunity(signal);
49000
49460
  }
@@ -49444,25 +49904,27 @@ var ArkmeService = class {
49444
49904
  return await this.world.publishWorldFileAssets(input);
49445
49905
  }
49446
49906
  async createText(recordUid, textContent) {
49447
- return await this.record.createText(recordUid, textContent);
49907
+ const result = await this.record.createText(recordUid, textContent);
49908
+ await this.realtime.invalidateRecordProjection();
49909
+ return result;
49448
49910
  }
49449
49911
  async createTextForConversation(recordUid, textContent) {
49450
- return await this.record.createTextForConversation(recordUid, textContent);
49912
+ const result = await this.record.createTextForConversation(recordUid, textContent);
49913
+ if (result.localState !== "failed") await this.realtime.invalidateRecordProjection();
49914
+ return result;
49451
49915
  }
49452
49916
  async createDSHAgentInputText(recordUid, textContent, sendAtMillis) {
49453
49917
  const result = await this.record.createDSHAgentInputText(recordUid, textContent, sendAtMillis);
49454
- this.realtime.emitChatClientEvent({
49455
- type: "projection-invalidated",
49456
- revision: this.realtime.nextChatClientRevision(),
49457
- projection: "record"
49458
- });
49918
+ await this.realtime.invalidateRecordProjection();
49459
49919
  return result;
49460
49920
  }
49461
49921
  async pendingWrites() {
49462
49922
  return await this.record.pendingWrites();
49463
49923
  }
49464
49924
  async retryPending(recordUid) {
49465
- return await this.record.retryPending(recordUid);
49925
+ const result = await this.record.retryPending(recordUid);
49926
+ await this.realtime.invalidateRecordProjection();
49927
+ return result;
49466
49928
  }
49467
49929
  recordUid(raw) {
49468
49930
  return this.record.recordUid(raw);