claude_session_viewer/lib/models/agent_info.dart
Mathias Beaulieu-Duncan 364877d376 Initial commit: Claude Code session viewer (Flutter macOS)
A desktop app that parses Claude Code .jsonl session logs and provides
a rich UI for exploring conversations, tool usage, subagents, and token
consumption. Features include project browser with auto-discovery of
~/.claude/projects, conversation timeline with inline subagent expansion,
agents overview, toolbelt chart, and token usage dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:17:23 -04:00

101 lines
2.6 KiB
Dart

import 'content_block.dart';
import 'log_entry.dart';
import 'token_usage.dart';
class AgentInfo {
final String id;
final String name;
final String? subagentType;
final String? description;
final String? prompt;
final String? model;
final List<LogEntry> messages;
final List<ToolUseBlock> toolsUsed;
final TokenUsage aggregatedUsage;
AgentInfo({
required this.id,
required this.name,
this.subagentType,
this.description,
this.prompt,
this.model,
required this.messages,
required this.toolsUsed,
required this.aggregatedUsage,
});
Set<String> get uniqueToolNames => toolsUsed.map((t) => t.name).toSet();
Map<String, int> get toolUsageCounts {
final counts = <String, int>{};
for (final tool in toolsUsed) {
counts[tool.name] = (counts[tool.name] ?? 0) + 1;
}
return counts;
}
int get messageCount => messages.length;
int get userMessageCount =>
messages.where((m) => m.type == 'user').length;
int get assistantMessageCount =>
messages.where((m) => m.type == 'assistant').length;
}
class SessionLog {
final String? sessionId;
final String? cwd;
final String? version;
final String? gitBranch;
final List<LogEntry> allEntries;
final List<LogEntry> mainConversation;
final List<AgentInfo> agents;
final AgentInfo mainAgent;
final TokenUsage totalUsage;
final Map<String, List<ToolUseBlock>> toolsByName;
final DateTime? startTime;
final DateTime? endTime;
/// Lookup subagent by ID (matches both full id and partial)
late final Map<String, AgentInfo> agentById = {
for (final a in agents) a.id: a,
};
SessionLog({
this.sessionId,
this.cwd,
this.version,
this.gitBranch,
required this.allEntries,
required this.mainConversation,
required this.agents,
required this.mainAgent,
required this.totalUsage,
required this.toolsByName,
this.startTime,
this.endTime,
});
/// Find an agent that matches a tool_use id (Agent tool calls use
/// the tool_use block id, subagent files use a shortened agentId)
AgentInfo? findAgentForToolUse(String toolUseId) {
// Direct match
if (agentById.containsKey(toolUseId)) return agentById[toolUseId];
// Partial match: subagent file ids are shortened (e.g. "a014e30b71de602bb")
for (final agent in agents) {
if (agent.id == 'main') continue;
if (toolUseId.contains(agent.id) || agent.id.contains(toolUseId)) {
return agent;
}
}
return null;
}
Duration? get duration {
if (startTime != null && endTime != null) {
return endTime!.difference(startTime!);
}
return null;
}
}