@phren/cli
Version:
Knowledge layer for AI agents - CLI, MCP server, and data layer
68 lines (67 loc) • 2.76 kB
JavaScript
/**
* Session metrics tracking.
* Extracted from hooks-session.ts for modularity.
*/
import * as fs from "fs";
import * as path from "path";
import { debugLog, errorMessage, withFileLock, getQualityMultiplier, recordFeedback, } from "./hooks-context.js";
import { sessionMetricsFile } from "../shared.js";
function parseSessionMetrics(phrenPathLocal) {
const file = sessionMetricsFile(phrenPathLocal);
if (!fs.existsSync(file))
return {};
try {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
catch (err) {
debugLog(`parseSessionMetrics: failed to read ${file}: ${errorMessage(err)}`);
return {};
}
}
function writeSessionMetrics(phrenPathLocal, data) {
const file = sessionMetricsFile(phrenPathLocal);
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
}
function updateSessionMetrics(phrenPathLocal, updater) {
const file = sessionMetricsFile(phrenPathLocal);
withFileLock(file, () => {
const metrics = parseSessionMetrics(phrenPathLocal);
updater(metrics);
writeSessionMetrics(phrenPathLocal, metrics);
});
}
export function trackSessionMetrics(phrenPathLocal, sessionId, selected) {
updateSessionMetrics(phrenPathLocal, (metrics) => {
if (!metrics[sessionId])
metrics[sessionId] = { prompts: 0, keys: {}, lastChangedCount: 0, lastKeys: [] };
metrics[sessionId].prompts += 1;
const injectedKeys = [];
for (const injected of selected) {
injectedKeys.push(injected.key);
const key = injected.key;
const seen = metrics[sessionId].keys[key] || 0;
metrics[sessionId].keys[key] = seen + 1;
if (seen >= 1)
recordFeedback(phrenPathLocal, key, "reprompt");
}
const relevantCount = selected.filter((s) => getQualityMultiplier(phrenPathLocal, s.key) > 0.5).length;
const prevRelevant = metrics[sessionId].lastChangedCount || 0;
const prevKeys = metrics[sessionId].lastKeys || [];
if (relevantCount > prevRelevant) {
for (const prevKey of prevKeys) {
recordFeedback(phrenPathLocal, prevKey, "helpful");
}
}
metrics[sessionId].lastChangedCount = relevantCount;
metrics[sessionId].lastKeys = injectedKeys;
metrics[sessionId].lastSeen = new Date().toISOString();
const thirtyDaysAgo = Date.now() - 30 * 86400000;
for (const sid of Object.keys(metrics)) {
const seen = metrics[sid].lastSeen;
if (seen && new Date(seen).getTime() < thirtyDaysAgo) {
delete metrics[sid];
}
}
});
}