dsh-monkeyai-sessions 0.1.7 → 0.1.9

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.
@@ -538,12 +538,19 @@ var t = {
538
538
  radiusLg: 10
539
539
  };
540
540
  var style = {
541
- page: { display: "grid", gap: 12, maxWidth: 1080, margin: "0 auto", padding: "16px 18px 24px", color: t.text, height: "100%", minHeight: 0, overflowY: "auto", boxSizing: "border-box", alignContent: "start", fontSize: 13, lineHeight: 1.5 },
541
+ page: { display: "grid", gap: 14, maxWidth: 1320, margin: "0 auto", padding: "18px 20px 24px", color: t.text, background: "#f7f8fb", height: "100%", minHeight: 0, overflowY: "auto", boxSizing: "border-box", alignContent: "start", fontSize: 13, lineHeight: 1.5 },
542
542
  header: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, paddingBottom: 2 },
543
- title: { margin: 0, fontSize: 15, fontWeight: 650, letterSpacing: "-0.01em", color: t.text },
543
+ title: { margin: 0, fontSize: 18, fontWeight: 700, letterSpacing: "-0.025em", color: "#182230" },
544
+ eyebrow: { margin: 0, fontSize: 10.5, fontWeight: 700, letterSpacing: "0.1em", color: t.text3, textTransform: "uppercase" },
544
545
  subtitle: { margin: 0, fontSize: 12, color: t.text3 },
545
546
  sectionTitle: { margin: 0, fontSize: 12, fontWeight: 650, color: t.text2, textTransform: "uppercase", letterSpacing: "0.04em" },
546
- card: { display: "grid", gap: 10, padding: 14, border: `1px solid ${t.border}`, borderRadius: t.radiusLg, background: t.surface },
547
+ card: { display: "grid", gap: 10, padding: 14, border: `1px solid ${t.border}`, borderRadius: 12, background: t.surface, boxShadow: "0 2px 8px rgba(16,24,40,0.035)" },
548
+ toolbarCard: { display: "grid", gap: 10, padding: "13px 15px", border: `1px solid ${t.border}`, borderRadius: 12, background: t.surface, boxShadow: "0 2px 8px rgba(16,24,40,0.035)" },
549
+ workspace: { display: "grid", gridTemplateColumns: "minmax(248px, 292px) minmax(0, 1fr)", gap: 12, minWidth: 0, alignItems: "stretch" },
550
+ sessionSidebar: { display: "grid", gridTemplateRows: "auto minmax(0, 1fr) auto", gap: 10, minWidth: 0, padding: 12, border: `1px solid ${t.border}`, borderRadius: 12, background: t.surface, boxShadow: "0 2px 8px rgba(16,24,40,0.035)" },
551
+ sessionList: { display: "grid", gridTemplateColumns: "1fr", alignContent: "start", gap: 6, minHeight: 0, overflowY: "auto", padding: "1px 3px 2px 1px" },
552
+ thread: { display: "grid", gridTemplateRows: "auto minmax(0, 1fr) auto", gap: 10, minWidth: 0, height: "min(70vh, 720px)", minHeight: 440, overflow: "hidden", padding: 12 },
553
+ emptyThread: { display: "grid", placeContent: "center", justifyItems: "center", gap: 9, minWidth: 0, minHeight: "min(70vh, 720px)", padding: 28, border: `1px dashed ${t.border2}`, borderRadius: 12, background: "linear-gradient(145deg, #ffffff 0%, #f8faff 100%)", textAlign: "center" },
547
554
  row: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" },
548
555
  label: { display: "grid", gap: 4, flex: 1, minWidth: 180, fontSize: 12, fontWeight: 600, color: t.text2 },
549
556
  input: { width: "100%", boxSizing: "border-box", padding: "7px 10px", borderRadius: t.radius, border: `1px solid ${t.border2}`, background: t.surface, color: t.text, outline: "none", fontSize: 13, transition: "border-color .15s, box-shadow .15s" },
@@ -562,13 +569,13 @@ var style = {
562
569
  sessionTitle: { fontSize: 13, fontWeight: 650, lineHeight: 1.35, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
563
570
  sessionMeta: { fontSize: 11.5, color: t.text3, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
564
571
  chip: { display: "inline-flex", alignItems: "center", gap: 5, padding: "2px 8px", borderRadius: 999, border: `1px solid ${t.border}`, background: t.surfaceAlt, fontSize: 11.5 },
565
- events: { display: "flex", flexDirection: "column", gap: 8, height: "56vh", minHeight: 200, overflowY: "auto", padding: "4px 6px 4px 2px" },
572
+ events: { display: "flex", flexDirection: "column", gap: 10, height: "auto", minHeight: 0, overflowY: "auto", padding: "6px 8px 8px 4px", overscrollBehavior: "contain" },
566
573
  event: { padding: "8px 11px", borderRadius: t.radius, background: t.surfaceAlt, border: `1px solid ${t.border}`, overflowWrap: "anywhere", color: t.text },
567
574
  bubbleWrap: { display: "flex", gap: 8, alignItems: "flex-start", width: "100%" },
568
575
  bubbleWrapUser: { display: "flex", gap: 8, alignItems: "flex-start", justifyContent: "flex-end", width: "100%" },
569
576
  avatar: { width: 24, height: 24, borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 10, fontWeight: 700, color: "#ffffff", flexShrink: 0, marginTop: 2 },
570
- bubble: { maxWidth: "88%", minWidth: 0, flex: "0 1 auto", width: "fit-content", borderRadius: t.radiusLg, padding: "8px 13px", background: t.surfaceAlt, border: `1px solid ${t.border}`, lineHeight: 1.55, fontSize: 13 },
571
- bubbleUser: { maxWidth: "88%", minWidth: 0, flex: "0 1 auto", width: "fit-content", borderRadius: t.radiusLg, padding: "8px 13px", background: t.accent, color: "#ffffff", lineHeight: 1.55, fontSize: 13 },
577
+ bubble: { maxWidth: "88%", minWidth: 0, flex: "0 1 auto", width: "fit-content", borderRadius: 12, padding: "9px 13px", background: t.surface, border: `1px solid ${t.border}`, boxShadow: "0 1px 2px rgba(16,24,40,0.04)", lineHeight: 1.6, fontSize: 13 },
578
+ bubbleUser: { maxWidth: "88%", minWidth: 0, flex: "0 1 auto", width: "fit-content", borderRadius: 12, padding: "9px 13px", background: "#315fe8", color: "#ffffff", boxShadow: "0 2px 5px rgba(49,95,232,0.18)", lineHeight: 1.6, fontSize: 13 },
572
579
  thought: { maxWidth: "88%", borderRadius: t.radiusLg, padding: "7px 11px", background: "#faf7fd", border: `1px solid #e8ddf5`, lineHeight: 1.5, fontSize: 12.5, color: "#5b4a72" },
573
580
  thoughtSummary: { cursor: "pointer", color: "#6b4fa0", fontWeight: 650, fontSize: 10.5, letterSpacing: "0.05em", textTransform: "uppercase", userSelect: "none" },
574
581
  approval: { padding: "11px 13px", borderRadius: t.radiusLg, border: `1px solid #f0d98c`, background: "#fffbeb", display: "grid", gap: 8 },
@@ -591,6 +598,36 @@ var PANEL_CSS = `
591
598
  .mai-panel [data-events]::-webkit-scrollbar-thumb:hover { background: #b8c2cf; }
592
599
  .mai-panel .mai-card-hover { transition: border-color .15s, background .15s; }
593
600
  .mai-panel .mai-card-hover:not(:disabled):hover { border-color: #b9cdf3 !important; background: #f7faff !important; }
601
+ .mai-panel .mai-toolbar { position: relative; overflow: hidden; }
602
+ .mai-panel .mai-toolbar::before { content: ''; position: absolute; inset: 0 0 auto; height: 3px; background: linear-gradient(90deg, #3568e8 0%, #8baaf4 46%, #e7edfc 100%); }
603
+ .mai-panel .mai-workspace { display: grid; grid-template-columns: minmax(248px, 292px) minmax(0, 1fr); gap: 12px; min-width: 0; align-items: stretch; }
604
+ .mai-panel .mai-session-sidebar { min-height: min(70vh, 720px); }
605
+ .mai-panel .mai-session-list { scrollbar-gutter: stable; }
606
+ .mai-panel .mai-session-card { position: relative; }
607
+ .mai-panel .mai-session-card[aria-current='true']::before { content: ''; position: absolute; left: 0; top: 9px; bottom: 9px; width: 3px; border-radius: 0 3px 3px 0; background: #3568e8; }
608
+ .mai-panel .mai-thread { height: min(70vh, 720px); min-height: 440px; }
609
+ .mai-panel .mai-thread-events { height: auto !important; min-height: 0 !important; }
610
+ .mai-panel .mai-empty-thread { min-height: min(70vh, 720px); }
611
+ .mai-panel .mai-empty-mark { display: grid; place-items: center; width: 54px; height: 54px; border-radius: 17px; background: #edf2ff; color: #315fe8; box-shadow: inset 0 0 0 1px #dce6ff; }
612
+ .mai-panel .mai-session-meta { overflow-wrap: anywhere; }
613
+ @media (max-width: 900px) {
614
+ .mai-panel .mai-workspace { grid-template-columns: minmax(0, 1fr); }
615
+ .mai-panel .mai-session-sidebar { min-height: 0; grid-template-rows: auto minmax(0, auto) auto; }
616
+ .mai-panel .mai-session-list { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); max-height: 235px; }
617
+ .mai-panel .mai-thread { height: 68vh; min-height: 390px; }
618
+ .mai-panel .mai-empty-thread { min-height: 330px; }
619
+ }
620
+ @media (max-width: 560px) {
621
+ .mai-panel .mai-page-title { font-size: 16px !important; }
622
+ .mai-panel .mai-toolbar-controls { align-items: stretch; }
623
+ .mai-panel .mai-toolbar-controls select { flex-basis: 100% !important; }
624
+ .mai-panel .mai-session-list { grid-template-columns: 1fr !important; max-height: 210px; }
625
+ .mai-panel .mai-thread { height: 68vh; min-height: 360px; padding: 10px; }
626
+ .mai-panel .mai-thread-header { gap: 6px !important; }
627
+ .mai-panel .mai-thread-header strong { min-width: 100px; }
628
+ .mai-panel .mai-thread-header select { flex-basis: 100% !important; }
629
+ .mai-panel .mai-events { gap: 7px; }
630
+ }
594
631
  @keyframes mai-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
595
632
  .mai-panel .mai-pulse { animation: mai-pulse 1.2s ease-in-out infinite; }
596
633
  .mai-panel .mai-md > div:first-child > :first-child { margin-top: 0; }
@@ -601,6 +638,24 @@ var PANEL_CSS = `
601
638
  function errorText(error) {
602
639
  return typeof error?.message === "string" ? error.message : typeof error === "string" ? error : "\u8FDC\u7A0B\u64CD\u4F5C\u5931\u8D25";
603
640
  }
641
+ function withTimeout(promise, timeoutMs, label) {
642
+ return new Promise((resolve, reject) => {
643
+ const timer = setTimeout(() => reject(new Error(`${label}\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5`)), timeoutMs);
644
+ Promise.resolve(promise).then(
645
+ (value) => {
646
+ clearTimeout(timer);
647
+ resolve(value);
648
+ },
649
+ (error) => {
650
+ clearTimeout(timer);
651
+ reject(error);
652
+ }
653
+ );
654
+ });
655
+ }
656
+ function isRemoteStreamCarrierError(error) {
657
+ return /api gateway: remote stream websocket (?:failed|closed)/i.test(errorText(error));
658
+ }
604
659
  function renderMarkdown(text) {
605
660
  if (typeof text !== "string" || text === "") return null;
606
661
  const blocks = [];
@@ -824,6 +879,7 @@ function RemoteSessionPage({ api }) {
824
879
  const authWindowRef = useRef(null);
825
880
  const eventsRef = useRef(null);
826
881
  const prevScrollHeightRef = useRef(null);
882
+ const streamGenerationRef = useRef(0);
827
883
  const title = useMemo(() => {
828
884
  const runtime = runtimeSessions.find((item) => item.id === sessionId);
829
885
  const summary = summarySessions.find((item) => item.id === sessionId);
@@ -844,6 +900,7 @@ function RemoteSessionPage({ api }) {
844
900
  });
845
901
  return () => {
846
902
  active = false;
903
+ streamGenerationRef.current += 1;
847
904
  streamRef.current?.dispose();
848
905
  streamRef.current = null;
849
906
  };
@@ -1006,6 +1063,7 @@ function RemoteSessionPage({ api }) {
1006
1063
  setError("");
1007
1064
  setLoading(true);
1008
1065
  try {
1066
+ streamGenerationRef.current += 1;
1009
1067
  const result = await unwrap(api.listDevices({ accountId, page: 1 }));
1010
1068
  setDevices(result.items);
1011
1069
  const first = result.items.length > 0 ? result.items[0].machineId : "";
@@ -1029,24 +1087,32 @@ function RemoteSessionPage({ api }) {
1029
1087
  setError("");
1030
1088
  setLoading(true);
1031
1089
  setSessionId("");
1090
+ streamGenerationRef.current += 1;
1032
1091
  streamRef.current?.dispose();
1033
1092
  streamRef.current = null;
1034
1093
  setFrames([]);
1035
1094
  setPendingRequests([]);
1036
- const results = await Promise.allSettled([
1037
- unwrap(api.listSessions({ accountId, machineId: mid, page: 1, pageSize: 50 })),
1038
- unwrap(api.listRuntimeSessions({ accountId, machineId: mid }))
1039
- ]);
1040
- const errors = [];
1041
- if (results[0].status === "fulfilled") setSummarySessions(results[0].value.items);
1042
- else errors.push(`\u4F1A\u8BDD\u6458\u8981\u7D22\u5F15\uFF1A${errorText(results[0].reason)}`);
1043
- if (results[1].status === "fulfilled") setRuntimeSessions(results[1].value.sessions);
1044
- else errors.push(`ACP \u4F1A\u8BDD\u5217\u8868\uFF1A${errorText(results[1].reason)}`);
1045
- setStatus(errors.length === 0 ? "\u4F1A\u8BDD\u5DF2\u5237\u65B0" : "\u90E8\u5206\u4F1A\u8BDD\u6765\u6E90\u4E0D\u53EF\u7528");
1046
- setError(errors.join("\n"));
1047
- setLoading(false);
1095
+ try {
1096
+ const results = await Promise.allSettled([
1097
+ withTimeout(Promise.resolve().then(() => unwrap(api.listSessions({ accountId, machineId: mid, page: 1, pageSize: 50 }))), 5e4, "\u4F1A\u8BDD\u6458\u8981\u8BFB\u53D6"),
1098
+ withTimeout(Promise.resolve().then(() => unwrap(api.listRuntimeSessions({ accountId, machineId: mid }))), 5e4, "ACP \u4F1A\u8BDD\u5217\u8868\u8BFB\u53D6")
1099
+ ]);
1100
+ const errors = [];
1101
+ if (results[0].status === "fulfilled") setSummarySessions(results[0].value.items);
1102
+ else errors.push(`\u4F1A\u8BDD\u6458\u8981\u7D22\u5F15\uFF1A${errorText(results[0].reason)}`);
1103
+ if (results[1].status === "fulfilled") setRuntimeSessions(results[1].value.sessions);
1104
+ else errors.push(`ACP \u4F1A\u8BDD\u5217\u8868\uFF1A${errorText(results[1].reason)}`);
1105
+ setStatus(errors.length === 0 ? "\u4F1A\u8BDD\u5DF2\u5237\u65B0" : "\u90E8\u5206\u4F1A\u8BDD\u6765\u6E90\u4E0D\u53EF\u7528");
1106
+ setError(errors.join("\n"));
1107
+ } catch (reason) {
1108
+ setStatus("\u4F1A\u8BDD\u5237\u65B0\u5931\u8D25");
1109
+ setError(errorText(reason));
1110
+ } finally {
1111
+ setLoading(false);
1112
+ }
1048
1113
  }
1049
1114
  async function openSession(session) {
1115
+ const generation = ++streamGenerationRef.current;
1050
1116
  streamRef.current?.dispose();
1051
1117
  streamRef.current = null;
1052
1118
  historyLoadedRef.current = false;
@@ -1063,90 +1129,116 @@ function RemoteSessionPage({ api }) {
1063
1129
  setVisibleCount(RECENT_LIMIT);
1064
1130
  setHistoryLoading(true);
1065
1131
  setStatus("\u6B63\u5728\u8FDE\u63A5\u8FDC\u7A0B\u4F1A\u8BDD");
1066
- let stream;
1067
- try {
1068
- stream = api.followSession({ accountId, machineId, sessionId: session.id, cwd: session.cwd });
1069
- streamRef.current = stream;
1070
- setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u8FDE\u63A5");
1071
- for await (const frame of stream) {
1072
- setFrames((current) => [...current, frame]);
1073
- if (frame.type === "ready" && !historyLoadedRef.current) setStatus("\u6B63\u5728\u56DE\u653E\u4F1A\u8BDD\u5386\u53F2");
1074
- if (frame.type === "reconnecting") setStatus(frame.message || "\u6B63\u5728\u91CD\u8FDE\u8FDC\u7A0B runtime");
1075
- if (frame.type === "history_loaded") {
1076
- historyLoadedRef.current = true;
1077
- setStatus("\u5386\u53F2\u5DF2\u52A0\u8F7D");
1078
- setHistoryLoading(false);
1079
- setSessionStatus((current) => current || "idle");
1080
- if (frame.model) setCurrentModel(frame.model);
1081
- }
1082
- if (frame.type === "history_incomplete") {
1083
- setStatus("\u5386\u53F2\u4E0D\u5B8C\u6574\uFF1A" + (frame.message || "\u56DE\u653E\u88AB\u4E2D\u65AD"));
1084
- setHistoryLoading(false);
1085
- }
1086
- if (frame.type === "model_switched") {
1087
- if (frame.data?.model) setCurrentModel(frame.data.model);
1088
- setSwitchingModel(false);
1089
- setStatus("\u6A21\u578B\u5DF2\u5207\u6362");
1090
- }
1091
- if (frame.type === "model_switch_failed") {
1092
- setSwitchingModel(false);
1093
- setError(frame.message || "\u6A21\u578B\u5207\u6362\u88AB\u62D2\u7EDD");
1094
- setStatus("\u6A21\u578B\u5207\u6362\u5931\u8D25");
1095
- }
1096
- if (frame.type === "session_missing") {
1097
- setError(frame.message || "\u8BE5\u4F1A\u8BDD\u5728\u6240\u9009\u8BBE\u5907\u4E0A\u5DF2\u4E0D\u5B58\u5728\u3002");
1098
- setStatus("\u8BBE\u5907\u4E0A\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD");
1099
- setHistoryLoading(false);
1100
- }
1101
- if (frame.type === "request") {
1102
- setPendingRequests((current) => [...current.filter((item) => item.requestId !== frame.requestId), frame]);
1132
+ let retries = 0;
1133
+ while (streamGenerationRef.current === generation) {
1134
+ let stream;
1135
+ try {
1136
+ stream = api.followSession({ accountId, machineId, sessionId: session.id, cwd: session.cwd });
1137
+ if (streamGenerationRef.current !== generation) {
1138
+ stream.dispose();
1139
+ return;
1103
1140
  }
1104
- if (frame.type === "notification" && frame.method === "_monkeyai/session/state") {
1105
- const status2 = frame.data?.state?.status;
1106
- if (typeof status2 === "string" && SESSION_STATUS_LABEL[status2]) {
1107
- setSessionStatus(status2);
1108
- setTurnActive(status2 === "running");
1109
- if (status2 !== "running") setStopping(false);
1141
+ streamRef.current = stream;
1142
+ setStatus(retries > 0 ? "\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u91CD\u65B0\u8FDE\u63A5" : "\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u8FDE\u63A5");
1143
+ for await (const frame of stream) {
1144
+ setFrames((current) => [...current, frame]);
1145
+ if (frame.type === "ready" && !historyLoadedRef.current) setStatus("\u6B63\u5728\u56DE\u653E\u4F1A\u8BDD\u5386\u53F2");
1146
+ if (frame.type === "reconnecting") setStatus(frame.message || "\u6B63\u5728\u91CD\u8FDE\u8FDC\u7A0B runtime");
1147
+ if (frame.type === "history_loaded") {
1148
+ historyLoadedRef.current = true;
1149
+ setStatus("\u5386\u53F2\u5DF2\u52A0\u8F7D");
1150
+ setHistoryLoading(false);
1151
+ setSessionStatus((current) => current || "idle");
1152
+ if (frame.model) setCurrentModel(frame.model);
1110
1153
  }
1111
- }
1112
- if (frame.type === "delivery") {
1113
- const accepted = Boolean(frame.data?.accepted);
1114
- setLastDelivery({ accepted, code: frame.data?.code, frame: frame.type });
1115
- setTurnActive(false);
1116
- setStopping(false);
1117
- if (accepted) {
1118
- const cancelled = frame.data?.result?.stopReason === "cancelled";
1119
- setStatus(cancelled ? "\u56DE\u5408\u5DF2\u505C\u6B62" : "\u56DE\u5408\u5DF2\u5B8C\u6210");
1120
- } else {
1121
- setPrompt((current) => current === "" ? lastDraftRef.current || "" : current);
1122
- setStatus("MonkeyAI \u672A\u63A5\u53D7\u8BE5\u6307\u4EE4\uFF0C\u8349\u7A3F\u5DF2\u4FDD\u7559");
1123
- if (frame.message) setError(frame.message);
1154
+ if (frame.type === "history_incomplete") {
1155
+ setStatus("\u5386\u53F2\u4E0D\u5B8C\u6574\uFF1A" + (frame.message || "\u56DE\u653E\u88AB\u4E2D\u65AD"));
1156
+ setHistoryLoading(false);
1157
+ }
1158
+ if (frame.type === "model_switched") {
1159
+ if (frame.data?.model) setCurrentModel(frame.data.model);
1160
+ setSwitchingModel(false);
1161
+ setStatus("\u6A21\u578B\u5DF2\u5207\u6362");
1162
+ }
1163
+ if (frame.type === "model_switch_failed") {
1164
+ setSwitchingModel(false);
1165
+ setError(frame.message || "\u6A21\u578B\u5207\u6362\u5931\u8D25");
1166
+ setStatus("\u6A21\u578B\u5207\u6362\u5931\u8D25");
1167
+ }
1168
+ if (frame.type === "session_missing") {
1169
+ setError(frame.message || "\u8BE5\u4F1A\u8BDD\u5728\u6240\u9009\u8BBE\u5907\u4E0A\u5DF2\u4E0D\u5B58\u5728\u3002");
1170
+ setStatus("\u8BBE\u5907\u4E0A\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD");
1171
+ setHistoryLoading(false);
1172
+ }
1173
+ if (frame.type === "request") {
1174
+ setPendingRequests((current) => [...current.filter((item) => item.requestId !== frame.requestId), frame]);
1175
+ }
1176
+ if (frame.type === "notification" && frame.method === "_monkeyai/session/state") {
1177
+ const status2 = frame.data?.state?.status;
1178
+ if (typeof status2 === "string" && SESSION_STATUS_LABEL[status2]) {
1179
+ setSessionStatus(status2);
1180
+ setTurnActive(status2 === "running");
1181
+ if (status2 !== "running") setStopping(false);
1182
+ }
1183
+ }
1184
+ if (frame.type === "delivery") {
1185
+ const accepted = Boolean(frame.data?.accepted);
1186
+ setLastDelivery({ accepted, code: frame.data?.code, frame: frame.type });
1187
+ setTurnActive(false);
1188
+ setStopping(false);
1189
+ if (accepted) {
1190
+ const cancelled = frame.data?.result?.stopReason === "cancelled";
1191
+ setStatus(cancelled ? "\u56DE\u5408\u5DF2\u505C\u6B62" : "\u56DE\u5408\u5DF2\u5B8C\u6210");
1192
+ } else {
1193
+ setPrompt((current) => current === "" ? lastDraftRef.current || "" : current);
1194
+ setStatus("MonkeyAI \u672A\u63A5\u53D7\u8BE5\u6307\u4EE4\uFF0C\u8349\u7A3F\u5DF2\u4FDD\u7559");
1195
+ if (frame.message) setError(frame.message);
1196
+ }
1197
+ }
1198
+ if (frame.type === "cancel_sent") {
1199
+ setStopping(false);
1200
+ setStatus("\u5DF2\u8BF7\u6C42\u505C\u6B62\uFF0C\u7B49\u5F85 runtime \u53D6\u6D88");
1124
1201
  }
1202
+ if (frame.type === "response_sent") {
1203
+ setPendingRequests((current) => current.filter((item) => item.requestId !== frame.requestId));
1204
+ }
1205
+ if (frame.type === "response_error") {
1206
+ setError(`\u8FDC\u7A0B\u56DE\u590D\u88AB\u62D2\u7EDD\uFF1A${frame.code || "\u672A\u77E5"}`);
1207
+ }
1208
+ if (frame.type === "error") setError(frame.message || frame.code || "\u8FDC\u7A0B\u4F1A\u8BDD\u9519\u8BEF");
1125
1209
  }
1126
- if (frame.type === "cancel_sent") {
1210
+ if (streamGenerationRef.current !== generation || streamRef.current !== stream) return;
1211
+ streamRef.current = null;
1212
+ setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u65AD\u5F00");
1213
+ setHistoryLoading(false);
1214
+ setTurnActive(false);
1215
+ setStopping(false);
1216
+ return;
1217
+ } catch (reason) {
1218
+ if (streamGenerationRef.current !== generation || stream && streamRef.current !== stream) return;
1219
+ if (isRemoteStreamCarrierError(reason)) {
1220
+ retries += 1;
1221
+ try {
1222
+ stream?.dispose();
1223
+ } catch {
1224
+ }
1225
+ streamRef.current = null;
1226
+ setError("");
1227
+ setTurnActive(false);
1127
1228
  setStopping(false);
1128
- setStatus("\u5DF2\u8BF7\u6C42\u505C\u6B62\uFF0C\u7B49\u5F85 runtime \u53D6\u6D88");
1229
+ setStatus("\u8FDC\u7A0B\u8FDE\u63A5\u4E2D\u65AD\uFF0C\u6B63\u5728\u91CD\u8FDE");
1230
+ const delay = Math.min(1e3 * 2 ** Math.min(retries - 1, 5), 3e4);
1231
+ await new Promise((resolve) => setTimeout(resolve, delay));
1232
+ continue;
1129
1233
  }
1130
- if (frame.type === "response_sent") {
1131
- setPendingRequests((current) => current.filter((item) => item.requestId !== frame.requestId));
1132
- }
1133
- if (frame.type === "response_error") {
1134
- setError(`\u8FDC\u7A0B\u56DE\u590D\u88AB\u62D2\u7EDD\uFF1A${frame.code || "\u672A\u77E5"}`);
1135
- }
1136
- if (frame.type === "error") setError(frame.message || frame.code || "\u8FDC\u7A0B\u4F1A\u8BDD\u9519\u8BEF");
1234
+ streamRef.current = null;
1235
+ setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u4E0D\u53EF\u7528");
1236
+ setError(errorText(reason));
1237
+ setHistoryLoading(false);
1238
+ setTurnActive(false);
1239
+ setStopping(false);
1240
+ return;
1137
1241
  }
1138
- if (streamRef.current !== stream) return;
1139
- setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u65AD\u5F00");
1140
- setHistoryLoading(false);
1141
- setTurnActive(false);
1142
- setStopping(false);
1143
- } catch (reason) {
1144
- if (stream && streamRef.current !== stream) return;
1145
- setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u4E0D\u53EF\u7528");
1146
- setError(errorText(reason));
1147
- setHistoryLoading(false);
1148
- setTurnActive(false);
1149
- setStopping(false);
1150
1242
  }
1151
1243
  }
1152
1244
  function sendPrompt() {
@@ -1354,9 +1446,14 @@ function RemoteSessionPage({ api }) {
1354
1446
  { style: style.header },
1355
1447
  h(
1356
1448
  "div",
1357
- { style: { display: "flex", alignItems: "center", gap: 8 } },
1358
- h(MonkeyIcon, { size: 22 }),
1359
- h("h1", { style: style.title }, "MonkeyAI \u4F1A\u8BDD")
1449
+ { style: { display: "grid", gap: 1 } },
1450
+ h("p", { style: style.eyebrow }, "\u8FDC\u7A0B\u5DE5\u4F5C\u533A"),
1451
+ h(
1452
+ "div",
1453
+ { style: { display: "flex", alignItems: "center", gap: 8 } },
1454
+ h(MonkeyIcon, { size: 22 }),
1455
+ h("h1", { style: style.title, className: "mai-page-title" }, "MonkeyAI \u4F1A\u8BDD")
1456
+ )
1360
1457
  ),
1361
1458
  h(StatusBadge, { status, live: isLive })
1362
1459
  ),
@@ -1510,16 +1607,21 @@ function RemoteSessionPage({ api }) {
1510
1607
  ) : null,
1511
1608
  currentAccount ? h(
1512
1609
  "section",
1513
- { style: style.card },
1610
+ { style: style.toolbarCard, className: "mai-toolbar" },
1514
1611
  h(
1515
1612
  "div",
1516
- { style: style.row },
1517
- h("h2", { style: { ...style.sectionTitle, flex: 1 } }, "\u8FDC\u7A0B\u4F1A\u8BDD"),
1518
- h("span", { style: { fontSize: 11, color: t.text3 } }, currentAccount.scopes.filter((s) => s !== "device:connect").join(" \xB7 "))
1613
+ { style: style.row, className: "mai-toolbar-heading" },
1614
+ h(
1615
+ "div",
1616
+ { style: { display: "grid", gap: 1, flex: 1, minWidth: 0 } },
1617
+ h("h2", { style: style.sectionTitle }, "\u8FDC\u7A0B\u4F1A\u8BDD"),
1618
+ h("p", { style: style.subtitle }, "\u9009\u62E9\u8BBE\u5907\uFF0C\u7EE7\u7EED\u5904\u7406 MonkeyAI \u4F1A\u8BDD")
1619
+ ),
1620
+ h("span", { style: style.chip }, currentAccount.scopes.filter((s) => s !== "device:connect").join(" \xB7 "))
1519
1621
  ),
1520
1622
  h(
1521
1623
  "div",
1522
- { style: style.row },
1624
+ { style: style.row, className: "mai-toolbar-controls" },
1523
1625
  h(
1524
1626
  "select",
1525
1627
  { style: { ...style.input, flex: "1 1 200px" }, value: accountId, onChange: (event) => setAccountId(event.target.value), "aria-label": "\u5DF2\u6388\u6743\u8D26\u53F7" },
@@ -1621,247 +1723,282 @@ function RemoteSessionPage({ api }) {
1621
1723
  }, newSessionBusy ? "\u521B\u5EFA\u4E2D\u2026" : "\u521B\u5EFA")
1622
1724
  )
1623
1725
  )
1624
- ),
1625
- sessionOptions.length > 0 ? h("div", { style: style.sessions }, sessionOptions.map((session) => {
1626
- const flag = session.stale ? "\u5DF2\u8FC7\u671F" : session.deviceStatus && session.deviceStatus !== "online" ? session.deviceStatus === "offline" ? "\u79BB\u7EBF" : session.deviceStatus : "";
1627
- return h(
1628
- "button",
1629
- {
1630
- style: { ...style.session, ...session.id === sessionId ? style.selected : {} },
1631
- className: "mai-card-hover",
1632
- key: session.id,
1633
- type: "button",
1634
- disabled: !session.cwd,
1635
- onClick: () => openSession(session),
1636
- title: session.cwd ? `\u6253\u5F00 ${session.cwd}` : "ACP \u4F1A\u8BDD\u5217\u8868\u672A\u63D0\u4F9B\u5DE5\u4F5C\u76EE\u5F55"
1637
- },
1638
- h("span", { style: style.sessionTitle }, session.title || session.id),
1639
- h("span", { style: style.sessionMeta }, session.cwd || "\u52A0\u8F7D\u5386\u53F2\u9700\u8981 runtime \u63D0\u4F9B\u5DE5\u4F5C\u76EE\u5F55"),
1640
- flag !== "" ? h("span", { style: { fontSize: 10.5, fontWeight: 650, color: session.stale ? t.text3 : "#8a6d1d", textTransform: "uppercase", letterSpacing: "0.04em" } }, flag) : null
1641
- );
1642
- })) : h("p", { style: style.subtitle }, machineId ? "\u8BE5\u8BBE\u5907\u6682\u65E0\u4F1A\u8BDD\u3002\u53EF\u5728\u4E0A\u65B9\u65B0\u5EFA\uFF0C\u6216\u70B9\u51FB\u5237\u65B0\u3002" : "\u8BF7\u9009\u62E9\u8BBE\u5907\u4EE5\u5217\u51FA\u5176\u4F1A\u8BDD\u3002")
1726
+ )
1643
1727
  ) : null,
1644
- sessionId ? h(
1645
- "section",
1646
- { style: style.card },
1728
+ currentAccount ? h(
1729
+ "div",
1730
+ { className: "mai-workspace" },
1647
1731
  h(
1648
- "div",
1649
- { style: style.row },
1650
- h("button", {
1651
- type: "button",
1652
- style: { ...style.button, flexShrink: 0 },
1653
- title: "\u5173\u95ED\u4F1A\u8BDD\u5E76\u8FD4\u56DE\u5217\u8868",
1654
- onClick: () => {
1655
- streamRef.current?.dispose();
1656
- streamRef.current = null;
1657
- setSessionId("");
1658
- setTurnActive(false);
1659
- setStopping(false);
1660
- setSessionStatus("");
1661
- setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u5173\u95ED");
1662
- }
1663
- }, "\u2190"),
1664
- h("strong", { style: { flex: 1, fontSize: 13.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title }, title),
1665
- sessionStatus ? h(
1666
- "span",
1667
- {
1668
- style: {
1669
- ...style.statusBadge,
1670
- background: sessionStatus === "running" ? t.accentSoft : sessionStatus === "error" ? t.dangerBg : t.surfaceAlt,
1671
- borderColor: sessionStatus === "running" ? t.accent : sessionStatus === "error" ? t.dangerBorder : t.border,
1672
- color: sessionStatus === "running" ? t.accent : sessionStatus === "error" ? t.danger : t.text2
1732
+ "aside",
1733
+ { style: style.sessionSidebar, className: "mai-session-sidebar" },
1734
+ h(
1735
+ "div",
1736
+ { style: { ...style.row, justifyContent: "space-between" } },
1737
+ h("h2", { style: style.sectionTitle }, "\u4F1A\u8BDD\u5217\u8868"),
1738
+ h("span", { style: style.chip }, String(sessionOptions.length))
1739
+ ),
1740
+ sessionOptions.length > 0 ? h("div", { style: style.sessions, className: "mai-session-list" }, sessionOptions.map((session) => {
1741
+ const flag = session.stale ? "\u5DF2\u8FC7\u671F" : session.deviceStatus && session.deviceStatus !== "online" ? session.deviceStatus === "offline" ? "\u79BB\u7EBF" : session.deviceStatus : "";
1742
+ return h(
1743
+ "button",
1744
+ {
1745
+ style: { ...style.session, ...session.id === sessionId ? style.selected : {} },
1746
+ className: "mai-card-hover mai-session-card",
1747
+ "aria-current": session.id === sessionId ? "true" : void 0,
1748
+ key: session.id,
1749
+ type: "button",
1750
+ disabled: !session.cwd,
1751
+ onClick: () => openSession(session),
1752
+ title: session.cwd ? `\u6253\u5F00 ${session.cwd}` : "ACP \u4F1A\u8BDD\u5217\u8868\u672A\u63D0\u4F9B\u5DE5\u4F5C\u76EE\u5F55"
1673
1753
  },
1674
- title: "\u4F1A\u8BDD\u72B6\u6001\uFF08\u542B\u8FDC\u7AEF\u5BA2\u6237\u7AEF\u53D1\u8D77\u7684\u56DE\u5408\uFF09",
1675
- "aria-label": `\u4F1A\u8BDD\u72B6\u6001\uFF1A${SESSION_STATUS_LABEL[sessionStatus]}`
1676
- },
1677
- sessionStatus === "running" ? h("span", { className: "mai-pulse", style: { ...style.dot, background: t.accent } }) : null,
1678
- SESSION_STATUS_LABEL[sessionStatus]
1679
- ) : null,
1754
+ h("span", { style: style.sessionTitle }, session.title || session.id),
1755
+ h("span", { style: style.sessionMeta, className: "mai-session-meta" }, session.cwd || "\u52A0\u8F7D\u5386\u53F2\u9700\u8981 runtime \u63D0\u4F9B\u5DE5\u4F5C\u76EE\u5F55"),
1756
+ flag !== "" ? h("span", { style: { fontSize: 10.5, fontWeight: 650, color: session.stale ? t.text3 : "#8a6d1d", textTransform: "uppercase", letterSpacing: "0.04em" } }, flag) : null
1757
+ );
1758
+ })) : h(
1759
+ "div",
1760
+ { style: { display: "grid", justifyItems: "center", gap: 7, padding: "22px 12px", textAlign: "center" } },
1761
+ h("span", { style: { ...style.avatar, width: 34, height: 34, background: t.surfaceAlt, color: t.text3, fontSize: 17 } }, "\xB7"),
1762
+ h("span", { style: { fontSize: 12, fontWeight: 600, color: t.text2 } }, machineId ? "\u8FD9\u91CC\u8FD8\u6CA1\u6709\u4F1A\u8BDD" : "\u8BF7\u9009\u62E9\u8FDC\u7A0B\u8BBE\u5907"),
1763
+ h("span", { style: { fontSize: 11, color: t.text3 } }, machineId ? "\u5237\u65B0\u4F1A\u8BDD\u6216\u521B\u5EFA\u4E00\u4E2A\u65B0\u4F1A\u8BDD" : "\u4F1A\u8BDD\u5217\u8868\u4F1A\u663E\u793A\u5728\u8FD9\u91CC")
1764
+ ),
1680
1765
  h(
1681
- "select",
1682
- {
1683
- style: { ...style.input, flex: "0 1 190px", minWidth: 130 },
1684
- value: currentModel,
1685
- disabled: !hasControl || switchingModel || models.length === 0,
1686
- title: "\u5207\u6362\u8BE5\u4F1A\u8BDD\u7684\u6A21\u578B",
1687
- onChange: (event) => {
1688
- const next = event.target.value;
1689
- if (next && next !== currentModel) switchSessionModel(next);
1690
- }
1691
- },
1692
- currentModel && !models.some((m) => m.id === currentModel) ? h("option", { value: currentModel }, currentModel.slice(0, 24)) : null,
1693
- models.map((m) => h("option", { key: m.id, value: m.id }, `${m.displayName} (${Math.round(m.contextWindowTokens / 1e3)}k)`))
1766
+ "div",
1767
+ { style: { borderTop: `1px solid ${t.border}`, paddingTop: 8 } },
1768
+ h("p", { style: { margin: 0, fontSize: 10.5, color: t.text3 } }, machineId ? "\u4F1A\u8BDD\u6765\u81EA\u6240\u9009\u8BBE\u5907\u7684 runtime" : "\u8FDE\u63A5\u8BBE\u5907\u540E\u67E5\u770B\u4F1A\u8BDD")
1694
1769
  )
1695
1770
  ),
1696
- h(
1697
- "div",
1698
- {
1699
- style: style.events,
1700
- "aria-live": "polite",
1701
- "data-events": "",
1702
- ref: eventsRef,
1703
- onScroll: (event) => {
1704
- const el = event.currentTarget;
1705
- if (el.scrollTop < 60 && visibleCount < displayEntries.length) {
1706
- prevScrollHeightRef.current = el.scrollHeight;
1707
- setVisibleCount((count) => Math.min(count + 100, displayEntries.length));
1771
+ sessionId ? h(
1772
+ "section",
1773
+ { style: { ...style.card, ...style.thread }, className: "mai-thread" },
1774
+ h(
1775
+ "div",
1776
+ { style: style.row, className: "mai-thread-header" },
1777
+ h("button", {
1778
+ type: "button",
1779
+ style: { ...style.button, flexShrink: 0 },
1780
+ title: "\u5173\u95ED\u4F1A\u8BDD\u5E76\u8FD4\u56DE\u5217\u8868",
1781
+ onClick: () => {
1782
+ streamGenerationRef.current += 1;
1783
+ streamRef.current?.dispose();
1784
+ streamRef.current = null;
1785
+ setSessionId("");
1786
+ setTurnActive(false);
1787
+ setStopping(false);
1788
+ setSessionStatus("");
1789
+ setStatus("\u8FDC\u7A0B\u4F1A\u8BDD\u5DF2\u5173\u95ED");
1708
1790
  }
1709
- }
1710
- },
1711
- historyLoading ? h(
1791
+ }, "\u2190"),
1792
+ h("strong", { style: { flex: 1, fontSize: 13.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title }, title),
1793
+ sessionStatus ? h(
1794
+ "span",
1795
+ {
1796
+ style: {
1797
+ ...style.statusBadge,
1798
+ background: sessionStatus === "running" ? t.accentSoft : sessionStatus === "error" ? t.dangerBg : t.surfaceAlt,
1799
+ borderColor: sessionStatus === "running" ? t.accent : sessionStatus === "error" ? t.dangerBorder : t.border,
1800
+ color: sessionStatus === "running" ? t.accent : sessionStatus === "error" ? t.danger : t.text2
1801
+ },
1802
+ title: "\u4F1A\u8BDD\u72B6\u6001",
1803
+ "aria-label": `\u4F1A\u8BDD\u72B6\u6001\uFF1A${SESSION_STATUS_LABEL[sessionStatus]}`
1804
+ },
1805
+ sessionStatus === "running" ? h("span", { className: "mai-pulse", style: { ...style.dot, background: t.accent } }) : null,
1806
+ SESSION_STATUS_LABEL[sessionStatus]
1807
+ ) : null,
1808
+ h(
1809
+ "select",
1810
+ {
1811
+ style: { ...style.input, flex: "0 1 190px", minWidth: 130 },
1812
+ value: currentModel,
1813
+ disabled: !hasControl || switchingModel || models.length === 0,
1814
+ title: "\u5207\u6362\u8BE5\u4F1A\u8BDD\u7684\u6A21\u578B",
1815
+ onChange: (event) => {
1816
+ const next = event.target.value;
1817
+ if (next && next !== currentModel) switchSessionModel(next);
1818
+ }
1819
+ },
1820
+ currentModel && !models.some((m) => m.id === currentModel) ? h("option", { value: currentModel }, currentModel.slice(0, 24)) : null,
1821
+ models.map((m) => h("option", { key: m.id, value: m.id }, `${m.displayName} (${Math.round(m.contextWindowTokens / 1e3)}k)`))
1822
+ )
1823
+ ),
1824
+ h(
1712
1825
  "div",
1713
- { style: { fontSize: 12.5, color: t.text3, textAlign: "center", padding: "32px 12px" } },
1714
- displayEntries.length > 0 ? `\u6B63\u5728\u52A0\u8F7D\u4F1A\u8BDD\u5386\u53F2\u2026\uFF08\u5DF2\u52A0\u8F7D ${displayEntries.length} \u6761\uFF09` : "\u6B63\u5728\u52A0\u8F7D\u4F1A\u8BDD\u5386\u53F2\u2026"
1715
- ) : h(
1716
- import_react.default.Fragment,
1717
- null,
1718
- displayEntries.length > visibleCount ? h("div", { style: { fontSize: 11, color: t.text3, textAlign: "center", padding: "6px 0" } }, `\u8FD8\u6709 ${displayEntries.length - visibleCount} \u6761\u66F4\u65E9\u7684\u6D88\u606F \u2014 \u5411\u4E0A\u6EDA\u52A8\u52A0\u8F7D`) : null,
1719
- displayEntries.slice(-visibleCount).map((entry) => {
1720
- if (entry.kind === "thought") {
1721
- return h(
1722
- "div",
1723
- { style: { display: "flex", gap: 8, alignItems: "flex-start", justifyContent: "flex-start" }, key: entry.key },
1724
- h("div", { style: { ...style.avatar, background: "#8a5fd0" } }, "\xB7\xB7\xB7"),
1725
- h(
1726
- "details",
1727
- { style: style.thought },
1728
- h("summary", { style: style.thoughtSummary }, "\u601D\u8003\u8FC7\u7A0B"),
1729
- h("p", { style: { margin: "6px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", color: "#5b4a72", fontStyle: "italic" } }, entry.text || "")
1730
- )
1731
- );
1826
+ {
1827
+ style: style.events,
1828
+ className: "mai-thread-events mai-events",
1829
+ "aria-live": "polite",
1830
+ "data-events": "",
1831
+ ref: eventsRef,
1832
+ onScroll: (event) => {
1833
+ const el = event.currentTarget;
1834
+ if (el.scrollTop < 60 && visibleCount < displayEntries.length) {
1835
+ prevScrollHeightRef.current = el.scrollHeight;
1836
+ setVisibleCount((count) => Math.min(count + 100, displayEntries.length));
1837
+ }
1732
1838
  }
1733
- if (entry.kind === "message") {
1734
- const isUser = entry.role === "user";
1735
- const isThought = entry.role === "thought";
1736
- const avatarColor = isUser ? "#5a6472" : isThought ? "#8a5fd0" : t.accent;
1737
- const bubbleStyle = isUser ? style.bubbleUser : isThought ? style.thought : style.bubble;
1738
- return h(
1739
- "div",
1740
- { style: isUser ? style.bubbleWrapUser : style.bubbleWrap, key: entry.key },
1741
- !isUser ? h("div", { style: { ...style.avatar, background: avatarColor, padding: isUser ? 0 : 3 } }, isThought ? "\xB7\xB7\xB7" : h(MonkeyIcon, { size: 18 })) : null,
1742
- h(
1839
+ },
1840
+ historyLoading ? h(
1841
+ "div",
1842
+ { style: { fontSize: 12.5, color: t.text3, textAlign: "center", padding: "32px 12px" } },
1843
+ displayEntries.length > 0 ? `\u6B63\u5728\u52A0\u8F7D\u4F1A\u8BDD\u5386\u53F2\u2026\uFF08\u5DF2\u52A0\u8F7D ${displayEntries.length} \u6761\uFF09` : "\u6B63\u5728\u52A0\u8F7D\u4F1A\u8BDD\u5386\u53F2\u2026"
1844
+ ) : h(
1845
+ import_react.default.Fragment,
1846
+ null,
1847
+ displayEntries.length > visibleCount ? h("div", { style: { fontSize: 11, color: t.text3, textAlign: "center", padding: "6px 0" } }, `\u8FD8\u6709 ${displayEntries.length - visibleCount} \u6761\u66F4\u65E9\u7684\u6D88\u606F \u2014 \u5411\u4E0A\u6EDA\u52A8\u52A0\u8F7D`) : null,
1848
+ displayEntries.slice(-visibleCount).map((entry) => {
1849
+ if (entry.kind === "thought") {
1850
+ return h(
1743
1851
  "div",
1744
- { style: bubbleStyle },
1745
- h("div", { style: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.04em", marginBottom: 3, opacity: 0.7, textTransform: "uppercase", color: isUser ? "rgba(255,255,255,0.85)" : t.text3 } }, entry.label),
1746
- isUser ? h("p", { style: { margin: 0, whiteSpace: "pre-wrap", overflowWrap: "anywhere" } }, entry.text || "") : h("div", { className: "mai-md" }, renderMarkdown(entry.text || ""))
1747
- ),
1748
- isUser ? h("div", { style: { ...style.avatar, background: avatarColor } }, "\u6211") : null
1749
- );
1750
- }
1751
- if (entry.kind === "tool") {
1752
- const statusLabel = entry.status ? TOOL_STATUS_LABEL[entry.status] ?? entry.status : "running";
1753
- const running = entry.status !== "completed" && entry.status !== "failed" && entry.status !== "cancelled";
1754
- const statusColor = entry.status === "completed" ? "#2f7d54" : entry.status === "failed" ? t.danger : "#8a6d1d";
1755
- const statusBg = entry.status === "completed" ? "#e9f5ee" : entry.status === "failed" ? t.dangerBg : "#fdf6e3";
1756
- return h(
1757
- "details",
1758
- { style: { ...style.event, padding: "6px 11px", background: t.surface }, key: entry.key },
1759
- h(
1760
- "summary",
1761
- { style: { ...style.row, cursor: "pointer", userSelect: "none", listStyle: "none" } },
1762
- h("span", { style: { fontSize: 10, fontWeight: 700, color: t.text3, textTransform: "uppercase", letterSpacing: "0.05em", flexShrink: 0 } }, "\u5DE5\u5177"),
1763
- h("span", { style: { fontSize: 12.5, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1, minWidth: 0 } }, entry.title || "\u6267\u884C\u4E2D"),
1764
- running ? h("span", { className: "mai-pulse", style: { ...style.dot, width: 6, height: 6, background: "#d4a72c" } }) : null,
1765
- h("span", { style: { fontSize: 10.5, padding: "1px 8px", borderRadius: 999, background: statusBg, color: statusColor, fontWeight: 650, flexShrink: 0 } }, statusLabel)
1766
- ),
1767
- entry.detail ? h("pre", { style: { margin: "6px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", fontSize: 11.5, maxHeight: 320, overflowY: "auto", background: t.surfaceAlt, borderRadius: 6, padding: "8px 10px" } }, entry.detail) : null
1768
- );
1769
- }
1770
- if (entry.kind === "plan") {
1852
+ { style: { display: "flex", gap: 8, alignItems: "flex-start", justifyContent: "flex-start" }, key: entry.key },
1853
+ h("div", { style: { ...style.avatar, background: "#8a5fd0" } }, "\xB7\xB7\xB7"),
1854
+ h(
1855
+ "details",
1856
+ { style: style.thought },
1857
+ h("summary", { style: style.thoughtSummary }, "\u601D\u8003\u8FC7\u7A0B"),
1858
+ h("p", { style: { margin: "6px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", color: "#5b4a72", fontStyle: "italic" } }, entry.text || "")
1859
+ )
1860
+ );
1861
+ }
1862
+ if (entry.kind === "message") {
1863
+ const isUser = entry.role === "user";
1864
+ const isThought = entry.role === "thought";
1865
+ const avatarColor = isUser ? "#5a6472" : isThought ? "#8a5fd0" : t.accent;
1866
+ const bubbleStyle = isUser ? style.bubbleUser : isThought ? style.thought : style.bubble;
1867
+ return h(
1868
+ "div",
1869
+ { style: isUser ? style.bubbleWrapUser : style.bubbleWrap, key: entry.key },
1870
+ !isUser ? h("div", { style: { ...style.avatar, background: avatarColor, padding: isUser ? 0 : 3 } }, isThought ? "\xB7\xB7\xB7" : h(MonkeyIcon, { size: 18 })) : null,
1871
+ h(
1872
+ "div",
1873
+ { style: bubbleStyle },
1874
+ h("div", { style: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.04em", marginBottom: 3, opacity: 0.7, textTransform: "uppercase", color: isUser ? "rgba(255,255,255,0.85)" : t.text3 } }, entry.label),
1875
+ isUser ? h("p", { style: { margin: 0, whiteSpace: "pre-wrap", overflowWrap: "anywhere" } }, entry.text || "") : h("div", { className: "mai-md" }, renderMarkdown(entry.text || ""))
1876
+ ),
1877
+ isUser ? h("div", { style: { ...style.avatar, background: avatarColor } }, "\u6211") : null
1878
+ );
1879
+ }
1880
+ if (entry.kind === "tool") {
1881
+ const statusLabel = entry.status ? TOOL_STATUS_LABEL[entry.status] ?? entry.status : "running";
1882
+ const running = entry.status !== "completed" && entry.status !== "failed" && entry.status !== "cancelled";
1883
+ const statusColor = entry.status === "completed" ? "#2f7d54" : entry.status === "failed" ? t.danger : "#8a6d1d";
1884
+ const statusBg = entry.status === "completed" ? "#e9f5ee" : entry.status === "failed" ? t.dangerBg : "#fdf6e3";
1885
+ return h(
1886
+ "details",
1887
+ { style: { ...style.event, padding: "6px 11px", background: t.surface }, key: entry.key },
1888
+ h(
1889
+ "summary",
1890
+ { style: { ...style.row, cursor: "pointer", userSelect: "none", listStyle: "none" } },
1891
+ h("span", { style: { fontSize: 10, fontWeight: 700, color: t.text3, textTransform: "uppercase", letterSpacing: "0.05em", flexShrink: 0 } }, "\u5DE5\u5177"),
1892
+ h("span", { style: { fontSize: 12.5, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1, minWidth: 0 } }, entry.title || "\u6267\u884C\u4E2D"),
1893
+ running ? h("span", { className: "mai-pulse", style: { ...style.dot, width: 6, height: 6, background: "#d4a72c" } }) : null,
1894
+ h("span", { style: { fontSize: 10.5, padding: "1px 8px", borderRadius: 999, background: statusBg, color: statusColor, fontWeight: 650, flexShrink: 0 } }, statusLabel)
1895
+ ),
1896
+ entry.detail ? h("pre", { style: { margin: "6px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", fontSize: 11.5, maxHeight: 320, overflowY: "auto", background: t.surfaceAlt, borderRadius: 6, padding: "8px 10px" } }, entry.detail) : null
1897
+ );
1898
+ }
1899
+ if (entry.kind === "plan") {
1900
+ return h(
1901
+ "article",
1902
+ { style: style.event, key: entry.key },
1903
+ h("div", { style: { fontSize: 10.5, fontWeight: 700, color: t.text3, marginBottom: 3, textTransform: "uppercase", letterSpacing: "0.04em" } }, "\u8BA1\u5212"),
1904
+ ...entry.entries.map((item, index) => h(
1905
+ "div",
1906
+ { style: { display: "flex", gap: 6, fontSize: 12.5, margin: "2px 0" }, key: index },
1907
+ h("span", { style: { opacity: 0.55 } }, index + 1 + "."),
1908
+ h(
1909
+ "span",
1910
+ { style: { textDecoration: item.status === "completed" ? "line-through" : "none", opacity: item.status === "completed" ? 0.55 : 1 } },
1911
+ typeof item.content === "string" ? item.content : JSON.stringify(item.content ?? item)
1912
+ )
1913
+ ))
1914
+ );
1915
+ }
1916
+ if (entry.kind === "system") {
1917
+ return h("div", { style: { fontSize: 11.5, color: t.text3, textAlign: "center", padding: "2px 0" }, key: entry.key }, entry.label);
1918
+ }
1771
1919
  return h(
1772
1920
  "article",
1773
1921
  { style: style.event, key: entry.key },
1774
- h("div", { style: { fontSize: 10.5, fontWeight: 700, color: t.text3, marginBottom: 3, textTransform: "uppercase", letterSpacing: "0.04em" } }, "\u8BA1\u5212"),
1775
- ...entry.entries.map((item, index) => h(
1776
- "div",
1777
- { style: { display: "flex", gap: 6, fontSize: 12.5, margin: "2px 0" }, key: index },
1778
- h("span", { style: { opacity: 0.55 } }, index + 1 + "."),
1779
- h(
1780
- "span",
1781
- { style: { textDecoration: item.status === "completed" ? "line-through" : "none", opacity: item.status === "completed" ? 0.55 : 1 } },
1782
- typeof item.content === "string" ? item.content : JSON.stringify(item.content ?? item)
1783
- )
1784
- ))
1922
+ h("div", { style: { opacity: 0.6, fontSize: 11, marginBottom: 4 } }, entry.label),
1923
+ h(
1924
+ "details",
1925
+ null,
1926
+ h("summary", { style: style.summary }, "\u67E5\u770B\u8BE6\u60C5"),
1927
+ h("pre", { style: { margin: "5px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", fontSize: 11.5 } }, JSON.stringify(entry.data ?? {}, null, 2))
1928
+ )
1785
1929
  );
1786
- }
1787
- if (entry.kind === "system") {
1788
- return h("div", { style: { fontSize: 11.5, color: t.text3, textAlign: "center", padding: "2px 0" }, key: entry.key }, entry.label);
1789
- }
1790
- return h(
1791
- "article",
1792
- { style: style.event, key: entry.key },
1793
- h("div", { style: { opacity: 0.6, fontSize: 11, marginBottom: 4 } }, entry.label),
1794
- h(
1795
- "details",
1796
- null,
1797
- h("summary", { style: style.summary }, "\u67E5\u770B\u8BE6\u60C5"),
1798
- h("pre", { style: { margin: "5px 0 0", whiteSpace: "pre-wrap", overflowWrap: "anywhere", fontSize: 11.5 } }, JSON.stringify(entry.data ?? {}, null, 2))
1799
- )
1800
- );
1801
- })
1802
- )
1803
- ),
1804
- ...pendingRequests.map((request) => h(ApprovalCard, {
1805
- key: `${typeof request.requestId}:${request.requestId}`,
1806
- request,
1807
- hasControl,
1808
- onRespond: respond,
1809
- answerText,
1810
- setAnswerText,
1811
- onAnswer: answerQuestion
1812
- })),
1813
- h(
1814
- "form",
1815
- { style: style.composer, onSubmit: (event) => {
1816
- event.preventDefault();
1817
- sendPrompt();
1818
- } },
1819
- h("textarea", {
1820
- style: style.textarea,
1821
- value: prompt,
1822
- onChange: (event) => setPrompt(event.target.value),
1823
- onKeyDown: (event) => {
1824
- if (event.key === "Enter" && !event.shiftKey) {
1825
- event.preventDefault();
1826
- sendPrompt();
1827
- }
1828
- },
1829
- placeholder: hasControl ? "\u5411\u8FDC\u7A0B\u4F1A\u8BDD\u4E0B\u53D1\u6307\u4EE4\uFF08\u56DE\u8F66\u53D1\u9001\uFF0CShift+\u56DE\u8F66\u6362\u884C\uFF09" : "\u53EA\u8BFB\u6A21\u5F0F\uFF1A\u4E0B\u53D1\u6307\u4EE4\u9700\u8981 session:control \u6743\u9650",
1830
- disabled: !hasControl
1831
- }),
1832
- turnActive ? h(
1833
- "button",
1834
- {
1835
- type: "button",
1836
- style: style.stopButton,
1837
- disabled: !hasControl || stopping,
1838
- onClick: stopTurn,
1839
- title: stopping ? "\u505C\u6B62\u4E2D\u2026" : "\u505C\u6B62\u5F53\u524D\u56DE\u5408",
1840
- "aria-label": "\u505C\u6B62\u5F53\u524D\u56DE\u5408"
1841
- },
1842
- h("span", { style: style.stopIcon })
1843
- ) : h(
1844
- "button",
1845
- {
1846
- type: "submit",
1847
- style: style.sendButton,
1848
- disabled: !streamRef.current || !hasControl || prompt.trim() === "",
1849
- title: "\u53D1\u9001\uFF08\u56DE\u8F66\uFF09",
1850
- "aria-label": "\u53D1\u9001\u6307\u4EE4"
1851
- },
1852
- h(
1853
- "svg",
1854
- { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", style: { display: "block" } },
1855
- h("path", { d: "M8 12.5V3.5M8 3.5L4 7.5M8 3.5L12 7.5", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round" })
1930
+ })
1856
1931
  )
1857
- )
1858
- ),
1859
- lastDelivery || turnActive ? h(
1860
- "div",
1861
- { style: { fontSize: 11.5, color: turnActive ? t.accent : lastDelivery?.accepted ? t.success : lastDelivery?.frame === "pending" ? t.text3 : t.danger, textAlign: "right", display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 6 } },
1862
- turnActive ? h("span", { className: "mai-pulse", style: { ...style.dot, background: t.accent } }) : null,
1863
- turnActive ? stopping ? "\u6B63\u5728\u505C\u6B62\u5F53\u524D\u56DE\u5408\u2026" : sessionStatus === "running" ? "MonkeyAI \u6B63\u5728\u5904\u7406\uFF08\u8FDC\u7AEF\u5BA2\u6237\u7AEF\u53D1\u8D77\uFF09\u2026" : "MonkeyAI \u6B63\u5728\u5904\u7406\u2026" : lastDelivery?.accepted ? "\u56DE\u5408\u5DF2\u7ED3\u675F" : lastDelivery?.frame === "pending" ? "\u7B49\u5F85\u6295\u9012" : "\u6295\u9012\u88AB\u62D2\u7EDD"
1864
- ) : null
1932
+ ),
1933
+ ...pendingRequests.map((request) => h(ApprovalCard, {
1934
+ key: `${typeof request.requestId}:${request.requestId}`,
1935
+ request,
1936
+ hasControl,
1937
+ onRespond: respond,
1938
+ answerText,
1939
+ setAnswerText,
1940
+ onAnswer: answerQuestion
1941
+ })),
1942
+ h(
1943
+ "form",
1944
+ { style: style.composer, onSubmit: (event) => {
1945
+ event.preventDefault();
1946
+ sendPrompt();
1947
+ } },
1948
+ h("textarea", {
1949
+ style: style.textarea,
1950
+ value: prompt,
1951
+ onChange: (event) => setPrompt(event.target.value),
1952
+ onKeyDown: (event) => {
1953
+ if (event.key === "Enter" && !event.shiftKey) {
1954
+ event.preventDefault();
1955
+ sendPrompt();
1956
+ }
1957
+ },
1958
+ placeholder: hasControl ? "\u5411\u8FDC\u7A0B\u4F1A\u8BDD\u4E0B\u53D1\u6307\u4EE4\uFF08\u56DE\u8F66\u53D1\u9001\uFF0CShift+\u56DE\u8F66\u6362\u884C\uFF09" : "\u53EA\u8BFB\u6A21\u5F0F\uFF1A\u4E0B\u53D1\u6307\u4EE4\u9700\u8981 session:control \u6743\u9650",
1959
+ disabled: !hasControl
1960
+ }),
1961
+ turnActive ? h(
1962
+ "button",
1963
+ {
1964
+ type: "button",
1965
+ style: style.stopButton,
1966
+ disabled: !hasControl || stopping,
1967
+ onClick: stopTurn,
1968
+ title: stopping ? "\u505C\u6B62\u4E2D\u2026" : "\u505C\u6B62\u5F53\u524D\u56DE\u5408",
1969
+ "aria-label": "\u505C\u6B62\u5F53\u524D\u56DE\u5408"
1970
+ },
1971
+ h("span", { style: style.stopIcon })
1972
+ ) : h(
1973
+ "button",
1974
+ {
1975
+ type: "submit",
1976
+ style: style.sendButton,
1977
+ disabled: !streamRef.current || !hasControl || prompt.trim() === "",
1978
+ title: "\u53D1\u9001\uFF08\u56DE\u8F66\uFF09",
1979
+ "aria-label": "\u53D1\u9001\u6307\u4EE4"
1980
+ },
1981
+ h(
1982
+ "svg",
1983
+ { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", style: { display: "block" } },
1984
+ h("path", { d: "M8 12.5V3.5M8 3.5L4 7.5M8 3.5L12 7.5", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round" })
1985
+ )
1986
+ )
1987
+ ),
1988
+ lastDelivery || turnActive ? h(
1989
+ "div",
1990
+ { style: { fontSize: 11.5, color: turnActive ? t.accent : lastDelivery?.accepted ? t.success : lastDelivery?.frame === "pending" ? t.text3 : t.danger, textAlign: "right", display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 6 } },
1991
+ turnActive ? h("span", { className: "mai-pulse", style: { ...style.dot, background: t.accent } }) : null,
1992
+ turnActive ? stopping ? "\u6B63\u5728\u505C\u6B62\u5F53\u524D\u56DE\u5408\u2026" : "MonkeyAI \u6B63\u5728\u5904\u7406\u2026" : lastDelivery?.accepted ? "\u56DE\u5408\u5DF2\u7ED3\u675F" : lastDelivery?.frame === "pending" ? "\u7B49\u5F85\u6295\u9012" : "\u6295\u9012\u88AB\u62D2\u7EDD"
1993
+ ) : null
1994
+ ) : h(
1995
+ "section",
1996
+ { style: style.emptyThread, className: "mai-empty-thread" },
1997
+ h("div", { className: "mai-empty-mark" }, h(MonkeyIcon, { size: 26 })),
1998
+ h("p", { style: style.eyebrow }, "\u8FDC\u7A0B\u4F1A\u8BDD"),
1999
+ h("h2", { style: { margin: 0, fontSize: 18, fontWeight: 650, color: "#182230" } }, "\u9009\u62E9\u4E00\u4E2A\u4F1A\u8BDD"),
2000
+ h("p", { style: { ...style.subtitle, maxWidth: 300 } }, sessionOptions.length > 0 ? "\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4F1A\u8BDD\uFF0C\u5728\u6B64\u7EE7\u7EED\u5BF9\u8BDD\u3002" : machineId ? "\u5F53\u524D\u8BBE\u5907\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u3002\u53EF\u5728\u4E0A\u65B9\u65B0\u5EFA\u6216\u5237\u65B0\u3002" : "\u5148\u9009\u62E9\u4E00\u4E2A\u8FDC\u7A0B\u8BBE\u5907\u3002")
2001
+ )
1865
2002
  ) : null
1866
2003
  );
1867
2004
  }