#!/usr/bin/env node /** * SubagentStart Hook: 子 Agent 路由上下文注入 (v5.2 Neural Gateway) * * 将父级路由信息注入子 Agent 上下文, * 确保子 Agent 感知当前路由决策。 * * stdin: { session_id, hook_event_name: "SubagentStart" } * stdout: JSON { hookSpecificOutput: { additionalContext } } * 退出码: 始终 0 */ const fs = require('fs'); const path = require('path'); const readStdin = require('./lib/read-stdin.js'); const STATE_EXPIRY_MS = 5 * 60 * 1000; // 5 分钟过期 const CLAUDE_ROOT = require('./lib/root.js'); const DEBUG_DIR = path.join(CLAUDE_ROOT, 'debug'); const STATE_FILE = path.join(DEBUG_DIR, 'route-state-current.json'); /** * 加载路由状态 (含过期检查) * @returns {Object|null} */ function loadRouteState() { try { if (!fs.existsSync(STATE_FILE)) return null; const state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); // 过期检查 if (state.ts) { const tsMs = new Date(state.ts).getTime(); if (isNaN(tsMs)) return null; const age = Date.now() - tsMs; if (age > STATE_EXPIRY_MS) return null; } return state; } catch { return null; } } // === 主流程 === function main() { readStdin({ maxSize: 128 * 1024 }).then(input => { // 读取路由状态 const state = loadRouteState(); if (!state) { // 无状态或已过期 → 静默放行 process.exit(0); return; } const primary = state.routing?.primary || 'developer-expert'; const confidence = state.routing?.confidence || 0; const complexity = state.intent?.complexity || 'medium'; const intents = (state.intent?.intents || []).join(','); const chain = (state.routing?.chain || []).join(' → '); let ctx = `[BWR 子Agent上下文] 父级路由: ${primary}, 置信度: ${(confidence * 100).toFixed(0)}%`; ctx += `, 复杂度: ${complexity}, 意图: ${intents}`; if (chain) { ctx += `, 技能链: ${chain}`; } // --- v6.6: Context Anchors Lite + 重读纪律 --- // 安全设计: 只注入路径提示, Agent 自行用 Read 工具获取 (经过 block-sensitive-reads) try { const anchorsFile = path.join(CLAUDE_ROOT, "context-anchors.json"); if (fs.existsSync(anchorsFile)) { const anchors = JSON.parse(fs.readFileSync(anchorsFile, "utf8")); const files = (anchors.files || []).slice(0, 10); if (files.length > 0) { ctx += String.fromCharCode(10)+"[CONTEXT_ANCHOR] 上下文锚点文件 (请用 Read 工具重读): " + files.join(", "); } } } catch {} ctx += String.fromCharCode(10)+"[重读纪律] 你是 fresh 实例。不要假设之前的 Agent 做了什么,从当前代码状态独立判断。"; // --- 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 + '] 请在回复末尾标注 ' + _traceId + ' 以便审计'; } catch {} // --- end AGENT_TRACE_INJECTOR_P0 --- const output = { hookSpecificOutput: { hookEventName: 'SubagentStart', additionalContext: ctx, }, }; process.stdout.write(JSON.stringify(output)); process.exit(0); }).catch(() => process.exit(0)); } // 模块导出 (供测试) if (typeof module !== 'undefined') { module.exports = { loadRouteState, CLAUDE_ROOT: require("./lib/root.js") }; } if (require.main === module) { main(); }