// REQ-LIMIT-4: hostile transcripts are a best reading, never proof of usage. // Linux dirfds anchor every lookup; no caller-provided symlink is followed. import { constants, openSync, closeSync, fstatSync, readSync, opendirSync } from 'node:fs'; import { isAbsolute } from 'node:path'; export const interfaceVersion = 1; const MAX_BYTES = 8 * 1024 * 1024; const MAX_LINE = 1024 * 1024; const MAX_ENTRIES = 512; const MAX_RECORDS = 20000; const ID = /^[A-Za-z0-9_-]{1,128}$/; const NAME = /^[A-Za-z0-9_.-]{1,240}$/; const DIR = constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW; const FILE = constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK; const refuse = code => { throw Object.assign(new Error(code), { code }); }; const at = (fd, name) => `/proc/self/fd/${fd}/${name}`; function rootFd(root) { if (typeof root !== 'string' || !isAbsolute(root) || root.split('/').includes('..')) refuse('transcript-path'); let fd = openSync('/', DIR); try { for (const part of root.split('/').filter(Boolean)) { const next = openSync(at(fd, part), DIR); closeSync(fd); fd = next; } return fd; } catch (e) { closeSync(fd); throw e; } } function readFile(parent, name, budget) { const fd = openSync(at(parent, name), FILE); try { const before = fstatSync(fd); if (!before.isFile() || before.nlink !== 1) refuse('transcript-path'); if (before.size > MAX_BYTES - budget.bytes) refuse('transcript-size'); const parts = []; let total = 0; const buffer = Buffer.alloc(65536); for (;;) { const n = readSync(fd, buffer, 0, Math.min(buffer.length, MAX_BYTES - budget.bytes + 1), null); if (!n) break; total += n; budget.bytes += n; if (budget.bytes > MAX_BYTES) refuse('transcript-size'); parts.push(Buffer.from(buffer.subarray(0, n))); } const after = fstatSync(fd); if (before.size !== after.size || total !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) refuse('transcript-changed'); return Buffer.concat(parts); } finally { closeSync(fd); } } function counters(usage, harness) { if (!usage || typeof usage !== 'object' || Array.isArray(usage)) refuse('transcript-usage'); const fields = harness === 'pi' ? ['input', 'output', 'cacheWrite', 'cacheRead'] : ['input_tokens', 'output_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens']; const values = fields.map((key, i) => harness === 'claude-code' && i > 1 ? (usage[key] ?? 0) : usage[key]); if (values.some(n => !Number.isSafeInteger(n) || n < 0)) refuse('transcript-usage'); return Object.fromEntries(['input', 'output', 'cacheWrite', 'cacheRead'].map((key, i) => [key, values[i]])); } function parse(bytes, harness, file, entries, budget) { let text; try { text = new TextDecoder('utf-8', { fatal: true }).decode(bytes); } catch { refuse('transcript-parse'); } if (text && !text.endsWith('\n')) refuse('transcript-parse'); for (const line of text.split('\n')) { if (!line.trim()) continue; if (Buffer.byteLength(line) > MAX_LINE) refuse('transcript-line'); if (++budget.records > MAX_RECORDS) refuse('transcript-records'); let record; try { record = JSON.parse(line); } catch { refuse('transcript-parse'); } if (!record || typeof record !== 'object' || Array.isArray(record)) refuse('transcript-parse'); if (harness === 'pi' ? record.type !== 'message' || record.message?.role !== 'assistant' : record.type !== 'assistant') continue; const id = harness === 'pi' ? record.id : record.message?.id; if (typeof id !== 'string' || !ID.test(id)) refuse('transcript-usage'); const value = counters(record.message?.usage, harness); const key = `${file}:${id}`; if (Object.hasOwn(entries, key) && JSON.stringify(entries[key]) !== JSON.stringify(value)) refuse('transcript-usage'); entries[key] = value; } } function walk(fd, harness, depth, prefix, entries, budget) { const directory = opendirSync(`/proc/self/fd/${fd}`); try { let entry; while ((entry = directory.readSync())) { if (++budget.entries > MAX_ENTRIES) refuse('transcript-entries'); if (!NAME.test(entry.name) || entry.name === '.' || entry.name === '..') refuse('transcript-path'); if (harness === 'claude-code' && depth === 0) { // Only the standard project directory / session.jsonl shape is admitted. if (entry.isFile()) continue; const child = openSync(at(fd, entry.name), DIR); try { walk(child, harness, 1, entry.name + '/', entries, budget); } finally { closeSync(child); } } else if (entry.name.endsWith('.jsonl')) { parse(readFile(fd, entry.name, budget), harness, prefix + entry.name, entries, budget); } } } finally { directory.closeSync(); } } export async function readTurn({ harness, root }) { let fd; try { if (!['pi', 'claude-code'].includes(harness)) refuse('transcript-harness'); fd = rootFd(root); const entries = Object.create(null); walk(fd, harness, 0, '', entries, { bytes: 0, entries: 0, records: 0 }); return { status: 'known', entries }; } catch (e) { // A new run can have no transcript directory before its first turn. if (e.code === 'ENOENT' && fd === undefined) return { status: 'known', entries: {} }; const code = typeof e.code === 'string' && e.code.startsWith('transcript-') ? e.code : 'transcript-path'; return { status: 'unknown', code }; } finally { if (fd !== undefined) closeSync(fd); } }