codemini-cli 0.3.1 → 0.3.2

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.
@@ -102,7 +102,7 @@ const TUI_COPY = {
102
102
  startupHint: '使用 /help、/commands、/compact、/exit、!<shell>。Tab 可自动补全 slash 命令。',
103
103
  toolSummaryExpanded: '工具摘要:已展开',
104
104
  toolSummaryCollapsed: '工具摘要:已收起',
105
- toolChainCollapsed: (count) => `已折叠更早的 ${count} 个工具调用,按 Ctrl+T 展开全部`,
105
+ toolChainCollapsed: (count) => `已折叠更早的 ${count} 个工具调用`,
106
106
  toggleToolSummary: 'Ctrl+T 切换',
107
107
  scrollHint: '使用终端自己的滚动条或 scrollback',
108
108
  keyboardDebugEnabled: '键盘调试已开启',
@@ -247,7 +247,7 @@ const TUI_COPY = {
247
247
  startupHint: 'Use /help, /commands, /compact, /exit, !<shell>. Tab for slash autocomplete.',
248
248
  toolSummaryExpanded: 'Tool summary: expanded',
249
249
  toolSummaryCollapsed: 'Tool summary: collapsed',
250
- toolChainCollapsed: (count) => `${count} earlier tool calls hidden, press Ctrl+T to expand`,
250
+ toolChainCollapsed: (count) => `${count} earlier tool calls hidden`,
251
251
  toggleToolSummary: 'Ctrl+T to toggle',
252
252
  scrollHint: 'Scroll with your terminal scrollbar or scrollback',
253
253
  keyboardDebugEnabled: 'Keyboard debug enabled',
@@ -697,6 +697,61 @@ function parseRichTextSegments(line, baseColor) {
697
697
  });
698
698
  }
699
699
 
700
+ export function sanitizeRenderableText(value) {
701
+ const input = String(value ?? '');
702
+ if (!input) return '';
703
+
704
+ return input
705
+ .replace(/\r\n/g, '\n')
706
+ .replace(/\r/g, '\n')
707
+ .replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g, '')
708
+ .replace(/[\u001b\u009b][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g, '')
709
+ .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/g, '');
710
+ }
711
+
712
+ function textFromSessionContent(content) {
713
+ if (typeof content === 'string') return sanitizeRenderableText(content);
714
+ if (Array.isArray(content)) {
715
+ return sanitizeRenderableText(
716
+ content
717
+ .map((part) => {
718
+ if (typeof part === 'string') return part;
719
+ if (part?.type === 'text') return part.text || '';
720
+ return '';
721
+ })
722
+ .join('')
723
+ );
724
+ }
725
+ return sanitizeRenderableText(String(content || ''));
726
+ }
727
+
728
+ function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
729
+ const source = Array.isArray(sessionMessages) ? sessionMessages : [];
730
+ const out = [];
731
+
732
+ for (const message of source) {
733
+ if (!message || typeof message !== 'object') continue;
734
+ if (message.role === 'tool') continue;
735
+
736
+ const text = textFromSessionContent(message.content);
737
+ if (!text.trim() && message.role !== 'assistant') continue;
738
+
739
+ if (message.role === 'user') {
740
+ out.push({ id: nextId(), label: 'you', text, color: 'blueBright' });
741
+ continue;
742
+ }
743
+ if (message.role === 'assistant') {
744
+ out.push({ id: nextId(), label: 'coder', text, color: 'greenBright' });
745
+ continue;
746
+ }
747
+ if (message.role === 'system') {
748
+ out.push({ id: nextId(), label: 'system', text, color: 'yellowBright' });
749
+ }
750
+ }
751
+
752
+ return out;
753
+ }
754
+
700
755
  function safeJsonParse(raw) {
701
756
  try {
702
757
  return JSON.parse(String(raw || '{}'));
@@ -1018,6 +1073,21 @@ function ContextProgressMeter({ runtimeState, runtimeStatus, compact = false })
1018
1073
  function PlanStrip({ planState, copy }) {
1019
1074
  if (!planState || !planState.total) return null;
1020
1075
  const progress = `${planState.current}/${planState.total}`;
1076
+ const stripComplete = Boolean(planState.completed) && !planState.failed;
1077
+ const statusLabel = planState.failed ? copy.generic.attention : stripComplete ? copy.generic.taskCompleted : copy.generic.active;
1078
+ const statusColor = planState.failed ? 'redBright' : stripComplete ? 'cyanBright' : 'greenBright';
1079
+ const roleLabel =
1080
+ planState.resultStatus || stripComplete || planState.failed
1081
+ ? copy?.roleLabels?.system === 'SYSTEM'
1082
+ ? 'RESULT'
1083
+ : '结果'
1084
+ : String(planState.role || 'agent').toUpperCase();
1085
+ const titleLabel =
1086
+ planState.resultStatus || stripComplete || planState.failed
1087
+ ? copy?.roleLabels?.system === 'SYSTEM'
1088
+ ? 'Plan execution result'
1089
+ : '计划执行结果'
1090
+ : planState.title || 'running plan step';
1021
1091
  return h(
1022
1092
  Box,
1023
1093
  {
@@ -1036,11 +1106,18 @@ function PlanStrip({ planState, copy }) {
1036
1106
  null,
1037
1107
  h(Text, { color: 'black', backgroundColor: planState.failed ? 'red' : 'cyanBright' }, ` ${copy.generic.plan} ${progress} `),
1038
1108
  h(Text, { color: 'gray' }, ' '),
1039
- h(Text, { color: 'magentaBright' }, String(planState.role || 'agent').toUpperCase())
1109
+ h(Text, { color: 'magentaBright' }, roleLabel)
1040
1110
  ),
1041
- h(Text, { color: planState.failed ? 'redBright' : 'greenBright' }, planState.failed ? copy.generic.attention : copy.generic.active)
1111
+ h(Text, { color: statusColor }, statusLabel)
1042
1112
  ),
1043
- h(Text, { color: 'white' }, planState.title || 'running plan step'),
1113
+ h(Text, { color: 'white' }, titleLabel),
1114
+ planState.resultVerified
1115
+ ? h(
1116
+ Box,
1117
+ { marginTop: 1 },
1118
+ h(Text, { color: 'gray' }, trimText(planState.resultVerified, 120))
1119
+ )
1120
+ : null,
1044
1121
  planState.steps.length > 0
1045
1122
  ? h(
1046
1123
  Box,
@@ -1058,15 +1135,24 @@ function PlanStrip({ planState, copy }) {
1058
1135
  )
1059
1136
  )
1060
1137
  )
1138
+ : null,
1139
+ planState.resultNext
1140
+ ? h(
1141
+ Box,
1142
+ { marginTop: 1 },
1143
+ h(Text, { color: 'black', backgroundColor: 'yellowBright' }, ' NEXT '),
1144
+ h(Text, { color: 'gray' }, ` ${trimText(planState.resultNext, 108)}`)
1145
+ )
1061
1146
  : null
1062
1147
  );
1063
1148
  }
1064
1149
 
1065
- function Header({ sessionId, model, shellName, safeMode = true }) {
1150
+ function Header({ sessionId, model, sdkProvider, shellName, safeMode = true }) {
1066
1151
  const shortSession = String(sessionId || '').slice(-12) || '-';
1067
1152
  const modeValue = safeMode ? 'SAFE' : 'OPEN';
1068
1153
  const modeColor = safeMode ? 'greenBright' : 'redBright';
1069
1154
  const modeTextColor = safeMode ? 'black' : 'white';
1155
+ const sdkValue = String(sdkProvider || 'openai-compatible');
1070
1156
  return h(
1071
1157
  Box,
1072
1158
  { width: '100%', justifyContent: 'center', marginTop: 1, marginBottom: 2 },
@@ -1094,6 +1180,7 @@ function Header({ sessionId, model, shellName, safeMode = true }) {
1094
1180
  h(
1095
1181
  Box,
1096
1182
  { flexDirection: 'row', justifyContent: 'center' },
1183
+ h(StatusPill, { label: 'SDK', value: sdkValue, color: 'blueBright', textColor: 'white' }),
1097
1184
  h(StatusPill, { label: 'MODEL', value: model, color: 'cyanBright', textColor: 'black' }),
1098
1185
  h(StatusPill, { label: 'SHELL', value: shellName || 'powershell', color: 'yellowBright', textColor: 'black' }),
1099
1186
  h(StatusPill, { label: 'SESSION', value: shortSession, color: 'magentaBright', textColor: 'black' }),
@@ -1149,6 +1236,7 @@ export function parseAutoPlanSummaryMessage(text) {
1149
1236
  filePath: '',
1150
1237
  planSummary: '',
1151
1238
  finalSummary: '',
1239
+ approval: '',
1152
1240
  stepsTotal: '',
1153
1241
  completed: '',
1154
1242
  warnings: '',
@@ -1159,8 +1247,10 @@ export function parseAutoPlanSummaryMessage(text) {
1159
1247
 
1160
1248
  for (const line of lines.slice(1)) {
1161
1249
  if (line.startsWith('File: ')) parsed.filePath = line.slice('File: '.length).trim();
1250
+ else if (line.startsWith('Plan File: ')) parsed.filePath = line.slice('Plan File: '.length).trim();
1162
1251
  else if (line.startsWith('Plan Summary: ')) parsed.planSummary = line.slice('Plan Summary: '.length).trim();
1163
1252
  else if (line.startsWith('Final Summary: ')) parsed.finalSummary = line.slice('Final Summary: '.length).trim();
1253
+ else if (line.startsWith('Approval: ')) parsed.approval = line.slice('Approval: '.length).trim();
1164
1254
  else if (line.startsWith('Steps: ')) parsed.stepsTotal = line.slice('Steps: '.length).trim();
1165
1255
  else if (line.startsWith('Completed: ')) parsed.completed = line.slice('Completed: '.length).trim();
1166
1256
  else if (line.startsWith('Warnings: ')) parsed.warnings = line.slice('Warnings: '.length).trim();
@@ -1184,6 +1274,31 @@ export function parsePlanProgressLine(text) {
1184
1274
  };
1185
1275
  }
1186
1276
 
1277
+ export function parsePlanExecutionResult(text) {
1278
+ const raw = String(text || '').trim();
1279
+ if (!raw) return null;
1280
+ const statusMatch = raw.match(/(?:^|\n)\s*Status:\s*(done|partial|blocked)\s*$/im);
1281
+ const verifiedMatch = raw.match(/(?:^|\n)\s*Verified:\s*(.+)\s*$/im);
1282
+ const nextMatch = raw.match(/(?:^|\n)\s*Next:\s*(.+)\s*$/im);
1283
+ if (!statusMatch && !verifiedMatch && !nextMatch) return null;
1284
+ return {
1285
+ status: String(statusMatch?.[1] || '').trim().toLowerCase(),
1286
+ verified: String(verifiedMatch?.[1] || '').trim(),
1287
+ next: String(nextMatch?.[1] || '').trim()
1288
+ };
1289
+ }
1290
+
1291
+ export function stripPlanExecutionResult(text) {
1292
+ const raw = String(text || '');
1293
+ if (!parsePlanExecutionResult(raw)) return raw;
1294
+ return raw
1295
+ .replace(/(?:^|\n)\s*Status:\s*(done|partial|blocked)\s*$/im, '')
1296
+ .replace(/(?:^|\n)\s*Verified:\s*.+\s*$/im, '')
1297
+ .replace(/(?:^|\n)\s*Next:\s*.+\s*$/im, '')
1298
+ .replace(/\n{3,}/g, '\n\n')
1299
+ .trim();
1300
+ }
1301
+
1187
1302
  function getTailPreviewWindow(text, maxLines = 3) {
1188
1303
  const source = String(text || '');
1189
1304
  if (!source.trim()) return { lines: [], startLine: 1 };
@@ -1410,7 +1525,7 @@ function finishCodeGeneration(msg, now = Date.now()) {
1410
1525
 
1411
1526
  export function injectPlanStateMessage(messages, planState, activeUserMessageId, activeAssistantId) {
1412
1527
  const source = Array.isArray(messages) ? messages : [];
1413
- if (!planState || !planState.total) return source;
1528
+ if (!planState || !planState.total || planState.pendingApproval) return source;
1414
1529
  const synthetic = {
1415
1530
  id: `plan-state-${planState.current}-${planState.total}-${planState.role || 'agent'}`,
1416
1531
  label: 'system',
@@ -1456,6 +1571,7 @@ function PlanSummaryBubble({ msg, copy }) {
1456
1571
  ? {
1457
1572
  conclusion: 'Conclusion',
1458
1573
  plan: 'Plan',
1574
+ approval: 'Approval',
1459
1575
  warnings: 'Warnings',
1460
1576
  failed: 'Failed',
1461
1577
  file: 'File',
@@ -1467,6 +1583,7 @@ function PlanSummaryBubble({ msg, copy }) {
1467
1583
  : {
1468
1584
  conclusion: '结论',
1469
1585
  plan: '计划',
1586
+ approval: '审批',
1470
1587
  warnings: '警告',
1471
1588
  failed: '失败',
1472
1589
  file: '文件',
@@ -1520,11 +1637,19 @@ function PlanSummaryBubble({ msg, copy }) {
1520
1637
  summary.planSummary
1521
1638
  ? h(
1522
1639
  Box,
1523
- { marginBottom: metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
1640
+ { marginBottom: summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
1524
1641
  h(Text, { color: 'cyanBright' }, labels.plan),
1525
1642
  h(Text, { color: 'gray' }, summary.planSummary)
1526
1643
  )
1527
1644
  : null,
1645
+ summary.approval
1646
+ ? h(
1647
+ Box,
1648
+ { marginBottom: metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
1649
+ h(Text, { color: 'yellowBright' }, labels.approval),
1650
+ h(Text, { color: 'gray' }, summary.approval)
1651
+ )
1652
+ : null,
1528
1653
  metaItems.length > 0
1529
1654
  ? h(
1530
1655
  Box,
@@ -2497,7 +2622,7 @@ function makeIdleStatus(copy, snapshot, variant = 'ready') {
2497
2622
  );
2498
2623
  }
2499
2624
 
2500
- export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName = 'powershell', version = '', safeMode = true }) {
2625
+ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compatible', language = 'zh', shellName = 'powershell', version = '', safeMode = true }) {
2501
2626
  const copy = getCopy(language);
2502
2627
  const stdoutCols = Number(process.stdout?.columns || 120);
2503
2628
  const [inputValue, setInputValue] = useState('');
@@ -2513,6 +2638,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2513
2638
  const [cursorVisible, setCursorVisible] = useState(true);
2514
2639
  const [displaySessionId, setDisplaySessionId] = useState(sessionId);
2515
2640
  const [displayModel, setDisplayModel] = useState(model);
2641
+ const [displaySdkProvider, setDisplaySdkProvider] = useState(sdkProvider);
2516
2642
  const [pendingQueue, setPendingQueue] = useState([]);
2517
2643
  const [loaderTick, setLoaderTick] = useState(0);
2518
2644
  const [runtimeStatus, setRuntimeStatus] = useState(
@@ -2526,7 +2652,12 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2526
2652
  role: '',
2527
2653
  title: '',
2528
2654
  failed: false,
2529
- steps: []
2655
+ steps: [],
2656
+ pendingApproval: false,
2657
+ completed: false,
2658
+ resultStatus: '',
2659
+ resultVerified: '',
2660
+ resultNext: ''
2530
2661
  });
2531
2662
  const [debugKeys, setDebugKeys] = useState(false);
2532
2663
  const [lastKeyDebug, setLastKeyDebug] = useState('');
@@ -2605,6 +2736,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2605
2736
  if (!snapshot) return;
2606
2737
  setDisplaySessionId(snapshot.sessionId || sessionId);
2607
2738
  setDisplayModel(snapshot.model || model);
2739
+ setDisplaySdkProvider(snapshot.sdkProvider || sdkProvider);
2608
2740
  setRuntimeState(snapshot);
2609
2741
  setRuntimeStatus(makeIdleStatus(copy, snapshot, variant));
2610
2742
  };
@@ -2641,6 +2773,11 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2641
2773
  role,
2642
2774
  title,
2643
2775
  failed: false,
2776
+ pendingApproval: false,
2777
+ completed: false,
2778
+ resultStatus: '',
2779
+ resultVerified: '',
2780
+ resultNext: '',
2644
2781
  steps: [...withoutCurrent, { index: current, total, role, title, status: 'active' }].sort((a, b) => a.index - b.index)
2645
2782
  };
2646
2783
  });
@@ -2763,6 +2900,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2763
2900
 
2764
2901
  const appendResultMessage = (result) => {
2765
2902
  if (result.type === 'noop') return;
2903
+ if (Array.isArray(result.restoredMessages)) {
2904
+ setMessages(buildUiMessagesFromSessionHistory(result.restoredMessages, nextId));
2905
+ syncRuntimeVisualState('after');
2906
+ }
2766
2907
  if (
2767
2908
  result.type === 'system' &&
2768
2909
  typeof result.text === 'string' &&
@@ -2790,7 +2931,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2790
2931
  {
2791
2932
  id: nextId(),
2792
2933
  label: 'system',
2793
- text: copy.generic.keyboardDebugStatus(debugKeys),
2934
+ text: sanitizeRenderableText(copy.generic.keyboardDebugStatus(debugKeys)),
2794
2935
  color: 'yellowBright'
2795
2936
  }
2796
2937
  ]);
@@ -2798,13 +2939,14 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2798
2939
  }
2799
2940
  }
2800
2941
  if (result.type === 'assistant') {
2801
- if (!activeAssistantIdRef.current && result.text) {
2942
+ const { displayText } = applyPlanExecutionResult(result.text);
2943
+ if (!activeAssistantIdRef.current && displayText) {
2802
2944
  setMessages((prev) => [
2803
2945
  ...prev,
2804
2946
  {
2805
2947
  id: nextId(),
2806
2948
  label: 'coder',
2807
- text: result.text,
2949
+ text: sanitizeRenderableText(displayText),
2808
2950
  color: 'greenBright',
2809
2951
  autoSkillNames: activeAssistantAutoSkillNamesRef.current
2810
2952
  }
@@ -2813,12 +2955,27 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2813
2955
  return;
2814
2956
  }
2815
2957
  const parsedPlanSummary = result.type === 'system' ? parseAutoPlanSummaryMessage(result.text || '') : null;
2958
+ if (parsedPlanSummary?.approval === 'pending') {
2959
+ setPlanState({
2960
+ current: 0,
2961
+ total: 0,
2962
+ role: '',
2963
+ title: '',
2964
+ failed: false,
2965
+ steps: [],
2966
+ pendingApproval: true,
2967
+ completed: false,
2968
+ resultStatus: '',
2969
+ resultVerified: '',
2970
+ resultNext: ''
2971
+ });
2972
+ }
2816
2973
  setMessages((prev) => [
2817
2974
  ...prev,
2818
2975
  {
2819
2976
  id: nextId(),
2820
2977
  label: 'system',
2821
- text: result.text || '',
2978
+ text: sanitizeRenderableText(result.text || ''),
2822
2979
  color: 'yellowBright',
2823
2980
  ...(parsedPlanSummary ? { planSummary: parsedPlanSummary } : {})
2824
2981
  }
@@ -2865,6 +3022,31 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2865
3022
  });
2866
3023
  };
2867
3024
 
3025
+ const applyPlanExecutionResult = (rawText) => {
3026
+ const parsedExecution = parsePlanExecutionResult(rawText);
3027
+ if (!parsedExecution || !planTextBufferRef.current) return { parsedExecution: null, displayText: rawText };
3028
+ setPlanState((prev) => {
3029
+ if (!prev.total) return prev;
3030
+ return {
3031
+ ...prev,
3032
+ completed: parsedExecution.status === 'done',
3033
+ failed: parsedExecution.status === 'blocked',
3034
+ resultStatus: parsedExecution.status || prev.resultStatus,
3035
+ resultVerified: parsedExecution.verified || prev.resultVerified,
3036
+ resultNext: parsedExecution.next || prev.resultNext,
3037
+ steps: (prev.steps || []).map((step) =>
3038
+ step.index === prev.current && step.status === 'active'
3039
+ ? { ...step, status: parsedExecution.status === 'blocked' ? 'failed' : 'done' }
3040
+ : step
3041
+ )
3042
+ };
3043
+ });
3044
+ return {
3045
+ parsedExecution,
3046
+ displayText: stripPlanExecutionResult(rawText)
3047
+ };
3048
+ };
3049
+
2868
3050
  const ensureActiveAssistant = () => {
2869
3051
  if (activeAssistantIdRef.current) return activeAssistantIdRef.current;
2870
3052
  const aid = nextId();
@@ -2913,7 +3095,19 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2913
3095
  setBusy(true);
2914
3096
  setInputStage('sending');
2915
3097
  setRuntimeStatus(makeStatus(copy.runtime.sendingToGateway, copy.runtime.preparingRequest, 'yellowBright'));
2916
- setPlanState({ current: 0, total: 0, role: '', title: '', failed: false, steps: [] });
3098
+ setPlanState({
3099
+ current: 0,
3100
+ total: 0,
3101
+ role: '',
3102
+ title: '',
3103
+ failed: false,
3104
+ steps: [],
3105
+ pendingApproval: false,
3106
+ completed: false,
3107
+ resultStatus: '',
3108
+ resultVerified: '',
3109
+ resultNext: ''
3110
+ });
2917
3111
  planTextBufferRef.current = '';
2918
3112
  activeAssistantIdRef.current = null;
2919
3113
  activeAssistantAutoSkillNamesRef.current = [];
@@ -2996,16 +3190,19 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
2996
3190
  if (hadActiveAssistant) {
2997
3191
  streamedAssistantHandledRef.current = true;
2998
3192
  }
3193
+ const { displayText } = applyPlanExecutionResult(event.text);
2999
3194
  if (targetId && !hasPlannedTools) {
3000
3195
  setMessages((prev) =>
3001
3196
  prev.map((m) => {
3002
3197
  if (m.id !== targetId) return m;
3003
- const responseText = typeof event.text === 'string' ? event.text.trim() : '';
3004
- const shouldSynthesizeCompletion = !responseText && m.syntheticPrelude;
3198
+ const responseText = typeof displayText === 'string' ? displayText.trim() : '';
3199
+ const cleanedExistingText = stripPlanExecutionResult(String(m.text || '')).trim();
3200
+ const finalText = responseText || cleanedExistingText;
3201
+ const shouldSynthesizeCompletion = !finalText && m.syntheticPrelude;
3005
3202
  return {
3006
3203
  ...m,
3007
- ...(responseText
3008
- ? { text: event.text, syntheticPrelude: false }
3204
+ ...(finalText
3205
+ ? { text: finalText, syntheticPrelude: false }
3009
3206
  : shouldSynthesizeCompletion
3010
3207
  ? { text: buildSyntheticCompletionText(m, copy), syntheticPrelude: false }
3011
3208
  : {}),
@@ -3024,9 +3221,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
3024
3221
  activeAssistantIdRef.current = null;
3025
3222
  }
3026
3223
  if (!hadActiveAssistant && !hasPlannedTools && event.text) {
3224
+ const cleanedStandaloneText = stripPlanExecutionResult(String(displayText || event.text)).trim();
3027
3225
  setMessages((prev) => [
3028
3226
  ...prev,
3029
- { id: nextId(), label: 'coder', text: event.text, color: 'greenBright' }
3227
+ { id: nextId(), label: 'coder', text: cleanedStandaloneText, color: 'greenBright' }
3030
3228
  ]);
3031
3229
  }
3032
3230
  }
@@ -3265,7 +3463,8 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
3265
3463
  appendResultMessage(result);
3266
3464
  })
3267
3465
  .catch((err) => {
3268
- setRuntimeStatus(makeStatus(copy.runtime.requestFailed, err.message, 'redBright'));
3466
+ const message = sanitizeRenderableText(err?.message || String(err));
3467
+ setRuntimeStatus(makeStatus(copy.runtime.requestFailed, message, 'redBright'));
3269
3468
  setInputStage('idle');
3270
3469
  updateMessageMeta(activeUserMessageIdRef.current, {
3271
3470
  loading: false,
@@ -3281,7 +3480,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
3281
3480
  }));
3282
3481
  setMessages((prev) => [
3283
3482
  ...prev,
3284
- { id: nextId(), label: 'error', text: err.message, color: 'redBright' }
3483
+ { id: nextId(), label: 'error', text: message, color: 'redBright' }
3285
3484
  ]);
3286
3485
  })
3287
3486
  .finally(() => {
@@ -3343,6 +3542,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
3343
3542
  appendResultMessage(result);
3344
3543
  })
3345
3544
  .catch((err) => {
3545
+ const message = sanitizeRenderableText(err?.message || String(err));
3346
3546
  updateMessageMeta(userMessageId, {
3347
3547
  loading: false,
3348
3548
  phase: undefined,
@@ -3350,7 +3550,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
3350
3550
  });
3351
3551
  setMessages((prev) => [
3352
3552
  ...prev,
3353
- { id: nextId(), label: 'error', text: err.message, color: 'redBright' }
3553
+ { id: nextId(), label: 'error', text: message, color: 'redBright' }
3354
3554
  ]);
3355
3555
  });
3356
3556
  };
@@ -3677,7 +3877,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
3677
3877
  return h(
3678
3878
  Box,
3679
3879
  { flexDirection: 'column' },
3680
- h(Header, { sessionId: displaySessionId, model: displayModel, shellName, safeMode }),
3880
+ h(Header, { sessionId: displaySessionId, model: displayModel, sdkProvider: displaySdkProvider, shellName, safeMode }),
3681
3881
  h(MessageList, {
3682
3882
  messages: visibleMessages,
3683
3883
  loaderTick,