Files
BridgeSwarm/native-host/agent/sessions.js
T
snxraven 9a5cbbe1ff
CI / Build & Test (push) Successful in 28m18s
Updates to Agent QVAC
2026-09-03 16:06:16 -04:00

154 lines
3.7 KiB
JavaScript

/**
* JSONL session store under BRIDGE_SWARM_STORAGE/agent/sessions/.
*/
const path = require('bare-path');
const fs = require('bare-fs');
const { ensureAgentRoot, ensureDir, sanitizeId } = require('../capabilities/paths.js');
function sessionsRoot() {
return ensureDir(path.join(ensureAgentRoot(), 'sessions'));
}
function sessionDir(id) {
return ensureDir(path.join(sessionsRoot(), sanitizeId(id)));
}
function readJsonl(file) {
try {
const raw = fs.readFileSync(file, 'utf8');
return raw
.split('\n')
.map((l) => l.trim())
.filter(Boolean)
.map((l) => {
try {
return JSON.parse(l);
} catch (_) {
return null;
}
})
.filter(Boolean);
} catch (_) {
return [];
}
}
function appendJsonl(file, obj) {
fs.appendFileSync(file, JSON.stringify(obj) + '\n');
}
function makeId() {
return 'sess_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
}
function create(meta) {
const id = meta.sessionId || makeId();
const dir = sessionDir(id);
const summary = {
id,
origin: meta.origin || '',
cwd: meta.cwd || '',
model: meta.model || '',
title: meta.title || 'New session',
hostWorkspace: meta.hostWorkspace !== false,
workspace: meta.workspace || meta.cwd || '',
builtinTools: meta.builtinTools,
createdAt: Date.now(),
updatedAt: Date.now(),
plan: [],
planMode: meta.planMode || { state: 'inactive', planPath: 'plan.md', reminderCount: 0 },
goal: meta.goal || null,
};
fs.writeFileSync(path.join(dir, 'summary.json'), JSON.stringify(summary, null, 2));
fs.writeFileSync(path.join(dir, 'chat_history.jsonl'), '');
return summary;
}
function load(id) {
const dir = sessionDir(id);
let summary;
try {
summary = JSON.parse(fs.readFileSync(path.join(dir, 'summary.json'), 'utf8'));
} catch (_) {
throw new Error('session not found: ' + id);
}
summary.history = readJsonl(path.join(dir, 'chat_history.jsonl'));
summary.updates = readJsonl(path.join(dir, 'updates.jsonl'));
return summary;
}
function saveSummary(summary) {
summary.updatedAt = Date.now();
const copy = Object.assign({}, summary);
delete copy.history;
delete copy.updates;
fs.writeFileSync(path.join(sessionDir(summary.id), 'summary.json'), JSON.stringify(copy, null, 2));
}
function appendHistory(id, msg) {
appendJsonl(path.join(sessionDir(id), 'chat_history.jsonl'), Object.assign({ ts: Date.now() }, msg));
}
function appendUpdate(id, update) {
appendJsonl(path.join(sessionDir(id), 'updates.jsonl'), Object.assign({ ts: Date.now() }, update));
}
function replaceHistory(id, history) {
const file = path.join(sessionDir(id), 'chat_history.jsonl');
const body = (history || []).map((m) => JSON.stringify(m)).join('\n');
fs.writeFileSync(file, body ? body + '\n' : '');
}
function planFile(id) {
return path.join(sessionDir(id), 'plan.md');
}
function readPlan(id) {
try {
return fs.readFileSync(planFile(id), 'utf8');
} catch (_) {
return '';
}
}
function writePlan(id, text) {
const file = planFile(id);
fs.writeFileSync(file, String(text != null ? text : ''));
return file;
}
function list() {
const root = sessionsRoot();
let names = [];
try {
names = fs.readdirSync(root);
} catch (_) {
return [];
}
const out = [];
for (const name of names) {
try {
const summary = JSON.parse(fs.readFileSync(path.join(root, name, 'summary.json'), 'utf8'));
out.push(summary);
} catch (_) {}
}
out.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
return out;
}
module.exports = {
create,
load,
saveSummary,
appendHistory,
appendUpdate,
replaceHistory,
sessionDir,
planFile,
readPlan,
writePlan,
list,
makeId,
};