// hooks/lib/approval-record.mjs // What the person was asked. pre-bash-executor.mjs writes one record per // asked call, keyed by session_id + tool_use_id (both in PreToolUse and // PostToolUse input); post-approval-log.mjs takes it after the call ran and // logs exactly that -- never a re-reading of the command afterwards, which // can see another branch than the one the question named. No record = the // person was not asked = nothing is logged. // // Records live in the OS temp dir (voyage-approvals-/), are removed when taken, // and anything older than a day is swept on the next write. import { mkdirSync, writeFileSync, readFileSync, rmSync, readdirSync, statSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; // One directory per user: another user's directory in a shared /tmp is never ours. const DIR = join(tmpdir(), `voyage-approvals-${process.getuid ? process.getuid() : 'u'}`); const MAX_AGE_MS = 24 * 60 * 60 * 1000; function pathFor(sessionId, toolUseId) { const key = createHash('sha256').update(`${sessionId || ''}\n${toolUseId}`).digest('hex').slice(0, 32); return join(DIR, `${key}.json`); } function sweep() { try { const now = Date.now(); for (const f of readdirSync(DIR)) { const p = join(DIR, f); try { if (now - statSync(p).mtimeMs > MAX_AGE_MS) rmSync(p, { force: true }); } catch { /* raced */ } } } catch { /* no dir yet */ } } /** @returns {boolean} written */ export function writeApprovalRecord(sessionId, toolUseId, record) { if (!toolUseId) return false; try { mkdirSync(DIR, { recursive: true }); sweep(); writeFileSync(pathFor(sessionId, toolUseId), JSON.stringify(record)); return true; } catch { return false; } } /** Read and remove the record; null when there is none. */ export function takeApprovalRecord(sessionId, toolUseId) { if (!toolUseId) return null; const p = pathFor(sessionId, toolUseId); try { const rec = JSON.parse(readFileSync(p, 'utf8')); rmSync(p, { force: true }); return rec; } catch { return null; } }