evolcore 0.0.33 → 0.0.35-test.0
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/CHANGELOG.md +59 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +38 -4
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +93 -14
- package/dist/agents/codex/diagnostics.js +79 -22
- package/dist/agents/codex-runner.js +245 -47
- package/dist/agents/ecagent-runner.js +90 -27
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/agentmd.js +13 -2
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/msg/managed-operation.js +16 -0
- package/dist/aun/msg/p2p.js +18 -2
- package/dist/channels/aun.js +123 -7
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +39 -14
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +130 -23
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/getmeta-command.js +211 -0
- package/dist/cli/index.js +26 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/memory-command.js +200 -156
- package/dist/cli/thread-command.js +17 -7
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +25 -8
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +22 -3
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/channel-runtime.js +1 -1
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +145 -5
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +24 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/command/topic-slash.js +4 -7
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/event-catalog.js +14 -0
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +47 -2
- package/dist/core/memory/batch-meta.js +133 -0
- package/dist/core/memory/consolidation-context.js +132 -28
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation-trigger.js +15 -7
- package/dist/core/memory/consolidation.js +146 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +50 -19
- package/dist/core/message/aun-ipc-sender.js +19 -1
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +5 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +17 -0
- package/dist/core/message/response-engine.js +598 -50
- package/dist/core/permission/ec-command-parser.js +169 -44
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +60 -62
- package/dist/core/relation/peer-identity.js +87 -12
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +131 -6
- package/dist/core/session/session-title.js +26 -1
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +289 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +6 -1
- package/dist/core/thread/ipc-operation.js +33 -16
- package/dist/core/thread/thread-operation.js +62 -24
- package/dist/core/thread/thread-service.js +258 -51
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +17 -5
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +9 -10
- package/kits/docs/evolcore/fs.md +2 -2
- package/kits/docs/evolcore/group-collaboration.md +3 -3
- package/kits/docs/evolcore/group.md +14 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -16
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/stats.md +1 -1
- package/kits/docs/evolcore/thread-chain.md +30 -14
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.auxiliary.json +9 -0
- package/kits/eck_manifest.json +20 -20
- package/kits/eck_manifest.memory.json +20 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +13 -16
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +8 -5
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/thread-ops/SKILL.md +18 -18
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +12 -12
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +42 -7
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bash-description.md +9 -0
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* consolidation-context.ts — 整理上下文 MD 文档序列化(v3)
|
|
3
3
|
*
|
|
4
|
-
* 把一次整理所需的全部信息序列化成 Markdown
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
4
|
+
* 把一次整理所需的全部信息序列化成 Markdown,写一处:
|
|
5
|
+
* - $EVOLCORE_HOME/memory/consolidation-batches/<批次目录>/context.md
|
|
6
|
+
*
|
|
7
|
+
* 任务书只有一个落点。此前另写一份到会话运行目录(sessionRuntimeDir,缺省回落
|
|
8
|
+
* $EVOLCORE_HOME/tmp),导致落点随触发路径漂移,且该目录的读取范围不由任务书所在
|
|
9
|
+
* 的整理会话决定——整理会话读自己的任务书会被只读判定拦成 workspace_escape。
|
|
10
|
+
* 见 docs/memory/session-profile-design.md §5.1。
|
|
7
11
|
*
|
|
8
12
|
* 批次目录命名:<YYYYMMDDTHHmmss>-<selfAid>-<mode>/
|
|
9
13
|
* 批次目录内:context.md(任务书)、ops.jsonl(操作流水)、threads/(话题快照)
|
|
@@ -15,6 +19,8 @@ import { resolveRoot, resolvePaths } from '../../paths.js';
|
|
|
15
19
|
import { DEFAULT_FORGET_AFTER_DAYS, collectCandidates, isDueForReview } from './staleness.js';
|
|
16
20
|
import { chatDirPath } from '../session/session-fs-store.js';
|
|
17
21
|
import { getMemoryArray, getMemoryString, parseMemoryDocument } from './memory-document.js';
|
|
22
|
+
import { writeBatchMeta } from './batch-meta.js';
|
|
23
|
+
import { feedbackFilePath } from './consolidation-ops-records.js';
|
|
18
24
|
export const CONSOLIDATION_MODES = ['notes', 'generalize', 'promote', 'profile', 'forget'];
|
|
19
25
|
/**
|
|
20
26
|
* 模式 → 允许写入的作用域(不含 forget)。
|
|
@@ -220,13 +226,50 @@ function fillCandidates(ctx, mode, forgetAfterDays) {
|
|
|
220
226
|
else if (mode === 'forget') {
|
|
221
227
|
const now = Date.now();
|
|
222
228
|
const thresholds = { forgetAfterDays };
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
// forget + scope=agent:扫描所有 relation 的 consolidated 目录,以及 agent 自身的 consolidated
|
|
230
|
+
if (ctx.scope === 'agent') {
|
|
231
|
+
ctx.candidates = [];
|
|
232
|
+
// ctx.consolidatedDir 是 agents/<selfAid>/personal/memory/consolidated,
|
|
233
|
+
// 往上走三级才是 agents/<selfAid>
|
|
234
|
+
const agentDir = path.dirname(path.dirname(path.dirname(ctx.consolidatedDir)));
|
|
235
|
+
const relationsRoot = path.join(agentDir, 'relations');
|
|
236
|
+
if (fs.existsSync(relationsRoot)) {
|
|
237
|
+
for (const peerKey of fs.readdirSync(relationsRoot)) {
|
|
238
|
+
const consolidatedDir = path.join(relationsRoot, peerKey, 'memory', 'consolidated');
|
|
239
|
+
if (fs.existsSync(consolidatedDir)) {
|
|
240
|
+
const candidates = collectCandidates(consolidatedDir, thresholds, now)
|
|
241
|
+
.filter(info => isDueForReview(info, thresholds, now))
|
|
242
|
+
.map(info => ({
|
|
243
|
+
relPath: `${peerKey}/consolidated/${info.name}`,
|
|
244
|
+
description: readFmValue(info.content, 'description') ?? info.name.replace(/\.md$/, ''),
|
|
245
|
+
tags: info.tags,
|
|
246
|
+
}));
|
|
247
|
+
ctx.candidates.push(...candidates);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// agent 自身的 consolidated(原 ⑤b 拉起 forget/agent 的来源)
|
|
252
|
+
if (fs.existsSync(ctx.consolidatedDir)) {
|
|
253
|
+
const own = collectCandidates(ctx.consolidatedDir, thresholds, now)
|
|
254
|
+
.filter(info => isDueForReview(info, thresholds, now))
|
|
255
|
+
.map(info => ({
|
|
256
|
+
relPath: `consolidated/${info.name}`,
|
|
257
|
+
description: readFmValue(info.content, 'description') ?? info.name.replace(/\.md$/, ''),
|
|
258
|
+
tags: info.tags,
|
|
259
|
+
}));
|
|
260
|
+
ctx.candidates.push(...own);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// relation / owner 级 forget:只扫描当前层级的 consolidated
|
|
265
|
+
ctx.candidates = collectCandidates(source, thresholds, now)
|
|
266
|
+
.filter(info => isDueForReview(info, thresholds, now))
|
|
267
|
+
.map(info => ({
|
|
268
|
+
relPath: `consolidated/${info.name}`,
|
|
269
|
+
description: readFmValue(info.content, 'description') ?? info.name.replace(/\.md$/, ''),
|
|
270
|
+
tags: info.tags,
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
230
273
|
}
|
|
231
274
|
}
|
|
232
275
|
// ── 渲染 ────────────────────────────────────────────────────────────────────
|
|
@@ -256,15 +299,45 @@ function renderScopeSection(ctx, mode, threadFiles) {
|
|
|
256
299
|
lines.push(`待整理的 note 在 \`${ctx.processingDir}\`(整理开始前已从 pending/ 整体移入,是本次的快照)。`);
|
|
257
300
|
}
|
|
258
301
|
else if (ctx.candidates) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
302
|
+
// forget + scope=agent:按 relation 分组展示
|
|
303
|
+
if (mode === 'forget' && ctx.scope === 'agent') {
|
|
304
|
+
const byRelation = new Map();
|
|
305
|
+
for (const c of ctx.candidates) {
|
|
306
|
+
const match = c.relPath.match(/^([^/\\]+)[/\\]consolidated[/\\]/);
|
|
307
|
+
const relation = match ? match[1] : 'unknown';
|
|
308
|
+
if (!byRelation.has(relation))
|
|
309
|
+
byRelation.set(relation, []);
|
|
310
|
+
byRelation.get(relation).push(c);
|
|
311
|
+
}
|
|
312
|
+
lines.push(`**候选记忆(共 ${ctx.candidates.length} 条,分布在 ${byRelation.size} 个关系)** —— 只列路径与描述,按需读正文:`);
|
|
313
|
+
lines.push('');
|
|
314
|
+
if (ctx.candidates.length === 0) {
|
|
315
|
+
lines.push('(无候选)');
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
for (const [relation, candidates] of byRelation) {
|
|
319
|
+
lines.push(`#### relation: ${relation} (${candidates.length} 条)`);
|
|
320
|
+
lines.push('');
|
|
321
|
+
for (const c of candidates) {
|
|
322
|
+
const tags = c.tags.length > 0 ? ` [${c.tags.join(', ')}]` : '';
|
|
323
|
+
lines.push(`- \`${c.relPath}\`${tags} — ${c.description}`);
|
|
324
|
+
}
|
|
325
|
+
lines.push('');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
263
328
|
}
|
|
264
329
|
else {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
330
|
+
// 其他模式:直接列出候选
|
|
331
|
+
lines.push(`**候选记忆(${ctx.candidates.length} 条)** —— 只列路径与描述,按需读正文:`);
|
|
332
|
+
lines.push('');
|
|
333
|
+
if (ctx.candidates.length === 0) {
|
|
334
|
+
lines.push('(无候选)');
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
for (const c of ctx.candidates) {
|
|
338
|
+
const tags = c.tags.length > 0 ? ` [${c.tags.join(', ')}]` : '';
|
|
339
|
+
lines.push(`- \`${c.relPath}\`${tags} — ${c.description}`);
|
|
340
|
+
}
|
|
268
341
|
}
|
|
269
342
|
}
|
|
270
343
|
}
|
|
@@ -421,8 +494,6 @@ export function serializeConsolidationContext(input) {
|
|
|
421
494
|
migrateLegacyConsolidationBatches();
|
|
422
495
|
const scopes = resolveScopeContexts(input);
|
|
423
496
|
const now = new Date();
|
|
424
|
-
// 会话内文件名用:去掉所有分隔符,无 T,14 字符(YYYYMMDDHHmmss)
|
|
425
|
-
const ts = now.toISOString().replace(/[-:T]/g, '').slice(0, 14);
|
|
426
497
|
const forgetAfterDays = input.forgetAfterDays ?? DEFAULT_FORGET_AFTER_DAYS;
|
|
427
498
|
for (const ctx of scopes) {
|
|
428
499
|
fillCandidates(ctx, input.mode, forgetAfterDays);
|
|
@@ -435,6 +506,9 @@ export function serializeConsolidationContext(input) {
|
|
|
435
506
|
fs.mkdirSync(batchRoot, { recursive: true });
|
|
436
507
|
const batchDir = fs.mkdtempSync(path.join(batchRoot, `${prettyTs}-${selfAid}-${input.mode}-`));
|
|
437
508
|
fs.mkdirSync(batchDir, { recursive: true });
|
|
509
|
+
// 反馈文件的父目录先建好:整理会话只被授权写这一个文件,无权建目录,
|
|
510
|
+
// 而各 runner 的补丁器 / 文件写入工具在父目录不存在时会失败。
|
|
511
|
+
fs.mkdirSync(path.dirname(feedbackFilePath(batchDir)), { recursive: true });
|
|
438
512
|
// ── 话题快照(notes / compress / session-new 模式,有 peerId 时生成)───────
|
|
439
513
|
let historyJsonlPath;
|
|
440
514
|
let threadFiles;
|
|
@@ -487,6 +561,22 @@ export function serializeConsolidationContext(input) {
|
|
|
487
561
|
'',
|
|
488
562
|
...scopes.flatMap(ctx => [renderScopeSection(ctx, input.mode, threadFiles), '', '---', '']),
|
|
489
563
|
];
|
|
564
|
+
// 列出可读根(工具 path/file_path 参数必须落在这些路径内)
|
|
565
|
+
lines.push('', '**可读根(工具 path/file_path 参数必须落在这些路径内)**:');
|
|
566
|
+
lines.push('- 批次目录:`' + batchDir + '`');
|
|
567
|
+
for (const ctx of scopes) {
|
|
568
|
+
if (ctx.processingDir)
|
|
569
|
+
lines.push('- processingDir: `' + ctx.processingDir + '`');
|
|
570
|
+
if (ctx.consolidatedDir)
|
|
571
|
+
lines.push('- consolidatedDir: `' + ctx.consolidatedDir + '`');
|
|
572
|
+
if (ctx.indexPath)
|
|
573
|
+
lines.push('- indexPath: `' + ctx.indexPath + '`');
|
|
574
|
+
}
|
|
575
|
+
if (historyJsonlPath)
|
|
576
|
+
lines.push('- 消息流水: `' + historyJsonlPath + '`');
|
|
577
|
+
if (threadFiles && threadFiles.length > 0)
|
|
578
|
+
lines.push('- 话题快照: `' + path.join(batchDir, 'threads') + '`');
|
|
579
|
+
lines.push('', '---', '');
|
|
490
580
|
if (input.mode === 'notes' && threadFiles && threadFiles.length > 0) {
|
|
491
581
|
const threadsDir = path.join(batchDir, 'threads');
|
|
492
582
|
lines.push('## 话题快照文件', '');
|
|
@@ -512,21 +602,35 @@ export function serializeConsolidationContext(input) {
|
|
|
512
602
|
lines.push('', '---', '');
|
|
513
603
|
}
|
|
514
604
|
lines.push('请按照本次会话注入的模式手册完成整理。');
|
|
605
|
+
lines.push('');
|
|
606
|
+
lines.push('整理结束后,用文件写入工具(Write / Edit)把本次整理的整体反馈写入以下**反馈文件路径**:');
|
|
607
|
+
lines.push('');
|
|
608
|
+
lines.push(`\`${feedbackFilePath(batchDir)}\``);
|
|
609
|
+
lines.push('');
|
|
610
|
+
lines.push('这是本次会话唯一可以写的文件(文件已建好,直接写即可);写别的路径会被拒绝。');
|
|
611
|
+
lines.push('未写入则收尾时回退到默认统计模板(模式、触发方式、耗时、新建/更新/归档条数等)。');
|
|
515
612
|
const text = lines.join('\n');
|
|
516
|
-
// ──
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
521
|
-
const filePath = path.join(tmpDir, `memory-consolidation-${ts}.md`);
|
|
522
|
-
fs.writeFileSync(filePath, text, 'utf-8');
|
|
523
|
-
// ── 写批次目录(长期留存)────────────────────────────────────────────────
|
|
613
|
+
// ── 写批次目录(任务书唯一落点)────────────────────────────────────────────
|
|
614
|
+
// 不再写会话运行目录:任务书落在 $EVOLCORE_HOME/tmp 或 %TEMP%/ec-session-<hash>/
|
|
615
|
+
// 会同时踩两个坑——落点随触发路径而变,且会话运行时目录的读取范围不由任务书
|
|
616
|
+
// 所在的会话决定(见 docs/memory/session-profile-design.md §5.1)。
|
|
524
617
|
const contextFilePath = path.join(batchDir, 'context.md');
|
|
525
618
|
try {
|
|
526
619
|
fs.writeFileSync(contextFilePath, text, 'utf-8');
|
|
527
620
|
}
|
|
528
621
|
catch { /* 批次任务书写入失败不影响整理 */ }
|
|
529
|
-
|
|
622
|
+
// 写入初始 batch.md 元数据
|
|
623
|
+
writeBatchMeta(batchDir, {
|
|
624
|
+
selfAid: input.taskCtx.selfAid ?? 'unknown',
|
|
625
|
+
...(input.taskCtx.peerKey ? { peerKey: input.taskCtx.peerKey } : {}),
|
|
626
|
+
...(input.ownerAid ? { ownerAid: input.ownerAid } : {}),
|
|
627
|
+
...(input.taskCtx.baseagent ? { baseagent: input.taskCtx.baseagent } : {}),
|
|
628
|
+
mode: input.mode,
|
|
629
|
+
triggeredBy: input.triggeredBy,
|
|
630
|
+
startedAt: now.toISOString(),
|
|
631
|
+
status: 'running',
|
|
632
|
+
});
|
|
633
|
+
return { filePath: contextFilePath, batchDir, contextFilePath, scopes, historyJsonlPath, threadFiles, chatDir,
|
|
530
634
|
...(threadFiles?.length ? { snapshotThroughTs: now.getTime() } : {}) };
|
|
531
635
|
}
|
|
532
636
|
/** 会话结束后回填本次产出,追加到任务书末尾。 */
|
|
@@ -38,7 +38,7 @@ export function summarizeOpsRecords(batchDir) {
|
|
|
38
38
|
try {
|
|
39
39
|
for (const line of fs.readFileSync(filePath, 'utf-8').split('\n').filter(Boolean)) {
|
|
40
40
|
const entry = JSON.parse(line);
|
|
41
|
-
if (entry.op === 'summary' || entry.op === 'session-start' || entry.op === 'session-end'
|
|
41
|
+
if (entry.op === 'summary' || entry.op === 'session-start' || entry.op === 'session-end')
|
|
42
42
|
continue;
|
|
43
43
|
if (!entry.ok) {
|
|
44
44
|
summary.errors++;
|
|
@@ -56,25 +56,23 @@ export function summarizeOpsRecords(batchDir) {
|
|
|
56
56
|
return summary;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
59
|
+
* 整理会话的反馈文件路径。任务书给出的路径、系统会话模块声明的唯一可写文件、
|
|
60
|
+
* 收尾读取与清理都以此为准,不要在别处重新拼接。
|
|
61
|
+
*/
|
|
62
|
+
export function feedbackFilePath(batchDir) {
|
|
63
|
+
return path.join(batchDir, 'scratch', 'feedback.txt');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 读整理会话写入的反馈文本:<batchDir>/scratch/feedback.txt。
|
|
67
|
+
* 文件不存在或内容为空时返回 undefined,收尾回退默认统计模板。
|
|
61
68
|
*/
|
|
62
69
|
export function readFeedbackText(batchDir) {
|
|
63
|
-
const filePath = path.join(batchDir, 'ops.jsonl');
|
|
64
|
-
if (!fs.existsSync(filePath))
|
|
65
|
-
return undefined;
|
|
66
70
|
try {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (entry.op === 'feedback-set') {
|
|
71
|
-
const text = entry.text;
|
|
72
|
-
if (typeof text === 'string' && text.trim())
|
|
73
|
-
return text;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
71
|
+
const text = fs.readFileSync(feedbackFilePath(batchDir), 'utf-8').trim();
|
|
72
|
+
if (text)
|
|
73
|
+
return text;
|
|
76
74
|
}
|
|
77
|
-
catch { /*
|
|
75
|
+
catch { /* 未写反馈属正常情况 */ }
|
|
78
76
|
return undefined;
|
|
79
77
|
}
|
|
80
78
|
/**
|
|
@@ -379,19 +379,27 @@ export function checkConsolidationCandidates(params) {
|
|
|
379
379
|
out.push({ mode: 'profile', peerKey, reason: '索引有变化且画像超期' });
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
|
-
// ⑤ 超期记忆 → forget
|
|
382
|
+
// ⑤ 超期记忆 → forget(agent 级,扫描所有 relation + agent 自身,合并成一个会话)
|
|
383
|
+
let hasRelationForget = false;
|
|
383
384
|
for (const peerKey of peerKeys) {
|
|
384
385
|
const dir = path.join(relationMemoryDir(root, selfAid, peerKey), 'consolidated');
|
|
385
386
|
const due = checkStale(dir, now, config.forgetAfterDays);
|
|
386
|
-
if (due > 0)
|
|
387
|
-
|
|
388
|
-
}
|
|
387
|
+
if (due > 0)
|
|
388
|
+
hasRelationForget = true;
|
|
389
389
|
}
|
|
390
|
-
// ⑤b
|
|
390
|
+
// ⑤b agent 自身的 consolidated 超期
|
|
391
391
|
const agentConsolidated = path.join(root, 'agents', selfAid, 'personal', 'memory', 'consolidated');
|
|
392
|
-
|
|
393
|
-
|
|
392
|
+
const hasAgentForget = config.enableAgent && checkStale(agentConsolidated, now, config.forgetAfterDays) > 0;
|
|
393
|
+
// 只要有任意一层超期,起一个 forget scope=agent 会话,由会话内统一扫描
|
|
394
|
+
if (hasRelationForget || hasAgentForget) {
|
|
395
|
+
const parts = [];
|
|
396
|
+
if (hasRelationForget)
|
|
397
|
+
parts.push('关系级记忆超期');
|
|
398
|
+
if (hasAgentForget)
|
|
399
|
+
parts.push('agent 级记忆超期');
|
|
400
|
+
out.push({ mode: 'forget', scope: 'agent', reason: parts.join('、') });
|
|
394
401
|
}
|
|
402
|
+
// ⑤c owner 级 forget 保持独立(owner 层与 agent/relation 层是平行的)
|
|
395
403
|
if (config.enableOwner && ownerAid) {
|
|
396
404
|
const ownerConsolidated = path.join(root, 'memory', 'owner', ownerAid, 'consolidated');
|
|
397
405
|
if (checkStale(ownerConsolidated, now, config.forgetAfterDays) > 0) {
|
|
@@ -12,35 +12,36 @@ import fs from 'fs';
|
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { agentDir, resolveRoot } from '../../paths.js';
|
|
14
14
|
import { serializeConsolidationContext } from './consolidation-context.js';
|
|
15
|
+
import { assertSystemSessionLaunchable } from '../system-session/registry.js';
|
|
16
|
+
import { resolveAgentLifecycle } from '../../config/lifecycle.js';
|
|
17
|
+
import { ensureSessionRuntimeDir } from '../../cli/task-context.js';
|
|
15
18
|
import { formatChannelKey } from '../channel-loader.js';
|
|
16
19
|
import { logger } from '../../utils/logger.js';
|
|
17
|
-
import { appendOpsRecord } from './consolidation-ops-records.js';
|
|
20
|
+
import { appendOpsRecord, feedbackFilePath, readFeedbackText } from './consolidation-ops-records.js';
|
|
21
|
+
import { updateBatchMeta } from './batch-meta.js';
|
|
18
22
|
import { checkConsolidationCandidates, shouldUseMetaIndex } from './consolidation-trigger.js';
|
|
19
23
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
24
|
+
* 清理旧整理批次,全局保留最新的 10 个(不区分 aid 或 mode)。
|
|
25
|
+
* 启动新整理时调用;selfAid 参数保留供日志上下文用,不参与过滤。
|
|
22
26
|
*/
|
|
23
|
-
function cleanupPreviousConsolidationBatches(
|
|
27
|
+
function cleanupPreviousConsolidationBatches(_selfAid) {
|
|
24
28
|
try {
|
|
25
29
|
const batchRoot = path.join(resolveRoot(), 'memory', 'consolidation-batches');
|
|
26
30
|
if (!fs.existsSync(batchRoot))
|
|
27
31
|
return;
|
|
28
|
-
// 匹配格式:YYYYMMDDTHHMMSS-<selfAid>-*
|
|
29
|
-
const escapedAid = selfAid.replace(/\./g, '\\.');
|
|
30
|
-
const pattern = new RegExp(`^\\d{8}T\\d{6}-${escapedAid}-`);
|
|
31
32
|
const entries = fs.readdirSync(batchRoot, { withFileTypes: true })
|
|
32
|
-
.filter(e => e.isDirectory()
|
|
33
|
+
.filter(e => e.isDirectory())
|
|
33
34
|
.map(e => ({ name: e.name, path: path.join(batchRoot, e.name) }))
|
|
34
|
-
.sort((a, b) => b.name.localeCompare(a.name)); //
|
|
35
|
-
|
|
36
|
-
for (let i =
|
|
35
|
+
.sort((a, b) => b.name.localeCompare(a.name)); // 时间戳前缀,字典序即时间序,最新的在前
|
|
36
|
+
const KEEP = 10;
|
|
37
|
+
for (let i = KEEP; i < entries.length; i++) {
|
|
37
38
|
const dir = entries[i];
|
|
38
39
|
try {
|
|
39
40
|
fs.rmSync(dir.path, { recursive: true, force: true });
|
|
40
|
-
logger.debug(`[Memory]
|
|
41
|
+
logger.debug(`[Memory] 清理旧整理批次: ${dir.name}`);
|
|
41
42
|
}
|
|
42
43
|
catch (e) {
|
|
43
|
-
logger.warn(`[Memory]
|
|
44
|
+
logger.warn(`[Memory] 清理旧整理批次失败 ${dir.name}: ${e?.message}`);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -48,6 +49,21 @@ function cleanupPreviousConsolidationBatches(selfAid) {
|
|
|
48
49
|
logger.warn(`[Memory] 清理整理批次时出错: ${e?.message}`);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* 首次设定(lifecycle)没完成的 agent 不能整理记忆:整理会话会被注入首次设定规则而拒绝干活,
|
|
54
|
+
* note 却照样被认领。返回拒绝原因;可以整理时返回 undefined。
|
|
55
|
+
* 直接读磁盘上的 config.json:首次设定完成时 lifecycle 就落在这里,不依赖内存里的注册表。
|
|
56
|
+
*/
|
|
57
|
+
export function consolidationBlockedReason(selfAid) {
|
|
58
|
+
const configPath = path.join(resolveRoot(), 'agents', selfAid, 'config.json');
|
|
59
|
+
const config = fs.existsSync(configPath)
|
|
60
|
+
? JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
61
|
+
: {};
|
|
62
|
+
const lifecycle = resolveAgentLifecycle(config);
|
|
63
|
+
if (lifecycle === 'active')
|
|
64
|
+
return undefined;
|
|
65
|
+
return `agent ${selfAid} 首次设定尚未完成(lifecycle=${lifecycle ?? String(config.lifecycle)}),完成后才能整理记忆`;
|
|
66
|
+
}
|
|
51
67
|
/**
|
|
52
68
|
* 序列化上下文并直接创建整理会话。
|
|
53
69
|
* 失败抛错,调用方负责决定是否记录诊断或回传错误。
|
|
@@ -60,6 +76,13 @@ export async function launchConsolidation(channel, params) {
|
|
|
60
76
|
const selfAid = effectiveCtx.selfAid;
|
|
61
77
|
if (!selfAid)
|
|
62
78
|
throw new Error('无法确定当前 agent AID(selfAid 为空)');
|
|
79
|
+
// 系统会话标记:类型 / subtype 必须已注册,runner 必须受支持(Gemini 拒绝)。
|
|
80
|
+
// 放在认领 processing 之前,拒绝时不留副作用。
|
|
81
|
+
const systemSession = { type: 'memory', subtype: mode };
|
|
82
|
+
assertSystemSessionLaunchable(systemSession, effectiveCtx.baseagent ?? 'claude');
|
|
83
|
+
const blockedReason = consolidationBlockedReason(selfAid);
|
|
84
|
+
if (blockedReason)
|
|
85
|
+
throw new Error(blockedReason);
|
|
63
86
|
// 清理旧的整理批次(保留最新的一个历史)
|
|
64
87
|
cleanupPreviousConsolidationBatches(selfAid);
|
|
65
88
|
// notes 模式:pending/ → processing/ 快照迁移(在序列化上下文之前执行,声明认领)
|
|
@@ -96,7 +119,6 @@ export async function launchConsolidation(channel, params) {
|
|
|
96
119
|
triggeredBy,
|
|
97
120
|
taskCtx: effectiveCtx,
|
|
98
121
|
ownerAid,
|
|
99
|
-
sessionRuntimeDir: effectiveCtx.sessionRuntimeDir,
|
|
100
122
|
ownerInvolved: params.ownerInvolved,
|
|
101
123
|
forgetAfterDays: params.forgetAfterDays,
|
|
102
124
|
scopeOverride: params.scopeOverride,
|
|
@@ -147,6 +169,8 @@ export async function launchConsolidation(channel, params) {
|
|
|
147
169
|
projectPath,
|
|
148
170
|
prompt,
|
|
149
171
|
metadata: {
|
|
172
|
+
// 系统会话的唯一判据:路径 / 工具 / ec 命令由 system-session/memory.ts 决定,与角色无关
|
|
173
|
+
systemSession,
|
|
150
174
|
sessionType: 'memory',
|
|
151
175
|
memoryMode: mode,
|
|
152
176
|
...(params.scopeOverride ? { memoryScope: params.scopeOverride } : {}),
|
|
@@ -154,6 +178,7 @@ export async function launchConsolidation(channel, params) {
|
|
|
154
178
|
consolidationBatchDir: ctxFile.batchDir,
|
|
155
179
|
...(ctxFile.chatDir ? { consolidationChatDir: ctxFile.chatDir } : {}),
|
|
156
180
|
...(claimedProcessing && memoryDir ? { consolidationMemoryDir: memoryDir } : {}),
|
|
181
|
+
...(ownerAid ? { consolidationOwnerAid: ownerAid } : {}),
|
|
157
182
|
consolidationUsedThreadSnapshots: !!ctxFile.threadFiles?.length,
|
|
158
183
|
...(ctxFile.snapshotThroughTs ? { consolidationSnapshotThroughTs: ctxFile.snapshotThroughTs } : {}),
|
|
159
184
|
...(effectiveCtx.peerKey ? { peerKey: effectiveCtx.peerKey } : {}),
|
|
@@ -207,6 +232,20 @@ function moveIntoDir(srcPath, destDir, stamp) {
|
|
|
207
232
|
}
|
|
208
233
|
fs.renameSync(srcPath, dest);
|
|
209
234
|
}
|
|
235
|
+
/** 本次整理认领到 processing/ 的 note 条数(收尾归位之前调用)。 */
|
|
236
|
+
function countClaimedNotes(memoryDir) {
|
|
237
|
+
if (!memoryDir)
|
|
238
|
+
return 0;
|
|
239
|
+
const processingDir = path.join(memoryDir, 'processing');
|
|
240
|
+
try {
|
|
241
|
+
return fs.readdirSync(processingDir)
|
|
242
|
+
.filter(entry => entry.endsWith('.md') && fs.statSync(path.join(processingDir, entry)).isFile())
|
|
243
|
+
.length;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
210
249
|
function restorePendingNotes(memoryDir) {
|
|
211
250
|
const processingDir = path.join(memoryDir, 'processing');
|
|
212
251
|
if (!fs.existsSync(processingDir))
|
|
@@ -357,6 +396,17 @@ export function createConsolidationSessionFinalizer({ sessionManager, getAgent,
|
|
|
357
396
|
finalizedSessions.add(sessionId);
|
|
358
397
|
releaseAid = session.selfAID;
|
|
359
398
|
const { summarizeOpsRecords, collectAffectedScopes } = await import('./consolidation-ops-records.js');
|
|
399
|
+
// 会话正常结束不等于整理做完了。notes 模式认领了 note,却既没写记忆也没写反馈,
|
|
400
|
+
// 说明 agent 没处理这批 note(例如拒绝了任务)——按失败收尾,note 回 pending 下次重试。
|
|
401
|
+
let failureReason;
|
|
402
|
+
if (success && meta.memoryMode === 'notes' && countClaimedNotes(meta.consolidationMemoryDir) > 0) {
|
|
403
|
+
const ops = summarizeOpsRecords(batchDir);
|
|
404
|
+
if (ops.created + ops.updated + ops.archived === 0 && !readFeedbackText(batchDir)) {
|
|
405
|
+
success = false;
|
|
406
|
+
failureReason = '整理会话结束时没有写入任何记忆,也没有写反馈,note 未被处理';
|
|
407
|
+
logger.warn(`[Memory] ${failureReason}(session=${sessionId}),按失败收尾`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
360
410
|
if (!success) {
|
|
361
411
|
await rollbackConsolidation(batchDir, session.selfAID);
|
|
362
412
|
}
|
|
@@ -380,9 +430,8 @@ export function createConsolidationSessionFinalizer({ sessionManager, getAgent,
|
|
|
380
430
|
for (const [scope, memoryDir] of affectedScopes) {
|
|
381
431
|
const consolidatedDir = path.join(memoryDir, 'consolidated');
|
|
382
432
|
const indexPath = path.join(memoryDir, 'MEMORY.md');
|
|
383
|
-
const invalidDir = path.join(memoryDir, 'archive', 'invalid');
|
|
384
433
|
try {
|
|
385
|
-
await rebuildIndex(consolidatedDir, indexPath, scope,
|
|
434
|
+
await rebuildIndex(consolidatedDir, indexPath, scope, maxBytes);
|
|
386
435
|
}
|
|
387
436
|
catch { }
|
|
388
437
|
}
|
|
@@ -464,18 +513,36 @@ export function createConsolidationSessionFinalizer({ sessionManager, getAgent,
|
|
|
464
513
|
fs.writeFileSync(historyPath, JSON.stringify({ note: `runner ${baseagent} does not support getSessionMessages` }) + '\n', 'utf-8');
|
|
465
514
|
}
|
|
466
515
|
}
|
|
516
|
+
// feedback 只读一次:先落 batch.md,再清 scratch/,投递复用同一份文本
|
|
517
|
+
const customText = readFeedbackText(batchDir);
|
|
518
|
+
// batch.md 收尾更新:写入最终状态和 feedback 正文
|
|
519
|
+
try {
|
|
520
|
+
updateBatchMeta(batchDir, {
|
|
521
|
+
status: success ? 'completed' : 'failed',
|
|
522
|
+
finishedAt: new Date().toISOString(),
|
|
523
|
+
success,
|
|
524
|
+
...(session.agentSessionId ? { agentSessionId: session.agentSessionId } : {}),
|
|
525
|
+
...(failureReason ? { failureReason } : {}),
|
|
526
|
+
feedbackText: customText,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
catch { /* 失败静默 */ }
|
|
530
|
+
// scratch/ 是会话期临时工作区,feedback 已落 batch.md,不分成败与触发方式一律清掉
|
|
531
|
+
try {
|
|
532
|
+
fs.rmSync(path.dirname(feedbackFilePath(batchDir)), { recursive: true, force: true });
|
|
533
|
+
}
|
|
534
|
+
catch { /* 静默,不影响投递 */ }
|
|
467
535
|
const feedbackOrigin = meta.feedbackOrigin;
|
|
468
536
|
const triggeredBy = meta.consolidationTriggeredBy;
|
|
469
537
|
if (feedbackOrigin && triggeredBy && triggeredBy !== 'periodic-trigger' && session.selfAID) {
|
|
470
538
|
try {
|
|
471
|
-
const { readFeedbackText } = await import('./consolidation-ops-records.js');
|
|
472
|
-
const customText = readFeedbackText(batchDir);
|
|
473
539
|
const text = customText ?? renderDefaultConsolidationFeedback({
|
|
474
540
|
mode: meta.memoryMode,
|
|
475
541
|
triggeredBy,
|
|
476
542
|
success,
|
|
477
543
|
summary: summarizeOpsRecords(batchDir),
|
|
478
544
|
batchDir,
|
|
545
|
+
failureReason,
|
|
479
546
|
});
|
|
480
547
|
await sendFeedback(feedbackOrigin, text, session.selfAID);
|
|
481
548
|
}
|
|
@@ -490,6 +557,21 @@ export function createConsolidationSessionFinalizer({ sessionManager, getAgent,
|
|
|
490
557
|
finally {
|
|
491
558
|
if (releaseAid && sessionId)
|
|
492
559
|
releaseSlot(releaseAid, sessionId);
|
|
560
|
+
// runtime 目录清理:immediate 策略下,finalizer 是最后一个要读它的消费者。
|
|
561
|
+
// 用 sessionId 无状态重算路径,不依赖 spec 传递——保证即使 spec 不传 runtimeDir
|
|
562
|
+
// 字段,目录也能在会话结束后被清掉。
|
|
563
|
+
if (sessionId) {
|
|
564
|
+
const runtimeDir = ensureSessionRuntimeDir(sessionId);
|
|
565
|
+
if (runtimeDir) {
|
|
566
|
+
try {
|
|
567
|
+
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
|
568
|
+
logger.debug(`[Memory] 清理 runtime 目录: ${runtimeDir}`);
|
|
569
|
+
}
|
|
570
|
+
catch (cleanupError) {
|
|
571
|
+
logger.warn(`[Memory] runtime 目录清理失败(session=${sessionId}): ${cleanupError?.message}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
493
575
|
}
|
|
494
576
|
};
|
|
495
577
|
}
|
|
@@ -514,19 +596,48 @@ export function createConsolidationFeedbackSender(getChannel) {
|
|
|
514
596
|
replyContext.threadId = threadId;
|
|
515
597
|
if (chatType)
|
|
516
598
|
replyContext.delivery = { chatType };
|
|
517
|
-
|
|
518
|
-
|
|
599
|
+
// 对比期:同一份反馈 notice、text 各发一条,观察两种模式在对端的差异,
|
|
600
|
+
// 跑通后删掉 text 那条。两条 taskId 后缀不同,避免共用 operationId 被出站去重吞掉一条;
|
|
601
|
+
// 各自 try,一条失败不连累另一条。
|
|
602
|
+
const stamp = Date.now();
|
|
603
|
+
const envelopeFor = (suffix) => buildEnvelope({
|
|
604
|
+
taskId: `memory-feedback-${stamp}-${suffix}`,
|
|
519
605
|
channel: binding.adapter.channelKey,
|
|
520
606
|
channelId,
|
|
521
607
|
agentName: binding.agentName,
|
|
522
608
|
replyContext: Object.keys(replyContext).length > 0 ? replyContext : undefined,
|
|
523
|
-
}), {
|
|
524
|
-
kind: 'result.text',
|
|
525
|
-
text,
|
|
526
|
-
isFinal: true,
|
|
527
|
-
format: 'markdown',
|
|
528
609
|
});
|
|
529
|
-
|
|
610
|
+
const attempts = [
|
|
611
|
+
{
|
|
612
|
+
mode: 'notice',
|
|
613
|
+
send: () => binding.adapter.send(envelopeFor('notice'), {
|
|
614
|
+
kind: 'system.notice',
|
|
615
|
+
text,
|
|
616
|
+
subtype: 'memory.consolidation.result',
|
|
617
|
+
}),
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
mode: 'text',
|
|
621
|
+
send: () => binding.adapter.send(envelopeFor('text'), {
|
|
622
|
+
kind: 'result.text',
|
|
623
|
+
text,
|
|
624
|
+
isFinal: true,
|
|
625
|
+
format: 'markdown',
|
|
626
|
+
}),
|
|
627
|
+
},
|
|
628
|
+
];
|
|
629
|
+
const failures = [];
|
|
630
|
+
for (const { mode, send } of attempts) {
|
|
631
|
+
try {
|
|
632
|
+
await send();
|
|
633
|
+
logger.info(`[Memory] 整理反馈已投递(mode=${mode},aid=${selfAid},channelId=${channelId})`);
|
|
634
|
+
}
|
|
635
|
+
catch (error) {
|
|
636
|
+
failures.push(`${mode}: ${error?.message}`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (failures.length > 0)
|
|
640
|
+
throw new Error(failures.join('; '));
|
|
530
641
|
};
|
|
531
642
|
}
|
|
532
643
|
export function registerConsolidationSessionEvents({ eventBus, permissionGateway, finalize, }) {
|
|
@@ -673,6 +784,10 @@ export function registerConsolidationLaunchExecutor({ ipcServer, enqueueConsolid
|
|
|
673
784
|
ipcServer.setConsolidationLaunchExecutor(async (params) => {
|
|
674
785
|
if (!params.taskCtx.selfAid)
|
|
675
786
|
throw new Error('无法确定当前 agent AID(selfAid 为空)');
|
|
787
|
+
// 入队前就拦:拉起失败在队列里只会记成一句笼统的「拉起整理会话失败」
|
|
788
|
+
const blockedReason = consolidationBlockedReason(params.taskCtx.selfAid);
|
|
789
|
+
if (blockedReason)
|
|
790
|
+
throw new Error(blockedReason);
|
|
676
791
|
const launched = await enqueueConsolidation(params);
|
|
677
792
|
if (!launched)
|
|
678
793
|
throw new Error('拉起整理会话失败,详见 daemon 日志');
|
|
@@ -687,6 +802,8 @@ export function registerConsolidationCheckExecutor({ ipcServer, agentRegistry, e
|
|
|
687
802
|
const agentConfig = agentRegistry.get(selfAid)?.config;
|
|
688
803
|
if (agentConfig?.memory?.enableRelation === false)
|
|
689
804
|
return { launched: [] };
|
|
805
|
+
if (consolidationBlockedReason(selfAid))
|
|
806
|
+
return { launched: [] };
|
|
690
807
|
const ownerAid = agentConfig?.owners?.[0];
|
|
691
808
|
const agentCount = agentRegistry.runnableAgents().length;
|
|
692
809
|
const relationsDir = path.join(agentDir(selfAid), 'relations');
|
|
@@ -743,6 +860,8 @@ export function registerRelationConsolidationEvents({ eventBus, agentRegistry, r
|
|
|
743
860
|
return;
|
|
744
861
|
if (taskCtx.sessionType === 'memory')
|
|
745
862
|
return;
|
|
863
|
+
if (consolidationBlockedReason(taskCtx.selfAid))
|
|
864
|
+
return;
|
|
746
865
|
const memoryConfig = agentRegistry.get(taskCtx.selfAid)?.config?.memory;
|
|
747
866
|
if (memoryConfig?.enableRelation === false)
|
|
748
867
|
return;
|
|
@@ -807,6 +926,7 @@ export function renderDefaultConsolidationFeedback(input) {
|
|
|
807
926
|
'',
|
|
808
927
|
`**模式**:${mode}`,
|
|
809
928
|
`**触发**:${cause}`,
|
|
929
|
+
...(input.failureReason ? [`**原因**:${input.failureReason}`] : []),
|
|
810
930
|
'',
|
|
811
931
|
'本次整理未完成,已回退未归档的改动。详情见整理批次:',
|
|
812
932
|
`\`${input.batchDir}\``,
|