* 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, )
| 4852 | * Accepts a shared buffer to avoid per-file allocation overhead. |
| 4853 | */ |
| 4854 | async function readLiteMetadata( |
| 4855 | filePath: string, |
| 4856 | fileSize: number, |
| 4857 | buf: Buffer, |
| 4858 | ): Promise<LiteMetadata> { |
| 4859 | const { head, tail } = await readHeadAndTail(filePath, fileSize, buf) |
| 4860 | if (!head) return { firstPrompt: '', isSidechain: false } |
| 4861 | |
| 4862 | // Extract stable metadata from the first line via string search. |
| 4863 | // Works even when the first line is truncated (>64KB message). |
| 4864 | const isSidechain = |
| 4865 | head.includes('"isSidechain":true') || head.includes('"isSidechain": true') |
| 4866 | const projectPath = extractJsonStringField(head, 'cwd') |
| 4867 | const teamName = extractJsonStringField(head, 'teamName') |
| 4868 | const agentSetting = extractJsonStringField(head, 'agentSetting') |
| 4869 | |
| 4870 | // Prefer the last-prompt tail entry — captured by extractFirstPrompt at |
| 4871 | // write time (filtered, authoritative) and shows what the user was most |
| 4872 | // recently doing. Head scan is the fallback for sessions written before |
| 4873 | // last-prompt entries existed. Raw string scrapes of head are last resort |
| 4874 | // and catch array-format content blocks (VS Code <ide_selection> metadata). |
| 4875 | const firstPrompt = |
| 4876 | extractLastJsonStringField(tail, 'lastPrompt') || |
| 4877 | extractFirstPromptFromChunk(head) || |
| 4878 | extractJsonStringFieldPrefix(head, 'content', 200) || |
| 4879 | extractJsonStringFieldPrefix(head, 'text', 200) || |
| 4880 | '' |
| 4881 | |
| 4882 | // Extract tail metadata via string search (last occurrence wins). |
| 4883 | // User titles (customTitle field, from custom-title entries) win over |
| 4884 | // AI titles (aiTitle field, from ai-title entries). The distinct field |
| 4885 | // names mean extractLastJsonStringField naturally disambiguates. |
| 4886 | const customTitle = |
| 4887 | extractLastJsonStringField(tail, 'customTitle') ?? |
| 4888 | extractLastJsonStringField(head, 'customTitle') ?? |
| 4889 | extractLastJsonStringField(tail, 'aiTitle') ?? |
| 4890 | extractLastJsonStringField(head, 'aiTitle') |
| 4891 | const summary = extractLastJsonStringField(tail, 'summary') |
| 4892 | const tag = extractLastJsonStringField(tail, 'tag') |
| 4893 | const gitBranch = |
| 4894 | extractLastJsonStringField(tail, 'gitBranch') ?? |
| 4895 | extractJsonStringField(head, 'gitBranch') |
| 4896 | |
| 4897 | // PR link fields — prNumber is a number not a string, so try both |
| 4898 | const prUrl = extractLastJsonStringField(tail, 'prUrl') |
| 4899 | const prRepository = extractLastJsonStringField(tail, 'prRepository') |
| 4900 | let prNumber: number | undefined |
| 4901 | const prNumStr = extractLastJsonStringField(tail, 'prNumber') |
| 4902 | if (prNumStr) { |
| 4903 | prNumber = parseInt(prNumStr, 10) || undefined |
| 4904 | } |
| 4905 | if (!prNumber) { |
| 4906 | const prNumMatch = tail.lastIndexOf('"prNumber":') |
| 4907 | if (prNumMatch >= 0) { |
| 4908 | const afterColon = tail.slice(prNumMatch + 11, prNumMatch + 25) |
| 4909 | const num = parseInt(afterColon.trim(), 10) |
| 4910 | if (num > 0) prNumber = num |
| 4911 | } |
no test coverage detected