---
title: Send Claude Code and Claude Agent SDK sessions to Agent Analytics
description: "Track agents you build on the Claude Agent SDK on your own users, or export Claude Code CLI telemetry over OTLP logs, then join sessions to product events and to the commits they produce."
product: general
lang: en
last_updated: 2026-11-09
token_estimate: 7989
---
# Send Claude Code and Claude Agent SDK sessions to Agent Analytics

Agent Analytics records Claude sessions on two paths. Agents you build on the Claude Agent SDK and ship to your own users run through the AI SDK's `ClaudeAgentSDKTracker`. Each session lands on your end user's ID, so it joins retention, conversion, and revenue in the same Amplitude project. Claude Code CLI sessions from your engineers export native OpenTelemetry logs to Amplitude, with no Amplitude code in the agent.

> For AI agents: a documentation index is available at [/docs/llms.txt](/docs/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

Start with the Agent SDK path when Claude is part of your product. The CLI path is for internal engineering fleets. It's useful for cost, adoption, and which agent-written code ships, but the user is an employee who usually isn't in your product analytics.

## Set up with your AI coding agent (recommended)

Paste this prompt into your AI coding agent, such as Cursor, Claude Code, Windsurf, GitHub Copilot, or Codex. The agent detects which path the repository uses, writes the configuration, and starts with a dry run.

```text
Connect Claude Code or our Claude Agent SDK app to Amplitude Agent Analytics.

Fetch https://raw.githubusercontent.com/amplitude/Amplitude-AI-Node/main/docs/integrations/claude-code.md
and follow "Part 2: Coding agent procedure".
If @amplitude/ai is installed, the same file is under node_modules/@amplitude/ai/docs/integrations/.
Also follow https://raw.githubusercontent.com/amplitude/Amplitude-AI-Node/main/docs/integrations/privacy.md
Follow links in the guide. If your source isn't listed, start at https://raw.githubusercontent.com/amplitude/Amplitude-AI-Node/main/docs/integrations/README.md
```

Review the changes, then verify with the steps in [Get started](#get-started).

## What this means for you

- **Agent SDK path**: You add `ClaudeAgentSDKTracker` to the code that calls `query()`. You need the AI SDK, your Amplitude project API key, and your product-analytics user ID for each session.
- **CLI path**: You commit Claude Code settings that export OpenTelemetry logs to Amplitude. You need Claude Code v2.1.310 or later and your Amplitude project API key. You don't need the AI SDK in the agent.
- **Privacy**: Amplitude doesn't scrub content after receipt. On the Agent SDK path, the AI SDK redacts PII before events leave your process. On the CLI path, Claude Code's content settings decide what leaves the machine. Refer to [Privacy gates](#privacy-gates).

## Claude Agent SDK

Wrap each user's conversation in an AI SDK session and pass every message from `query()` to the tracker. `tracker.hooks(session)` adds tool hooks with precise tool latency.

#### Node

```typescript
import { AmplitudeAI } from "@amplitude/ai";
import { ClaudeAgentSDKTracker } from "@amplitude/ai/integrations/claude-agent-sdk";
import { query } from "@anthropic-ai/claude-agent-sdk";

const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
const agent = ai.agent({ agentId: "app-builder" });
const tracker = new ClaudeAgentSDKTracker();

await agent.session({ userId, sessionId: conversationId }).run(async (s) => {
  for await (const message of query({ prompt, options: { hooks: tracker.hooks(s) } })) {
    tracker.process(s, message);
  }
});
```

#### Python

```python
from amplitude_ai.integrations.claude_agent_sdk import ClaudeAgentSDKTracker
from claude_agent_sdk import ClaudeAgentOptions, query

agent = ai.agent("app-builder")
tracker = ClaudeAgentSDKTracker()

async with agent.session(user_id=user_id, session_id=conversation_id) as s:
    options = ClaudeAgentOptions(hooks=tracker.hooks(s))
    async for message in query(prompt=prompt, options=options):
        tracker.process(s, message)
```

`userId` is the same user ID your product analytics uses. Every turn records the user message, the assistant reply, each tool call with its input and output, the model, tokens, and cost.

To record how the session started, pass `sessionTrigger` (Node) or `session_trigger` (Python) to the tracker: `interactive`, `headless`, `ci`, `scheduled`, or `event`. Every event then carries it as `[Agent] Session Trigger`. Without a valid option, the tracker uses `AMPLITUDE_SESSION_TRIGGER`, then `ci` when `GITHUB_ACTIONS` is `true` or `CI` is set to anything other than an empty value, `false`, or `0`. Otherwise it leaves the property out. The tracker doesn't set `[Agent] Root Agent Name` or `[Agent] Agent Chain Depth`. For the tracker reference, refer to [Claude Agent SDK](https://amplitude.com/docs/sdks/agent-analytics/sdk#claude-agent-sdk) in the SDK reference.

## Claude Code CLI

Run this in the repository root. It requires `@amplitude/ai` 0.28.0 or `amplitude-ai` 1.25.0 or later.

```bash
export AMPLITUDE_AI_API_KEY=AMPLITUDE_API_KEY
npx -y -p @amplitude/ai amplitude-ai coding-agents init --harness claude-code
# or, in a Python project
amplitude-ai coding-agents init --harness claude-code
```

`init` reads the project API key from `AMPLITUDE_AI_API_KEY`, then `AMPLITUDE_API_KEY`. It writes:

- **`.claude/settings.json`**: Committed. Turns on Claude Code telemetry, points the logs exporter at Amplitude, and adds a `SessionStart` hook that sets `core.hooksPath` to `.githooks` only when the clone has no hooks path yet.
- **`.claude/settings.local.json`**: Gitignored. Holds the `Authorization` header with your key and `enduser.id` from `AMPLITUDE_USER_ID`, else `git config user.email`. When no key is set, `init` skips this file and prints the shell exports to set instead.
- **`.githooks/prepare-commit-msg`**: The commit trailer hook. Refer to [Connect to outcomes](#connect-to-outcomes).
- **`.github/workflows/agent-outcomes.yml`**: The outcomes workflow.

`init` also sets `core.hooksPath` to `.githooks` for the current clone. If the clone already uses another hooks path, for example from husky, `init` reports a conflict and leaves it alone. Copy `.githooks/prepare-commit-msg` into that hooks directory instead. In the Node CLI, `--force` overrides the existing path.

| Flag | Effect |
| --- | --- |
| `--harness claude-code|codex|goose` | Repeatable. Without it, `init` sets up all three. |
| `--privacy full|metadata` | `full` (default) sends prompts, replies, and tool details. `metadata` leaves out the three content settings. |
| `--region us|eu` | Data region. Without it, `init` uses `$AMPLITUDE_REGION`, then the region a previous `init` set, then `us`. |
| `--dry-run` | Shows what would change and writes nothing. |
| `--json` | Machine-readable output. |
| `--dir PATH` | Any directory inside the repository. |
| `--api-key-stdin`, `--api-key-env NAME` | Node only. Reads the key from standard input or from a named environment variable. |
| `--force` | Replaces a commit hook or outcomes workflow that differs from the template. In Node, it also replaces an existing `core.hooksPath`. |

Merges are idempotent and keep your existing settings. `init` doesn't overwrite a file whose content differs from the template unless you pass `--force`. It reports the file as a conflict and exits with status 1. The one exception is an unchanged copy of an earlier outcomes workflow, which `init` upgrades. In a Node repository, `init` also adds a `prepare` script that sets `core.hooksPath` on install. Run `coding-agents doctor` to check the hooks path, the commit hook, the workflow, and each harness's settings, including whether the API key header is present. It never prints the key. It accepts `--dir` and `--json`, and the Python CLI also accepts `--harness`.

The committed settings match this template:

```json
{
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "https://api.amplitude.com/otlp/v1/logs",
    "OTEL_LOG_USER_PROMPTS": "1",
    "OTEL_LOG_ASSISTANT_RESPONSES": "1",
    "OTEL_LOG_TOOL_DETAILS": "1"
  },
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "git config --local --get core.hooksPath >/dev/null 2>&1 || git config --local core.hooksPath .githooks 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}
```

With `--region eu`, the endpoint is `https://api.eu.amplitude.com/otlp/v1/logs`. With `--privacy metadata`, `init` leaves out `OTEL_LOG_USER_PROMPTS`, `OTEL_LOG_ASSISTANT_RESPONSES`, and `OTEL_LOG_TOOL_DETAILS`.

Logs carry the turn. Trace export is optional. If you also export Claude Code traces, Amplitude records the `claude_code.*` spans as `[Agent] Span` events without tokens or cost, so nothing counts twice.

### Identity

| Value | Where it comes from |
| --- | --- |
| Session | `session.id` |
| Trace | `prompt.id`, one per user prompt |
| User | `enduser.id` in `OTEL_RESOURCE_ATTRIBUTES` when set. Otherwise `user.email`, then `user.account_uuid`. Claude Code's anonymous `user.id` is used only behind an OIDC gateway. |
| Agent | `agent_id`, else `claude-code` |
| Parent agent | `parent_agent_id`, so subagents nest under the agent that started them |
| Session trigger | `amplitude.session_trigger` in `OTEL_RESOURCE_ATTRIBUTES`, else `ci` when the resource has any `cicd.pipeline.*` attribute |

Amplitude skips a record without a session ID or a user ID that can join your data, rather than storing it under a trace ID or as `unknown`. Use `user.email` only when the email is the user ID in your product analytics.

Claude Code doesn't report how a session started, so add `amplitude.session_trigger` to `OTEL_RESOURCE_ATTRIBUTES`, next to `enduser.id`, with one of `interactive`, `headless`, `ci`, `scheduled`, or `event`. Amplitude sets it as `[Agent] Session Trigger` on every event and ignores other values. The commit hook reads `AMPLITUDE_SESSION_TRIGGER` for the `Agent-Trigger` trailer, so in a CI job, set both from one variable:

```bash
export AMPLITUDE_SESSION_TRIGGER=ci
export OTEL_RESOURCE_ATTRIBUTES="enduser.id=<user ID>,amplitude.session_trigger=$AMPLITUDE_SESSION_TRIGGER"
```

The resource attribute needs the Amplitude OTLP receiver release that maps it. Until that release is live in your data center, Claude Code events have no `[Agent] Session Trigger`. Claude Code events don't carry `[Agent] Root Agent Name` or `[Agent] Agent Chain Depth`.

## What carries over

| Claude Code log event | Amplitude event |
| --- | --- |
| `claude_code.user_prompt` | `[Agent] User Message` |
| `claude_code.api_request` | `[Agent] AI Response` with model, input, output, cache read, and cache creation tokens, cost, and latency |
| `claude_code.assistant_response` | Reply text on the same `[Agent] AI Response`, matched by `request_id` in either arrival order |
| `claude_code.tool_result` | `[Agent] Tool Call` with tool name, success, duration, and error type. When the record carries `vcs.ref.head.revision`, Amplitude sets `commit_sha` from it. Refer to [Privacy gates](#privacy-gates). |
| `claude_code.subagent_completed` | `[Agent] Span` for the subagent run |

Each model call is one turn, whichever record arrives first. Tokens, cost, and turn counts aren't doubled. Amplitude accepts other Claude Code log events and doesn't turn them into `[Agent]` events.

## Privacy gates

Claude Code sends no prompt, reply, or tool text unless these settings are on. `init` turns on all three by default. `init --privacy metadata` leaves all three out. You can also remove any of them from `.claude/settings.json` yourself.

| Setting | Sends |
| --- | --- |
| `OTEL_LOG_USER_PROMPTS=1` | User prompt text |
| `OTEL_LOG_ASSISTANT_RESPONSES=1` | Reply text |
| `OTEL_LOG_TOOL_DETAILS=1` | Tool input, such as the shell command |

With every gate off, sessions, users, agents, models, tokens, cost, tool names, and tool success still arrive. Evaluators that find commits or pull requests in tool calls need tool details.

Claude Code v2.1.269 or later adds `vcs.ref.head.revision` to `claude_code.tool_result` only when `OTEL_LOG_TOOL_DETAILS=1`, and only for a successful `git commit` run by the Bash or PowerShell tool, so that's the only case where the tool call carries `commit_sha`. With `--privacy metadata`, or for commits made through other tools such as a GitHub MCP server, the tool call has no `commit_sha`, but the commit trailers from the `prepare-commit-msg` hook still attribute merged commits to the session. Amplitude doesn't scrub content after receipt, so scrub in a Collector before export when you need to. Refer to [Agent Analytics privacy modes](https://amplitude.com/docs/amplitude-ai/agent-analytics/privacy-modes).

## Connect to outcomes

**Agent SDK path.** Sessions share a user ID with your product events, so funnels, cohorts, and experiments work with no extra setup. Chart an outcome you already track, such as `App Published`, `Suggestion Accepted`, or `Plan Upgraded`, after an agent session. For a new user, "the first session ended in a published app or merged pull request" is the adoption predictor to chart first. Compare users whose sessions had tool failures with those whose sessions didn't.

**CLI path.** The outcome is what happened to the code. The trailer hook stamps `CLAUDE_CODE_SESSION_ID`, which equals the `session.id` on Claude Code's telemetry, so each outcome score lands on the session that wrote the commit.

**In-session outcomes** come from your `[Agent] Tool Call` events, with no extra setup. Binary evaluators can label sessions that made a commit, opened a pull request, or ended with passing tests. A code evaluator with a regex over tool turns, such as `\bgit\s+commit\b`, finds commits and pull requests. An LLM evaluator judges whether the last test command succeeded. Refer to [Create and calibrate evaluators](https://amplitude.com/docs/amplitude-ai/agent-analytics/evaluators).

Code evaluators read the tool input of every turn, so a commit at the end of a long session still counts. Each evaluator writes an `[Agent] Evaluator Result`, so you can compare it with built-in signals: do Task Completed sessions actually produce commits? A tool call that creates a commit can also carry a `commit_sha` event property. It's read from the `[branch sha]` line that `git commit` prints, or from the `sha` in a JSON result when the tool name or input names a commit, pull request, or file push as a whole word, such as `create_pull_request` or `push_files`. Output from `git log`, `git show`, and other hex strings is never read as a commit.

**Downstream outcomes**, such as merged, reverted, and deployed, need two pieces that `coding-agents init` writes into the repository:

1. A `prepare-commit-msg` hook in `.githooks/` that stamps `Agent-Session-Id` and `Agent-Name` trailers on every commit the agent makes. Human commits stay unchanged, and `--no-verify` doesn't skip this hook. It also adds an `Agent-Trigger` trailer when it knows how the session started, as described after the score tables.
2. A GitHub Actions workflow, `.github/workflows/agent-outcomes.yml`, that reads those trailers and sends each outcome to the Amplitude HTTP API as an `[Agent] Score` on the session that wrote the commit. The outcome appears next to the session's own events and evaluator results, with no new event type. Store the project API key in the `AMPLITUDE_API_KEY` repository secret. These repository variables are optional:

| Variable | Effect |
| --- | --- |
| `AMPLITUDE_REGION` | `eu` sends to the EU data center. The default is US. |
| `AMPLITUDE_OUTCOMES_DRY_RUN` | `true` prints the scores instead of sending them. |
| `AMPLITUDE_USER_ID_SOURCE` | `email` (default) uses the commit author email as the user ID. `login` uses the GitHub login. Use the value your agent sessions use as the user ID. |
| `AMPLITUDE_AGENT_SETUP_PATHS` | Comma-separated root-level files or folders that make up your agent setup, for `agent_setup_version`. The default is `AGENTS.md,CLAUDE.md,GEMINI.md,.claude,.codex,.cursor,.cursorrules,.gemini,.goosehints,.mcp.json`. The workflow ignores names that contain `/` and uses the first 20 names. Changing it changes every later `agent_setup_version`. |

The workflow asks for read access to `contents`, `pull-requests`, `deployments`, `actions`, `checks`, and `statuses`. It reads workflow runs, check runs, and commit statuses for `ci_passed_first_run`. For a pull request from a fork, meaning one whose head branch is in another repository, GitHub doesn't pass repository secrets to the run, so the workflow logs a notice and sends nothing instead of failing.

Each score sets these properties:

| Property | Value |
| --- | --- |
| `[Agent] Session ID` and `[Agent] Target ID` | The commit's `Agent-Session-Id` trailer. |
| `[Agent] Target Type` | `session`. |
| `[Agent] Agent ID` | The commit's `Agent-Name` trailer, such as `claude-code`, `codex`, `cursor`, or `goose`. Otherwise the agent that signed the commit, as its author or in a `Co-authored-by` line. |
| `[Agent] Evaluation Source` | `outcome`. |
| `[Agent] Score Name` and `[Agent] Score Value` | One of the scores in the next table. |

The event time is the commit's author date, so the score falls inside the session that wrote the commit. `outcome_time` holds when the outcome happened: the merge, the revert's merge, or the deployment.

| Score Name | Value | Sent when | Also carries |
| --- | --- | --- | --- |
| `commit_merged` | 1 | A pull request merges. One score for each agent commit in the pull request. | `pr_number`, `merge_commit_sha`, and `base_branch`. |
| `commit_reverted` | 1 | A merged pull request's description or commits contain `This reverts commit <sha>`. One score on the session that wrote the reverted commit. | `revert_pr_number` and `revert_merge_commit_sha`. |
| `deploy_succeeded` | 1 | A GitHub deployment succeeds. One score for each agent commit from the previous successful deployment to the same environment up to the deployed commit. When there's no earlier successful deployment, the deployed commit only. | `deployment_sha` and `environment`. |
| `deploy_failed` | 1 | A GitHub deployment status is `failure` or `error`. One score for each agent commit in the same range as `deploy_succeeded`. | `deployment_sha`, `environment`, and `deployment_state`, which is `failure` or `error`. |
| `ci_passed_first_run`, `review_rounds`, `review_count`, `changes_requested_count`, `hours_open`, and `hours_to_first_review` | The measure. `ci_passed_first_run` is 1 or 0. | A pull request merges. One of each for every agent session in the pull request, on that session's first commit in it. | `pr_number`, `merge_commit_sha`, `base_branch`, `commit_count`, `agent_commit_count`, `session_commit_count`, and `pr_session_count`. |

Every score also carries `commit_sha`, `agent_session_id`, `agent_name`, `agent_trigger`, `agent_setup_version`, `repo`, `outcome_time`, and `outcomes_workflow_version`, set to `3`.

- **Only agent sessions get scores.** A commit without an `Agent-Session-Id` trailer sends nothing. That covers human commits, Gemini CLI commits, commits made through the GitHub API or MCP tools, and cloud-agent commits known only by their author or `Co-authored-by` line. For a pull request with no such commit, the workflow also skips the reviews and CI lookups.
- **A pull request counts once for each session.** When several sessions wrote one pull request, each gets the pull request scores, and `pr_session_count` says how many sessions there were. To count pull requests, count distinct `pr_number` values.
- **The user ID** is the commit author email, or the GitHub login with `AMPLITUDE_USER_ID_SOURCE=login`. It must match the user ID your agent sessions use. `coding-agents init` sets the session user ID to the git email. With `login`, the pull request scores fall back to the pull request author when the session's first commit has no GitHub account. A commit with no user ID gets no scores.
- **A score with no value isn't sent**, such as `hours_to_first_review` for a pull request with no human review.
- **Insert IDs** are the score name, the repository, and the commit SHA, plus the deployment ID for deploy scores, the revert's pull request number for `commit_reverted`, and the pull request number and session instead of the SHA for the pull request scores. A rerun within Amplitude's 7-day deduplication window doesn't count twice.
- **Outcome scores aren't user feedback.** Agent Analytics reads only scores with the `user` source as user feedback, so an outcome never counts as a thumbs up or down.

`agent_trigger` says how the session that wrote the commit started, with the same values as `[Agent] Session Trigger` on the session's events: `interactive`, `headless`, `ci`, `scheduled`, or `event`. It comes from the `Agent-Trigger` trailer. The hook adds that trailer only to commits it stamps with an agent session or name, and only when it knows the value. It resolves the value the way the Codex and Cursor hooks resolve `[Agent] Session Trigger`, so a commit and its session get the same value:

1. `AMPLITUDE_SESSION_TRIGGER` in the environment, when it's one of the five values. Set it where the agent runs, for example `AMPLITUDE_SESSION_TRIGGER=headless` for a script that runs the agent without a person, or `scheduled` for a scheduled job. When the variable is set, the hook doesn't read `.amplitude-ai/local.env` for it.
2. Otherwise `AMPLITUDE_SESSION_TRIGGER` in `.amplitude-ai/local.env` at the repository root. The hook reads the file as `KEY=value` lines and never runs it.
3. Otherwise `ci`, when `GITHUB_ACTIONS` is `true`, or `CI` is set to anything other than an empty value, `false`, or `0`.
4. Otherwise, for Codex, how the session started, from `source` in the first line of the session transcript under `$CODEX_HOME/sessions` (by default `~/.codex/sessions`): `codex exec` or the MCP server gives `headless`, and the CLI or VS Code gives `interactive`.
5. Otherwise no trailer.

Without a valid trailer, `agent_trigger` is `event` when a coding agent is the commit author, as when a cloud agent works from an issue or an assignment, and `null` otherwise.

The pull request scores use these definitions:

| Score Name | Description |
| --- | --- |
| `hours_open` | Hours from opening the pull request to merging it, rounded to 2 decimals. Time as a draft counts. |
| `review_count` | Human reviews submitted as approved, changes requested, or commented with a comment. A reply in a review thread doesn't count, and neither do reviews from bots or the pull request author. A review with only inline comments and no summary doesn't count either. |
| `changes_requested_count` | The reviews in `review_count` that requested changes. |
| `hours_to_first_review` | Hours from opening the pull request to the first review in `review_count`, rounded to 2 decimals. Not sent when there's no such review. |
| `review_rounds` | The reviews in `changes_requested_count` that at least one later commit followed, meaning a commit in the pull request whose committer date is after the review. A rebase gives commits a new committer date, so a rebase after a review counts as a commit that followed it. |
| `ci_passed_first_run` | 1 when CI passed on its first attempt, 0 when it didn't. The workflow finds the first push that CI ran on, which is the head commit of the pull request's earliest GitHub Actions workflow run between opening and merging, other than the outcomes workflow itself, and reads the earliest run of each check and commit status on it. A push of several commits runs CI on the last one only, so this works however many commits the pull request opened with. Runs are matched by the pull request's commits, so after a force-push the earliest push that survived counts. Without GitHub Actions runs, the workflow takes the first commit with finished CI results among the pull request's first 20 commits. `success`, `neutral`, and `skipped` count as passing. Cancelled and unfinished runs don't count. A later rerun that passes doesn't change the value. Not sent when the pull request has no CI. |

`commit_count` is the pull request's commit count. GitHub lists at most 250 commits, so `commit_merged`, `agent_commit_count`, and `session_commit_count` cover the first 250.

`agent_setup_version` identifies the agent setup a commit was written with. It's the first 12 hex characters of a SHA-256 over the root-level entries named in `AMPLITUDE_AGENT_SETUP_PATHS`, read from the commit's parent. Any change to one of those files, or to a file inside one of those folders, produces a new value. It's `null` when none of the entries exist. A commit that edits `AGENTS.md` carries the version from before the edit, and the next commit carries the new one. On the pull request scores, it's the value for the session's first commit in the pull request.

`agent_setup_version` and the review and CI scores are best effort. Together they make at most 300 GitHub API requests in a run, counting each page of results. When one fails or the budget runs out, the workflow leaves out that property or score, logs a warning, and still sends the rest. The run log includes an `enrichments:` line that shows which ones succeeded and how many API calls the run made. Treat a missing value as unknown, not as 0.

Because each outcome is a score on its session, you can chart it next to the session's own events with no join. Every chart below starts from `[Agent] Score` filtered on `[Agent] Score Name`. Charts by event time bucket outcomes by when the work was written. The step-by-step recipe is the [code outcomes recipe](https://github.com/amplitude/Amplitude-AI-Node/blob/main/docs/integrations/recipes/code-outcomes.md).

- **How much agent work holds?** Chart the count of `commit_merged` minus the count of `commit_reverted`, grouped by `[Agent] Agent ID`. Both are timed at the commit's author date, so a date range that ends 30 days ago counts the changes written at least 30 days ago that are still unreverted. The revert rate is `commit_reverted` divided by `commit_merged`.
- **Did a change to the agent setup help?** Divide `commit_reverted` by `commit_merged`, and `deploy_failed` by both deploy scores, grouped by `agent_setup_version`. A deploy score counts one commit in one deployment attempt, so a commit in a failed deployment that later succeeds counts once in each.
- **Do agent pull requests pass CI the first time, and how much review do they need?** Chart the average `[Agent] Score Value` of `ci_passed_first_run`, which is the pass rate, and of `review_rounds` and `changes_requested_count`, grouped by `[Agent] Agent ID` or `agent_setup_version`. Every session in a pull request carries these scores, so for rates by pull request, count distinct `pr_number` values.
- **Which sessions reach production?** Build a funnel from an `[Agent] Tool Call` where `commit_sha` is set, to `commit_merged`, to `deploy_succeeded`, held on `[Agent] Session ID`.
- **Which session patterns get reverted?** Group `commit_reverted` by an evaluator's result, such as the Session Shape evaluator.

These charts compare work across stages: a person at the keyboard, a headless run, CI, a schedule, or an event.

- **Sessions by stage**: Count distinct `[Agent] Session ID` values on `[Agent] User Message`, grouped by `[Agent] Session Trigger`. Add a filter on `[Agent] Agent Chain Depth` greater than 1 to see delegated work. Sessions whose harness can't tell how they started have no trigger.
- **Grind rate by intent and model**: Chart `[Agent] Evaluator Result` where `[Agent] Evaluator Name` is the Session Shape evaluator, grouped by `[Agent] Primary Label` and `[Agent] Models Used`. The Grind share is the grind rate. Chart the Coding Intent evaluator's results the same way, as the second breakdown next to it. Each Evaluator Result carries one evaluator's label, so the two results don't combine on one event. Refer to [Coding agent templates](https://amplitude.com/docs/amplitude-ai/agent-analytics/evaluators#coding-agent-templates).
- **Claimed done without verification, by stage**: Chart `[Agent] Evaluator Result` where `[Agent] Evaluator Name` is the Claimed Done Without Verification evaluator, grouped by `[Agent] Session Trigger`, with the average of `[Agent] Binary Value` as the metric. That average is the rate for each stage. Evaluator Results carry the session's trigger, so this is one event and no join.
- **Review effort by stage**: Chart the average `[Agent] Score Value` of `review_rounds` and `hours_to_first_review`, grouped by `agent_trigger`.
- **Cost per merged change, by stage or by agent tree**: A formula that divides the sum of `[Agent] Cost USD` on `[Agent] AI Response` by the count of `[Agent] Score` where `[Agent] Score Name` is `commit_merged`. Group the two sides on the matching value: `[Agent] Session Trigger` against `agent_trigger`, or `[Agent] Root Agent Name` against `agent_name`. It's an aggregate ratio, not a join for each change, and it counts cost only from harnesses that report one.

**Measure a prompt that went viral.** A prompt or skill can spread to many teams before anyone checks whether it works on their code. To find out across every session, by outcome:

- **A pasted prompt**: Create an evaluator from the Prompt or Skill Used template, a Regex evaluator that scans user messages, and replace its placeholder pattern with the prompt's text. It reads user message text, so it matches only sessions that send prompts, as in the `full` content mode or with `OTEL_LOG_USER_PROMPTS=1` for Claude Code. Then compare Session Shape and Claimed Done Without Verification on the matching sessions against the rest: filter the Sessions tab by the Prompt or Skill Used result, once `True` and once `False`, and read the other two evaluators' results.
- **A prompt saved as a command or skill**: Saving it under a path in `AMPLITUDE_AGENT_SETUP_PATHS`, such as the default `.claude`, `.codex`, or `.cursor`, changes `agent_setup_version`. Compare the `review_rounds` and `ci_passed_first_run` scores, the revert rate (`commit_reverted` divided by `commit_merged`), and the deploy failure rate (`deploy_failed` divided by both deploy scores) by `agent_setup_version`, before and after the version that added it.
- **Across harnesses**: Group the session charts by `[Agent] Agent ID`, for example `claude-code` against `codex`, to see whether the prompt works as well in each one.

**Upgrade from an earlier version**: Run `init` again, with `npx -y -p @amplitude/ai amplitude-ai coding-agents init`, or `amplitude-ai coding-agents init` in a Python project. It replaces an unchanged copy of an earlier workflow. `init` reports a workflow you edited as customized and leaves it alone. `--force` replaces it, or you can move your edits to the new workflow by hand. `coding-agents doctor` reports the workflow as current, outdated, or customized. Version 3 sends `[Agent] Score` events in place of the `Commit Merged`, `Commit Reverted`, `Pull Request Merged`, `Deploy Succeeded`, and `Deploy Failed` events that earlier versions sent, so rebuild charts that use those events on `[Agent] Score Name`.

## Get started

Follow the [Claude Code integration guide](https://github.com/amplitude/Amplitude-AI-Node/blob/main/docs/integrations/claude-code.md). Start a Claude Code session in a fresh clone, ask for a change with a subagent and a commit, then confirm in Live Events:

1. Session ID is the Claude Code `session.id`, not a trace ID.
2. User ID is your `enduser.id`, not `unknown`.
3. Each model call is one `[Agent] AI Response` with text, model, tokens, and cost.
4. The subagent's events carry `[Agent] Parent Agent ID`.
5. The `git commit` tool call carries `commit_sha`, and the commit has an `Agent-Session-Id` trailer.

On the Agent SDK path, confirm the session's user ID is your product user. After the session has been idle for 30 minutes, open it in the session viewer to see the built-in signals.

