codemini-cli 0.3.0 → 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/souls/anime.md +13 -2
- package/souls/caveman.md +12 -2
- package/souls/ceo.md +14 -3
- package/souls/default.md +10 -2
- package/souls/pirate.md +13 -3
- package/souls/playful.md +13 -3
- package/souls/professional.md +13 -3
- package/src/cli.js +2 -1
- package/src/commands/chat.js +3 -0
- package/src/commands/run.js +6 -2
- package/src/core/agent-loop.js +13 -9
- package/src/core/ast.js +2 -55
- package/src/core/bounded-cache.js +121 -0
- package/src/core/chat-runtime.js +552 -143
- package/src/core/config-store.js +30 -0
- package/src/core/constants.js +171 -0
- package/src/core/crypto-utils.js +18 -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/project-index.js +6 -34
- package/src/core/provider/anthropic.js +388 -0
- package/src/core/provider/index.js +37 -0
- package/src/core/soul.js +3 -2
- package/src/core/tools.js +175 -71
- package/src/tui/chat-app.js +291 -38
- package/src/tui/tool-activity/presenters/command.js +14 -1
- package/src/tui/tool-activity/presenters/files.js +23 -1
- package/src/tui/tool-activity/presenters/system.js +1 -1
- package/src/tui/tool-narration/presenters/glob.js +2 -2
- package/src/tui/tool-narration/presenters/grep.js +2 -2
- package/src/tui/tool-narration/presenters/list.js +2 -2
- package/src/tui/tool-narration/presenters/run.js +2 -2
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: '键盘调试已开启',
|
|
@@ -128,8 +128,12 @@ const TUI_COPY = {
|
|
|
128
128
|
doingWrite: '正在写入文件',
|
|
129
129
|
donePatch: '已应用补丁',
|
|
130
130
|
doingPatch: '正在应用补丁',
|
|
131
|
-
doneList: '
|
|
132
|
-
doingList: '
|
|
131
|
+
doneList: '已列出目录',
|
|
132
|
+
doingList: '正在列出目录',
|
|
133
|
+
doneGlob: '已按模式查找文件',
|
|
134
|
+
doingGlob: '正在按模式查找文件',
|
|
135
|
+
doneGrep: '已搜索关键词',
|
|
136
|
+
doingGrep: '正在搜索关键词',
|
|
133
137
|
doneCommand: '已执行命令',
|
|
134
138
|
doingCommand: '正在执行命令',
|
|
135
139
|
doneCreateTask: '已创建任务',
|
|
@@ -152,6 +156,14 @@ const TUI_COPY = {
|
|
|
152
156
|
doingDatabase: '正在启动数据库服务',
|
|
153
157
|
doneDocker: '已完成 Docker 命令',
|
|
154
158
|
doingDocker: '正在执行 Docker 命令',
|
|
159
|
+
doneListServices: '已列出服务',
|
|
160
|
+
doingListServices: '正在列出服务',
|
|
161
|
+
doneServiceStatus: '已查看服务状态',
|
|
162
|
+
doingServiceStatus: '正在查看服务状态',
|
|
163
|
+
doneServiceLogs: '已查看服务日志',
|
|
164
|
+
doingServiceLogs: '正在查看服务日志',
|
|
165
|
+
doneStopService: '已停止服务',
|
|
166
|
+
doingStopService: '正在停止服务',
|
|
155
167
|
doneCodeGeneration: '已生成代码',
|
|
156
168
|
doingCodeGeneration: '正在生成代码',
|
|
157
169
|
doneSkill: '已完成技能',
|
|
@@ -177,6 +189,7 @@ const TUI_COPY = {
|
|
|
177
189
|
runtime: {
|
|
178
190
|
sendingToGateway: '正在发送到网关',
|
|
179
191
|
preparingRequest: '准备本轮请求',
|
|
192
|
+
submittedWaiting: '已提交,等待开始处理',
|
|
180
193
|
modelThinking: '模型正在思考',
|
|
181
194
|
requestDelivered: '请求已送达,等待首个 token',
|
|
182
195
|
generatingReply: '正在生成回复',
|
|
@@ -234,7 +247,7 @@ const TUI_COPY = {
|
|
|
234
247
|
startupHint: 'Use /help, /commands, /compact, /exit, !<shell>. Tab for slash autocomplete.',
|
|
235
248
|
toolSummaryExpanded: 'Tool summary: expanded',
|
|
236
249
|
toolSummaryCollapsed: 'Tool summary: collapsed',
|
|
237
|
-
toolChainCollapsed: (count) => `${count} earlier tool calls hidden
|
|
250
|
+
toolChainCollapsed: (count) => `${count} earlier tool calls hidden`,
|
|
238
251
|
toggleToolSummary: 'Ctrl+T to toggle',
|
|
239
252
|
scrollHint: 'Scroll with your terminal scrollbar or scrollback',
|
|
240
253
|
keyboardDebugEnabled: 'Keyboard debug enabled',
|
|
@@ -262,6 +275,10 @@ const TUI_COPY = {
|
|
|
262
275
|
doingPatch: 'Applying patch',
|
|
263
276
|
doneList: 'Listed directory',
|
|
264
277
|
doingList: 'Listing directory',
|
|
278
|
+
doneGlob: 'Matched files by pattern',
|
|
279
|
+
doingGlob: 'Matching files by pattern',
|
|
280
|
+
doneGrep: 'Searched keywords',
|
|
281
|
+
doingGrep: 'Searching keywords',
|
|
265
282
|
doneCommand: 'Ran command',
|
|
266
283
|
doingCommand: 'Running command',
|
|
267
284
|
doneCreateTask: 'Created task',
|
|
@@ -284,6 +301,14 @@ const TUI_COPY = {
|
|
|
284
301
|
doingDatabase: 'Starting database service',
|
|
285
302
|
doneDocker: 'Docker command completed',
|
|
286
303
|
doingDocker: 'Running Docker command',
|
|
304
|
+
doneListServices: 'Listed services',
|
|
305
|
+
doingListServices: 'Listing services',
|
|
306
|
+
doneServiceStatus: 'Checked service status',
|
|
307
|
+
doingServiceStatus: 'Checking service status',
|
|
308
|
+
doneServiceLogs: 'Viewed service logs',
|
|
309
|
+
doingServiceLogs: 'Viewing service logs',
|
|
310
|
+
doneStopService: 'Stopped service',
|
|
311
|
+
doingStopService: 'Stopping service',
|
|
287
312
|
doneCodeGeneration: 'Code generated',
|
|
288
313
|
doingCodeGeneration: 'Generating code',
|
|
289
314
|
doneSkill: 'Completed skill',
|
|
@@ -309,6 +334,7 @@ const TUI_COPY = {
|
|
|
309
334
|
runtime: {
|
|
310
335
|
sendingToGateway: 'sending to gateway',
|
|
311
336
|
preparingRequest: 'preparing this turn',
|
|
337
|
+
submittedWaiting: 'submitted, waiting to start',
|
|
312
338
|
modelThinking: 'model is thinking',
|
|
313
339
|
requestDelivered: 'request sent, waiting for first token',
|
|
314
340
|
generatingReply: 'generating reply',
|
|
@@ -671,6 +697,61 @@ function parseRichTextSegments(line, baseColor) {
|
|
|
671
697
|
});
|
|
672
698
|
}
|
|
673
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
|
+
|
|
674
755
|
function safeJsonParse(raw) {
|
|
675
756
|
try {
|
|
676
757
|
return JSON.parse(String(raw || '{}'));
|
|
@@ -773,6 +854,27 @@ export function formatActivityDurationText(row, nowMs = Date.now()) {
|
|
|
773
854
|
return '';
|
|
774
855
|
}
|
|
775
856
|
|
|
857
|
+
export function getPendingUserMessageMeta(copy, { immediateLocal = false, inFlight = false } = {}) {
|
|
858
|
+
if (immediateLocal) {
|
|
859
|
+
return {
|
|
860
|
+
phase: 'sending',
|
|
861
|
+
liveStatus: copy.runtime.localCommandRunning
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (inFlight) {
|
|
866
|
+
return {
|
|
867
|
+
phase: 'queued',
|
|
868
|
+
liveStatus: copy.runtime.queuedWaiting
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
return {
|
|
873
|
+
phase: 'sending',
|
|
874
|
+
liveStatus: copy.runtime.submittedWaiting || copy.runtime.sendingToGateway
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
776
878
|
function getActivityDisplayParts(activity) {
|
|
777
879
|
if (isCodeGenerationActivityName(activity?.name)) {
|
|
778
880
|
return {
|
|
@@ -795,6 +897,12 @@ function getActivityDisplayParts(activity) {
|
|
|
795
897
|
};
|
|
796
898
|
}
|
|
797
899
|
if ((activity?.type || 'tool') === 'system_tool') {
|
|
900
|
+
if (parsed.base === 'project_index') {
|
|
901
|
+
return { primary: 'Project Index', secondary: '' };
|
|
902
|
+
}
|
|
903
|
+
if (parsed.base === 'file_index') {
|
|
904
|
+
return { primary: 'File Index', secondary: parsed.target ? `(${parsed.target})` : '' };
|
|
905
|
+
}
|
|
798
906
|
return {
|
|
799
907
|
primary: 'Index',
|
|
800
908
|
secondary: parsed.target ? `(${parsed.target})` : parsed.base ? `(${parsed.base})` : ''
|
|
@@ -806,14 +914,14 @@ function getActivityDisplayParts(activity) {
|
|
|
806
914
|
write: 'Write',
|
|
807
915
|
patch: 'Patch',
|
|
808
916
|
run: 'Run',
|
|
809
|
-
grep: '
|
|
917
|
+
grep: 'Search',
|
|
810
918
|
glob: 'Glob',
|
|
811
919
|
list: 'List',
|
|
812
920
|
start_service: 'Service',
|
|
813
|
-
list_services: '
|
|
814
|
-
get_service_status: '
|
|
815
|
-
get_service_logs: '
|
|
816
|
-
stop_service: '
|
|
921
|
+
list_services: 'Services',
|
|
922
|
+
get_service_status: 'Status',
|
|
923
|
+
get_service_logs: 'Logs',
|
|
924
|
+
stop_service: 'Stop',
|
|
817
925
|
list_files: 'Glob',
|
|
818
926
|
create_task: 'Task',
|
|
819
927
|
update_task: 'Task'
|
|
@@ -965,6 +1073,21 @@ function ContextProgressMeter({ runtimeState, runtimeStatus, compact = false })
|
|
|
965
1073
|
function PlanStrip({ planState, copy }) {
|
|
966
1074
|
if (!planState || !planState.total) return null;
|
|
967
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';
|
|
968
1091
|
return h(
|
|
969
1092
|
Box,
|
|
970
1093
|
{
|
|
@@ -983,11 +1106,18 @@ function PlanStrip({ planState, copy }) {
|
|
|
983
1106
|
null,
|
|
984
1107
|
h(Text, { color: 'black', backgroundColor: planState.failed ? 'red' : 'cyanBright' }, ` ${copy.generic.plan} ${progress} `),
|
|
985
1108
|
h(Text, { color: 'gray' }, ' '),
|
|
986
|
-
h(Text, { color: 'magentaBright' },
|
|
1109
|
+
h(Text, { color: 'magentaBright' }, roleLabel)
|
|
987
1110
|
),
|
|
988
|
-
h(Text, { color:
|
|
1111
|
+
h(Text, { color: statusColor }, statusLabel)
|
|
989
1112
|
),
|
|
990
|
-
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,
|
|
991
1121
|
planState.steps.length > 0
|
|
992
1122
|
? h(
|
|
993
1123
|
Box,
|
|
@@ -1005,15 +1135,24 @@ function PlanStrip({ planState, copy }) {
|
|
|
1005
1135
|
)
|
|
1006
1136
|
)
|
|
1007
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
|
+
)
|
|
1008
1146
|
: null
|
|
1009
1147
|
);
|
|
1010
1148
|
}
|
|
1011
1149
|
|
|
1012
|
-
function Header({ sessionId, model, shellName, safeMode = true }) {
|
|
1150
|
+
function Header({ sessionId, model, sdkProvider, shellName, safeMode = true }) {
|
|
1013
1151
|
const shortSession = String(sessionId || '').slice(-12) || '-';
|
|
1014
1152
|
const modeValue = safeMode ? 'SAFE' : 'OPEN';
|
|
1015
1153
|
const modeColor = safeMode ? 'greenBright' : 'redBright';
|
|
1016
1154
|
const modeTextColor = safeMode ? 'black' : 'white';
|
|
1155
|
+
const sdkValue = String(sdkProvider || 'openai-compatible');
|
|
1017
1156
|
return h(
|
|
1018
1157
|
Box,
|
|
1019
1158
|
{ width: '100%', justifyContent: 'center', marginTop: 1, marginBottom: 2 },
|
|
@@ -1041,6 +1180,7 @@ function Header({ sessionId, model, shellName, safeMode = true }) {
|
|
|
1041
1180
|
h(
|
|
1042
1181
|
Box,
|
|
1043
1182
|
{ flexDirection: 'row', justifyContent: 'center' },
|
|
1183
|
+
h(StatusPill, { label: 'SDK', value: sdkValue, color: 'blueBright', textColor: 'white' }),
|
|
1044
1184
|
h(StatusPill, { label: 'MODEL', value: model, color: 'cyanBright', textColor: 'black' }),
|
|
1045
1185
|
h(StatusPill, { label: 'SHELL', value: shellName || 'powershell', color: 'yellowBright', textColor: 'black' }),
|
|
1046
1186
|
h(StatusPill, { label: 'SESSION', value: shortSession, color: 'magentaBright', textColor: 'black' }),
|
|
@@ -1096,6 +1236,7 @@ export function parseAutoPlanSummaryMessage(text) {
|
|
|
1096
1236
|
filePath: '',
|
|
1097
1237
|
planSummary: '',
|
|
1098
1238
|
finalSummary: '',
|
|
1239
|
+
approval: '',
|
|
1099
1240
|
stepsTotal: '',
|
|
1100
1241
|
completed: '',
|
|
1101
1242
|
warnings: '',
|
|
@@ -1106,8 +1247,10 @@ export function parseAutoPlanSummaryMessage(text) {
|
|
|
1106
1247
|
|
|
1107
1248
|
for (const line of lines.slice(1)) {
|
|
1108
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();
|
|
1109
1251
|
else if (line.startsWith('Plan Summary: ')) parsed.planSummary = line.slice('Plan Summary: '.length).trim();
|
|
1110
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();
|
|
1111
1254
|
else if (line.startsWith('Steps: ')) parsed.stepsTotal = line.slice('Steps: '.length).trim();
|
|
1112
1255
|
else if (line.startsWith('Completed: ')) parsed.completed = line.slice('Completed: '.length).trim();
|
|
1113
1256
|
else if (line.startsWith('Warnings: ')) parsed.warnings = line.slice('Warnings: '.length).trim();
|
|
@@ -1131,6 +1274,31 @@ export function parsePlanProgressLine(text) {
|
|
|
1131
1274
|
};
|
|
1132
1275
|
}
|
|
1133
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
|
+
|
|
1134
1302
|
function getTailPreviewWindow(text, maxLines = 3) {
|
|
1135
1303
|
const source = String(text || '');
|
|
1136
1304
|
if (!source.trim()) return { lines: [], startLine: 1 };
|
|
@@ -1357,7 +1525,7 @@ function finishCodeGeneration(msg, now = Date.now()) {
|
|
|
1357
1525
|
|
|
1358
1526
|
export function injectPlanStateMessage(messages, planState, activeUserMessageId, activeAssistantId) {
|
|
1359
1527
|
const source = Array.isArray(messages) ? messages : [];
|
|
1360
|
-
if (!planState || !planState.total) return source;
|
|
1528
|
+
if (!planState || !planState.total || planState.pendingApproval) return source;
|
|
1361
1529
|
const synthetic = {
|
|
1362
1530
|
id: `plan-state-${planState.current}-${planState.total}-${planState.role || 'agent'}`,
|
|
1363
1531
|
label: 'system',
|
|
@@ -1403,6 +1571,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1403
1571
|
? {
|
|
1404
1572
|
conclusion: 'Conclusion',
|
|
1405
1573
|
plan: 'Plan',
|
|
1574
|
+
approval: 'Approval',
|
|
1406
1575
|
warnings: 'Warnings',
|
|
1407
1576
|
failed: 'Failed',
|
|
1408
1577
|
file: 'File',
|
|
@@ -1414,6 +1583,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1414
1583
|
: {
|
|
1415
1584
|
conclusion: '结论',
|
|
1416
1585
|
plan: '计划',
|
|
1586
|
+
approval: '审批',
|
|
1417
1587
|
warnings: '警告',
|
|
1418
1588
|
failed: '失败',
|
|
1419
1589
|
file: '文件',
|
|
@@ -1467,11 +1637,19 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1467
1637
|
summary.planSummary
|
|
1468
1638
|
? h(
|
|
1469
1639
|
Box,
|
|
1470
|
-
{ 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' },
|
|
1471
1641
|
h(Text, { color: 'cyanBright' }, labels.plan),
|
|
1472
1642
|
h(Text, { color: 'gray' }, summary.planSummary)
|
|
1473
1643
|
)
|
|
1474
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,
|
|
1475
1653
|
metaItems.length > 0
|
|
1476
1654
|
? h(
|
|
1477
1655
|
Box,
|
|
@@ -2444,7 +2622,7 @@ function makeIdleStatus(copy, snapshot, variant = 'ready') {
|
|
|
2444
2622
|
);
|
|
2445
2623
|
}
|
|
2446
2624
|
|
|
2447
|
-
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 }) {
|
|
2448
2626
|
const copy = getCopy(language);
|
|
2449
2627
|
const stdoutCols = Number(process.stdout?.columns || 120);
|
|
2450
2628
|
const [inputValue, setInputValue] = useState('');
|
|
@@ -2460,6 +2638,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2460
2638
|
const [cursorVisible, setCursorVisible] = useState(true);
|
|
2461
2639
|
const [displaySessionId, setDisplaySessionId] = useState(sessionId);
|
|
2462
2640
|
const [displayModel, setDisplayModel] = useState(model);
|
|
2641
|
+
const [displaySdkProvider, setDisplaySdkProvider] = useState(sdkProvider);
|
|
2463
2642
|
const [pendingQueue, setPendingQueue] = useState([]);
|
|
2464
2643
|
const [loaderTick, setLoaderTick] = useState(0);
|
|
2465
2644
|
const [runtimeStatus, setRuntimeStatus] = useState(
|
|
@@ -2473,7 +2652,12 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2473
2652
|
role: '',
|
|
2474
2653
|
title: '',
|
|
2475
2654
|
failed: false,
|
|
2476
|
-
steps: []
|
|
2655
|
+
steps: [],
|
|
2656
|
+
pendingApproval: false,
|
|
2657
|
+
completed: false,
|
|
2658
|
+
resultStatus: '',
|
|
2659
|
+
resultVerified: '',
|
|
2660
|
+
resultNext: ''
|
|
2477
2661
|
});
|
|
2478
2662
|
const [debugKeys, setDebugKeys] = useState(false);
|
|
2479
2663
|
const [lastKeyDebug, setLastKeyDebug] = useState('');
|
|
@@ -2552,6 +2736,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2552
2736
|
if (!snapshot) return;
|
|
2553
2737
|
setDisplaySessionId(snapshot.sessionId || sessionId);
|
|
2554
2738
|
setDisplayModel(snapshot.model || model);
|
|
2739
|
+
setDisplaySdkProvider(snapshot.sdkProvider || sdkProvider);
|
|
2555
2740
|
setRuntimeState(snapshot);
|
|
2556
2741
|
setRuntimeStatus(makeIdleStatus(copy, snapshot, variant));
|
|
2557
2742
|
};
|
|
@@ -2588,6 +2773,11 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2588
2773
|
role,
|
|
2589
2774
|
title,
|
|
2590
2775
|
failed: false,
|
|
2776
|
+
pendingApproval: false,
|
|
2777
|
+
completed: false,
|
|
2778
|
+
resultStatus: '',
|
|
2779
|
+
resultVerified: '',
|
|
2780
|
+
resultNext: '',
|
|
2591
2781
|
steps: [...withoutCurrent, { index: current, total, role, title, status: 'active' }].sort((a, b) => a.index - b.index)
|
|
2592
2782
|
};
|
|
2593
2783
|
});
|
|
@@ -2710,6 +2900,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2710
2900
|
|
|
2711
2901
|
const appendResultMessage = (result) => {
|
|
2712
2902
|
if (result.type === 'noop') return;
|
|
2903
|
+
if (Array.isArray(result.restoredMessages)) {
|
|
2904
|
+
setMessages(buildUiMessagesFromSessionHistory(result.restoredMessages, nextId));
|
|
2905
|
+
syncRuntimeVisualState('after');
|
|
2906
|
+
}
|
|
2713
2907
|
if (
|
|
2714
2908
|
result.type === 'system' &&
|
|
2715
2909
|
typeof result.text === 'string' &&
|
|
@@ -2737,7 +2931,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2737
2931
|
{
|
|
2738
2932
|
id: nextId(),
|
|
2739
2933
|
label: 'system',
|
|
2740
|
-
text: copy.generic.keyboardDebugStatus(debugKeys),
|
|
2934
|
+
text: sanitizeRenderableText(copy.generic.keyboardDebugStatus(debugKeys)),
|
|
2741
2935
|
color: 'yellowBright'
|
|
2742
2936
|
}
|
|
2743
2937
|
]);
|
|
@@ -2745,13 +2939,14 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2745
2939
|
}
|
|
2746
2940
|
}
|
|
2747
2941
|
if (result.type === 'assistant') {
|
|
2748
|
-
|
|
2942
|
+
const { displayText } = applyPlanExecutionResult(result.text);
|
|
2943
|
+
if (!activeAssistantIdRef.current && displayText) {
|
|
2749
2944
|
setMessages((prev) => [
|
|
2750
2945
|
...prev,
|
|
2751
2946
|
{
|
|
2752
2947
|
id: nextId(),
|
|
2753
2948
|
label: 'coder',
|
|
2754
|
-
text:
|
|
2949
|
+
text: sanitizeRenderableText(displayText),
|
|
2755
2950
|
color: 'greenBright',
|
|
2756
2951
|
autoSkillNames: activeAssistantAutoSkillNamesRef.current
|
|
2757
2952
|
}
|
|
@@ -2760,12 +2955,27 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2760
2955
|
return;
|
|
2761
2956
|
}
|
|
2762
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
|
+
}
|
|
2763
2973
|
setMessages((prev) => [
|
|
2764
2974
|
...prev,
|
|
2765
2975
|
{
|
|
2766
2976
|
id: nextId(),
|
|
2767
2977
|
label: 'system',
|
|
2768
|
-
text: result.text || '',
|
|
2978
|
+
text: sanitizeRenderableText(result.text || ''),
|
|
2769
2979
|
color: 'yellowBright',
|
|
2770
2980
|
...(parsedPlanSummary ? { planSummary: parsedPlanSummary } : {})
|
|
2771
2981
|
}
|
|
@@ -2812,6 +3022,31 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2812
3022
|
});
|
|
2813
3023
|
};
|
|
2814
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
|
+
|
|
2815
3050
|
const ensureActiveAssistant = () => {
|
|
2816
3051
|
if (activeAssistantIdRef.current) return activeAssistantIdRef.current;
|
|
2817
3052
|
const aid = nextId();
|
|
@@ -2860,7 +3095,19 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2860
3095
|
setBusy(true);
|
|
2861
3096
|
setInputStage('sending');
|
|
2862
3097
|
setRuntimeStatus(makeStatus(copy.runtime.sendingToGateway, copy.runtime.preparingRequest, 'yellowBright'));
|
|
2863
|
-
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
|
+
});
|
|
2864
3111
|
planTextBufferRef.current = '';
|
|
2865
3112
|
activeAssistantIdRef.current = null;
|
|
2866
3113
|
activeAssistantAutoSkillNamesRef.current = [];
|
|
@@ -2943,16 +3190,19 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2943
3190
|
if (hadActiveAssistant) {
|
|
2944
3191
|
streamedAssistantHandledRef.current = true;
|
|
2945
3192
|
}
|
|
3193
|
+
const { displayText } = applyPlanExecutionResult(event.text);
|
|
2946
3194
|
if (targetId && !hasPlannedTools) {
|
|
2947
3195
|
setMessages((prev) =>
|
|
2948
3196
|
prev.map((m) => {
|
|
2949
3197
|
if (m.id !== targetId) return m;
|
|
2950
|
-
const responseText = typeof
|
|
2951
|
-
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;
|
|
2952
3202
|
return {
|
|
2953
3203
|
...m,
|
|
2954
|
-
...(
|
|
2955
|
-
? { text:
|
|
3204
|
+
...(finalText
|
|
3205
|
+
? { text: finalText, syntheticPrelude: false }
|
|
2956
3206
|
: shouldSynthesizeCompletion
|
|
2957
3207
|
? { text: buildSyntheticCompletionText(m, copy), syntheticPrelude: false }
|
|
2958
3208
|
: {}),
|
|
@@ -2971,9 +3221,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
2971
3221
|
activeAssistantIdRef.current = null;
|
|
2972
3222
|
}
|
|
2973
3223
|
if (!hadActiveAssistant && !hasPlannedTools && event.text) {
|
|
3224
|
+
const cleanedStandaloneText = stripPlanExecutionResult(String(displayText || event.text)).trim();
|
|
2974
3225
|
setMessages((prev) => [
|
|
2975
3226
|
...prev,
|
|
2976
|
-
{ id: nextId(), label: 'coder', text:
|
|
3227
|
+
{ id: nextId(), label: 'coder', text: cleanedStandaloneText, color: 'greenBright' }
|
|
2977
3228
|
]);
|
|
2978
3229
|
}
|
|
2979
3230
|
}
|
|
@@ -3212,7 +3463,8 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3212
3463
|
appendResultMessage(result);
|
|
3213
3464
|
})
|
|
3214
3465
|
.catch((err) => {
|
|
3215
|
-
|
|
3466
|
+
const message = sanitizeRenderableText(err?.message || String(err));
|
|
3467
|
+
setRuntimeStatus(makeStatus(copy.runtime.requestFailed, message, 'redBright'));
|
|
3216
3468
|
setInputStage('idle');
|
|
3217
3469
|
updateMessageMeta(activeUserMessageIdRef.current, {
|
|
3218
3470
|
loading: false,
|
|
@@ -3228,7 +3480,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3228
3480
|
}));
|
|
3229
3481
|
setMessages((prev) => [
|
|
3230
3482
|
...prev,
|
|
3231
|
-
{ id: nextId(), label: 'error', text:
|
|
3483
|
+
{ id: nextId(), label: 'error', text: message, color: 'redBright' }
|
|
3232
3484
|
]);
|
|
3233
3485
|
})
|
|
3234
3486
|
.finally(() => {
|
|
@@ -3257,7 +3509,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3257
3509
|
updateMessageMeta(next.messageId, {
|
|
3258
3510
|
loading: true,
|
|
3259
3511
|
phase: 'sending',
|
|
3260
|
-
liveStatus: copy.runtime.sendingToGateway
|
|
3512
|
+
liveStatus: copy.runtime.submittedWaiting || copy.runtime.sendingToGateway
|
|
3261
3513
|
});
|
|
3262
3514
|
runSubmission(next.line, next.messageId);
|
|
3263
3515
|
}
|
|
@@ -3290,6 +3542,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3290
3542
|
appendResultMessage(result);
|
|
3291
3543
|
})
|
|
3292
3544
|
.catch((err) => {
|
|
3545
|
+
const message = sanitizeRenderableText(err?.message || String(err));
|
|
3293
3546
|
updateMessageMeta(userMessageId, {
|
|
3294
3547
|
loading: false,
|
|
3295
3548
|
phase: undefined,
|
|
@@ -3297,7 +3550,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3297
3550
|
});
|
|
3298
3551
|
setMessages((prev) => [
|
|
3299
3552
|
...prev,
|
|
3300
|
-
{ id: nextId(), label: 'error', text:
|
|
3553
|
+
{ id: nextId(), label: 'error', text: message, color: 'redBright' }
|
|
3301
3554
|
]);
|
|
3302
3555
|
});
|
|
3303
3556
|
};
|
|
@@ -3449,6 +3702,10 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3449
3702
|
const immediateLocal =
|
|
3450
3703
|
typeof runtime.isImmediateLocalInput === 'function' &&
|
|
3451
3704
|
runtime.isImmediateLocalInput(line);
|
|
3705
|
+
const pendingUserMeta = getPendingUserMessageMeta(copy, {
|
|
3706
|
+
immediateLocal,
|
|
3707
|
+
inFlight: inFlightRef.current
|
|
3708
|
+
});
|
|
3452
3709
|
setMessages((prev) => [
|
|
3453
3710
|
...prev,
|
|
3454
3711
|
{
|
|
@@ -3457,12 +3714,8 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3457
3714
|
text: line,
|
|
3458
3715
|
color: 'white',
|
|
3459
3716
|
loading: true,
|
|
3460
|
-
phase:
|
|
3461
|
-
liveStatus:
|
|
3462
|
-
? copy.runtime.localCommandRunning
|
|
3463
|
-
: inFlightRef.current
|
|
3464
|
-
? copy.runtime.queuedWaiting
|
|
3465
|
-
: copy.runtime.sendingToGateway
|
|
3717
|
+
phase: pendingUserMeta.phase,
|
|
3718
|
+
liveStatus: pendingUserMeta.liveStatus
|
|
3466
3719
|
}
|
|
3467
3720
|
]);
|
|
3468
3721
|
if (immediateLocal) {
|
|
@@ -3624,7 +3877,7 @@ export function ChatApp({ runtime, sessionId, model, language = 'zh', shellName
|
|
|
3624
3877
|
return h(
|
|
3625
3878
|
Box,
|
|
3626
3879
|
{ flexDirection: 'column' },
|
|
3627
|
-
h(Header, { sessionId: displaySessionId, model: displayModel, shellName, safeMode }),
|
|
3880
|
+
h(Header, { sessionId: displaySessionId, model: displayModel, sdkProvider: displaySdkProvider, shellName, safeMode }),
|
|
3628
3881
|
h(MessageList, {
|
|
3629
3882
|
messages: visibleMessages,
|
|
3630
3883
|
loaderTick,
|
|
@@ -21,7 +21,20 @@ export function describeCommandToolActivity(copy, parsed, { done = false, blocke
|
|
|
21
21
|
if (parsed.base === 'run') return phaseText(copy, blocked, done, trimText(target, 72) || parsed.base, copy.toolActivity.doingCommand, copy.toolActivity.doneCommand);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if (parsed.base === '
|
|
24
|
+
if (parsed.base === 'list_services') {
|
|
25
|
+
return phaseText(copy, blocked, done, trimText(parsed.target, 72) || parsed.base, copy.toolActivity.doingListServices, copy.toolActivity.doneListServices);
|
|
26
|
+
}
|
|
27
|
+
if (parsed.base === 'get_service_status') {
|
|
28
|
+
return phaseText(copy, blocked, done, trimText(parsed.target, 72) || parsed.base, copy.toolActivity.doingServiceStatus, copy.toolActivity.doneServiceStatus);
|
|
29
|
+
}
|
|
30
|
+
if (parsed.base === 'get_service_logs') {
|
|
31
|
+
return phaseText(copy, blocked, done, trimText(parsed.target, 72) || parsed.base, copy.toolActivity.doingServiceLogs, copy.toolActivity.doneServiceLogs);
|
|
32
|
+
}
|
|
33
|
+
if (parsed.base === 'stop_service') {
|
|
34
|
+
return phaseText(copy, blocked, done, trimText(parsed.target, 72) || parsed.base, copy.toolActivity.doingStopService, copy.toolActivity.doneStopService);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (parsed.base === 'start_service') {
|
|
25
38
|
return phaseText(copy, blocked, done, trimText(parsed.target, 72) || parsed.base, copy.toolActivity.doingGeneric, copy.toolActivity.doneGeneric);
|
|
26
39
|
}
|
|
27
40
|
|