(log: LogOption)
| 741 | } |
| 742 | |
| 743 | function logToSessionMeta(log: LogOption): SessionMeta { |
| 744 | const stats = extractToolStats(log) |
| 745 | const sessionId = getSessionIdFromLog(log) || 'unknown' |
| 746 | const startTime = log.created.toISOString() |
| 747 | const durationMinutes = Math.round( |
| 748 | (log.modified.getTime() - log.created.getTime()) / 1000 / 60, |
| 749 | ) |
| 750 | |
| 751 | let userMessageCount = 0 |
| 752 | let assistantMessageCount = 0 |
| 753 | for (const msg of log.messages) { |
| 754 | if (msg.type === 'assistant') assistantMessageCount++ |
| 755 | // Only count user messages that have actual text content (human messages) |
| 756 | // not just tool_result messages (matching Python reference) |
| 757 | if (msg.type === 'user' && msg.message) { |
| 758 | const content = msg.message.content |
| 759 | let isHumanMessage = false |
| 760 | if (typeof content === 'string' && content.trim()) { |
| 761 | isHumanMessage = true |
| 762 | } else if (Array.isArray(content)) { |
| 763 | for (const block of content) { |
| 764 | if (block.type === 'text' && 'text' in block) { |
| 765 | isHumanMessage = true |
| 766 | break |
| 767 | } |
| 768 | } |
| 769 | } |
| 770 | if (isHumanMessage) { |
| 771 | userMessageCount++ |
| 772 | } |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | return { |
| 777 | session_id: sessionId, |
| 778 | project_path: log.projectPath || '', |
| 779 | start_time: startTime, |
| 780 | duration_minutes: durationMinutes, |
| 781 | user_message_count: userMessageCount, |
| 782 | assistant_message_count: assistantMessageCount, |
| 783 | tool_counts: stats.toolCounts, |
| 784 | languages: stats.languages, |
| 785 | git_commits: stats.gitCommits, |
| 786 | git_pushes: stats.gitPushes, |
| 787 | input_tokens: stats.inputTokens, |
| 788 | output_tokens: stats.outputTokens, |
| 789 | first_prompt: log.firstPrompt || '', |
| 790 | summary: log.summary, |
| 791 | // New stats |
| 792 | user_interruptions: stats.userInterruptions, |
| 793 | user_response_times: stats.userResponseTimes, |
| 794 | tool_errors: stats.toolErrors, |
| 795 | tool_error_categories: stats.toolErrorCategories, |
| 796 | uses_task_agent: stats.usesTaskAgent, |
| 797 | uses_mcp: stats.usesMcp, |
| 798 | uses_web_search: stats.usesWebSearch, |
| 799 | uses_web_fetch: stats.usesWebFetch, |
| 800 | // Additional stats |
no test coverage detected