Skip to content

[codex] collect claude usage events#30

Merged
Miss-you merged 1 commit into
mainfrom
codex/ebta-004-claude-events
Apr 16, 2026
Merged

[codex] collect claude usage events#30
Miss-you merged 1 commit into
mainfrom
codex/ebta-004-claude-events

Conversation

@Miss-you

Copy link
Copy Markdown
Owner

Summary

  • Add native Claude UsageEvent collection from local assistant message.usage JSONL records.
  • Preserve existing CollectSessions behavior while reusing Claude path discovery for top-level and subagent files.
  • Add EBTA-004 workspace artifacts and mark the task board item done.

Behavior

Claude native events now keep assistant-message timestamps, source paths, model/session/title metadata, and the same token-field mapping used by session aggregation. Streaming duplicate records are deduplicated by message.id + requestId, with the latest file record winning; records without either ID remain unique.

Verification

  • make fmt
  • go test -count=1 ./provider/claude -run 'Test(ParseClaudeUsageEvents|CollectClaudeUsageEvents)'
  • go test -count=1 -race -cover ./provider/claude
  • heartbeat-wrapped make test
  • make vet
  • make build
  • make lint

Notes

make test was wrapped with heartbeat output because the e2e phase is quiet for several minutes in this environment; the underlying command remained the repository make test target.

@Miss-you
Miss-you force-pushed the codex/ebta-004-claude-events branch from 578e143 to 0192d87 Compare April 16, 2026 09:59
- add native Claude UsageEvent collection from assistant message usage records

- cover assistant timestamps, cross-day events, streaming dedupe, and subagent discovery

- close EBTA-004 with workspace review and verification evidence
@Miss-you
Miss-you force-pushed the codex/ebta-004-claude-events branch from 0192d87 to bac32aa Compare April 16, 2026 10:09
@Miss-you
Miss-you marked this pull request as ready for review April 16, 2026 10:19
Copilot AI review requested due to automatic review settings April 16, 2026 10:19
@Miss-you
Miss-you merged commit d357b45 into main Apr 16, 2026
9 checks passed
@Miss-you
Miss-you deleted the codex/ebta-004-claude-events branch April 16, 2026 10:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds native Claude UsageEvent collection so event-based token aggregation can use assistant-message timestamps and per-message usage deltas, while preserving existing Claude session aggregation behavior.

Changes:

  • Implement (*claude.Provider).CollectUsageEvents plus parseUsageEvents to emit deduped, timestamped usage events from Claude JSONL logs.
  • Add focused tests covering assistant timestamps, cross-day events, streaming dedupe semantics, no-ID uniqueness, and subagent path discovery.
  • Add EBTA-004 workspace artifacts and mark EBTA-004 as done in the task plan.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
provider/claude/parser.go Refactors path discovery into a shared helper and adds native usage-event parsing + collection for Claude JSONL files.
provider/claude/parser_test.go Adds unit tests for Claude usage-event parsing, dedupe rules, and subagent path inclusion.
docs/plans/2026-04-16-event-based-token-aggregation-task.md Updates the EBTA task board entry to mark EBTA-004 as done and logs timeline notes.
workspace/EBTA-004/test_strategy.md Captures the test plan used for EBTA-004 implementation/verification.
workspace/EBTA-004/review.md Records review outcome and follow-up test hardening for file-order dedupe semantics.
workspace/EBTA-004/research_review.md Summarizes research/review decisions and accepted constraints for the implementation.
workspace/EBTA-004/original_impl.md Documents pre-change Claude parsing behavior and constraints relevant to EBTA-004.
workspace/EBTA-004/new_impl.md Documents the implementation plan for adding Claude native usage events.
workspace/EBTA-004/final_impl_v1.md Records the intended final design (v1) for Claude native usage events.
workspace/EBTA-004/final_impl.md Records the finalized intended behavior/constraints for EBTA-004.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread provider/claude/parser.go
Comment on lines +413 to +422
func tokenUsageFromClaudeUsage(usage *claudeUsage) provider.TokenUsage {
if usage == nil {
return provider.TokenUsage{}
}
return provider.TokenUsage{
InputOther: usage.InputTokens,
InputCacheRead: usage.CacheReadInputTokens,
InputCacheCreate: usage.CacheCreationInputTokens,
Output: usage.OutputTokens,
}

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tokenUsageFromClaudeUsage centralizes the Claude->TokenUsage field mapping, but parseSession still duplicates that mapping inline. To prevent the two code paths from drifting over time (sessions vs. native events), consider reusing this helper in parseSession as well.

Suggested change
func tokenUsageFromClaudeUsage(usage *claudeUsage) provider.TokenUsage {
if usage == nil {
return provider.TokenUsage{}
}
return provider.TokenUsage{
InputOther: usage.InputTokens,
InputCacheRead: usage.CacheReadInputTokens,
InputCacheCreate: usage.CacheCreationInputTokens,
Output: usage.OutputTokens,
}
func tokenUsage(inputOther, inputCacheRead, inputCacheCreate, output int) provider.TokenUsage {
return provider.TokenUsage{
InputOther: inputOther,
InputCacheRead: inputCacheRead,
InputCacheCreate: inputCacheCreate,
Output: output,
}
}
func tokenUsageFromClaudeUsage(usage *claudeUsage) provider.TokenUsage {
if usage == nil {
return provider.TokenUsage{}
}
return tokenUsage(
usage.InputTokens,
usage.CacheReadInputTokens,
usage.CacheCreationInputTokens,
usage.OutputTokens,
)

Copilot uses AI. Check for mistakes.
Comment thread provider/claude/parser.go
continue
}
events = append(events, parsed...)
}

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CollectUsageEvents appends per-file event slices without sorting the combined result. Because discovery order can vary (e.g., across baseDirs or future changes in collectPaths), this can make the returned event list non-deterministic even though parseUsageEvents sorts within a file. Consider sorting the final events slice (e.g., by Timestamp/SourcePath/EventID) before returning, similar to cursor's CollectUsageEvents.

Suggested change
}
}
sort.Slice(events, func(i, j int) bool {
if !events[i].Timestamp.Equal(events[j].Timestamp) {
return events[i].Timestamp.Before(events[j].Timestamp)
}
if events[i].SourcePath != events[j].SourcePath {
return events[i].SourcePath < events[j].SourcePath
}
return events[i].EventID < events[j].EventID
})

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants