Cursor CLI omits beforeSubmitPrompt, afterAgentResponse, and stop hooks, loses token usage, and emits inconsistent generation_id values

Where does the bug appear (feature/product)?

Cursor CLI

Describe the Bug

Cursor CLI invokes some hooks on macOS, but it does not emit the complete lifecycle that an equivalent Cursor IDE invocation emits.

The CLI successfully invoked session, thought, tool, file-read, and session-end hooks. However, it did not invoke: beforeSubmitPrompt , afterAgentResponse, stop. The equivalent IDE invocation emitted all three events.

This is particularly important for observability integrations. We are building an agent observability tool that reconstructs traces from Cursor hook events. It relies on:

  • conversation_id for conversation-level correlation,
  • generation_id for model-generation correlation,
  • beforeSubmitPrompt for the beginning of a user turn ,
  • afterAgentResponse for the authoritative final response,
  • stop for final status, loop count, and aggregate token usage.

In CLI mode, the missing afterAgentResponse and stop events mean that the hook stream contains neither the authoritative final assistant response nor any token-usage information.

There is also a separate correlation issue: the CLI uses the conversation_id as the generation_id for session and tool events, while afterAgentThought events use a different family of suffixed generation IDs. This makes it unclear how tool events should be associated with the actual model generation.

Steps to Reproduce

  1. Create a non-blocking hook handler that reads the JSON payload from standard input and appends it to a log file.

  2. Register the handler against all available Cursor hooks in hooks.json, including at least:
    sessionStart
    beforeSubmitPrompt
    afterAgentThought
    preToolUse
    postToolUse
    beforeReadFile
    afterAgentResponse
    stop
    sessionEnd

  3. From a repository, invoke Cursor CLI using model composer-2.5.

  4. Give the agent a small task. Let the run complete normally.

  5. Review the recorded hook sequence.

  6. Run an equivalent prompt from Cursor IDE against the same repository and model.

  7. Compare the hook sequences and payload fields.

Expected Behavior

  • Cursor CLI should provide enough hook information to reconstruct the same logical agent turn as Cursor IDE. Ideally, the CLI should emit:
    sessionStart
    beforeSubmitPrompt
    afterAgentThought
    preToolUse
    postToolUse
    afterAgentResponse
    stop
    sessionEnd

  • At minimum, one terminal CLI hook should provide:
    conversation_id
    generation_id
    model
    model_id
    final response text
    status
    loop_count
    input_tokens
    output_tokens
    cache_read_tokens
    cache_write_tokens

  • All thought, tool, response, and terminal events belonging to one model generation should expose either:
    The same stable base generation_id, or
    An explicitly documented parent-generation field that permits deterministic correlation.

Clarity on the behaviour of hooks in cli mode will be much helpful:

  1. Is the absence of beforeSubmitPrompt, afterAgentResponse, and stop in Cursor CLI 2026.08.11-e8db854 expected?

  2. Is this a known regression or an intentionally unsupported part of CLI hook behavior?

  3. Is sessionEnd intended to replace stop in CLI mode?

  4. If sessionEnd replaces stop, can it expose token usage, loop count, final response text, and the actual model-generation ID?

  5. Why do CLI afterAgentThought hooks use a separate suffixed generation-ID family which is different from the base generation id with which the current generation starts ?

  6. Is full lifecycle and payload parity between Cursor IDE and Cursor CLI planned?

Operating System

MacOS

Version Information

Cursor CLI version: 2026.08.11-e8db85

Does this stop you from using Cursor

No - Cursor works, but with this issue. Although, this is a blocker for us to build our internal observability tool.

sample-output-ide-run.json.txt (12.5 KB)

sample-output-cli-run.json.txt (8.0 KB)

Hey @Rahul_Chauhan, thanks for the detailed report!

First, could you confirm you were running the CLI in non-interactive mode (with -p/--print)? Assuming so, here’s where things stand:

Yes, in non-interactive mode there’s a known gap where beforeSubmitPrompt, afterAgentResponse, and stop aren’t emitted.

No,sessionEnd is a separate session-teardown event (it carries the end reason and duration) and isn’t meant as a stand-in for stop. Today no non-interactive event carries token usage, loop count, or the final response text. In interactive mode, afterAgentResponse carries input/output/cache token counts and stop carries status and loop count.

Those are per-model-generation IDs, while in non-interactive mode the session and tool events currently fall back to using the conversation ID as generation_id instead of the real per-generation ID. For now, conversation_id is the reliable correlation key in non-interactive runs.

Closing the hook lifecycle gap between the IDE and CLI (including non-interactive mode) is on the team’s radar, though I can’t share a timeline!

Thanks for the update @Colin . Is there any alternative that you recommend to record token usage from some other data source ? We are facing a similar problem for normal IDE based sub-agent runs, no token usage is sent for those as well while the same is visible in the “Usage” dashboard (https://cursor.com/dashboard/usage) . Is there any API or something that we can hit to get the token splits for CLI based runs and sub-agent runs ?

Hey @Rahul_Chauhan!

I’d recommend checking out the Admin API, specifically POST /teams/filtered-usage-events, which returns each event with the model, input/output/cache-read/cache-write token counts, and cost.

Each event also carries a conversation ID, which should let you correlate events with the conversation_id from your hook logs.

Two caveats: there’s currently no field distinguishing CLI from IDE events, and sub-agent activity shows up as separate events under the sub-agent’s own conversation ID rather than linked to the parent run.

Thank you for the response!