> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squasher.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent telemetry SDK

> Capture agent sessions, tool calls, spans, and model generations from server-side runtimes.

Use `@squasher-ai/agent` when you need structured telemetry for AI workflows rather than plain errors and logs.

It is a good fit for:

* agent backends
* customer chat assistants
* tool-calling workflows
* OpenRouter / OpenAI-compatible inference pipelines
* internal copilots and runbooks

## Install

```bash theme={null}
bun add @squasher-ai/agent
```

## Initialize

```typescript src/agent.ts theme={null}
import { init } from "@squasher-ai/agent";

init({
  apiKey: process.env.SQUASHER_API_KEY!,
  projectId: process.env.SQUASHER_PROJECT_ID!,
  environment: process.env.NODE_ENV,
  agentId: "support-bot",
  workflowId: "triage-and-reply",
});
```

## Capture a session lifecycle

```typescript theme={null}
import {
  captureGeneration,
  captureSessionEnd,
  captureSessionStart,
  captureToolCall,
  flush,
} from "@squasher-ai/agent";

await captureSessionStart({ sessionId: "sess_123", runId: "run_123" });
await captureToolCall("db.lookup", {
  sessionId: "sess_123",
  traceId: "run_123",
  status: "ok",
  durationMs: 42,
});
await captureGeneration("Drafted reply", {
  sessionId: "sess_123",
  traceId: "run_123",
  provider: "openai",
  model: "gpt-4o-mini",
  promptTokens: 820,
  completionTokens: 140,
  totalTokens: 960,
  cachedInputTokens: 320,
  costUsd: 0.0012,
});
await captureSessionEnd({ sessionId: "sess_123", runId: "run_123", status: "completed" });
await flush();
```

## Vercel AI SDK / `streamText()` integration

If you already use AI SDK 7, attach Squasher as an AI SDK telemetry integration through `telemetry`. AI SDK 7 requires Node.js 22+ and ESM imports. Squasher turns its lifecycle events into session, span, generation, and tool-call observations for the AI observability views.

The shortest path is the AI SDK telemetry integration hook:

```typescript theme={null}
import { AgentTelemetryClient, createAiSdkTelemetryIntegration } from "@squasher-ai/agent";
import { streamText } from "ai";

const telemetry = new AgentTelemetryClient({
  apiKey: process.env.SQUASHER_API_KEY!,
  projectId: process.env.SQUASHER_PROJECT_ID!,
  environment: process.env.NODE_ENV,
  agentId: "support-bot",
  workflowId: "support-chat",
});

const result = streamText({
  model,
  messages,
  telemetry: {
    isEnabled: true,
    functionId: "support.reply",
    integrations: [
      createAiSdkTelemetryIntegration(telemetry, {
        sessionId: "chat_123",
        traceId: "run_123",
        distinctId: "user_123",
        tags: { workflow: "support-chat" },
        generationName: "support.reply",
        spanName: "support.run",
        privacyMode: true,
      }),
    ],
  },
});

result.consumeStream();
```

You can also use the lower-level callback factory when adapting a runtime that invokes lifecycle methods itself. For AI SDK 7 calls, prefer `createAiSdkTelemetryIntegration()` so the v7 `onStepEnd`, `onToolExecutionStart`, `onToolExecutionEnd`, and `onEnd` events are mapped correctly.

```typescript theme={null}
import { AgentTelemetryClient, createAiSdkTelemetryCallbacks } from "@squasher-ai/agent";

const telemetry = new AgentTelemetryClient({
  apiKey: process.env.SQUASHER_API_KEY!,
  projectId: process.env.SQUASHER_PROJECT_ID!,
  endpoint: process.env.SQUASHER_ENDPOINT,
  environment: process.env.NODE_ENV,
  agentId: "support-bot",
  workflowId: "support-chat",
});

const callbacks = createAiSdkTelemetryCallbacks(telemetry, {
  sessionId: "chat_123",
  distinctId: "user_123",
  provider: "openrouter",
  model: "openai/gpt-4.1-nano",
  generationName: "support.reply",
  spanName: "support.run",
});

await callbacks.onStart();
// Invoke the remaining callback methods from your runtime's lifecycle hooks.
```

This captures, automatically and per step:

* the enclosing session and run span
* each completed generation
* tool call start/finish events with sanitized input/output payloads
* prompt / completion / total tokens
* **reasoning tokens** (`usage.outputTokenDetails.reasoningTokens` from the AI SDK) — surfaced as `reasoning_tokens` on the observation
* cached input tokens (`usage.inputTokenDetails.cacheReadTokens`) — surfaced as `cached_input_tokens`
* the assistant **text output** (final `text`)
* the **reasoning text** only when you explicitly set `captureReasoning: true` — stored alongside the text in the generation's output payload as `reasoning`
* **cost in USD** — taken from `providerMetadata.openrouter.usage.cost` (OpenRouter), `providerMetadata.gateway.cost` (Vercel AI Gateway), or any explicit `costUsd` you pass through. When none of those are present, Squasher computes cost server-side from the token counts × a maintained per-model rate table, so the dashboard always shows a number for known models
* provider + model metadata (with the AI SDK's `.chat` / `.responses` suffix stripped)

If you want a cost value Squasher cannot derive from `providerMetadata`, pass `costUsd` in the second argument to `captureGeneration("message", { ..., costUsd: 0.0123 })` or set it on the result you forward to `onStepFinish`.

Use `privacyMode: true` when customer or model content should not leave your service. Token counts, model/provider identifiers, finish status, tool names, timings, prompt references without variables, and your custom attributes still flow through. Prompt messages, prompt variables, generation text, reasoning text, provider payloads, warnings, raw error text, and tool input/output are omitted.

Reasoning text is opt-in even when privacy mode is disabled. Set `captureReasoning: true` only when you have a deliberate retention and access policy for provider-supplied reasoning content. `privacyMode: true` always wins and suppresses reasoning text.

```typescript theme={null}
telemetry: {
  isEnabled: true,
  functionId: "support.reply",
  integrations: [
    createAiSdkTelemetryIntegration(telemetry, {
      sessionId: "chat_123",
      model: "openai/gpt-4.1-nano",
      privacyMode: true,
      attributes: {
        "customer.tier": "enterprise",
        "workflow.step": "draft_reply",
      },
    }),
  ],
}
```

To capture reasoning text explicitly:

```typescript theme={null}
createAiSdkTelemetryIntegration(telemetry, {
  sessionId: "chat_123",
  captureReasoning: true,
});
```

## Editable human feedback

Ratings and thumbs feedback can be attached to the exact generation a user
judged. A stable feedback id makes edits replace the prior value:

```typescript theme={null}
await telemetry.captureFeedback({
  feedbackId: `message:${userId}:${messageId}`,
  rating: 5,
  traceId,
  observationId: generationSpanId,
  sessionId: chatId,
  userId,
  categories: ["helpful", "accurate"],
  comment: "Solved it",
});

// Removing feedback writes a tombstone with the same stable id.
await telemetry.captureFeedback({ feedbackId, traceId, deleted: true });
```

Use `sentiment: "positive" | "negative"` instead of `rating` for thumbs
feedback. Squasher records both forms as human annotations.

## How it appears in Squasher

Agent telemetry is sent to Squasher's remote observability pipeline and is stored with your project telemetry. Use local logs or development artifacts only as temporary debugging aids; Squasher remains the system of record for production AI observability, historical search, and AI triage context.

## Good fit

* Agent backends that need per-run traces.
* Tool execution and generation visibility.
* Session-level analytics tied back to a customer, workflow, or environment.

## Agent handoff

Use this prompt when a coding agent is instrumenting an AI workflow:

```text theme={null}
Instrument this server-side AI workflow with @squasher-ai/agent. Use SQUASHER_API_KEY and SQUASHER_PROJECT_ID from environment variables, create stable sessionId and traceId values, pass traceId to tool calls and generations, enable privacyMode when prompts, model output, reasoning, or tool payloads should stay private, leave captureReasoning disabled unless retention is intentional, and flush before shutdown.
```

## Notes

* `@squasher-ai/agent` is for server-side runtimes.
* Flush before shutdown so queued telemetry is delivered.
* Completed generations count toward your plan's AI request quota.
* Free plans drop additional AI requests after the monthly included quota is exhausted.
* Paid plans continue ingesting above the included quota and meter overage usage.
* For a full OpenRouter example, see [/integrations/openrouter](/integrations/openrouter).
* If you don't want to install an SDK at all, send the same generations directly to the AI batch ingest endpoint — see [Direct HTTP ingest](/integrations/openrouter#direct-http-ingest-no-sdk) on the OpenRouter page for the worked curl example.
