Skip to content

Fix Cursor hook misattribution and add token usage support#1263

Open
SnowingFox wants to merge 5 commits into
entireio:mainfrom
SnowingFox:feat/cursor-hook-guard-and-tokens
Open

Fix Cursor hook misattribution and add token usage support#1263
SnowingFox wants to merge 5 commits into
entireio:mainfrom
SnowingFox:feat/cursor-hook-guard-and-tokens

Conversation

@SnowingFox

@SnowingFox SnowingFox commented May 26, 2026

Copy link
Copy Markdown

close #1262
close #1264
image

Dispatch — feat/cursor-hook-guard-and-tokens

2026-05-26, 1 commit, 1 checkpoint (2a89e658287e)

Summary

This branch fixes Cursor session misattribution when only Claude Code hooks are installed (#1262), and adds token usage reporting for Cursor sessions by parsing the stop hook payload — the only authoritative source, since Cursor's JSONL transcript contains no usage fields.

Work Items

Bug Fix: Cross-agent hook forwarding guard (#1262)

  • Added shouldSkipForwardedHook() in hook_guard.go — checks if event.SessionRef (transcript path) belongs to a different registered agent via AgentForTranscriptPath
  • Wired the guard into executeAgentHook() in hook_registry.go, before DispatchLifecycleEvent — silently skips with a debug log when a forwarded hook is detected
  • Covers the scenario: user has .claude/settings.json but no .cursor/hooks.json; Cursor IDE fires Claude Code hooks; transcript path proves it's a Cursor session

Feature: Cursor token usage from stop hook

  • Added TokenUsage *TokenUsage field to agent.Event — allows hook parsers to inject authoritative token data that the framework prefers over transcript-based computation
  • Extended stopHookInputRaw in cursor/types.go with input_tokens, output_tokens, cache_read_tokens, cache_write_tokens fields
  • Implemented tokenUsageFromStop() in cursor/lifecycle.go — derives fresh input tokens as max(0, total_input - cache_read - cache_write), matching the story/apps/cli approach
  • Modified handleLifecycleTurnEnd in lifecycle.go to prefer event.TokenUsage over agent.CalculateTokenUsage() fallback

Tests

  • hook_guard_test.go: transcript belongs to other agent (skip), transcript belongs to self (pass through), full executeAgentHook integration test confirming no state file created
  • lifecycle_test.go: TestHandleLifecycleTurnEnd_PrefersEventTokenUsage — verifies hook-provided tokens flow into session state
  • cursor/lifecycle_test.go: token extraction from stop hook, nil when omitted, negative clamp to zero

1. Add transcript-owner guard in executeAgentHook to prevent Cursor
   sessions from being claimed by Claude Code when only
   .claude/settings.json is installed (fixes entireio#1262).

2. Parse token fields (input_tokens, output_tokens, cache_read_tokens,
   cache_write_tokens) from Cursor's stop hook payload into
   Event.TokenUsage so Cursor sessions report non-zero token counts.

3. Prefer hook-provided TokenUsage over transcript-based calculation
   in handleLifecycleTurnEnd, since Cursor's JSONL transcript has no
   usage data.

Co-authored-by: Cursor <cursoragent@cursor.com>
Entire-Checkpoint: 2a89e658287e
@SnowingFox SnowingFox requested a review from a team as a code owner May 26, 2026 07:42

@claude claude Bot 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@Soph

Soph commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

@SnowingFox thanks for your contribution! I had a look and I don't think this works yet. The issue is that the current code would overwrite the tokens again at the end since the same logic kicks in as for every other agent and tries to calculate the token usage from the script - where there isn't any for cursor. I let claude make two tests on top of your branch that proof the token calculation would be overridden but also highlights that there is more work to make sure the we add the right token count to each checkpoint. Since we would always get the whole token count in the hook of the session until then.

Let me know if you want to address this otherwise I'm also happy to take over your work.

Here is the draft PR with the two tests: #1388

@SnowingFox

Copy link
Copy Markdown
Author

@SnowingFox thanks for your contribution! I had a look and I don't think this works yet. The issue is that the current code would overwrite the tokens again at the end since the same logic kicks in as for every other agent and tries to calculate the token usage from the script - where there isn't any for cursor. I let claude make two tests on top of your branch that proof the token calculation would be overridden but also highlights that there is more work to make sure the we add the right token count to each checkpoint. Since we would always get the whole token count in the hook of the session until then.

Let me know if you want to address this otherwise I'm also happy to take over your work.

Here is the draft PR with the two tests: #1388

Thanks for the detailed feedback and the tests! That makes sense. I'll dig into #1388 and work on fixing the override issue + per-checkpoint delta calculation. Will push updates to this branch.

@SnowingFox

Copy link
Copy Markdown
Author

@Soph thanks again for the detailed review and for putting together #1388.

I pushed an update to this PR. The branch now includes the two acceptance tests from #1388:

  • TestCursorTokenUsage_SurvivesCondensation
  • TestCursorTokenUsage_PerCheckpointScoping

The fix keeps Cursor hook-provided token usage as a pending checkpoint-scoped delta, uses it during condensation only when transcript-derived usage is absent, and clears it after successful condensation so the next checkpoint does not inherit cumulative totals. I also merged current main and kept both Event.SkillEvents and Event.TokenUsage through the conflict.

Verified locally:

  • go test -tags integration ./cmd/entire/cli/integration_test -run TestCursorTokenUsage -count=1
  • mise run test:integration
  • mise run test
  • mise run lint

Could you please take another look when you have a chance?

@SnowingFox

Copy link
Copy Markdown
Author

Hi @Soph , just a gentle follow-up on this PR.

I’ve pushed the update addressing your previous feedback: the branch now includes the two acceptance tests from #1388, fixes the Cursor token usage override/condensation issue, and keeps the token usage scoped per checkpoint.

I also rebased/merged the latest main and verified it locally with the integration tests, full test suite, and lint.

Could you take another look when you get a chance? No rush, I’d mainly like to know if there are any remaining blockers or changes you’d like me to make.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants