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/core/chat-runtime.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseInput } from './input-parser.js';
|
|
2
2
|
import { loadCommandsAndSkills, renderCommandPrompt } from './command-loader.js';
|
|
3
|
-
import { runAgentLoop
|
|
3
|
+
import { runAgentLoop } from './agent-loop.js';
|
|
4
|
+
import { setResultDir, clearResultStore } from './tool-result-store.js';
|
|
4
5
|
import { trimInline, normalizePath } from './string-utils.js';
|
|
5
6
|
import fs from 'node:fs/promises';
|
|
6
7
|
import path from 'node:path';
|
|
@@ -25,10 +26,16 @@ import { buildSystemPromptWithSoul } from './soul.js';
|
|
|
25
26
|
import { getProjectPlansDir, getProjectSpecsDir, getProjectWorkspaceDir, getSessionsDir } from './paths.js';
|
|
26
27
|
import { buildProjectContextSnippet, initializeProjectIndex } from './project-index.js';
|
|
27
28
|
import { buildMemorySnapshot } from './memory-prompt.js';
|
|
28
|
-
import { forgetMemory, listMemories, searchMemories, captureToInbox, listInbox } from './memory-store.js';
|
|
29
|
+
import { forgetMemory, listMemories, rememberMemory, searchMemories, captureToInbox, listInbox } from './memory-store.js';
|
|
29
30
|
import { runDreamConsolidation } from './dream-consolidate.js';
|
|
30
31
|
import { normalizePlanState } from './plan-state.js';
|
|
31
32
|
import { countActiveTodos, normalizeTodos } from './todo-state.js';
|
|
33
|
+
import {
|
|
34
|
+
attachReflectTargets,
|
|
35
|
+
buildReflectSkillDraft,
|
|
36
|
+
parseReflectScope,
|
|
37
|
+
writeReflectSkillDraft
|
|
38
|
+
} from './reflect-skill.js';
|
|
32
39
|
|
|
33
40
|
const STREAM_SAVE_DEBOUNCE_MS = 120;
|
|
34
41
|
|
|
@@ -153,12 +160,14 @@ function getCompletionCopy(language = 'zh') {
|
|
|
153
160
|
config: '设置/读取/列出/重置配置',
|
|
154
161
|
memory: '查看/搜索/删除持久记忆',
|
|
155
162
|
dream: '整理记忆收件箱(dream consolidation)',
|
|
163
|
+
reflect: '复盘成功链路并生成可审阅 skill 草稿',
|
|
156
164
|
history: '查看/恢复会话',
|
|
157
165
|
debug: '运行时调试开关',
|
|
158
166
|
retry: '重试上一条用户请求',
|
|
159
167
|
stop: '中止当前回答',
|
|
160
168
|
new: '开始新会话',
|
|
161
169
|
yes: '确认当前待审批计划并开始执行',
|
|
170
|
+
no: '放弃当前待审批事项',
|
|
162
171
|
edit: '修改当前待审批计划',
|
|
163
172
|
reject: '拒绝当前待审批计划'
|
|
164
173
|
},
|
|
@@ -172,6 +181,7 @@ function getCompletionCopy(language = 'zh') {
|
|
|
172
181
|
agentCommand: '子代理命令',
|
|
173
182
|
memoryCommand: '记忆命令',
|
|
174
183
|
dreamCommand: '记忆整理命令',
|
|
184
|
+
reflectCommand: '复盘生成 skill 草稿',
|
|
175
185
|
debugCommand: '调试命令',
|
|
176
186
|
keyboardDebugCommand: '键盘调试命令',
|
|
177
187
|
compactCommand: '上下文压缩命令',
|
|
@@ -250,12 +260,14 @@ function getCompletionCopy(language = 'zh') {
|
|
|
250
260
|
config: 'set/get/list/reset config values',
|
|
251
261
|
memory: 'list/search/delete persistent memories',
|
|
252
262
|
dream: 'consolidate memory inbox (dream)',
|
|
263
|
+
reflect: 'reflect on a successful workflow and draft a reusable skill',
|
|
253
264
|
history: 'list/resume sessions',
|
|
254
265
|
debug: 'runtime debug switches',
|
|
255
266
|
retry: 'retry the last user request',
|
|
256
267
|
stop: 'stop the current response',
|
|
257
268
|
new: 'start a new session',
|
|
258
269
|
yes: 'approve the pending plan and start execution',
|
|
270
|
+
no: 'discard the pending item',
|
|
259
271
|
edit: 'revise the pending plan',
|
|
260
272
|
reject: 'reject the pending plan'
|
|
261
273
|
},
|
|
@@ -269,6 +281,7 @@ function getCompletionCopy(language = 'zh') {
|
|
|
269
281
|
agentCommand: 'sub-agent command',
|
|
270
282
|
memoryCommand: 'memory command',
|
|
271
283
|
dreamCommand: 'dream consolidation command',
|
|
284
|
+
reflectCommand: 'reflect skill draft command',
|
|
272
285
|
debugCommand: 'debug command',
|
|
273
286
|
keyboardDebugCommand: 'keyboard debug command',
|
|
274
287
|
compactCommand: 'context compaction command',
|
|
@@ -413,7 +426,10 @@ function buildPipelineStepGuidance({ role, stepIndex, totalSteps, isFirst, isLas
|
|
|
413
426
|
lines.push('- If you discover something new, record it under the requested headings instead of burying it in prose.');
|
|
414
427
|
lines.push('- Continue the established direction unless you have concrete contradictory evidence.');
|
|
415
428
|
lines.push('- Output only what the next step needs to know. Skip obvious observations.');
|
|
416
|
-
if (
|
|
429
|
+
if (role !== 'summarizer') {
|
|
430
|
+
lines.push('- Do not produce a final overall summary; the final summarizer step owns synthesis.');
|
|
431
|
+
}
|
|
432
|
+
if (isLast && role === 'summarizer') {
|
|
417
433
|
lines.push('- Since you are the final step, give a concise overall verdict the user can act on.');
|
|
418
434
|
}
|
|
419
435
|
return lines.join('\n');
|
|
@@ -719,8 +735,9 @@ function buildAutoPlanPlannerGuidance() {
|
|
|
719
735
|
'- Prefer the smallest local approach that satisfies the goal.',
|
|
720
736
|
'- Do not output multiple alternative branches in the final plan.',
|
|
721
737
|
'- Do not assume implementation should begin before the plan is coherent.',
|
|
722
|
-
'- Available sub-agent roles are planner, coder, reviewer, tester, and summarizer. Use only the roles the task actually needs.',
|
|
723
|
-
'- The summarizer
|
|
738
|
+
'- Available sub-agent roles are planner, coder, reviewer, tester, and summarizer. Use only the non-summary roles the task actually needs.',
|
|
739
|
+
'- Always include a summarizer as the final step. The summarizer reads accumulated step results from the plan file and synthesizes the final summary. It does NOT re-analyze the codebase.',
|
|
740
|
+
'- Do not ask planner, coder, reviewer, or tester steps to produce the final summary. They should write detailed step results for the summarizer.',
|
|
724
741
|
'- For implementation-heavy or risky changes, prefer adding review and/or verification steps.',
|
|
725
742
|
'- For analysis, recommendation, or planning-only goals, you may omit reviewer/tester if they do not add value.',
|
|
726
743
|
'- Prefer 3-5 steps total unless the task is clearly larger.',
|
|
@@ -1040,7 +1057,12 @@ async function buildTesterVerificationPacket(focusPaths = []) {
|
|
|
1040
1057
|
return lines.join('\n');
|
|
1041
1058
|
}
|
|
1042
1059
|
|
|
1043
|
-
function
|
|
1060
|
+
function isBundledSkillCommand(command) {
|
|
1061
|
+
return command?.metadata?.type === 'skill' && command?.source === 'bundled-skill';
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function isSkillEnabled(config, name, command = null) {
|
|
1065
|
+
if (isBundledSkillCommand(command)) return true;
|
|
1044
1066
|
return config.skills?.enabled?.[name] !== false;
|
|
1045
1067
|
}
|
|
1046
1068
|
|
|
@@ -1165,7 +1187,7 @@ function buildMediumTaskSystemPrompt(systemPrompt) {
|
|
|
1165
1187
|
}
|
|
1166
1188
|
|
|
1167
1189
|
function buildAutoSkillSystemPrompt(baseSystemPrompt, commands, config, text) {
|
|
1168
|
-
const selected = classifyAutoRoute(text).selectedSkills.filter((name) => isSkillEnabled(config, name));
|
|
1190
|
+
const selected = classifyAutoRoute(text).selectedSkills.filter((name) => isSkillEnabled(config, name, commands.get(name)));
|
|
1169
1191
|
if (selected.length === 0) return baseSystemPrompt;
|
|
1170
1192
|
|
|
1171
1193
|
const blocks = [];
|
|
@@ -1251,6 +1273,7 @@ function buildFallbackAutoPlan(goal) {
|
|
|
1251
1273
|
: `Auto fallback plan for: ${goal}`;
|
|
1252
1274
|
|
|
1253
1275
|
if (lightweightGoal) {
|
|
1276
|
+
const summarizerStep = buildDefaultSummarizerStep(goal);
|
|
1254
1277
|
return {
|
|
1255
1278
|
summary,
|
|
1256
1279
|
steps: [
|
|
@@ -1263,7 +1286,8 @@ function buildFallbackAutoPlan(goal) {
|
|
|
1263
1286
|
title: 'Verify the change',
|
|
1264
1287
|
role: 'tester',
|
|
1265
1288
|
task: `Verify the completed change for: ${goal}. Run the most relevant focused checks available and report concrete evidence plus anything still unverified.`
|
|
1266
|
-
}
|
|
1289
|
+
},
|
|
1290
|
+
summarizerStep
|
|
1267
1291
|
]
|
|
1268
1292
|
};
|
|
1269
1293
|
}
|
|
@@ -1344,16 +1368,18 @@ function enforceAutoPlanGuardrailSteps(plan, goal) {
|
|
|
1344
1368
|
|
|
1345
1369
|
if (taskClass === 'advisory') {
|
|
1346
1370
|
const advisorySteps = source.filter((step) => step.role === 'planner' || step.role === 'coder');
|
|
1371
|
+
const baseSteps = advisorySteps.length > 0 ? advisorySteps.slice(0, 6) : [primaryImplementationStep];
|
|
1347
1372
|
return {
|
|
1348
1373
|
summary: String(plan?.summary || `Auto plan for: ${goal}`).trim(),
|
|
1349
|
-
steps:
|
|
1374
|
+
steps: [...baseSteps, summarizerStep]
|
|
1350
1375
|
};
|
|
1351
1376
|
}
|
|
1352
1377
|
|
|
1353
1378
|
if (lightweightGoal) {
|
|
1379
|
+
const baseSteps = hasTester ? [primaryImplementationStep, testerStep] : [primaryImplementationStep];
|
|
1354
1380
|
return {
|
|
1355
1381
|
summary: String(plan?.summary || `Auto plan for: ${goal}`).trim(),
|
|
1356
|
-
steps:
|
|
1382
|
+
steps: [...baseSteps, summarizerStep]
|
|
1357
1383
|
};
|
|
1358
1384
|
}
|
|
1359
1385
|
|
|
@@ -1362,11 +1388,9 @@ function enforceAutoPlanGuardrailSteps(plan, goal) {
|
|
|
1362
1388
|
...(hasReviewer ? [reviewerStep] : []),
|
|
1363
1389
|
...(testerStep ? [testerStep] : [])
|
|
1364
1390
|
];
|
|
1365
|
-
const needsSummarizer = executionSteps.length >= 3;
|
|
1366
|
-
|
|
1367
1391
|
return {
|
|
1368
1392
|
summary: String(plan?.summary || `Auto plan for: ${goal}`).trim(),
|
|
1369
|
-
steps:
|
|
1393
|
+
steps: [...executionSteps, summarizerStep]
|
|
1370
1394
|
};
|
|
1371
1395
|
}
|
|
1372
1396
|
|
|
@@ -1656,35 +1680,51 @@ async function removePlanFileIfPresent(planState) {
|
|
|
1656
1680
|
|
|
1657
1681
|
function buildSpecTemplate(topic) {
|
|
1658
1682
|
return `
|
|
1659
|
-
#
|
|
1683
|
+
# ${topic} Design
|
|
1660
1684
|
|
|
1661
|
-
##
|
|
1662
|
-
-
|
|
1663
|
-
-
|
|
1685
|
+
## Summary
|
|
1686
|
+
- Problem statement
|
|
1687
|
+
- Desired outcome
|
|
1688
|
+
- Why this is worth doing
|
|
1664
1689
|
|
|
1665
|
-
##
|
|
1690
|
+
## Goals
|
|
1666
1691
|
- Primary goal
|
|
1667
|
-
-
|
|
1692
|
+
- Secondary goals
|
|
1668
1693
|
|
|
1669
|
-
##
|
|
1670
|
-
-
|
|
1671
|
-
-
|
|
1694
|
+
## Non-Goals
|
|
1695
|
+
- Out-of-scope behavior
|
|
1696
|
+
- Explicitly rejected approaches
|
|
1672
1697
|
|
|
1673
|
-
##
|
|
1698
|
+
## User Experience / Command Behavior
|
|
1699
|
+
- User-facing commands or flows
|
|
1700
|
+
- Review or approval behavior
|
|
1701
|
+
- Expected outputs
|
|
1702
|
+
|
|
1703
|
+
## Architecture
|
|
1704
|
+
- Main modules and responsibilities
|
|
1705
|
+
- Data flow
|
|
1706
|
+
- Integration points
|
|
1707
|
+
|
|
1708
|
+
## Data / State Model
|
|
1709
|
+
- New or changed state
|
|
1710
|
+
- Persistence locations
|
|
1711
|
+
- Lifecycle and cleanup behavior
|
|
1712
|
+
|
|
1713
|
+
## Safety Rules
|
|
1714
|
+
- Guardrails
|
|
1715
|
+
- Permission or approval requirements
|
|
1716
|
+
- Failure behavior
|
|
1717
|
+
|
|
1718
|
+
## Requirements
|
|
1674
1719
|
- Functional requirements
|
|
1675
1720
|
- Non-functional requirements
|
|
1676
1721
|
- Win10 compatibility requirements
|
|
1677
1722
|
|
|
1678
|
-
##
|
|
1679
|
-
- Architecture sketch
|
|
1680
|
-
- Data flow
|
|
1681
|
-
- Key interfaces/commands
|
|
1682
|
-
|
|
1683
|
-
## 6. Risks and Mitigations
|
|
1723
|
+
## Risks and Mitigations
|
|
1684
1724
|
- Risk
|
|
1685
1725
|
- Mitigation
|
|
1686
1726
|
|
|
1687
|
-
##
|
|
1727
|
+
## Testing / Validation
|
|
1688
1728
|
- Test strategy
|
|
1689
1729
|
- Acceptance checklist
|
|
1690
1730
|
`;
|
|
@@ -1703,17 +1743,20 @@ async function buildSpecWithModel({
|
|
|
1703
1743
|
systemPrompt
|
|
1704
1744
|
}) {
|
|
1705
1745
|
const prompt = [
|
|
1706
|
-
'Write a practical engineering spec in markdown.',
|
|
1746
|
+
'Write a practical engineering spec in markdown, like an implementation-ready design document.',
|
|
1707
1747
|
'Use these sections exactly:',
|
|
1708
|
-
'#
|
|
1709
|
-
'##
|
|
1710
|
-
'##
|
|
1711
|
-
'##
|
|
1712
|
-
'##
|
|
1713
|
-
'##
|
|
1714
|
-
'##
|
|
1715
|
-
'##
|
|
1716
|
-
'
|
|
1748
|
+
'# <Feature> Design',
|
|
1749
|
+
'## Summary',
|
|
1750
|
+
'## Goals',
|
|
1751
|
+
'## Non-Goals',
|
|
1752
|
+
'## User Experience / Command Behavior',
|
|
1753
|
+
'## Architecture',
|
|
1754
|
+
'## Data / State Model',
|
|
1755
|
+
'## Safety Rules',
|
|
1756
|
+
'## Requirements',
|
|
1757
|
+
'## Risks and Mitigations',
|
|
1758
|
+
'## Testing / Validation',
|
|
1759
|
+
'Make it concrete, scoped, and suitable for turning into a sub-agent implementation plan.'
|
|
1717
1760
|
].join('\n');
|
|
1718
1761
|
|
|
1719
1762
|
const result = await createChatCompletion({
|
|
@@ -1914,6 +1957,11 @@ function buildRuntimeStateSnapshot({ currentSession, config, model, executionMod
|
|
|
1914
1957
|
value: currentSession?.planState?.status === 'pending_approval',
|
|
1915
1958
|
enumerable: false,
|
|
1916
1959
|
writable: false
|
|
1960
|
+
},
|
|
1961
|
+
pendingReflectSkill: {
|
|
1962
|
+
value: currentSession?.planState?.status === 'pending_reflect_skill',
|
|
1963
|
+
enumerable: false,
|
|
1964
|
+
writable: false
|
|
1917
1965
|
}
|
|
1918
1966
|
});
|
|
1919
1967
|
return snapshot;
|
|
@@ -1940,6 +1988,10 @@ function hasPendingPlanApproval(session) {
|
|
|
1940
1988
|
return session?.planState?.status === 'pending_approval';
|
|
1941
1989
|
}
|
|
1942
1990
|
|
|
1991
|
+
function hasPendingReflectSkill(session) {
|
|
1992
|
+
return session?.planState?.status === 'pending_reflect_skill';
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1943
1995
|
function isApprovalText(text = '') {
|
|
1944
1996
|
const value = String(text || '').trim().toLowerCase();
|
|
1945
1997
|
if (!value) return false;
|
|
@@ -1976,6 +2028,28 @@ function buildPendingPlanApprovalMessage(planState) {
|
|
|
1976
2028
|
return lines.join('\n');
|
|
1977
2029
|
}
|
|
1978
2030
|
|
|
2031
|
+
function buildPendingReflectSkillMessage(reflectState) {
|
|
2032
|
+
const candidates = Array.isArray(reflectState?.candidates) ? reflectState.candidates : [];
|
|
2033
|
+
if (candidates.length === 0) {
|
|
2034
|
+
return 'Reflect found no reusable skill candidate.';
|
|
2035
|
+
}
|
|
2036
|
+
const lines = [
|
|
2037
|
+
'Reflect skill draft pending.',
|
|
2038
|
+
`Scope: ${reflectState?.targetScope || 'project'}`
|
|
2039
|
+
];
|
|
2040
|
+
for (const candidate of candidates) {
|
|
2041
|
+
lines.push('');
|
|
2042
|
+
lines.push(`[${candidate.id || 1}] ${candidate.name}`);
|
|
2043
|
+
lines.push(`Confidence: ${Number(candidate.confidence ?? 0.75).toFixed(2)}`);
|
|
2044
|
+
lines.push(`Target: ${candidate.targetPath || '-'}`);
|
|
2045
|
+
lines.push('');
|
|
2046
|
+
lines.push(String(candidate.content || '').trim());
|
|
2047
|
+
}
|
|
2048
|
+
lines.push('');
|
|
2049
|
+
lines.push('Use /yes to write this skill, /edit <feedback> to revise it, or /no to discard it.');
|
|
2050
|
+
return lines.join('\n');
|
|
2051
|
+
}
|
|
2052
|
+
|
|
1979
2053
|
function buildApprovedPlanExecutionPrompt(planState, approvalText = '') {
|
|
1980
2054
|
const requirementPacket = buildGoalRequirementPacket(planState?.goal || '', 'coder');
|
|
1981
2055
|
const lines = [
|
|
@@ -2375,6 +2449,12 @@ async function runSubAgentTask({
|
|
|
2375
2449
|
}
|
|
2376
2450
|
} catch {}
|
|
2377
2451
|
}
|
|
2452
|
+
if (
|
|
2453
|
+
role !== 'summarizer' &&
|
|
2454
|
+
['assistant:start', 'assistant:delta', 'assistant:response', 'assistant:tool_call_delta'].includes(String(evt?.type || ''))
|
|
2455
|
+
) {
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2378
2458
|
if (onAgentEvent) onAgentEvent(evt);
|
|
2379
2459
|
};
|
|
2380
2460
|
const roleAllowedTools = ROLE_TOOL_POLICY[role];
|
|
@@ -2503,7 +2583,14 @@ async function executePlanWithSubAgents({
|
|
|
2503
2583
|
);
|
|
2504
2584
|
}
|
|
2505
2585
|
|
|
2506
|
-
if (stepRecord.failed && i < steps.length - 1)
|
|
2586
|
+
if (stepRecord.failed && i < steps.length - 1) {
|
|
2587
|
+
const summarizerIndex = steps.findIndex((candidate, index) => index > i && candidate.role === 'summarizer');
|
|
2588
|
+
if (summarizerIndex > i) {
|
|
2589
|
+
i = summarizerIndex - 1;
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
break;
|
|
2593
|
+
}
|
|
2507
2594
|
}
|
|
2508
2595
|
|
|
2509
2596
|
const summaryLines = [];
|
|
@@ -2590,8 +2677,9 @@ async function buildAutoPlanAndRun({
|
|
|
2590
2677
|
content: [
|
|
2591
2678
|
'Create an execution plan and assign best sub-agent role for each step.',
|
|
2592
2679
|
'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.',
|
|
2593
|
-
'The available roles are planner, coder, reviewer, tester, and summarizer. Use only the roles the task actually needs.',
|
|
2594
|
-
'The summarizer
|
|
2680
|
+
'The available roles are planner, coder, reviewer, tester, and summarizer. Use only the non-summary roles the task actually needs.',
|
|
2681
|
+
'Always include a summarizer as the final step. The summarizer synthesizes prior step results without re-analyzing.',
|
|
2682
|
+
'Planner, coder, reviewer, and tester steps should write detailed step results, not final summaries.',
|
|
2595
2683
|
`Task class: ${normalizedTaskClass}`,
|
|
2596
2684
|
'Before choosing roles, decide whether the request is advisory, implementation, or verification-heavy.',
|
|
2597
2685
|
requirementPacket,
|
|
@@ -2708,7 +2796,8 @@ async function revisePendingPlanWithModel({
|
|
|
2708
2796
|
buildAutoPlanPlannerGuidance(),
|
|
2709
2797
|
'You are revising an existing plan based on explicit user feedback.',
|
|
2710
2798
|
'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.',
|
|
2711
|
-
'Keep roles minimal and only include steps that materially help the goal.'
|
|
2799
|
+
'Keep roles minimal and only include steps that materially help the goal.',
|
|
2800
|
+
'Always keep a summarizer as the final step.'
|
|
2712
2801
|
].join('\n');
|
|
2713
2802
|
const result = await createChatCompletion({
|
|
2714
2803
|
sdkProvider: config.sdk?.provider,
|
|
@@ -2782,6 +2871,44 @@ async function handleShellInput(shellText, config) {
|
|
|
2782
2871
|
return { text: chunks.join('\n') };
|
|
2783
2872
|
}
|
|
2784
2873
|
|
|
2874
|
+
function formatHistoryTimestamp(value) {
|
|
2875
|
+
const raw = String(value || '').trim();
|
|
2876
|
+
if (!raw) return 'updated unknown';
|
|
2877
|
+
const parsed = new Date(raw);
|
|
2878
|
+
if (Number.isNaN(parsed.getTime())) return `updated ${raw}`;
|
|
2879
|
+
return `updated ${parsed.toISOString().slice(0, 16).replace('T', ' ')}`;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
function compactHistoryPreview(value, maxChars = 72) {
|
|
2883
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
2884
|
+
if (!text) return '(no preview)';
|
|
2885
|
+
if (text.length <= maxChars) return text;
|
|
2886
|
+
return `${text.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
function formatHistoryList({ currentSession, sessions }) {
|
|
2890
|
+
const currentMessages = Array.isArray(currentSession?.messages) ? currentSession.messages.length : 0;
|
|
2891
|
+
const lines = [
|
|
2892
|
+
`Current session ${currentSession.id}`,
|
|
2893
|
+
`Messages ${currentMessages}`,
|
|
2894
|
+
'',
|
|
2895
|
+
'Recent sessions'
|
|
2896
|
+
];
|
|
2897
|
+
|
|
2898
|
+
for (const [index, session] of sessions.entries()) {
|
|
2899
|
+
const count = Number(session.messageCount || 0);
|
|
2900
|
+
lines.push(
|
|
2901
|
+
`${index + 1}. ${session.id}`,
|
|
2902
|
+
` ${count} ${count === 1 ? 'msg' : 'msgs'} | ${formatHistoryTimestamp(session.updatedAt)}`,
|
|
2903
|
+
` ${compactHistoryPreview(session.preview)}`,
|
|
2904
|
+
` resume: /history resume ${session.id}`
|
|
2905
|
+
);
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
lines.push('', 'Tip: use /history resume <session_id>');
|
|
2909
|
+
return lines.join('\n');
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2785
2912
|
export async function createChatRuntime({
|
|
2786
2913
|
session,
|
|
2787
2914
|
config: initialConfig,
|
|
@@ -2830,6 +2957,13 @@ export async function createChatRuntime({
|
|
|
2830
2957
|
executionMode = 'plan';
|
|
2831
2958
|
}
|
|
2832
2959
|
const commands = await loadCommandsAndSkills();
|
|
2960
|
+
const reloadCommandsAndSkills = async () => {
|
|
2961
|
+
const next = await loadCommandsAndSkills();
|
|
2962
|
+
commands.clear();
|
|
2963
|
+
for (const [name, command] of next.entries()) {
|
|
2964
|
+
commands.set(name, command);
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2833
2967
|
|
|
2834
2968
|
// Set up tool result store under session directory
|
|
2835
2969
|
const sessionResultsDir = path.join(getSessionsDir(), String(currentSession.id));
|
|
@@ -2940,6 +3074,7 @@ export async function createChatRuntime({
|
|
|
2940
3074
|
{ name: 'config', description: completionCopy.commands.config },
|
|
2941
3075
|
{ name: 'memory', description: completionCopy.commands.memory },
|
|
2942
3076
|
{ name: 'dream', description: completionCopy.commands.dream },
|
|
3077
|
+
{ name: 'reflect', description: completionCopy.commands.reflect },
|
|
2943
3078
|
{ name: 'history', description: completionCopy.commands.history },
|
|
2944
3079
|
{ name: 'debug', description: completionCopy.commands.debug },
|
|
2945
3080
|
{ name: 'retry', description: completionCopy.commands.retry },
|
|
@@ -2948,7 +3083,7 @@ export async function createChatRuntime({
|
|
|
2948
3083
|
];
|
|
2949
3084
|
const out = [];
|
|
2950
3085
|
for (const cmd of commands.values()) {
|
|
2951
|
-
if (cmd.metadata.type === 'skill' && config
|
|
3086
|
+
if (cmd.metadata.type === 'skill' && !isSkillEnabled(config, cmd.name, cmd)) {
|
|
2952
3087
|
continue;
|
|
2953
3088
|
}
|
|
2954
3089
|
out.push({
|
|
@@ -2991,6 +3126,7 @@ export async function createChatRuntime({
|
|
|
2991
3126
|
const agentTemplates = ['/agents list', '/agents run planner <task>', '/agents run coder <task>', '/agents run reviewer <task>', '/agents run tester <task>', '/agents run summarizer <task>'];
|
|
2992
3127
|
const debugTemplates = ['/debug keys on', '/debug keys off', '/debug keys status'];
|
|
2993
3128
|
const dreamTemplates = ['/dream', '/dream --dry-run', '/dream --scope=project', '/dream --scope=global'];
|
|
3129
|
+
const reflectTemplates = ['/reflect', '/reflect --scope=global <request>', '/reflect <request>'];
|
|
2994
3130
|
const compactTemplates = compactOptions.map((opt) => `/compact ${opt}`);
|
|
2995
3131
|
const slashTemplates = [
|
|
2996
3132
|
...configTemplates,
|
|
@@ -3003,6 +3139,7 @@ export async function createChatRuntime({
|
|
|
3003
3139
|
...agentTemplates,
|
|
3004
3140
|
...debugTemplates,
|
|
3005
3141
|
...dreamTemplates,
|
|
3142
|
+
...reflectTemplates,
|
|
3006
3143
|
...compactTemplates,
|
|
3007
3144
|
'/retry',
|
|
3008
3145
|
'/status'
|
|
@@ -3070,6 +3207,7 @@ export async function createChatRuntime({
|
|
|
3070
3207
|
for (const template of agentTemplates) registerSuggestion(template, completionCopy.generic.agentCommand);
|
|
3071
3208
|
for (const template of debugTemplates) registerSuggestion(template, completionCopy.generic.debugCommand);
|
|
3072
3209
|
for (const template of dreamTemplates) registerSuggestion(template, completionCopy.generic.dreamCommand);
|
|
3210
|
+
for (const template of reflectTemplates) registerSuggestion(template, completionCopy.generic.reflectCommand);
|
|
3073
3211
|
for (const template of compactTemplates) registerSuggestion(template, completionCopy.generic.compactCommand);
|
|
3074
3212
|
registerSuggestion('/retry', completionCopy.generic.retryCommand);
|
|
3075
3213
|
registerSuggestion('/status', completionCopy.generic.statusCommand);
|
|
@@ -3312,6 +3450,96 @@ export async function createChatRuntime({
|
|
|
3312
3450
|
await saveSession(currentSession);
|
|
3313
3451
|
};
|
|
3314
3452
|
|
|
3453
|
+
const captureCompactSummary = async ({ summary, mode, beforeTokens, afterTokens }) => {
|
|
3454
|
+
if (config?.memory?.enabled === false || config?.memory?.auto_capture === false) return null;
|
|
3455
|
+
const normalizedSummary = String(summary || '').trim();
|
|
3456
|
+
if (!normalizedSummary) return null;
|
|
3457
|
+
const entrySummary = `Context compacted (${mode}): ${beforeTokens} -> ${afterTokens} tokens`;
|
|
3458
|
+
return captureToInbox({
|
|
3459
|
+
scope: 'repo',
|
|
3460
|
+
type: 'observation',
|
|
3461
|
+
summary: entrySummary,
|
|
3462
|
+
details: normalizedSummary,
|
|
3463
|
+
tags: ['compact', 'context-summary'],
|
|
3464
|
+
source: 'auto-compact'
|
|
3465
|
+
}).catch(() => null);
|
|
3466
|
+
};
|
|
3467
|
+
|
|
3468
|
+
const shouldAutoCaptureUserPrompt = (text) => {
|
|
3469
|
+
if (config?.memory?.enabled === false || config?.memory?.auto_capture === false) return false;
|
|
3470
|
+
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
3471
|
+
if (value.length < 12) return false;
|
|
3472
|
+
const actionPattern =
|
|
3473
|
+
/\b(add|build|fix|implement|change|update|refactor|test|debug|remember|capture|continue|review)\b|实现|增加|添加|修复|修改|更新|重构|测试|调试|记住|继续|检查|沉淀|捕获/i;
|
|
3474
|
+
return actionPattern.test(value);
|
|
3475
|
+
};
|
|
3476
|
+
|
|
3477
|
+
const classifyDirectMemoryPrompt = (text) => {
|
|
3478
|
+
if (config?.memory?.enabled === false || config?.memory?.auto_capture === false) return null;
|
|
3479
|
+
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
3480
|
+
if (value.length < 6) return null;
|
|
3481
|
+
const userPreferencePattern =
|
|
3482
|
+
/(?:记住|请记住|以后|后续|我偏好|我的偏好|我喜欢|我习惯|不要再|别再|always remember|remember that|i prefer|my preference|don't|do not)/i;
|
|
3483
|
+
if (!userPreferencePattern.test(value)) return null;
|
|
3484
|
+
const projectPattern = /(?:本项目|这个项目|当前项目|这个仓库|当前仓库|repo|repository|project)/i;
|
|
3485
|
+
const isProject = projectPattern.test(value);
|
|
3486
|
+
return {
|
|
3487
|
+
scope: isProject ? 'project' : 'user',
|
|
3488
|
+
kind: isProject ? 'workflow' : 'preference',
|
|
3489
|
+
content: value
|
|
3490
|
+
};
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3493
|
+
const saveDirectMemoryPrompt = async (text) => {
|
|
3494
|
+
const direct = classifyDirectMemoryPrompt(text);
|
|
3495
|
+
if (!direct) return null;
|
|
3496
|
+
const existing = await listMemories({
|
|
3497
|
+
scope: direct.scope,
|
|
3498
|
+
workspaceRoot: process.cwd()
|
|
3499
|
+
}).catch(() => []);
|
|
3500
|
+
const directText = String(direct.content || '').toLowerCase();
|
|
3501
|
+
const directTokens = new Set(directText.match(/[a-z0-9_\u4e00-\u9fa5]+/g) || []);
|
|
3502
|
+
const directAsciiTokens = new Set(directText.match(/[a-z0-9_]{4,}/g) || []);
|
|
3503
|
+
const overlapsExisting = existing.some((item) => {
|
|
3504
|
+
const existingText = `${item.content || ''} ${item.summary || ''}`.toLowerCase();
|
|
3505
|
+
for (const token of directAsciiTokens) {
|
|
3506
|
+
if (existingText.includes(token)) return true;
|
|
3507
|
+
}
|
|
3508
|
+
let hits = 0;
|
|
3509
|
+
for (const token of directTokens) {
|
|
3510
|
+
if (token.length < 2) continue;
|
|
3511
|
+
if (existingText.includes(token)) hits += 1;
|
|
3512
|
+
if (hits >= 2) return true;
|
|
3513
|
+
}
|
|
3514
|
+
return false;
|
|
3515
|
+
});
|
|
3516
|
+
if (overlapsExisting) return null;
|
|
3517
|
+
return rememberMemory({
|
|
3518
|
+
scope: direct.scope,
|
|
3519
|
+
content: direct.content,
|
|
3520
|
+
kind: direct.kind,
|
|
3521
|
+
summary: direct.content.slice(0, 80),
|
|
3522
|
+
source: 'auto-user-directive',
|
|
3523
|
+
replaceSimilar: true,
|
|
3524
|
+
workspaceRoot: process.cwd(),
|
|
3525
|
+
config
|
|
3526
|
+
}).catch(() => null);
|
|
3527
|
+
};
|
|
3528
|
+
|
|
3529
|
+
const captureUserPromptForDream = async (text) => {
|
|
3530
|
+
if (classifyDirectMemoryPrompt(text)) return null;
|
|
3531
|
+
if (!shouldAutoCaptureUserPrompt(text)) return null;
|
|
3532
|
+
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
3533
|
+
return captureToInbox({
|
|
3534
|
+
scope: 'repo',
|
|
3535
|
+
type: 'observation',
|
|
3536
|
+
summary: `User task: ${value.slice(0, 120)}`,
|
|
3537
|
+
details: value,
|
|
3538
|
+
tags: ['user-prompt'],
|
|
3539
|
+
source: 'auto-user-prompt'
|
|
3540
|
+
}).catch(() => null);
|
|
3541
|
+
};
|
|
3542
|
+
|
|
3315
3543
|
const buildActiveSystemPrompt = async () => {
|
|
3316
3544
|
const soulPrompt = await buildSystemPromptWithSoul(baseSystemPrompt, config);
|
|
3317
3545
|
const memorySnapshot = await buildMemorySnapshot({
|
|
@@ -3426,7 +3654,7 @@ export async function createChatRuntime({
|
|
|
3426
3654
|
if (parsedInput.command === 'help') {
|
|
3427
3655
|
return {
|
|
3428
3656
|
type: 'system',
|
|
3429
|
-
text: 'Commands: /help /exit /new /stop /commands /status /mode /compact /checkpoint /spec /plan /yes /edit /reject /agents /config /memory /capture /inbox /dream /history /debug /retry /<custom> !<shell>'
|
|
3657
|
+
text: 'Commands: /help /exit /new /stop /commands /status /mode /compact /checkpoint /spec /plan /yes /no /edit /reject /agents /config /memory /capture /inbox /dream /reflect /history /debug /retry /<custom> !<shell>'
|
|
3430
3658
|
};
|
|
3431
3659
|
}
|
|
3432
3660
|
if (parsedInput.command === 'status') {
|
|
@@ -3452,6 +3680,27 @@ export async function createChatRuntime({
|
|
|
3452
3680
|
return { type: 'system', text };
|
|
3453
3681
|
}
|
|
3454
3682
|
if (parsedInput.command === 'yes') {
|
|
3683
|
+
if (hasPendingReflectSkill(currentSession)) {
|
|
3684
|
+
const state = { ...currentSession.planState };
|
|
3685
|
+
const candidate = Array.isArray(state.candidates) ? state.candidates[0] : null;
|
|
3686
|
+
if (!candidate) {
|
|
3687
|
+
currentSession.planState = null;
|
|
3688
|
+
const text = 'No reflect skill draft to write.';
|
|
3689
|
+
await persistLocalExchange(line, text, { includeUser: false });
|
|
3690
|
+
return { type: 'system', text };
|
|
3691
|
+
}
|
|
3692
|
+
const written = await writeReflectSkillDraft({
|
|
3693
|
+
draft: candidate,
|
|
3694
|
+
scope: state.targetScope || 'project',
|
|
3695
|
+
workspaceRoot: process.cwd()
|
|
3696
|
+
});
|
|
3697
|
+
currentSession.planState = null;
|
|
3698
|
+
executionMode = 'auto';
|
|
3699
|
+
await reloadCommandsAndSkills();
|
|
3700
|
+
const text = `Reflect skill written and loaded: /${written.draft.name}\nPath: ${written.filePath}`;
|
|
3701
|
+
await persistLocalExchange(line, text, { includeUser: false });
|
|
3702
|
+
return { type: 'system', text };
|
|
3703
|
+
}
|
|
3455
3704
|
if (!hasPendingPlanApproval(currentSession)) {
|
|
3456
3705
|
return { type: 'system', text: 'No pending plan approval. Use /plan auto <goal> first.' };
|
|
3457
3706
|
}
|
|
@@ -3475,6 +3724,35 @@ export async function createChatRuntime({
|
|
|
3475
3724
|
return { type: 'assistant', text: result.text, aborted: !!result.aborted };
|
|
3476
3725
|
}
|
|
3477
3726
|
if (parsedInput.command === 'edit') {
|
|
3727
|
+
if (hasPendingReflectSkill(currentSession)) {
|
|
3728
|
+
const feedback = parsedInput.args.join(' ').trim();
|
|
3729
|
+
if (!feedback) {
|
|
3730
|
+
return { type: 'system', text: 'Usage: /edit <feedback>' };
|
|
3731
|
+
}
|
|
3732
|
+
const state = { ...currentSession.planState };
|
|
3733
|
+
const previousDraft = Array.isArray(state.candidates) ? state.candidates[0] : null;
|
|
3734
|
+
const drafts = await buildReflectSkillDraft({
|
|
3735
|
+
request: state.request || '',
|
|
3736
|
+
scope: state.targetScope || 'project',
|
|
3737
|
+
session: currentSession,
|
|
3738
|
+
config,
|
|
3739
|
+
model,
|
|
3740
|
+
systemPrompt: activeReplySystemPrompt,
|
|
3741
|
+
previousDraft,
|
|
3742
|
+
feedback
|
|
3743
|
+
});
|
|
3744
|
+
currentSession.planState = {
|
|
3745
|
+
...state,
|
|
3746
|
+
candidates: attachReflectTargets({
|
|
3747
|
+
candidates: drafts,
|
|
3748
|
+
scope: state.targetScope || 'project',
|
|
3749
|
+
workspaceRoot: process.cwd()
|
|
3750
|
+
})
|
|
3751
|
+
};
|
|
3752
|
+
const text = `Reflect skill draft revised.\n${buildPendingReflectSkillMessage(currentSession.planState)}`;
|
|
3753
|
+
await persistLocalExchange(line, text);
|
|
3754
|
+
return { type: 'system', text };
|
|
3755
|
+
}
|
|
3478
3756
|
if (!hasPendingPlanApproval(currentSession)) {
|
|
3479
3757
|
return { type: 'system', text: 'No pending plan approval. Use /plan auto <goal> first.' };
|
|
3480
3758
|
}
|
|
@@ -3495,6 +3773,23 @@ export async function createChatRuntime({
|
|
|
3495
3773
|
await persistLocalExchange(line, text);
|
|
3496
3774
|
return { type: 'system', text };
|
|
3497
3775
|
}
|
|
3776
|
+
if (parsedInput.command === 'no') {
|
|
3777
|
+
if (hasPendingReflectSkill(currentSession)) {
|
|
3778
|
+
currentSession.planState = null;
|
|
3779
|
+
executionMode = 'auto';
|
|
3780
|
+
const text = 'Reflect skill draft discarded.';
|
|
3781
|
+
await persistLocalExchange(line, text, { includeUser: false });
|
|
3782
|
+
return { type: 'system', text };
|
|
3783
|
+
}
|
|
3784
|
+
if (hasPendingPlanApproval(currentSession)) {
|
|
3785
|
+
currentSession.planState = null;
|
|
3786
|
+
executionMode = 'auto';
|
|
3787
|
+
const text = 'Pending plan rejected and cleared.';
|
|
3788
|
+
await persistLocalExchange(line, text, { includeUser: false });
|
|
3789
|
+
return { type: 'system', text };
|
|
3790
|
+
}
|
|
3791
|
+
return { type: 'system', text: 'No pending reflect skill draft.' };
|
|
3792
|
+
}
|
|
3498
3793
|
if (parsedInput.command === 'reject') {
|
|
3499
3794
|
if (!hasPendingPlanApproval(currentSession)) {
|
|
3500
3795
|
return { type: 'system', text: 'No pending plan approval.' };
|
|
@@ -3753,13 +4048,9 @@ export async function createChatRuntime({
|
|
|
3753
4048
|
messageCount: Number(s.messageCount || 0)
|
|
3754
4049
|
}));
|
|
3755
4050
|
if (sessions.length === 0) return { type: 'system', text: 'No sessions found' };
|
|
3756
|
-
const rows = sessions.map(
|
|
3757
|
-
(s, idx) =>
|
|
3758
|
-
`${idx + 1}. ${s.id} | msgs:${s.messageCount} | updated:${s.updatedAt || '-'}${s.preview ? ` | ${s.preview}` : ''}`
|
|
3759
|
-
);
|
|
3760
4051
|
return {
|
|
3761
4052
|
type: 'system',
|
|
3762
|
-
text:
|
|
4053
|
+
text: formatHistoryList({ currentSession, sessions })
|
|
3763
4054
|
};
|
|
3764
4055
|
}
|
|
3765
4056
|
if (sub === 'current') {
|
|
@@ -3902,6 +4193,37 @@ export async function createChatRuntime({
|
|
|
3902
4193
|
return { type: 'system', text: `Dream failed: ${err.message}` };
|
|
3903
4194
|
}
|
|
3904
4195
|
}
|
|
4196
|
+
if (parsedInput.command === 'reflect') {
|
|
4197
|
+
const parsedReflect = parseReflectScope(parsedInput.args);
|
|
4198
|
+
const drafts = await buildReflectSkillDraft({
|
|
4199
|
+
request: parsedReflect.request,
|
|
4200
|
+
scope: parsedReflect.scope,
|
|
4201
|
+
session: currentSession,
|
|
4202
|
+
config,
|
|
4203
|
+
model,
|
|
4204
|
+
systemPrompt: activeReplySystemPrompt
|
|
4205
|
+
});
|
|
4206
|
+
const candidates = attachReflectTargets({
|
|
4207
|
+
candidates: drafts,
|
|
4208
|
+
scope: parsedReflect.scope,
|
|
4209
|
+
workspaceRoot: process.cwd()
|
|
4210
|
+
});
|
|
4211
|
+
if (candidates.length === 0) {
|
|
4212
|
+
const text = 'Reflect found no reusable skill candidate.';
|
|
4213
|
+
await persistLocalExchange(line, text);
|
|
4214
|
+
return { type: 'system', text };
|
|
4215
|
+
}
|
|
4216
|
+
currentSession.planState = {
|
|
4217
|
+
status: 'pending_reflect_skill',
|
|
4218
|
+
source: 'reflect',
|
|
4219
|
+
targetScope: parsedReflect.scope,
|
|
4220
|
+
request: parsedReflect.request,
|
|
4221
|
+
candidates
|
|
4222
|
+
};
|
|
4223
|
+
const text = buildPendingReflectSkillMessage(currentSession.planState);
|
|
4224
|
+
await persistLocalExchange(line, text);
|
|
4225
|
+
return { type: 'system', text };
|
|
4226
|
+
}
|
|
3905
4227
|
if (parsedInput.command === 'retry') {
|
|
3906
4228
|
const lastUser = [...currentSession.messages].reverse().find((m) => m.role === 'user');
|
|
3907
4229
|
if (!lastUser?.content) {
|
|
@@ -4005,6 +4327,12 @@ export async function createChatRuntime({
|
|
|
4005
4327
|
compactState.backupMessages = structuredClone(currentSession.messages);
|
|
4006
4328
|
currentSession.messages = result.compacted.map((m) => ({ ...m, at: new Date().toISOString() }));
|
|
4007
4329
|
await saveSession(currentSession);
|
|
4330
|
+
await captureCompactSummary({
|
|
4331
|
+
summary: result.summary,
|
|
4332
|
+
mode: compactState.mode,
|
|
4333
|
+
beforeTokens,
|
|
4334
|
+
afterTokens
|
|
4335
|
+
});
|
|
4008
4336
|
await persistLocalExchange(line, report, { includeUser: false });
|
|
4009
4337
|
return { type: 'system', text: report };
|
|
4010
4338
|
}
|
|
@@ -4021,7 +4349,7 @@ export async function createChatRuntime({
|
|
|
4021
4349
|
if (!custom) {
|
|
4022
4350
|
return { type: 'system', text: `Unknown slash command: /${parsedInput.command}` };
|
|
4023
4351
|
}
|
|
4024
|
-
if (custom.metadata.type === 'skill' && config
|
|
4352
|
+
if (custom.metadata.type === 'skill' && !isSkillEnabled(config, custom.name, custom)) {
|
|
4025
4353
|
return { type: 'system', text: `Skill is disabled: ${custom.name}` };
|
|
4026
4354
|
}
|
|
4027
4355
|
|
|
@@ -4125,6 +4453,12 @@ export async function createChatRuntime({
|
|
|
4125
4453
|
at: new Date().toISOString()
|
|
4126
4454
|
}));
|
|
4127
4455
|
await saveSession(currentSession);
|
|
4456
|
+
await captureCompactSummary({
|
|
4457
|
+
summary: autoResult.summary,
|
|
4458
|
+
mode: compactState.mode,
|
|
4459
|
+
beforeTokens: currentTokens,
|
|
4460
|
+
afterTokens: estimateMessagesTokens(currentSession.messages)
|
|
4461
|
+
});
|
|
4128
4462
|
if (onAgentEvent) {
|
|
4129
4463
|
onAgentEvent({
|
|
4130
4464
|
type: 'compact:auto',
|
|
@@ -4165,7 +4499,7 @@ export async function createChatRuntime({
|
|
|
4165
4499
|
return { type: 'system', text };
|
|
4166
4500
|
}
|
|
4167
4501
|
|
|
4168
|
-
const selectedAutoSkills = autoRoute.selectedSkills.filter((name) => isSkillEnabled(config, name));
|
|
4502
|
+
const selectedAutoSkills = autoRoute.selectedSkills.filter((name) => isSkillEnabled(config, name, commands.get(name)));
|
|
4169
4503
|
if (selectedAutoSkills.length > 0 && onAgentEvent) {
|
|
4170
4504
|
onAgentEvent({
|
|
4171
4505
|
type: 'skill:auto',
|
|
@@ -4188,6 +4522,8 @@ export async function createChatRuntime({
|
|
|
4188
4522
|
executionMode,
|
|
4189
4523
|
signal
|
|
4190
4524
|
});
|
|
4525
|
+
await saveDirectMemoryPrompt(expandedText);
|
|
4526
|
+
await captureUserPromptForDream(expandedText);
|
|
4191
4527
|
return { type: 'assistant', text: result.text, aborted: !!result.aborted };
|
|
4192
4528
|
};
|
|
4193
4529
|
|