/** * Sessions already on disk, read back as something a browser can show. * * Every CLI run writes a JSONL transcript into `.koneck/sessions`, and until now the web side * listed their titles and could do nothing with them. That is the larger half of what a harness is * for: most of the value is in looking at a run that has already happened — what it did, in what * order, and where the time went — and none of that needs an agent to be running. * * The file is the record, so this only reads. A past session cannot be resumed from the browser * because resuming is what the CLI does, and pretending otherwise would produce a second live * session pointed at the same transcript. */ import { type TrajectoryEvent, type TrajectorySummary } from '../trajectory.js'; export interface PastSessionMeta { file: string; id: string; task: string; model: string; provider: string; turns: number; totalTokens: number; /** When the session began. The id carries this too. */ at: number; /** * When it was last worked in. * * A different question from when it started, and the one anybody actually asks of a list. A * session opened three hours ago and worked in five minutes ago read as "3h ago", because the * listing showed its start time — so the thing you were just doing looked like the oldest thing * there. */ lastAt: number; bytes: number; /** * The session this one continues, when it continues one. * * A resumed session inherits its parent's turn and token counts but carries only a compressed * summary of what was said — so its own file honestly reports 29 turns while holding a handful of * messages and two trajectory events, and reading it alone shows a "[COMPRESSED HISTORY — 50 * messages]" block where the work used to be. The parent file still has all of it, and this is * the pointer to it. */ forkedFrom?: string; } /** One entry in a rendered transcript. */ export type PastEntry = { kind: 'user'; text: string; } | { kind: 'assistant'; text: string; } | { kind: 'tool'; name: string; args: string; result: string; ok: boolean; } | { kind: 'system'; text: string; } /** * The stretch that was left out of a long transcript, said where it happened. * * A gap reported only as a flag on the whole session is a gap nobody can place. This sits between * the opening and the recent work, which is exactly where the missing part was. */ | { kind: 'gap'; omitted: number; }; export interface PastSession { meta: PastSessionMeta; entries: PastEntry[]; trajectory: TrajectoryEvent[]; summary: TrajectorySummary | null; /** True when the file held more than was read, so the view can say so. */ truncated: boolean; } /** * How much of a long transcript is shown. * * Measured rather than chosen: rendering is roughly linear and six hundred entries costs about * 600ms in a browser on the machine this was built on, which is the most that can be spent before * opening a session stops feeling immediate. */ export declare const MAX_ENTRIES = 600; /** * How much of the opening to keep when a transcript will not fit. * * Enough for the request and the first moves that answered it. The rest of the budget goes to the * end, because the far more common question of a long session is "what did it just do". */ export declare const KEEP_OPENING = 20; /** Only the first line of each file is read: it holds the metadata. */ export declare function listPast(cwd: string, limit?: number): Promise; /** * One session, read into entries. * * The file is a sequence of records; older KONECK versions wrote slightly different shapes, so this * accepts what it recognises and ignores the rest rather than refusing to open an old transcript. */ export declare function readPast(cwd: string, file: string): Promise; /** * A transcript trimmed to fit, from the middle. * * It used to stop reading at six hundred entries and drop everything after, which spends the whole * budget on the oldest part of a long session — so a session that had spent an hour writing code * opened on its first few exchanges with the work missing, and reported only a flag to say some of * it was gone. The question a long transcript is opened to answer is nearly always "what did it * just do". * * So the opening is kept, because that is the request everything else was in service of, and the * rest of the budget goes to the end. What was dropped is reported where it was dropped, so the gap * can be placed rather than merely known about. */ export declare function trimToFit(entries: readonly PastEntry[], max?: number, opening?: number): { entries: PastEntry[]; truncated: boolean; }; //# sourceMappingURL=history.d.ts.map