MCPcopy Index your code
hub / github.com/getagentseal/codeburn / parseSessionFile

Function parseSessionFile

src/parser.ts:1406–1477  ·  view source on GitHub ↗
(
  filePath: string,
  project: string,
  seenMsgIds: Set<string>,
  dateRange?: DateRange,
)

Source from the content-addressed store, hash-verified

1404}
1405
1406async function parseSessionFile(
1407 filePath: string,
1408 project: string,
1409 seenMsgIds: Set<string>,
1410 dateRange?: DateRange,
1411): Promise<{ session: SessionSummary; canonicalCwd?: string } | null> {
1412 // Skip files whose mtime is older than the range start. A session file
1413 // can only contain entries up to its last-modified time; if that predates
1414 // the requested range, nothing in this file can match.
1415 if (dateRange) {
1416 try {
1417 const s = await stat(filePath)
1418 if (s.mtimeMs < dateRange.start.getTime()) return null
1419 } catch { /* fall through to normal read; missing stat shouldn't break parsing */ }
1420 }
1421 const entries: JournalEntry[] = []
1422 let hasLines = false
1423
1424 // When a dateRange is given, skip user/assistant lines whose timestamp
1425 // is older than range.start - 24h without calling JSON.parse. Huge lines
1426 // that cannot be skipped are yielded as Buffers and compact-parsed without
1427 // converting the whole line into a V8 string.
1428 const earlySkipThreshold = dateRange
1429 ? new Date(dateRange.start.getTime() - 86_400_000).toISOString()
1430 : null
1431 const skipFn = earlySkipThreshold
1432 ? (head: string) => shouldSkipLine(head, earlySkipThreshold)
1433 : undefined
1434
1435 for await (const line of readSessionLines(filePath, skipFn, { largeLineAsBuffer: true })) {
1436 hasLines = true
1437 const entry = parseJsonlLine(line)
1438 if (entry) entries.push(compactEntry(entry))
1439 }
1440
1441 if (!hasLines) return null
1442
1443 if (entries.length === 0) return null
1444
1445 const sessionId = basename(filePath, '.jsonl')
1446 const dedupedEntries = dedupeStreamingMessageIds(entries)
1447 let turns = groupIntoTurns(dedupedEntries, seenMsgIds)
1448 if (dateRange) {
1449 // Bucket a turn by the timestamp of its first assistant call (when the cost was
1450 // actually incurred). Filtering entries directly produced orphan assistant calls
1451 // when a user message sat in one day and the response landed in another -- those
1452 // got pushed as turns with empty timestamps, which some code paths counted and
1453 // others dropped, producing inconsistent Today totals.
1454 turns = turns.filter(turn => {
1455 if (turn.assistantCalls.length === 0) return false
1456 const firstCallTs = turn.assistantCalls[0]!.timestamp
1457 if (!firstCallTs) return false
1458 const ts = new Date(firstCallTs)
1459 return ts >= dateRange.start && ts <= dateRange.end
1460 })
1461 if (turns.length === 0) return null
1462 }
1463 const classified = turns.map(classifyTurn)

Callers

nothing calls this directly

Calls 9

shouldSkipLineFunction · 0.85
readSessionLinesFunction · 0.85
parseJsonlLineFunction · 0.85
compactEntryFunction · 0.85
groupIntoTurnsFunction · 0.85
extractMcpInventoryFunction · 0.85
extractCanonicalCwdFunction · 0.85
buildSessionSummaryFunction · 0.85

Tested by

no test coverage detected