#!/usr/bin/env node /** * patch-route-precision-10x-batch-b2.js * 路由精度10项改进 — Batch B2: route-interceptor-bundle.js * Item 2: 图片继承链修复 — tryInherit() 处理 primary='none' 时保留 lastValidPrimary * Item 9: 确认词强制继承 — 短确认词精确匹配时 force tryInherit() * * 注意: 文件含 CRLF 行尾,使用正则 \r?\n 匹配,输出保留原文件行尾格式 */ 'use strict'; const fs = require('fs'); const path = require('path'); const SENTINEL_2 = 'IMAGE_INHERIT_LAST_VALID_PRIMARY_v1_APPLIED'; const SENTINEL_9 = 'CONFIRM_WORDS_FORCE_INHERIT_v1_APPLIED'; const TARGET = path.join(__dirname, '..', '..', 'hooks', 'route-interceptor-bundle.js'); const BAK = TARGET + '.bak'; if (!fs.existsSync(TARGET)) { console.error('[ERROR] route-interceptor-bundle.js not found:', TARGET); process.exit(1); } let src = fs.readFileSync(TARGET, 'utf8'); // Detect line ending used by file (CRLF or LF) const CRLF = src.includes('\r\n'); const NL = CRLF ? '\r\n' : '\n'; const alreadyItem2 = src.includes(SENTINEL_2); const alreadyItem9 = src.includes(SENTINEL_9); if (alreadyItem2 && alreadyItem9) { console.log('[SKIP] Both Item 2 and Item 9 patches already applied.'); process.exit(0); } fs.writeFileSync(BAK, src, 'utf8'); console.log('[BAK] Backed up to', BAK); // Helper: join lines using the file's native line ending function L(...lines) { return lines.join(NL); } // ───────────────────────────────────────────────────────────── // Item 2: 图片继承链修复 // Replace the tryInherit function body using a regex that handles CRLF/LF // ───────────────────────────────────────────────────────────── if (!alreadyItem2) { // Match the tryInherit function — use a regex with \r?\n throughout // The function spans from "function tryInherit() {" to its closing "}" // We match the exact known structure const tryInheritRegex = /function tryInherit\(\) \{\r?\n\s+if \(!_cachedPrevState\) return null;\r?\n\s+const prevTs = _cachedPrevState\.ts \? new Date\(_cachedPrevState\.ts\)\.getTime\(\) : 0;\r?\n\s+const elapsed = Date\.now\(\) - prevTs;\r?\n\s+if \(\r?\n\s+elapsed > INHERIT_WINDOW_MS \|\|\r?\n\s+!_cachedPrevState\.routing\?\.primary \|\|\r?\n\s+_cachedPrevState\.routing\.primary === 'none'\r?\n\s+\) return null;\r?\n\s+const prevRouting = _cachedPrevState\.routing;\r?\n\s+return \{\r?\n\s+primary: prevRouting\.primary,\r?\n\s+candidates: \(prevRouting\.candidates \|\| \[\]\)\.map\(c => \(\{\r?\n\s+\.\.\.c,\r?\n\s+confidence: Math\.round\(c\.confidence \* 0\.7 \* 100\) \/ 100,\r?\n\s+\}\)\),\r?\n\s+confidence: Math\.round\(\(prevRouting\.confidence \|\| 0\) \* 0\.7 \* 100\) \/ 100,\r?\n\s+chain: prevRouting\.chain \|\| \[\],\r?\n\s+\/\/ 宪法 13\.1: 继承路由保留 mustInvoke 标记\r?\n\s+_inheritedMustInvoke: _cachedPrevState\.mustInvoke \|\| false,\r?\n\s+\};\r?\n\s+\}/; // Build the replacement using the file's native line ending const ind6 = ' '; // 6 spaces (inner function body) const ind8 = ' '; // 8 spaces (inside function) const replacement = L( `// ${SENTINEL_2}`, `${ind6}function tryInherit() {`, `${ind8}if (!_cachedPrevState) return null;`, `${ind8}const prevTs = _cachedPrevState.ts ? new Date(_cachedPrevState.ts).getTime() : 0;`, `${ind8}const elapsed = Date.now() - prevTs;`, `${ind8}if (elapsed > INHERIT_WINDOW_MS) return null;`, ``, `${ind8}// Item 2: 图片继承链修复 — primary='none' 时回退到 lastValidPrimary`, `${ind8}let prevRouting = _cachedPrevState.routing;`, `${ind8}if (!prevRouting) return null;`, `${ind8}let effectivePrimary = prevRouting.primary;`, `${ind8}if (!effectivePrimary || effectivePrimary === 'none') {`, `${ind8} const lvp = _cachedPrevState.lastValidPrimary || (prevRouting && prevRouting.lastValidPrimary);`, `${ind8} if (!lvp || lvp === 'none') return null;`, `${ind8} effectivePrimary = lvp;`, `${ind8}}`, `${ind8}return {`, `${ind8} primary: effectivePrimary,`, `${ind8} candidates: (prevRouting.candidates || []).map(c => ({`, `${ind8} ...c,`, `${ind8} confidence: Math.round(c.confidence * 0.7 * 100) / 100,`, `${ind8} })),`, `${ind8} confidence: Math.round((prevRouting.confidence || 0) * 0.7 * 100) / 100,`, `${ind8} chain: prevRouting.chain || [],`, `${ind8} // 宪法 13.1: 继承路由保留 mustInvoke 标记`, `${ind8} _inheritedMustInvoke: _cachedPrevState.mustInvoke || false,`, `${ind8}};`, `${ind6}}` ); if (!tryInheritRegex.test(src)) { console.error('[ERROR Item2] tryInherit regex did not match. Skipping Item 2 tryInherit patch.'); } else { src = src.replace(tryInheritRegex, replacement); console.log('[DONE] Item 2a: tryInherit() patched with lastValidPrimary fallback'); } // Inject lastValidPrimary maintenance before writeRouteState call // Match: "// 写入 route-state\r?\n writeRouteState(traceId, prompt, intent, routing);" const writeStateRegex = /\/\/ 写入 route-state\r?\n(\s+)writeRouteState\(traceId, prompt, intent, routing\);/; const writeStateMatch = writeStateRegex.exec(src); if (!writeStateMatch) { console.error('[ERROR Item2] writeRouteState anchor not found. lastValidPrimary maintenance skipped.'); } else { const ind = writeStateMatch[1]; // actual indentation of the writeRouteState call const writeReplacement = L( `// Item 2: 维护 lastValidPrimary — 供后续 tryInherit() 使用`, `${ind}if (routing.primary && routing.primary !== 'none') {`, `${ind} routing.lastValidPrimary = routing.primary;`, `${ind}} else if (_cachedPrevState) {`, `${ind} const _oldLvp = _cachedPrevState.lastValidPrimary ||`, `${ind} (_cachedPrevState.routing && _cachedPrevState.routing.lastValidPrimary);`, `${ind} if (_oldLvp && _oldLvp !== 'none') routing.lastValidPrimary = _oldLvp;`, `${ind}}`, ``, `${ind}// 写入 route-state`, `${ind}writeRouteState(traceId, prompt, intent, routing);` ); src = src.replace(writeStateRegex, writeReplacement); console.log('[DONE] Item 2b: lastValidPrimary maintenance injected before writeRouteState'); } } // ───────────────────────────────────────────────────────────── // Item 9: 确认词强制继承 // Insert confirm-word check before the isImageQuery branch // ───────────────────────────────────────────────────────────── if (!alreadyItem9) { // Match "if (isImageQuery) {\r?\n // 斧四..." const flowRegex = /if \(isImageQuery\) \{\r?\n(\s+)\/\/ 斧四: 图片查询/; const flowMatch = flowRegex.exec(src); if (!flowMatch) { console.error('[ERROR Item9] isImageQuery flow anchor not found. Skipping Item 9 patch.'); } else { const innerInd = flowMatch[1]; // indentation inside the if block const outerInd = innerInd.slice(0, innerInd.length - 2); // one level out (2 spaces less) const confirmBlock = L( `// ${SENTINEL_9}`, `${outerInd}// Item 9: 确认词强制继承 — 精确短确认词直接 tryInherit(),不走 TF-IDF`, `${outerInd}const _CONFIRM_WORDS = ['执行', '开始', '继续', '确认', '好的', '行', '可以', 'go', 'yes', 'proceed', 'ok'];`, `${outerInd}const _promptTrimmed = prompt.trim().toLowerCase();`, `${outerInd}const _isConfirmWord = _CONFIRM_WORDS.some(w => _promptTrimmed === w) ||`, `${outerInd} (_promptTrimmed.length <= 4 && _CONFIRM_WORDS.some(w => _promptTrimmed.includes(w)));`, ``, `${outerInd}if (isImageQuery) {`, `${innerInd}// 斧四: 图片查询` ); src = src.replace(flowRegex, confirmBlock); // Now insert the _isConfirmWord branch AFTER the isImageQuery block close // Find "} else if (intent.complexity === 'simple') {" and prepend the confirm branch const simpleRegex = /(\} else if \(intent\.complexity === 'simple'\) \{)/; if (!simpleRegex.test(src)) { console.error('[ERROR Item9] simple-complexity anchor not found. Confirm branch not injected.'); } else { const confirmBranch = L( `} else if (_isConfirmWord) {`, `${innerInd}// Item 9: 确认词 → 强制继承,使用 lastValidPrimary 机制`, `${innerInd}const _confirmInherit = tryInherit();`, `${innerInd}if (_confirmInherit && _confirmInherit.primary && _confirmInherit.primary !== 'none') {`, `${innerInd} routing = _confirmInherit;`, `${innerInd} inherited = true;`, `${innerInd}} else {`, `${innerInd} routing = { primary: 'none', candidates: [], confidence: 0, chain: [] };`, `${innerInd}}`, `${outerInd}} else if (intent.complexity === 'simple') {` ); src = src.replace(simpleRegex, confirmBranch); console.log('[DONE] Item 9: confirm-words force-inherit injected into routing flow'); } } } // ── 写入 ────────────────────────────────────────────────────── fs.writeFileSync(TARGET, src, 'utf8'); console.log('\n[SUMMARY] route-interceptor-bundle.js patched.'); console.log(' Item 2 (image inherit chain):', alreadyItem2 ? 'SKIPPED (already applied)' : 'DONE'); console.log(' Item 9 (confirm words inherit):', alreadyItem9 ? 'SKIPPED (already applied)' : 'DONE');