* Reads the first and last ~64KB of a JSONL file and extracts lite metadata. * * Head (first 64KB): isSidechain, projectPath, teamName, firstPrompt. * Tail (last 64KB): customTitle, tag, PR link, latest gitBranch. * * Accepts a shared buffer to avoid per-file allocation overhead.
( filePath: string, fileSize: number, buf: Buffer, )
| 5007 | * Accepts a shared buffer to avoid per-file allocation overhead. |
| 5008 | */ |
| 5009 | async function readLiteMetadata( |
| 5010 | filePath: string, |
| 5011 | fileSize: number, |
| 5012 | buf: Buffer, |
| 5013 | ): Promise<LiteMetadata> { |
| 5014 | const { head, tail } = await readHeadAndTail(filePath, fileSize, buf) |
| 5015 | if (!head) return { firstPrompt: '', isSidechain: false } |
| 5016 | |
| 5017 | // Extract stable metadata from the first line via string search. |
| 5018 | // Works even when the first line is truncated (>64KB message). |
| 5019 | const isSidechain = |
| 5020 | head.includes('"isSidechain":true') || head.includes('"isSidechain": true') |
| 5021 | const projectPath = extractJsonStringField(head, 'cwd') |
| 5022 | const teamName = extractJsonStringField(head, 'teamName') |
| 5023 | const agentSetting = extractJsonStringField(head, 'agentSetting') |
| 5024 | |
| 5025 | // Prefer the last-prompt tail entry — captured by extractFirstPrompt at |
| 5026 | // write time (filtered, authoritative) and shows what the user was most |
| 5027 | // recently doing. Head scan is the fallback for sessions written before |
| 5028 | // last-prompt entries existed. Raw string scrapes of head are last resort |
| 5029 | // and catch array-format content blocks (VS Code <ide_selection> metadata). |
| 5030 | const firstPrompt = |
| 5031 | extractLastJsonStringField(tail, 'lastPrompt') || |
| 5032 | extractFirstPromptFromChunk(head) || |
| 5033 | extractJsonStringFieldPrefix(head, 'content', 200) || |
| 5034 | extractJsonStringFieldPrefix(head, 'text', 200) || |
| 5035 | '' |
| 5036 | |
| 5037 | // Extract tail metadata via string search (last occurrence wins). |
| 5038 | // User titles (customTitle field, from custom-title entries) win over |
| 5039 | // AI titles (aiTitle field, from ai-title entries). The distinct field |
| 5040 | // names mean extractLastJsonStringField naturally disambiguates. |
| 5041 | const customTitle = |
| 5042 | extractLastJsonStringField(tail, 'customTitle') ?? |
| 5043 | extractLastJsonStringField(head, 'customTitle') ?? |
| 5044 | extractLastJsonStringField(tail, 'aiTitle') ?? |
| 5045 | extractLastJsonStringField(head, 'aiTitle') |
| 5046 | const summary = extractLastJsonStringField(tail, 'summary') |
| 5047 | const tag = extractLastJsonStringField(tail, 'tag') |
| 5048 | const gitBranch = |
| 5049 | extractLastJsonStringField(tail, 'gitBranch') ?? |
| 5050 | extractJsonStringField(head, 'gitBranch') |
| 5051 | |
| 5052 | // PR link fields — prNumber is a number not a string, so try both |
| 5053 | const prUrl = extractLastJsonStringField(tail, 'prUrl') |
| 5054 | const prRepository = extractLastJsonStringField(tail, 'prRepository') |
| 5055 | let prNumber: number | undefined |
| 5056 | const prNumStr = extractLastJsonStringField(tail, 'prNumber') |
| 5057 | if (prNumStr) { |
| 5058 | prNumber = parseInt(prNumStr, 10) || undefined |
| 5059 | } |
| 5060 | if (!prNumber) { |
| 5061 | const prNumMatch = tail.lastIndexOf('"prNumber":') |
| 5062 | if (prNumMatch >= 0) { |
| 5063 | const afterColon = tail.slice(prNumMatch + 11, prNumMatch + 25) |
| 5064 | const num = parseInt(afterColon.trim(), 10) |
| 5065 | if (num > 0) prNumber = num |
| 5066 | } |
no test coverage detected