@senguoyun/dsh-arkme 0.1.40 → 0.1.41

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.
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as inspectNativeBundleArtifactV3, D as bundleSha256, E as arkmeSandboxEntryId, F as assertExtensionArtifactSize, G as validateVoiceprintPcm16Wav, I as canonicalExtensionJson, J as arkmeRichBackgroundSound, K as isRecordingInstantOnOrAfterUnixEpoch, L as sha256Hex, M as packLocalNativeBundleDirectoryV3, N as readNativeBundleTarballV3, O as canonicalBundleJson, P as renderArkmeSandboxHostEntry, R as unpackArkmeExtension, S as ARKME_BUNDLE_ARTIFACT_KIND, T as ArkmeBundleArtifactError, U as ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE, W as ArkmeService, X as ARKME_PROVIDER_CONTRACT_VERSION, Y as ARKME_DEFAULT_SHARE_WEBSITE, _ as arkmeClientContentDigest, at as arkmeNormalizedFileMimeType, b as verifyExtensionResolutionSignature, c as persistentExtensionBundleDirectory, ct as recordingImportFileKind, d as readPersistentExtensionActivation, dt as ArkmePluginError, et as ARKME_WORLD_PUBLISH_MAX_IMAGES, f as refreshPersistentClientWrapper, ft as ArkmeOutgoingCallError, g as activeProfileExtensionOwnerConflicts, h as renderArkmeBundleClientBundle, i as invokePersistentArkmeExtension, it as arkmeCanInlineLocalFile, j as packBundleFiles, k as inspectBundleArtifact, l as persistentExtensionPackageIdentity, lt as sameRecordingImportIdentity, m as writePersistentExtensionActivation, nt as ARKME_RUNTIME_INSTANCE_ID, o as persistentArkmeExtensionRuntimeState, ot as arkmePickedFileKind, p as repairPersistentExtensionInstallation, pt as SecretValue, q as arkmeFileBackgroundSound, r as deactivatePersistentArkmeExtension, rt as ARKME_TOOL_FILE_MAX_BYTES, s as materializePersistentExtensionBundle, st as isArkmeBotAvatarRef, t as activatePersistentArkmeExtension, tt as ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES, u as quarantinePersistentExtension, v as arkmeClientContentDigestFromSource, w as ARKME_NATIVE_BUNDLE_ARTIFACT_KIND, x as verifyNativeBundleResolutionSignature, y as verifyBundleResolutionSignature, z as ARKME_EXTENSION_FORMAT } from "./persistent-runtime-BDvChgZV.js";
1
+ import { A as inspectNativeBundleArtifactV3, D as bundleSha256, E as arkmeSandboxEntryId, F as assertExtensionArtifactSize, G as validateVoiceprintPcm16Wav, I as canonicalExtensionJson, J as arkmeRichBackgroundSound, K as isRecordingInstantOnOrAfterUnixEpoch, L as sha256Hex, M as packLocalNativeBundleDirectoryV3, N as readNativeBundleTarballV3, O as canonicalBundleJson, P as renderArkmeSandboxHostEntry, R as unpackArkmeExtension, S as ARKME_BUNDLE_ARTIFACT_KIND, T as ArkmeBundleArtifactError, U as ARKME_EXTENSION_RUNTIME_UNAVAILABLE_MESSAGE, W as ArkmeService, X as ARKME_PROVIDER_CONTRACT_VERSION, Y as ARKME_DEFAULT_SHARE_WEBSITE, _ as arkmeClientContentDigest, at as arkmeNormalizedFileMimeType, b as verifyExtensionResolutionSignature, c as persistentExtensionBundleDirectory, ct as recordingImportFileKind, d as readPersistentExtensionActivation, dt as ArkmePluginError, et as ARKME_WORLD_PUBLISH_MAX_IMAGES, f as refreshPersistentClientWrapper, ft as ArkmeOutgoingCallError, g as activeProfileExtensionOwnerConflicts, h as renderArkmeBundleClientBundle, i as invokePersistentArkmeExtension, it as arkmeCanInlineLocalFile, j as packBundleFiles, k as inspectBundleArtifact, l as persistentExtensionPackageIdentity, lt as sameRecordingImportIdentity, m as writePersistentExtensionActivation, nt as ARKME_RUNTIME_INSTANCE_ID, o as persistentArkmeExtensionRuntimeState, ot as arkmePickedFileKind, p as repairPersistentExtensionInstallation, pt as SecretValue, q as arkmeFileBackgroundSound, r as deactivatePersistentArkmeExtension, rt as ARKME_TOOL_FILE_MAX_BYTES, s as materializePersistentExtensionBundle, st as isArkmeBotAvatarRef, t as activatePersistentArkmeExtension, tt as ARKME_WORLD_PUBLISH_MAX_IMAGE_BYTES, u as quarantinePersistentExtension, v as arkmeClientContentDigestFromSource, w as ARKME_NATIVE_BUNDLE_ARTIFACT_KIND, x as verifyNativeBundleResolutionSignature, y as verifyBundleResolutionSignature, z as ARKME_EXTENSION_FORMAT } from "./persistent-runtime-B1gJH2Nx.js";
2
2
  import { a as securePrivateDirectory, c as securePrivateFileSync, i as prepareProfilePackageManager, n as isArkmeProfilePackageName, o as securePrivateDirectorySync, r as localExtensionPnpmArgs, s as securePrivateFile, t as applyArkmeProfileBundlePolicy } from "./profile-bundle-policy-CvojZuTD.js";
3
3
  import { _ as parsePluginUpdateManifest, b as validatePluginUpdateServiceOrigin, d as writePluginUpdateLifecycleLog, f as PluginUpdateInstallStateStore, g as existingCachedPluginArtifactPath, h as downloadAndCachePluginArtifact, m as PluginUpdateArtifactError, n as buildTargetInstallArgs, o as reconcilePluginUpdaterProfilePolicy, p as ARKME_PLUGIN_PACKAGE_NAME$1, t as assertTargetArtifactIntegrity, u as detachManagedProfilePluginLink, v as pluginUpdateEndpointUrl, y as validatePluginUpdateArtifactOrigin } from "./plugin-updater-helper-BByw0ZyG.js";
4
4
  import { i as invokeArkmeBundle, n as arkmeBundleActive, r as deactivateArkmeBundle } from "./bundle-runtime-BTzRlc6V.js";
@@ -26,6 +26,7 @@ import { Buffer as Buffer$1 } from "node:buffer";
26
26
  import sharp from "sharp";
27
27
  import { AttachmentError, AttachmentId } from "@deepseek-ai/dsh-attachment";
28
28
  import WebSocket from "ws";
29
+ import { scheduler } from "node:timers/promises";
29
30
  //#region src/openclaw/cli-adapter.ts
30
31
  const JOTMO_CHANNEL_PACKAGE = "@jotmo/openclaw-channel@0.1.12";
31
32
  function assertSucceeded(result, operation) {
@@ -2061,8 +2062,7 @@ function createArkmeHostApi(service, options) {
2061
2062
  "recordings.import.retry",
2062
2063
  "recordings.import.cancel",
2063
2064
  "recordings.speaker.assign-item",
2064
- "openapi.mcp.retry",
2065
- "openapi.mcp.reauthorize"
2065
+ "openapi.mcp.retry"
2066
2066
  ].includes(request.operation) && origin === void 0) throw new ArkmePluginError("origin-required", "该敏感变更必须从当前 DSH 页面发起", false, 403);
2067
2067
  writeJson$5(res, 200, {
2068
2068
  ok: true,
@@ -4488,7 +4488,8 @@ var ManagedOpenApiMcpController = class {
4488
4488
  }
4489
4489
  guardToolExecution(toolName) {
4490
4490
  const ownsManagedNamespace = this.mounted !== void 0 || this.mountTransition !== void 0;
4491
- if (!toolName.startsWith(MANAGED_TOOL_PREFIX$1) || !ownsManagedNamespace || this.toolExecutionEnabled) return void 0;
4491
+ if (!toolName.startsWith(MANAGED_TOOL_PREFIX$1) || !ownsManagedNamespace) return void 0;
4492
+ if (this.mounted !== void 0 && this.executionMatches(this.mounted)) return void 0;
4492
4493
  return "Arkme OpenAPI MCP tools are not ready for the current account";
4493
4494
  }
4494
4495
  /** Fences both sides of an official MCP dispatch and diagnoses managed failures in the background. */
@@ -4583,12 +4584,15 @@ var ManagedOpenApiMcpController = class {
4583
4584
  const abort = new AbortController();
4584
4585
  this.activeAbort = abort;
4585
4586
  const progress = { phase: "credential" };
4586
- if (!background && !this.toolExecutionEnabled) this.setStatus({
4587
+ const activeCredentialExpired = this.activeCredential !== void 0 && this.activeCredential.expiresAtMillis <= this.now();
4588
+ if (activeCredentialExpired) this.disableToolExecution();
4589
+ if ((!background || activeCredentialExpired) && !this.toolExecutionEnabled) this.setStatus({
4587
4590
  state: "reconciling",
4588
4591
  retryable: false,
4589
4592
  userAction: "none"
4590
4593
  });
4591
4594
  try {
4595
+ if (activeCredentialExpired) await this.disposeMount();
4592
4596
  const session = await this.options.sessionStore.read();
4593
4597
  const principal = openApiMcpPrincipal(session);
4594
4598
  if (session === void 0) {
@@ -4724,7 +4728,7 @@ var ManagedOpenApiMcpController = class {
4724
4728
  session = await this.options.sessionStore.read();
4725
4729
  } catch {
4726
4730
  this.scheduleCredential(credentialRetryDelay(this.credentialFailures), operationEpoch);
4727
- this.setDegradedStatus();
4731
+ this.setStatusFromExecutionAvailability();
4728
4732
  return;
4729
4733
  }
4730
4734
  if (session === void 0) {
@@ -4736,14 +4740,13 @@ var ManagedOpenApiMcpController = class {
4736
4740
  return;
4737
4741
  }
4738
4742
  this.scheduleCredential(credentialRetryDelay(this.credentialFailures), operationEpoch);
4739
- this.setDegradedStatus();
4743
+ this.setStatusFromExecutionAvailability();
4740
4744
  }
4741
4745
  async handleManifestFailure(error, operationEpoch) {
4742
4746
  this.manifestFailures++;
4743
4747
  this.warn(failureCategory(error));
4744
4748
  this.scheduleManifest(manifestRetryDelay(this.manifestFailures), operationEpoch);
4745
- if (this.toolExecutionEnabled && this.mounted !== void 0) this.setReadyStatus();
4746
- else this.setDegradedStatus();
4749
+ this.setStatusFromExecutionAvailability();
4747
4750
  }
4748
4751
  scheduleDiagnosticReconcile(mounted) {
4749
4752
  if (this.disposed || this.diagnosticPending || !this.executionMatches(mounted)) return;
@@ -4839,7 +4842,8 @@ var ManagedOpenApiMcpController = class {
4839
4842
  this.toolExecutionAborts.clear();
4840
4843
  }
4841
4844
  executionMatches(mounted) {
4842
- return !this.disposed && this.toolExecutionEnabled && this.mounted === mounted && samePrincipal(this.activePrincipal, mounted.principal);
4845
+ const credential = this.activeCredential;
4846
+ return !this.disposed && credential !== void 0 && credential.expiresAtMillis > this.now() && credential.keyId === mounted.keyId && credential.generation === mounted.generation && credentialBelongsTo(credential, mounted.principal) && this.toolExecutionEnabled && this.mounted === mounted && samePrincipal(this.activePrincipal, mounted.principal);
4843
4847
  }
4844
4848
  mountMatchesCredential(credential, principal) {
4845
4849
  return this.mounted !== void 0 && this.mounted.keyId === credential.keyId && this.mounted.generation === credential.generation && samePrincipal(this.mounted.principal, principal);
@@ -4902,7 +4906,10 @@ var ManagedOpenApiMcpController = class {
4902
4906
  scheduleCredential(delayMillis, operationEpoch) {
4903
4907
  if (this.credentialTimer !== void 0) clearTimeout(this.credentialTimer);
4904
4908
  const jittered = this.jitter(delayMillis, 10080 * 6e4);
4905
- const next = this.now() + jittered;
4909
+ const scheduledAt = this.now();
4910
+ const remainingValidity = this.activeCredential === void 0 ? void 0 : this.activeCredential.expiresAtMillis - scheduledAt;
4911
+ const boundedDelay = remainingValidity !== void 0 && remainingValidity > 0 ? Math.min(jittered, remainingValidity) : jittered;
4912
+ const next = scheduledAt + boundedDelay;
4906
4913
  this.credentialNextAtMillis = next;
4907
4914
  this.credentialTimer = setTimeout(() => {
4908
4915
  this.credentialTimer = void 0;
@@ -4910,7 +4917,7 @@ var ManagedOpenApiMcpController = class {
4910
4917
  if (!this.disposed && operationEpoch === this.epoch) this.enqueue(async () => {
4911
4918
  await this.reconcileCredential(operationEpoch);
4912
4919
  });
4913
- }, jittered);
4920
+ }, boundedDelay);
4914
4921
  this.credentialTimer.unref();
4915
4922
  return next;
4916
4923
  }
@@ -4954,6 +4961,11 @@ var ManagedOpenApiMcpController = class {
4954
4961
  ...next === void 0 ? {} : { nextReconcileAtMillis: next }
4955
4962
  });
4956
4963
  }
4964
+ setStatusFromExecutionAvailability() {
4965
+ const mounted = this.mounted;
4966
+ if (mounted !== void 0 && this.executionMatches(mounted)) this.setReadyStatus();
4967
+ else this.setDegradedStatus();
4968
+ }
4957
4969
  cancelCurrent(quarantineTools) {
4958
4970
  this.epoch++;
4959
4971
  this.activeAbort?.abort(/* @__PURE__ */ new Error("OpenAPI MCP lifecycle superseded"));
@@ -5365,24 +5377,31 @@ function formatArkmeIdMutationResult(result) {
5365
5377
  }
5366
5378
  //#endregion
5367
5379
  //#region src/openapi-mcp/status-tool.ts
5380
+ function modelSafeStatus(status) {
5381
+ return JSON.stringify({
5382
+ state: status.state,
5383
+ retryable: status.retryable,
5384
+ userAction: status.userAction
5385
+ });
5386
+ }
5368
5387
  function registerOpenApiMcpLifecycleTools(ctx, controller) {
5369
5388
  ctx.tools.register(defineTool({
5370
5389
  name: "arkme_openapi_mcp_status",
5371
- description: "Check whether managed OpenAPI MCP tools are available for the current Arkme account and whether login is required.",
5390
+ description: "Report the managed Arkme OpenAPI MCP connection for the current account. state is inactive, reconciling, ready, or degraded; ready means the current mcp__arkme__* tools are mounted and executable. userAction is none or login. Returns no credentials or account identifiers.",
5372
5391
  parameters: {},
5373
5392
  output: TEXT_OUTPUT$3,
5374
5393
  isConcurrencySafe: () => true,
5375
5394
  execute() {
5376
- return Promise.resolve(JSON.stringify(controller.status()));
5395
+ return Promise.resolve(modelSafeStatus(controller.status()));
5377
5396
  }
5378
5397
  }));
5379
5398
  ctx.tools.register(defineTool({
5380
5399
  name: "arkme_openapi_mcp_retry",
5381
- description: "Retry the managed OpenAPI MCP connection for the current Arkme account and return its safe lifecycle status.",
5400
+ description: "Retry the managed Arkme OpenAPI MCP connection when state is degraded and retryable is true. userAction is none or login. Returns no credentials, account identifiers, or scheduler details.",
5382
5401
  parameters: {},
5383
5402
  output: TEXT_OUTPUT$3,
5384
5403
  execute() {
5385
- return controller.retry().then((status) => JSON.stringify(status));
5404
+ return controller.retry().then(modelSafeStatus);
5386
5405
  }
5387
5406
  }));
5388
5407
  }
@@ -20070,6 +20089,7 @@ function extractCompletedTurnWindows(source) {
20070
20089
  //#endregion
20071
20090
  //#region src/dsh-remote/host.ts
20072
20091
  const HISTORY_RECONCILE_INTERVAL_MILLIS = 3e4;
20092
+ const HISTORY_RECONCILE_SESSION_BATCH_ITEMS = 4;
20073
20093
  const PROJECTION_SYNC_INTERVAL_MILLIS = 3e4;
20074
20094
  const BACKEND_HISTORY_BATCH_ITEMS = 100;
20075
20095
  const BACKEND_TURN_BATCH_ITEMS = 100;
@@ -20789,19 +20809,17 @@ var ArkmeRemoteRealtimeHost = class {
20789
20809
  return false;
20790
20810
  }
20791
20811
  }
20792
- scheduleHistoryReconcile(delayMillis = 0) {
20812
+ scheduleHistoryReconcile(delayMillis = HISTORY_RECONCILE_INTERVAL_MILLIS) {
20813
+ if (this.options.eagerHistoryBackfill !== true) return;
20793
20814
  const runtime = this.runtime;
20794
20815
  if (!this.started || this.accountId === void 0 || runtime?.desktopRef === void 0) return;
20795
20816
  const capabilities = new Set(runtime.capabilities);
20796
20817
  if (!capabilities.has("session.list") || !capabilities.has("session.history")) return;
20797
- if (delayMillis === 0 && this.historyReconcileFlight !== void 0) {
20818
+ if (this.historyReconcileFlight !== void 0) {
20798
20819
  this.historyReconcileRequested = true;
20799
20820
  return;
20800
20821
  }
20801
- if (this.historyReconcileTimer !== void 0) {
20802
- if (delayMillis > 0) return;
20803
- clearTimeout(this.historyReconcileTimer);
20804
- }
20822
+ if (this.historyReconcileTimer !== void 0) return;
20805
20823
  this.historyReconcileTimer = setTimeout(() => {
20806
20824
  this.historyReconcileTimer = void 0;
20807
20825
  this.reconcileAllHistorySafely();
@@ -20837,6 +20855,9 @@ var ArkmeRemoteRealtimeHost = class {
20837
20855
  historyOwnerMatches(accountId, runtime) {
20838
20856
  return this.started && this.accountId === accountId && this.runtime?.runtimeRef === runtime.runtimeRef && this.runtime.hostGeneration === runtime.hostGeneration;
20839
20857
  }
20858
+ async yieldHistoryReconciliation() {
20859
+ await (this.options.yieldToEventLoop?.() ?? scheduler.yield());
20860
+ }
20840
20861
  async reconcileAllHistory() {
20841
20862
  const accountId = this.accountId;
20842
20863
  const runtime = this.runtime;
@@ -20857,6 +20878,7 @@ var ArkmeRemoteRealtimeHost = class {
20857
20878
  if (cursor !== void 0) {
20858
20879
  if (seenSessionCursors.has(cursor)) throw new DshRemoteError("REMOTE_INVALID_RESPONSE", "DSH 会话游标发生循环", true);
20859
20880
  seenSessionCursors.add(cursor);
20881
+ await this.yieldHistoryReconciliation();
20860
20882
  }
20861
20883
  } while (cursor !== void 0);
20862
20884
  const statuses = /* @__PURE__ */ new Map();
@@ -20873,18 +20895,24 @@ var ArkmeRemoteRealtimeHost = class {
20873
20895
  if (status === void 0) throw new DshRemoteError("REMOTE_INVALID_RESPONSE", "Backend 缺少会话历史完整性条目", true);
20874
20896
  statuses.set(ref, status);
20875
20897
  }
20898
+ if (offset + 50 < sessions.length) await this.yieldHistoryReconciliation();
20876
20899
  }
20877
- let firstFailure;
20878
- for (const session of sessions) {
20879
- if (!this.historyOwnerMatches(accountId, runtime)) return;
20900
+ const pendingSessions = sessions.filter((session) => {
20880
20901
  const status = statuses.get(session.sessionId);
20881
- if (status === void 0) continue;
20882
- if (session.projectionAsOfSeq !== void 0 && status.historyCompleteThroughSeq >= session.projectionAsOfSeq && (this.options.controlPlane.syncSessionTurns === void 0 || status.turnProjectionCompleteThroughSeq >= session.projectionAsOfSeq)) continue;
20883
- try {
20884
- await this.reconcileSessionHistory(accountId, runtime, session.sessionId, session.projectionAsOfSeq);
20885
- } catch (error) {
20886
- firstFailure ??= error;
20902
+ if (status === void 0) return false;
20903
+ return session.projectionAsOfSeq === void 0 || status.historyCompleteThroughSeq < session.projectionAsOfSeq || this.options.controlPlane.syncSessionTurns !== void 0 && status.turnProjectionCompleteThroughSeq < session.projectionAsOfSeq;
20904
+ });
20905
+ let firstFailure;
20906
+ for (let offset = 0; offset < pendingSessions.length; offset += HISTORY_RECONCILE_SESSION_BATCH_ITEMS) {
20907
+ for (const session of pendingSessions.slice(offset, offset + HISTORY_RECONCILE_SESSION_BATCH_ITEMS)) {
20908
+ if (!this.historyOwnerMatches(accountId, runtime)) return;
20909
+ try {
20910
+ await this.reconcileSessionHistory(accountId, runtime, session.sessionId, session.projectionAsOfSeq);
20911
+ } catch (error) {
20912
+ firstFailure ??= error;
20913
+ }
20887
20914
  }
20915
+ if (offset + HISTORY_RECONCILE_SESSION_BATCH_ITEMS < pendingSessions.length) await this.yieldHistoryReconciliation();
20888
20916
  }
20889
20917
  if (firstFailure !== void 0) throw firstFailure;
20890
20918
  }
@@ -20920,6 +20948,7 @@ var ArkmeRemoteRealtimeHost = class {
20920
20948
  const next = page.nextCursor;
20921
20949
  if (next === void 0 || next < 0 || seenCursors.has(next)) throw new DshRemoteError("REMOTE_INVALID_RESPONSE", "DSH 历史游标无效或发生循环", true);
20922
20950
  seenCursors.add(next);
20951
+ await this.yieldHistoryReconciliation();
20923
20952
  beforeSeq = next;
20924
20953
  }
20925
20954
  const throughSeq = completeThroughSeq ?? maximumEntrySeq;
@@ -21002,7 +21031,6 @@ var ArkmeRemoteRealtimeHost = class {
21002
21031
  desktopRef
21003
21032
  };
21004
21033
  await this.syncProjectionSnapshot(true);
21005
- this.scheduleHistoryReconcile();
21006
21034
  const controller = new AbortController();
21007
21035
  await this.options.realtime.connect({
21008
21036
  profileRef: this.options.profileRef,
@@ -1,2 +1,2 @@
1
- import { a as persistentArkmeExtensionActive, i as invokePersistentArkmeExtension, n as applyPersistentArkmeHostExtension, o as persistentArkmeExtensionRuntimeState, r as deactivatePersistentArkmeExtension, t as activatePersistentArkmeExtension } from "./persistent-runtime-BDvChgZV.js";
1
+ import { a as persistentArkmeExtensionActive, i as invokePersistentArkmeExtension, n as applyPersistentArkmeHostExtension, o as persistentArkmeExtensionRuntimeState, r as deactivatePersistentArkmeExtension, t as activatePersistentArkmeExtension } from "./persistent-runtime-B1gJH2Nx.js";
2
2
  export { activatePersistentArkmeExtension, applyPersistentArkmeHostExtension, deactivatePersistentArkmeExtension, invokePersistentArkmeExtension, persistentArkmeExtensionActive, persistentArkmeExtensionRuntimeState };
@@ -19376,6 +19376,57 @@ function chatExtensionTreeItemFromData(value, fallbackParentRecordUid) {
19376
19376
  ...structuredContent === void 0 ? {} : { structuredContent }
19377
19377
  };
19378
19378
  }
19379
+ function extensionMediaRecord(recordUid, contentPayload, displayItems = []) {
19380
+ return {
19381
+ record_uid: recordUid,
19382
+ content_payload: contentPayload,
19383
+ ...displayItems.length === 0 ? {} : { media_display_items: displayItems }
19384
+ };
19385
+ }
19386
+ function publicExtensionMediaRecord(value, recordUid) {
19387
+ const data = objectValue$1(value);
19388
+ const core = objectValue$1(data.record_core ?? data.recordCore);
19389
+ const contentPayload = objectValue$1(data.content_payload ?? data.contentPayload ?? core.content_payload ?? core.contentPayload);
19390
+ const mediaItems = listValue$16(data.media_items ?? data.mediaItems ?? core.media_items ?? core.mediaItems);
19391
+ const mediaRefs = listValue$16(contentPayload.media_refs ?? contentPayload.mediaRefs);
19392
+ const displayItems = [
19393
+ ...listValue$16(data.media_display_items ?? data.mediaDisplayItems),
19394
+ ...listValue$16(core.media_display_items ?? core.mediaDisplayItems),
19395
+ ...mediaItems
19396
+ ];
19397
+ return extensionMediaRecord(recordUid, {
19398
+ ...contentPayload,
19399
+ ...mediaRefs.length > 0 || mediaItems.length === 0 ? {} : { media_refs: mediaItems }
19400
+ }, displayItems);
19401
+ }
19402
+ function chatExtensionMediaRecord(value, recordUid) {
19403
+ const child = objectValue$1(value);
19404
+ const item = objectValue$1(child.item);
19405
+ const record = objectValue$1(item.record);
19406
+ const payload = objectValue$1(record.payload);
19407
+ return extensionMediaRecord(recordUid, objectValue$1(payload.content_payload ?? payload.contentPayload ?? record.content_payload ?? record.contentPayload), [
19408
+ ...listValue$16(child.media_display_items ?? child.mediaDisplayItems),
19409
+ ...listValue$16(item.media_display_items ?? item.mediaDisplayItems),
19410
+ ...listValue$16(record.media_display_items ?? record.mediaDisplayItems),
19411
+ ...listValue$16(payload.media_display_items ?? payload.mediaDisplayItems)
19412
+ ]);
19413
+ }
19414
+ function uploadedExtensionMediaRecord(recordUid, assets) {
19415
+ return extensionMediaRecord(recordUid, {
19416
+ payload_kind: 2,
19417
+ schema_version: 1,
19418
+ media_refs: assets.map((asset, index) => ({
19419
+ file_asset_uid: asset.fileAssetUid,
19420
+ content_file_role: 1,
19421
+ render_role: 1,
19422
+ sort_order: index,
19423
+ file_name: asset.fileName,
19424
+ file_kind: asset.fileKind,
19425
+ mime_type: asset.mimeType,
19426
+ size: asset.size
19427
+ }))
19428
+ });
19429
+ }
19379
19430
  function messageCopyLinkRecordContextFromData(data) {
19380
19431
  const context = objectValue$1(data.record_context ?? data.recordContext);
19381
19432
  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);
@@ -19671,6 +19722,66 @@ var ChatService = class {
19671
19722
  this.privacy = privacy;
19672
19723
  this.messageActions = messageActions;
19673
19724
  }
19725
+ async hydrateExtensionMedia(projections, session, signal) {
19726
+ const mediaProjections = projections.filter(({ extension }) => extension.mediaItems.length > 0);
19727
+ if (mediaProjections.length === 0) return projections.map(({ extension }) => extension);
19728
+ let directlyHydrated;
19729
+ try {
19730
+ directlyHydrated = new Map(mediaProjections.map(({ extension, mediaRecord }) => [extension.recordUid, this.media.richContentBlocks(mediaRecord, session.userId)]));
19731
+ } catch (error) {
19732
+ if (signal?.aborted === true) throw error;
19733
+ const mediaRecordUids = new Set(mediaProjections.map(({ extension }) => extension.recordUid));
19734
+ return projections.map(({ extension }) => mediaRecordUids.has(extension.recordUid) ? {
19735
+ ...extension,
19736
+ mediaUnavailable: true
19737
+ } : extension);
19738
+ }
19739
+ const unresolved = mediaProjections.filter(({ extension }) => (directlyHydrated.get(extension.recordUid)?.length ?? 0) === 0);
19740
+ if (unresolved.length === 0) return projections.map(({ extension }) => {
19741
+ const contentBlocks = directlyHydrated.get(extension.recordUid);
19742
+ return contentBlocks === void 0 ? extension : {
19743
+ ...extension,
19744
+ contentBlocks
19745
+ };
19746
+ });
19747
+ try {
19748
+ const media = await this.media.hydrateRecordMediaPage(unresolved.map(({ mediaRecord }) => mediaRecord), session, signal);
19749
+ const hydratedByRecordUid = new Map(mediaProjections.map(({ extension, mediaRecord }) => {
19750
+ const directContentBlocks = directlyHydrated.get(extension.recordUid) ?? [];
19751
+ const displayItems = directContentBlocks.length > 0 ? [] : media.displayItemsByRecordUid.get(extension.recordUid) ?? [];
19752
+ const contentBlocks = directContentBlocks.length > 0 ? directContentBlocks : this.media.richContentBlocks(mediaRecord, session.userId, displayItems);
19753
+ return [extension.recordUid, {
19754
+ ...extension,
19755
+ ...contentBlocks.length === 0 ? { mediaUnavailable: true } : { contentBlocks }
19756
+ }];
19757
+ }));
19758
+ return projections.map(({ extension }) => hydratedByRecordUid.get(extension.recordUid) ?? extension);
19759
+ } catch (error) {
19760
+ if (signal?.aborted === true) throw error;
19761
+ const mediaRecordUids = new Set(mediaProjections.map(({ extension }) => extension.recordUid));
19762
+ return projections.map(({ extension }) => {
19763
+ const contentBlocks = directlyHydrated.get(extension.recordUid) ?? [];
19764
+ if (contentBlocks.length > 0) return {
19765
+ ...extension,
19766
+ contentBlocks
19767
+ };
19768
+ return mediaRecordUids.has(extension.recordUid) ? {
19769
+ ...extension,
19770
+ mediaUnavailable: true
19771
+ } : extension;
19772
+ });
19773
+ }
19774
+ }
19775
+ async hydrateUploadedExtension(extension, assets, session, signal) {
19776
+ if (assets.length === 0) return extension;
19777
+ return (await this.hydrateExtensionMedia([{
19778
+ extension,
19779
+ mediaRecord: uploadedExtensionMediaRecord(extension.recordUid, assets)
19780
+ }], session, signal))[0] ?? {
19781
+ ...extension,
19782
+ mediaUnavailable: true
19783
+ };
19784
+ }
19674
19785
  async readDirectBotConversation(botId, chatSessionUid, options = {}) {
19675
19786
  const normalizedBotId = botId.trim();
19676
19787
  const normalizedSessionUid = chatSessionUid.trim();
@@ -20460,7 +20571,14 @@ var ChatService = class {
20460
20571
  });
20461
20572
  const rawItems = listValue$16(data.list ?? data.items);
20462
20573
  if (offset === 0) total = Math.max(0, integerLikeValue(data.total));
20463
- const pageItems = rawItems.map((raw) => messageCopyLinkExtensionItemFromData(raw)).filter((item) => item !== void 0);
20574
+ const pageProjections = rawItems.flatMap((raw) => {
20575
+ const extension = messageCopyLinkExtensionItemFromData(raw);
20576
+ return extension === void 0 ? [] : [{
20577
+ extension,
20578
+ mediaRecord: publicExtensionMediaRecord(raw, extension.recordUid)
20579
+ }];
20580
+ });
20581
+ const pageItems = await this.hydrateExtensionMedia(pageProjections, session, signal);
20464
20582
  extensions.push(...pageItems);
20465
20583
  if (rawItems.length === 0) break;
20466
20584
  offset += rawItems.length;
@@ -20493,7 +20611,14 @@ var ChatService = class {
20493
20611
  parent_record_uid: parentRecordUid,
20494
20612
  limit: 100
20495
20613
  }, session, signal);
20496
- const extensions = listValue$16(data.children).map((child) => chatExtensionTreeItemFromData(child, parentRecordUid)).filter((item) => item !== void 0).sort((left, right) => right.sendAtMillis - left.sendAtMillis);
20614
+ const projections = listValue$16(data.children).flatMap((child) => {
20615
+ const extension = chatExtensionTreeItemFromData(child, parentRecordUid);
20616
+ return extension === void 0 ? [] : [{
20617
+ extension,
20618
+ mediaRecord: chatExtensionMediaRecord(child, extension.recordUid)
20619
+ }];
20620
+ });
20621
+ const extensions = (await this.hydrateExtensionMedia(projections, session, signal)).sort((left, right) => right.sendAtMillis - left.sendAtMillis);
20497
20622
  const extensionCount = Math.max(integerLikeValue(data.total), extensions.length);
20498
20623
  return extensionCount <= 0 ? void 0 : {
20499
20624
  extensionCount,
@@ -20578,6 +20703,26 @@ var ChatService = class {
20578
20703
  const createdRecordUid = stringValue$1(data.child_record_uid).trim() || normalizedUid;
20579
20704
  const sequence = numberValue$19(data.seq);
20580
20705
  this.realtime.scheduleChatSessionProjection(reference.chatSessionUid, sequence);
20706
+ const extension = await this.hydrateUploadedExtension({
20707
+ recordUid: createdRecordUid,
20708
+ parentRecordUid: reference.recordUid,
20709
+ recordOwnerUserId: session.userId,
20710
+ level: extensionLevel,
20711
+ sourceKind: "record_extension",
20712
+ senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20713
+ ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20714
+ title: "",
20715
+ textContent: normalizedText,
20716
+ sendAtMillis,
20717
+ templateKind: assets.length === 0 ? 1 : 2,
20718
+ displayKind: 0,
20719
+ officialMark: 0,
20720
+ mediaItems: assets.map((asset) => ({
20721
+ fileKind: asset.fileKind,
20722
+ fileName: asset.fileName,
20723
+ size: asset.size
20724
+ }))
20725
+ }, assets, session, options.signal);
20581
20726
  return {
20582
20727
  recordUid: createdRecordUid,
20583
20728
  parentRecordUid: reference.recordUid,
@@ -20585,26 +20730,7 @@ var ChatService = class {
20585
20730
  ...sequence > 0 ? { sequence } : {},
20586
20731
  status: 1,
20587
20732
  localState: "synced",
20588
- extension: {
20589
- recordUid: createdRecordUid,
20590
- parentRecordUid: reference.recordUid,
20591
- recordOwnerUserId: session.userId,
20592
- level: extensionLevel,
20593
- sourceKind: "record_extension",
20594
- senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20595
- ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20596
- title: "",
20597
- textContent: normalizedText,
20598
- sendAtMillis,
20599
- templateKind: assets.length === 0 ? 1 : 2,
20600
- displayKind: 0,
20601
- officialMark: 0,
20602
- mediaItems: assets.map((asset) => ({
20603
- fileKind: asset.fileKind,
20604
- fileName: asset.fileName,
20605
- size: asset.size
20606
- }))
20607
- }
20733
+ extension
20608
20734
  };
20609
20735
  }
20610
20736
  if (source.kind === "topic") {
@@ -20648,32 +20774,33 @@ var ChatService = class {
20648
20774
  revision: this.realtime.nextChatClientRevision(),
20649
20775
  projection: "record"
20650
20776
  });
20777
+ const extension = await this.hydrateUploadedExtension({
20778
+ recordUid: createdRecordUid,
20779
+ parentRecordUid: reference.recordUid,
20780
+ recordOwnerUserId: session.userId,
20781
+ level: extensionLevel,
20782
+ sourceKind: "record_extension",
20783
+ senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20784
+ ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20785
+ title: "",
20786
+ textContent: normalizedText,
20787
+ sendAtMillis,
20788
+ templateKind: assets.length === 0 ? 1 : 2,
20789
+ displayKind: 0,
20790
+ officialMark: 0,
20791
+ mediaItems: assets.map((asset) => ({
20792
+ fileKind: asset.fileKind,
20793
+ fileName: asset.fileName,
20794
+ size: asset.size
20795
+ }))
20796
+ }, assets, session, options.signal);
20651
20797
  return {
20652
20798
  recordUid: createdRecordUid,
20653
20799
  parentRecordUid: createdParentUid,
20654
20800
  ...relationUid === "" ? {} : { relationUid },
20655
20801
  status: numberValue$19(data.record_status ?? data.status),
20656
20802
  localState: "synced",
20657
- extension: {
20658
- recordUid: createdRecordUid,
20659
- parentRecordUid: reference.recordUid,
20660
- recordOwnerUserId: session.userId,
20661
- level: extensionLevel,
20662
- sourceKind: "record_extension",
20663
- senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20664
- ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20665
- title: "",
20666
- textContent: normalizedText,
20667
- sendAtMillis,
20668
- templateKind: assets.length === 0 ? 1 : 2,
20669
- displayKind: 0,
20670
- officialMark: 0,
20671
- mediaItems: assets.map((asset) => ({
20672
- fileKind: asset.fileKind,
20673
- fileName: asset.fileName,
20674
- size: asset.size
20675
- }))
20676
- }
20803
+ extension
20677
20804
  };
20678
20805
  }
20679
20806
  if (source.kind === "send_to_self" || source.kind === "default_category") {
@@ -20687,32 +20814,33 @@ var ChatService = class {
20687
20814
  revision: this.realtime.nextChatClientRevision(),
20688
20815
  projection: "record"
20689
20816
  });
20817
+ const extension = await this.hydrateUploadedExtension({
20818
+ recordUid: recordResult.recordUid,
20819
+ parentRecordUid: reference.recordUid,
20820
+ recordOwnerUserId: session.userId,
20821
+ level: extensionLevel,
20822
+ sourceKind: "record_extension",
20823
+ senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20824
+ ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20825
+ title: "",
20826
+ textContent: normalizedText,
20827
+ sendAtMillis,
20828
+ templateKind: assets.length === 0 ? 1 : 2,
20829
+ displayKind: 0,
20830
+ officialMark: 0,
20831
+ mediaItems: assets.map((asset) => ({
20832
+ fileKind: asset.fileKind,
20833
+ fileName: asset.fileName,
20834
+ size: asset.size
20835
+ }))
20836
+ }, assets, session, options.signal);
20690
20837
  return {
20691
20838
  recordUid: recordResult.recordUid,
20692
20839
  parentRecordUid: reference.recordUid,
20693
20840
  status: recordResult.status,
20694
20841
  localState: recordResult.localState,
20695
20842
  ...recordResult.error === void 0 ? {} : { error: recordResult.error },
20696
- extension: {
20697
- recordUid: recordResult.recordUid,
20698
- parentRecordUid: reference.recordUid,
20699
- recordOwnerUserId: session.userId,
20700
- level: extensionLevel,
20701
- sourceKind: "record_extension",
20702
- senderDisplayName: profile.nickname.trim() || profile.displayName.trim() || "我",
20703
- ...profile.avatarRef.trim() === "" ? {} : { senderAvatarUrl: profile.avatarRef.trim() },
20704
- title: "",
20705
- textContent: normalizedText,
20706
- sendAtMillis,
20707
- templateKind: assets.length === 0 ? 1 : 2,
20708
- displayKind: 0,
20709
- officialMark: 0,
20710
- mediaItems: assets.map((asset) => ({
20711
- fileKind: asset.fileKind,
20712
- fileName: asset.fileName,
20713
- size: asset.size
20714
- }))
20715
- }
20843
+ extension
20716
20844
  };
20717
20845
  }
20718
20846
  const existingExtension = (await this.loadMessageCopyLinkPublicExtensions([reference.recordUid], session, options.signal, { strict: true }))?.extensions.find((extension) => extension.recordUid === normalizedUid);
@@ -20792,29 +20920,30 @@ var ChatService = class {
20792
20920
  revision: this.realtime.nextChatClientRevision(),
20793
20921
  projection: "record"
20794
20922
  });
20923
+ const extension = await this.hydrateUploadedExtension({
20924
+ recordUid: publishedRecordUid,
20925
+ level: 2,
20926
+ sourceKind: "record_extension",
20927
+ senderDisplayName,
20928
+ ...senderAvatarUrl === "" ? {} : { senderAvatarUrl },
20929
+ title: "",
20930
+ textContent: normalizedText,
20931
+ sendAtMillis,
20932
+ templateKind: assets.length === 0 ? 1 : 2,
20933
+ displayKind: 0,
20934
+ officialMark: 0,
20935
+ mediaItems: assets.map((asset) => ({
20936
+ fileKind: asset.fileKind,
20937
+ fileName: asset.fileName,
20938
+ size: asset.size
20939
+ }))
20940
+ }, assets, session, options.signal);
20795
20941
  return {
20796
20942
  recordUid: publishedRecordUid,
20797
20943
  parentRecordUid: reference.recordUid,
20798
20944
  status: numberValue$19(data.status ?? data.check_status ?? createdRecordStatus),
20799
20945
  localState: "synced",
20800
- extension: {
20801
- recordUid: publishedRecordUid,
20802
- level: 2,
20803
- sourceKind: "record_extension",
20804
- senderDisplayName,
20805
- ...senderAvatarUrl === "" ? {} : { senderAvatarUrl },
20806
- title: "",
20807
- textContent: normalizedText,
20808
- sendAtMillis,
20809
- templateKind: assets.length === 0 ? 1 : 2,
20810
- displayKind: 0,
20811
- officialMark: 0,
20812
- mediaItems: assets.map((asset) => ({
20813
- fileKind: asset.fileKind,
20814
- fileName: asset.fileName,
20815
- size: asset.size
20816
- }))
20817
- }
20946
+ extension
20818
20947
  };
20819
20948
  }
20820
20949
  async extendMessageCopyLink(sid, itemIndex, textContent, recordUid, options = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"ArkmeNoteDetails.d.ts","sourceRoot":"","sources":["../../../src/client/ArkmeNoteDetails.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAMV,8BAA8B,EAE9B,iBAAiB,EAClB,MAAM,aAAa,CAAA;AA0WpB,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAE7E;AA6ID,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,OAAO,GACxH,EAAE;IACD,IAAI,EAAE,iBAAiB,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,8BAA8B,KAAK,IAAI,CAAA;IAClE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,+BAmMA;AAcD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,sCAiDvG"}
1
+ {"version":3,"file":"ArkmeNoteDetails.d.ts","sourceRoot":"","sources":["../../../src/client/ArkmeNoteDetails.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAMV,8BAA8B,EAE9B,iBAAiB,EAClB,MAAM,aAAa,CAAA;AAyWpB,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAE7E;AA6ID,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,OAAO,GACxH,EAAE;IACD,IAAI,EAAE,iBAAiB,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,8BAA8B,KAAK,IAAI,CAAA;IAClE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,+BAmMA;AAcD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,sCAiDvG"}