0878535cb8
主要变更: - Capability Evolver 技能大幅升级 - 新增 LLM 审查、叙事记忆、反思机制模块 - 改进 Agent 间通信协议和技能搜索 - 整合 Git 备份到 evolver-run.sh - Agent 配置与日志更新 - dev-assist/haonan/main 认证和会话配置 - 5 个 agent 的 AGENTS.md 文档更新 - haonan 的 MEMORY.md 和 SOUL.md 更新 - 新增功能目录 - hooks/self-improvement 自我改进钩子 - memory/evolution 进化记忆状态 - workspace/.learnings/ 学习记录 - 清理 - 删除 scripts/git-auto-sync.sh(已整合到 evolver) - 删除旧的 _meta.json 配置 - 忽略临时工作空间目录
109 lines
3.7 KiB
JavaScript
109 lines
3.7 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { getNarrativePath, getEvolutionDir } = require('./paths');
|
|
|
|
const MAX_NARRATIVE_ENTRIES = 30;
|
|
const MAX_NARRATIVE_SIZE = 12000;
|
|
|
|
function ensureDir(dir) {
|
|
try { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } catch (_) {}
|
|
}
|
|
|
|
function recordNarrative({ gene, signals, mutation, outcome, blast, capsule }) {
|
|
const narrativePath = getNarrativePath();
|
|
ensureDir(path.dirname(narrativePath));
|
|
|
|
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
const geneId = gene && gene.id ? gene.id : '(auto)';
|
|
const category = (mutation && mutation.category) || (gene && gene.category) || 'unknown';
|
|
const status = outcome && outcome.status ? outcome.status : 'unknown';
|
|
const score = outcome && typeof outcome.score === 'number' ? outcome.score.toFixed(2) : '?';
|
|
const signalsSummary = Array.isArray(signals) ? signals.slice(0, 4).join(', ') : '(none)';
|
|
const filesChanged = blast ? blast.files : 0;
|
|
const linesChanged = blast ? blast.lines : 0;
|
|
const rationale = mutation && mutation.rationale
|
|
? String(mutation.rationale).slice(0, 200) : '';
|
|
const strategy = gene && Array.isArray(gene.strategy)
|
|
? gene.strategy.slice(0, 3).map((s, i) => ` ${i + 1}. ${s}`).join('\n') : '';
|
|
const capsuleSummary = capsule && capsule.summary ? String(capsule.summary).slice(0, 200) : '';
|
|
|
|
const entry = [
|
|
`### [${ts}] ${category.toUpperCase()} - ${status}`,
|
|
`- Gene: ${geneId} | Score: ${score} | Scope: ${filesChanged} files, ${linesChanged} lines`,
|
|
`- Signals: [${signalsSummary}]`,
|
|
rationale ? `- Why: ${rationale}` : null,
|
|
strategy ? `- Strategy:\n${strategy}` : null,
|
|
capsuleSummary ? `- Result: ${capsuleSummary}` : null,
|
|
'',
|
|
].filter(line => line !== null).join('\n');
|
|
|
|
let existing = '';
|
|
try {
|
|
if (fs.existsSync(narrativePath)) {
|
|
existing = fs.readFileSync(narrativePath, 'utf8');
|
|
}
|
|
} catch (_) {}
|
|
|
|
if (!existing.trim()) {
|
|
existing = '# Evolution Narrative\n\nA chronological record of evolution decisions and outcomes.\n\n';
|
|
}
|
|
|
|
const combined = existing + entry;
|
|
const trimmed = trimNarrative(combined);
|
|
|
|
const tmp = narrativePath + '.tmp';
|
|
fs.writeFileSync(tmp, trimmed, 'utf8');
|
|
fs.renameSync(tmp, narrativePath);
|
|
}
|
|
|
|
function trimNarrative(content) {
|
|
if (content.length <= MAX_NARRATIVE_SIZE) return content;
|
|
|
|
const headerEnd = content.indexOf('###');
|
|
if (headerEnd < 0) return content.slice(-MAX_NARRATIVE_SIZE);
|
|
|
|
const header = content.slice(0, headerEnd);
|
|
const entries = content.slice(headerEnd).split(/(?=^### \[)/m);
|
|
|
|
while (entries.length > MAX_NARRATIVE_ENTRIES) {
|
|
entries.shift();
|
|
}
|
|
|
|
let result = header + entries.join('');
|
|
if (result.length > MAX_NARRATIVE_SIZE) {
|
|
const keep = Math.max(1, entries.length - 5);
|
|
result = header + entries.slice(-keep).join('');
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function loadNarrativeSummary(maxChars) {
|
|
const limit = Number.isFinite(maxChars) ? maxChars : 4000;
|
|
const narrativePath = getNarrativePath();
|
|
try {
|
|
if (!fs.existsSync(narrativePath)) return '';
|
|
const content = fs.readFileSync(narrativePath, 'utf8');
|
|
if (!content.trim()) return '';
|
|
|
|
const headerEnd = content.indexOf('###');
|
|
if (headerEnd < 0) return '';
|
|
|
|
const entries = content.slice(headerEnd).split(/(?=^### \[)/m);
|
|
const recent = entries.slice(-8);
|
|
let summary = recent.join('');
|
|
if (summary.length > limit) {
|
|
summary = summary.slice(-limit);
|
|
const firstEntry = summary.indexOf('### [');
|
|
if (firstEntry > 0) summary = summary.slice(firstEntry);
|
|
}
|
|
return summary.trim();
|
|
} catch (_) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
module.exports = { recordNarrative, loadNarrativeSummary, trimNarrative };
|