/**
 * Detached-session store — the record `agents sessions detach` writes and both
 * `agents sessions attach` and the active-session scan read to know an agent is
 * "backgrounded": running headless with no terminal, continuing its task
 * unattended.
 *
 * One file per detached session under `~/.agents/.system/detached/<id>.json`.
 * Presence is DERIVED, never asserted: a record only means "this session was
 * detached to a headless continuation"; whether it is still `background`
 * (that continuation is alive) or `parked` (it has exited) is decided live from
 * the recorded pid + its start-time fingerprint. That keeps the store honest
 * even across a crash that never ran `attach`.
 */
import fs from 'node:fs';
import path from 'node:path';
import { getSystemAgentsDir } from '../state.js';
import { captureProcessStartTime } from '../pty-server.js';
function detachedDir() {
    return path.join(getSystemAgentsDir(), 'detached');
}
function recordPath(sessionId) {
    return path.join(detachedDir(), `${sessionId}.json`);
}
export function writeDetachRecord(rec) {
    fs.mkdirSync(detachedDir(), { recursive: true });
    fs.writeFileSync(recordPath(rec.sessionId), JSON.stringify(rec, null, 2));
}
export function readDetachRecord(sessionId) {
    try {
        return JSON.parse(fs.readFileSync(recordPath(sessionId), 'utf8'));
    }
    catch {
        return undefined;
    }
}
export function clearDetachRecord(sessionId) {
    try {
        fs.rmSync(recordPath(sessionId));
    }
    catch {
        /* already gone */
    }
}
export function listDetachRecords() {
    let names;
    try {
        names = fs.readdirSync(detachedDir());
    }
    catch {
        return [];
    }
    const out = [];
    for (const name of names) {
        if (!name.endsWith('.json'))
            continue;
        const rec = readDetachRecord(name.slice(0, -'.json'.length));
        if (rec)
            out.push(rec);
    }
    return out;
}
/** True while the recorded headless continuation is still the live process we spawned. */
export function isHeadlessAlive(rec) {
    if (!rec.headlessPid || rec.headlessPid <= 0)
        return false;
    try {
        process.kill(rec.headlessPid, 0);
    }
    catch {
        return false;
    }
    // Defeat PID reuse: if the pid now belongs to a different process, it is not ours.
    if (rec.headlessStartTime !== null) {
        const now = captureProcessStartTime(rec.headlessPid);
        if (now !== null && now !== rec.headlessStartTime)
            return false;
    }
    return true;
}
/**
 * Presence for a session id from the detach store alone:
 *   - no record            -> undefined (caller decides: `attached` for a live
 *                             interactive row, nothing for cloud/team rows)
 *   - record + pid alive    -> `background` (headless continuation running)
 *   - record + pid exited   -> `parked` (the run finished; transcript is durable)
 */
export function presenceFromStore(sessionId) {
    const rec = readDetachRecord(sessionId);
    if (!rec)
        return undefined;
    return isHeadlessAlive(rec) ? 'background' : 'parked';
}
