| 399 | } |
| 400 | |
| 401 | function analyzeTranscript(transcriptPath, options = {}) { |
| 402 | const normalizedOptions = normalizeOptions(options); |
| 403 | const absoluteTranscriptPath = path.resolve(transcriptPath); |
| 404 | const now = normalizedOptions.nowDate || getNow(normalizedOptions); |
| 405 | const nowMs = now.getTime(); |
| 406 | const { entries, parseErrors } = readJsonlEntries(absoluteTranscriptPath); |
| 407 | const pendingTools = new Map(); |
| 408 | let latestAssistantProgressAt = null; |
| 409 | let lastEventAt = null; |
| 410 | let latestWake = null; |
| 411 | let sessionId = path.basename(absoluteTranscriptPath, '.jsonl'); |
| 412 | |
| 413 | for (const entry of entries) { |
| 414 | sessionId = getSessionId(entry, absoluteTranscriptPath) || sessionId; |
| 415 | const timestamp = getEntryTimestamp(entry); |
| 416 | if (timestamp && (!lastEventAt || timestamp.getTime() > lastEventAt.getTime())) { |
| 417 | lastEventAt = timestamp; |
| 418 | } |
| 419 | if ( |
| 420 | timestamp |
| 421 | && isAssistantProgressEntry(entry) |
| 422 | && (!latestAssistantProgressAt || timestamp.getTime() > latestAssistantProgressAt.getTime()) |
| 423 | ) { |
| 424 | latestAssistantProgressAt = timestamp; |
| 425 | } |
| 426 | |
| 427 | for (const toolUse of extractToolUses(entry)) { |
| 428 | const startedAt = timestamp || lastEventAt; |
| 429 | pendingTools.set(toolUse.id, { |
| 430 | command: toolUse.input && toolUse.input.command ? String(toolUse.input.command) : null, |
| 431 | input: toolUse.input || {}, |
| 432 | name: toolUse.name, |
| 433 | startedAt: toIso(startedAt), |
| 434 | toolUseId: toolUse.id, |
| 435 | }); |
| 436 | |
| 437 | if (toolUse.name === 'ScheduleWakeup') { |
| 438 | const delaySeconds = readDelaySeconds(toolUse.input); |
| 439 | if (delaySeconds && startedAt) { |
| 440 | const dueAt = new Date(startedAt.getTime() + delaySeconds * 1000); |
| 441 | latestWake = { |
| 442 | delaySeconds, |
| 443 | dueAt: dueAt.toISOString(), |
| 444 | reason: toolUse.input && toolUse.input.reason ? String(toolUse.input.reason) : null, |
| 445 | scheduledAt: startedAt.toISOString(), |
| 446 | toolUseId: toolUse.id, |
| 447 | }; |
| 448 | } |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | for (const toolUseId of extractToolResultIds(entry)) { |
| 453 | pendingTools.delete(toolUseId); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | const pendingToolList = Array.from(pendingTools.values()).map(tool => { |
| 458 | const startedAt = parseTimestamp(tool.startedAt); |