chore: snapshot working tree - pty_exited notifications + in-flight inference WIP

feat(booterm): structured pty_exited WS notifications. Plan-validated, impl-validated, code-reviewed green (contracts build clean, contracts test 29/29, booterm + web typecheck clean).

wip: in-progress inference/provider refactor (agents.ts, provider.ts, new llama-providers.ts, removed llama-args-validator), plus arena, dispatcher, compaction, schema changes.

openspec: pty-exit-notifications complete; x-agent-flags planned (not yet implemented).
This commit is contained in:
2026-06-14 12:48:47 +00:00
parent 0ed506f1da
commit b18de2a331
204 changed files with 25344 additions and 867 deletions

View File

@@ -182,6 +182,7 @@ export async function sweepExpired(
? 'idle timeout'
: 'absolute timeout';
log.info({ paneId: meta.paneId, reason }, 'sweeping expired PTY session');
meta.timedOut = true;
const sessionName = tmuxSessionName(meta.paneId);
try {
const ok = await killSession(tmuxConfPath, sessionName);
@@ -191,7 +192,6 @@ export async function sweepExpired(
} catch (err) {
log.warn({ paneId: meta.paneId, err }, 'killSession threw during sweep');
}
registry.unregister(meta.paneId);
killed.push(meta.paneId);
}
return killed;

View File

@@ -10,6 +10,7 @@ export interface SessionMeta {
timeoutSeconds?: number;
idleExpiresAt?: Date;
absoluteExpiresAt?: Date;
timedOut?: boolean;
}
const sessions = new Map<string, SessionMeta>();
@@ -115,6 +116,18 @@ export interface SearchMatch {
const ringBuffers = new Map<string, string[]>();
/**
* Return the last N non-empty lines from the ring buffer for a pane.
* ANSI escape sequences are preserved (xterm handles them).
* Partial lines from mid-stream exit are included as-is.
*/
export function getLastLines(paneId: string, n: number): string[] {
const buf = ringBuffers.get(paneId);
if (!buf || buf.length === 0) return [];
const nonEmpty = buf.filter(l => l.trim().length > 0);
return nonEmpty.slice(-n);
}
/**
* Append raw PTY data to the ring buffer for a given pane.
* Splits incoming data on newlines and pushes each line into the buffer,

View File

@@ -9,7 +9,7 @@ import {
} from '../pty/manager.js';
import { attachPty } from '../pty/pty.js';
import { getUser } from '../auth.js';
import { register, unregister, appendOutput, touchActivity, consumePendingMetadata } from '../pty/registry.js';
import { register, unregister, appendOutput, touchActivity, consumePendingMetadata, get as getRegistry, getLastLines } from '../pty/registry.js';
export function registerWsAttachRoute(
app: FastifyInstance,
@@ -168,9 +168,22 @@ export function registerWsAttachRoute(
});
handle.onExit(({ exitCode }) => {
const meta = getRegistry(pid);
const lastLines = getLastLines(pid, 5);
const frame = {
type: 'pty_exited' as const,
session_id: sid,
pane_id: pid,
exit_code: exitCode,
last_lines: lastLines,
session_title: meta?.title ?? null,
session_description: meta?.description ?? null,
parent_agent: meta?.parentAgent ?? null,
timed_out: meta?.timedOut ?? false,
};
try {
if (socket.readyState === socket.OPEN) {
socket.send(JSON.stringify({ type: 'exit', code: exitCode }));
socket.send(JSON.stringify(frame));
}
} catch {
/* ignore */