bookworm-smart-assistant/scripts/patches/patch-p1-agent-model-privileges.js
Bookworm Admin b7a8e29d21 release: v6.7.0 - OTA E2E test release
- VERSION file as authoritative version source
- export.mjs reads VERSION with package.json fallback
- bw-ota.ps1 DryRun mode for safe testing
- auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
2026-04-27 17:59:44 +08:00

69 lines
3.1 KiB
JavaScript

#!/usr/bin/env node
/**
* P1 Patch: Agent 动态模型 + 最小权限
* - model: opus(质量关键) / sonnet(平衡) / haiku(速度)
* - allowed-tools: 按角色最小权限
* 幂等: 检查 frontmatter 是否已含 model/allowed-tools
*/
'use strict';
const fs = require('fs');
const path = require('path');
const AGENTS_DIR = path.resolve(__dirname, '..', '..', 'agents');
const CONFIG = {
'orchestrator': { model: 'sonnet' },
'production-reviewer': { model: 'opus', tools: 'Read, Glob, Grep, Bash, WebFetch, WebSearch' },
'red-team-attacker': { model: 'opus', tools: 'Read, Glob, Grep, Bash' },
'red-team-logic': { model: 'opus', tools: 'Read, Glob, Grep, Bash' },
'security-hardener': { model: 'sonnet', tools: 'Read, Edit, Write, Glob, Grep, Bash' },
'full-stack-builder': { model: 'sonnet', tools: 'Read, Edit, Write, Glob, Grep, Bash' },
'module-integrator': { model: 'sonnet', tools: 'Read, Edit, Write, Glob, Grep, Bash' },
'quality-gate': { model: 'sonnet', tools: 'Read, Glob, Grep, Bash' },
'research-analyst': { model: 'sonnet', tools: 'Read, Glob, Grep, Bash, WebFetch, WebSearch' },
'self-auditor': { model: 'sonnet', tools: 'Read, Glob, Grep, Bash' },
'self-healer': { model: 'sonnet', tools: 'Read, Edit, Write, Glob, Grep, Bash' },
'canvas-ui-designer': { model: 'sonnet', tools: 'Read, Edit, Write, Glob, Grep, Bash' },
'delivery-quality-assessor':{ model: 'sonnet', tools: 'Read, Glob, Grep, Bash' },
'explore': { model: 'haiku', tools: 'Read, Glob, Grep, Bash' },
'desktop-automator': { model: 'haiku' },
};
function patchAgent(name, cfg) {
const fp = path.join(AGENTS_DIR, name + '.md');
if (!fs.existsSync(fp)) { process.stderr.write('[SKIP] ' + name + ' not found\n'); return false; }
const src = fs.readFileSync(fp, 'utf8');
const lines = src.split('\n');
let inFm = false, closeIdx = -1;
for (let i = 0; i < lines.length; i++) {
if (lines[i].trim() === '---') {
if (!inFm) { inFm = true; continue; }
closeIdx = i; break;
}
}
if (closeIdx === -1) { process.stderr.write('[SKIP] ' + name + ' no frontmatter\n'); return false; }
const fmBlock = lines.slice(1, closeIdx).join('\n');
const toAdd = [];
if (cfg.model && !fmBlock.includes('model:')) toAdd.push('model: ' + cfg.model);
if (cfg.tools && !fmBlock.includes('allowed-tools:')) toAdd.push('allowed-tools: "' + cfg.tools + '"');
if (toAdd.length === 0) { process.stderr.write('[SKIP] ' + name + ' already configured\n'); return false; }
fs.writeFileSync(fp + '.bak-p1.' + Date.now(), src);
lines.splice(closeIdx, 0, ...toAdd);
const tmp = fp + '.tmp.' + process.pid;
fs.writeFileSync(tmp, lines.join('\n'), 'utf8');
fs.renameSync(tmp, fp);
process.stderr.write('[DONE] ' + name + ' +' + toAdd.map(l => l.split(':')[0]).join(',') + '\n');
return true;
}
let count = 0;
for (const [name, cfg] of Object.entries(CONFIG)) {
if (patchAgent(name, cfg)) count++;
}
process.stderr.write('\nP1 Agent Config: ' + count + '/' + Object.keys(CONFIG).length + ' agents updated\n');