codemini-cli 0.4.1 → 0.4.3
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 +87 -7
- package/deployment.md +14 -7
- package/package.json +1 -3
- package/skills/grill-me/SKILL.md +30 -0
- package/skills/project-requirements/SKILL.md +245 -0
- package/skills/superpowers-lite/SKILL.md +5 -1
- package/src/cli.js +1 -1
- package/src/commands/run.js +5 -4
- package/src/commands/skill.js +145 -53
- package/src/core/agent-loop.js +9 -214
- package/src/core/chat-runtime.js +520 -78
- package/src/core/command-loader.js +12 -5
- package/src/core/config-store.js +6 -3
- package/src/core/context-compact.js +2 -1
- 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/anthropic.js +2 -2
- package/src/core/provider/openai-compatible.js +2 -2
- package/src/core/reflect-skill.js +178 -0
- package/src/core/shell.js +1 -1
- package/src/core/tool-result-store.js +206 -0
- package/src/core/tools.js +242 -69
- package/src/tui/chat-app.js +298 -48
- package/src/tui/tool-activity/presenters/system.js +6 -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
|
@@ -45,6 +45,22 @@ const ROLE_STYLES = {
|
|
|
45
45
|
badgeText: 'black',
|
|
46
46
|
chrome: 'gray'
|
|
47
47
|
},
|
|
48
|
+
general: {
|
|
49
|
+
accent: 'greenBright',
|
|
50
|
+
border: 'green',
|
|
51
|
+
text: 'greenBright',
|
|
52
|
+
badgeBg: 'green',
|
|
53
|
+
badgeText: 'black',
|
|
54
|
+
chrome: 'gray'
|
|
55
|
+
},
|
|
56
|
+
advisor: {
|
|
57
|
+
accent: 'blueBright',
|
|
58
|
+
border: 'blue',
|
|
59
|
+
text: 'blueBright',
|
|
60
|
+
badgeBg: 'blue',
|
|
61
|
+
badgeText: 'white',
|
|
62
|
+
chrome: 'gray'
|
|
63
|
+
},
|
|
48
64
|
planner: {
|
|
49
65
|
accent: 'magentaBright',
|
|
50
66
|
border: 'magenta',
|
|
@@ -105,7 +121,7 @@ const ROLE_STYLES = {
|
|
|
105
121
|
|
|
106
122
|
const TUI_COPY = {
|
|
107
123
|
zh: {
|
|
108
|
-
roleLabels: { you: '👤 你', coder: '💻 CODER', planner: '📋 PLANNER', reviewer: '🔍 REVIEWER', tester: '🧪 TESTER', summarizer: '📝 SUMMARIZER', system: '⚙️ 系统', error: '❌ 错误', pending: '⏳ 等待中' },
|
|
124
|
+
roleLabels: { you: '👤 你', general: 'GENERAL', advisor: '💡 ADVISOR', coder: '💻 CODER', planner: '📋 PLANNER', reviewer: '🔍 REVIEWER', tester: '🧪 TESTER', summarizer: '📝 SUMMARIZER', system: '⚙️ 系统', error: '❌ 错误', pending: '⏳ 等待中' },
|
|
109
125
|
generic: {
|
|
110
126
|
waitingForInput: '等待输入',
|
|
111
127
|
ready: '就绪',
|
|
@@ -142,6 +158,7 @@ const TUI_COPY = {
|
|
|
142
158
|
'🧩 用 /mode plan 切换到规划模式,让 AI 先出方案再动手。',
|
|
143
159
|
'🆕 /new 可以新建一个干净的会话,重新开始工作。',
|
|
144
160
|
'🧠 /memory 查看和管理 AI 的持久记忆,帮助它更好地理解你的偏好。',
|
|
161
|
+
'🌐 web_fetch 默认轻量读取网页;如需更好读取 JS 渲染页面,可运行 npm install -g playwright && playwright install chromium。',
|
|
145
162
|
'💤 CodeMini 会自动"做梦"休息,整理错误信息并自我优化,越用越聪明~'
|
|
146
163
|
],
|
|
147
164
|
toolSummaryExpanded: '工具摘要:已展开',
|
|
@@ -218,6 +235,8 @@ const TUI_COPY = {
|
|
|
218
235
|
doingProjectIndex: '正在初始化项目索引',
|
|
219
236
|
doneFileIndex: '已刷新文件索引',
|
|
220
237
|
doingFileIndex: '正在刷新文件索引',
|
|
238
|
+
donePromptBudget: '已测量 Prompt 预算',
|
|
239
|
+
doingPromptBudget: '正在测量 Prompt 预算',
|
|
221
240
|
toolFailed: (name) => `工具执行失败: ${name}`,
|
|
222
241
|
waitingModelContinue: (detail) => `${detail},等待模型继续`,
|
|
223
242
|
waitingModelAdjust: (detail) => `${detail},等待模型调整`
|
|
@@ -314,10 +333,22 @@ const TUI_COPY = {
|
|
|
314
333
|
inputLocked: '计划审批进行中,请在审批框输入 /yes、/edit 或 /reject',
|
|
315
334
|
answerLabel: '审批输入',
|
|
316
335
|
answerPlaceholder: '/yes | /edit <反馈> | /reject'
|
|
336
|
+
},
|
|
337
|
+
reflectApproval: {
|
|
338
|
+
title: '审阅 Reflect 技能草稿?',
|
|
339
|
+
scopeLabel: '范围',
|
|
340
|
+
nameLabel: '名称',
|
|
341
|
+
targetLabel: '目标',
|
|
342
|
+
prompt: '输入 /yes 写入,输入 /edit <反馈> 修改,输入 /no 丢弃。',
|
|
343
|
+
invalidAnswer: '请输入 /yes、/edit <反馈> 或 /no。',
|
|
344
|
+
missingFeedback: '请在 /edit 后提供反馈内容。',
|
|
345
|
+
inputLocked: 'Reflect 审阅进行中,请在审阅框输入 /yes、/edit 或 /no',
|
|
346
|
+
answerLabel: '审阅输入',
|
|
347
|
+
answerPlaceholder: '/yes | /edit <反馈> | /no'
|
|
317
348
|
}
|
|
318
349
|
},
|
|
319
350
|
en: {
|
|
320
|
-
roleLabels: { you: 'YOU', coder: 'CODER', planner: 'PLANNER', reviewer: 'REVIEWER', tester: 'TESTER', summarizer: 'SUMMARIZER', system: 'SYSTEM', error: 'ERROR', pending: 'PENDING' },
|
|
351
|
+
roleLabels: { you: 'YOU', general: 'GENERAL', advisor: 'ADVISOR', coder: 'CODER', planner: 'PLANNER', reviewer: 'REVIEWER', tester: 'TESTER', summarizer: 'SUMMARIZER', system: 'SYSTEM', error: 'ERROR', pending: 'PENDING' },
|
|
321
352
|
generic: {
|
|
322
353
|
waitingForInput: 'waiting for input',
|
|
323
354
|
ready: 'ready',
|
|
@@ -354,6 +385,7 @@ const TUI_COPY = {
|
|
|
354
385
|
'🧩 Use /mode plan to switch to planning mode — AI proposes a plan before coding.',
|
|
355
386
|
'🆕 /new starts a fresh session to begin a clean slate.',
|
|
356
387
|
'🧠 /memory lets you view and manage the AI\'s persistent memory for better personalization.',
|
|
388
|
+
'🌐 web_fetch uses a lightweight reader by default. For better JS-rendered pages: npm install -g playwright && playwright install chromium.',
|
|
357
389
|
'💤 CodeMini auto-"dreams" to rest, consolidate errors, and self-optimize — it gets smarter over time~'
|
|
358
390
|
],
|
|
359
391
|
toolSummaryExpanded: 'Tool summary: expanded',
|
|
@@ -430,6 +462,8 @@ const TUI_COPY = {
|
|
|
430
462
|
doingProjectIndex: 'Initializing project index',
|
|
431
463
|
doneFileIndex: 'File index refreshed',
|
|
432
464
|
doingFileIndex: 'Refreshing file index',
|
|
465
|
+
donePromptBudget: 'Prompt budget measured',
|
|
466
|
+
doingPromptBudget: 'Measuring prompt budget',
|
|
433
467
|
toolFailed: (name) => `Tool failed: ${name}`,
|
|
434
468
|
waitingModelContinue: (detail) => `${detail}, waiting for model to continue`,
|
|
435
469
|
waitingModelAdjust: (detail) => `${detail}, waiting for model to adjust`
|
|
@@ -526,6 +560,18 @@ const TUI_COPY = {
|
|
|
526
560
|
inputLocked: 'Plan approval is active; type /yes, /edit <feedback>, or /reject',
|
|
527
561
|
answerLabel: 'Approval input',
|
|
528
562
|
answerPlaceholder: '/yes | /edit <feedback> | /reject'
|
|
563
|
+
},
|
|
564
|
+
reflectApproval: {
|
|
565
|
+
title: 'Review this reflected skill draft?',
|
|
566
|
+
scopeLabel: 'Scope',
|
|
567
|
+
nameLabel: 'Name',
|
|
568
|
+
targetLabel: 'Target',
|
|
569
|
+
prompt: 'Type /yes to write, /edit <feedback> to revise, or /no to discard.',
|
|
570
|
+
invalidAnswer: 'Please enter /yes, /edit <feedback>, or /no.',
|
|
571
|
+
missingFeedback: 'Please provide feedback after /edit.',
|
|
572
|
+
inputLocked: 'Reflect review is active; type /yes, /edit <feedback>, or /no',
|
|
573
|
+
answerLabel: 'Review input',
|
|
574
|
+
answerPlaceholder: '/yes | /edit <feedback> | /no'
|
|
529
575
|
}
|
|
530
576
|
}
|
|
531
577
|
};
|
|
@@ -539,13 +585,24 @@ function getCopy(language) {
|
|
|
539
585
|
}
|
|
540
586
|
|
|
541
587
|
function messageLabel(label, copy) {
|
|
542
|
-
return copy
|
|
588
|
+
return copy?.roleLabels?.[label] || String(label || '').toUpperCase();
|
|
543
589
|
}
|
|
544
590
|
|
|
545
591
|
function roleStyle(label) {
|
|
546
592
|
return ROLE_STYLES[label] || ROLE_STYLES.system;
|
|
547
593
|
}
|
|
548
594
|
|
|
595
|
+
const PLAN_AGENT_ROLES = new Set(['planner', 'advisor', 'coder', 'reviewer', 'tester', 'summarizer']);
|
|
596
|
+
|
|
597
|
+
function normalizePlanAgentRole(role) {
|
|
598
|
+
const roleKey = String(role || '').trim().toLowerCase();
|
|
599
|
+
return PLAN_AGENT_ROLES.has(roleKey) ? roleKey : 'coder';
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export function formatPlanAgentLabel(role, copy) {
|
|
603
|
+
return messageLabel(normalizePlanAgentRole(role), copy);
|
|
604
|
+
}
|
|
605
|
+
|
|
549
606
|
function StatusPill({ label, value, color = 'cyanBright', textColor = 'black' }) {
|
|
550
607
|
return h(
|
|
551
608
|
Box,
|
|
@@ -892,7 +949,7 @@ function textFromSessionContent(content) {
|
|
|
892
949
|
return sanitizeRenderableText(String(content || ''));
|
|
893
950
|
}
|
|
894
951
|
|
|
895
|
-
function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
952
|
+
export function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
896
953
|
const source = Array.isArray(sessionMessages) ? sessionMessages : [];
|
|
897
954
|
const out = [];
|
|
898
955
|
|
|
@@ -901,14 +958,14 @@ function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
|
901
958
|
if (message.role === 'tool') continue;
|
|
902
959
|
|
|
903
960
|
const text = textFromSessionContent(message.content);
|
|
904
|
-
if (!text.trim()
|
|
961
|
+
if (!text.trim()) continue;
|
|
905
962
|
|
|
906
963
|
if (message.role === 'user') {
|
|
907
964
|
out.push({ id: nextId(), label: 'you', text, color: 'blueBright' });
|
|
908
965
|
continue;
|
|
909
966
|
}
|
|
910
967
|
if (message.role === 'assistant') {
|
|
911
|
-
out.push({ id: nextId(), label: '
|
|
968
|
+
out.push({ id: nextId(), label: 'general', text, color: 'greenBright' });
|
|
912
969
|
continue;
|
|
913
970
|
}
|
|
914
971
|
if (message.role === 'system') {
|
|
@@ -1109,6 +1166,25 @@ export function parsePlanApprovalAnswer(value) {
|
|
|
1109
1166
|
return { action: 'invalid', command: '' };
|
|
1110
1167
|
}
|
|
1111
1168
|
|
|
1169
|
+
export function parseReflectApprovalAnswer(value) {
|
|
1170
|
+
const raw = String(value || '').trim();
|
|
1171
|
+
if (!raw) return { action: 'empty', command: '' };
|
|
1172
|
+
const normalized = raw.toLowerCase();
|
|
1173
|
+
if (normalized === '/yes' || normalized === 'yes') {
|
|
1174
|
+
return { action: 'approve', command: '/yes' };
|
|
1175
|
+
}
|
|
1176
|
+
if (normalized === '/no' || normalized === 'no') {
|
|
1177
|
+
return { action: 'reject', command: '/no' };
|
|
1178
|
+
}
|
|
1179
|
+
const editMatch = raw.match(/^\/?edit(?:\s+(.+))?$/i);
|
|
1180
|
+
if (editMatch) {
|
|
1181
|
+
const feedback = String(editMatch[1] || '').trim();
|
|
1182
|
+
if (!feedback) return { action: 'missing_feedback', command: '' };
|
|
1183
|
+
return { action: 'edit', feedback, command: `/edit ${feedback}` };
|
|
1184
|
+
}
|
|
1185
|
+
return { action: 'invalid', command: '' };
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1112
1188
|
export function parsePendingPlanApprovalMessage(text = '') {
|
|
1113
1189
|
const raw = String(text || '');
|
|
1114
1190
|
if (!/^Plan approval is still pending\./i.test(raw.trim())) return null;
|
|
@@ -1122,6 +1198,21 @@ export function parsePendingPlanApprovalMessage(text = '') {
|
|
|
1122
1198
|
return out;
|
|
1123
1199
|
}
|
|
1124
1200
|
|
|
1201
|
+
export function parsePendingReflectSkillMessage(text = '') {
|
|
1202
|
+
const raw = String(text || '');
|
|
1203
|
+
if (!/\bReflect skill draft pending\./i.test(raw)) return null;
|
|
1204
|
+
const lines = raw.split(/\r?\n/);
|
|
1205
|
+
const out = { scope: '', name: '', confidence: '', targetPath: '' };
|
|
1206
|
+
for (const line of lines) {
|
|
1207
|
+
const trimmed = line.trim();
|
|
1208
|
+
if (trimmed.startsWith('Scope: ')) out.scope = trimmed.slice('Scope: '.length).trim();
|
|
1209
|
+
else if (/^\[\d+\]\s+/.test(trimmed) && !out.name) out.name = trimmed.replace(/^\[\d+\]\s+/, '').trim();
|
|
1210
|
+
else if (trimmed.startsWith('Confidence: ')) out.confidence = trimmed.slice('Confidence: '.length).trim();
|
|
1211
|
+
else if (trimmed.startsWith('Target: ')) out.targetPath = trimmed.slice('Target: '.length).trim();
|
|
1212
|
+
}
|
|
1213
|
+
return out;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1125
1216
|
export function formatDeleteApprovalLines(copy, request) {
|
|
1126
1217
|
const details = normalizeDeleteApprovalRequest(request);
|
|
1127
1218
|
if (!details) return [];
|
|
@@ -1141,6 +1232,17 @@ export function formatPlanApprovalLines(copy, request) {
|
|
|
1141
1232
|
return [String(copy?.planApproval?.title || '').trim()].filter(Boolean);
|
|
1142
1233
|
}
|
|
1143
1234
|
|
|
1235
|
+
export function formatReflectApprovalLines(copy, request) {
|
|
1236
|
+
if (!request) return [];
|
|
1237
|
+
const c = copy?.reflectApproval || {};
|
|
1238
|
+
const lines = [String(c.title || '').trim()];
|
|
1239
|
+
if (request.scope) lines.push(`${c.scopeLabel || 'Scope'}: ${request.scope}`);
|
|
1240
|
+
if (request.name) lines.push(`${c.nameLabel || 'Name'}: ${request.name}`);
|
|
1241
|
+
if (request.targetPath) lines.push(`${c.targetLabel || 'Target'}: ${request.targetPath}`);
|
|
1242
|
+
if (c.prompt) lines.push(c.prompt);
|
|
1243
|
+
return lines.filter(Boolean);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1144
1246
|
function getActivityDisplayParts(activity) {
|
|
1145
1247
|
if (isCodeGenerationActivityName(activity?.name)) {
|
|
1146
1248
|
return {
|
|
@@ -1243,7 +1345,7 @@ export function isIndexSystemToolName(name) {
|
|
|
1243
1345
|
export function shouldShowCompletionFooter(msg) {
|
|
1244
1346
|
if (!msg || msg.loading || (msg.phase || '').trim()) return false;
|
|
1245
1347
|
const label = (msg.label || '').toLowerCase();
|
|
1246
|
-
return label === 'coder' || label === 'planner' || label === 'reviewer' || label === 'tester';
|
|
1348
|
+
return label === 'general' || label === 'advisor' || label === 'coder' || label === 'planner' || label === 'reviewer' || label === 'tester';
|
|
1247
1349
|
}
|
|
1248
1350
|
|
|
1249
1351
|
function describeToolActivity(name, copy, { done = false, blocked = false } = {}) {
|
|
@@ -1419,10 +1521,9 @@ function PlanStrip({ planState, copy }) {
|
|
|
1419
1521
|
Box,
|
|
1420
1522
|
{ flexDirection: 'column' },
|
|
1421
1523
|
...planState.steps.map((step, idx) => {
|
|
1422
|
-
const
|
|
1423
|
-
const normalizedRole = ['planner', 'coder', 'reviewer', 'tester', 'summarizer'].includes(roleKey) ? roleKey : 'coder';
|
|
1524
|
+
const normalizedRole = normalizePlanAgentRole(step.role);
|
|
1424
1525
|
const stepTheme = roleStyle(normalizedRole);
|
|
1425
|
-
const roleTag =
|
|
1526
|
+
const roleTag = formatPlanAgentLabel(normalizedRole, copy);
|
|
1426
1527
|
const stepDone = step.status === 'done' || isDone;
|
|
1427
1528
|
const stepFailed = step.status === 'failed';
|
|
1428
1529
|
const marker = stepFailed ? '✗' : stepDone ? '✓' : '·';
|
|
@@ -1431,8 +1532,8 @@ function PlanStrip({ planState, copy }) {
|
|
|
1431
1532
|
Box,
|
|
1432
1533
|
{ key: `plan-step-${idx}` },
|
|
1433
1534
|
h(Text, { color: markerColor }, `${marker} `),
|
|
1434
|
-
|
|
1435
|
-
|
|
1535
|
+
h(Text, { color: stepTheme.badgeText, backgroundColor: stepTheme.badgeBg }, ` ${roleTag} `),
|
|
1536
|
+
h(Text, { color: 'gray' }, ' '),
|
|
1436
1537
|
h(Text, { color: stepDone && !stepFailed ? 'gray' : 'white' }, `${step.index}. ${step.title}`)
|
|
1437
1538
|
);
|
|
1438
1539
|
}
|
|
@@ -1519,6 +1620,30 @@ function renderTextLine(msg, line, idx, color) {
|
|
|
1519
1620
|
);
|
|
1520
1621
|
}
|
|
1521
1622
|
|
|
1623
|
+
function historyListLineColor(line, fallbackColor) {
|
|
1624
|
+
const raw = String(line || '');
|
|
1625
|
+
const trimmed = raw.trim();
|
|
1626
|
+
if (!trimmed) return fallbackColor;
|
|
1627
|
+
if (/^Current session\s+/i.test(trimmed) || /^Recent sessions$/i.test(trimmed) || /^\d+\.\s+\S+/.test(trimmed)) {
|
|
1628
|
+
return 'cyanBright';
|
|
1629
|
+
}
|
|
1630
|
+
if (/^Messages\s+\d+$/i.test(trimmed) || /^\d+\s+msgs?\s+\|\s+updated\b/i.test(trimmed) || /^Tip:/i.test(trimmed)) {
|
|
1631
|
+
return 'gray';
|
|
1632
|
+
}
|
|
1633
|
+
if (/^resume:\s+\/history resume\b/i.test(trimmed)) {
|
|
1634
|
+
return 'blueBright';
|
|
1635
|
+
}
|
|
1636
|
+
return 'white';
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function isHistoryListMessage(msg) {
|
|
1640
|
+
const text = String(msg?.text || '');
|
|
1641
|
+
return msg?.label === 'system' &&
|
|
1642
|
+
/^Current session\s+/m.test(text) &&
|
|
1643
|
+
/^Recent sessions$/m.test(text) &&
|
|
1644
|
+
/resume:\s+\/history resume\b/m.test(text);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1522
1647
|
export function parseAutoPlanSummaryMessage(text) {
|
|
1523
1648
|
const raw = String(text || '').trim();
|
|
1524
1649
|
if (!/^Auto plan finished\b/i.test(raw)) return null;
|
|
@@ -1963,7 +2088,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1963
2088
|
Box,
|
|
1964
2089
|
{ marginBottom: planSteps.length > 0 || summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1965
2090
|
h(Text, { color: 'cyanBright' }, labels.plan),
|
|
1966
|
-
h(Text, { color: '
|
|
2091
|
+
h(Text, { color: 'white' }, summary.planSummary)
|
|
1967
2092
|
)
|
|
1968
2093
|
: null,
|
|
1969
2094
|
planSteps.length > 0
|
|
@@ -1972,12 +2097,20 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1972
2097
|
{ marginBottom: summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1973
2098
|
h(Text, { color: 'cyanBright' }, labels.steps),
|
|
1974
2099
|
...planSteps.flatMap((step, idx) => {
|
|
1975
|
-
const
|
|
2100
|
+
const roleKey = normalizePlanAgentRole(step?.role);
|
|
2101
|
+
const stepTheme = roleStyle(roleKey);
|
|
2102
|
+
const roleTag = formatPlanAgentLabel(roleKey, copy);
|
|
1976
2103
|
const titleText = String(step?.title || '-').trim() || '-';
|
|
1977
2104
|
const taskText = String(step?.task || '').trim();
|
|
1978
|
-
const titleRow = h(
|
|
2105
|
+
const titleRow = h(
|
|
2106
|
+
Text,
|
|
2107
|
+
{ key: `plan-step-title-${idx}`, color: 'white' },
|
|
2108
|
+
`${idx + 1}. `,
|
|
2109
|
+
h(Text, { color: stepTheme.badgeText, backgroundColor: stepTheme.badgeBg }, ` ${roleTag} `),
|
|
2110
|
+
` ${titleText}`
|
|
2111
|
+
);
|
|
1979
2112
|
if (!taskText) return [titleRow];
|
|
1980
|
-
const taskRow = h(Text, { key: `plan-step-task-${idx}`, color: '
|
|
2113
|
+
const taskRow = h(Text, { key: `plan-step-task-${idx}`, color: 'white' }, ` - task: ${taskText}`);
|
|
1981
2114
|
return [titleRow, taskRow];
|
|
1982
2115
|
})
|
|
1983
2116
|
)
|
|
@@ -1987,7 +2120,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1987
2120
|
Box,
|
|
1988
2121
|
{ marginBottom: metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1989
2122
|
h(Text, { color: 'yellowBright' }, labels.approval),
|
|
1990
|
-
h(Text, { color: '
|
|
2123
|
+
h(Text, { color: 'white' }, summary.approval)
|
|
1991
2124
|
)
|
|
1992
2125
|
: null,
|
|
1993
2126
|
metaItems.length > 0
|
|
@@ -1996,7 +2129,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1996
2129
|
{ marginBottom: summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0 },
|
|
1997
2130
|
...metaItems.flatMap((item, idx) => [
|
|
1998
2131
|
idx > 0 ? h(Text, { key: `sep-${idx}`, color: 'gray' }, ' ') : null,
|
|
1999
|
-
h(Text, { key: `meta-${idx}`, color: '
|
|
2132
|
+
h(Text, { key: `meta-${idx}`, color: 'white' }, item)
|
|
2000
2133
|
])
|
|
2001
2134
|
)
|
|
2002
2135
|
: null,
|
|
@@ -2005,7 +2138,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
2005
2138
|
Box,
|
|
2006
2139
|
{ marginBottom: summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
2007
2140
|
h(Text, { color: 'yellowBright' }, labels.warnings),
|
|
2008
|
-
h(Text, { color: '
|
|
2141
|
+
h(Text, { color: 'white' }, summary.warningSteps)
|
|
2009
2142
|
)
|
|
2010
2143
|
: null,
|
|
2011
2144
|
summary.failedSteps
|
|
@@ -2013,7 +2146,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
2013
2146
|
Box,
|
|
2014
2147
|
{ marginBottom: shortFile ? 1 : 0, flexDirection: 'column' },
|
|
2015
2148
|
h(Text, { color: 'redBright' }, labels.failed),
|
|
2016
|
-
h(Text, { color: '
|
|
2149
|
+
h(Text, { color: 'white' }, summary.failedSteps)
|
|
2017
2150
|
)
|
|
2018
2151
|
: null,
|
|
2019
2152
|
shortFile
|
|
@@ -2309,6 +2442,7 @@ export function collapseActivityChainRows(inputRows, showToolDetails, copy, maxV
|
|
|
2309
2442
|
|
|
2310
2443
|
export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy) {
|
|
2311
2444
|
const rows = [];
|
|
2445
|
+
const isHistoryList = isHistoryListMessage(msg);
|
|
2312
2446
|
const pushTextRows = (text) => {
|
|
2313
2447
|
const lines = String(text || '').split('\n');
|
|
2314
2448
|
let codeFence = false;
|
|
@@ -2329,8 +2463,8 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2329
2463
|
continue;
|
|
2330
2464
|
}
|
|
2331
2465
|
if (isMarkdownTableHeader(line, lines[lineIndex + 1])) {
|
|
2332
|
-
const tableLines = [line];
|
|
2333
|
-
lineIndex += 1; //
|
|
2466
|
+
const tableLines = [line, lines[lineIndex + 1]];
|
|
2467
|
+
lineIndex += 1; // separator included above
|
|
2334
2468
|
while (lineIndex + 1 < lines.length && splitMarkdownTableCells(lines[lineIndex + 1]).length > 1) {
|
|
2335
2469
|
tableLines.push(lines[lineIndex + 1]);
|
|
2336
2470
|
lineIndex += 1;
|
|
@@ -2339,7 +2473,8 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2339
2473
|
continue;
|
|
2340
2474
|
}
|
|
2341
2475
|
let color = msg.color || roleStyle(msg.label).text || 'white';
|
|
2342
|
-
if (
|
|
2476
|
+
if (isHistoryList) color = historyListLineColor(line, color);
|
|
2477
|
+
else if (line.startsWith('#')) color = 'cyanBright';
|
|
2343
2478
|
else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) color = 'magentaBright';
|
|
2344
2479
|
else if (trimmed.startsWith('>')) color = 'yellow';
|
|
2345
2480
|
else if (/^[|└├│]/.test(trimmed)) color = 'gray';
|
|
@@ -2391,13 +2526,27 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2391
2526
|
}
|
|
2392
2527
|
};
|
|
2393
2528
|
|
|
2529
|
+
const visiblePendingToolCalls = (existingCalls = []) => {
|
|
2530
|
+
const pendingToolCalls = Array.isArray(msg?.pendingToolCalls) ? msg.pendingToolCalls : [];
|
|
2531
|
+
return pendingToolCalls.filter((pending) => {
|
|
2532
|
+
if (!pending) return false;
|
|
2533
|
+
if (pending.id && existingCalls.some((tool) => tool?.id && tool.id === pending.id)) return false;
|
|
2534
|
+
const pendingBase = parseToolDisplayName(pending.name).base;
|
|
2535
|
+
return !existingCalls.some(
|
|
2536
|
+
(tool) => parseToolDisplayName(tool?.name).base === pendingBase && tool?.status === 'running'
|
|
2537
|
+
);
|
|
2538
|
+
});
|
|
2539
|
+
};
|
|
2540
|
+
|
|
2394
2541
|
if (Array.isArray(msg?.segments) && msg.segments.length > 0) {
|
|
2395
|
-
const
|
|
2542
|
+
const segmentTools = msg.segments.filter(
|
|
2396
2543
|
(segment) =>
|
|
2397
2544
|
segment.type === 'tool' ||
|
|
2398
2545
|
segment.type === 'skill' ||
|
|
2399
2546
|
(segment.type === 'system_tool' && (showToolDetails || !isIndexSystemToolName(segment.name)))
|
|
2400
|
-
)
|
|
2547
|
+
);
|
|
2548
|
+
const pendingToolCalls = visiblePendingToolCalls(segmentTools);
|
|
2549
|
+
const totalTools = segmentTools.length + pendingToolCalls.length;
|
|
2401
2550
|
let toolIndex = 0;
|
|
2402
2551
|
for (const segment of msg.segments) {
|
|
2403
2552
|
if (segment.type === 'tool' || segment.type === 'skill' || segment.type === 'system_tool') {
|
|
@@ -2410,19 +2559,14 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2410
2559
|
pushTextRows(segment.text || '');
|
|
2411
2560
|
}
|
|
2412
2561
|
}
|
|
2562
|
+
pendingToolCalls.forEach((tool) => {
|
|
2563
|
+
pushActivityRows(tool, toolIndex, totalTools);
|
|
2564
|
+
toolIndex += 1;
|
|
2565
|
+
});
|
|
2413
2566
|
} else {
|
|
2414
2567
|
pushTextRows(msg?.text || '');
|
|
2415
2568
|
const toolCalls = Array.isArray(msg?.toolCalls) ? msg.toolCalls : [];
|
|
2416
|
-
const
|
|
2417
|
-
const visibleCalls = [
|
|
2418
|
-
...toolCalls,
|
|
2419
|
-
...pendingToolCalls.filter((pending) => {
|
|
2420
|
-
if (!pending) return false;
|
|
2421
|
-
if (pending.id && toolCalls.some((tool) => tool?.id && tool.id === pending.id)) return false;
|
|
2422
|
-
const pendingBase = parseToolDisplayName(pending.name).base;
|
|
2423
|
-
return !toolCalls.some((tool) => parseToolDisplayName(tool?.name).base === pendingBase && tool?.status === 'running');
|
|
2424
|
-
})
|
|
2425
|
-
];
|
|
2569
|
+
const visibleCalls = [...toolCalls, ...visiblePendingToolCalls(toolCalls)];
|
|
2426
2570
|
visibleCalls.forEach((tool, idx) => pushActivityRows(tool, idx, visibleCalls.length));
|
|
2427
2571
|
}
|
|
2428
2572
|
|
|
@@ -2515,36 +2659,36 @@ export function renderMessageRow(msg, row, idx, loaderTick) {
|
|
|
2515
2659
|
return h(
|
|
2516
2660
|
Box,
|
|
2517
2661
|
{ key: `row-table-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2518
|
-
h(Text, { color:
|
|
2662
|
+
h(Text, { color: 'white', bold: Boolean(row.isHeader) }, row.text)
|
|
2519
2663
|
);
|
|
2520
2664
|
}
|
|
2521
2665
|
if (row.kind === 'table-separator') {
|
|
2522
2666
|
return h(
|
|
2523
2667
|
Box,
|
|
2524
2668
|
{ key: `row-table-sep-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2525
|
-
h(Text, { color: '
|
|
2669
|
+
h(Text, { color: 'white' }, row.text)
|
|
2526
2670
|
);
|
|
2527
2671
|
}
|
|
2528
2672
|
if (row.kind === 'table-vertical') {
|
|
2529
2673
|
return h(
|
|
2530
2674
|
Box,
|
|
2531
2675
|
{ key: `row-table-v-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2532
|
-
h(Text, { color: '
|
|
2533
|
-
h(Text, { color: '
|
|
2676
|
+
h(Text, { color: 'white', bold: true }, `${row.label}:`),
|
|
2677
|
+
h(Text, { color: 'white' }, row.text ? ` ${row.text}` : '')
|
|
2534
2678
|
);
|
|
2535
2679
|
}
|
|
2536
2680
|
if (row.kind === 'table-vertical-continuation') {
|
|
2537
2681
|
return h(
|
|
2538
2682
|
Box,
|
|
2539
2683
|
{ key: `row-table-vc-${msg.id}-${idx}`, marginLeft: 3 },
|
|
2540
|
-
h(Text, { color: '
|
|
2684
|
+
h(Text, { color: 'white' }, row.text)
|
|
2541
2685
|
);
|
|
2542
2686
|
}
|
|
2543
2687
|
if (row.kind === 'table-vertical-separator') {
|
|
2544
2688
|
return h(
|
|
2545
2689
|
Box,
|
|
2546
2690
|
{ key: `row-table-vs-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2547
|
-
h(Text, { color: '
|
|
2691
|
+
h(Text, { color: 'white' }, row.text)
|
|
2548
2692
|
);
|
|
2549
2693
|
}
|
|
2550
2694
|
if (row.kind === 'activity-collapsed') {
|
|
@@ -3150,6 +3294,38 @@ function PlanApprovalPanel({ request, inputValue, errorText, copy, cursorVisible
|
|
|
3150
3294
|
);
|
|
3151
3295
|
}
|
|
3152
3296
|
|
|
3297
|
+
function ReflectApprovalPanel({ request, inputValue, errorText, copy, cursorVisible }) {
|
|
3298
|
+
if (!request) return null;
|
|
3299
|
+
const placeholder = String(copy.reflectApproval.answerPlaceholder || '').trim();
|
|
3300
|
+
const lines = formatReflectApprovalLines(copy, request);
|
|
3301
|
+
return h(
|
|
3302
|
+
Box,
|
|
3303
|
+
{
|
|
3304
|
+
marginTop: 1,
|
|
3305
|
+
flexDirection: 'column',
|
|
3306
|
+
borderStyle: 'round',
|
|
3307
|
+
borderColor: 'yellowBright',
|
|
3308
|
+
paddingX: 1,
|
|
3309
|
+
paddingY: 0
|
|
3310
|
+
},
|
|
3311
|
+
...lines.map((line, index) =>
|
|
3312
|
+
h(Text, { key: `reflect-approval-line-${index}`, color: 'yellowBright' }, line)
|
|
3313
|
+
),
|
|
3314
|
+
h(
|
|
3315
|
+
Box,
|
|
3316
|
+
{ marginTop: 1 },
|
|
3317
|
+
h(Text, { color: 'yellowBright' }, `${copy.reflectApproval.answerLabel}: `),
|
|
3318
|
+
h(ApprovalCursorLine, {
|
|
3319
|
+
inputValue,
|
|
3320
|
+
placeholder: placeholder || ' ',
|
|
3321
|
+
cursorVisible,
|
|
3322
|
+
accent: 'yellowBright'
|
|
3323
|
+
})
|
|
3324
|
+
),
|
|
3325
|
+
errorText ? h(Text, { color: 'yellowBright' }, errorText) : null
|
|
3326
|
+
);
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3153
3329
|
function SignatureBar({ version = '' }) {
|
|
3154
3330
|
return h(
|
|
3155
3331
|
Box,
|
|
@@ -3177,6 +3353,7 @@ function formatRuntimeSnapshot(snapshot) {
|
|
|
3177
3353
|
if (!snapshot || typeof snapshot !== 'object') return '';
|
|
3178
3354
|
return [
|
|
3179
3355
|
`mode=${snapshot.mode || '-'}`,
|
|
3356
|
+
`role=${snapshot.agentRole || 'general'}`,
|
|
3180
3357
|
`model=${snapshot.model || '-'}`,
|
|
3181
3358
|
`max_ctx=${snapshot.maxContextTokens || '-'}`,
|
|
3182
3359
|
`session=${snapshot.sessionId || '-'}`
|
|
@@ -3240,7 +3417,10 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3240
3417
|
const [pendingPlanApproval, setPendingPlanApproval] = useState(null);
|
|
3241
3418
|
const [planApprovalInput, setPlanApprovalInput] = useState('');
|
|
3242
3419
|
const [planApprovalError, setPlanApprovalError] = useState('');
|
|
3243
|
-
const
|
|
3420
|
+
const [pendingReflectApproval, setPendingReflectApproval] = useState(null);
|
|
3421
|
+
const [reflectApprovalInput, setReflectApprovalInput] = useState('');
|
|
3422
|
+
const [reflectApprovalError, setReflectApprovalError] = useState('');
|
|
3423
|
+
const approvalLockActive = Boolean(pendingDeleteApproval || pendingRunApproval || pendingPlanApproval || pendingReflectApproval);
|
|
3244
3424
|
const activeAssistantIdRef = useRef(null);
|
|
3245
3425
|
const activeAssistantAutoSkillNamesRef = useRef([]);
|
|
3246
3426
|
const streamedAssistantHandledRef = useRef(false);
|
|
@@ -3403,7 +3583,7 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3403
3583
|
const current = Number(last[1]);
|
|
3404
3584
|
const total = Number(last[2]);
|
|
3405
3585
|
const role = String(last[3] || '').trim().toLowerCase();
|
|
3406
|
-
const normalizedRole =
|
|
3586
|
+
const normalizedRole = PLAN_AGENT_ROLES.has(role) ? role : 'coder';
|
|
3407
3587
|
const title = String(last[4] || '').trim();
|
|
3408
3588
|
|
|
3409
3589
|
// Detect step transition — finalize old assistant and create a new one
|
|
@@ -3611,7 +3791,7 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3611
3791
|
...prev,
|
|
3612
3792
|
{
|
|
3613
3793
|
id: nextId(),
|
|
3614
|
-
label: '
|
|
3794
|
+
label: 'general',
|
|
3615
3795
|
text: sanitizeRenderableText(displayText),
|
|
3616
3796
|
color: 'greenBright',
|
|
3617
3797
|
autoSkillNames: activeAssistantAutoSkillNamesRef.current
|
|
@@ -3656,6 +3836,13 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3656
3836
|
setPlanState((prev) => ({ ...prev, pendingApproval: true }));
|
|
3657
3837
|
setPlanApprovalInput('');
|
|
3658
3838
|
setPlanApprovalError('');
|
|
3839
|
+
} else {
|
|
3840
|
+
const pendingReflectMeta = parsePendingReflectSkillMessage(result.text || '');
|
|
3841
|
+
if (pendingReflectMeta) {
|
|
3842
|
+
setPendingReflectApproval(pendingReflectMeta);
|
|
3843
|
+
setReflectApprovalInput('');
|
|
3844
|
+
setReflectApprovalError('');
|
|
3845
|
+
}
|
|
3659
3846
|
}
|
|
3660
3847
|
}
|
|
3661
3848
|
setMessages((prev) => [
|
|
@@ -3744,8 +3931,8 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3744
3931
|
const aid = nextId();
|
|
3745
3932
|
activeAssistantIdRef.current = aid;
|
|
3746
3933
|
const planRole = activePlanStepRoleRef.current;
|
|
3747
|
-
const label = planRole || '
|
|
3748
|
-
const style = ROLE_STYLES[label] || ROLE_STYLES.
|
|
3934
|
+
const label = planRole || 'general';
|
|
3935
|
+
const style = ROLE_STYLES[label] || ROLE_STYLES.general;
|
|
3749
3936
|
const planStepInfo = activePlanStepInfoRef.current;
|
|
3750
3937
|
const planStepTitle = activePlanStepTitleRef.current;
|
|
3751
3938
|
const planStepDisplay = planStepInfo ? `${planStepInfo.current}/${planStepInfo.total} · ${planStepTitle}` : undefined;
|
|
@@ -3798,6 +3985,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3798
3985
|
setPendingPlanApproval(null);
|
|
3799
3986
|
setPlanApprovalInput('');
|
|
3800
3987
|
setPlanApprovalError('');
|
|
3988
|
+
setPendingReflectApproval(null);
|
|
3989
|
+
setReflectApprovalInput('');
|
|
3990
|
+
setReflectApprovalError('');
|
|
3801
3991
|
setPlanState({
|
|
3802
3992
|
current: 0,
|
|
3803
3993
|
total: 0,
|
|
@@ -3930,7 +4120,7 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3930
4120
|
const cleanedStandaloneText = stripPlanExecutionResult(String(displayText || event.text)).trim();
|
|
3931
4121
|
setMessages((prev) => [
|
|
3932
4122
|
...prev,
|
|
3933
|
-
{ id: nextId(), label: '
|
|
4123
|
+
{ id: nextId(), label: 'general', text: cleanedStandaloneText, color: 'greenBright' }
|
|
3934
4124
|
]);
|
|
3935
4125
|
}
|
|
3936
4126
|
}
|
|
@@ -4485,6 +4675,46 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4485
4675
|
return;
|
|
4486
4676
|
}
|
|
4487
4677
|
|
|
4678
|
+
if (pendingReflectApproval) {
|
|
4679
|
+
if (key.return) {
|
|
4680
|
+
const parsed = parseReflectApprovalAnswer(reflectApprovalInput);
|
|
4681
|
+
if (parsed.action === 'approve' || parsed.action === 'reject' || parsed.action === 'edit') {
|
|
4682
|
+
setPendingReflectApproval(null);
|
|
4683
|
+
setReflectApprovalInput('');
|
|
4684
|
+
setReflectApprovalError('');
|
|
4685
|
+
runSubmission(parsed.command);
|
|
4686
|
+
} else if (parsed.action === 'missing_feedback') {
|
|
4687
|
+
setReflectApprovalError(copy.reflectApproval.missingFeedback);
|
|
4688
|
+
} else {
|
|
4689
|
+
setReflectApprovalError(copy.reflectApproval.invalidAnswer);
|
|
4690
|
+
}
|
|
4691
|
+
return;
|
|
4692
|
+
}
|
|
4693
|
+
|
|
4694
|
+
if (isBackspaceKey(value, key) || isDeleteKey(value, key)) {
|
|
4695
|
+
setReflectApprovalInput((prev) => prev.slice(0, -1));
|
|
4696
|
+
setReflectApprovalError('');
|
|
4697
|
+
return;
|
|
4698
|
+
}
|
|
4699
|
+
|
|
4700
|
+
if (isPrintableInput(value, key)) {
|
|
4701
|
+
setReflectApprovalInput((prev) => `${prev}${value}`);
|
|
4702
|
+
setReflectApprovalError('');
|
|
4703
|
+
return;
|
|
4704
|
+
}
|
|
4705
|
+
|
|
4706
|
+
if (key.ctrl && value === 'c') {
|
|
4707
|
+
if (busy && typeof runtime.abort === 'function') {
|
|
4708
|
+
runtime.abort();
|
|
4709
|
+
return;
|
|
4710
|
+
}
|
|
4711
|
+
exit();
|
|
4712
|
+
return;
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4488
4718
|
if (key.upArrow) {
|
|
4489
4719
|
if (suggestionNav && commandSuggestions.length > 0) {
|
|
4490
4720
|
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'up'));
|
|
@@ -4754,6 +4984,17 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4754
4984
|
}
|
|
4755
4985
|
}, [runtimeState?.pendingPlanApproval, busy]);
|
|
4756
4986
|
|
|
4987
|
+
useEffect(() => {
|
|
4988
|
+
const pending = Boolean(runtimeState?.pendingReflectSkill);
|
|
4989
|
+
if (!pending) {
|
|
4990
|
+
setPendingReflectApproval(null);
|
|
4991
|
+
return;
|
|
4992
|
+
}
|
|
4993
|
+
if (!busy) {
|
|
4994
|
+
setPendingReflectApproval((prev) => prev || { scope: '', name: '', targetPath: '' });
|
|
4995
|
+
}
|
|
4996
|
+
}, [runtimeState?.pendingReflectSkill, busy]);
|
|
4997
|
+
|
|
4757
4998
|
useEffect(() => {
|
|
4758
4999
|
if (commandSuggestions.length === 0) {
|
|
4759
5000
|
setSuggestionNav(false);
|
|
@@ -4800,7 +5041,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4800
5041
|
? { text: copy.runApproval.inputLocked }
|
|
4801
5042
|
: pendingPlanApproval
|
|
4802
5043
|
? { text: copy.planApproval.inputLocked }
|
|
4803
|
-
:
|
|
5044
|
+
: pendingReflectApproval
|
|
5045
|
+
? { text: copy.reflectApproval.inputLocked }
|
|
5046
|
+
: null;
|
|
4804
5047
|
|
|
4805
5048
|
return h(
|
|
4806
5049
|
Box,
|
|
@@ -4850,6 +5093,13 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4850
5093
|
copy,
|
|
4851
5094
|
cursorVisible
|
|
4852
5095
|
}),
|
|
5096
|
+
h(ReflectApprovalPanel, {
|
|
5097
|
+
request: pendingReflectApproval,
|
|
5098
|
+
inputValue: reflectApprovalInput,
|
|
5099
|
+
errorText: reflectApprovalError,
|
|
5100
|
+
copy,
|
|
5101
|
+
cursorVisible
|
|
5102
|
+
}),
|
|
4853
5103
|
debugKeys
|
|
4854
5104
|
? h(
|
|
4855
5105
|
Box,
|