everything-claude-code/scripts/lib/session-bridge.js
Affaan Mustafa 940135ea47 feat: add ECC statusline observability hooks
Salvages the useful statusline/context monitor work from stale PR #1504 while preserving the current continuous-learning hook runner wiring.

Adds the metrics bridge, context monitor, statusline script, shared cost/session bridge utilities, and tests. Fixes the reviewed false loop-detection hash collision for non-file tools, avoids default-session cost inflation, sanitizes statusline task lookup, and records hook payload session IDs in cost-tracker.
2026-05-11 23:44:06 -04:00

82 lines
2.1 KiB
JavaScript

'use strict';
/**
* Shared session bridge utilities for ECC hooks.
*
* The bridge file is a small JSON aggregate in /tmp that allows
* statusline, metrics-bridge, and context-monitor to share state
* without scanning large JSONL logs on every invocation.
*/
const fs = require('fs');
const os = require('os');
const path = require('path');
const MAX_SESSION_ID_LENGTH = 64;
/**
* Sanitize a session ID for safe use in file paths.
* Rejects path traversal, strips unsafe chars, limits length.
* @param {string} raw
* @returns {string|null} Safe session ID or null if invalid
*/
function sanitizeSessionId(raw) {
if (!raw || typeof raw !== 'string') return null;
if (/[/\\]|\.\./.test(raw)) return null;
const safe = raw.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, MAX_SESSION_ID_LENGTH);
return safe || null;
}
/**
* Get the bridge file path for a session.
* @param {string} sessionId - Already-sanitized session ID
* @returns {string}
*/
function getBridgePath(sessionId) {
return path.join(os.tmpdir(), `ecc-metrics-${sessionId}.json`);
}
/**
* Read bridge data. Returns null on any error.
* @param {string} sessionId - Already-sanitized session ID
* @returns {object|null}
*/
function readBridge(sessionId) {
try {
const raw = fs.readFileSync(getBridgePath(sessionId), 'utf8');
return JSON.parse(raw);
} catch {
return null;
}
}
/**
* Write bridge data atomically (write .tmp then rename).
* @param {string} sessionId - Already-sanitized session ID
* @param {object} data
*/
function writeBridgeAtomic(sessionId, data) {
const target = getBridgePath(sessionId);
const tmp = `${target}.tmp`;
fs.writeFileSync(tmp, JSON.stringify(data), 'utf8');
fs.renameSync(tmp, target);
}
/**
* Resolve session ID from environment variables.
* @returns {string|null} Sanitized session ID or null
*/
function resolveSessionId() {
const raw = process.env.ECC_SESSION_ID || process.env.CLAUDE_SESSION_ID || '';
return sanitizeSessionId(raw);
}
module.exports = {
sanitizeSessionId,
getBridgePath,
readBridge,
writeBridgeAtomic,
resolveSessionId,
MAX_SESSION_ID_LENGTH
};