Created on init, completed on result — with agent name, user ID, and metadata.
Tool Calls
Every tool execution (Bash, Read, Write, etc.) with input, output, and tool use ID.
Tokens & Cost
Prompt tokens, completion tokens, total tokens, and estimated cost in USD.
Conversation Threading
Group related sessions with convo_id — multi-turn conversations appear linked.
Errors
Failed sessions and generator exceptions — all recorded with failure reasons.
Duration
Wall-clock time from start to result, plus API-side duration when available.
Both wrap_claude_agent (for query()) and wrap_claude_client (for ClaudeSDKClient) require
consuming the stream until the result message to record final tokens/cost/output. If you close the
generator early, the session is marked failed with Session interrupted (generator closed early).
Use convo_id to group related sessions into a conversation. Each call to tracked_query() creates a new session, but they appear linked in the dashboard.
convo_id = f"user-{user_id}-{uuid.uuid4().hex[:8]}"tracked_query = wrap_claude_agent( query, client=sentrial, default_agent="code-assistant", user_id=user_id, convo_id=convo_id,)options = {"max_turns": 10, "permission_mode": "bypassPermissions"}# Session 1: Explore the codebaseasync for msg in tracked_query( prompt="List all API routes and their handlers", options=options,): pass# Session 2: Follow-up (same convo_id, linked in dashboard)async for msg in tracked_query( prompt="Add rate limiting to the /api/users endpoint", options=options,): pass
If you use ClaudeSDKClient for multi-turn conversations (instead of query()), use wrap_claude_client:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptionsfrom sentrial import AsyncSentrialClientfrom sentrial.claude_code import wrap_claude_clientsentrial = AsyncSentrialClient(api_key="sentrial_live_xxx")options = ClaudeAgentOptions( permission_mode="bypassPermissions", allowed_tools=["Bash", "Read", "Write"],)sdk_client = ClaudeSDKClient(options=options)# Wrap the client — all receive_response() calls are trackedtracked = wrap_claude_client( sdk_client, sentrial_client=sentrial, default_agent="code-assistant", user_id="user-123", convo_id="convo-abc",)async with tracked: # Turn 1 await tracked.query("List all API routes") async for msg in tracked.receive_response(): print(msg) # Turn 2 — new Sentrial session, same convo_id await tracked.query("Add rate limiting to /api/users") async for msg in tracked.receive_response(): print(msg)await sentrial.close()
Each query() + receive_response() cycle creates a separate Sentrial session. All sessions share the same convo_id so they appear linked in the dashboard.
The wrapper observes the message stream to detect tool calls. When an AssistantMessage contains a ToolUseBlock, the wrapper captures the tool name and input. When the following UserMessage arrives with tool results, it matches them and records the tool call event. This approach is reliable across all SDK versions.
# No extra code needed — tool calls are tracked automaticallyoptions = {"max_turns": 5, "allowed_tools": ["Bash", "Read", "Glob"]}async for message in tracked_query( prompt="Read package.json and tell me the project name", options=options,): # Each tool call (Read, Bash, etc.) is recorded as an event # with input args, output, and execution metadata pass
In the dashboard, each tool call appears as an event under the session timeline.
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: async for message in tracked_query(prompt="Deploy to production"): passexcept Exception as error: # Session already marked as failed in Sentrial # with failure_reason = str(error) print(f"Agent failed: {error}")