codemini-cli 0.5.10 → 0.5.11
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 +242 -242
- package/README.md +588 -588
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-7HL7yft8.js → highlighted-body-OFNGDK62-CANOG7Xg.js} +1 -1
- package/codemini-web/dist/assets/{index-BK75hMb2.js → index-B71xykPM.js} +108 -108
- package/codemini-web/dist/assets/index-Dkq1DdDX.css +2 -0
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-Z_w7M93P.js +1 -0
- package/codemini-web/dist/index.html +23 -23
- package/codemini-web/lib/approval-manager.js +32 -32
- package/codemini-web/lib/runtime-bridge.js +17 -11
- package/codemini-web/server.js +534 -205
- package/deployment.md +212 -212
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +77 -77
- package/skills/codemini.skills.json +40 -40
- package/skills/grill-me/SKILL.md +30 -30
- package/skills/superpowers-lite/SKILL.md +82 -82
- package/src/cli.js +74 -74
- package/src/commands/chat.js +210 -210
- package/src/commands/run.js +313 -313
- package/src/commands/skill.js +438 -304
- package/src/commands/web.js +57 -57
- package/src/core/agent-loop.js +980 -980
- package/src/core/ast.js +309 -307
- package/src/core/chat-runtime.js +6261 -6253
- package/src/core/command-evaluator.js +72 -72
- package/src/core/command-loader.js +311 -311
- package/src/core/command-policy.js +301 -301
- package/src/core/command-risk.js +156 -156
- package/src/core/config-store.js +289 -289
- package/src/core/constants.js +18 -1
- package/src/core/context-compact.js +365 -365
- package/src/core/default-system-prompt.js +114 -107
- package/src/core/dream-audit.js +105 -105
- package/src/core/dream-consolidate.js +229 -229
- package/src/core/dream-evaluator.js +185 -185
- package/src/core/fff-adapter.js +383 -383
- package/src/core/memory-store.js +543 -543
- package/src/core/project-index.js +737 -548
- package/src/core/project-instructions.js +98 -98
- package/src/core/provider/anthropic.js +514 -514
- package/src/core/provider/openai-compatible.js +501 -501
- package/src/core/reflect-skill.js +178 -178
- package/src/core/reply-language.js +40 -40
- package/src/core/session-store.js +474 -474
- package/src/core/shell-profile.js +237 -237
- package/src/core/shell.js +323 -323
- package/src/core/soul.js +69 -69
- package/src/core/system-prompt-composer.js +52 -52
- package/src/core/tool-args.js +199 -154
- package/src/core/tool-output.js +184 -184
- package/src/core/tool-result-store.js +206 -206
- package/src/core/tools.js +3024 -2893
- package/src/core/version.js +11 -11
- package/src/tui/chat-app.js +5171 -5171
- package/src/tui/tool-activity/presenters/misc.js +30 -30
- package/src/tui/tool-activity/presenters/system.js +20 -20
- package/templates/project-requirements/report-shell.html +582 -582
- package/codemini-web/dist/assets/index-BSdIdn3L.css +0 -2
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-Dg9qh8mg.js +0 -1
|
@@ -1,229 +1,229 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getMemoryBucketMaintenance,
|
|
3
|
-
listMemories,
|
|
4
|
-
listInbox,
|
|
5
|
-
archiveEntry,
|
|
6
|
-
promoteMemory,
|
|
7
|
-
replaceMemoryBucket
|
|
8
|
-
} from './memory-store.js';
|
|
9
|
-
import { writeDreamAuditReport } from './dream-audit.js';
|
|
10
|
-
import { evaluateInboxBatch, evaluateMemoryMaintenance } from './dream-evaluator.js';
|
|
11
|
-
|
|
12
|
-
const LONGTERM_TYPES = new Set(['preference', 'pattern', 'win', 'decision']);
|
|
13
|
-
const OPERATIONAL_TYPES = new Set(['correction', 'failure', 'gap', 'observation']);
|
|
14
|
-
|
|
15
|
-
function normalizeText(value) {
|
|
16
|
-
return String(value || '').trim().toLowerCase();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function chooseLifecycle(type) {
|
|
20
|
-
const value = normalizeText(type);
|
|
21
|
-
if (LONGTERM_TYPES.has(value)) return 'longterm';
|
|
22
|
-
if (OPERATIONAL_TYPES.has(value)) return 'operational';
|
|
23
|
-
return 'operational';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function memoryContainsSummary(memory, summaryKey) {
|
|
27
|
-
const content = normalizeText(memory?.content);
|
|
28
|
-
const summary = normalizeText(memory?.summary);
|
|
29
|
-
return content.includes(summaryKey) || summary.includes(summaryKey);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function maintenanceScopes(scopeFilter) {
|
|
33
|
-
const scope = normalizeText(scopeFilter);
|
|
34
|
-
if (!scope) return ['user', 'global', 'project'];
|
|
35
|
-
if (scope === 'repo') return ['project'];
|
|
36
|
-
if (['user', 'global', 'project'].includes(scope)) return [scope];
|
|
37
|
-
return ['user', 'global', 'project'];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function runMemoryMaintenance({
|
|
41
|
-
dryRun = false,
|
|
42
|
-
scope = null,
|
|
43
|
-
workspaceRoot = process.cwd(),
|
|
44
|
-
config = {}
|
|
45
|
-
} = {}) {
|
|
46
|
-
const reports = [];
|
|
47
|
-
const filesChanged = [];
|
|
48
|
-
|
|
49
|
-
for (const memoryScope of maintenanceScopes(scope)) {
|
|
50
|
-
const maintenance = await getMemoryBucketMaintenance({ scope: memoryScope, workspaceRoot });
|
|
51
|
-
const items = await listMemories({ scope: memoryScope, workspaceRoot });
|
|
52
|
-
if (items.length === 0) {
|
|
53
|
-
reports.push({ scope: memoryScope, skipped: true, reason: 'empty' });
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (maintenance.fresh) {
|
|
57
|
-
reports.push({ scope: memoryScope, skipped: true, reason: 'already-maintained', itemCount: items.length });
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const evaluated = dryRun
|
|
62
|
-
? { items, archives: [] }
|
|
63
|
-
: await evaluateMemoryMaintenance({ scope: memoryScope, items, config, workspaceRoot });
|
|
64
|
-
if (evaluated.error) {
|
|
65
|
-
reports.push({ scope: memoryScope, skipped: true, reason: `maintenance-error: ${evaluated.error}`, itemCount: items.length });
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const nextItems = Array.isArray(evaluated.items) && evaluated.items.length > 0 ? evaluated.items : items;
|
|
69
|
-
const changed =
|
|
70
|
-
JSON.stringify(nextItems.map((item) => [item.kind, item.content, item.summary, item.lifecycle || ''])) !==
|
|
71
|
-
JSON.stringify(items.map((item) => [item.kind, item.content, item.summary, item.lifecycle || '']));
|
|
72
|
-
|
|
73
|
-
if (!dryRun) {
|
|
74
|
-
await replaceMemoryBucket({
|
|
75
|
-
scope: memoryScope,
|
|
76
|
-
items: nextItems,
|
|
77
|
-
workspaceRoot,
|
|
78
|
-
markMaintained: true
|
|
79
|
-
});
|
|
80
|
-
filesChanged.push({
|
|
81
|
-
file: memoryScope === 'project' ? 'memory/project/*.json' : `memory/${memoryScope}.json`,
|
|
82
|
-
why: changed
|
|
83
|
-
? `LLM-maintained ${items.length} item(s) into ${nextItems.length} item(s)`
|
|
84
|
-
: `Marked ${items.length} item(s) as maintained`
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
reports.push({
|
|
89
|
-
scope: memoryScope,
|
|
90
|
-
skipped: false,
|
|
91
|
-
before: items.length,
|
|
92
|
-
after: nextItems.length,
|
|
93
|
-
changed,
|
|
94
|
-
archives: evaluated.archives || [],
|
|
95
|
-
dryRun
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { reports, filesChanged };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export async function runDreamConsolidation({
|
|
103
|
-
dryRun = false,
|
|
104
|
-
scope = null,
|
|
105
|
-
workspaceRoot = process.cwd(),
|
|
106
|
-
config = {},
|
|
107
|
-
writeAudit = true
|
|
108
|
-
} = {}) {
|
|
109
|
-
const scopeFilter = scope || null;
|
|
110
|
-
const inbox = await listInbox({ scope: scopeFilter });
|
|
111
|
-
|
|
112
|
-
const [globalMemories, userMemories, projectMemories] = await Promise.all([
|
|
113
|
-
listMemories({ scope: 'global', workspaceRoot }),
|
|
114
|
-
listMemories({ scope: 'user', workspaceRoot }),
|
|
115
|
-
listMemories({ scope: 'project', workspaceRoot })
|
|
116
|
-
]);
|
|
117
|
-
const knownMemories = [...globalMemories, ...userMemories, ...projectMemories];
|
|
118
|
-
|
|
119
|
-
const promotions = [];
|
|
120
|
-
const rejections = [];
|
|
121
|
-
const archives = [];
|
|
122
|
-
const filesRead = ['memory/inbox/*', 'memory/global.json', 'memory/user.json', 'memory/project/*.json'];
|
|
123
|
-
const filesChanged = [];
|
|
124
|
-
|
|
125
|
-
/* ── Phase 1: 规则预过滤(快速剔除明显垃圾) ─────────────────── */
|
|
126
|
-
const candidates = [];
|
|
127
|
-
const seen = new Map();
|
|
128
|
-
|
|
129
|
-
for (const entry of inbox) {
|
|
130
|
-
const summaryKey = normalizeText(entry.summary);
|
|
131
|
-
if (!summaryKey) {
|
|
132
|
-
if (!dryRun) await archiveEntry(entry, 'invalid-summary', 'Summary is empty after normalization');
|
|
133
|
-
archives.push({ summary: String(entry.summary || ''), reason: 'invalid-summary' });
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (seen.has(summaryKey)) {
|
|
138
|
-
if (!dryRun) await archiveEntry(entry, 'duplicate', `Duplicate of ${seen.get(summaryKey)}`);
|
|
139
|
-
archives.push({ summary: entry.summary, reason: 'duplicate' });
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
seen.set(summaryKey, entry.id);
|
|
143
|
-
|
|
144
|
-
const alreadyKnown = knownMemories.some((memory) => memoryContainsSummary(memory, summaryKey));
|
|
145
|
-
if (alreadyKnown) {
|
|
146
|
-
if (!dryRun) await archiveEntry(entry, 'already-known', 'Already present in memory');
|
|
147
|
-
rejections.push({ summary: entry.summary, reason: 'already-known' });
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
candidates.push(entry);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (candidates.length > 0) {
|
|
155
|
-
/* ── Phase 2: LLM 批量评估(质量门控 + scope 分类 + 内容提炼) ── */
|
|
156
|
-
const llmResults = dryRun
|
|
157
|
-
? candidates.map((e) => ({ id: e.id, action: 'keep', scope: 'global', kind: e.type || 'observation', content: e.details || e.summary, summary: e.summary, confidence: 0.9 }))
|
|
158
|
-
: await evaluateInboxBatch({ entries: candidates, config, workspaceRoot });
|
|
159
|
-
|
|
160
|
-
const resultMap = new Map(llmResults.map((r) => [r.id, r]));
|
|
161
|
-
|
|
162
|
-
/* ── Phase 3: 按评估结果 promote 或 archive ─────────────────── */
|
|
163
|
-
for (const entry of candidates) {
|
|
164
|
-
const evaluation = resultMap.get(entry.id);
|
|
165
|
-
|
|
166
|
-
if (!evaluation || evaluation.action === 'discard') {
|
|
167
|
-
const reason = evaluation?.reason || 'LLM discarded';
|
|
168
|
-
if (!dryRun) await archiveEntry(entry, 'discarded-by-evaluator', reason);
|
|
169
|
-
rejections.push({ summary: entry.summary, reason: `evaluator-discard: ${reason}` });
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const promoteScope = evaluation.scope || 'global';
|
|
174
|
-
const lifecycle = chooseLifecycle(evaluation.kind);
|
|
175
|
-
const enrichedEntry = {
|
|
176
|
-
...entry,
|
|
177
|
-
/* 用 LLM 提炼后的内容覆盖原始报错 */
|
|
178
|
-
summary: evaluation.summary || entry.summary,
|
|
179
|
-
details: evaluation.content || entry.details || entry.summary,
|
|
180
|
-
type: evaluation.kind || entry.type || 'observation'
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
if (!dryRun) {
|
|
184
|
-
try {
|
|
185
|
-
await promoteMemory({
|
|
186
|
-
entry: enrichedEntry,
|
|
187
|
-
scope: promoteScope,
|
|
188
|
-
lifecycle,
|
|
189
|
-
workspaceRoot,
|
|
190
|
-
config,
|
|
191
|
-
confidence: evaluation.confidence || 0.8
|
|
192
|
-
});
|
|
193
|
-
filesChanged.push({ file: `memory/${promoteScope}.json`, why: `Promoted "${enrichedEntry.summary}" as ${lifecycle} (${promoteScope})` });
|
|
194
|
-
promotions.push({ summary: enrichedEntry.summary, scope: promoteScope, lifecycle, rationale: evaluation.kind, confidence: evaluation.confidence });
|
|
195
|
-
} catch (error) {
|
|
196
|
-
const reason = String(error?.message || error || 'promotion failed').slice(0, 180);
|
|
197
|
-
await archiveEntry(entry, 'promotion-failed', reason);
|
|
198
|
-
rejections.push({ summary: entry.summary, reason: `promotion-failed: ${reason}` });
|
|
199
|
-
archives.push({ summary: entry.summary, reason: 'promotion-failed' });
|
|
200
|
-
}
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
promotions.push({ summary: enrichedEntry.summary, scope: promoteScope, lifecycle, rationale: evaluation.kind, confidence: evaluation.confidence, dryRun: true });
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const maintenance = await runMemoryMaintenance({ dryRun, scope: scopeFilter, workspaceRoot, config });
|
|
209
|
-
filesChanged.push(...maintenance.filesChanged);
|
|
210
|
-
|
|
211
|
-
const report = {
|
|
212
|
-
timestamp: new Date().toISOString(),
|
|
213
|
-
filesRead,
|
|
214
|
-
filesChanged,
|
|
215
|
-
candidatesGenerated: inbox.length,
|
|
216
|
-
promotions,
|
|
217
|
-
rejections,
|
|
218
|
-
archives,
|
|
219
|
-
maintenance: maintenance.reports,
|
|
220
|
-
...(inbox.length === 0 ? { message: 'No inbox entries to consolidate; maintained existing memory buckets.' } : {})
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
if (!dryRun && writeAudit) {
|
|
224
|
-
const reportPath = await writeDreamAuditReport(report);
|
|
225
|
-
report.auditReport = reportPath;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return { ok: true, dryRun, ...report };
|
|
229
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
getMemoryBucketMaintenance,
|
|
3
|
+
listMemories,
|
|
4
|
+
listInbox,
|
|
5
|
+
archiveEntry,
|
|
6
|
+
promoteMemory,
|
|
7
|
+
replaceMemoryBucket
|
|
8
|
+
} from './memory-store.js';
|
|
9
|
+
import { writeDreamAuditReport } from './dream-audit.js';
|
|
10
|
+
import { evaluateInboxBatch, evaluateMemoryMaintenance } from './dream-evaluator.js';
|
|
11
|
+
|
|
12
|
+
const LONGTERM_TYPES = new Set(['preference', 'pattern', 'win', 'decision']);
|
|
13
|
+
const OPERATIONAL_TYPES = new Set(['correction', 'failure', 'gap', 'observation']);
|
|
14
|
+
|
|
15
|
+
function normalizeText(value) {
|
|
16
|
+
return String(value || '').trim().toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function chooseLifecycle(type) {
|
|
20
|
+
const value = normalizeText(type);
|
|
21
|
+
if (LONGTERM_TYPES.has(value)) return 'longterm';
|
|
22
|
+
if (OPERATIONAL_TYPES.has(value)) return 'operational';
|
|
23
|
+
return 'operational';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function memoryContainsSummary(memory, summaryKey) {
|
|
27
|
+
const content = normalizeText(memory?.content);
|
|
28
|
+
const summary = normalizeText(memory?.summary);
|
|
29
|
+
return content.includes(summaryKey) || summary.includes(summaryKey);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function maintenanceScopes(scopeFilter) {
|
|
33
|
+
const scope = normalizeText(scopeFilter);
|
|
34
|
+
if (!scope) return ['user', 'global', 'project'];
|
|
35
|
+
if (scope === 'repo') return ['project'];
|
|
36
|
+
if (['user', 'global', 'project'].includes(scope)) return [scope];
|
|
37
|
+
return ['user', 'global', 'project'];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runMemoryMaintenance({
|
|
41
|
+
dryRun = false,
|
|
42
|
+
scope = null,
|
|
43
|
+
workspaceRoot = process.cwd(),
|
|
44
|
+
config = {}
|
|
45
|
+
} = {}) {
|
|
46
|
+
const reports = [];
|
|
47
|
+
const filesChanged = [];
|
|
48
|
+
|
|
49
|
+
for (const memoryScope of maintenanceScopes(scope)) {
|
|
50
|
+
const maintenance = await getMemoryBucketMaintenance({ scope: memoryScope, workspaceRoot });
|
|
51
|
+
const items = await listMemories({ scope: memoryScope, workspaceRoot });
|
|
52
|
+
if (items.length === 0) {
|
|
53
|
+
reports.push({ scope: memoryScope, skipped: true, reason: 'empty' });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (maintenance.fresh) {
|
|
57
|
+
reports.push({ scope: memoryScope, skipped: true, reason: 'already-maintained', itemCount: items.length });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const evaluated = dryRun
|
|
62
|
+
? { items, archives: [] }
|
|
63
|
+
: await evaluateMemoryMaintenance({ scope: memoryScope, items, config, workspaceRoot });
|
|
64
|
+
if (evaluated.error) {
|
|
65
|
+
reports.push({ scope: memoryScope, skipped: true, reason: `maintenance-error: ${evaluated.error}`, itemCount: items.length });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const nextItems = Array.isArray(evaluated.items) && evaluated.items.length > 0 ? evaluated.items : items;
|
|
69
|
+
const changed =
|
|
70
|
+
JSON.stringify(nextItems.map((item) => [item.kind, item.content, item.summary, item.lifecycle || ''])) !==
|
|
71
|
+
JSON.stringify(items.map((item) => [item.kind, item.content, item.summary, item.lifecycle || '']));
|
|
72
|
+
|
|
73
|
+
if (!dryRun) {
|
|
74
|
+
await replaceMemoryBucket({
|
|
75
|
+
scope: memoryScope,
|
|
76
|
+
items: nextItems,
|
|
77
|
+
workspaceRoot,
|
|
78
|
+
markMaintained: true
|
|
79
|
+
});
|
|
80
|
+
filesChanged.push({
|
|
81
|
+
file: memoryScope === 'project' ? 'memory/project/*.json' : `memory/${memoryScope}.json`,
|
|
82
|
+
why: changed
|
|
83
|
+
? `LLM-maintained ${items.length} item(s) into ${nextItems.length} item(s)`
|
|
84
|
+
: `Marked ${items.length} item(s) as maintained`
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
reports.push({
|
|
89
|
+
scope: memoryScope,
|
|
90
|
+
skipped: false,
|
|
91
|
+
before: items.length,
|
|
92
|
+
after: nextItems.length,
|
|
93
|
+
changed,
|
|
94
|
+
archives: evaluated.archives || [],
|
|
95
|
+
dryRun
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { reports, filesChanged };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function runDreamConsolidation({
|
|
103
|
+
dryRun = false,
|
|
104
|
+
scope = null,
|
|
105
|
+
workspaceRoot = process.cwd(),
|
|
106
|
+
config = {},
|
|
107
|
+
writeAudit = true
|
|
108
|
+
} = {}) {
|
|
109
|
+
const scopeFilter = scope || null;
|
|
110
|
+
const inbox = await listInbox({ scope: scopeFilter });
|
|
111
|
+
|
|
112
|
+
const [globalMemories, userMemories, projectMemories] = await Promise.all([
|
|
113
|
+
listMemories({ scope: 'global', workspaceRoot }),
|
|
114
|
+
listMemories({ scope: 'user', workspaceRoot }),
|
|
115
|
+
listMemories({ scope: 'project', workspaceRoot })
|
|
116
|
+
]);
|
|
117
|
+
const knownMemories = [...globalMemories, ...userMemories, ...projectMemories];
|
|
118
|
+
|
|
119
|
+
const promotions = [];
|
|
120
|
+
const rejections = [];
|
|
121
|
+
const archives = [];
|
|
122
|
+
const filesRead = ['memory/inbox/*', 'memory/global.json', 'memory/user.json', 'memory/project/*.json'];
|
|
123
|
+
const filesChanged = [];
|
|
124
|
+
|
|
125
|
+
/* ── Phase 1: 规则预过滤(快速剔除明显垃圾) ─────────────────── */
|
|
126
|
+
const candidates = [];
|
|
127
|
+
const seen = new Map();
|
|
128
|
+
|
|
129
|
+
for (const entry of inbox) {
|
|
130
|
+
const summaryKey = normalizeText(entry.summary);
|
|
131
|
+
if (!summaryKey) {
|
|
132
|
+
if (!dryRun) await archiveEntry(entry, 'invalid-summary', 'Summary is empty after normalization');
|
|
133
|
+
archives.push({ summary: String(entry.summary || ''), reason: 'invalid-summary' });
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (seen.has(summaryKey)) {
|
|
138
|
+
if (!dryRun) await archiveEntry(entry, 'duplicate', `Duplicate of ${seen.get(summaryKey)}`);
|
|
139
|
+
archives.push({ summary: entry.summary, reason: 'duplicate' });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
seen.set(summaryKey, entry.id);
|
|
143
|
+
|
|
144
|
+
const alreadyKnown = knownMemories.some((memory) => memoryContainsSummary(memory, summaryKey));
|
|
145
|
+
if (alreadyKnown) {
|
|
146
|
+
if (!dryRun) await archiveEntry(entry, 'already-known', 'Already present in memory');
|
|
147
|
+
rejections.push({ summary: entry.summary, reason: 'already-known' });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
candidates.push(entry);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (candidates.length > 0) {
|
|
155
|
+
/* ── Phase 2: LLM 批量评估(质量门控 + scope 分类 + 内容提炼) ── */
|
|
156
|
+
const llmResults = dryRun
|
|
157
|
+
? candidates.map((e) => ({ id: e.id, action: 'keep', scope: 'global', kind: e.type || 'observation', content: e.details || e.summary, summary: e.summary, confidence: 0.9 }))
|
|
158
|
+
: await evaluateInboxBatch({ entries: candidates, config, workspaceRoot });
|
|
159
|
+
|
|
160
|
+
const resultMap = new Map(llmResults.map((r) => [r.id, r]));
|
|
161
|
+
|
|
162
|
+
/* ── Phase 3: 按评估结果 promote 或 archive ─────────────────── */
|
|
163
|
+
for (const entry of candidates) {
|
|
164
|
+
const evaluation = resultMap.get(entry.id);
|
|
165
|
+
|
|
166
|
+
if (!evaluation || evaluation.action === 'discard') {
|
|
167
|
+
const reason = evaluation?.reason || 'LLM discarded';
|
|
168
|
+
if (!dryRun) await archiveEntry(entry, 'discarded-by-evaluator', reason);
|
|
169
|
+
rejections.push({ summary: entry.summary, reason: `evaluator-discard: ${reason}` });
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const promoteScope = evaluation.scope || 'global';
|
|
174
|
+
const lifecycle = chooseLifecycle(evaluation.kind);
|
|
175
|
+
const enrichedEntry = {
|
|
176
|
+
...entry,
|
|
177
|
+
/* 用 LLM 提炼后的内容覆盖原始报错 */
|
|
178
|
+
summary: evaluation.summary || entry.summary,
|
|
179
|
+
details: evaluation.content || entry.details || entry.summary,
|
|
180
|
+
type: evaluation.kind || entry.type || 'observation'
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
if (!dryRun) {
|
|
184
|
+
try {
|
|
185
|
+
await promoteMemory({
|
|
186
|
+
entry: enrichedEntry,
|
|
187
|
+
scope: promoteScope,
|
|
188
|
+
lifecycle,
|
|
189
|
+
workspaceRoot,
|
|
190
|
+
config,
|
|
191
|
+
confidence: evaluation.confidence || 0.8
|
|
192
|
+
});
|
|
193
|
+
filesChanged.push({ file: `memory/${promoteScope}.json`, why: `Promoted "${enrichedEntry.summary}" as ${lifecycle} (${promoteScope})` });
|
|
194
|
+
promotions.push({ summary: enrichedEntry.summary, scope: promoteScope, lifecycle, rationale: evaluation.kind, confidence: evaluation.confidence });
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const reason = String(error?.message || error || 'promotion failed').slice(0, 180);
|
|
197
|
+
await archiveEntry(entry, 'promotion-failed', reason);
|
|
198
|
+
rejections.push({ summary: entry.summary, reason: `promotion-failed: ${reason}` });
|
|
199
|
+
archives.push({ summary: entry.summary, reason: 'promotion-failed' });
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
promotions.push({ summary: enrichedEntry.summary, scope: promoteScope, lifecycle, rationale: evaluation.kind, confidence: evaluation.confidence, dryRun: true });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const maintenance = await runMemoryMaintenance({ dryRun, scope: scopeFilter, workspaceRoot, config });
|
|
209
|
+
filesChanged.push(...maintenance.filesChanged);
|
|
210
|
+
|
|
211
|
+
const report = {
|
|
212
|
+
timestamp: new Date().toISOString(),
|
|
213
|
+
filesRead,
|
|
214
|
+
filesChanged,
|
|
215
|
+
candidatesGenerated: inbox.length,
|
|
216
|
+
promotions,
|
|
217
|
+
rejections,
|
|
218
|
+
archives,
|
|
219
|
+
maintenance: maintenance.reports,
|
|
220
|
+
...(inbox.length === 0 ? { message: 'No inbox entries to consolidate; maintained existing memory buckets.' } : {})
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
if (!dryRun && writeAudit) {
|
|
224
|
+
const reportPath = await writeDreamAuditReport(report);
|
|
225
|
+
report.auditReport = reportPath;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { ok: true, dryRun, ...report };
|
|
229
|
+
}
|