export.mjs now removes hooks referencing npm packages not included in the Portable distribution (session-continuity-mcp). Eliminates MODULE_NOT_FOUND errors on Portable installations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
179 lines
9.6 KiB
JavaScript
179 lines
9.6 KiB
JavaScript
#!/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');
|