codemini-cli 0.4.0 → 0.4.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/OPERATIONS.md +4 -2
- package/README.md +89 -11
- package/deployment.md +14 -7
- package/package.json +1 -2
- package/src/cli.js +1 -1
- package/src/commands/skill.js +145 -53
- package/src/core/agent-loop.js +18 -311
- package/src/core/chat-runtime.js +389 -53
- package/src/core/command-loader.js +12 -5
- package/src/core/config-store.js +2 -0
- package/src/core/context-compact.js +34 -9
- package/src/core/default-system-prompt.js +5 -5
- package/src/core/dream-audit.js +12 -0
- package/src/core/dream-consolidate.js +131 -59
- package/src/core/dream-evaluator.js +86 -0
- package/src/core/fff-adapter.js +1 -1
- package/src/core/memory-store.js +145 -10
- package/src/core/provider/openai-compatible.js +40 -5
- package/src/core/reflect-skill.js +178 -0
- package/src/core/shell-profile.js +8 -8
- package/src/core/tool-args.js +181 -0
- package/src/core/tool-result-store.js +206 -0
- package/src/core/tools.js +144 -190
- package/src/tui/chat-app.js +270 -28
- package/src/tui/tool-activity/presenters/misc.js +14 -0
- package/src/core/provider/anthropic.sdk-backup.js +0 -439
- package/src/core/provider/openai-compatible.sdk-backup.js +0 -412
package/src/tui/chat-app.js
CHANGED
|
@@ -142,6 +142,7 @@ const TUI_COPY = {
|
|
|
142
142
|
'🧩 用 /mode plan 切换到规划模式,让 AI 先出方案再动手。',
|
|
143
143
|
'🆕 /new 可以新建一个干净的会话,重新开始工作。',
|
|
144
144
|
'🧠 /memory 查看和管理 AI 的持久记忆,帮助它更好地理解你的偏好。',
|
|
145
|
+
'🌐 web_fetch 默认轻量读取网页;如需更好读取 JS 渲染页面,可运行 npm install -g playwright && playwright install chromium。',
|
|
145
146
|
'💤 CodeMini 会自动"做梦"休息,整理错误信息并自我优化,越用越聪明~'
|
|
146
147
|
],
|
|
147
148
|
toolSummaryExpanded: '工具摘要:已展开',
|
|
@@ -180,6 +181,10 @@ const TUI_COPY = {
|
|
|
180
181
|
doingGlob: '正在按模式查找文件',
|
|
181
182
|
doneGrep: '已搜索关键词',
|
|
182
183
|
doingGrep: '正在搜索关键词',
|
|
184
|
+
doneWebFetch: '已抓取网页',
|
|
185
|
+
doingWebFetch: '正在抓取网页',
|
|
186
|
+
doneWebSearch: '已搜索网页',
|
|
187
|
+
doingWebSearch: '正在搜索网页',
|
|
183
188
|
doneCommand: '已执行命令',
|
|
184
189
|
doingCommand: '正在执行命令',
|
|
185
190
|
doneUpdateTodos: '已更新待办',
|
|
@@ -310,6 +315,18 @@ const TUI_COPY = {
|
|
|
310
315
|
inputLocked: '计划审批进行中,请在审批框输入 /yes、/edit 或 /reject',
|
|
311
316
|
answerLabel: '审批输入',
|
|
312
317
|
answerPlaceholder: '/yes | /edit <反馈> | /reject'
|
|
318
|
+
},
|
|
319
|
+
reflectApproval: {
|
|
320
|
+
title: '审阅 Reflect 技能草稿?',
|
|
321
|
+
scopeLabel: '范围',
|
|
322
|
+
nameLabel: '名称',
|
|
323
|
+
targetLabel: '目标',
|
|
324
|
+
prompt: '输入 /yes 写入,输入 /edit <反馈> 修改,输入 /no 丢弃。',
|
|
325
|
+
invalidAnswer: '请输入 /yes、/edit <反馈> 或 /no。',
|
|
326
|
+
missingFeedback: '请在 /edit 后提供反馈内容。',
|
|
327
|
+
inputLocked: 'Reflect 审阅进行中,请在审阅框输入 /yes、/edit 或 /no',
|
|
328
|
+
answerLabel: '审阅输入',
|
|
329
|
+
answerPlaceholder: '/yes | /edit <反馈> | /no'
|
|
313
330
|
}
|
|
314
331
|
},
|
|
315
332
|
en: {
|
|
@@ -350,6 +367,7 @@ const TUI_COPY = {
|
|
|
350
367
|
'🧩 Use /mode plan to switch to planning mode — AI proposes a plan before coding.',
|
|
351
368
|
'🆕 /new starts a fresh session to begin a clean slate.',
|
|
352
369
|
'🧠 /memory lets you view and manage the AI\'s persistent memory for better personalization.',
|
|
370
|
+
'🌐 web_fetch uses a lightweight reader by default. For better JS-rendered pages: npm install -g playwright && playwright install chromium.',
|
|
353
371
|
'💤 CodeMini auto-"dreams" to rest, consolidate errors, and self-optimize — it gets smarter over time~'
|
|
354
372
|
],
|
|
355
373
|
toolSummaryExpanded: 'Tool summary: expanded',
|
|
@@ -388,6 +406,10 @@ const TUI_COPY = {
|
|
|
388
406
|
doingGlob: 'Matching files by pattern',
|
|
389
407
|
doneGrep: 'Searched keywords',
|
|
390
408
|
doingGrep: 'Searching keywords',
|
|
409
|
+
doneWebFetch: 'Fetched page',
|
|
410
|
+
doingWebFetch: 'Fetching page',
|
|
411
|
+
doneWebSearch: 'Searched web',
|
|
412
|
+
doingWebSearch: 'Searching web',
|
|
391
413
|
doneCommand: 'Ran command',
|
|
392
414
|
doingCommand: 'Running command',
|
|
393
415
|
doneUpdateTodos: 'Updated todos',
|
|
@@ -518,6 +540,18 @@ const TUI_COPY = {
|
|
|
518
540
|
inputLocked: 'Plan approval is active; type /yes, /edit <feedback>, or /reject',
|
|
519
541
|
answerLabel: 'Approval input',
|
|
520
542
|
answerPlaceholder: '/yes | /edit <feedback> | /reject'
|
|
543
|
+
},
|
|
544
|
+
reflectApproval: {
|
|
545
|
+
title: 'Review this reflected skill draft?',
|
|
546
|
+
scopeLabel: 'Scope',
|
|
547
|
+
nameLabel: 'Name',
|
|
548
|
+
targetLabel: 'Target',
|
|
549
|
+
prompt: 'Type /yes to write, /edit <feedback> to revise, or /no to discard.',
|
|
550
|
+
invalidAnswer: 'Please enter /yes, /edit <feedback>, or /no.',
|
|
551
|
+
missingFeedback: 'Please provide feedback after /edit.',
|
|
552
|
+
inputLocked: 'Reflect review is active; type /yes, /edit <feedback>, or /no',
|
|
553
|
+
answerLabel: 'Review input',
|
|
554
|
+
answerPlaceholder: '/yes | /edit <feedback> | /no'
|
|
521
555
|
}
|
|
522
556
|
}
|
|
523
557
|
};
|
|
@@ -531,13 +565,24 @@ function getCopy(language) {
|
|
|
531
565
|
}
|
|
532
566
|
|
|
533
567
|
function messageLabel(label, copy) {
|
|
534
|
-
return copy
|
|
568
|
+
return copy?.roleLabels?.[label] || String(label || '').toUpperCase();
|
|
535
569
|
}
|
|
536
570
|
|
|
537
571
|
function roleStyle(label) {
|
|
538
572
|
return ROLE_STYLES[label] || ROLE_STYLES.system;
|
|
539
573
|
}
|
|
540
574
|
|
|
575
|
+
const PLAN_AGENT_ROLES = new Set(['planner', 'coder', 'reviewer', 'tester', 'summarizer']);
|
|
576
|
+
|
|
577
|
+
function normalizePlanAgentRole(role) {
|
|
578
|
+
const roleKey = String(role || '').trim().toLowerCase();
|
|
579
|
+
return PLAN_AGENT_ROLES.has(roleKey) ? roleKey : 'coder';
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export function formatPlanAgentLabel(role, copy) {
|
|
583
|
+
return messageLabel(normalizePlanAgentRole(role), copy);
|
|
584
|
+
}
|
|
585
|
+
|
|
541
586
|
function StatusPill({ label, value, color = 'cyanBright', textColor = 'black' }) {
|
|
542
587
|
return h(
|
|
543
588
|
Box,
|
|
@@ -884,7 +929,7 @@ function textFromSessionContent(content) {
|
|
|
884
929
|
return sanitizeRenderableText(String(content || ''));
|
|
885
930
|
}
|
|
886
931
|
|
|
887
|
-
function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
932
|
+
export function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
888
933
|
const source = Array.isArray(sessionMessages) ? sessionMessages : [];
|
|
889
934
|
const out = [];
|
|
890
935
|
|
|
@@ -893,7 +938,7 @@ function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
|
893
938
|
if (message.role === 'tool') continue;
|
|
894
939
|
|
|
895
940
|
const text = textFromSessionContent(message.content);
|
|
896
|
-
if (!text.trim()
|
|
941
|
+
if (!text.trim()) continue;
|
|
897
942
|
|
|
898
943
|
if (message.role === 'user') {
|
|
899
944
|
out.push({ id: nextId(), label: 'you', text, color: 'blueBright' });
|
|
@@ -1101,6 +1146,25 @@ export function parsePlanApprovalAnswer(value) {
|
|
|
1101
1146
|
return { action: 'invalid', command: '' };
|
|
1102
1147
|
}
|
|
1103
1148
|
|
|
1149
|
+
export function parseReflectApprovalAnswer(value) {
|
|
1150
|
+
const raw = String(value || '').trim();
|
|
1151
|
+
if (!raw) return { action: 'empty', command: '' };
|
|
1152
|
+
const normalized = raw.toLowerCase();
|
|
1153
|
+
if (normalized === '/yes' || normalized === 'yes') {
|
|
1154
|
+
return { action: 'approve', command: '/yes' };
|
|
1155
|
+
}
|
|
1156
|
+
if (normalized === '/no' || normalized === 'no') {
|
|
1157
|
+
return { action: 'reject', command: '/no' };
|
|
1158
|
+
}
|
|
1159
|
+
const editMatch = raw.match(/^\/?edit(?:\s+(.+))?$/i);
|
|
1160
|
+
if (editMatch) {
|
|
1161
|
+
const feedback = String(editMatch[1] || '').trim();
|
|
1162
|
+
if (!feedback) return { action: 'missing_feedback', command: '' };
|
|
1163
|
+
return { action: 'edit', feedback, command: `/edit ${feedback}` };
|
|
1164
|
+
}
|
|
1165
|
+
return { action: 'invalid', command: '' };
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1104
1168
|
export function parsePendingPlanApprovalMessage(text = '') {
|
|
1105
1169
|
const raw = String(text || '');
|
|
1106
1170
|
if (!/^Plan approval is still pending\./i.test(raw.trim())) return null;
|
|
@@ -1114,6 +1178,21 @@ export function parsePendingPlanApprovalMessage(text = '') {
|
|
|
1114
1178
|
return out;
|
|
1115
1179
|
}
|
|
1116
1180
|
|
|
1181
|
+
export function parsePendingReflectSkillMessage(text = '') {
|
|
1182
|
+
const raw = String(text || '');
|
|
1183
|
+
if (!/\bReflect skill draft pending\./i.test(raw)) return null;
|
|
1184
|
+
const lines = raw.split(/\r?\n/);
|
|
1185
|
+
const out = { scope: '', name: '', confidence: '', targetPath: '' };
|
|
1186
|
+
for (const line of lines) {
|
|
1187
|
+
const trimmed = line.trim();
|
|
1188
|
+
if (trimmed.startsWith('Scope: ')) out.scope = trimmed.slice('Scope: '.length).trim();
|
|
1189
|
+
else if (/^\[\d+\]\s+/.test(trimmed) && !out.name) out.name = trimmed.replace(/^\[\d+\]\s+/, '').trim();
|
|
1190
|
+
else if (trimmed.startsWith('Confidence: ')) out.confidence = trimmed.slice('Confidence: '.length).trim();
|
|
1191
|
+
else if (trimmed.startsWith('Target: ')) out.targetPath = trimmed.slice('Target: '.length).trim();
|
|
1192
|
+
}
|
|
1193
|
+
return out;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1117
1196
|
export function formatDeleteApprovalLines(copy, request) {
|
|
1118
1197
|
const details = normalizeDeleteApprovalRequest(request);
|
|
1119
1198
|
if (!details) return [];
|
|
@@ -1133,6 +1212,17 @@ export function formatPlanApprovalLines(copy, request) {
|
|
|
1133
1212
|
return [String(copy?.planApproval?.title || '').trim()].filter(Boolean);
|
|
1134
1213
|
}
|
|
1135
1214
|
|
|
1215
|
+
export function formatReflectApprovalLines(copy, request) {
|
|
1216
|
+
if (!request) return [];
|
|
1217
|
+
const c = copy?.reflectApproval || {};
|
|
1218
|
+
const lines = [String(c.title || '').trim()];
|
|
1219
|
+
if (request.scope) lines.push(`${c.scopeLabel || 'Scope'}: ${request.scope}`);
|
|
1220
|
+
if (request.name) lines.push(`${c.nameLabel || 'Name'}: ${request.name}`);
|
|
1221
|
+
if (request.targetPath) lines.push(`${c.targetLabel || 'Target'}: ${request.targetPath}`);
|
|
1222
|
+
if (c.prompt) lines.push(c.prompt);
|
|
1223
|
+
return lines.filter(Boolean);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1136
1226
|
function getActivityDisplayParts(activity) {
|
|
1137
1227
|
if (isCodeGenerationActivityName(activity?.name)) {
|
|
1138
1228
|
return {
|
|
@@ -1175,6 +1265,8 @@ function getActivityDisplayParts(activity) {
|
|
|
1175
1265
|
patch: 'Patch',
|
|
1176
1266
|
run: 'Run',
|
|
1177
1267
|
grep: 'Search',
|
|
1268
|
+
web_fetch: 'Fetch',
|
|
1269
|
+
web_search: 'Web Search',
|
|
1178
1270
|
glob: 'Glob',
|
|
1179
1271
|
list: 'List',
|
|
1180
1272
|
list_background_tasks: 'Tasks',
|
|
@@ -1193,6 +1285,8 @@ function getActivityDisplayParts(activity) {
|
|
|
1193
1285
|
patch: '🩹',
|
|
1194
1286
|
run: '⚙️',
|
|
1195
1287
|
grep: '🔍',
|
|
1288
|
+
web_fetch: '🌐',
|
|
1289
|
+
web_search: '🌐',
|
|
1196
1290
|
glob: '🧭',
|
|
1197
1291
|
list: '📂',
|
|
1198
1292
|
list_background_tasks: '🗃️',
|
|
@@ -1407,10 +1501,9 @@ function PlanStrip({ planState, copy }) {
|
|
|
1407
1501
|
Box,
|
|
1408
1502
|
{ flexDirection: 'column' },
|
|
1409
1503
|
...planState.steps.map((step, idx) => {
|
|
1410
|
-
const
|
|
1411
|
-
const normalizedRole = ['planner', 'coder', 'reviewer', 'tester', 'summarizer'].includes(roleKey) ? roleKey : 'coder';
|
|
1504
|
+
const normalizedRole = normalizePlanAgentRole(step.role);
|
|
1412
1505
|
const stepTheme = roleStyle(normalizedRole);
|
|
1413
|
-
const roleTag =
|
|
1506
|
+
const roleTag = formatPlanAgentLabel(normalizedRole, copy);
|
|
1414
1507
|
const stepDone = step.status === 'done' || isDone;
|
|
1415
1508
|
const stepFailed = step.status === 'failed';
|
|
1416
1509
|
const marker = stepFailed ? '✗' : stepDone ? '✓' : '·';
|
|
@@ -1419,8 +1512,8 @@ function PlanStrip({ planState, copy }) {
|
|
|
1419
1512
|
Box,
|
|
1420
1513
|
{ key: `plan-step-${idx}` },
|
|
1421
1514
|
h(Text, { color: markerColor }, `${marker} `),
|
|
1422
|
-
|
|
1423
|
-
|
|
1515
|
+
h(Text, { color: stepTheme.badgeText, backgroundColor: stepTheme.badgeBg }, ` ${roleTag} `),
|
|
1516
|
+
h(Text, { color: 'gray' }, ' '),
|
|
1424
1517
|
h(Text, { color: stepDone && !stepFailed ? 'gray' : 'white' }, `${step.index}. ${step.title}`)
|
|
1425
1518
|
);
|
|
1426
1519
|
}
|
|
@@ -1507,6 +1600,30 @@ function renderTextLine(msg, line, idx, color) {
|
|
|
1507
1600
|
);
|
|
1508
1601
|
}
|
|
1509
1602
|
|
|
1603
|
+
function historyListLineColor(line, fallbackColor) {
|
|
1604
|
+
const raw = String(line || '');
|
|
1605
|
+
const trimmed = raw.trim();
|
|
1606
|
+
if (!trimmed) return fallbackColor;
|
|
1607
|
+
if (/^Current session\s+/i.test(trimmed) || /^Recent sessions$/i.test(trimmed) || /^\d+\.\s+\S+/.test(trimmed)) {
|
|
1608
|
+
return 'cyanBright';
|
|
1609
|
+
}
|
|
1610
|
+
if (/^Messages\s+\d+$/i.test(trimmed) || /^\d+\s+msgs?\s+\|\s+updated\b/i.test(trimmed) || /^Tip:/i.test(trimmed)) {
|
|
1611
|
+
return 'gray';
|
|
1612
|
+
}
|
|
1613
|
+
if (/^resume:\s+\/history resume\b/i.test(trimmed)) {
|
|
1614
|
+
return 'blueBright';
|
|
1615
|
+
}
|
|
1616
|
+
return 'white';
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
function isHistoryListMessage(msg) {
|
|
1620
|
+
const text = String(msg?.text || '');
|
|
1621
|
+
return msg?.label === 'system' &&
|
|
1622
|
+
/^Current session\s+/m.test(text) &&
|
|
1623
|
+
/^Recent sessions$/m.test(text) &&
|
|
1624
|
+
/resume:\s+\/history resume\b/m.test(text);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1510
1627
|
export function parseAutoPlanSummaryMessage(text) {
|
|
1511
1628
|
const raw = String(text || '').trim();
|
|
1512
1629
|
if (!/^Auto plan finished\b/i.test(raw)) return null;
|
|
@@ -1951,7 +2068,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1951
2068
|
Box,
|
|
1952
2069
|
{ marginBottom: planSteps.length > 0 || summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1953
2070
|
h(Text, { color: 'cyanBright' }, labels.plan),
|
|
1954
|
-
h(Text, { color: '
|
|
2071
|
+
h(Text, { color: 'white' }, summary.planSummary)
|
|
1955
2072
|
)
|
|
1956
2073
|
: null,
|
|
1957
2074
|
planSteps.length > 0
|
|
@@ -1960,12 +2077,20 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1960
2077
|
{ marginBottom: summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1961
2078
|
h(Text, { color: 'cyanBright' }, labels.steps),
|
|
1962
2079
|
...planSteps.flatMap((step, idx) => {
|
|
1963
|
-
const
|
|
2080
|
+
const roleKey = normalizePlanAgentRole(step?.role);
|
|
2081
|
+
const stepTheme = roleStyle(roleKey);
|
|
2082
|
+
const roleTag = formatPlanAgentLabel(roleKey, copy);
|
|
1964
2083
|
const titleText = String(step?.title || '-').trim() || '-';
|
|
1965
2084
|
const taskText = String(step?.task || '').trim();
|
|
1966
|
-
const titleRow = h(
|
|
2085
|
+
const titleRow = h(
|
|
2086
|
+
Text,
|
|
2087
|
+
{ key: `plan-step-title-${idx}`, color: 'white' },
|
|
2088
|
+
`${idx + 1}. `,
|
|
2089
|
+
h(Text, { color: stepTheme.badgeText, backgroundColor: stepTheme.badgeBg }, ` ${roleTag} `),
|
|
2090
|
+
` ${titleText}`
|
|
2091
|
+
);
|
|
1967
2092
|
if (!taskText) return [titleRow];
|
|
1968
|
-
const taskRow = h(Text, { key: `plan-step-task-${idx}`, color: '
|
|
2093
|
+
const taskRow = h(Text, { key: `plan-step-task-${idx}`, color: 'white' }, ` - task: ${taskText}`);
|
|
1969
2094
|
return [titleRow, taskRow];
|
|
1970
2095
|
})
|
|
1971
2096
|
)
|
|
@@ -1975,7 +2100,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1975
2100
|
Box,
|
|
1976
2101
|
{ marginBottom: metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1977
2102
|
h(Text, { color: 'yellowBright' }, labels.approval),
|
|
1978
|
-
h(Text, { color: '
|
|
2103
|
+
h(Text, { color: 'white' }, summary.approval)
|
|
1979
2104
|
)
|
|
1980
2105
|
: null,
|
|
1981
2106
|
metaItems.length > 0
|
|
@@ -1984,7 +2109,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1984
2109
|
{ marginBottom: summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0 },
|
|
1985
2110
|
...metaItems.flatMap((item, idx) => [
|
|
1986
2111
|
idx > 0 ? h(Text, { key: `sep-${idx}`, color: 'gray' }, ' ') : null,
|
|
1987
|
-
h(Text, { key: `meta-${idx}`, color: '
|
|
2112
|
+
h(Text, { key: `meta-${idx}`, color: 'white' }, item)
|
|
1988
2113
|
])
|
|
1989
2114
|
)
|
|
1990
2115
|
: null,
|
|
@@ -1993,7 +2118,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1993
2118
|
Box,
|
|
1994
2119
|
{ marginBottom: summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1995
2120
|
h(Text, { color: 'yellowBright' }, labels.warnings),
|
|
1996
|
-
h(Text, { color: '
|
|
2121
|
+
h(Text, { color: 'white' }, summary.warningSteps)
|
|
1997
2122
|
)
|
|
1998
2123
|
: null,
|
|
1999
2124
|
summary.failedSteps
|
|
@@ -2001,7 +2126,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
2001
2126
|
Box,
|
|
2002
2127
|
{ marginBottom: shortFile ? 1 : 0, flexDirection: 'column' },
|
|
2003
2128
|
h(Text, { color: 'redBright' }, labels.failed),
|
|
2004
|
-
h(Text, { color: '
|
|
2129
|
+
h(Text, { color: 'white' }, summary.failedSteps)
|
|
2005
2130
|
)
|
|
2006
2131
|
: null,
|
|
2007
2132
|
shortFile
|
|
@@ -2297,6 +2422,7 @@ export function collapseActivityChainRows(inputRows, showToolDetails, copy, maxV
|
|
|
2297
2422
|
|
|
2298
2423
|
export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy) {
|
|
2299
2424
|
const rows = [];
|
|
2425
|
+
const isHistoryList = isHistoryListMessage(msg);
|
|
2300
2426
|
const pushTextRows = (text) => {
|
|
2301
2427
|
const lines = String(text || '').split('\n');
|
|
2302
2428
|
let codeFence = false;
|
|
@@ -2317,8 +2443,8 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2317
2443
|
continue;
|
|
2318
2444
|
}
|
|
2319
2445
|
if (isMarkdownTableHeader(line, lines[lineIndex + 1])) {
|
|
2320
|
-
const tableLines = [line];
|
|
2321
|
-
lineIndex += 1; //
|
|
2446
|
+
const tableLines = [line, lines[lineIndex + 1]];
|
|
2447
|
+
lineIndex += 1; // separator included above
|
|
2322
2448
|
while (lineIndex + 1 < lines.length && splitMarkdownTableCells(lines[lineIndex + 1]).length > 1) {
|
|
2323
2449
|
tableLines.push(lines[lineIndex + 1]);
|
|
2324
2450
|
lineIndex += 1;
|
|
@@ -2327,7 +2453,8 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2327
2453
|
continue;
|
|
2328
2454
|
}
|
|
2329
2455
|
let color = msg.color || roleStyle(msg.label).text || 'white';
|
|
2330
|
-
if (
|
|
2456
|
+
if (isHistoryList) color = historyListLineColor(line, color);
|
|
2457
|
+
else if (line.startsWith('#')) color = 'cyanBright';
|
|
2331
2458
|
else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) color = 'magentaBright';
|
|
2332
2459
|
else if (trimmed.startsWith('>')) color = 'yellow';
|
|
2333
2460
|
else if (/^[|└├│]/.test(trimmed)) color = 'gray';
|
|
@@ -2401,7 +2528,17 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2401
2528
|
} else {
|
|
2402
2529
|
pushTextRows(msg?.text || '');
|
|
2403
2530
|
const toolCalls = Array.isArray(msg?.toolCalls) ? msg.toolCalls : [];
|
|
2404
|
-
|
|
2531
|
+
const pendingToolCalls = Array.isArray(msg?.pendingToolCalls) ? msg.pendingToolCalls : [];
|
|
2532
|
+
const visibleCalls = [
|
|
2533
|
+
...toolCalls,
|
|
2534
|
+
...pendingToolCalls.filter((pending) => {
|
|
2535
|
+
if (!pending) return false;
|
|
2536
|
+
if (pending.id && toolCalls.some((tool) => tool?.id && tool.id === pending.id)) return false;
|
|
2537
|
+
const pendingBase = parseToolDisplayName(pending.name).base;
|
|
2538
|
+
return !toolCalls.some((tool) => parseToolDisplayName(tool?.name).base === pendingBase && tool?.status === 'running');
|
|
2539
|
+
})
|
|
2540
|
+
];
|
|
2541
|
+
visibleCalls.forEach((tool, idx) => pushActivityRows(tool, idx, visibleCalls.length));
|
|
2405
2542
|
}
|
|
2406
2543
|
|
|
2407
2544
|
const codeGenerationRows = getCodeGenerationActivityRows(msg);
|
|
@@ -2493,36 +2630,36 @@ export function renderMessageRow(msg, row, idx, loaderTick) {
|
|
|
2493
2630
|
return h(
|
|
2494
2631
|
Box,
|
|
2495
2632
|
{ key: `row-table-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2496
|
-
h(Text, { color:
|
|
2633
|
+
h(Text, { color: 'white', bold: Boolean(row.isHeader) }, row.text)
|
|
2497
2634
|
);
|
|
2498
2635
|
}
|
|
2499
2636
|
if (row.kind === 'table-separator') {
|
|
2500
2637
|
return h(
|
|
2501
2638
|
Box,
|
|
2502
2639
|
{ key: `row-table-sep-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2503
|
-
h(Text, { color: '
|
|
2640
|
+
h(Text, { color: 'white' }, row.text)
|
|
2504
2641
|
);
|
|
2505
2642
|
}
|
|
2506
2643
|
if (row.kind === 'table-vertical') {
|
|
2507
2644
|
return h(
|
|
2508
2645
|
Box,
|
|
2509
2646
|
{ key: `row-table-v-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2510
|
-
h(Text, { color: '
|
|
2511
|
-
h(Text, { color: '
|
|
2647
|
+
h(Text, { color: 'white', bold: true }, `${row.label}:`),
|
|
2648
|
+
h(Text, { color: 'white' }, row.text ? ` ${row.text}` : '')
|
|
2512
2649
|
);
|
|
2513
2650
|
}
|
|
2514
2651
|
if (row.kind === 'table-vertical-continuation') {
|
|
2515
2652
|
return h(
|
|
2516
2653
|
Box,
|
|
2517
2654
|
{ key: `row-table-vc-${msg.id}-${idx}`, marginLeft: 3 },
|
|
2518
|
-
h(Text, { color: '
|
|
2655
|
+
h(Text, { color: 'white' }, row.text)
|
|
2519
2656
|
);
|
|
2520
2657
|
}
|
|
2521
2658
|
if (row.kind === 'table-vertical-separator') {
|
|
2522
2659
|
return h(
|
|
2523
2660
|
Box,
|
|
2524
2661
|
{ key: `row-table-vs-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2525
|
-
h(Text, { color: '
|
|
2662
|
+
h(Text, { color: 'white' }, row.text)
|
|
2526
2663
|
);
|
|
2527
2664
|
}
|
|
2528
2665
|
if (row.kind === 'activity-collapsed') {
|
|
@@ -3128,6 +3265,38 @@ function PlanApprovalPanel({ request, inputValue, errorText, copy, cursorVisible
|
|
|
3128
3265
|
);
|
|
3129
3266
|
}
|
|
3130
3267
|
|
|
3268
|
+
function ReflectApprovalPanel({ request, inputValue, errorText, copy, cursorVisible }) {
|
|
3269
|
+
if (!request) return null;
|
|
3270
|
+
const placeholder = String(copy.reflectApproval.answerPlaceholder || '').trim();
|
|
3271
|
+
const lines = formatReflectApprovalLines(copy, request);
|
|
3272
|
+
return h(
|
|
3273
|
+
Box,
|
|
3274
|
+
{
|
|
3275
|
+
marginTop: 1,
|
|
3276
|
+
flexDirection: 'column',
|
|
3277
|
+
borderStyle: 'round',
|
|
3278
|
+
borderColor: 'yellowBright',
|
|
3279
|
+
paddingX: 1,
|
|
3280
|
+
paddingY: 0
|
|
3281
|
+
},
|
|
3282
|
+
...lines.map((line, index) =>
|
|
3283
|
+
h(Text, { key: `reflect-approval-line-${index}`, color: 'yellowBright' }, line)
|
|
3284
|
+
),
|
|
3285
|
+
h(
|
|
3286
|
+
Box,
|
|
3287
|
+
{ marginTop: 1 },
|
|
3288
|
+
h(Text, { color: 'yellowBright' }, `${copy.reflectApproval.answerLabel}: `),
|
|
3289
|
+
h(ApprovalCursorLine, {
|
|
3290
|
+
inputValue,
|
|
3291
|
+
placeholder: placeholder || ' ',
|
|
3292
|
+
cursorVisible,
|
|
3293
|
+
accent: 'yellowBright'
|
|
3294
|
+
})
|
|
3295
|
+
),
|
|
3296
|
+
errorText ? h(Text, { color: 'yellowBright' }, errorText) : null
|
|
3297
|
+
);
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3131
3300
|
function SignatureBar({ version = '' }) {
|
|
3132
3301
|
return h(
|
|
3133
3302
|
Box,
|
|
@@ -3218,7 +3387,10 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3218
3387
|
const [pendingPlanApproval, setPendingPlanApproval] = useState(null);
|
|
3219
3388
|
const [planApprovalInput, setPlanApprovalInput] = useState('');
|
|
3220
3389
|
const [planApprovalError, setPlanApprovalError] = useState('');
|
|
3221
|
-
const
|
|
3390
|
+
const [pendingReflectApproval, setPendingReflectApproval] = useState(null);
|
|
3391
|
+
const [reflectApprovalInput, setReflectApprovalInput] = useState('');
|
|
3392
|
+
const [reflectApprovalError, setReflectApprovalError] = useState('');
|
|
3393
|
+
const approvalLockActive = Boolean(pendingDeleteApproval || pendingRunApproval || pendingPlanApproval || pendingReflectApproval);
|
|
3222
3394
|
const activeAssistantIdRef = useRef(null);
|
|
3223
3395
|
const activeAssistantAutoSkillNamesRef = useRef([]);
|
|
3224
3396
|
const streamedAssistantHandledRef = useRef(false);
|
|
@@ -3634,6 +3806,13 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3634
3806
|
setPlanState((prev) => ({ ...prev, pendingApproval: true }));
|
|
3635
3807
|
setPlanApprovalInput('');
|
|
3636
3808
|
setPlanApprovalError('');
|
|
3809
|
+
} else {
|
|
3810
|
+
const pendingReflectMeta = parsePendingReflectSkillMessage(result.text || '');
|
|
3811
|
+
if (pendingReflectMeta) {
|
|
3812
|
+
setPendingReflectApproval(pendingReflectMeta);
|
|
3813
|
+
setReflectApprovalInput('');
|
|
3814
|
+
setReflectApprovalError('');
|
|
3815
|
+
}
|
|
3637
3816
|
}
|
|
3638
3817
|
}
|
|
3639
3818
|
setMessages((prev) => [
|
|
@@ -3776,6 +3955,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3776
3955
|
setPendingPlanApproval(null);
|
|
3777
3956
|
setPlanApprovalInput('');
|
|
3778
3957
|
setPlanApprovalError('');
|
|
3958
|
+
setPendingReflectApproval(null);
|
|
3959
|
+
setReflectApprovalInput('');
|
|
3960
|
+
setReflectApprovalError('');
|
|
3779
3961
|
setPlanState({
|
|
3780
3962
|
current: 0,
|
|
3781
3963
|
total: 0,
|
|
@@ -4463,6 +4645,46 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4463
4645
|
return;
|
|
4464
4646
|
}
|
|
4465
4647
|
|
|
4648
|
+
if (pendingReflectApproval) {
|
|
4649
|
+
if (key.return) {
|
|
4650
|
+
const parsed = parseReflectApprovalAnswer(reflectApprovalInput);
|
|
4651
|
+
if (parsed.action === 'approve' || parsed.action === 'reject' || parsed.action === 'edit') {
|
|
4652
|
+
setPendingReflectApproval(null);
|
|
4653
|
+
setReflectApprovalInput('');
|
|
4654
|
+
setReflectApprovalError('');
|
|
4655
|
+
runSubmission(parsed.command);
|
|
4656
|
+
} else if (parsed.action === 'missing_feedback') {
|
|
4657
|
+
setReflectApprovalError(copy.reflectApproval.missingFeedback);
|
|
4658
|
+
} else {
|
|
4659
|
+
setReflectApprovalError(copy.reflectApproval.invalidAnswer);
|
|
4660
|
+
}
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4663
|
+
|
|
4664
|
+
if (isBackspaceKey(value, key) || isDeleteKey(value, key)) {
|
|
4665
|
+
setReflectApprovalInput((prev) => prev.slice(0, -1));
|
|
4666
|
+
setReflectApprovalError('');
|
|
4667
|
+
return;
|
|
4668
|
+
}
|
|
4669
|
+
|
|
4670
|
+
if (isPrintableInput(value, key)) {
|
|
4671
|
+
setReflectApprovalInput((prev) => `${prev}${value}`);
|
|
4672
|
+
setReflectApprovalError('');
|
|
4673
|
+
return;
|
|
4674
|
+
}
|
|
4675
|
+
|
|
4676
|
+
if (key.ctrl && value === 'c') {
|
|
4677
|
+
if (busy && typeof runtime.abort === 'function') {
|
|
4678
|
+
runtime.abort();
|
|
4679
|
+
return;
|
|
4680
|
+
}
|
|
4681
|
+
exit();
|
|
4682
|
+
return;
|
|
4683
|
+
}
|
|
4684
|
+
|
|
4685
|
+
return;
|
|
4686
|
+
}
|
|
4687
|
+
|
|
4466
4688
|
if (key.upArrow) {
|
|
4467
4689
|
if (suggestionNav && commandSuggestions.length > 0) {
|
|
4468
4690
|
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'up'));
|
|
@@ -4732,6 +4954,17 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4732
4954
|
}
|
|
4733
4955
|
}, [runtimeState?.pendingPlanApproval, busy]);
|
|
4734
4956
|
|
|
4957
|
+
useEffect(() => {
|
|
4958
|
+
const pending = Boolean(runtimeState?.pendingReflectSkill);
|
|
4959
|
+
if (!pending) {
|
|
4960
|
+
setPendingReflectApproval(null);
|
|
4961
|
+
return;
|
|
4962
|
+
}
|
|
4963
|
+
if (!busy) {
|
|
4964
|
+
setPendingReflectApproval((prev) => prev || { scope: '', name: '', targetPath: '' });
|
|
4965
|
+
}
|
|
4966
|
+
}, [runtimeState?.pendingReflectSkill, busy]);
|
|
4967
|
+
|
|
4735
4968
|
useEffect(() => {
|
|
4736
4969
|
if (commandSuggestions.length === 0) {
|
|
4737
4970
|
setSuggestionNav(false);
|
|
@@ -4778,7 +5011,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4778
5011
|
? { text: copy.runApproval.inputLocked }
|
|
4779
5012
|
: pendingPlanApproval
|
|
4780
5013
|
? { text: copy.planApproval.inputLocked }
|
|
4781
|
-
:
|
|
5014
|
+
: pendingReflectApproval
|
|
5015
|
+
? { text: copy.reflectApproval.inputLocked }
|
|
5016
|
+
: null;
|
|
4782
5017
|
|
|
4783
5018
|
return h(
|
|
4784
5019
|
Box,
|
|
@@ -4828,6 +5063,13 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4828
5063
|
copy,
|
|
4829
5064
|
cursorVisible
|
|
4830
5065
|
}),
|
|
5066
|
+
h(ReflectApprovalPanel, {
|
|
5067
|
+
request: pendingReflectApproval,
|
|
5068
|
+
inputValue: reflectApprovalInput,
|
|
5069
|
+
errorText: reflectApprovalError,
|
|
5070
|
+
copy,
|
|
5071
|
+
cursorVisible
|
|
5072
|
+
}),
|
|
4831
5073
|
debugKeys
|
|
4832
5074
|
? h(
|
|
4833
5075
|
Box,
|
|
@@ -12,5 +12,19 @@ export function describeMiscToolActivity(copy, parsed, rawName, { done = false,
|
|
|
12
12
|
if (parsed.base === 'update_todos') {
|
|
13
13
|
return blocked ? makeBlocked(copy, 'update_todos') : done ? copy.toolActivity.doneUpdateTodos : copy.toolActivity.doingUpdateTodos;
|
|
14
14
|
}
|
|
15
|
+
if (parsed.base === 'web_fetch') {
|
|
16
|
+
const target = parsed.target || parsed.raw;
|
|
17
|
+
const label = done
|
|
18
|
+
? (copy.toolActivity.doneWebFetch || copy.toolActivity.doneGeneric)
|
|
19
|
+
: (copy.toolActivity.doingWebFetch || copy.toolActivity.doingGeneric);
|
|
20
|
+
return blocked ? makeBlocked(copy, target) : `${label}: ${target}`;
|
|
21
|
+
}
|
|
22
|
+
if (parsed.base === 'web_search') {
|
|
23
|
+
const target = parsed.target || parsed.raw;
|
|
24
|
+
const label = done
|
|
25
|
+
? (copy.toolActivity.doneWebSearch || copy.toolActivity.doneGeneric)
|
|
26
|
+
: (copy.toolActivity.doingWebSearch || copy.toolActivity.doingGeneric);
|
|
27
|
+
return blocked ? makeBlocked(copy, target) : `${label}: ${target}`;
|
|
28
|
+
}
|
|
15
29
|
return blocked ? `${copy.toolActivity.blocked}: ${parsed.raw}` : done ? `${copy.toolActivity.doneGeneric}: ${parsed.raw}` : `${copy.toolActivity.doingGeneric}: ${parsed.raw}`;
|
|
16
30
|
}
|