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.
- package/package.json +2 -1
- package/src/commands/chat.js +1 -0
- package/src/commands/run.js +6 -2
- package/src/core/agent-loop.js +1 -1
- package/src/core/chat-runtime.js +546 -136
- package/src/core/config-store.js +30 -0
- package/src/core/memory-policy.js +27 -0
- package/src/core/memory-prompt.js +45 -0
- package/src/core/memory-store.js +181 -0
- package/src/core/paths.js +8 -0
- package/src/core/provider/anthropic.js +388 -0
- package/src/core/provider/index.js +37 -0
- package/src/core/tools.js +173 -0
- package/src/tui/chat-app.js +224 -24
package/src/tui/chat-app.js
CHANGED
|
@@ -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}
|
|
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
|
|
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' },
|
|
1109
|
+
h(Text, { color: 'magentaBright' }, roleLabel)
|
|
1040
1110
|
),
|
|
1041
|
-
h(Text, { color:
|
|
1111
|
+
h(Text, { color: statusColor }, statusLabel)
|
|
1042
1112
|
),
|
|
1043
|
-
h(Text, { color: 'white' },
|
|
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
|
-
|
|
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:
|
|
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({
|
|
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
|
|
3004
|
-
const
|
|
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
|
-
...(
|
|
3008
|
-
? { text:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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,
|