* 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, )
| 4737 | * Accepts a shared buffer to avoid per-file allocation overhead. |
| 4738 | */ |
| 4739 | async function readLiteMetadata( |
| 4740 | filePath: string, |
| 4741 | fileSize: number, |
| 4742 | buf: Buffer, |
| 4743 | ): Promise<LiteMetadata> { |
| 4744 | const { head, tail } = await readHeadAndTail(filePath, fileSize, buf) |
| 4745 | if (!head) return { firstPrompt: '', isSidechain: false } |
| 4746 | |
| 4747 | // Extract stable metadata from the first line via string search. |
| 4748 | // Works even when the first line is truncated (>64KB message). |
| 4749 | const isSidechain = |
| 4750 | head.includes('"isSidechain":true') || head.includes('"isSidechain": true') |
| 4751 | const projectPath = extractJsonStringField(head, 'cwd') |
| 4752 | const teamName = extractJsonStringField(head, 'teamName') |
| 4753 | const agentSetting = extractJsonStringField(head, 'agentSetting') |
| 4754 | |
| 4755 | // Prefer the last-prompt tail entry — captured by extractFirstPrompt at |
| 4756 | // write time (filtered, authoritative) and shows what the user was most |
| 4757 | // recently doing. Head scan is the fallback for sessions written before |
| 4758 | // last-prompt entries existed. Raw string scrapes of head are last resort |
| 4759 | // and catch array-format content blocks (VS Code <ide_selection> metadata). |
| 4760 | const firstPrompt = |
| 4761 | extractLastJsonStringField(tail, 'lastPrompt') || |
| 4762 | extractFirstPromptFromChunk(head) || |
| 4763 | extractJsonStringFieldPrefix(head, 'content', 200) || |
| 4764 | extractJsonStringFieldPrefix(head, 'text', 200) || |
| 4765 | '' |
| 4766 | |
| 4767 | // Extract tail metadata via string search (last occurrence wins). |
| 4768 | // User titles (customTitle field, from custom-title entries) win over |
| 4769 | // AI titles (aiTitle field, from ai-title entries). The distinct field |
| 4770 | // names mean extractLastJsonStringField naturally disambiguates. |
| 4771 | const customTitle = |
| 4772 | extractLastJsonStringField(tail, 'customTitle') ?? |
| 4773 | extractLastJsonStringField(head, 'customTitle') ?? |
| 4774 | extractLastJsonStringField(tail, 'aiTitle') ?? |
| 4775 | extractLastJsonStringField(head, 'aiTitle') |
| 4776 | const summary = extractLastJsonStringField(tail, 'summary') |
| 4777 | const tag = extractLastJsonStringField(tail, 'tag') |
| 4778 | const gitBranch = |
| 4779 | extractLastJsonStringField(tail, 'gitBranch') ?? |
| 4780 | extractJsonStringField(head, 'gitBranch') |
| 4781 | |
| 4782 | // PR link fields — prNumber is a number not a string, so try both |
| 4783 | const prUrl = extractLastJsonStringField(tail, 'prUrl') |
| 4784 | const prRepository = extractLastJsonStringField(tail, 'prRepository') |
| 4785 | let prNumber: number | undefined |
| 4786 | const prNumStr = extractLastJsonStringField(tail, 'prNumber') |
| 4787 | if (prNumStr) { |
| 4788 | prNumber = parseInt(prNumStr, 10) || undefined |
| 4789 | } |
| 4790 | if (!prNumber) { |
| 4791 | const prNumMatch = tail.lastIndexOf('"prNumber":') |
| 4792 | if (prNumMatch >= 0) { |
| 4793 | const afterColon = tail.slice(prNumMatch + 11, prNumMatch + 25) |
| 4794 | const num = parseInt(afterColon.trim(), 10) |
| 4795 | if (num > 0) prNumber = num |
| 4796 | } |
no test coverage detected