
agtrace-provider-normalization
by lanegrid
See What Your AI Agent Is Actually Doing
SKILL.md
name: agtrace-provider-normalization description: Investigate AI agent provider tool schemas (Claude Code, Codex, Gemini) and design unified domain abstractions in agtrace architecture.
Agtrace Provider Normalization Expert
This skill provides deep knowledge of how agtrace normalizes diverse AI agent log formats into unified domain types. Use this when working with provider implementations, tool normalization, or understanding the schema-on-read architecture.
Three-Tier Provider Architecture
Tier 1: Trait-Based Adapter Pattern
Every provider implements three core traits bundled in a ProviderAdapter:
LogDiscovery → File discovery and session location
SessionParser → Raw log parsing to AgentEvent timeline
ToolMapper → Tool call normalization and classification
Key Files:
crates/agtrace-providers/src/traits.rs- trait definitionscrates/agtrace-providers/src/registry.rs- adapter factory
Tier 2: Provider-Specific Implementation
Each provider has this structure:
provider/
├── discovery.rs # LogDiscovery trait impl
├── parser.rs # SessionParser trait impl
├── mapper.rs # ToolMapper trait impl
├── io.rs # Raw file I/O
├── schema.rs # Provider-specific schemas
├── tools.rs # Provider-specific tool args
├── tool_mapping.rs # Tool classification
└── models.rs # Data structures
Provider directories:
crates/agtrace-providers/src/claude/crates/agtrace-providers/src/codex/crates/agtrace-providers/src/gemini/
Tier 3: Unified Domain Types
All providers normalize to common types in agtrace-types:
| Type | Location | Variants |
|---|---|---|
EventPayload | event/payload.rs | User, Reasoning, ToolCall, ToolResult, Message, TokenUsage, Notification, SlashCommand |
ToolCallPayload | tool/payload.rs | FileRead, FileEdit, FileWrite, Execute, Search, Mcp, Generic |
ToolKind | tool/types.rs | Read, Write, Execute, Plan, Search, Ask, Other |
ToolOrigin | tool/types.rs | System, Mcp |
Schema-on-Read Architecture
The architecture enforces Schema-on-Read where:
- Raw logs are source of truth - Original files never modified
- Lazy parsing - Files parsed on demand
- Type-safe conversion - Raw JSON → Provider Args → Domain ToolCallPayload
Provider-Specific Args Pattern
Each provider defines schema-specific argument types:
// Claude: crates/agtrace-providers/src/claude/tools.rs
ClaudeReadArgs, ClaudeGlobArgs, ClaudeEditArgs, ClaudeWriteArgs,
ClaudeBashArgs, ClaudeGrepArgs, ClaudeTodoWriteArgs
// Codex: crates/agtrace-providers/src/codex/tools.rs
ShellArgs, ApplyPatchArgs, ReadMcpResourceArgs
// Gemini: crates/agtrace-providers/src/gemini/tools.rs
GeminiReadFileArgs, GeminiWriteFileArgs, GeminiReplaceArgs,
GeminiRunShellCommandArgs, GeminiGoogleWebSearchArgs
Each has conversion methods:
impl ClaudeReadArgs {
fn to_file_read_args(&self) -> FileReadArgs { ... }
}
Tool Call Normalization Flow
Raw JSON Arguments
↓
Provider-specific Args deserialization (strict)
↓
Optional: Semantic reclassification (e.g., shell → Read/Write/Search)
↓
Convert to typed ToolCallPayload variant
↓
If parse fails → Generic variant (safe fallback)
Semantic Reclassification Example
Codex shell commands are intelligently classified:
// File: codex/mapper.rs
match tool_name {
"shell" => {
if let Ok(shell_args) = parse::<ShellArgs>(arguments) {
match classify_execute_command(&command) {
Some(ToolKind::Search) => return Search variant
Some(ToolKind::Read) => return FileRead variant
_ => return Execute variant
}
}
}
}
MCP Tool Handling
Different providers use different MCP naming conventions:
| Provider | Format | Example |
|---|---|---|
| Claude | mcp__server__tool | mcp__filesystem__read_file |
| Codex | mcp__server__tool | mcp__memory__store |
| Gemini | tool-name + display_name | Display: "(ServerName MCP Server)" |
All normalize to ToolCallPayload::Mcp with McpArgs:
pub struct McpArgs {
pub server: Option<String>,
pub tool: Option<String>,
pub inner: Value,
}
Event Building
The EventBuilder in builder.rs provides deterministic event construction:
pub struct EventBuilder {
session_id: Uuid,
stream_tips: HashMap<StreamId, Uuid>, // Per-stream parent tracking
tool_map: HashMap<String, Uuid>, // Provider ID → UUID mapping
}
Key features:
- Deterministic UUIDs: v5 UUID from session_id + "base_id:suffix"
- Multi-stream support: Main, Sidechain, Subagent independent chains
- Tool result linking: O(1) lookup from provider tool ID to UUID
Adding a New Provider
-
Create provider module:
crates/agtrace-providers/src/<provider_name>/ -
Implement Discovery:
pub struct NewProviderDiscovery;
impl LogDiscovery for NewProviderDiscovery {
fn id(&self) -> &'static str { "new_provider" }
fn probe(&self, path: &Path) -> ProbeResult { ... }
fn scan_sessions(&self, log_root: &Path) -> Result<Vec<SessionIndex>> { ... }
}
- Implement Parser:
pub struct NewProviderParser;
impl SessionParser for NewProviderParser {
fn parse_file(&self, path: &Path) -> Result<Vec<AgentEvent>> { ... }
fn parse_record(&self, content: &str) -> Result<Option<AgentEvent>> { ... }
}
- Implement ToolMapper:
pub struct NewProviderToolMapper;
impl ToolMapper for NewProviderToolMapper {
fn classify(&self, tool_name: &str) -> (ToolOrigin, ToolKind) { ... }
fn normalize_call(&self, name: &str, args: Value, call_id: Option<String>)
-> ToolCallPayload { ... }
fn summarize(&self, kind: ToolKind, args: &Value) -> String { ... }
}
- Register in registry.rs:
pub fn create_adapter(name: &str) -> Result<ProviderAdapter> {
match name {
"new_provider" => Ok(ProviderAdapter::new(
Box::new(crate::new_provider::NewProviderDiscovery),
Box::new(crate::new_provider::NewProviderParser),
Box::new(crate::new_provider::NewProviderToolMapper),
)),
// ...
}
}
Tool Classification Pattern
Provider-Specific Classification
Each provider defines in tool_mapping.rs:
fn classify_tool(tool_name: &str) -> Option<(ToolOrigin, ToolKind)>
Returns Some if recognized, None for fallback.
Common Heuristic Fallback
When provider doesn't recognize a tool (tool_analyzer.rs):
pub fn classify_common(tool_name: &str) -> (ToolOrigin, ToolKind) {
// name.contains("search") → ToolKind::Search
// name.contains("read") → ToolKind::Read
// name.starts_with("mcp__") → ToolOrigin::Mcp
}
Key Architectural Decisions
| Aspect | Decision | Rationale |
|---|---|---|
| Schema Location | Provider Args in providers, ToolCallPayload in types | Domain model pure |
| Parsing Errors | Fall back to Generic, never fail | No data loss |
| Tool Classification | Provider-specific first, then heuristics | Accuracy + graceful fallback |
| Event IDs | Deterministic v5 UUIDs | Reproducible sessions |
| Multi-Stream | Separate parent chains per StreamId | Independent flows |
Testing Patterns
Each provider has tests for:
- Discovery: probe, scan, session extraction
- Parser: record parsing, content blocks
- Mapper: tool normalization per tool type
- Edge cases: malformed data, unknown tools
#[test]
fn test_normalize_read() {
let payload = normalize_claude_tool_call(
"Read".to_string(),
serde_json::json!({"file_path": "src/main.rs"}),
Some("call_123".to_string()),
);
match payload {
ToolCallPayload::FileRead { arguments, .. } => {
assert_eq!(arguments.file_path, Some("src/main.rs".to_string()));
}
_ => panic!("Expected FileRead"),
}
}
When to Use This Skill
This skill should be activated when:
- Adding a new AI agent provider to agtrace
- Modifying tool normalization logic
- Understanding how provider logs become unified events
- Debugging provider-specific parsing issues
- Designing new tool classification strategies
- Working with MCP tool handling
- Understanding the schema-on-read architecture
Key Files Reference
| Purpose | File Path |
|---|---|
| Trait definitions | crates/agtrace-providers/src/traits.rs |
| Provider registry | crates/agtrace-providers/src/registry.rs |
| Event builder | crates/agtrace-providers/src/builder.rs |
| Claude parser | crates/agtrace-providers/src/claude/parser.rs |
| Claude tools | crates/agtrace-providers/src/claude/tools.rs |
| Codex parser | crates/agtrace-providers/src/codex/parser.rs |
| Gemini parser | crates/agtrace-providers/src/gemini/parser.rs |
| Domain events | crates/agtrace-types/src/event/payload.rs |
| Tool payload | crates/agtrace-types/src/tool/payload.rs |
| Tool types | crates/agtrace-types/src/tool/types.rs |
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon


