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