- 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)
103 lines
3.8 KiB
JavaScript
103 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Patch v6.6-rc2-02 · 扩展 subagent-route-injector 注入 traceId + state 占位
|
|
* AGENT_TRACE_INJECTOR_P0 · 代号 v6.6-rc2-20260422-1859
|
|
*
|
|
* 在 hooks/subagent-route-injector.js 的 "[重读纪律]" 行之后插入:
|
|
* 1. 生成 traceId = bwr-{yyyymmddhhMMss}-{rand6}
|
|
* 2. 写 state/agent-traces/{traceId}.json 占位 (pre_snapshot/claims 留 null)
|
|
* 3. additionalContext 追加 [trace:{id}] 约定 Agent 在回复末尾 echo
|
|
*
|
|
* 幂等: sentinel AGENT_TRACE_INJECTOR_P0
|
|
* 回滚: pwsh scripts/rollback-v6.6-rc2.ps1 -Scope all (自动从 .bak 恢复)
|
|
* 重签: node hooks/integrity-check.js --generate (必须)
|
|
*/
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const CLAUDE_ROOT = path.resolve(__dirname, '..', '..');
|
|
const INJECTOR_PATH = path.join(CLAUDE_ROOT, 'hooks', 'subagent-route-injector.js');
|
|
const SENTINEL = 'AGENT_TRACE_INJECTOR_P0';
|
|
const BAK_TAG = 'v6.6-rc2-02-inject-traceid';
|
|
|
|
// 锚点 (原文件第 84 行附近, 足够唯一可做字符串替换)
|
|
const ANCHOR = 'ctx += String.fromCharCode(10)+"[重读纪律] 你是 fresh 实例。不要假设之前的 Agent 做了什么,从当前代码状态独立判断。";';
|
|
|
|
// 注入段 (用 String.raw 保持 \n 等字面量, 避免模板串转义)
|
|
const INJECTION = String.raw`
|
|
|
|
// --- v6.6-rc2-02 AGENT_TRACE_INJECTOR_P0 ---
|
|
try {
|
|
const _crypto = require('crypto');
|
|
const _tsC = new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14);
|
|
const _rand6 = _crypto.randomBytes(3).toString('hex');
|
|
const _traceId = 'bwr-' + _tsC + '-' + _rand6;
|
|
const _traceFile = path.join(CLAUDE_ROOT, 'state', 'agent-traces', _traceId + '.json');
|
|
try {
|
|
fs.mkdirSync(path.dirname(_traceFile), { recursive: true });
|
|
const _placeholder = {
|
|
traceId: _traceId,
|
|
session_id: input.session_id || '',
|
|
agent_type: (input.tool_input && input.tool_input.subagent_type) || '',
|
|
started_at: new Date().toISOString(),
|
|
status: 'started',
|
|
pre_snapshot: null,
|
|
claims: null,
|
|
};
|
|
const _tmp = _traceFile + '.tmp.' + process.pid;
|
|
fs.writeFileSync(_tmp, JSON.stringify(_placeholder, null, 2));
|
|
fs.renameSync(_tmp, _traceFile);
|
|
} catch {}
|
|
ctx += String.fromCharCode(10) + '[trace: ' + _traceId + '] 请在回复末尾标注 <trace>' + _traceId + '</trace> 以便审计';
|
|
} catch {}
|
|
// --- end AGENT_TRACE_INJECTOR_P0 ---`;
|
|
|
|
function tsStamp() {
|
|
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
}
|
|
|
|
function atomicWrite(target, content) {
|
|
const tmp = target + '.tmp.' + process.pid;
|
|
fs.writeFileSync(tmp, content);
|
|
fs.renameSync(tmp, target);
|
|
}
|
|
|
|
function patchInjector() {
|
|
if (!fs.existsSync(INJECTOR_PATH)) {
|
|
console.error('[patch-02] 目标不存在: ' + INJECTOR_PATH);
|
|
return false;
|
|
}
|
|
const raw = fs.readFileSync(INJECTOR_PATH, 'utf8');
|
|
if (raw.includes(SENTINEL)) {
|
|
console.log('[patch-02] 已含 sentinel, 跳过');
|
|
return false;
|
|
}
|
|
if (!raw.includes(ANCHOR)) {
|
|
console.error('[patch-02] 未找到锚点字符串, 停止 (文件可能已被修改)');
|
|
return false;
|
|
}
|
|
// 备份
|
|
fs.copyFileSync(INJECTOR_PATH, INJECTOR_PATH + '.bak.' + BAK_TAG + '.' + tsStamp());
|
|
// 在锚点后追加注入段
|
|
const patched = raw.replace(ANCHOR, ANCHOR + INJECTION);
|
|
atomicWrite(INJECTOR_PATH, patched);
|
|
console.log('[patch-02] subagent-route-injector.js 已注入 traceId 生成段');
|
|
return true;
|
|
}
|
|
|
|
function main() {
|
|
console.log('[patch-02] v6.6-rc2-02 注入 traceId + state 占位');
|
|
const wrote = patchInjector();
|
|
if (!wrote) {
|
|
console.log('[patch-02] 补丁已生效或失败, 无变更');
|
|
} else {
|
|
console.log('[patch-02] 完成');
|
|
console.log('[patch-02] 后续必跑: node hooks/integrity-check.js --generate (重签 HMAC)');
|
|
}
|
|
process.exit(0);
|
|
}
|
|
|
|
main();
|