Documentation
Claude Code (TypeScript)
Wrap the Claude Agent SDK's query() to automatically track sessions, tool calls, tokens, costs, and conversation threading.
What Gets Tracked Automatically
Created on init, completed on result — with agent name, user ID, and metadata.
Every tool execution (Bash, Read, Write, etc.) with input, output, and tool use ID.
Prompt tokens, completion tokens, total tokens, and estimated cost in USD.
Group related sessions with convoId — multi-turn conversations appear linked.
Failed tool calls, errored sessions, and generator exceptions — all recorded.
Wall-clock time from start to result, plus API-side duration when available.
Installation
npm install @guardy/sdk @anthropic-ai/claude-agent-sdkQuick Start
Wrap the query() function once, then use it exactly like normal.
import { query } from '@anthropic-ai/claude-agent-sdk';
import { GuardyClient, wrapClaudeAgent } from '@guardy/sdk';
// 1. Create a Guardy client
const guardy = new GuardyClient({
apiKey: process.env.GUARDY_API_KEY,
});
// 2. Wrap query()
const trackedQuery = wrapClaudeAgent(query, {
client: guardy,
defaultAgent: 'my-agent',
userId: 'user-123',
});
// 3. Use normally — everything is tracked
for await (const message of trackedQuery({
prompt: 'Fix the failing tests in src/auth.ts',
options: {
maxTurns: 10,
permissionMode: 'bypassPermissions',
},
})) {
if (message.type === 'assistant') {
// Handle assistant messages
}
}
// Session auto-completed with tokens, cost, duration, and all tool callsConfiguration Options
const trackedQuery = wrapClaudeAgent(query, {
client: guardy, // required — GuardyClient instance
defaultAgent: 'my-agent', // optional — agent name for grouping (default: 'claude-agent')
userId: 'user-123', // optional — ties sessions to your end users (default: 'anonymous')
convoId: 'convo-abc', // optional — group sessions into a conversation thread
extraMetadata: { // optional — merged into every session's metadata
environment: 'production',
version: '1.2.0',
},
});Conversation Threading
Use convoId to group related sessions into a conversation. Each call to trackedQuery() creates a new session, but they appear linked in the dashboard.
const convoId = `user-${userId}-${Date.now()}`;
const trackedQuery = wrapClaudeAgent(query, {
client: guardy,
defaultAgent: 'code-assistant',
userId,
convoId,
});
// Session 1: Explore the codebase
for await (const msg of trackedQuery({
prompt: 'List all API routes and their handlers',
})) { /* ... */ }
// Session 2: Follow-up (same convoId, linked in dashboard)
for await (const msg of trackedQuery({
prompt: 'Add rate limiting to the /api/users endpoint',
})) { /* ... */ }How Tool Tracking Works
The wrapper uses PostToolUse and PostToolUseFailure hooks to capture every tool execution. Hooks fire asynchronously and never block the agent. A session-ready gate ensures hooks wait for session creation before tracking.
// No extra code needed — tool calls are tracked automatically
for await (const message of trackedQuery({
prompt: 'Read package.json and tell me the project name',
options: {
allowedTools: ['Bash', 'Read', 'Glob'],
},
})) {
// Each tool call (Read, Bash, etc.) is recorded as an event
// with input args, output, and execution metadata
}In the dashboard, each tool call appears as an event under the session timeline.
Error Handling
If the agent errors, the generator throws, or the consumer closes the stream early, the session is automatically marked as failed with a clear failure reason.
try {
for await (const message of trackedQuery({
prompt: 'Deploy to production',
})) {
// ...
}
} catch (error) {
// Session already marked as failed in Guardy
// with failure_reason = error.message
console.error(error);
}Full Production Example
import { query } from '@anthropic-ai/claude-agent-sdk';
import { GuardyClient, wrapClaudeAgent } from '@guardy/sdk';
const guardy = new GuardyClient({
apiKey: process.env.GUARDY_API_KEY!,
});
async function runAgent(userPrompt: string, userId: string) {
const convoId = `session-${userId}-${Date.now()}`;
const trackedQuery = wrapClaudeAgent(query, {
client: guardy,
defaultAgent: 'code-review-agent',
userId,
convoId,
extraMetadata: {
environment: process.env.NODE_ENV,
},
});
let result = '';
for await (const message of trackedQuery({
prompt: userPrompt,
options: {
model: 'claude-sonnet-4-20250514',
maxTurns: 15,
permissionMode: 'bypassPermissions',
allowedTools: ['Bash', 'Read', 'Write', 'Glob', 'Grep'],
},
})) {
if (message.type === 'result') {
result = message.result ?? '';
}
}
return result;
}What You See in the Dashboard
Agent name, user ID, conversation thread, status, duration, cost.
The prompt and Claude's final response.
Every Bash, Read, Write, Glob call — with input, output, and tool use ID.
Prompt tokens, completion tokens, total, estimated USD cost.