feat: Capability Evolver 升级 + 日常运行日志

主要变更:
- 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 配置
  - 忽略临时工作空间目录
This commit is contained in:
2026-03-05 19:20:35 +08:00
parent 8fb52c48fb
commit 0878535cb8
67 changed files with 3762 additions and 513 deletions
+100 -1
View File
@@ -17,6 +17,9 @@ const {
const { computeAssetId, SCHEMA_VERSION } = require('./contentHash');
const { captureEnvFingerprint } = require('./envFingerprint');
const { buildValidationReport } = require('./validationReport');
const { logAssetCall } = require('./assetCallLog');
const { recordNarrative } = require('./narrativeMemory');
const { isLlmReviewEnabled, runLlmReview } = require('./llmReview');
function nowIso() {
return new Date().toISOString();
@@ -701,10 +704,14 @@ function rollbackNewUntrackedFiles({ repoRoot, baselineUntracked }) {
// This prevents "ghost skill directories" where mkdir succeeded but
// file creation failed/was rolled back. Without this, empty dirs like
// skills/anima/, skills/oblivion/ etc. accumulate after failed innovations.
// SAFETY: never remove top-level structural directories (skills/, src/, etc.)
// or critical protected directories. Only remove leaf subdirectories.
var dirsToCheck = new Set();
for (var di = 0; di < deleted.length; di++) {
var dir = path.dirname(deleted[di]);
while (dir && dir !== '.' && dir !== '/') {
var normalized = dir.replace(/\\/g, '/');
if (!normalized.includes('/')) break;
dirsToCheck.add(dir);
dir = path.dirname(dir);
}
@@ -713,6 +720,7 @@ function rollbackNewUntrackedFiles({ repoRoot, baselineUntracked }) {
var sortedDirs = Array.from(dirsToCheck).sort(function (a, b) { return b.length - a.length; });
var removedDirs = [];
for (var si = 0; si < sortedDirs.length; si++) {
if (isCriticalProtectedPath(sortedDirs[si] + '/')) continue;
var dirAbs = path.join(repoRoot, sortedDirs[si]);
try {
var entries = fs.readdirSync(dirAbs);
@@ -957,8 +965,32 @@ function readRecentSessionInputs() {
return { recentSessionTranscript, todayLog: todayLogContent, memorySnippet, userSnippet };
}
function isGitRepo(dir) {
try {
execSync('git rev-parse --git-dir', {
cwd: dir, encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000,
});
return true;
} catch (_) {
return false;
}
}
function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true } = {}) {
const repoRoot = getRepoRoot();
if (!isGitRepo(repoRoot)) {
console.error('[Solidify] FATAL: Not a git repository (' + repoRoot + ').');
console.error('[Solidify] Solidify requires git for rollback, diff capture, and blast radius.');
console.error('[Solidify] Run "git init && git add -A && git commit -m init" first.');
return {
ok: false,
status: 'failed',
failure_reason: 'not_a_git_repository',
event: null,
};
}
const state = readStateForSolidify();
const lastRun = state && state.last_run ? state.last_run : null;
const genes = loadGenes();
@@ -1053,6 +1085,29 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
console.error(`[Solidify] CANARY FAILED: ${canary.err}`);
}
// Optional LLM review: when EVOLVER_LLM_REVIEW=true, submit diff for review.
let llmReviewResult = null;
if (constraintCheck.ok && validation.ok && protocolViolations.length === 0 && isLlmReviewEnabled()) {
try {
const reviewDiff = captureDiffSnapshot(repoRoot);
llmReviewResult = runLlmReview({
diff: reviewDiff,
gene: geneUsed,
signals,
mutation,
});
if (llmReviewResult && llmReviewResult.approved === false) {
constraintCheck.violations.push('llm_review_rejected: ' + (llmReviewResult.summary || 'no reason'));
constraintCheck.ok = false;
console.log('[LLMReview] Change REJECTED: ' + (llmReviewResult.summary || ''));
} else if (llmReviewResult) {
console.log('[LLMReview] Change approved (confidence: ' + (llmReviewResult.confidence || '?') + ')');
}
} catch (e) {
console.log('[LLMReview] Failed (non-fatal): ' + (e && e.message ? e.message : e));
}
}
// Build standardized ValidationReport (machine-readable, interoperable).
const validationReport = buildValidationReport({
geneId: geneUsed && geneUsed.id ? geneUsed.id : null,
@@ -1212,7 +1267,12 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
if (!dryRun && !success && rollbackOnFailure) {
rollbackTracked(repoRoot);
rollbackNewUntrackedFiles({ repoRoot, baselineUntracked: lastRun && lastRun.baseline_untracked ? lastRun.baseline_untracked : [] });
// Only clean up new untracked files when a valid baseline exists.
// Without a baseline, we cannot distinguish pre-existing untracked files
// from AI-generated ones, so deleting would be destructive.
if (lastRun && Array.isArray(lastRun.baseline_untracked)) {
rollbackNewUntrackedFiles({ repoRoot, baselineUntracked: lastRun.baseline_untracked });
}
}
// Apply epigenetic marks to the gene based on outcome and environment
@@ -1254,6 +1314,21 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
};
if (!dryRun) writeStateForSolidify(state);
if (!dryRun) {
try {
recordNarrative({
gene: geneUsed,
signals,
mutation,
outcome: event.outcome,
blast,
capsule,
});
} catch (e) {
console.log('[Narrative] Record failed (non-fatal): ' + (e && e.message ? e.message : e));
}
}
// Search-First Evolution: auto-publish eligible capsules to the Hub (as Gene+Capsule bundle).
let publishResult = null;
if (!dryRun && capsule && capsule.a2a && capsule.a2a.eligible_to_broadcast) {
@@ -1327,6 +1402,21 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
});
}
publishResult = { attempted: true, asset_id: capsule.asset_id || capsule.id, bundle: true };
logAssetCall({
run_id: lastRun && lastRun.run_id ? lastRun.run_id : null,
action: 'asset_publish',
asset_id: capsule.asset_id || capsule.id,
asset_type: 'Capsule',
source_node_id: null,
chain_id: publishChainId || null,
signals: Array.isArray(capsule.trigger) ? capsule.trigger : [],
extra: {
source_type: sourceType,
reused_asset_id: reusedAssetId,
gene_id: publishGene && publishGene.id ? publishGene.id : null,
parent: parentRef || null,
},
});
} else {
publishResult = { attempted: false, reason: 'no_hub_url' };
}
@@ -1340,6 +1430,14 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
: sourceType === 'reused' ? 'skip_direct_reused_asset'
: 'below_min_score';
publishResult = { attempted: false, reason };
logAssetCall({
run_id: lastRun && lastRun.run_id ? lastRun.run_id : null,
action: 'asset_publish_skip',
asset_id: capsule.asset_id || capsule.id,
asset_type: 'Capsule',
reason,
signals: Array.isArray(capsule.trigger) ? capsule.trigger : [],
});
}
}
@@ -1452,6 +1550,7 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
module.exports = {
solidify,
isGitRepo,
readStateForSolidify,
writeStateForSolidify,
isValidationCommandAllowed,