()
| 82 | } |
| 83 | |
| 84 | function main() { |
| 85 | const mode = getMode(); |
| 86 | |
| 87 | // Absolute watchdog, armed at process entry and INDEPENDENT of the search |
| 88 | // budget. Fires at ABSOLUTE_DEADLINE_MS (3300ms) — strictly under the host's |
| 89 | // timeout (5000ms) so the host can never kill us mid-write. If stdin never |
| 90 | // closes OR anything hangs, we emit {} and exit cleanly first. |
| 91 | watchdog = setTimeout(() => finish({}), ABSOLUTE_DEADLINE_MS); |
| 92 | |
| 93 | let buf = ''; |
| 94 | try { |
| 95 | process.stdin.setEncoding('utf8'); |
| 96 | } catch (_) { /* some hosts pass no stdin */ } |
| 97 | process.stdin.on('data', (c) => { buf += c; }); |
| 98 | process.stdin.on('error', () => finish({})); |
| 99 | process.stdin.on('end', () => { |
| 100 | if (handled) return; |
| 101 | |
| 102 | // off: do NOT even parse the prompt body (privacy: nothing read/sent). |
| 103 | if (mode === 'off') return finish({}); |
| 104 | |
| 105 | let prompt = ''; |
| 106 | let sessionId = ''; |
| 107 | try { |
| 108 | const input = buf.trim() ? JSON.parse(buf) : {}; |
| 109 | prompt = String(input.prompt || '').trim(); |
| 110 | sessionId = String(input.session_id || input.sessionId || '').trim(); |
| 111 | } catch (_) { |
| 112 | return finish({}); |
| 113 | } |
| 114 | if (prompt.length < 8) return finish({}); // trivial prompt -> skip |
| 115 | |
| 116 | // Heavy require INSIDE try/catch: a broken require graph must fail open, |
| 117 | // not crash the user's prompt (this is also why the e2e test runs the |
| 118 | // copied hook with mode=off and asserts exit 0 + parseable stdout). |
| 119 | let core; |
| 120 | try { |
| 121 | const { findEvolverRoot } = require('./_runtimePaths'); |
| 122 | const root = findEvolverRoot(); |
| 123 | if (!root) return finish({}); |
| 124 | core = require(path.join(root, 'src', 'gep', 'recallInject.js')); |
| 125 | } catch (_) { |
| 126 | return finish({}); |
| 127 | } |
| 128 | |
| 129 | // Pass the ABSOLUTE deadline (T0 + watchdog window) plus the configured |
| 130 | // search cap. The core bounds the Hub call by the time REMAINING to that |
| 131 | // deadline (minus its own post-await safety margin) and runs local-gene |
| 132 | // disk I/O BEFORE the Hub await — so neither the Hub search nor the |
| 133 | // post-processing can overrun the watchdog (Bugbot #183: slow startup or a |
| 134 | // budget-eating Hub call must not let the timer fire mid-work). If too |
| 135 | // little time remains even before starting, skip and fail open. |
| 136 | const elapsed = Date.now() - T0; |
| 137 | const remaining = ABSOLUTE_DEADLINE_MS - elapsed - SEARCH_SAFETY_MS; |
| 138 | if (remaining < MIN_SEARCH_MS) return finish({}); |
| 139 | const deadlineMs = T0 + ABSOLUTE_DEADLINE_MS; |
| 140 | |
| 141 | Promise.resolve() |
no test coverage detected