Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
5.3 kB · 138 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139/** * Packed structure of a session transcript: where every entry lives and how the * entries link, without any payload. * * The structure is what a transcript tree or a lazy open needs first: one fixed * 32-byte record per entry means an append is a concatenation and a walk is a * loop over a buffer, instead of per-entry rows through a database driver. * * Records carry absolute byte offsets into the transcript file, so an appended * chunk needs no fixups for the records already stored. */
export interface SessionEntryStructure { id: string; parentId: string | null; type: string; role: string | null; timestampMs: number | null; /** Byte offset of the entry line in the transcript file. */ offset: number; /** Byte length of the entry line, without its newline. */ length: number;}
export const SESSION_ENTRY_STRUCTURE_VERSION = 1;export const SESSION_ENTRY_STRUCTURE_RECORD_BYTES = 32;
const HEADER_BYTES = 8;// Entry ids are randomUUID().slice(0, 8): four bytes of hex.const ID_BYTES = 4;const NULL_ROLE = 0xfe;const OTHER_ROLE = 4;const HEX_ID = /^[0-9a-f]{8}$/;
/** Built-in vocabularies. An unknown entry type makes the blob unpackable. */const ENTRY_TYPES = [ "message", "custom_message", "custom", "compaction", "branch_summary", "model_change", "agent_status", "child_usage_attributed", "session_info", "session_state", "git_state", "label", "thinking_level_change", "service_tier_change",] as const;
const ENTRY_ROLES = ["user", "assistant", "toolResult", "system"] as const;
function writeHexId(id: string, blob: Buffer, at: number): boolean { if (!HEX_ID.test(id)) return false; for (let i = 0; i < ID_BYTES; i++) blob[at + i] = Number.parseInt(id.slice(i * 2, i * 2 + 2), 16); return true;}
function readHexId(blob: Buffer | Uint8Array, at: number): string { let id = ""; for (let i = 0; i < ID_BYTES; i++) id += (blob[at + i] ?? 0).toString(16).padStart(2, "0"); return id;}
function isZeroId(blob: Buffer | Uint8Array, at: number): boolean { for (let i = 0; i < ID_BYTES; i++) if (blob[at + i]) return false; return true;}
function roleCode(role: string | null): number { if (role === null) return NULL_ROLE; const builtIn = ENTRY_ROLES.indexOf(role as (typeof ENTRY_ROLES)[number]); return builtIn === -1 ? OTHER_ROLE : builtIn;}
/** * Packs entry structure records. Returns undefined when a record cannot be * represented losslessly (an id that is not 16 hex characters, an unknown entry * type); callers then treat the session's structure as unavailable and fall * back to parsing the transcript. */export function packSessionEntryStructure(records: readonly SessionEntryStructure[]): Buffer | undefined { const blob = Buffer.alloc(HEADER_BYTES + records.length * SESSION_ENTRY_STRUCTURE_RECORD_BYTES); blob[0] = SESSION_ENTRY_STRUCTURE_VERSION; let at = HEADER_BYTES; for (const record of records) { const typeIndex = ENTRY_TYPES.indexOf(record.type as (typeof ENTRY_TYPES)[number]); if (typeIndex === -1) return undefined; if (!writeHexId(record.id, blob, at)) return undefined; if (record.parentId !== null && !writeHexId(record.parentId, blob, at + ID_BYTES)) return undefined; blob[at + 8] = typeIndex; blob[at + 9] = roleCode(record.role); blob.writeUInt32LE(record.offset, at + 12); blob.writeUInt32LE(record.length, at + 16); blob.writeDoubleLE(record.timestampMs ?? 0, at + 24); at += SESSION_ENTRY_STRUCTURE_RECORD_BYTES; } return blob;}
/** Appends records to a packed structure, or starts a new one when the file is rescanned. */export function appendSessionEntryStructure( blob: Buffer | undefined, fromOffset: number, records: readonly SessionEntryStructure[],): Buffer | undefined { const packed = packSessionEntryStructure(records); if (!packed) return undefined; if (fromOffset === 0 || !blob) return packed; // One header per blob: an appended chunk contributes records only. return Buffer.concat([blob, packed.subarray(HEADER_BYTES)]);}
/** Decodes a packed structure; a truncated or unknown blob yields no records. */export function readSessionEntryStructure(source: Buffer | Uint8Array | null | undefined): SessionEntryStructure[] { if (!source || source.length < HEADER_BYTES || source[0] !== SESSION_ENTRY_STRUCTURE_VERSION) return []; // A sqlite BLOB arrives as a plain Uint8Array; the Buffer view adds the readers. const blob = Buffer.isBuffer(source) ? source : Buffer.from(source.buffer, source.byteOffset, source.byteLength); const records: SessionEntryStructure[] = []; const available = Math.floor((blob.length - HEADER_BYTES) / SESSION_ENTRY_STRUCTURE_RECORD_BYTES); for (let index = 0; index < available; index++) { const at = HEADER_BYTES + index * SESSION_ENTRY_STRUCTURE_RECORD_BYTES; const role = blob[at + 9] ?? NULL_ROLE; const timestampMs = blob.readDoubleLE(at + 24); records.push({ id: readHexId(blob, at), parentId: isZeroId(blob, at + ID_BYTES) ? null : readHexId(blob, at + ID_BYTES), type: ENTRY_TYPES[blob[at + 8] ?? 0] ?? "", role: role === NULL_ROLE ? null : role === OTHER_ROLE ? "other" : (ENTRY_ROLES[role] ?? "other"), timestampMs: timestampMs === 0 ? null : timestampMs, offset: blob.readUInt32LE(at + 12), length: blob.readUInt32LE(at + 16), }); } return records;}