@senguoyun/dsh-arkme 0.1.72 → 0.1.74

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 (33) hide show
  1. package/docs/quick-add-hover-regression-2026-09-21.md +11 -0
  2. package/lib/client.js +678 -1843
  3. package/lib/client.js.map +1 -1
  4. package/lib/harness-native-selection-client.js +5 -3
  5. package/lib/index.js +6 -43
  6. package/lib/types/client/ArkmeArkoSurface.d.ts +31 -4
  7. package/lib/types/client/ArkmeArkoSurface.d.ts.map +1 -1
  8. package/lib/types/client/ArkmeDshMenu.d.ts.map +1 -1
  9. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  10. package/lib/types/client/ArkmeQuickAdd.d.ts.map +1 -1
  11. package/lib/types/harness-embed-route.d.ts +0 -1
  12. package/lib/types/harness-embed-route.d.ts.map +1 -1
  13. package/lib/types/index.d.ts.map +1 -1
  14. package/package.json +5 -5
  15. package/lib/arko-native-client.js +0 -6077
  16. package/lib/types/client/ArkmeArkoPanel.d.ts +0 -5
  17. package/lib/types/client/ArkmeArkoPanel.d.ts.map +0 -1
  18. package/lib/types/client/ArkoNativeSurface.d.ts +0 -27
  19. package/lib/types/client/ArkoNativeSurface.d.ts.map +0 -1
  20. package/lib/types/client/arko-conversation-contract.d.ts +0 -47
  21. package/lib/types/client/arko-conversation-contract.d.ts.map +0 -1
  22. package/lib/types/client/arko-native-client.d.ts +0 -19
  23. package/lib/types/client/arko-native-client.d.ts.map +0 -1
  24. package/lib/types/client/arko-native-composer.d.ts +0 -31
  25. package/lib/types/client/arko-native-composer.d.ts.map +0 -1
  26. package/lib/types/client/arko-native-emoji-picker.d.ts +0 -17
  27. package/lib/types/client/arko-native-emoji-picker.d.ts.map +0 -1
  28. package/lib/types/client/arko-native-frame.d.ts +0 -3
  29. package/lib/types/client/arko-native-frame.d.ts.map +0 -1
  30. package/lib/types/client/arko-native-transport.d.ts +0 -20
  31. package/lib/types/client/arko-native-transport.d.ts.map +0 -1
  32. package/lib/types/client/useArkoConversationController.d.ts +0 -57
  33. package/lib/types/client/useArkoConversationController.d.ts.map +0 -1
package/lib/client.js CHANGED
@@ -1979,7 +1979,7 @@ window.__ModuleLoader__.load({
1979
1979
  }
1980
1980
  //#endregion
1981
1981
  //#region src/avatar-diagnostics.ts
1982
- function record$1(value) {
1982
+ function record(value) {
1983
1983
  return value !== null && typeof value === "object" ? value : {};
1984
1984
  }
1985
1985
  function positiveId(value) {
@@ -1995,7 +1995,7 @@ window.__ModuleLoader__.load({
1995
1995
  const encoded = parts[1] ?? "";
1996
1996
  if (parts.length !== 3 || encoded.length > 1024 || !/^[A-Za-z0-9_-]+$/.test(encoded)) return context;
1997
1997
  const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
1998
- const payload = record$1(JSON.parse(globalThis.atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "="))));
1998
+ const payload = record(JSON.parse(globalThis.atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "="))));
1999
1999
  if (payload.version !== 1) return context;
2000
2000
  const viewer = positiveId(payload.viewerUserId);
2001
2001
  const target = positiveId(payload.targetUserId);
@@ -2025,8 +2025,8 @@ window.__ModuleLoader__.load({
2025
2025
  }
2026
2026
  function logArkmeAvatarDiagnostic(event, context, error) {
2027
2027
  try {
2028
- const source = record$1(error);
2029
- const body = record$1(source.body);
2028
+ const source = record(error);
2029
+ const body = record(source.body);
2030
2030
  const detail = source.name === "ArkmeClientError" ? body : source;
2031
2031
  const errorName = [
2032
2032
  "Error",
@@ -2046,7 +2046,7 @@ window.__ModuleLoader__.load({
2046
2046
  retryable: typeof detail.retryable === "boolean" ? detail.retryable : void 0,
2047
2047
  httpStatus: httpStatus(source.httpStatus),
2048
2048
  upstreamStatus: httpStatus(source.upstreamStatus) ?? imageDownloadStatus(source),
2049
- causeCode: safeErrorCode(record$1(source.cause).code)
2049
+ causeCode: safeErrorCode(record(source.cause).code)
2050
2050
  } }
2051
2051
  })}`);
2052
2052
  } catch {}
@@ -15537,7 +15537,7 @@ function(property) {
15537
15537
  * @returns {Check}
15538
15538
  */
15539
15539
  function(test) {
15540
- if (test === null || test === void 0) return ok$1;
15540
+ if (test === null || test === void 0) return ok;
15541
15541
  if (typeof test === "function") return castFactory(test);
15542
15542
  if (typeof test === "object") return Array.isArray(test) ? anyFactory(test) : propertiesFactory(test);
15543
15543
  if (typeof test === "string") return typeFactory(test);
@@ -15615,7 +15615,7 @@ function(test) {
15615
15615
  return Boolean(looksLikeANode(value) && testFunction.call(this, value, typeof index === "number" ? index : void 0, parent || void 0));
15616
15616
  }
15617
15617
  }
15618
- function ok$1() {
15618
+ function ok() {
15619
15619
  return true;
15620
15620
  }
15621
15621
  /**
@@ -110836,7 +110836,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
110836
110836
  else scheduleClose();
110837
110837
  };
110838
110838
  const leave = (event) => {
110839
- if (event.relatedTarget === null) close();
110839
+ if (event.relatedTarget === null) scheduleClose();
110840
110840
  };
110841
110841
  const observer = new MutationObserver(() => {
110842
110842
  if (!anchor.isConnected) close();
@@ -110846,14 +110846,16 @@ Please report this to https://github.com/markedjs/marked.`, e) {
110846
110846
  subtree: true
110847
110847
  });
110848
110848
  doc.addEventListener("pointermove", move, true);
110849
+ doc.addEventListener("pointerover", move, true);
110849
110850
  doc.addEventListener("pointerout", leave, true);
110850
- doc.defaultView?.addEventListener("blur", close);
110851
+ doc.defaultView?.addEventListener("blur", scheduleClose);
110851
110852
  return () => {
110852
110853
  cancel();
110853
110854
  observer.disconnect();
110854
110855
  doc.removeEventListener("pointermove", move, true);
110856
+ doc.removeEventListener("pointerover", move, true);
110855
110857
  doc.removeEventListener("pointerout", leave, true);
110856
- doc.defaultView?.removeEventListener("blur", close);
110858
+ doc.defaultView?.removeEventListener("blur", scheduleClose);
110857
110859
  };
110858
110860
  }, [
110859
110861
  open,
@@ -146351,7 +146353,7 @@ body:has([data-arkme-self-topic-menu]) > [role="menu"] { z-index: 10030; }
146351
146353
  * Wrap `React.useLayoutEffect` which will not throw warning message in test env
146352
146354
  */
146353
146355
  const useInternalLayoutEffect = canUseDom() ? react.useLayoutEffect : react.useEffect;
146354
- const useLayoutEffect$22 = (callback, deps) => {
146356
+ const useLayoutEffect$21 = (callback, deps) => {
146355
146357
  const firstMountRef = react.useRef(true);
146356
146358
  useInternalLayoutEffect(() => {
146357
146359
  return callback(firstMountRef.current);
@@ -146395,7 +146397,7 @@ body:has([data-arkme-self-topic-menu]) > [role="menu"] { z-index: 10030; }
146395
146397
  function useControlledState(defaultStateValue, value) {
146396
146398
  const [innerValue, setInnerValue] = (0, react.useState)(defaultStateValue);
146397
146399
  const mergedValue = value !== void 0 ? value : innerValue;
146398
- useLayoutEffect$22((mount) => {
146400
+ useLayoutEffect$21((mount) => {
146399
146401
  if (!mount) setInnerValue(value);
146400
146402
  }, [value]);
146401
146403
  return [mergedValue, setInnerValue];
@@ -146458,7 +146460,7 @@ body:has([data-arkme-self-topic-menu]) > [role="menu"] { z-index: 10030; }
146458
146460
  };
146459
146461
  //#endregion
146460
146462
  //#region node_modules/@rc-component/util/es/hooks/useMemo.js
146461
- function useMemo$32(getValue, condition, shouldUpdate) {
146463
+ function useMemo$31(getValue, condition, shouldUpdate) {
146462
146464
  const cacheRef = react.useRef({});
146463
146465
  if (!("value" in cacheRef.current) || shouldUpdate(cacheRef.current.condition, condition)) {
146464
146466
  cacheRef.current.value = getValue();
@@ -146571,7 +146573,7 @@ body:has([data-arkme-self-topic-menu]) > [role="menu"] { z-index: 10030; }
146571
146573
  };
146572
146574
  };
146573
146575
  const useComposeRef = (...refs) => {
146574
- return useMemo$32(() => composeRef(...refs), refs, (prev, next) => prev.length !== next.length || prev.every((ref, i) => ref !== next[i]));
146576
+ return useMemo$31(() => composeRef(...refs), refs, (prev, next) => prev.length !== next.length || prev.every((ref, i) => ref !== next[i]));
146575
146577
  };
146576
146578
  const supportRef = (nodeOrComponent) => {
146577
146579
  if (!nodeOrComponent) return false;
@@ -147461,13 +147463,13 @@ ${height ? `height: ${webkitScrollbarStyle.height};` : ""}
147461
147463
  ele.parentElement?.removeChild(ele);
147462
147464
  appendedRef.current = false;
147463
147465
  }
147464
- useLayoutEffect$22(() => {
147466
+ useLayoutEffect$21(() => {
147465
147467
  if (render) if (queueCreate) queueCreate(append);
147466
147468
  else append();
147467
147469
  else cleanup();
147468
147470
  return cleanup;
147469
147471
  }, [render]);
147470
- useLayoutEffect$22(() => {
147472
+ useLayoutEffect$21(() => {
147471
147473
  if (queue.length) {
147472
147474
  queue.forEach((appendFn) => appendFn());
147473
147475
  setQueue(EMPTY_LIST);
@@ -147493,7 +147495,7 @@ ${height ? `height: ${webkitScrollbarStyle.height};` : ""}
147493
147495
  uuid$1 += 1;
147494
147496
  return `${UNIQUE_ID}_${uuid$1}`;
147495
147497
  });
147496
- useLayoutEffect$22(() => {
147498
+ useLayoutEffect$21(() => {
147497
147499
  if (mergedLock) {
147498
147500
  const scrollbarSize = getTargetScrollBarSize(document.body).width;
147499
147501
  updateCSS(`
@@ -148436,7 +148438,7 @@ html body {
148436
148438
  ]);
148437
148439
  const getPopupContainerNeedParams = getPopupContainer?.length > 0;
148438
148440
  const [show, setShow] = react.useState(!getPopupContainer || !getPopupContainerNeedParams);
148439
- useLayoutEffect$22(() => {
148441
+ useLayoutEffect$21(() => {
148440
148442
  if (!show && getPopupContainerNeedParams && target) setShow(true);
148441
148443
  }, [
148442
148444
  show,
@@ -148988,8 +148990,8 @@ html body {
148988
148990
  ready: false
148989
148991
  }));
148990
148992
  };
148991
- useLayoutEffect$22(resetReady, [placement]);
148992
- useLayoutEffect$22(() => {
148993
+ useLayoutEffect$21(resetReady, [placement]);
148994
+ useLayoutEffect$21(() => {
148993
148995
  if (!open) resetReady();
148994
148996
  }, [open]);
148995
148997
  return [
@@ -149033,7 +149035,7 @@ html body {
149033
149035
  //#endregion
149034
149036
  //#region node_modules/@rc-component/trigger/es/hooks/useWatch.js
149035
149037
  function useWatch(open, target, popup, onAlign, onScroll) {
149036
- useLayoutEffect$22(() => {
149038
+ useLayoutEffect$21(() => {
149037
149039
  if (open && target && popup) {
149038
149040
  const targetElement = target;
149039
149041
  const popupElement = popup;
@@ -149188,7 +149190,7 @@ html body {
149188
149190
  id,
149189
149191
  onEsc
149190
149192
  }));
149191
- useLayoutEffect$22(() => {
149193
+ useLayoutEffect$21(() => {
149192
149194
  if (uniqueContext && unique && targetEle && !openUncontrolled && !parentContext) if (mergedOpen) uniqueContext.show(getUniqueOptions(mouseEnterDelay), isOpen);
149193
149195
  else uniqueContext.hide(mouseLeaveDelay);
149194
149196
  }, [mergedOpen, targetEle]);
@@ -149224,7 +149226,7 @@ html body {
149224
149226
  if (top) triggerOpen(false);
149225
149227
  }
149226
149228
  const [inMotion, setInMotion] = react.useState(false);
149227
- useLayoutEffect$22((firstMount) => {
149229
+ useLayoutEffect$21((firstMount) => {
149228
149230
  if (!firstMount || mergedOpen) setInMotion(true);
149229
149231
  }, [mergedOpen]);
149230
149232
  const [motionPrepareResolve, setMotionPrepareResolve] = react.useState(null);
@@ -149243,10 +149245,10 @@ html body {
149243
149245
  if (openRef.current && alignPoint && clickToHide) triggerOpen(false);
149244
149246
  };
149245
149247
  useWatch(mergedOpen, targetEle, popupEle, triggerAlign, onScroll);
149246
- useLayoutEffect$22(() => {
149248
+ useLayoutEffect$21(() => {
149247
149249
  triggerAlign();
149248
149250
  }, [mousePos, popupPlacement]);
149249
- useLayoutEffect$22(() => {
149251
+ useLayoutEffect$21(() => {
149250
149252
  if (mergedOpen && !builtinPlacements?.[popupPlacement]) triggerAlign();
149251
149253
  }, [JSON.stringify(popupAlign)]);
149252
149254
  const alignedClassName = react.useMemo(() => {
@@ -149286,7 +149288,7 @@ html body {
149286
149288
  syncTargetSize();
149287
149289
  setMotionPrepareResolve(() => resolve);
149288
149290
  });
149289
- useLayoutEffect$22(() => {
149291
+ useLayoutEffect$21(() => {
149290
149292
  if (motionPrepareResolve) {
149291
149293
  onAlign();
149292
149294
  motionPrepareResolve();
@@ -149493,7 +149495,7 @@ html body {
149493
149495
  }
149494
149496
  function useTarget(target, open, gap, scrollIntoViewOptions, inlineMode, placeholderRef) {
149495
149497
  const [targetElement, setTargetElement] = (0, react.useState)(void 0);
149496
- useLayoutEffect$22(() => {
149498
+ useLayoutEffect$21(() => {
149497
149499
  const nextElement = typeof target === "function" ? target() : target;
149498
149500
  setTargetElement(nextElement || null);
149499
149501
  });
@@ -149523,7 +149525,7 @@ html body {
149523
149525
  } else setPosInfo(null);
149524
149526
  });
149525
149527
  const getGapOffset = (index) => (Array.isArray(gap?.offset) ? gap?.offset[index] : gap?.offset) ?? 6;
149526
- useLayoutEffect$22(() => {
149528
+ useLayoutEffect$21(() => {
149527
149529
  updatePos();
149528
149530
  window.addEventListener("resize", updatePos);
149529
149531
  window.addEventListener("scroll", updatePos);
@@ -149813,7 +149815,7 @@ html body {
149813
149815
  const mergedOpen = mergedCurrent < 0 || mergedCurrent >= steps.length ? false : internalOpen ?? true;
149814
149816
  const [hasOpened, setHasOpened] = react.useState(mergedOpen);
149815
149817
  const openRef = react.useRef(mergedOpen);
149816
- useLayoutEffect$22(() => {
149818
+ useLayoutEffect$21(() => {
149817
149819
  if (mergedOpen) {
149818
149820
  if (!openRef.current) setMergedCurrent(0);
149819
149821
  setHasOpened(true);
@@ -149830,7 +149832,7 @@ html body {
149830
149832
  const mergedPlacement = getPlacement(targetElement, placement, stepPlacement);
149831
149833
  const mergedArrow = targetElement ? typeof stepArrow === "undefined" ? arrow : stepArrow : false;
149832
149834
  const arrowPointAtCenter = typeof mergedArrow === "object" ? mergedArrow.pointAtCenter : false;
149833
- useLayoutEffect$22(() => {
149835
+ useLayoutEffect$21(() => {
149834
149836
  triggerRef.current?.forceAlign();
149835
149837
  }, [arrowPointAtCenter, mergedCurrent]);
149836
149838
  const onInternalChange = (nextCurrent) => {
@@ -149868,7 +149870,7 @@ html body {
149868
149870
  return;
149869
149871
  }
149870
149872
  });
149871
- useLayoutEffect$22(() => {
149873
+ useLayoutEffect$21(() => {
149872
149874
  if (!mergedOpen) return;
149873
149875
  window.addEventListener("keydown", keyboardHandler);
149874
149876
  return () => {
@@ -153135,7 +153137,7 @@ html body {
153135
153137
  anchor,
153136
153138
  align: "end",
153137
153139
  onClose,
153138
- hoverCloseDelayMs: 250,
153140
+ hoverCloseDelayMs: 500,
153139
153141
  ...getAnchorRect === void 0 ? {} : { getAnchorRect },
153140
153142
  ...hoverAnchor === void 0 ? {} : { hoverAnchor },
153141
153143
  actions: [
@@ -153184,7 +153186,9 @@ html body {
153184
153186
  }
153185
153187
  function ArkmeQuickAddButton({ onContactAdd, onSourceCreated, onBotCreated, onNewDshSession, notificationActivationRevision = 0, onBlockingOverlayChange }) {
153186
153188
  useArkmeLocale();
153187
- const [menuOpen, setMenuOpen] = (0, react$1.useState)(false);
153189
+ const [menuMode, setMenuMode] = (0, react$1.useState)();
153190
+ const menuOpen = menuMode !== void 0;
153191
+ const closeMenu = () => setMenuMode(void 0);
153188
153192
  const [menuError, setMenuError] = (0, react$1.useState)("");
153189
153193
  const [dialogKind, setDialogKind] = (0, react$1.useState)();
153190
153194
  const [dialogBusy, setDialogBusy] = (0, react$1.useState)(false);
@@ -153207,7 +153211,7 @@ html body {
153207
153211
  if (notificationRevisionRef.current === notificationActivationRevision) return;
153208
153212
  notificationRevisionRef.current = notificationActivationRevision;
153209
153213
  pendingNotificationDismissRef.current = true;
153210
- setMenuOpen(false);
153214
+ closeMenu();
153211
153215
  if (!dialogBusy) {
153212
153216
  pendingNotificationDismissRef.current = false;
153213
153217
  setDialogKind(void 0);
@@ -153219,18 +153223,18 @@ html body {
153219
153223
  setDialogKind(void 0);
153220
153224
  }, [dialogBusy]);
153221
153225
  const chooseDialog = (kind) => {
153222
- setMenuOpen(false);
153226
+ closeMenu();
153223
153227
  setDialogBusy(false);
153224
153228
  setDialogKind(kind);
153225
153229
  };
153226
153230
  const openMenu = () => {
153227
153231
  setMenuError("");
153228
- setMenuOpen(true);
153232
+ setMenuMode((current) => current ?? "hover");
153229
153233
  };
153230
153234
  const menu = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeQuickAddMenu, {
153231
153235
  open: menuOpen,
153232
- onClose: () => setMenuOpen(false),
153233
- hoverAnchor: triggerRef.current ?? void 0,
153236
+ onClose: closeMenu,
153237
+ hoverAnchor: menuMode === "hover" ? triggerRef.current ?? void 0 : void 0,
153234
153238
  anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
153235
153239
  "data-arkme-feedback": "neutral",
153236
153240
  ref: triggerRef,
@@ -153243,9 +153247,9 @@ html body {
153243
153247
  onPointerEnter: (event) => {
153244
153248
  if (event?.pointerType !== "touch") openMenu();
153245
153249
  },
153246
- onClick: (event) => {
153247
- if ((event?.detail ?? 0) === 0) setMenuOpen((open) => !open);
153248
- else openMenu();
153250
+ onClick: () => {
153251
+ setMenuError("");
153252
+ setMenuMode("pinned");
153249
153253
  },
153250
153254
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {
153251
153255
  size: 20,
@@ -153257,13 +153261,13 @@ html body {
153257
153261
  try {
153258
153262
  onNewDshSession();
153259
153263
  setMenuError("");
153260
- setMenuOpen(false);
153264
+ closeMenu();
153261
153265
  } catch (error) {
153262
153266
  setMenuError(error instanceof Error ? error.message : "暂时无法新建 DSH 会话,请稍后再试");
153263
153267
  }
153264
153268
  },
153265
153269
  onContactAdd: () => {
153266
- setMenuOpen(false);
153270
+ closeMenu();
153267
153271
  onContactAdd();
153268
153272
  },
153269
153273
  onCreateGroup: () => {
@@ -163486,1642 +163490,6 @@ html body {
163486
163490
  });
163487
163491
  }
163488
163492
  //#endregion
163489
- //#region src/client/arko-native-transport.ts
163490
- function record(value) {
163491
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
163492
- }
163493
- function requestOf(payload) {
163494
- return record(record(record(payload).args).request);
163495
- }
163496
- function messageKey(message) {
163497
- return message.presentationKey ?? (message.messageId === void 0 ? message.id : `arko:${String(message.sessionId ?? "")}:${String(message.messageId)}`);
163498
- }
163499
- function renderContent(message) {
163500
- return [...message.reasoning ? [{
163501
- type: "reasoning",
163502
- text: message.reasoning
163503
- }] : [], {
163504
- type: "text",
163505
- text: message.text
163506
- }];
163507
- }
163508
- function endReason(message) {
163509
- if (message.status === "error" || message.runStatus === "failed") return {
163510
- kind: "error",
163511
- error: {
163512
- code: "ARKO_RUN_FAILED",
163513
- message: message.text || "Arko 运行失败"
163514
- }
163515
- };
163516
- if (message.runStatus === "cancelled") return {
163517
- kind: "aborted",
163518
- reason: { kind: "user" }
163519
- };
163520
- if (message.runStatus === "waiting_user") return { kind: "blocked" };
163521
- if (message.runStatus === "expired" || message.runStatus === "partial") return { kind: "interrupted" };
163522
- return { kind: "completed" };
163523
- }
163524
- const ok = (value) => ({
163525
- ok: true,
163526
- value
163527
- });
163528
- const denied = (message, code = "gateway/bad-request") => ({
163529
- ok: false,
163530
- error: {
163531
- code,
163532
- message,
163533
- details: {}
163534
- }
163535
- });
163536
- /**
163537
- * Render-only protocol adapter. Snapshots and admitted actions remain owned by
163538
- * ArkmeArkoSurface; this object owns only DSH cursor/identity presentation data.
163539
- * Unsupported calls never reach the host transport or any Arkme API.
163540
- */
163541
- function createArkoNativeTransport(initial, initialActions) {
163542
- let snapshot = initial;
163543
- let actions = initialActions;
163544
- const accountKey = initial.accountKey;
163545
- const sessionId = `arko-native-${crypto.randomUUID()}`;
163546
- const createdAt = Date.now();
163547
- let disposed = false;
163548
- let historyRevision = 0;
163549
- let submitting = false;
163550
- let changingModel = false;
163551
- let pendingPrompt;
163552
- const listeners = /* @__PURE__ */ new Set();
163553
- const streams = /* @__PURE__ */ new Set();
163554
- const sequenceOrigin = 2 ** 40;
163555
- let nextSeq = sequenceOrigin;
163556
- let pageRequest;
163557
- let journal = [];
163558
- let projected = [];
163559
- let turn = 0;
163560
- let assistantRevision = 0;
163561
- let activeAssistant;
163562
- function emit(endpoint, value) {
163563
- for (const stream of streams) if (stream.endpoint === endpoint && (endpoint !== "session/follow" || stream.historyRevision === historyRevision)) stream.push(value);
163564
- }
163565
- function append(type, data, message, surfaceOp) {
163566
- const event = {
163567
- type,
163568
- seq: nextSeq++,
163569
- time: Math.max(0, Math.trunc(message.createdAtMillis ?? createdAt)),
163570
- data,
163571
- ...surfaceOp === void 0 ? {} : { surfaceOp },
163572
- ...type.startsWith("arko/") ? { ignorable: true } : {}
163573
- };
163574
- const entry = {
163575
- type: "event",
163576
- event
163577
- };
163578
- journal.push(entry);
163579
- emit("session/follow", entry);
163580
- return event.seq;
163581
- }
163582
- function appendMessage(row) {
163583
- const { message, key, rpcId } = row;
163584
- const source = message.role === "user" ? {
163585
- kind: "user",
163586
- ...rpcId === void 0 ? {} : { rpcId }
163587
- } : message.role === "divider" ? {
163588
- kind: "plugin",
163589
- plugin: "arko",
163590
- form: "notice",
163591
- summary: message.text
163592
- } : {
163593
- kind: "model",
163594
- provider: "arko",
163595
- model: "unspecified"
163596
- };
163597
- const wire = {
163598
- id: key,
163599
- role: message.role === "assistant" ? "assistant" : "user",
163600
- source,
163601
- content: renderContent(message)
163602
- };
163603
- row.seq = append(message.role === "assistant" ? "assistant/message" : "user/message", message.role === "assistant" ? {
163604
- turn: row.turn,
163605
- step: 1,
163606
- message: wire,
163607
- stream: activeAssistant?.attemptId === key ? activeAssistant.stream.slice() : [],
163608
- ...[
163609
- "cancelled",
163610
- "partial",
163611
- "expired"
163612
- ].includes(message.runStatus ?? "") ? { interrupted: true } : {}
163613
- } : wire, message, "append");
163614
- }
163615
- function assistantFrame(frame) {
163616
- emit("session/follow", {
163617
- type: "assistant-stream",
163618
- frame: {
163619
- ...frame,
163620
- revision: ++assistantRevision
163621
- }
163622
- });
163623
- }
163624
- function assistantChunk(chunk, message) {
163625
- if (activeAssistant === void 0) return;
163626
- const time = Math.max(0, Math.trunc(message.createdAtMillis ?? createdAt));
163627
- const index = activeAssistant.nextIndex++;
163628
- activeAssistant.stream.push({
163629
- type: "chunk",
163630
- time,
163631
- chunk
163632
- });
163633
- assistantFrame({
163634
- type: "chunk",
163635
- attemptId: activeAssistant.attemptId,
163636
- index,
163637
- time,
163638
- chunk
163639
- });
163640
- }
163641
- function streamMessage(row, previous) {
163642
- if (activeAssistant?.attemptId !== row.key) {
163643
- activeAssistant = {
163644
- attemptId: row.key,
163645
- startedAfterSeq: nextSeq - 1,
163646
- turn: row.turn,
163647
- step: 1,
163648
- nextIndex: 0,
163649
- stream: []
163650
- };
163651
- assistantFrame({
163652
- type: "start",
163653
- attemptId: row.key,
163654
- startedAfterSeq: activeAssistant.startedAfterSeq,
163655
- turn: row.turn,
163656
- step: 1
163657
- });
163658
- }
163659
- for (const [index, kind] of ["reasoning", "text"].entries()) {
163660
- const text = kind === "reasoning" ? row.message.reasoning ?? "" : row.message.text;
163661
- const before = previous === void 0 ? void 0 : kind === "reasoning" ? previous.reasoning ?? "" : previous.text;
163662
- if (before === void 0 || !text.startsWith(before)) assistantChunk({
163663
- type: "block-start",
163664
- index,
163665
- blockType: kind
163666
- }, row.message);
163667
- const delta = before !== void 0 && text.startsWith(before) ? text.slice(before.length) : text;
163668
- if (delta !== "") assistantChunk({
163669
- type: `${kind}-delta`,
163670
- index,
163671
- text: delta
163672
- }, row.message);
163673
- }
163674
- }
163675
- function settleAssistant(row) {
163676
- appendMessage(row);
163677
- if (activeAssistant?.attemptId === row.key) {
163678
- assistantFrame({
163679
- type: "end",
163680
- attemptId: row.key,
163681
- index: activeAssistant.nextIndex,
163682
- outcome: {
163683
- kind: "committed",
163684
- eventType: "assistant/message",
163685
- seq: row.seq
163686
- }
163687
- });
163688
- activeAssistant = void 0;
163689
- }
163690
- }
163691
- function endMessage(row) {
163692
- if (row.message.role !== "assistant" || row.message.status === "sending" || row.ended) return;
163693
- append("step/end", {
163694
- turn: row.turn,
163695
- step: 1
163696
- }, row.message);
163697
- append("turn/end", {
163698
- turn: row.turn,
163699
- reason: endReason(row.message)
163700
- }, row.message);
163701
- row.ended = true;
163702
- }
163703
- function addMessage(message) {
163704
- if (message.role === "user" || turn === 0 || message.role === "assistant" && projected.at(-1)?.ended === true) {
163705
- turn++;
163706
- append("turn/start", { turn }, message);
163707
- append("step/start", {
163708
- turn,
163709
- step: 1
163710
- }, message);
163711
- }
163712
- const row = {
163713
- message,
163714
- key: messageKey(message),
163715
- seq: -1,
163716
- turn,
163717
- ended: false
163718
- };
163719
- if (message.role === "user" && pendingPrompt !== void 0 && message.text.trim() === pendingPrompt.text) {
163720
- row.rpcId = pendingPrompt.requestId;
163721
- pendingPrompt = void 0;
163722
- }
163723
- if (message.role === "assistant" && message.status === "sending") streamMessage(row);
163724
- else appendMessage(row);
163725
- endMessage(row);
163726
- projected.push(row);
163727
- }
163728
- function historyRecord(message, seq) {
163729
- const key = messageKey(message);
163730
- const wire = {
163731
- id: key,
163732
- role: message.role === "assistant" ? "assistant" : "user",
163733
- source: message.role === "assistant" ? {
163734
- kind: "model",
163735
- provider: "arko",
163736
- model: "unspecified"
163737
- } : message.role === "divider" ? {
163738
- kind: "plugin",
163739
- plugin: "arko",
163740
- form: "notice",
163741
- summary: message.text
163742
- } : { kind: "user" },
163743
- content: renderContent(message)
163744
- };
163745
- const presentationTurn = seq + 1;
163746
- return {
163747
- entry: {
163748
- type: "event",
163749
- event: {
163750
- type: message.role === "assistant" ? "assistant/message" : "user/message",
163751
- seq,
163752
- time: Math.max(0, Math.trunc(message.createdAtMillis ?? createdAt)),
163753
- surfaceOp: "append",
163754
- data: message.role === "assistant" ? {
163755
- turn: presentationTurn,
163756
- step: 1,
163757
- message: wire,
163758
- stream: [],
163759
- ...[
163760
- "cancelled",
163761
- "partial",
163762
- "expired"
163763
- ].includes(message.runStatus ?? "") ? { interrupted: true } : {}
163764
- } : wire
163765
- }
163766
- },
163767
- row: {
163768
- message,
163769
- key,
163770
- seq,
163771
- turn: presentationTurn,
163772
- ended: true
163773
- }
163774
- };
163775
- }
163776
- function prependHistory(messages) {
163777
- const known = new Set(projected.map((row) => row.key));
163778
- const persistedKey = (message) => message.messageId === void 0 ? void 0 : `${message.sessionId}:${message.messageId}`;
163779
- const persisted = new Set(projected.map((row) => persistedKey(row.message)).filter((key) => key !== void 0));
163780
- const older = messages.filter((message) => {
163781
- const key = messageKey(message), identity = persistedKey(message);
163782
- if (known.has(key) || identity !== void 0 && persisted.has(identity)) return false;
163783
- known.add(key);
163784
- if (identity !== void 0) persisted.add(identity);
163785
- return true;
163786
- });
163787
- const base = journal[0]?.event.seq ?? nextSeq;
163788
- if (older.length > base) throw new Error("Arko 历史窗口边界已变化,请重新打开会话");
163789
- const entries = older.map((message, index) => historyRecord(message, base - older.length + index));
163790
- journal = [...entries.map((value) => value.entry), ...journal];
163791
- projected = [...entries.map((value) => value.row), ...projected];
163792
- }
163793
- function seedHistory(messages) {
163794
- for (const message of messages) {
163795
- if (message.status === "sending" || message.messageId === void 0) {
163796
- addMessage(message);
163797
- continue;
163798
- }
163799
- const value = historyRecord(message, nextSeq++);
163800
- journal.push(value.entry);
163801
- projected.push(value.row);
163802
- turn = Math.max(turn, value.row.turn);
163803
- }
163804
- }
163805
- function selectedModel() {
163806
- const route = snapshot.modelCatalog?.effectiveRouteKey;
163807
- return route ? {
163808
- provider: "arko",
163809
- model: route
163810
- } : null;
163811
- }
163812
- function projectionValues() {
163813
- return {
163814
- title: snapshot.displayName,
163815
- modelSelection: {
163816
- lastUsed: null,
163817
- next: selectedModel()
163818
- },
163819
- sessionListMetadata: {
163820
- blank: false,
163821
- lastPromptAt: snapshot.messages.filter((message) => message.role === "user").at(-1)?.createdAtMillis ?? null
163822
- }
163823
- };
163824
- }
163825
- function projections() {
163826
- return {
163827
- asOfSeq: nextSeq - 1,
163828
- values: projectionValues()
163829
- };
163830
- }
163831
- function summary() {
163832
- return {
163833
- sessionId,
163834
- updatedAt: snapshot.messages.at(-1)?.createdAtMillis ?? createdAt,
163835
- running: snapshot.busy,
163836
- blank: false,
163837
- projections: projections()
163838
- };
163839
- }
163840
- function checkSession(request) {
163841
- return request.sessionId === sessionId;
163842
- }
163843
- function blocked() {
163844
- return snapshot.loading ? "Arko 正在加载" : snapshot.inputBlockedReason || (snapshot.busy || submitting || changingModel ? "Arko 正在处理上一项操作" : void 0);
163845
- }
163846
- async function dispatch(method, payload) {
163847
- const request = requestOf(payload);
163848
- switch (method) {
163849
- case "settings/describe": return ok({
163850
- writable: false,
163851
- hasDocument: false,
163852
- namespaces: []
163853
- });
163854
- case "credentials/describe": return ok({});
163855
- case "dynamicCordisRunner/syncInspectManifest": return ok(null);
163856
- case "dynamicCordisRunner/inventory":
163857
- case "commands/list":
163858
- case "llm/listConfigurableProviders": return ok([]);
163859
- case "agentPresets/list": return ok({
163860
- presets: [],
163861
- authorable: false
163862
- });
163863
- case "subagents/list": return ok({
163864
- entries: [],
163865
- parentAvailable: false
163866
- });
163867
- case "skills/list": return ok({ skills: [] });
163868
- case "session/list": return ok({ items: [summary()] });
163869
- case "llm/listProviders": return ok([{
163870
- id: "arko",
163871
- name: snapshot.displayName
163872
- }]);
163873
- case "session/modelCatalog": {
163874
- const catalog = snapshot.modelCatalog;
163875
- return ok({
163876
- default: {
163877
- provider: "arko",
163878
- model: catalog?.defaultRouteKey || catalog?.effectiveRouteKey || ""
163879
- },
163880
- routableProviders: catalog?.options.length ? ["arko"] : [],
163881
- groups: catalog === null ? [] : [{
163882
- id: "arko",
163883
- name: snapshot.displayName,
163884
- models: catalog.options.map((option) => ({
163885
- id: option.routeKey,
163886
- name: option.displayName,
163887
- description: option.description
163888
- }))
163889
- }],
163890
- failures: []
163891
- });
163892
- }
163893
- case "session/selectModel": {
163894
- if (!checkSession(request)) return denied("Arko 会话已失效");
163895
- const reason = blocked();
163896
- if (reason !== void 0) return denied(reason);
163897
- if (request.provider !== "arko" || typeof request.model !== "string" || request.reasoningEffort !== void 0 || !snapshot.modelCatalog?.options.some((option) => option.routeKey === request.model)) return denied("此 Arko 模型不可用");
163898
- if (request.model === snapshot.modelCatalog.effectiveRouteKey) return ok({ selected: selectedModel() });
163899
- changingModel = true;
163900
- try {
163901
- if (await actions.selectModel(request.model) !== true && snapshot.modelCatalog?.effectiveRouteKey !== request.model) return denied(snapshot.error || "Arko 模型切换未确认");
163902
- return ok({ selected: {
163903
- provider: "arko",
163904
- model: request.model
163905
- } });
163906
- } finally {
163907
- changingModel = false;
163908
- }
163909
- }
163910
- case "session/prompt": {
163911
- if (!checkSession(request)) return denied("Arko 会话已失效");
163912
- const reason = blocked();
163913
- if (reason !== void 0) return denied(reason);
163914
- if (request.mode !== "queue" || typeof request.requestId !== "string" || request.requestId.length === 0) return denied("Arko 不支持此发送方式");
163915
- if (!Array.isArray(request.content) || request.content.some((part) => record(part).type !== "text" || typeof record(part).text !== "string")) return denied("Arko 当前仅支持文字输入");
163916
- const text = request.content.map((part) => record(part).text).join("").trim();
163917
- if (!text || text.length > 60 * 1024) return denied("Arko 输入为空或超过长度上限");
163918
- submitting = true;
163919
- pendingPrompt = {
163920
- requestId: request.requestId,
163921
- text
163922
- };
163923
- try {
163924
- if (await actions.submit(text) !== true) {
163925
- pendingPrompt = void 0;
163926
- return denied(snapshot.error || "Arko 未接受此次发送");
163927
- }
163928
- return ok({ accepted: true });
163929
- } catch (error) {
163930
- pendingPrompt = void 0;
163931
- throw error;
163932
- } finally {
163933
- submitting = false;
163934
- }
163935
- }
163936
- case "session/cancel":
163937
- if (!checkSession(request)) return denied("Arko 会话已失效");
163938
- if (!snapshot.busy) return denied("Arko 当前没有可停止的运行");
163939
- return await actions.cancel() === true ? ok({ accepted: true }) : denied(snapshot.error || "Arko 停止请求未确认");
163940
- case "session/page": {
163941
- if (record(request.address).kind !== "session" || record(request.address).sessionId !== sessionId) return denied("Arko 会话已失效");
163942
- if (request.beforeSeq !== void 0 && (!Number.isSafeInteger(request.beforeSeq) || Number(request.beforeSeq) < 0)) return denied("Arko 历史边界无效");
163943
- const before = typeof request.beforeSeq === "number" ? request.beforeSeq : Infinity;
163944
- const through = typeof request.throughSeq === "number" ? request.throughSeq : nextSeq - 1;
163945
- if (snapshot.hasMore && before <= (journal[0]?.event.seq ?? nextSeq)) {
163946
- pageRequest ??= (async () => {
163947
- const page = await actions.loadEarlier();
163948
- if (disposed) throw new Error("Arko 原生呈现已关闭");
163949
- prependHistory(page.messages);
163950
- snapshot = {
163951
- ...snapshot,
163952
- hasMore: page.hasMore
163953
- };
163954
- })().finally(() => {
163955
- pageRequest = void 0;
163956
- });
163957
- await pageRequest;
163958
- }
163959
- const available = journal.filter((entry) => entry.event.seq <= through && entry.event.seq < before);
163960
- const limit = typeof request.maxMessages === "number" && Number.isSafeInteger(request.maxMessages) ? Math.min(50, Math.max(1, request.maxMessages)) : 50;
163961
- return ok({
163962
- records: available.slice(-limit),
163963
- hasMore: available.length > limit || snapshot.hasMore === true
163964
- });
163965
- }
163966
- default: return denied(`Arko 不支持原生操作:${method}`);
163967
- }
163968
- }
163969
- seedHistory(snapshot.messages);
163970
- return {
163971
- sessionId,
163972
- get historyRevision() {
163973
- return historyRevision;
163974
- },
163975
- getSnapshot: () => snapshot,
163976
- subscribe(listener) {
163977
- if (disposed) return () => {};
163978
- listeners.add(listener);
163979
- return () => {
163980
- listeners.delete(listener);
163981
- };
163982
- },
163983
- update(next, nextActions) {
163984
- if (disposed) throw new Error("Arko 原生呈现已销毁");
163985
- if (next.accountKey !== accountKey) throw new Error("Arko 账号切换必须建立新的原生呈现");
163986
- const previous = snapshot;
163987
- const oldValues = projectionValues();
163988
- snapshot = next;
163989
- if (nextActions !== void 0) actions = nextActions;
163990
- if (projected.length > 0) {
163991
- const head = next.messages.findIndex((message) => messageKey(message) === projected[0].key);
163992
- if (head > 0) prependHistory(next.messages.slice(0, head));
163993
- }
163994
- if (projected.some((row, index) => {
163995
- const message = next.messages[index];
163996
- if (message === void 0 || messageKey(message) !== row.key) return true;
163997
- return row.seq >= 0 && (row.message.text !== message.text || row.message.reasoning !== message.reasoning || row.message.role !== message.role || row.message.status !== message.status || JSON.stringify(endReason(row.message)) !== JSON.stringify(endReason(message)));
163998
- })) {
163999
- historyRevision++;
164000
- journal = [];
164001
- projected = [];
164002
- turn = 0;
164003
- activeAssistant = void 0;
164004
- assistantRevision = 0;
164005
- nextSeq = sequenceOrigin;
164006
- seedHistory(next.messages);
164007
- } else for (let index = 0; index < next.messages.length; index++) {
164008
- const message = next.messages[index];
164009
- const row = projected[index];
164010
- if (row === void 0) {
164011
- addMessage(message);
164012
- continue;
164013
- }
164014
- const previousMessage = row.message;
164015
- row.message = message;
164016
- if (message.role === "assistant" && row.seq < 0) if (message.status === "sending") streamMessage(row, previousMessage);
164017
- else settleAssistant(row);
164018
- endMessage(row);
164019
- }
164020
- const values = projectionValues();
164021
- const changedKeys = Object.keys(values).filter((key) => JSON.stringify(values[key]) !== JSON.stringify(oldValues[key]));
164022
- const context = {
164023
- id: "",
164024
- role: "divider",
164025
- text: "",
164026
- status: "done",
164027
- createdAtMillis: Date.now()
164028
- };
164029
- if (changedKeys.length > 0) append("arko/presentation", Object.fromEntries(changedKeys.map((key) => [key, values[key]])), context);
164030
- for (const key of changedKeys) emit("session/control", {
164031
- type: "projection",
164032
- sessionId,
164033
- key,
164034
- value: values[key],
164035
- seq: nextSeq - 1
164036
- });
164037
- if (previous.busy !== next.busy) emit("$events", {
164038
- type: "emit",
164039
- event: "api-session/status",
164040
- args: [sessionId, next.busy]
164041
- });
164042
- if (next.error && previous.error !== next.error) emit("$events", {
164043
- type: "emit",
164044
- event: "api-session/error",
164045
- args: [sessionId, next.error]
164046
- });
164047
- if (JSON.stringify(previous.modelCatalog) !== JSON.stringify(next.modelCatalog)) emit("$events", {
164048
- type: "emit",
164049
- event: "llm/adapters-updated",
164050
- args: []
164051
- });
164052
- for (const listener of listeners) listener();
164053
- },
164054
- async fetch(_input, init) {
164055
- let rpcId = "";
164056
- let result;
164057
- try {
164058
- if (typeof init.body !== "string") throw new Error("Arko 原生请求格式无效");
164059
- const envelope = record(JSON.parse(init.body));
164060
- rpcId = typeof envelope.rpcId === "string" ? envelope.rpcId : "";
164061
- if (envelope.type !== "client-request" || typeof envelope.method !== "string" || rpcId === "") throw new Error("Arko 原生请求格式无效");
164062
- if (disposed || init.signal?.aborted) result = denied("Arko 原生呈现已关闭");
164063
- else {
164064
- result = await dispatch(envelope.method, envelope.payload);
164065
- if (disposed || init.signal?.aborted) result = denied("Arko 原生呈现已关闭");
164066
- }
164067
- } catch (error) {
164068
- result = denied(error instanceof Error ? error.message : "Arko 原生请求失败");
164069
- }
164070
- return new Response(JSON.stringify({
164071
- type: "server-response",
164072
- rpcId,
164073
- result
164074
- }), { headers: { "content-type": "application/json" } });
164075
- },
164076
- async *openStream(endpoint, payload, signal) {
164077
- if (disposed || signal.aborted) return;
164078
- if (![
164079
- "$events",
164080
- "session/control",
164081
- "session/follow",
164082
- "workspace/follow"
164083
- ].includes(endpoint)) throw new Error(`Arko 不支持原生流:${endpoint}`);
164084
- if (endpoint === "session/follow") {
164085
- const address = record(requestOf(payload).address);
164086
- if (address.kind !== "session" || address.sessionId !== sessionId) throw new Error("Arko 会话已失效");
164087
- }
164088
- const queue = [];
164089
- let wake;
164090
- let ended = false;
164091
- const sink = {
164092
- endpoint,
164093
- historyRevision,
164094
- push(value) {
164095
- if (!ended) {
164096
- queue.push(value);
164097
- wake?.();
164098
- }
164099
- },
164100
- close() {
164101
- ended = true;
164102
- wake?.();
164103
- }
164104
- };
164105
- streams.add(sink);
164106
- const abort = () => sink.close();
164107
- signal.addEventListener("abort", abort, { once: true });
164108
- try {
164109
- if (endpoint === "$events") yield {
164110
- type: "ready",
164111
- clientId: crypto.randomUUID(),
164112
- host: { home: "" }
164113
- };
164114
- if (endpoint === "workspace/follow") yield {
164115
- type: "baseline",
164116
- value: {
164117
- items: [],
164118
- archivedSessionIds: []
164119
- }
164120
- };
164121
- if (endpoint === "session/control") yield {
164122
- type: "baseline",
164123
- value: {
164124
- queues: { [sessionId]: [] },
164125
- jobs: { [sessionId]: [] },
164126
- projections: { [sessionId]: projections() }
164127
- }
164128
- };
164129
- if (endpoint === "session/follow") yield {
164130
- type: "snapshot",
164131
- header: {
164132
- version: 3,
164133
- id: sessionId,
164134
- createdAt,
164135
- isSeeded: false
164136
- },
164137
- cursor: nextSeq - 1,
164138
- records: journal.slice(),
164139
- hasMore: snapshot.hasMore === true,
164140
- projections: projections(),
164141
- assistantStream: {
164142
- revision: assistantRevision,
164143
- ...activeAssistant === void 0 ? {} : { activeAttempt: {
164144
- ...activeAssistant,
164145
- stream: activeAssistant.stream.slice()
164146
- } }
164147
- }
164148
- };
164149
- while (!ended) {
164150
- if (queue.length > 0) {
164151
- yield queue.shift();
164152
- continue;
164153
- }
164154
- await new Promise((resolve) => {
164155
- wake = resolve;
164156
- });
164157
- wake = void 0;
164158
- }
164159
- } finally {
164160
- streams.delete(sink);
164161
- signal.removeEventListener("abort", abort);
164162
- queue.length = 0;
164163
- }
164164
- },
164165
- dispose() {
164166
- if (disposed) return;
164167
- disposed = true;
164168
- for (const stream of streams) stream.close();
164169
- streams.clear();
164170
- listeners.clear();
164171
- journal = [];
164172
- projected = [];
164173
- pendingPrompt = void 0;
164174
- activeAssistant = void 0;
164175
- }
164176
- };
164177
- }
164178
- //#endregion
164179
- //#region src/client/arko-native-frame.ts
164180
- const conversationOnlyLayout = `
164181
- button[aria-haspopup="listbox"][aria-label="指令"],
164182
- button[aria-haspopup="listbox"][aria-label="Commands"] {
164183
- display: none !important;
164184
- }
164185
- body div:has(> :first-child > [data-slot="sidebar"]):has(> [data-rightbar-col]) > :first-child,
164186
- body div:has(> :first-child > [data-slot="sidebar"]):has(> [data-rightbar-col]) > [data-side="sidebar"] {
164187
- display: none !important;
164188
- }
164189
- body div:has(> :first-child > [data-slot="sidebar"]):has(> [data-rightbar-col]) > :nth-child(2) {
164190
- grid-column: 1 / 3; grid-row: 1;
164191
- }
164192
- body div:has(> :first-child > [data-slot="sidebar"]):has(> [data-rightbar-col]) > [data-rightbar-col] {
164193
- grid-column: 3; grid-row: 1;
164194
- }
164195
- `;
164196
- /** The owning parent installs a per-frame carrier before any native DSH code executes. */
164197
- function arkoNativeFrameDocument(html, key, origin) {
164198
- if (!/^[a-zA-Z0-9-]+$/.test(key) || !/<head(?:\s[^>]*)?>/i.test(html)) throw new Error("Arko 原生页面启动配置无效");
164199
- const base = new URL("/", origin).href.replaceAll("&", "&amp;").replaceAll("\"", "&quot;");
164200
- return html.replace(/<head(?:\s[^>]*)?>/i, (head) => `${head}<base href="${base}"><style data-arkme-arko-layout>${conversationOnlyLayout}</style><script>
164201
- const carrier = parent.__arkmeArkoNativeFrames?.get(${JSON.stringify(key)});
164202
- if (!carrier) throw new Error('Arko native carrier unavailable');
164203
- // Session selection belongs to this document, not the ordinary DSH tab.
164204
- const storage = globalThis.localStorage;
164205
- const isolated = new Map();
164206
- const conversationKey = 'dsh.conversation.' + carrier.sessionId;
164207
- for (const method of ['getItem', 'setItem', 'removeItem']) {
164208
- const original = Storage.prototype[method];
164209
- Storage.prototype[method] = function(key, value) {
164210
- if (this !== storage || (key !== 'dsh.sessions.current' && key !== conversationKey)) return original.apply(this, arguments);
164211
- if (method === 'getItem') return isolated.get(key) ?? null;
164212
- if (method === 'removeItem') isolated.delete(key);
164213
- else isolated.set(key, String(value));
164214
- };
164215
- }
164216
- globalThis.__DSH_TRANSPORT__ = {
164217
- async fetch(...args) {
164218
- const response = await carrier.fetch(...args);
164219
- return new Response(await response.text(), { status: response.status, headers: { 'content-type': 'application/json' } });
164220
- },
164221
- async *openStream(...args) {
164222
- for await (const value of carrier.openStream(...args)) yield JSON.parse(JSON.stringify(value));
164223
- },
164224
- };
164225
- globalThis.__ARKME_NATIVE_ARKO__ = carrier;
164226
- <\/script>`);
164227
- }
164228
- //#endregion
164229
- //#region src/client/ArkoNativeSurface.tsx
164230
- /** This component owns only the native document and its disposable presentation carrier. */
164231
- function ArkoNativeSurface({ snapshot, actions, ui }) {
164232
- const latest = (0, react$1.useRef)({
164233
- snapshot,
164234
- actions,
164235
- ui
164236
- });
164237
- latest.current = {
164238
- snapshot,
164239
- actions,
164240
- ui
164241
- };
164242
- const carrier = (0, react$1.useRef)();
164243
- const [html, setHtml] = (0, react$1.useState)("");
164244
- const [error, setError] = (0, react$1.useState)("");
164245
- const [ready, setReady] = (0, react$1.useState)(false);
164246
- const [attempt, setAttempt] = (0, react$1.useState)(0);
164247
- (0, react$1.useLayoutEffect)(() => {
164248
- const transport = carrier.current;
164249
- if (!transport || transport.getSnapshot().accountKey !== snapshot.accountKey) return;
164250
- const revision = transport.historyRevision;
164251
- transport.ui = ui;
164252
- transport.update(snapshot, actions);
164253
- if (transport.historyRevision !== revision) setAttempt((value) => value + 1);
164254
- }, [
164255
- snapshot,
164256
- actions,
164257
- ui
164258
- ]);
164259
- (0, react$1.useEffect)(() => {
164260
- if (snapshot.loading) return;
164261
- const controller = new AbortController();
164262
- const key = crypto.randomUUID();
164263
- const current = latest.current;
164264
- const transport = Object.assign(createArkoNativeTransport(current.snapshot, current.actions), {
164265
- ui: current.ui,
164266
- ready: () => {
164267
- if (!controller.signal.aborted) {
164268
- setError("");
164269
- setReady(true);
164270
- }
164271
- }
164272
- });
164273
- carrier.current = transport;
164274
- const frames = window.__arkmeArkoNativeFrames ??= /* @__PURE__ */ new Map();
164275
- frames.set(key, transport);
164276
- setError("");
164277
- setHtml("");
164278
- setReady(false);
164279
- fetch("/arkme-self/harness-frame?arkme-arko=1", {
164280
- signal: controller.signal,
164281
- credentials: "same-origin"
164282
- }).then(async (response) => {
164283
- if (!response.ok) throw new Error(`原生页面加载失败(${response.status})`);
164284
- const source = await response.text();
164285
- if (!controller.signal.aborted) setHtml(arkoNativeFrameDocument(source, key, location.origin));
164286
- }).catch((caught) => {
164287
- if (!controller.signal.aborted) setError(caught instanceof Error ? caught.message : String(caught));
164288
- });
164289
- const deadline = window.setTimeout(() => {
164290
- if (!controller.signal.aborted) setReady((value) => {
164291
- if (!value) setError("原生页面启动超时,请重试");
164292
- return value;
164293
- });
164294
- }, 3e4);
164295
- return () => {
164296
- controller.abort();
164297
- clearTimeout(deadline);
164298
- transport.dispose();
164299
- frames.delete(key);
164300
- if (carrier.current === transport) carrier.current = void 0;
164301
- };
164302
- }, [
164303
- snapshot.accountKey,
164304
- attempt,
164305
- snapshot.loading
164306
- ]);
164307
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
164308
- "data-arkme-owned": "arko-native-surface",
164309
- style: {
164310
- height: "100%",
164311
- minHeight: 0,
164312
- position: "relative",
164313
- display: "flex",
164314
- flexDirection: "column"
164315
- },
164316
- children: [
164317
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164318
- role: "alert",
164319
- children: [error, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
164320
- onClick: () => setAttempt((value) => value + 1),
164321
- children: "重新加载原生页面"
164322
- })]
164323
- }),
164324
- !ready && !error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164325
- role: "status",
164326
- "aria-label": "正在加载 Arko 对话",
164327
- "data-arko-loading-skeleton": true,
164328
- style: {
164329
- position: "absolute",
164330
- inset: 0,
164331
- display: "flex",
164332
- flexDirection: "column",
164333
- background: arkmeTheme.base
164334
- },
164335
- children: [
164336
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164337
- style: {
164338
- display: "flex",
164339
- alignItems: "center",
164340
- gap: 10,
164341
- padding: "16px 24px",
164342
- borderBottom: `1px solid ${arkmeTheme.border}`
164343
- },
164344
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeArkoAvatar, { size: 30 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
164345
- style: { color: arkmeTheme.text },
164346
- children: snapshot.displayName
164347
- })]
164348
- }),
164349
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
164350
- "aria-hidden": true,
164351
- style: {
164352
- flex: 1,
164353
- width: "min(720px, 86%)",
164354
- margin: "24px auto",
164355
- display: "flex",
164356
- flexDirection: "column",
164357
- gap: 20
164358
- },
164359
- children: [
164360
- 70,
164361
- 45,
164362
- 82
164363
- ].map((width, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
164364
- width: `${width}%`,
164365
- height: index === 1 ? 40 : 64,
164366
- alignSelf: index === 1 ? "flex-end" : "flex-start",
164367
- borderRadius: 12,
164368
- background: arkmeTheme.hover
164369
- } }, index))
164370
- }),
164371
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164372
- style: {
164373
- width: "min(720px, 86%)",
164374
- margin: "0 auto 24px"
164375
- },
164376
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
164377
- "aria-hidden": true,
164378
- style: {
164379
- height: 92,
164380
- border: `1px solid ${arkmeTheme.border}`,
164381
- borderRadius: 16,
164382
- background: arkmeTheme.hover
164383
- }
164384
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
164385
- style: {
164386
- textAlign: "center",
164387
- color: arkmeTheme.secondary,
164388
- fontSize: 12,
164389
- paddingTop: 10
164390
- },
164391
- children: "正在加载对话…"
164392
- })]
164393
- })
164394
- ]
164395
- }),
164396
- html && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
164397
- name: "arkme-arko-native",
164398
- title: "Arko 原生 DSH 页面",
164399
- srcDoc: html,
164400
- style: {
164401
- border: 0,
164402
- width: "100%",
164403
- flex: 1,
164404
- minHeight: 0,
164405
- visibility: ready && !error ? "visible" : "hidden"
164406
- },
164407
- "aria-hidden": !ready || !!error,
164408
- allow: "clipboard-read; clipboard-write"
164409
- })
164410
- ]
164411
- });
164412
- }
164413
- //#endregion
164414
- //#region src/client/arko-pending-turn-store.ts
164415
- const STORAGE_KEY_PREFIX = "arkme:arko:pending-turn:v1:";
164416
- const UUID_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;
164417
- function defaultStorage() {
164418
- try {
164419
- return typeof globalThis.sessionStorage === "undefined" ? void 0 : globalThis.sessionStorage;
164420
- } catch {
164421
- return;
164422
- }
164423
- }
164424
- function storageKey(userId) {
164425
- return `${STORAGE_KEY_PREFIX}${String(userId)}`;
164426
- }
164427
- function optionalTrimmedString(value) {
164428
- if (typeof value !== "string") return void 0;
164429
- const normalized = value.trim();
164430
- return normalized === "" ? void 0 : normalized;
164431
- }
164432
- function isValidArkoPendingTurn(value, expectedUserId) {
164433
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
164434
- const turn = value;
164435
- const hasContinuationRun = optionalTrimmedString(turn.replyToRunUid) !== void 0;
164436
- const hasContinuationMessage = Number.isSafeInteger(turn.replyToAssistantMsgId) && (turn.replyToAssistantMsgId ?? 0) > 0;
164437
- return Number.isSafeInteger(turn.userId) && (turn.userId ?? 0) > 0 && (expectedUserId === void 0 || turn.userId === expectedUserId) && Number.isSafeInteger(turn.sessionId) && (turn.sessionId ?? 0) > 0 && typeof turn.clientTurnUid === "string" && UUID_PATTERN.test(turn.clientTurnUid) && typeof turn.text === "string" && turn.text.trim() !== "" && Number.isSafeInteger(turn.createdAtMillis) && (turn.createdAtMillis ?? 0) > 0 && optionalTrimmedString(turn.localUserMessageId) !== void 0 && optionalTrimmedString(turn.localAssistantMessageId) !== void 0 && (turn.modelRouteKey === void 0 || optionalTrimmedString(turn.modelRouteKey) !== void 0) && hasContinuationRun === hasContinuationMessage && (!hasContinuationRun || turn.modelRouteKey === void 0);
164438
- }
164439
- function readArkoPendingTurn(userId, storage = defaultStorage()) {
164440
- if (!Number.isSafeInteger(userId) || userId <= 0 || storage === void 0) return void 0;
164441
- try {
164442
- const encoded = storage.getItem(storageKey(userId));
164443
- if (encoded === null) return void 0;
164444
- const value = JSON.parse(encoded);
164445
- if (!isValidArkoPendingTurn(value, userId)) {
164446
- storage.removeItem(storageKey(userId));
164447
- return;
164448
- }
164449
- return value;
164450
- } catch {
164451
- return;
164452
- }
164453
- }
164454
- function writeArkoPendingTurn(turn, storage = defaultStorage()) {
164455
- if (!isValidArkoPendingTurn(turn) || storage === void 0) return false;
164456
- try {
164457
- storage.setItem(storageKey(turn.userId), JSON.stringify(turn));
164458
- return true;
164459
- } catch {
164460
- return false;
164461
- }
164462
- }
164463
- function removeArkoPendingTurn(userId, storage = defaultStorage()) {
164464
- if (!Number.isSafeInteger(userId) || userId <= 0 || storage === void 0) return;
164465
- try {
164466
- storage.removeItem(storageKey(userId));
164467
- } catch {}
164468
- }
164469
- //#endregion
164470
- //#region src/client/useArkoConversationController.ts
164471
- const arkoQuestionMaxLength = 60 * 1024;
164472
- const ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set([
164473
- "accepted",
164474
- "queued",
164475
- "running",
164476
- "stream_timeout",
164477
- "waiting_tool"
164478
- ]);
164479
- function errorMessage$8(error) {
164480
- if (error instanceof ArkmeClientError) return error.body.message;
164481
- return error instanceof Error ? error.message : String(error);
164482
- }
164483
- function isActiveRunStatus(status) {
164484
- return status !== void 0 && ACTIVE_RUN_STATUSES.has(status);
164485
- }
164486
- function isActivityPlaceholderText(value) {
164487
- return /^(正在)?(思考|处理)(中)?[.。…]*$/.test(value.trim());
164488
- }
164489
- function isActiveHistoryRun(item) {
164490
- const placeholder = isActivityPlaceholderText(item.text);
164491
- return item.role === "assistant" && (isActiveRunStatus(item.runStatus) || item.runStatus === void 0 && item.status === 2 && (item.text.trim() === "" || placeholder));
164492
- }
164493
- function arkoHistoryHasTerminalRun(items, sessionId, assistantMsgId, runUid) {
164494
- const matchingItem = items.find((item) => item.role === "assistant" && item.sessionId === sessionId && item.messageId === assistantMsgId && (item.runUid === void 0 || item.runUid === runUid));
164495
- return matchingItem !== void 0 && !isActiveHistoryRun(matchingItem);
164496
- }
164497
- function waitForNextPoll(signal, delayMillis = 1200) {
164498
- return new Promise((resolve) => {
164499
- if (signal.aborted) {
164500
- resolve();
164501
- return;
164502
- }
164503
- const finish = () => {
164504
- clearTimeout(timeout);
164505
- signal.removeEventListener("abort", finish);
164506
- resolve();
164507
- };
164508
- const timeout = setTimeout(finish, delayMillis);
164509
- signal.addEventListener("abort", finish, { once: true });
164510
- });
164511
- }
164512
- function historyMessage(item) {
164513
- const placeholder = isActivityPlaceholderText(item.text);
164514
- const reasoningPlaceholder = isActivityPlaceholderText(item.reasoning);
164515
- const active = isActiveHistoryRun(item);
164516
- return {
164517
- id: `history:${String(item.messageId)}`,
164518
- messageId: item.messageId,
164519
- sessionId: item.sessionId,
164520
- role: item.role,
164521
- text: active && placeholder ? "" : item.text,
164522
- status: active ? "sending" : item.runStatus === "failed" ? "error" : "done",
164523
- createdAtMillis: item.createdAtMillis,
164524
- ...item.reasoning.trim() === "" || !active && reasoningPlaceholder ? {} : { reasoning: item.reasoning },
164525
- ...item.role !== "assistant" ? {} : { assistantMsgId: item.messageId },
164526
- ...item.runUid === void 0 ? {} : { runUid: item.runUid },
164527
- ...item.runStatus === void 0 ? {} : { runStatus: item.runStatus },
164528
- ...item.messageActionRef === void 0 ? {} : {
164529
- messageActionRef: item.messageActionRef,
164530
- ...item.messageActionConversationRef === void 0 ? {} : { messageActionConversationRef: item.messageActionConversationRef },
164531
- copyLinkAvailable: item.messageActionCapabilities?.copyLink === true,
164532
- forwardAvailable: item.messageActionCapabilities?.forward === true
164533
- }
164534
- };
164535
- }
164536
- function latestActiveRun(items, sessionId) {
164537
- const item = items.filter((candidate) => candidate.sessionId === sessionId && candidate.role === "assistant" && candidate.runUid !== void 0 && isActiveRunStatus(candidate.runStatus)).sort((left, right) => right.createdAtMillis - left.createdAtMillis || right.messageId - left.messageId)[0];
164538
- if (item?.runUid === void 0) return void 0;
164539
- return {
164540
- sessionId: item.sessionId,
164541
- assistantMsgId: item.messageId,
164542
- runUid: item.runUid
164543
- };
164544
- }
164545
- function mergeHistory(current, items) {
164546
- const byId = new Map(current.map((item) => [item.id, item]));
164547
- for (const item of items) {
164548
- const key = `history:${String(item.messageId)}`;
164549
- const prior = byId.get(key);
164550
- byId.set(key, {
164551
- ...historyMessage(item),
164552
- ...prior?.presentationKey ? { presentationKey: prior.presentationKey } : {}
164553
- });
164554
- }
164555
- return [...byId.values()].sort((left, right) => {
164556
- const leftAt = left.createdAtMillis ?? Number.MAX_SAFE_INTEGER;
164557
- const rightAt = right.createdAtMillis ?? Number.MAX_SAFE_INTEGER;
164558
- if (leftAt !== rightAt) return leftAt - rightAt;
164559
- return (left.messageId ?? Number.MAX_SAFE_INTEGER) - (right.messageId ?? Number.MAX_SAFE_INTEGER);
164560
- });
164561
- }
164562
- function resultText(result) {
164563
- if (result.text.trim() !== "") return result.text.trim();
164564
- if (result.errorMessage?.trim()) return result.errorMessage.trim();
164565
- if (isActiveRunStatus(result.run?.status ?? result.status)) return "";
164566
- return "任务已处理。";
164567
- }
164568
- function latestContinuation(messages, sessionId) {
164569
- if (sessionId === void 0) return void 0;
164570
- for (let index = messages.length - 1; index >= 0; index -= 1) {
164571
- const message = messages[index];
164572
- if (message === void 0) continue;
164573
- if (message.role === "divider") return void 0;
164574
- if (message.sessionId !== sessionId) continue;
164575
- if (message.role === "user") return void 0;
164576
- if (message.runStatus !== "waiting_user" || message.runUid === void 0 || message.assistantMsgId === void 0) return;
164577
- return {
164578
- runUid: message.runUid,
164579
- assistantMsgId: message.assistantMsgId
164580
- };
164581
- }
164582
- }
164583
- function selectedModelName(catalog) {
164584
- return catalog?.options.find((option) => option.routeKey === catalog.effectiveRouteKey)?.displayName ?? "模型目录暂不可用";
164585
- }
164586
- function useArkoConversationController() {
164587
- const alive = (0, react$1.useRef)(true);
164588
- (0, react$1.useEffect)(() => {
164589
- alive.current = true;
164590
- return () => {
164591
- alive.current = false;
164592
- };
164593
- }, []);
164594
- useArkmeLocale();
164595
- const historyLoadInFlightRef = (0, react$1.useRef)(false);
164596
- const sendInFlightRef = (0, react$1.useRef)(false);
164597
- const authSnapshot = (0, react$1.useSyncExternalStore)(arkmeAuthStore.subscribe, arkmeAuthStore.getSnapshot, arkmeAuthStore.getSnapshot);
164598
- const profileSnapshot = (0, react$1.useSyncExternalStore)(arkmeArkoProfileStore.subscribe, arkmeArkoProfileStore.getSnapshot, arkmeArkoProfileStore.getSnapshot);
164599
- const profileUserId = authSnapshot.auth?.status === "authenticated" ? authSnapshot.auth.userId : void 0;
164600
- const composerDraftKey = arkmeArkoComposerDraftKey(profileUserId);
164601
- (0, react$1.useSyncExternalStore)(arkmeComposerDraftStore.subscribe, arkmeComposerDraftStore.getRevision, arkmeComposerDraftStore.getRevision);
164602
- const draftSnapshot = arkmeComposerDraftStore.get(composerDraftKey);
164603
- const draft = draftSnapshot.text;
164604
- const accountKey = authSnapshot.auth?.status === "authenticated" && profileUserId !== void 0 ? `${authSnapshot.auth.environment}:${String(profileUserId)}` : void 0;
164605
- const profile = profileSnapshot.userId === profileUserId ? profileSnapshot.profile : void 0;
164606
- const [userProfile, setUserProfile] = (0, react$1.useState)(null);
164607
- const [session, setSession] = (0, react$1.useState)();
164608
- const [catalog, setCatalog] = (0, react$1.useState)();
164609
- const [messages, setMessages] = (0, react$1.useState)([]);
164610
- const [historyOffset, setHistoryOffset] = (0, react$1.useState)(null);
164611
- const [historyError, setHistoryError] = (0, react$1.useState)("");
164612
- const [historyPageError, setHistoryPageError] = (0, react$1.useState)("");
164613
- const historyPageAbort = (0, react$1.useRef)();
164614
- (0, react$1.useEffect)(() => () => {
164615
- historyPageAbort.current?.abort();
164616
- }, [accountKey]);
164617
- const [loading, setLoading] = (0, react$1.useState)(true);
164618
- const [historyLoading, setHistoryLoading] = (0, react$1.useState)(false);
164619
- const [sending, setSending] = (0, react$1.useState)(false);
164620
- const [clearing, setClearing] = (0, react$1.useState)(false);
164621
- const clearInFlightRef = (0, react$1.useRef)(false);
164622
- const [selectingModel, setSelectingModel] = (0, react$1.useState)(false);
164623
- const modelSelectionInFlightRef = (0, react$1.useRef)(false);
164624
- const [cancelling, setCancelling] = (0, react$1.useState)(false);
164625
- const [error, setError] = (0, react$1.useState)("");
164626
- const [notice, setNotice] = (0, react$1.useState)("");
164627
- const [activeRun, setActiveRun] = (0, react$1.useState)();
164628
- const [pendingTurn, setPendingTurn] = (0, react$1.useState)();
164629
- (0, react$1.useEffect)(() => {
164630
- arkmeArkoProfileStore.activateUser(profileUserId);
164631
- arkmeArkoConversationPreviewStore.activateUser(profileUserId);
164632
- }, [profileUserId]);
164633
- (0, react$1.useEffect)(() => {
164634
- if (profileUserId === void 0) return;
164635
- arkmeArkoConversationPreviewStore.setLatestFromSurface(profileUserId, messages.filter((message) => message.role !== "divider").map((message) => ({
164636
- key: message.id,
164637
- text: message.text,
164638
- ...message.messageId === void 0 ? {} : { messageId: message.messageId },
164639
- ...message.createdAtMillis === void 0 ? {} : { createdAtMillis: message.createdAtMillis }
164640
- })));
164641
- }, [messages, profileUserId]);
164642
- (0, react$1.useEffect)(() => {
164643
- if (profileUserId === void 0) {
164644
- setPendingTurn(void 0);
164645
- return;
164646
- }
164647
- const restored = readArkoPendingTurn(profileUserId);
164648
- setPendingTurn(restored);
164649
- if (restored === void 0) return;
164650
- setMessages((current) => {
164651
- if (current.some((item) => item.id === restored.localAssistantMessageId)) return current;
164652
- return [
164653
- ...current,
164654
- {
164655
- id: restored.localUserMessageId,
164656
- presentationKey: restored.localUserMessageId,
164657
- sessionId: restored.sessionId,
164658
- role: "user",
164659
- text: restored.text,
164660
- status: "done",
164661
- createdAtMillis: restored.createdAtMillis
164662
- },
164663
- {
164664
- id: restored.localAssistantMessageId,
164665
- presentationKey: restored.localAssistantMessageId,
164666
- sessionId: restored.sessionId,
164667
- role: "assistant",
164668
- text: "",
164669
- status: "error",
164670
- createdAtMillis: restored.createdAtMillis + 1
164671
- }
164672
- ];
164673
- });
164674
- }, [profileUserId]);
164675
- (0, react$1.useEffect)(() => {
164676
- const controller = new AbortController();
164677
- setLoading(true);
164678
- setError("");
164679
- setHistoryError("");
164680
- Promise.allSettled([
164681
- callArkme("arko.session", void 0, controller.signal),
164682
- callArkme("arko.profile", void 0, controller.signal),
164683
- callArkme("arko.models", void 0, controller.signal),
164684
- callArkme("arko.history", {
164685
- limit: 50,
164686
- offset: 0
164687
- }, controller.signal),
164688
- callArkme("user.profile", void 0, controller.signal).then(async (snapshot) => snapshot.profile === null ? await callArkme("user.profile.refresh", void 0, controller.signal) : snapshot)
164689
- ]).then(([sessionResult, profileResult, modelResult, historyResult, userProfileResult]) => {
164690
- if (controller.signal.aborted) return;
164691
- if (sessionResult.status === "rejected") {
164692
- setError(errorMessage$8(sessionResult.reason));
164693
- return;
164694
- }
164695
- setSession(sessionResult.value);
164696
- if (profileResult.status === "fulfilled" && profileUserId !== void 0) arkmeArkoProfileStore.setProfile(profileUserId, profileResult.value);
164697
- if (modelResult.status === "fulfilled") setCatalog(modelResult.value);
164698
- if (userProfileResult.status === "fulfilled") setUserProfile(userProfileResult.value.profile);
164699
- if (historyResult.status === "fulfilled") {
164700
- setMessages((current) => mergeHistory(current, historyResult.value.items));
164701
- setHistoryOffset(historyResult.value.nextOffset);
164702
- const restoredRun = latestActiveRun(historyResult.value.items, sessionResult.value.sessionId);
164703
- if (restoredRun !== void 0) {
164704
- setActiveRun(restoredRun);
164705
- setSending(true);
164706
- }
164707
- } else setHistoryError(tr$1("加载 Arko 对话记录失败:{v0}", { v0: errorMessage$8(historyResult.reason) }));
164708
- }).finally(() => {
164709
- if (!controller.signal.aborted) setLoading(false);
164710
- });
164711
- return () => {
164712
- controller.abort();
164713
- };
164714
- }, [profileUserId]);
164715
- (0, react$1.useEffect)(() => {
164716
- if (activeRun === void 0) return;
164717
- const controller = new AbortController();
164718
- let consecutiveFailures = 0;
164719
- const finishRun = async (knownHistory) => {
164720
- const [historyResult, profileResult] = await Promise.allSettled([knownHistory === void 0 ? callArkme("arko.history", {
164721
- limit: 50,
164722
- offset: 0
164723
- }, controller.signal) : Promise.resolve(knownHistory), callArkme("arko.profile", void 0, controller.signal)]);
164724
- if (controller.signal.aborted) return;
164725
- if (historyResult.status === "fulfilled") {
164726
- setMessages((current) => mergeHistory(current, historyResult.value.items));
164727
- setHistoryOffset((current) => current === null ? historyResult.value.nextOffset : current);
164728
- setHistoryError("");
164729
- } else setHistoryError(tr$1("刷新 Arko 对话记录失败:{v0}", { v0: errorMessage$8(historyResult.reason) }));
164730
- if (profileResult.status === "fulfilled" && profileUserId !== void 0) arkmeArkoProfileStore.setProfile(profileUserId, profileResult.value);
164731
- setActiveRun((current) => current?.runUid === activeRun.runUid ? void 0 : current);
164732
- setSending(false);
164733
- };
164734
- const poll = async () => {
164735
- await waitForNextPoll(controller.signal);
164736
- while (!controller.signal.aborted) {
164737
- try {
164738
- const status = await callArkme("arko.run.status", {
164739
- sessionId: activeRun.sessionId,
164740
- runUid: activeRun.runUid
164741
- }, controller.signal);
164742
- if (controller.signal.aborted) return;
164743
- consecutiveFailures = 0;
164744
- if (status.status === "waiting_tool") setNotice("当前任务需要 DSH 尚未支持的客户端操作,可以停止任务后换一种方式重试");
164745
- setMessages((current) => current.map((item) => item.assistantMsgId === activeRun.assistantMsgId ? {
164746
- ...item,
164747
- runStatus: status.status,
164748
- status: isActiveRunStatus(status.status) ? "sending" : status.status === "failed" ? "error" : "done"
164749
- } : item));
164750
- if (!isActiveRunStatus(status.status)) {
164751
- await finishRun();
164752
- return;
164753
- }
164754
- } catch {
164755
- if (controller.signal.aborted) return;
164756
- consecutiveFailures += 1;
164757
- try {
164758
- const history = await callArkme("arko.history", {
164759
- limit: 50,
164760
- offset: 0
164761
- }, controller.signal);
164762
- if (controller.signal.aborted) return;
164763
- if (arkoHistoryHasTerminalRun(history.items, activeRun.sessionId, activeRun.assistantMsgId, activeRun.runUid)) {
164764
- await finishRun(history);
164765
- return;
164766
- }
164767
- } catch {
164768
- if (controller.signal.aborted) return;
164769
- }
164770
- }
164771
- const retryDelay = consecutiveFailures === 0 ? 1200 : Math.min(1e4, 1200 * 2 ** Math.min(consecutiveFailures - 1, 3));
164772
- await waitForNextPoll(controller.signal, retryDelay);
164773
- }
164774
- };
164775
- poll();
164776
- return () => {
164777
- controller.abort();
164778
- };
164779
- }, [activeRun, profileUserId]);
164780
- const loadEarlier = (0, react$1.useCallback)(async () => {
164781
- const offset = historyOffset;
164782
- if (typeof offset !== "number") return {
164783
- messages: [],
164784
- hasMore: false
164785
- };
164786
- if (historyLoadInFlightRef.current) throw new Error("正在加载更早记录");
164787
- const controller = new AbortController();
164788
- historyPageAbort.current = controller;
164789
- historyLoadInFlightRef.current = true;
164790
- setHistoryPageError("");
164791
- setHistoryLoading(true);
164792
- try {
164793
- const page = await callArkme("arko.history", {
164794
- limit: 50,
164795
- offset
164796
- }, controller.signal);
164797
- if (controller.signal.aborted) throw new Error("Arko 历史读取已取消");
164798
- if (page.hasMore && (page.nextOffset === void 0 || page.nextOffset <= offset)) throw new Error("历史分页未前进,请重试");
164799
- setMessages((current) => {
164800
- const ids = new Set(current.map((item) => item.messageId));
164801
- return [...page.items.filter((item) => {
164802
- if (ids.has(item.messageId)) return false;
164803
- ids.add(item.messageId);
164804
- return true;
164805
- }).reverse().map(historyMessage), ...current];
164806
- });
164807
- setHistoryOffset(page.nextOffset);
164808
- return {
164809
- messages: [...page.items].reverse().map(historyMessage),
164810
- hasMore: page.hasMore
164811
- };
164812
- } catch (caught) {
164813
- if (!controller.signal.aborted) setHistoryPageError(`加载更早记录失败:${errorMessage$8(caught)}。请重试。`);
164814
- throw caught;
164815
- } finally {
164816
- if (historyPageAbort.current === controller) {
164817
- historyLoadInFlightRef.current = false;
164818
- setHistoryLoading(false);
164819
- historyPageAbort.current = void 0;
164820
- }
164821
- }
164822
- }, [historyOffset]);
164823
- const retryHistory = (0, react$1.useCallback)(async () => {
164824
- if (historyLoadInFlightRef.current) return;
164825
- const controller = new AbortController();
164826
- historyPageAbort.current = controller;
164827
- historyLoadInFlightRef.current = true;
164828
- setHistoryLoading(true);
164829
- setHistoryError("");
164830
- try {
164831
- const page = await callArkme("arko.history", {
164832
- limit: 50,
164833
- offset: 0
164834
- }, controller.signal);
164835
- if (controller.signal.aborted) return;
164836
- setMessages((current) => mergeHistory(current, page.items));
164837
- setHistoryOffset((current) => current === null ? page.nextOffset : current);
164838
- if (session !== void 0) {
164839
- const restoredRun = latestActiveRun(page.items, session.sessionId);
164840
- if (restoredRun !== void 0) {
164841
- setActiveRun(restoredRun);
164842
- setSending(true);
164843
- }
164844
- }
164845
- } catch (caught) {
164846
- if (!controller.signal.aborted) setHistoryError(tr$1("加载 Arko 对话记录失败:{v0}", { v0: errorMessage$8(caught) }));
164847
- } finally {
164848
- if (historyPageAbort.current === controller) {
164849
- historyLoadInFlightRef.current = false;
164850
- historyPageAbort.current = void 0;
164851
- setHistoryLoading(false);
164852
- }
164853
- }
164854
- }, [session]);
164855
- const submitTurn = (0, react$1.useCallback)(async (turn) => {
164856
- if (sendInFlightRef.current || sending || activeRun !== void 0) return false;
164857
- sendInFlightRef.current = true;
164858
- let handedOffToPolling = false;
164859
- setSending(true);
164860
- setError("");
164861
- setNotice("");
164862
- setMessages((current) => current.map((item) => item.id === turn.localAssistantMessageId ? {
164863
- ...item,
164864
- text: "",
164865
- status: "sending",
164866
- runStatus: "accepted"
164867
- } : item));
164868
- try {
164869
- const result = await callArkme("arko.ask", {
164870
- text: turn.text,
164871
- sessionId: turn.sessionId,
164872
- clientTurnUid: turn.clientTurnUid,
164873
- waitSeconds: 1,
164874
- ...turn.modelRouteKey === void 0 ? {} : { modelRouteKey: turn.modelRouteKey },
164875
- ...turn.replyToRunUid === void 0 ? {} : { replyToRunUid: turn.replyToRunUid },
164876
- ...turn.replyToAssistantMsgId === void 0 ? {} : { replyToAssistantMsgId: turn.replyToAssistantMsgId }
164877
- });
164878
- if (!alive.current) return false;
164879
- removeArkoPendingTurn(turn.userId);
164880
- setPendingTurn((current) => current?.clientTurnUid === turn.clientTurnUid ? void 0 : current);
164881
- setSession((current) => current === void 0 ? current : {
164882
- ...current,
164883
- sessionId: result.sessionId
164884
- });
164885
- if (result.profile !== void 0 && profileUserId !== void 0) arkmeArkoProfileStore.setProfile(profileUserId, result.profile);
164886
- const runStatus = result.run?.status ?? result.status;
164887
- const runActive = isActiveRunStatus(runStatus);
164888
- const hasVisibleReasoning = result.reasoning.trim() !== "" && (runActive || !isActivityPlaceholderText(result.reasoning));
164889
- setMessages((current) => mergeHistory(current.map((item) => item.id === turn.localUserMessageId ? {
164890
- ...item,
164891
- id: `history:${String(result.userMsgId)}`,
164892
- messageId: result.userMsgId,
164893
- sessionId: result.sessionId
164894
- } : item.id === turn.localAssistantMessageId ? {
164895
- id: `history:${String(result.assistantMsgId)}`,
164896
- presentationKey: item.presentationKey ?? item.id,
164897
- messageId: result.assistantMsgId,
164898
- sessionId: result.sessionId,
164899
- role: "assistant",
164900
- text: resultText(result),
164901
- status: runActive ? "sending" : result.errorMessage === void 0 ? "done" : "error",
164902
- assistantMsgId: result.assistantMsgId,
164903
- ...item.createdAtMillis === void 0 ? {} : { createdAtMillis: item.createdAtMillis },
164904
- ...hasVisibleReasoning ? { reasoning: result.reasoning } : {},
164905
- ...result.runUid === void 0 ? {} : { runUid: result.runUid },
164906
- runStatus
164907
- } : item), []));
164908
- if (runActive && result.runUid !== void 0) {
164909
- handedOffToPolling = true;
164910
- setActiveRun({
164911
- sessionId: result.sessionId,
164912
- assistantMsgId: result.assistantMsgId,
164913
- runUid: result.runUid
164914
- });
164915
- }
164916
- return true;
164917
- } catch (caught) {
164918
- if (!alive.current) return false;
164919
- const message = errorMessage$8(caught);
164920
- if (!(caught instanceof ArkmeClientError) || caught.body.retryable) {
164921
- setPendingTurn(turn);
164922
- writeArkoPendingTurn(turn);
164923
- setError(`Arko 发送结果暂未确认:${message}。请重试确认,系统会复用同一次请求,不会重复执行。`);
164924
- setMessages((current) => current.map((item) => item.id === turn.localAssistantMessageId ? {
164925
- ...item,
164926
- role: "assistant",
164927
- text: "",
164928
- status: "error"
164929
- } : item));
164930
- } else {
164931
- removeArkoPendingTurn(turn.userId);
164932
- setPendingTurn((current) => current?.clientTurnUid === turn.clientTurnUid ? void 0 : current);
164933
- setError(message);
164934
- setMessages((current) => current.map((item) => item.id === turn.localAssistantMessageId ? {
164935
- ...item,
164936
- role: "assistant",
164937
- text: message,
164938
- status: "error"
164939
- } : item));
164940
- }
164941
- return false;
164942
- } finally {
164943
- sendInFlightRef.current = false;
164944
- if (!handedOffToPolling) setSending(false);
164945
- }
164946
- }, [
164947
- activeRun,
164948
- composerDraftKey,
164949
- profileUserId,
164950
- sending
164951
- ]);
164952
- const interactionLocked = sending || pendingTurn !== void 0 || activeRun !== void 0;
164953
- const sendDisabled = loading || interactionLocked || clearing || selectingModel || session === void 0 || profileUserId === void 0;
164954
- const inputDisabled = loading || interactionLocked || session === void 0 || profileUserId === void 0;
164955
- const send = (0, react$1.useCallback)(async (presetText) => {
164956
- const text = (presetText ?? serializeArkmeComposerDraft(arkmeComposerDraftStore.get(composerDraftKey)).text).trim();
164957
- if (text === "" || sendInFlightRef.current || modelSelectionInFlightRef.current || clearInFlightRef.current || sendDisabled || session === void 0 || profileUserId === void 0 || composerDraftKey === void 0) return false;
164958
- if (text.length > 61440) {
164959
- setError("内容长度超过上限,请删减后再发送");
164960
- return false;
164961
- }
164962
- const continuation = latestContinuation(messages, session.sessionId);
164963
- const createdAtMillis = Date.now();
164964
- const turn = {
164965
- userId: profileUserId,
164966
- sessionId: session.sessionId,
164967
- clientTurnUid: crypto.randomUUID(),
164968
- text,
164969
- createdAtMillis,
164970
- localUserMessageId: crypto.randomUUID(),
164971
- localAssistantMessageId: crypto.randomUUID(),
164972
- ...continuation === void 0 ? { ...catalog === void 0 ? {} : { modelRouteKey: catalog.effectiveRouteKey } } : {
164973
- replyToRunUid: continuation.runUid,
164974
- replyToAssistantMsgId: continuation.assistantMsgId
164975
- }
164976
- };
164977
- writeArkoPendingTurn(turn);
164978
- setPendingTurn(turn);
164979
- if (presetText === void 0) arkmeComposerDraftStore.clear(composerDraftKey);
164980
- setMessages((current) => [
164981
- ...current,
164982
- {
164983
- id: turn.localUserMessageId,
164984
- presentationKey: turn.localUserMessageId,
164985
- sessionId: turn.sessionId,
164986
- role: "user",
164987
- text,
164988
- status: "done",
164989
- createdAtMillis
164990
- },
164991
- {
164992
- id: turn.localAssistantMessageId,
164993
- presentationKey: turn.localAssistantMessageId,
164994
- sessionId: turn.sessionId,
164995
- role: "assistant",
164996
- text: "",
164997
- status: "sending",
164998
- runStatus: "accepted",
164999
- createdAtMillis: createdAtMillis + 1
165000
- }
165001
- ]);
165002
- return await submitTurn(turn);
165003
- }, [
165004
- catalog,
165005
- composerDraftKey,
165006
- messages,
165007
- profileUserId,
165008
- sendDisabled,
165009
- session,
165010
- submitTurn
165011
- ]);
165012
- const selectModel = (0, react$1.useCallback)(async (routeKey) => {
165013
- if (modelSelectionInFlightRef.current || sendInFlightRef.current || clearInFlightRef.current || loading || interactionLocked || clearing || catalog === void 0 || routeKey === catalog.effectiveRouteKey || !catalog.options.some((option) => option.routeKey === routeKey)) return false;
165014
- modelSelectionInFlightRef.current = true;
165015
- setSelectingModel(true);
165016
- setError("");
165017
- try {
165018
- const next = await callArkme("arko.model.activate", { routeKey });
165019
- if (!alive.current) return false;
165020
- setCatalog(next);
165021
- return true;
165022
- } catch (caught) {
165023
- setError(errorMessage$8(caught));
165024
- return false;
165025
- } finally {
165026
- modelSelectionInFlightRef.current = false;
165027
- setSelectingModel(false);
165028
- }
165029
- }, [
165030
- catalog,
165031
- clearing,
165032
- interactionLocked,
165033
- loading
165034
- ]);
165035
- const cancelActiveRun = (0, react$1.useCallback)(async () => {
165036
- if (activeRun === void 0 || cancelling) return false;
165037
- setCancelling(true);
165038
- setError("");
165039
- try {
165040
- await callArkme("arko.cancel", {
165041
- sessionId: activeRun.sessionId,
165042
- assistantMsgId: activeRun.assistantMsgId,
165043
- runUid: activeRun.runUid
165044
- });
165045
- setNotice("已请求停止当前任务,正在确认最终状态");
165046
- return true;
165047
- } catch (caught) {
165048
- setError(tr$1("停止 Arko 任务失败:{v0}", { v0: errorMessage$8(caught) }));
165049
- return false;
165050
- } finally {
165051
- setCancelling(false);
165052
- }
165053
- }, [activeRun, cancelling]);
165054
- const clearContext = (0, react$1.useCallback)(async () => {
165055
- if (clearInFlightRef.current || sendInFlightRef.current || modelSelectionInFlightRef.current || loading || interactionLocked || clearing) return;
165056
- clearInFlightRef.current = true;
165057
- setClearing(true);
165058
- setError("");
165059
- setNotice("");
165060
- try {
165061
- const nextSession = await callArkme("arko.new-session");
165062
- if (!alive.current) return;
165063
- setSession(nextSession);
165064
- setMessages((current) => [...current, {
165065
- id: crypto.randomUUID(),
165066
- role: "divider",
165067
- text: "新的对话",
165068
- status: "done",
165069
- createdAtMillis: Date.now()
165070
- }]);
165071
- setNotice("上下文已清除,历史记录仍然保留");
165072
- } catch (caught) {
165073
- setError(errorMessage$8(caught));
165074
- } finally {
165075
- clearInFlightRef.current = false;
165076
- setClearing(false);
165077
- }
165078
- }, [
165079
- clearing,
165080
- interactionLocked,
165081
- loading
165082
- ]);
165083
- return {
165084
- accountKey,
165085
- profileUserId,
165086
- composerDraftKey,
165087
- draftSnapshot,
165088
- draft,
165089
- userProfile,
165090
- session,
165091
- catalog,
165092
- messages,
165093
- historyOffset,
165094
- historyError,
165095
- historyPageError,
165096
- loading,
165097
- historyLoading,
165098
- sending,
165099
- clearing,
165100
- selectingModel,
165101
- cancelling,
165102
- error,
165103
- notice,
165104
- activeRun,
165105
- pendingTurn,
165106
- interactionLocked,
165107
- sendDisabled,
165108
- inputDisabled,
165109
- displayName: arkoPresentationName(profile),
165110
- selectedModel: selectedModelName(catalog),
165111
- canChooseModel: (catalog?.options.length ?? 0) > 1,
165112
- continuation: (0, react$1.useMemo)(() => latestContinuation(messages, session?.sessionId), [messages, session?.sessionId]),
165113
- send,
165114
- submitTurn,
165115
- selectModel,
165116
- cancelActiveRun,
165117
- clearContext,
165118
- loadEarlier,
165119
- retryHistory,
165120
- setDraft: (text) => arkmeComposerDraftStore.setText(composerDraftKey, text),
165121
- feedback: setError
165122
- };
165123
- }
165124
- //#endregion
165125
163493
  //#region src/harness-conversation-layout-contract.ts
165126
163494
  /** Optional, presentation-only exports from the installed Harness runtime. */
165127
163495
  const HARNESS_LAYOUT_MODULES = {
@@ -165591,6 +163959,62 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
165591
163959
  r$3.displayName = "ArrowsClockwiseIcon";
165592
163960
  const m$4 = r$3;
165593
163961
  //#endregion
163962
+ //#region src/client/arko-pending-turn-store.ts
163963
+ const STORAGE_KEY_PREFIX = "arkme:arko:pending-turn:v1:";
163964
+ const UUID_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;
163965
+ function defaultStorage() {
163966
+ try {
163967
+ return typeof globalThis.sessionStorage === "undefined" ? void 0 : globalThis.sessionStorage;
163968
+ } catch {
163969
+ return;
163970
+ }
163971
+ }
163972
+ function storageKey(userId) {
163973
+ return `${STORAGE_KEY_PREFIX}${String(userId)}`;
163974
+ }
163975
+ function optionalTrimmedString(value) {
163976
+ if (typeof value !== "string") return void 0;
163977
+ const normalized = value.trim();
163978
+ return normalized === "" ? void 0 : normalized;
163979
+ }
163980
+ function isValidArkoPendingTurn(value, expectedUserId) {
163981
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
163982
+ const turn = value;
163983
+ const hasContinuationRun = optionalTrimmedString(turn.replyToRunUid) !== void 0;
163984
+ const hasContinuationMessage = Number.isSafeInteger(turn.replyToAssistantMsgId) && (turn.replyToAssistantMsgId ?? 0) > 0;
163985
+ return Number.isSafeInteger(turn.userId) && (turn.userId ?? 0) > 0 && (expectedUserId === void 0 || turn.userId === expectedUserId) && Number.isSafeInteger(turn.sessionId) && (turn.sessionId ?? 0) > 0 && typeof turn.clientTurnUid === "string" && UUID_PATTERN.test(turn.clientTurnUid) && typeof turn.text === "string" && turn.text.trim() !== "" && Number.isSafeInteger(turn.createdAtMillis) && (turn.createdAtMillis ?? 0) > 0 && optionalTrimmedString(turn.localUserMessageId) !== void 0 && optionalTrimmedString(turn.localAssistantMessageId) !== void 0 && (turn.modelRouteKey === void 0 || optionalTrimmedString(turn.modelRouteKey) !== void 0) && hasContinuationRun === hasContinuationMessage && (!hasContinuationRun || turn.modelRouteKey === void 0);
163986
+ }
163987
+ function readArkoPendingTurn(userId, storage = defaultStorage()) {
163988
+ if (!Number.isSafeInteger(userId) || userId <= 0 || storage === void 0) return void 0;
163989
+ try {
163990
+ const encoded = storage.getItem(storageKey(userId));
163991
+ if (encoded === null) return void 0;
163992
+ const value = JSON.parse(encoded);
163993
+ if (!isValidArkoPendingTurn(value, userId)) {
163994
+ storage.removeItem(storageKey(userId));
163995
+ return;
163996
+ }
163997
+ return value;
163998
+ } catch {
163999
+ return;
164000
+ }
164001
+ }
164002
+ function writeArkoPendingTurn(turn, storage = defaultStorage()) {
164003
+ if (!isValidArkoPendingTurn(turn) || storage === void 0) return false;
164004
+ try {
164005
+ storage.setItem(storageKey(turn.userId), JSON.stringify(turn));
164006
+ return true;
164007
+ } catch {
164008
+ return false;
164009
+ }
164010
+ }
164011
+ function removeArkoPendingTurn(userId, storage = defaultStorage()) {
164012
+ if (!Number.isSafeInteger(userId) || userId <= 0 || storage === void 0) return;
164013
+ try {
164014
+ storage.removeItem(storageKey(userId));
164015
+ } catch {}
164016
+ }
164017
+ //#endregion
165594
164018
  //#region src/client/conversation-composer-presentation.ts
165595
164019
  const arkmeConversationComposerMaxHeight = 336;
165596
164020
  /** Keep each border property present across target and resize transitions. */
@@ -166949,7 +165373,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
166949
165373
  }]))
166950
165374
  };
166951
165375
  }
166952
- function errorMessage$7(error) {
165376
+ function errorMessage$8(error) {
166953
165377
  if (error instanceof ArkmeClientError) return error.body.message;
166954
165378
  return error instanceof Error ? error.message : String(error);
166955
165379
  }
@@ -167264,7 +165688,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
167264
165688
  await copyText$3(value);
167265
165689
  showStatus("已复制", revision);
167266
165690
  } catch (error) {
167267
- showStatus(errorMessage$7(error) || "复制失败,请稍后重试", revision);
165691
+ showStatus(errorMessage$8(error) || "复制失败,请稍后重试", revision);
167268
165692
  }
167269
165693
  }, [showStatus]);
167270
165694
  const copyLink = (0, react$1.useCallback)(async (items) => {
@@ -167292,7 +165716,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
167292
165716
  setSelectedIds(void 0);
167293
165717
  showStatus("已复制链接");
167294
165718
  } catch (error) {
167295
- if (revision === revisionRef.current) showStatus(controller.signal.aborted ? "复制链接超时,请重试" : errorMessage$7(error) || "分享失败,请重试");
165719
+ if (revision === revisionRef.current) showStatus(controller.signal.aborted ? "复制链接超时,请重试" : errorMessage$8(error) || "分享失败,请重试");
167296
165720
  } finally {
167297
165721
  window.clearTimeout(timeout);
167298
165722
  if (revision === revisionRef.current) setBusy(void 0);
@@ -167355,7 +165779,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
167355
165779
  setPicker((current) => current === void 0 ? current : {
167356
165780
  ...current,
167357
165781
  loading: false,
167358
- error: controller.signal.aborted ? "转发对象加载超时" : errorMessage$7(error) || "转发对象加载失败"
165782
+ error: controller.signal.aborted ? "转发对象加载超时" : errorMessage$8(error) || "转发对象加载失败"
167359
165783
  });
167360
165784
  } finally {
167361
165785
  window.clearTimeout(timeout);
@@ -167422,7 +165846,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
167422
165846
  const warningText = successes.map((success) => success.value.result.warningText?.trim() ?? "").find((value) => value !== "") ?? "转发已完成,附言发送失败";
167423
165847
  for (const success of successes) input.onForwarded?.(success.value.target, success.value.result);
167424
165848
  if (successes.length === 0) {
167425
- const message = controller.signal.aborted ? "转发超时,请重试" : errorMessage$7(results[0]?.reason) || "发送失败,请重试";
165849
+ const message = controller.signal.aborted ? "转发超时,请重试" : errorMessage$8(results[0]?.reason) || "发送失败,请重试";
167426
165850
  setPicker((current) => current === void 0 ? current : {
167427
165851
  ...current,
167428
165852
  error: message
@@ -167794,6 +166218,14 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
167794
166218
  }
167795
166219
  //#endregion
167796
166220
  //#region src/client/ArkmeArkoSurface.tsx
166221
+ const arkoQuestionMaxLength = 60 * 1024;
166222
+ const ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set([
166223
+ "accepted",
166224
+ "queued",
166225
+ "running",
166226
+ "stream_timeout",
166227
+ "waiting_tool"
166228
+ ]);
167797
166229
  const colors$5 = {
167798
166230
  text: arkmeTheme.text,
167799
166231
  secondary: arkmeTheme.secondary,
@@ -168296,6 +166728,10 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168296
166728
  },
168297
166729
  stop: { background: colors$5.danger }
168298
166730
  };
166731
+ function errorMessage$7(error) {
166732
+ if (error instanceof ArkmeClientError) return error.body.message;
166733
+ return error instanceof Error ? error.message : String(error);
166734
+ }
168299
166735
  function arkoRunActivityLabel(status) {
168300
166736
  if (status === "accepted" || status === "queued") return "正在思考";
168301
166737
  if (status === "running" || status === "stream_timeout") return tr$1("正在处理");
@@ -168313,6 +166749,29 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168313
166749
  function arkoPreservedScrollTop(previousScrollTop, previousScrollHeight, nextScrollHeight) {
168314
166750
  return Math.max(0, previousScrollTop + (nextScrollHeight - previousScrollHeight));
168315
166751
  }
166752
+ function isActiveRunStatus(status) {
166753
+ return status !== void 0 && ACTIVE_RUN_STATUSES.has(status);
166754
+ }
166755
+ function isActivityPlaceholderText(value) {
166756
+ return /^(正在)?(思考|处理)(中)?[.。…]*$/.test(value.trim());
166757
+ }
166758
+ function isActiveHistoryRun(item) {
166759
+ const placeholder = isActivityPlaceholderText(item.text);
166760
+ return item.role === "assistant" && (isActiveRunStatus(item.runStatus) || item.runStatus === void 0 && item.status === 2 && (item.text.trim() === "" || placeholder));
166761
+ }
166762
+ function arkoHistoryHasTerminalRun(items, sessionId, assistantMsgId, runUid) {
166763
+ const matchingItem = items.find((item) => item.role === "assistant" && item.sessionId === sessionId && item.messageId === assistantMsgId && (item.runUid === void 0 || item.runUid === runUid));
166764
+ return matchingItem !== void 0 && !isActiveHistoryRun(matchingItem);
166765
+ }
166766
+ function waitForNextPoll(signal, delayMillis = 1200) {
166767
+ return new Promise((resolve) => {
166768
+ const timeout = setTimeout(resolve, delayMillis);
166769
+ signal.addEventListener("abort", () => {
166770
+ clearTimeout(timeout);
166771
+ resolve();
166772
+ }, { once: true });
166773
+ });
166774
+ }
168316
166775
  function ArkoThinkingPanel({ reasoning, activity }) {
168317
166776
  useArkmeLocale();
168318
166777
  const [expanded, setExpanded] = (0, react$1.useState)(true);
@@ -168359,37 +166818,115 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168359
166818
  })]
168360
166819
  });
168361
166820
  }
168362
- function ArkmeArkoSurface({ native = false } = {}) {
168363
- const controller = useArkoConversationController();
168364
- return native ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkoNativeConversation, { controller }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkoConversationView, { controller });
166821
+ function historyMessage(item) {
166822
+ const placeholder = isActivityPlaceholderText(item.text);
166823
+ const reasoningPlaceholder = isActivityPlaceholderText(item.reasoning);
166824
+ const active = isActiveHistoryRun(item);
166825
+ return {
166826
+ id: `history:${String(item.messageId)}`,
166827
+ messageId: item.messageId,
166828
+ sessionId: item.sessionId,
166829
+ role: item.role,
166830
+ text: active && placeholder ? "" : item.text,
166831
+ status: active ? "sending" : item.runStatus === "failed" ? "error" : "done",
166832
+ createdAtMillis: item.createdAtMillis,
166833
+ ...item.reasoning.trim() === "" || !active && reasoningPlaceholder ? {} : { reasoning: item.reasoning },
166834
+ ...item.role !== "assistant" ? {} : { assistantMsgId: item.messageId },
166835
+ ...item.runUid === void 0 ? {} : { runUid: item.runUid },
166836
+ ...item.runStatus === void 0 ? {} : { runStatus: item.runStatus },
166837
+ ...item.messageActionRef === void 0 ? {} : {
166838
+ messageActionRef: item.messageActionRef,
166839
+ ...item.messageActionConversationRef === void 0 ? {} : { messageActionConversationRef: item.messageActionConversationRef },
166840
+ copyLinkAvailable: item.messageActionCapabilities?.copyLink === true,
166841
+ forwardAvailable: item.messageActionCapabilities?.forward === true
166842
+ }
166843
+ };
166844
+ }
166845
+ function latestActiveRun(items, sessionId) {
166846
+ const item = items.filter((candidate) => candidate.sessionId === sessionId && candidate.role === "assistant" && candidate.runUid !== void 0 && isActiveRunStatus(candidate.runStatus)).sort((left, right) => right.createdAtMillis - left.createdAtMillis || right.messageId - left.messageId)[0];
166847
+ if (item?.runUid === void 0) return void 0;
166848
+ return {
166849
+ sessionId: item.sessionId,
166850
+ assistantMsgId: item.messageId,
166851
+ runUid: item.runUid
166852
+ };
166853
+ }
166854
+ function mergeHistory(current, items) {
166855
+ const byId = new Map(current.map((item) => [item.id, item]));
166856
+ for (const item of items) byId.set(`history:${String(item.messageId)}`, historyMessage(item));
166857
+ return [...byId.values()].sort((left, right) => {
166858
+ const leftAt = left.createdAtMillis ?? Number.MAX_SAFE_INTEGER;
166859
+ const rightAt = right.createdAtMillis ?? Number.MAX_SAFE_INTEGER;
166860
+ if (leftAt !== rightAt) return leftAt - rightAt;
166861
+ return (left.messageId ?? Number.MAX_SAFE_INTEGER) - (right.messageId ?? Number.MAX_SAFE_INTEGER);
166862
+ });
166863
+ }
166864
+ function resultText(result) {
166865
+ if (result.text.trim() !== "") return result.text.trim();
166866
+ if (result.errorMessage?.trim()) return result.errorMessage.trim();
166867
+ if (isActiveRunStatus(result.run?.status ?? result.status)) return "";
166868
+ return "任务已处理。";
168365
166869
  }
168366
- function ArkoConversationView({ controller }) {
166870
+ function latestContinuation(messages, sessionId) {
166871
+ if (sessionId === void 0) return void 0;
166872
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
166873
+ const message = messages[index];
166874
+ if (message === void 0) continue;
166875
+ if (message.role === "divider") return void 0;
166876
+ if (message.sessionId !== sessionId) continue;
166877
+ if (message.role === "user") return void 0;
166878
+ if (message.runStatus !== "waiting_user" || message.runUid === void 0 || message.assistantMsgId === void 0) return;
166879
+ return {
166880
+ runUid: message.runUid,
166881
+ assistantMsgId: message.assistantMsgId
166882
+ };
166883
+ }
166884
+ }
166885
+ function selectedModelName(catalog) {
166886
+ return catalog?.options.find((option) => option.routeKey === catalog.effectiveRouteKey)?.displayName ?? "模型目录暂不可用";
166887
+ }
166888
+ function ArkmeArkoSurface() {
168367
166889
  useArkmeLocale();
168368
- const { accountKey, profileUserId, composerDraftKey, draftSnapshot, draft, userProfile, session, catalog, messages, historyOffset, historyError, historyPageError, loading, historyLoading, sending, clearing, selectingModel, cancelling, error, notice, activeRun, pendingTurn, interactionLocked, sendDisabled, inputDisabled, displayName, selectedModel, canChooseModel, continuation, submitTurn, cancelActiveRun, retryHistory, feedback: setError } = controller;
168369
166890
  const bodyRef = (0, react$1.useRef)(null);
168370
166891
  const historySentinelRef = (0, react$1.useRef)(null);
166892
+ const historyLoadInFlightRef = (0, react$1.useRef)(false);
168371
166893
  const composerInputBoundaryRef = (0, react$1.useRef)(null);
168372
166894
  const composerRef = (0, react$1.useRef)(null);
168373
166895
  const textareaRef = (0, react$1.useRef)(null);
168374
166896
  const pendingComposerFocusRef = (0, react$1.useRef)(false);
166897
+ const sendInFlightRef = (0, react$1.useRef)(false);
166898
+ const authSnapshot = (0, react$1.useSyncExternalStore)(arkmeAuthStore.subscribe, arkmeAuthStore.getSnapshot, arkmeAuthStore.getSnapshot);
166899
+ const profileSnapshot = (0, react$1.useSyncExternalStore)(arkmeArkoProfileStore.subscribe, arkmeArkoProfileStore.getSnapshot, arkmeArkoProfileStore.getSnapshot);
166900
+ const profileUserId = authSnapshot.auth?.status === "authenticated" ? authSnapshot.auth.userId : void 0;
166901
+ const composerDraftKey = arkmeArkoComposerDraftKey(profileUserId);
166902
+ (0, react$1.useSyncExternalStore)(arkmeComposerDraftStore.subscribe, arkmeComposerDraftStore.getRevision, arkmeComposerDraftStore.getRevision);
166903
+ const draftSnapshot = arkmeComposerDraftStore.get(composerDraftKey);
166904
+ const draft = draftSnapshot.text;
166905
+ const accountKey = authSnapshot.auth?.status === "authenticated" && profileUserId !== void 0 ? `${authSnapshot.auth.environment}:${String(profileUserId)}` : void 0;
166906
+ const profile = profileSnapshot.userId === profileUserId ? profileSnapshot.profile : void 0;
166907
+ const [userProfile, setUserProfile] = (0, react$1.useState)(null);
166908
+ const [session, setSession] = (0, react$1.useState)();
166909
+ const [catalog, setCatalog] = (0, react$1.useState)();
166910
+ const [messages, setMessages] = (0, react$1.useState)([]);
166911
+ const [historyOffset, setHistoryOffset] = (0, react$1.useState)();
166912
+ const [historyError, setHistoryError] = (0, react$1.useState)("");
166913
+ const [loading, setLoading] = (0, react$1.useState)(true);
166914
+ const [historyLoading, setHistoryLoading] = (0, react$1.useState)(false);
166915
+ const [sending, setSending] = (0, react$1.useState)(false);
166916
+ const [clearing, setClearing] = (0, react$1.useState)(false);
166917
+ const [selectingModel, setSelectingModel] = (0, react$1.useState)(false);
166918
+ const [cancelling, setCancelling] = (0, react$1.useState)(false);
168375
166919
  const [modelDialogOpen, setModelDialogOpen] = (0, react$1.useState)(false);
168376
166920
  const [clearConfirmOpen, setClearConfirmOpen] = (0, react$1.useState)(false);
168377
- const send = (text) => {
168378
- pendingComposerFocusRef.current = true;
168379
- return controller.send(text);
168380
- };
168381
- const selectModel = async (routeKey) => {
168382
- if (await controller.selectModel(routeKey)) setModelDialogOpen(false);
168383
- };
168384
- const clearContext = async () => {
168385
- setClearConfirmOpen(false);
168386
- await controller.clearContext();
168387
- };
166921
+ const [error, setError] = (0, react$1.useState)("");
166922
+ const [notice, setNotice] = (0, react$1.useState)("");
166923
+ const [activeRun, setActiveRun] = (0, react$1.useState)();
166924
+ const [pendingTurn, setPendingTurn] = (0, react$1.useState)();
168388
166925
  const detailTriggerRef = (0, react$1.useRef)(null);
168389
166926
  const detailBodyRef = (0, react$1.useRef)(null);
168390
- const detailScope = `${accountKey ?? ""}:${String(profileUserId)}:${String(session?.sessionId)}`;
166927
+ const detailScope = `${authSnapshot.auth?.environment ?? ""}:${String(profileUserId)}:${String(session?.sessionId)}`;
168391
166928
  const [detailSelection, setDetailSelection] = (0, react$1.useState)();
168392
- const detailMessage = detailSelection?.scope === detailScope ? messages.find((message) => (message.presentationKey ?? message.id) === detailSelection.id && message.role !== "divider") : void 0;
166929
+ const detailMessage = detailSelection?.scope === detailScope ? messages.find((message) => message.id === detailSelection.id && message.role !== "divider") : void 0;
168393
166930
  const detailActivity = detailMessage?.status === "sending" ? arkoRunActivityLabel(detailMessage.runStatus) ?? "等待回复" : detailMessage?.status === "error" ? pendingTurn?.localAssistantMessageId === detailMessage.id ? "发送结果待确认" : "消息处理失败" : void 0;
168394
166931
  (0, react$1.useEffect)(() => {
168395
166932
  setDetailSelection(void 0);
@@ -168400,7 +166937,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168400
166937
  trigger.focus({ preventScroll: true });
168401
166938
  setDetailSelection({
168402
166939
  scope: detailScope,
168403
- id: messages.find((message) => message.id === id)?.presentationKey ?? id
166940
+ id
168404
166941
  });
168405
166942
  };
168406
166943
  const messageActionItems = (0, react$1.useMemo)(() => messages.flatMap((message) => message.role === "divider" || message.messageActionRef === void 0 || message.messageActionConversationRef === void 0 ? [] : [{
@@ -168429,33 +166966,195 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168429
166966
  if (bodyRef.current !== null) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
168430
166967
  });
168431
166968
  }, []);
168432
- const lastMessage = messages.at(-1);
168433
166969
  (0, react$1.useEffect)(() => {
166970
+ arkmeArkoProfileStore.activateUser(profileUserId);
166971
+ arkmeArkoConversationPreviewStore.activateUser(profileUserId);
166972
+ }, [profileUserId]);
166973
+ (0, react$1.useEffect)(() => {
166974
+ if (profileUserId === void 0) return;
166975
+ arkmeArkoConversationPreviewStore.setLatestFromSurface(profileUserId, messages.filter((message) => message.role !== "divider").map((message) => ({
166976
+ key: message.id,
166977
+ text: message.text,
166978
+ ...message.messageId === void 0 ? {} : { messageId: message.messageId },
166979
+ ...message.createdAtMillis === void 0 ? {} : { createdAtMillis: message.createdAtMillis }
166980
+ })));
166981
+ }, [messages, profileUserId]);
166982
+ (0, react$1.useEffect)(() => {
166983
+ if (profileUserId === void 0) {
166984
+ setPendingTurn(void 0);
166985
+ return;
166986
+ }
166987
+ const restored = readArkoPendingTurn(profileUserId);
166988
+ setPendingTurn(restored);
166989
+ if (restored === void 0) return;
166990
+ setMessages((current) => {
166991
+ if (current.some((item) => item.id === restored.localAssistantMessageId)) return current;
166992
+ return [
166993
+ ...current,
166994
+ {
166995
+ id: restored.localUserMessageId,
166996
+ sessionId: restored.sessionId,
166997
+ role: "user",
166998
+ text: restored.text,
166999
+ status: "done",
167000
+ createdAtMillis: restored.createdAtMillis
167001
+ },
167002
+ {
167003
+ id: restored.localAssistantMessageId,
167004
+ sessionId: restored.sessionId,
167005
+ role: "assistant",
167006
+ text: "",
167007
+ status: "error",
167008
+ createdAtMillis: restored.createdAtMillis + 1
167009
+ }
167010
+ ];
167011
+ });
168434
167012
  scrollToBottom();
167013
+ }, [profileUserId, scrollToBottom]);
167014
+ (0, react$1.useEffect)(() => {
167015
+ const controller = new AbortController();
167016
+ setLoading(true);
167017
+ setError("");
167018
+ setHistoryError("");
167019
+ Promise.allSettled([
167020
+ callArkme("arko.session", void 0, controller.signal),
167021
+ callArkme("arko.profile", void 0, controller.signal),
167022
+ callArkme("arko.models", void 0, controller.signal),
167023
+ callArkme("arko.history", {
167024
+ limit: 50,
167025
+ offset: 0
167026
+ }, controller.signal),
167027
+ callArkme("user.profile", void 0, controller.signal).then(async (snapshot) => snapshot.profile === null ? await callArkme("user.profile.refresh", void 0, controller.signal) : snapshot)
167028
+ ]).then(([sessionResult, profileResult, modelResult, historyResult, userProfileResult]) => {
167029
+ if (controller.signal.aborted) return;
167030
+ if (sessionResult.status === "rejected") {
167031
+ setError(errorMessage$7(sessionResult.reason));
167032
+ return;
167033
+ }
167034
+ setSession(sessionResult.value);
167035
+ if (profileResult.status === "fulfilled" && profileUserId !== void 0) arkmeArkoProfileStore.setProfile(profileUserId, profileResult.value);
167036
+ if (modelResult.status === "fulfilled") setCatalog(modelResult.value);
167037
+ if (userProfileResult.status === "fulfilled") setUserProfile(userProfileResult.value.profile);
167038
+ if (historyResult.status === "fulfilled") {
167039
+ setMessages((current) => mergeHistory(current, historyResult.value.items));
167040
+ setHistoryOffset(historyResult.value.nextOffset);
167041
+ scrollToBottom();
167042
+ const restoredRun = latestActiveRun(historyResult.value.items, sessionResult.value.sessionId);
167043
+ if (restoredRun !== void 0) {
167044
+ setActiveRun(restoredRun);
167045
+ setSending(true);
167046
+ }
167047
+ } else setHistoryError(tr$1("加载 Arko 对话记录失败:{v0}", { v0: errorMessage$7(historyResult.reason) }));
167048
+ }).finally(() => {
167049
+ if (!controller.signal.aborted) setLoading(false);
167050
+ });
167051
+ return () => {
167052
+ controller.abort();
167053
+ };
167054
+ }, [profileUserId, scrollToBottom]);
167055
+ (0, react$1.useEffect)(() => {
167056
+ if (activeRun === void 0) return;
167057
+ const controller = new AbortController();
167058
+ let consecutiveFailures = 0;
167059
+ const finishRun = async (knownHistory) => {
167060
+ const [historyResult, profileResult] = await Promise.allSettled([knownHistory === void 0 ? callArkme("arko.history", {
167061
+ limit: 50,
167062
+ offset: 0
167063
+ }, controller.signal) : Promise.resolve(knownHistory), callArkme("arko.profile", void 0, controller.signal)]);
167064
+ if (controller.signal.aborted) return;
167065
+ if (historyResult.status === "fulfilled") {
167066
+ setMessages((current) => mergeHistory(current, historyResult.value.items));
167067
+ setHistoryOffset(historyResult.value.nextOffset);
167068
+ setHistoryError("");
167069
+ scrollToBottom();
167070
+ } else setHistoryError(tr$1("刷新 Arko 对话记录失败:{v0}", { v0: errorMessage$7(historyResult.reason) }));
167071
+ if (profileResult.status === "fulfilled" && profileUserId !== void 0) arkmeArkoProfileStore.setProfile(profileUserId, profileResult.value);
167072
+ setActiveRun((current) => current?.runUid === activeRun.runUid ? void 0 : current);
167073
+ setSending(false);
167074
+ };
167075
+ const poll = async () => {
167076
+ await waitForNextPoll(controller.signal);
167077
+ while (!controller.signal.aborted) {
167078
+ try {
167079
+ const status = await callArkme("arko.run.status", {
167080
+ sessionId: activeRun.sessionId,
167081
+ runUid: activeRun.runUid
167082
+ }, controller.signal);
167083
+ if (controller.signal.aborted) return;
167084
+ consecutiveFailures = 0;
167085
+ if (status.status === "waiting_tool") setNotice("当前任务需要 DSH 尚未支持的客户端操作,可以停止任务后换一种方式重试");
167086
+ setMessages((current) => current.map((item) => item.assistantMsgId === activeRun.assistantMsgId ? {
167087
+ ...item,
167088
+ runStatus: status.status,
167089
+ status: isActiveRunStatus(status.status) ? "sending" : status.status === "failed" ? "error" : "done"
167090
+ } : item));
167091
+ if (!isActiveRunStatus(status.status)) {
167092
+ await finishRun();
167093
+ return;
167094
+ }
167095
+ } catch {
167096
+ if (controller.signal.aborted) return;
167097
+ consecutiveFailures += 1;
167098
+ try {
167099
+ const history = await callArkme("arko.history", {
167100
+ limit: 50,
167101
+ offset: 0
167102
+ }, controller.signal);
167103
+ if (controller.signal.aborted) return;
167104
+ if (arkoHistoryHasTerminalRun(history.items, activeRun.sessionId, activeRun.assistantMsgId, activeRun.runUid)) {
167105
+ await finishRun(history);
167106
+ return;
167107
+ }
167108
+ } catch {
167109
+ if (controller.signal.aborted) return;
167110
+ }
167111
+ }
167112
+ const retryDelay = consecutiveFailures === 0 ? 1200 : Math.min(1e4, 1200 * 2 ** Math.min(consecutiveFailures - 1, 3));
167113
+ await waitForNextPoll(controller.signal, retryDelay);
167114
+ }
167115
+ };
167116
+ poll();
167117
+ return () => {
167118
+ controller.abort();
167119
+ };
168435
167120
  }, [
168436
- lastMessage?.id,
168437
- lastMessage?.text,
168438
- lastMessage?.reasoning,
167121
+ activeRun,
167122
+ profileUserId,
168439
167123
  scrollToBottom
168440
167124
  ]);
168441
167125
  const loadEarlier = (0, react$1.useCallback)(async () => {
167126
+ if (historyOffset === void 0 || historyLoadInFlightRef.current) return;
168442
167127
  const body = bodyRef.current;
168443
167128
  const previousScrollHeight = body?.scrollHeight ?? 0;
168444
167129
  const previousScrollTop = body?.scrollTop ?? 0;
167130
+ historyLoadInFlightRef.current = true;
167131
+ setHistoryLoading(true);
168445
167132
  try {
168446
- await controller.loadEarlier();
167133
+ const page = await callArkme("arko.history", {
167134
+ limit: 50,
167135
+ offset: historyOffset
167136
+ });
167137
+ setMessages((current) => mergeHistory(current, page.items));
167138
+ setHistoryOffset(page.nextOffset);
167139
+ setHistoryError("");
168447
167140
  requestAnimationFrame(() => {
168448
167141
  const target = bodyRef.current;
168449
- if (target) target.scrollTop = arkoPreservedScrollTop(previousScrollTop, previousScrollHeight, target.scrollHeight);
167142
+ if (target === null) return;
167143
+ target.scrollTop = arkoPreservedScrollTop(previousScrollTop, previousScrollHeight, target.scrollHeight);
168450
167144
  });
168451
- } catch {}
168452
- }, [controller.loadEarlier]);
167145
+ } catch (caught) {
167146
+ setHistoryError(tr$1("加载更早的 Arko 对话记录失败:{v0}", { v0: errorMessage$7(caught) }));
167147
+ } finally {
167148
+ historyLoadInFlightRef.current = false;
167149
+ setHistoryLoading(false);
167150
+ }
167151
+ }, [historyOffset]);
168453
167152
  (0, react$1.useEffect)(() => {
168454
167153
  const root = bodyRef.current;
168455
167154
  const sentinel = historySentinelRef.current;
168456
- if (loading || historyLoading || historyError !== "" || historyPageError !== "" || root === null || sentinel === null || typeof historyOffset !== "number") return;
167155
+ if (loading || historyLoading || historyError !== "" || root === null || sentinel === null || historyOffset === void 0) return;
168457
167156
  const observer = new IntersectionObserver((entries) => {
168458
- if (entries[0]?.isIntersecting !== true) return;
167157
+ if (entries[0]?.isIntersecting !== true || historyLoadInFlightRef.current) return;
168459
167158
  loadEarlier();
168460
167159
  }, {
168461
167160
  root,
@@ -168466,19 +167165,280 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168466
167165
  observer.disconnect();
168467
167166
  };
168468
167167
  }, [
168469
- historyPageError,
168470
167168
  historyError,
168471
167169
  historyLoading,
168472
167170
  historyOffset,
168473
167171
  loadEarlier,
168474
167172
  loading
168475
167173
  ]);
167174
+ const retryHistory = (0, react$1.useCallback)(async () => {
167175
+ if (historyLoading) return;
167176
+ setHistoryLoading(true);
167177
+ setHistoryError("");
167178
+ try {
167179
+ const page = await callArkme("arko.history", {
167180
+ limit: 50,
167181
+ offset: 0
167182
+ });
167183
+ setMessages((current) => mergeHistory(current, page.items));
167184
+ setHistoryOffset(page.nextOffset);
167185
+ scrollToBottom();
167186
+ if (session !== void 0) {
167187
+ const restoredRun = latestActiveRun(page.items, session.sessionId);
167188
+ if (restoredRun !== void 0) {
167189
+ setActiveRun(restoredRun);
167190
+ setSending(true);
167191
+ }
167192
+ }
167193
+ } catch (caught) {
167194
+ setHistoryError(tr$1("加载 Arko 对话记录失败:{v0}", { v0: errorMessage$7(caught) }));
167195
+ } finally {
167196
+ setHistoryLoading(false);
167197
+ }
167198
+ }, [
167199
+ historyLoading,
167200
+ scrollToBottom,
167201
+ session
167202
+ ]);
167203
+ const submitTurn = (0, react$1.useCallback)(async (turn) => {
167204
+ if (sending || activeRun !== void 0) return;
167205
+ let handedOffToPolling = false;
167206
+ setSending(true);
167207
+ setError("");
167208
+ setNotice("");
167209
+ setMessages((current) => current.map((item) => item.id === turn.localAssistantMessageId ? {
167210
+ ...item,
167211
+ text: "",
167212
+ status: "sending",
167213
+ runStatus: "accepted"
167214
+ } : item));
167215
+ try {
167216
+ const result = await callArkme("arko.ask", {
167217
+ text: turn.text,
167218
+ sessionId: turn.sessionId,
167219
+ clientTurnUid: turn.clientTurnUid,
167220
+ waitSeconds: 1,
167221
+ ...turn.modelRouteKey === void 0 ? {} : { modelRouteKey: turn.modelRouteKey },
167222
+ ...turn.replyToRunUid === void 0 ? {} : { replyToRunUid: turn.replyToRunUid },
167223
+ ...turn.replyToAssistantMsgId === void 0 ? {} : { replyToAssistantMsgId: turn.replyToAssistantMsgId }
167224
+ });
167225
+ removeArkoPendingTurn(turn.userId);
167226
+ setPendingTurn((current) => current?.clientTurnUid === turn.clientTurnUid ? void 0 : current);
167227
+ setSession((current) => current === void 0 ? current : {
167228
+ ...current,
167229
+ sessionId: result.sessionId
167230
+ });
167231
+ if (result.profile !== void 0 && profileUserId !== void 0) arkmeArkoProfileStore.setProfile(profileUserId, result.profile);
167232
+ const runStatus = result.run?.status ?? result.status;
167233
+ const runActive = isActiveRunStatus(runStatus);
167234
+ const hasVisibleReasoning = result.reasoning.trim() !== "" && (runActive || !isActivityPlaceholderText(result.reasoning));
167235
+ setMessages((current) => mergeHistory(current.map((item) => item.id === turn.localUserMessageId ? {
167236
+ ...item,
167237
+ id: `history:${String(result.userMsgId)}`,
167238
+ messageId: result.userMsgId,
167239
+ sessionId: result.sessionId
167240
+ } : item.id === turn.localAssistantMessageId ? {
167241
+ id: `history:${String(result.assistantMsgId)}`,
167242
+ messageId: result.assistantMsgId,
167243
+ sessionId: result.sessionId,
167244
+ role: "assistant",
167245
+ text: resultText(result),
167246
+ status: runActive ? "sending" : result.errorMessage === void 0 ? "done" : "error",
167247
+ assistantMsgId: result.assistantMsgId,
167248
+ ...item.createdAtMillis === void 0 ? {} : { createdAtMillis: item.createdAtMillis },
167249
+ ...hasVisibleReasoning ? { reasoning: result.reasoning } : {},
167250
+ ...result.runUid === void 0 ? {} : { runUid: result.runUid },
167251
+ runStatus
167252
+ } : item), []));
167253
+ setDetailSelection((current) => {
167254
+ if (current?.scope !== detailScope) return current;
167255
+ if (current.id === turn.localUserMessageId) return {
167256
+ ...current,
167257
+ id: `history:${String(result.userMsgId)}`
167258
+ };
167259
+ if (current.id === turn.localAssistantMessageId) return {
167260
+ ...current,
167261
+ id: `history:${String(result.assistantMsgId)}`
167262
+ };
167263
+ return current;
167264
+ });
167265
+ if (runActive && result.runUid !== void 0) {
167266
+ handedOffToPolling = true;
167267
+ setActiveRun({
167268
+ sessionId: result.sessionId,
167269
+ assistantMsgId: result.assistantMsgId,
167270
+ runUid: result.runUid
167271
+ });
167272
+ }
167273
+ } catch (caught) {
167274
+ const message = errorMessage$7(caught);
167275
+ if (!(caught instanceof ArkmeClientError) || caught.body.retryable) {
167276
+ setPendingTurn(turn);
167277
+ writeArkoPendingTurn(turn);
167278
+ setError(`Arko 发送结果暂未确认:${message}。请重试确认,系统会复用同一次请求,不会重复执行。`);
167279
+ setMessages((current) => current.map((item) => item.id === turn.localAssistantMessageId ? {
167280
+ ...item,
167281
+ role: "assistant",
167282
+ text: "",
167283
+ status: "error"
167284
+ } : item));
167285
+ } else {
167286
+ removeArkoPendingTurn(turn.userId);
167287
+ setPendingTurn((current) => current?.clientTurnUid === turn.clientTurnUid ? void 0 : current);
167288
+ setError(message);
167289
+ setMessages((current) => current.map((item) => item.id === turn.localAssistantMessageId ? {
167290
+ ...item,
167291
+ role: "assistant",
167292
+ text: message,
167293
+ status: "error"
167294
+ } : item));
167295
+ }
167296
+ } finally {
167297
+ if (!handedOffToPolling) setSending(false);
167298
+ }
167299
+ }, [
167300
+ activeRun,
167301
+ detailScope,
167302
+ profileUserId,
167303
+ sending
167304
+ ]);
167305
+ const interactionLocked = sending || pendingTurn !== void 0 || activeRun !== void 0;
167306
+ const sendDisabled = loading || interactionLocked || clearing || selectingModel || session === void 0 || profileUserId === void 0;
167307
+ const inputDisabled = loading || interactionLocked || session === void 0 || profileUserId === void 0;
168476
167308
  const insertEmoji = (0, react$1.useCallback)((emoji) => {
168477
167309
  if (inputDisabled || composerDraftKey === void 0) return false;
168478
167310
  const result = textareaRef.current?.insertEmoji(emoji);
168479
167311
  if (result === "length-limit") setError("内容长度已达上限,请删减后再添加表情");
168480
167312
  return result === "inserted";
168481
167313
  }, [composerDraftKey, inputDisabled]);
167314
+ const send = (0, react$1.useCallback)(async (presetText) => {
167315
+ const text = (presetText ?? serializeArkmeComposerDraft(arkmeComposerDraftStore.get(composerDraftKey)).text).trim();
167316
+ if (text === "" || sendInFlightRef.current || sendDisabled || session === void 0 || profileUserId === void 0 || composerDraftKey === void 0) return;
167317
+ if (text.length > arkoQuestionMaxLength) {
167318
+ setError("内容长度超过上限,请删减后再发送");
167319
+ return;
167320
+ }
167321
+ sendInFlightRef.current = true;
167322
+ try {
167323
+ const continuation = latestContinuation(messages, session.sessionId);
167324
+ const createdAtMillis = Date.now();
167325
+ const turn = {
167326
+ userId: profileUserId,
167327
+ sessionId: session.sessionId,
167328
+ clientTurnUid: crypto.randomUUID(),
167329
+ text,
167330
+ createdAtMillis,
167331
+ localUserMessageId: crypto.randomUUID(),
167332
+ localAssistantMessageId: crypto.randomUUID(),
167333
+ ...continuation === void 0 ? { ...catalog === void 0 ? {} : { modelRouteKey: catalog.effectiveRouteKey } } : {
167334
+ replyToRunUid: continuation.runUid,
167335
+ replyToAssistantMsgId: continuation.assistantMsgId
167336
+ }
167337
+ };
167338
+ writeArkoPendingTurn(turn);
167339
+ pendingComposerFocusRef.current = true;
167340
+ setPendingTurn(turn);
167341
+ if (presetText === void 0) arkmeComposerDraftStore.clear(composerDraftKey);
167342
+ setMessages((current) => [
167343
+ ...current,
167344
+ {
167345
+ id: turn.localUserMessageId,
167346
+ sessionId: turn.sessionId,
167347
+ role: "user",
167348
+ text,
167349
+ status: "done",
167350
+ createdAtMillis
167351
+ },
167352
+ {
167353
+ id: turn.localAssistantMessageId,
167354
+ sessionId: turn.sessionId,
167355
+ role: "assistant",
167356
+ text: "",
167357
+ status: "sending",
167358
+ runStatus: "accepted",
167359
+ createdAtMillis: createdAtMillis + 1
167360
+ }
167361
+ ]);
167362
+ scrollToBottom();
167363
+ await submitTurn(turn);
167364
+ } finally {
167365
+ sendInFlightRef.current = false;
167366
+ }
167367
+ }, [
167368
+ catalog,
167369
+ composerDraftKey,
167370
+ messages,
167371
+ profileUserId,
167372
+ scrollToBottom,
167373
+ sendDisabled,
167374
+ session,
167375
+ submitTurn
167376
+ ]);
167377
+ const selectModel = (0, react$1.useCallback)(async (routeKey) => {
167378
+ if (selectingModel) return;
167379
+ setSelectingModel(true);
167380
+ setError("");
167381
+ try {
167382
+ const next = await callArkme("arko.model.activate", { routeKey });
167383
+ setCatalog(next);
167384
+ setModelDialogOpen(false);
167385
+ setNotice(tr$1("已切换到 {v0}", { v0: selectedModelName(next) }));
167386
+ } catch (caught) {
167387
+ setError(errorMessage$7(caught));
167388
+ } finally {
167389
+ setSelectingModel(false);
167390
+ }
167391
+ }, [selectingModel]);
167392
+ const cancelActiveRun = (0, react$1.useCallback)(async () => {
167393
+ if (activeRun === void 0 || cancelling) return;
167394
+ setCancelling(true);
167395
+ setError("");
167396
+ try {
167397
+ await callArkme("arko.cancel", {
167398
+ sessionId: activeRun.sessionId,
167399
+ assistantMsgId: activeRun.assistantMsgId,
167400
+ runUid: activeRun.runUid
167401
+ });
167402
+ setNotice("已请求停止当前任务,正在确认最终状态");
167403
+ } catch (caught) {
167404
+ setError(tr$1("停止 Arko 任务失败:{v0}", { v0: errorMessage$7(caught) }));
167405
+ } finally {
167406
+ setCancelling(false);
167407
+ }
167408
+ }, [activeRun, cancelling]);
167409
+ const clearContext = (0, react$1.useCallback)(async () => {
167410
+ if (sending || clearing || pendingTurn !== void 0) return;
167411
+ setClearConfirmOpen(false);
167412
+ setClearing(true);
167413
+ setError("");
167414
+ setNotice("");
167415
+ try {
167416
+ const nextSession = await callArkme("arko.new-session");
167417
+ setSession(nextSession);
167418
+ setMessages((current) => [...current, {
167419
+ id: crypto.randomUUID(),
167420
+ role: "divider",
167421
+ text: "新的对话",
167422
+ status: "done",
167423
+ createdAtMillis: Date.now()
167424
+ }]);
167425
+ scrollToBottom();
167426
+ setNotice("上下文已清除,历史记录仍然保留");
167427
+ } catch (caught) {
167428
+ setError(errorMessage$7(caught));
167429
+ } finally {
167430
+ setClearing(false);
167431
+ }
167432
+ }, [
167433
+ clearing,
167434
+ pendingTurn,
167435
+ scrollToBottom,
167436
+ sending
167437
+ ]);
167438
+ const displayName = arkoPresentationName(profile);
167439
+ const selectedModel = selectedModelName(catalog);
167440
+ const canChooseModel = (catalog?.options.length ?? 0) > 1;
167441
+ const continuation = (0, react$1.useMemo)(() => latestContinuation(messages, session?.sessionId), [messages, session?.sessionId]);
168482
167442
  (0, react$1.useEffect)(() => {
168483
167443
  if (!pendingComposerFocusRef.current || loading || interactionLocked || session === void 0 || profileUserId === void 0) return;
168484
167444
  pendingComposerFocusRef.current = false;
@@ -168601,23 +167561,6 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168601
167561
  "data-arko-message-viewport": true,
168602
167562
  style: styles$24.body,
168603
167563
  children: [
168604
- historyPageError && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
168605
- role: "alert",
168606
- style: {
168607
- ...styles$24.error,
168608
- ...styles$24.feedbackRow
168609
- },
168610
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: historyPageError }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
168611
- "data-arkme-feedback": "neutral",
168612
- type: "button",
168613
- style: styles$24.retryButton,
168614
- disabled: historyLoading,
168615
- onClick: () => {
168616
- loadEarlier();
168617
- },
168618
- children: tr$1("重试加载更早记录")
168619
- })]
168620
- }),
168621
167564
  notice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
168622
167565
  style: styles$24.notice,
168623
167566
  children: notice
@@ -168637,7 +167580,7 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
168637
167580
  style: styles$24.retryButton,
168638
167581
  disabled: historyLoading,
168639
167582
  onClick: () => {
168640
- if (typeof historyOffset !== "number") retryHistory();
167583
+ if (historyOffset === void 0) retryHistory();
168641
167584
  else loadEarlier();
168642
167585
  },
168643
167586
  children: historyLoading ? tr$1("加载中...") : tr$1("重新加载")
@@ -169120,89 +168063,6 @@ globalThis.__ARKME_NATIVE_ARKO__ = carrier;
169120
168063
  })]
169121
168064
  });
169122
168065
  }
169123
- function ArkoNativeConversation({ controller }) {
169124
- const { accountKey, session, catalog, messages, loading, sending, activeRun, draft, error, historyError, notice, historyOffset, historyPageError, inputDisabled, sendDisabled, selectModel, cancelActiveRun, loadEarlier, pendingTurn, submitTurn, retryHistory, setDraft, feedback: setError, send, displayName, clearing, selectingModel, continuation } = controller;
169125
- const [clearConfirmOpen, setClearConfirmOpen] = (0, react$1.useState)(false);
169126
- const clearContext = async () => {
169127
- setClearConfirmOpen(false);
169128
- await controller.clearContext();
169129
- };
169130
- const hint = loading ? "正在恢复会话" : clearing ? "正在清除上下文" : selectingModel ? "正在切换模型" : pendingTurn !== void 0 && !sending ? "请先确认上一次发送结果" : sending ? "等待回复" : continuation === void 0 ? "" : "继续当前任务";
169131
- if (accountKey === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
169132
- role: "status",
169133
- children: "正在恢复账号…"
169134
- });
169135
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkoNativeSurface, {
169136
- snapshot: {
169137
- accountKey,
169138
- displayName,
169139
- ...session ? { sessionId: session.sessionId } : {},
169140
- messages,
169141
- modelCatalog: catalog ?? null,
169142
- loading,
169143
- busy: sending || activeRun !== void 0,
169144
- draft,
169145
- error: error || historyError,
169146
- notice: notice || hint,
169147
- hasMore: typeof historyOffset === "number",
169148
- historyError: historyPageError,
169149
- ...inputDisabled && activeRun === void 0 ? { inputBlockedReason: hint || "当前输入暂不可用" } : {}
169150
- },
169151
- actions: {
169152
- submit: async (text) => {
169153
- if (sendDisabled || text.trim() === "" || text.length > 61440) return false;
169154
- setDraft(text);
169155
- return await send() === true;
169156
- },
169157
- selectModel,
169158
- cancel: cancelActiveRun,
169159
- loadEarlier
169160
- },
169161
- ui: {
169162
- clearDisabled: sendDisabled,
169163
- setDraft: (text) => setDraft(text),
169164
- clearContext: () => setClearConfirmOpen(true),
169165
- retry: async () => {
169166
- if (pendingTurn) await submitTurn(pendingTurn);
169167
- else await retryHistory();
169168
- },
169169
- ...pendingTurn ? { retryLabel: "重试确认" } : historyError ? { retryLabel: "重试加载记录" } : {},
169170
- feedback: setError
169171
- }
169172
- }), clearConfirmOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
169173
- role: "dialog",
169174
- "aria-label": "清除上下文",
169175
- style: {
169176
- position: "absolute",
169177
- inset: 0,
169178
- zIndex: 30,
169179
- display: "grid",
169180
- placeItems: "center",
169181
- background: "#0004"
169182
- },
169183
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
169184
- style: {
169185
- background: arkmeTheme.base,
169186
- padding: 24,
169187
- borderRadius: 12
169188
- },
169189
- children: [
169190
- "清除当前上下文?历史记录仍然保留。",
169191
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
169192
- onClick: () => setClearConfirmOpen(false),
169193
- children: "取消"
169194
- }),
169195
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
169196
- disabled: sendDisabled,
169197
- onClick: () => {
169198
- clearContext();
169199
- },
169200
- children: "确认清除"
169201
- })
169202
- ]
169203
- })
169204
- })] });
169205
- }
169206
168066
  //#endregion
169207
168067
  //#region src/client/ArkmePrivateCallMenu.tsx
169208
168068
  const maskStyle = {
@@ -201413,7 +200273,7 @@ registerProcessor('arkme-pcm-journal', ArkmePcmJournal);
201413
200273
  const c$3 = e$24;
201414
200274
  //#endregion
201415
200275
  //#region package.json
201416
- var version = "0.1.72";
200276
+ var version = "0.1.74";
201417
200277
  //#endregion
201418
200278
  //#region src/client/profile-change-store.ts
201419
200279
  let revision = 0;
@@ -222334,7 +221194,7 @@ registerProcessor('arkme-pcm-journal', ArkmePcmJournal);
222334
221194
  onPrivateChatOpened: activateSource
222335
221195
  }) : ui.mode === "voiceprint" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeVoiceprintSurface, { onBack: () => {
222336
221196
  arkmeUi.showRecordings();
222337
- } }) : ui.mode === "arko" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeArkoSurface, { native: true }, arkmeArkoSurfaceKey(auth)) : botConversationVisible && ui.selectedBot !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeBotConversationSurface, {
221197
+ } }) : ui.mode === "arko" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeArkoSurface, {}, arkmeArkoSurfaceKey(auth)) : botConversationVisible && ui.selectedBot !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeBotConversationSurface, {
222338
221198
  bot: ui.selectedBot,
222339
221199
  onConversationActivity: (bot) => {
222340
221200
  arkmeUi.openBotConversation(bot);
@@ -236865,29 +235725,6 @@ body[data-ds-dark-theme] .arkme-member-label[data-tier="2"] {
236865
235725
  };
236866
235726
  }
236867
235727
  //#endregion
236868
- //#region src/client/ArkmeArkoPanel.tsx
236869
- /** The workspace keys this single retained panel by authenticated account scope. */
236870
- function ArkmeArkoPanel({ visible }) {
236871
- const [visited, setVisited] = (0, react$1.useState)(false);
236872
- (0, react$1.useEffect)(() => {
236873
- if (visible) setVisited(true);
236874
- }, [visible]);
236875
- if (!visible && !visited) return null;
236876
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
236877
- "data-arkme-owned": "arko-panel",
236878
- "aria-hidden": !visible || void 0,
236879
- ...!visible ? { inert: "" } : {},
236880
- style: {
236881
- position: "absolute",
236882
- inset: 0,
236883
- visibility: visible ? "visible" : "hidden",
236884
- pointerEvents: visible ? "auto" : "none",
236885
- zIndex: visible ? 1 : 0
236886
- },
236887
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeArkoSurface, { native: true })
236888
- });
236889
- }
236890
- //#endregion
236891
235728
  //#region src/client/redesign/contacts/ContactDirectoryAddDialog.tsx
236892
235729
  const styles$4 = {
236893
235730
  backdrop: {
@@ -240893,8 +239730,7 @@ body[data-ds-dark-theme] .arkme-member-label[data-tier="2"] {
240893
239730
  const contactsMode = ui.mode === "source" && ui.productMode === "contacts";
240894
239731
  const webLockedHarness = !startupAuthGateEnabled() && authState.auth?.status !== "authenticated";
240895
239732
  const harnessVisible = ui.mode === "harness" || webLockedHarness;
240896
- const arkoVisible = ui.mode === "arko" && !webLockedHarness;
240897
- const conversationHidden = harnessVisible || contactsMode || arkoVisible;
239733
+ const conversationHidden = harnessVisible || contactsMode;
240898
239734
  const conversationActive = !conversationHidden && ui.calendarOpen !== true;
240899
239735
  const contactsContextRef = (0, react$1.useRef)({
240900
239736
  accountKey: contactsAccountKey,
@@ -240927,7 +239763,6 @@ body[data-ds-dark-theme] .arkme-member-label[data-tier="2"] {
240927
239763
  accountScope: contactsAccountKey,
240928
239764
  followSession: ui.mode === "harness"
240929
239765
  }, contactsAccountKey ?? "guest"),
240930
- contactsAccountKey !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeArkoPanel, { visible: arkoVisible }, contactsAccountKey),
240931
239766
  !webLockedHarness && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
240932
239767
  "data-arkme-owned": "arkme-conversation-layer",
240933
239768
  style: {