@xcanwin/manyoyo 5.5.2 → 5.6.1

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.
@@ -40,19 +40,24 @@
40
40
  active: '',
41
41
  messages: [],
42
42
  messageRenderKeys: [],
43
+ activeTab: 'activity',
43
44
  mode: 'agent',
44
45
  sending: false,
45
46
  loadingSessions: false,
46
47
  loadingMessages: false,
48
+ loadingSessionDetail: false,
47
49
  mobileSidebarOpen: false,
48
50
  mobileActionsOpen: false,
49
- modeMenuOpen: false,
50
51
  configModalOpen: false,
51
52
  createModalOpen: false,
52
53
  configLoading: false,
53
54
  configSaving: false,
54
55
  createLoading: false,
55
56
  createSubmitting: false,
57
+ configSnapshot: null,
58
+ sessionDetail: null,
59
+ sessionDetailError: '',
60
+ sessionDetailRequestId: 0,
56
61
  createAgentPromptAuto: false,
57
62
  createDefaults: null,
58
63
  createRuns: {},
@@ -82,8 +87,10 @@
82
87
  const mobileSessionToggle = document.getElementById('mobileSessionToggle');
83
88
  const mobileActionsToggle = document.getElementById('mobileActionsToggle');
84
89
  const headerActions = document.getElementById('headerActions');
85
- const modeToggle = document.getElementById('modeToggle');
86
- const modeMenu = document.getElementById('modeMenu');
90
+ const viewActivityBtn = document.getElementById('viewActivityBtn');
91
+ const viewTerminalBtn = document.getElementById('viewTerminalBtn');
92
+ const viewConfigBtn = document.getElementById('viewConfigBtn');
93
+ const viewCheckBtn = document.getElementById('viewCheckBtn');
87
94
  const mobileSidebarClose = document.getElementById('mobileSidebarClose');
88
95
  const sidebarBackdrop = document.getElementById('sidebarBackdrop');
89
96
  const openConfigBtn = document.getElementById('openConfigBtn');
@@ -118,11 +125,15 @@
118
125
  const createVolumes = document.getElementById('createVolumes');
119
126
  const activeTitle = document.getElementById('activeTitle');
120
127
  const activeMeta = document.getElementById('activeMeta');
121
- const modeCommandBtn = document.getElementById('modeCommandBtn');
122
- const modeAgentBtn = document.getElementById('modeAgentBtn');
123
- const modeTerminalBtn = document.getElementById('modeTerminalBtn');
128
+ const activityCommandBtn = document.getElementById('activityCommandBtn');
129
+ const activityAgentBtn = document.getElementById('activityAgentBtn');
124
130
  const messagesNode = document.getElementById('messages');
125
131
  const terminalPanel = document.getElementById('terminalPanel');
132
+ const configPanel = document.getElementById('configPanel');
133
+ const checkPanel = document.getElementById('checkPanel');
134
+ const configSummary = document.getElementById('configSummary');
135
+ const checkSummary = document.getElementById('checkSummary');
136
+ const contextSummary = document.getElementById('contextSummary');
126
137
  const terminalScreen = document.getElementById('terminalScreen');
127
138
  const composer = document.getElementById('composer');
128
139
  const commandInput = document.getElementById('commandInput');
@@ -154,7 +165,7 @@
154
165
  const AGENT_PROMPT_TEMPLATE_MAP = {
155
166
  claude: 'claude -p {prompt}',
156
167
  gemini: 'gemini -p {prompt}',
157
- codex: 'codex exec {prompt}',
168
+ codex: 'codex exec --skip-git-repo-check {prompt}',
158
169
  opencode: 'opencode run {prompt}'
159
170
  };
160
171
  const markdownRenderer = window.ManyoyoMarkdown
@@ -534,7 +545,7 @@
534
545
  }
535
546
 
536
547
  function isComposerMode() {
537
- return state.mode === 'command' || state.mode === 'agent';
548
+ return state.activeTab === 'activity';
538
549
  }
539
550
 
540
551
  function isActiveAgentEnabled() {
@@ -898,18 +909,220 @@
898
909
  setMobileActionsMenu(false);
899
910
  }
900
911
 
901
- const MODE_LABELS = { agent: 'AGENT 模式', command: '命令模式', terminal: '终端模式' };
912
+ const VIEW_LABELS = {
913
+ activity: '活动',
914
+ terminal: '终端',
915
+ config: '配置',
916
+ check: '检查'
917
+ };
902
918
 
903
- function setModeMenu(open) {
904
- state.modeMenuOpen = Boolean(open);
905
- document.body.classList.toggle('mode-menu-open', state.modeMenuOpen);
906
- if (modeToggle) {
907
- modeToggle.setAttribute('aria-expanded', state.modeMenuOpen ? 'true' : 'false');
919
+ function setActiveTab(tab) {
920
+ const next = String(tab || '').trim();
921
+ if (!VIEW_LABELS[next]) {
922
+ return;
923
+ }
924
+ state.activeTab = next;
925
+ syncUi();
926
+ if (next === 'terminal' && state.active && ensureTerminalReady()) {
927
+ scheduleTerminalFit(false);
928
+ if (!state.terminal.connected && !state.terminal.connecting && !isActiveSessionHistoryOnly()) {
929
+ connectTerminal();
930
+ }
931
+ if (state.terminal.term) {
932
+ state.terminal.term.focus();
933
+ }
908
934
  }
909
935
  }
910
936
 
911
- function closeModeMenu() {
912
- setModeMenu(false);
937
+ function escapeHtml(value) {
938
+ return String(value == null ? '' : value)
939
+ .replace(/&/g, '&')
940
+ .replace(/</g, '&lt;')
941
+ .replace(/>/g, '&gt;')
942
+ .replace(/"/g, '&quot;')
943
+ .replace(/'/g, '&#39;');
944
+ }
945
+
946
+ function renderEmptyInspector(container, title, description) {
947
+ if (!container) return;
948
+ container.innerHTML = `
949
+ <section class="info-card empty-card">
950
+ <div class="card-title">${escapeHtml(title)}</div>
951
+ <div class="card-desc">${escapeHtml(description)}</div>
952
+ </section>
953
+ `;
954
+ }
955
+
956
+ function renderKeyValueCard(container, title, entries, options) {
957
+ if (!container) return;
958
+ const opts = options && typeof options === 'object' ? options : {};
959
+ const actionHtml = opts.actionLabel && opts.actionId
960
+ ? `<button type="button" class="secondary inline-action" id="${escapeHtml(opts.actionId)}">${escapeHtml(opts.actionLabel)}</button>`
961
+ : '';
962
+ const rowsHtml = (entries || []).map(function (entry) {
963
+ const tone = entry && entry.tone ? ` tone-${entry.tone}` : '';
964
+ return `
965
+ <div class="kv-row${tone}">
966
+ <span class="kv-label">${escapeHtml(entry.label || '')}</span>
967
+ <span class="kv-value">${escapeHtml(entry.value || '—')}</span>
968
+ </div>
969
+ `;
970
+ }).join('');
971
+ container.insertAdjacentHTML('beforeend', `
972
+ <section class="info-card">
973
+ <div class="card-head">
974
+ <div class="card-title">${escapeHtml(title)}</div>
975
+ ${actionHtml}
976
+ </div>
977
+ <div class="kv-list">${rowsHtml}</div>
978
+ </section>
979
+ `);
980
+ }
981
+
982
+ function renderCheckCard(container, title, checks) {
983
+ if (!container) return;
984
+ const itemsHtml = (checks || []).map(function (item) {
985
+ return `
986
+ <div class="check-item tone-${escapeHtml(item.tone || 'info')}">
987
+ <div class="check-main">
988
+ <span class="check-label">${escapeHtml(item.label || '')}</span>
989
+ <span class="check-value">${escapeHtml(item.value || '')}</span>
990
+ </div>
991
+ <div class="check-detail">${escapeHtml(item.detail || '')}</div>
992
+ </div>
993
+ `;
994
+ }).join('');
995
+ container.insertAdjacentHTML('beforeend', `
996
+ <section class="info-card">
997
+ <div class="card-title">${escapeHtml(title)}</div>
998
+ <div class="check-list">${itemsHtml}</div>
999
+ </section>
1000
+ `);
1001
+ }
1002
+
1003
+ function renderSessionDetailPanels() {
1004
+ const detail = state.sessionDetail;
1005
+ if (!state.active) {
1006
+ renderEmptyInspector(contextSummary, '当前上下文', '选择左侧会话后,这里会显示容器状态、配置摘要与 Agent 信息。');
1007
+ renderEmptyInspector(configSummary, '配置视图', '选择会话后可查看当前容器会话的运行参数摘要。');
1008
+ renderEmptyInspector(checkSummary, '检查视图', '选择会话后可查看当前会话的基础健康检查。');
1009
+ return;
1010
+ }
1011
+ if (state.loadingSessionDetail) {
1012
+ renderEmptyInspector(contextSummary, '当前上下文', '正在加载会话详情...');
1013
+ renderEmptyInspector(configSummary, '配置视图', '正在加载会话详情...');
1014
+ renderEmptyInspector(checkSummary, '检查视图', '正在加载会话详情...');
1015
+ return;
1016
+ }
1017
+ if (!detail) {
1018
+ const message = state.sessionDetailError || '当前会话详情暂时不可用。';
1019
+ renderEmptyInspector(contextSummary, '当前上下文', message);
1020
+ renderEmptyInspector(configSummary, '配置视图', message);
1021
+ renderEmptyInspector(checkSummary, '检查视图', message);
1022
+ return;
1023
+ }
1024
+
1025
+ const applied = detail.applied || {};
1026
+ const status = sessionStatusInfo(detail.status);
1027
+ const updatedText = formatDateTime(detail.updatedAt) || '暂无更新';
1028
+
1029
+ if (contextSummary) {
1030
+ contextSummary.innerHTML = '';
1031
+ renderKeyValueCard(contextSummary, '会话概览', [
1032
+ { label: '会话', value: detail.name || state.active },
1033
+ { label: '状态', value: status.label, tone: status.tone },
1034
+ { label: '镜像', value: detail.image || applied.imageName || '—' },
1035
+ { label: '最近更新', value: updatedText },
1036
+ { label: '消息数', value: String(safeMessageCount(detail.messageCount)) }
1037
+ ]);
1038
+ renderKeyValueCard(contextSummary, 'Agent 上下文', [
1039
+ { label: '已启用', value: detail.agentEnabled ? '是' : '否', tone: detail.agentEnabled ? 'ok' : 'warn' },
1040
+ { label: '程序', value: detail.agentProgram || '—' },
1041
+ { label: '支持 resume', value: detail.resumeSupported ? '是' : '否', tone: detail.resumeSupported ? 'ok' : 'warn' },
1042
+ { label: '最近 resume', value: detail.lastResumeAt ? formatDateTime(detail.lastResumeAt) : '暂无' },
1043
+ { label: '最近结果', value: detail.lastResumeOk == null ? '暂无' : (detail.lastResumeOk ? '成功' : '失败'), tone: detail.lastResumeOk == null ? 'info' : (detail.lastResumeOk ? 'ok' : 'danger') }
1044
+ ]);
1045
+ renderKeyValueCard(contextSummary, '运行参数', [
1046
+ { label: 'hostPath', value: applied.hostPath || '—' },
1047
+ { label: 'containerPath', value: applied.containerPath || '—' },
1048
+ { label: 'imageVersion', value: applied.imageVersion || '—' },
1049
+ { label: 'containerMode', value: applied.containerMode || 'default' },
1050
+ { label: 'env/vol/ports', value: `${applied.envCount || 0} / ${applied.volumeCount || 0} / ${applied.portCount || 0}` }
1051
+ ]);
1052
+ }
1053
+
1054
+ if (configSummary) {
1055
+ configSummary.innerHTML = '';
1056
+ renderKeyValueCard(configSummary, '配置摘要', [
1057
+ { label: 'containerName', value: applied.containerName || detail.name || state.active },
1058
+ { label: 'hostPath', value: applied.hostPath || '—' },
1059
+ { label: 'containerPath', value: applied.containerPath || '—' },
1060
+ { label: 'imageName', value: applied.imageName || detail.image || '—' },
1061
+ { label: 'imageVersion', value: applied.imageVersion || '—' },
1062
+ { label: 'containerMode', value: applied.containerMode || 'default' }
1063
+ ], { actionLabel: '打开配置', actionId: 'configSummaryOpenBtn' });
1064
+ renderKeyValueCard(configSummary, '命令与 Agent', [
1065
+ { label: 'shellPrefix', value: applied.shellPrefix || '—' },
1066
+ { label: 'shell', value: applied.shell || '—' },
1067
+ { label: 'shellSuffix', value: applied.shellSuffix || '—' },
1068
+ { label: '默认命令', value: applied.defaultCommand || '—' },
1069
+ { label: 'Agent 模板', value: detail.agentPromptCommand || '—' },
1070
+ { label: 'yolo', value: applied.yolo || '—' }
1071
+ ]);
1072
+ }
1073
+
1074
+ if (checkSummary) {
1075
+ checkSummary.innerHTML = '';
1076
+ renderCheckCard(checkSummary, '基础检查', [
1077
+ {
1078
+ label: '容器状态',
1079
+ value: status.label,
1080
+ tone: status.tone === 'running' ? 'ok' : (status.tone === 'history' ? 'warn' : 'danger'),
1081
+ detail: status.tone === 'running' ? '容器处于可交互状态。' : '当前不是活跃运行态,部分功能可能受限。'
1082
+ },
1083
+ {
1084
+ label: 'Agent 模板',
1085
+ value: detail.agentEnabled ? '已配置' : '未配置',
1086
+ tone: detail.agentEnabled ? 'ok' : 'warn',
1087
+ detail: detail.agentEnabled ? '活动页可直接发送 Agent 提示词。' : '当前会话不支持 Agent 模式。'
1088
+ },
1089
+ {
1090
+ label: 'Resume 能力',
1091
+ value: detail.resumeSupported ? '支持' : '不支持',
1092
+ tone: detail.resumeSupported ? 'ok' : 'warn',
1093
+ detail: detail.resumeSupported ? '可以尝试基于历史继续 Agent 会话。' : '当前 Agent 程序或模板不支持 resume。'
1094
+ },
1095
+ {
1096
+ label: '镜像版本格式',
1097
+ value: applied.imageVersion || '缺失',
1098
+ tone: /^\d+\.\d+\.\d+-[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(String(applied.imageVersion || '')) ? 'ok' : 'danger',
1099
+ detail: '建议保持 x.y.z-后缀 格式,便于 manyoyo 的版本校验。'
1100
+ },
1101
+ {
1102
+ label: '工作目录',
1103
+ value: applied.hostPath && applied.containerPath ? '完整' : '缺失',
1104
+ tone: applied.hostPath && applied.containerPath ? 'ok' : 'danger',
1105
+ detail: 'hostPath / containerPath 是容器会话最关键的上下文。'
1106
+ }
1107
+ ]);
1108
+ if (detail.lastResumeError) {
1109
+ renderCheckCard(checkSummary, '最近问题', [
1110
+ {
1111
+ label: 'Resume 错误',
1112
+ value: '有错误输出',
1113
+ tone: 'danger',
1114
+ detail: detail.lastResumeError
1115
+ }
1116
+ ]);
1117
+ }
1118
+ }
1119
+
1120
+ const configSummaryOpenBtn = document.getElementById('configSummaryOpenBtn');
1121
+ if (configSummaryOpenBtn) {
1122
+ configSummaryOpenBtn.addEventListener('click', function () {
1123
+ openConfigModal();
1124
+ });
1125
+ }
913
1126
  }
914
1127
 
915
1128
  function syncUi() {
@@ -924,34 +1137,44 @@
924
1137
  activeMeta.textContent = buildActiveMeta(getActiveSession());
925
1138
  }
926
1139
 
1140
+ const activityTab = state.activeTab === 'activity';
1141
+ const terminalTab = state.activeTab === 'terminal';
1142
+ const configTab = state.activeTab === 'config';
1143
+ const checkTab = state.activeTab === 'check';
927
1144
  const commandMode = state.mode === 'command';
928
1145
  const agentMode = state.mode === 'agent';
929
- const terminalMode = state.mode === 'terminal';
930
- const composerMode = commandMode || agentMode;
931
1146
  const agentEnabled = isActiveAgentEnabled();
932
1147
 
933
1148
  document.body.classList.toggle('command-mode', commandMode);
934
1149
  document.body.classList.toggle('agent-mode', agentMode);
935
- document.body.classList.toggle('terminal-mode', terminalMode);
936
- if (modeCommandBtn) {
937
- modeCommandBtn.classList.toggle('is-active', commandMode);
938
- modeCommandBtn.setAttribute('aria-pressed', commandMode ? 'true' : 'false');
1150
+ document.body.classList.toggle('terminal-mode', terminalTab);
1151
+ document.body.classList.toggle('config-tab', configTab);
1152
+ document.body.classList.toggle('check-tab', checkTab);
1153
+ if (activityCommandBtn) {
1154
+ activityCommandBtn.classList.toggle('is-active', commandMode);
1155
+ activityCommandBtn.setAttribute('aria-pressed', commandMode ? 'true' : 'false');
1156
+ }
1157
+ if (activityAgentBtn) {
1158
+ activityAgentBtn.classList.toggle('is-active', agentMode);
1159
+ activityAgentBtn.setAttribute('aria-pressed', agentMode ? 'true' : 'false');
1160
+ }
1161
+ if (viewActivityBtn) viewActivityBtn.classList.toggle('is-active', activityTab);
1162
+ if (viewTerminalBtn) viewTerminalBtn.classList.toggle('is-active', terminalTab);
1163
+ if (viewConfigBtn) viewConfigBtn.classList.toggle('is-active', configTab);
1164
+ if (viewCheckBtn) viewCheckBtn.classList.toggle('is-active', checkTab);
1165
+ if (messagesNode) {
1166
+ messagesNode.hidden = !activityTab;
939
1167
  }
940
- if (modeAgentBtn) {
941
- modeAgentBtn.classList.toggle('is-active', agentMode);
942
- modeAgentBtn.setAttribute('aria-pressed', agentMode ? 'true' : 'false');
1168
+ if (terminalPanel) {
1169
+ terminalPanel.hidden = !terminalTab;
943
1170
  }
944
- if (modeTerminalBtn) {
945
- modeTerminalBtn.classList.toggle('is-active', terminalMode);
946
- modeTerminalBtn.setAttribute('aria-pressed', terminalMode ? 'true' : 'false');
1171
+ if (configPanel) {
1172
+ configPanel.hidden = !configTab;
947
1173
  }
948
- if (modeToggle) {
949
- modeToggle.textContent = MODE_LABELS[state.mode] || '模式';
1174
+ if (checkPanel) {
1175
+ checkPanel.hidden = !checkTab;
950
1176
  }
951
- if (terminalPanel) {
952
- terminalPanel.hidden = !terminalMode;
953
- }
954
- if (terminalMode && state.terminal.terminalReady) {
1177
+ if (terminalTab && state.terminal.terminalReady) {
955
1178
  scheduleTerminalFit(false);
956
1179
  }
957
1180
 
@@ -959,8 +1182,8 @@
959
1182
  refreshBtn.disabled = busy;
960
1183
  removeBtn.disabled = !state.active || busy;
961
1184
  removeAllBtn.disabled = !state.active || busy;
962
- sendBtn.disabled = !composerMode || !state.active || busy || (agentMode && !agentEnabled);
963
- commandInput.disabled = !composerMode || !state.active || (agentMode && !agentEnabled);
1185
+ sendBtn.disabled = !activityTab || !state.active || busy || (agentMode && !agentEnabled);
1186
+ commandInput.disabled = !activityTab || !state.active || (agentMode && !agentEnabled);
964
1187
  if (commandInput) {
965
1188
  commandInput.placeholder = agentMode
966
1189
  ? '输入提示词,例如:请帮我分析当前项目结构并给出重构建议'
@@ -1013,8 +1236,12 @@
1013
1236
  sendState.textContent = '就绪';
1014
1237
  }
1015
1238
  sendState.classList.toggle('is-active', state.sending);
1239
+ if (composer) {
1240
+ composer.hidden = !activityTab;
1241
+ }
1016
1242
  setMobileSessionPanel(state.mobileSidebarOpen);
1017
1243
  setMobileActionsMenu(state.mobileActionsOpen);
1244
+ renderSessionDetailPanels();
1018
1245
  }
1019
1246
 
1020
1247
  async function api(url, options) {
@@ -1041,7 +1268,9 @@
1041
1268
  }
1042
1269
 
1043
1270
  async function fetchConfigSnapshot() {
1044
- return await api('/api/config');
1271
+ const snapshot = await api('/api/config');
1272
+ state.configSnapshot = snapshot;
1273
+ return snapshot;
1045
1274
  }
1046
1275
 
1047
1276
  async function openConfigModal() {
@@ -1084,6 +1313,7 @@
1084
1313
  method: 'PUT',
1085
1314
  body: JSON.stringify({ raw: configEditor ? configEditor.value : '' })
1086
1315
  });
1316
+ await fetchConfigSnapshot();
1087
1317
  showConfigError('');
1088
1318
  alert('配置已保存。后续新建会读取最新配置。');
1089
1319
  } catch (e) {
@@ -1195,10 +1425,12 @@
1195
1425
  disconnectTerminal('会话切换,终端已断开', true);
1196
1426
  }
1197
1427
  state.active = sessionName;
1428
+ state.sessionDetail = null;
1429
+ state.sessionDetailError = '';
1198
1430
  if (isMobileLayout()) {
1199
1431
  closeMobileSessionPanel();
1200
1432
  }
1201
- if (state.mode === 'terminal' && ensureTerminalReady()) {
1433
+ if (state.activeTab === 'terminal' && ensureTerminalReady()) {
1202
1434
  scheduleTerminalFit(false);
1203
1435
  if (!state.terminal.connected && !state.terminal.connecting) {
1204
1436
  if (isActiveSessionHistoryOnly()) {
@@ -1210,7 +1442,10 @@
1210
1442
  }
1211
1443
  renderSessionActiveState();
1212
1444
  syncUi();
1213
- loadMessagesForSession(sessionName).catch(function (e) {
1445
+ Promise.all([
1446
+ loadMessagesForSession(sessionName),
1447
+ loadSessionDetailForSession(sessionName)
1448
+ ]).catch(function (e) {
1214
1449
  alert(e.message);
1215
1450
  });
1216
1451
  }
@@ -1501,6 +1736,8 @@
1501
1736
 
1502
1737
  if (state.active && !state.sessions.some(function (session) { return session.name === state.active; })) {
1503
1738
  state.active = '';
1739
+ state.sessionDetail = null;
1740
+ state.sessionDetailError = '';
1504
1741
  }
1505
1742
  if (!state.active && state.sessions.length) {
1506
1743
  state.active = state.sessions[0].name;
@@ -1538,13 +1775,20 @@
1538
1775
  throw requestError;
1539
1776
  }
1540
1777
 
1541
- if (state.mode === 'terminal' && ensureTerminalReady() && !state.terminal.connected && !state.terminal.connecting && !isActiveSessionHistoryOnly()) {
1778
+ if (state.activeTab === 'terminal' && ensureTerminalReady() && !state.terminal.connected && !state.terminal.connecting && !isActiveSessionHistoryOnly()) {
1542
1779
  scheduleTerminalFit(false);
1543
1780
  connectTerminal();
1544
1781
  }
1545
1782
 
1546
1783
  if (opts.reloadMessages) {
1547
- await loadMessagesForSession(state.active, { silent: false });
1784
+ await Promise.all([
1785
+ loadMessagesForSession(state.active, { silent: false }),
1786
+ loadSessionDetailForSession(state.active)
1787
+ ]);
1788
+ } else if (state.active) {
1789
+ loadSessionDetailForSession(state.active).catch(function () {
1790
+ // 静默失败不打断主流程
1791
+ });
1548
1792
  }
1549
1793
  }
1550
1794
 
@@ -1624,6 +1868,43 @@
1624
1868
  await loadMessagesForSession(state.active, { silent: false });
1625
1869
  }
1626
1870
 
1871
+ async function loadSessionDetailForSession(sessionName) {
1872
+ const targetSession = typeof sessionName === 'string' ? sessionName.trim() : '';
1873
+ const requestId = state.sessionDetailRequestId + 1;
1874
+ state.sessionDetailRequestId = requestId;
1875
+
1876
+ if (!targetSession) {
1877
+ state.sessionDetail = null;
1878
+ state.sessionDetailError = '';
1879
+ state.loadingSessionDetail = false;
1880
+ renderSessionDetailPanels();
1881
+ return;
1882
+ }
1883
+
1884
+ state.loadingSessionDetail = true;
1885
+ state.sessionDetailError = '';
1886
+ renderSessionDetailPanels();
1887
+ try {
1888
+ const data = await api('/api/sessions/' + encodeURIComponent(targetSession) + '/detail');
1889
+ if (requestId !== state.sessionDetailRequestId) {
1890
+ return;
1891
+ }
1892
+ state.sessionDetail = data && data.detail ? data.detail : null;
1893
+ } catch (e) {
1894
+ if (requestId !== state.sessionDetailRequestId) {
1895
+ return;
1896
+ }
1897
+ state.sessionDetail = null;
1898
+ state.sessionDetailError = e && e.message ? e.message : '加载会话详情失败';
1899
+ } finally {
1900
+ if (requestId !== state.sessionDetailRequestId) {
1901
+ return;
1902
+ }
1903
+ state.loadingSessionDetail = false;
1904
+ renderSessionDetailPanels();
1905
+ }
1906
+ }
1907
+
1627
1908
  function bumpSessionMetaAfterSend(sessionName) {
1628
1909
  if (!sessionName) return;
1629
1910
  const session = state.sessions.find(function (item) {
@@ -1762,6 +2043,7 @@
1762
2043
  })
1763
2044
  });
1764
2045
  closeCreateModal();
2046
+ state.activeTab = 'activity';
1765
2047
  state.mode = 'agent';
1766
2048
  await loadSessions(data.name);
1767
2049
  if (isMobileLayout()) {
@@ -1781,7 +2063,7 @@
1781
2063
  if (!state.active) return;
1782
2064
  if (state.sending) return;
1783
2065
  if (state.loadingSessions || state.loadingMessages) return;
1784
- if (!isComposerMode()) return;
2066
+ if (state.activeTab !== 'activity') return;
1785
2067
  const mode = state.mode === 'agent' ? 'agent' : 'command';
1786
2068
  if (mode === 'agent' && !isActiveAgentEnabled()) {
1787
2069
  syncUi();
@@ -1860,22 +2142,14 @@
1860
2142
 
1861
2143
  // Enter / Ctrl+Enter: 发送
1862
2144
  event.preventDefault();
1863
- if (!state.active || state.sending || !isComposerMode()) {
2145
+ if (!state.active || state.sending || state.activeTab !== 'activity') {
1864
2146
  return;
1865
2147
  }
1866
2148
  composer.requestSubmit();
1867
2149
  });
1868
2150
 
1869
- if (modeToggle) {
1870
- modeToggle.addEventListener('click', function () {
1871
- closeMobileActionsMenu();
1872
- setModeMenu(!state.modeMenuOpen);
1873
- });
1874
- }
1875
-
1876
- if (modeCommandBtn) {
1877
- modeCommandBtn.addEventListener('click', function () {
1878
- closeModeMenu();
2151
+ if (activityCommandBtn) {
2152
+ activityCommandBtn.addEventListener('click', function () {
1879
2153
  state.mode = 'command';
1880
2154
  renderMessages(state.messages, { forceFullRender: true });
1881
2155
  syncUi();
@@ -1883,9 +2157,8 @@
1883
2157
  });
1884
2158
  }
1885
2159
 
1886
- if (modeAgentBtn) {
1887
- modeAgentBtn.addEventListener('click', function () {
1888
- closeModeMenu();
2160
+ if (activityAgentBtn) {
2161
+ activityAgentBtn.addEventListener('click', function () {
1889
2162
  state.mode = 'agent';
1890
2163
  renderMessages(state.messages, { forceFullRender: true });
1891
2164
  syncUi();
@@ -1893,19 +2166,28 @@
1893
2166
  });
1894
2167
  }
1895
2168
 
1896
- if (modeTerminalBtn) {
1897
- modeTerminalBtn.addEventListener('click', function () {
1898
- closeModeMenu();
1899
- state.mode = 'terminal';
1900
- renderMessages(state.messages, { forceFullRender: true });
1901
- syncUi();
1902
- if (ensureTerminalReady()) {
1903
- scheduleTerminalFit(false);
1904
- state.terminal.term.focus();
1905
- if (!state.terminal.connected && !state.terminal.connecting && !isActiveSessionHistoryOnly()) {
1906
- connectTerminal();
1907
- }
1908
- }
2169
+ if (viewActivityBtn) {
2170
+ viewActivityBtn.addEventListener('click', function () {
2171
+ setActiveTab('activity');
2172
+ commandInput.focus();
2173
+ });
2174
+ }
2175
+
2176
+ if (viewTerminalBtn) {
2177
+ viewTerminalBtn.addEventListener('click', function () {
2178
+ setActiveTab('terminal');
2179
+ });
2180
+ }
2181
+
2182
+ if (viewConfigBtn) {
2183
+ viewConfigBtn.addEventListener('click', function () {
2184
+ setActiveTab('config');
2185
+ });
2186
+ }
2187
+
2188
+ if (viewCheckBtn) {
2189
+ viewCheckBtn.addEventListener('click', function () {
2190
+ setActiveTab('check');
1909
2191
  });
1910
2192
  }
1911
2193
 
@@ -2006,21 +2288,18 @@
2006
2288
  if (event.key === 'Escape' && state.mobileActionsOpen) {
2007
2289
  closeMobileActionsMenu();
2008
2290
  }
2009
- if (event.key === 'Escape' && state.modeMenuOpen) {
2010
- closeModeMenu();
2011
- }
2012
2291
  });
2013
2292
 
2014
2293
  function onLayoutMediaChange() {
2015
2294
  setMobileSessionPanel(state.mobileSidebarOpen);
2016
2295
  setMobileActionsMenu(state.mobileActionsOpen);
2017
- if (state.mode === 'terminal' && state.terminal.terminalReady) {
2296
+ if (state.activeTab === 'terminal' && state.terminal.terminalReady) {
2018
2297
  scheduleTerminalFit(false);
2019
2298
  }
2020
2299
  }
2021
2300
 
2022
2301
  window.addEventListener('resize', function () {
2023
- if (state.mode === 'terminal' && state.terminal.terminalReady) {
2302
+ if (state.activeTab === 'terminal' && state.terminal.terminalReady) {
2024
2303
  scheduleTerminalFit(false);
2025
2304
  }
2026
2305
  });
@@ -2044,11 +2323,6 @@
2044
2323
  if (headerActions && headerActions.contains(target)) return;
2045
2324
  closeMobileActionsMenu();
2046
2325
  }
2047
- if (state.modeMenuOpen) {
2048
- if (modeToggle && modeToggle.contains(target)) return;
2049
- if (modeMenu && modeMenu.contains(target)) return;
2050
- closeModeMenu();
2051
- }
2052
2326
  });
2053
2327
 
2054
2328
  removeBtn.addEventListener('click', async function () {