#!/usr/bin/env node /** * P0-1 Patch: 可观测性指标 * - prompt-dispatcher.js: 追加 metrics.emit('route', ...) 含路由延迟/置信度/技能 * - agent-claim-observer.js: 追加 model/duration_ms 字段 + metrics.emit('agent', ...) * 幂等: sentinel [P0-1] METRICS_EMIT_v1 */ 'use strict'; const fs = require('fs'); const path = require('path'); const HOOKS = path.resolve(__dirname, '..', '..', 'hooks'); const SENTINEL = '[P0-1] METRICS_EMIT_v1'; function patchFile(name, fn) { const fp = path.join(HOOKS, name); if (!fs.existsSync(fp)) { console.log('[SKIP] ' + name + ' — not found'); return false; } const src = fs.readFileSync(fp, 'utf8'); if (src.includes(SENTINEL)) { console.log('[SKIP] ' + name + ' — already patched'); return false; } fs.writeFileSync(fp + '.bak-p01.' + Date.now(), src); const out = fn(src); const tmp = fp + '.tmp.' + process.pid; fs.writeFileSync(tmp, out, 'utf8'); fs.renameSync(tmp, fp); console.log('[DONE] ' + name); return true; } // --- prompt-dispatcher.js --- function patchDispatcher(src) { const marker = "require('fs').appendFileSync(timingPath, entry);"; if (!src.includes(marker)) throw new Error('marker not found in prompt-dispatcher.js'); const lines = [ '', ' // ' + SENTINEL, " try {", " const metrics = require('./lib/metrics.js');", " let ri = {};", " try {", " const parsed = JSON.parse(stdout || '{}');", " const ctx = (parsed.hookSpecificOutput && parsed.hookSpecificOutput.additionalContext) || '';", " const cm = ctx.match(/\\u7f6e\\u4fe1\\u5ea6\\s+(\\d+)%/);", " const sm = ctx.match(/\\u4e3b\\u8def\\u7531:\\s+(\\S+)/);", " ri = { confidence: cm ? +cm[1] : null, skill: sm ? sm[1] : null };", " } catch {}", " metrics.emit('route', Object.assign({ hook: 'prompt-dispatcher', elapsed_ms: elapsed }, ri));", " } catch {}", ]; return src.replace(marker, marker + lines.join('\n')); } // --- agent-claim-observer.js --- function patchObserver(src) { // 1. 追加字段到 record 对象 const fieldMarker = ' text_length: text.length,'; if (!src.includes(fieldMarker)) throw new Error('field marker not found in agent-claim-observer.js'); const newFields = [ ' text_length: text.length,', ' // ' + SENTINEL, " model: (input.tool_input && input.tool_input.model) || '',", " tool_name: (input.tool_input && input.tool_input.name) || 'Agent',", " duration_ms: (input.tool_response && input.tool_response.duration_ms) || null,", ].join('\n'); let result = src.replace(fieldMarker, newFields); // 2. 追加 metrics emit const appendMarker = "fs.appendFileSync(LOG_FILE, JSON.stringify(record) + '\\n');"; if (!result.includes(appendMarker)) throw new Error('append marker not found'); result = result.replace( appendMarker, appendMarker + "\n // " + SENTINEL + " — 指标发射\n try { require('./lib/metrics.js').emit('agent', record); } catch {}" ); return result; } // --- Execute --- let count = 0; if (patchFile('prompt-dispatcher.js', patchDispatcher)) count++; if (patchFile('agent-claim-observer.js', patchObserver)) count++; console.log('\nP0-1 Metrics: ' + count + '/2 files patched');