Skip to content

Data models

The structured types produced and consumed by caw. See Concepts for how they fit together.

Trajectory

Trajectory dataclass

Trajectory(agent: str, model: str = '', session_id: str = '', created_at: str = '', completed_at: str = '', usage_limited: bool = False, system_prompt: str = '', reasoning: str = '', mcp_servers: list[MCPServer] = list(), turns: list[Turn] = list(), usage: UsageStats = UsageStats(), duration_ms: int = 0, metadata: dict[str, Any] = dict(), usage_in_parent: bool = False)

Complete record of a session.

usage is what this session was billed for. Use total_usage to add the nested subagent trajectories that were billed separately — see usage_in_parent for the ones that were not.

result property

result: str

The final result from the last turn.

failure_reason property

failure_reason: str | None

Why the session's last turn failed, or None if it didn't.

The question a caller recording an outcome has: not "did anything ever go wrong" but "did this run end on a failure". A mid-run turn that failed and was retried never reaches the trajectory, and a usage-limited turn that the auto-wait loop resumed past is followed by the clean turn that replaced it — so both correctly read None here.

ended_on_failure property

ended_on_failure: bool

Whether the last turn failed. See failure_reason.

failed_turns property

failed_turns: list[Turn]

Every turn the CLI reported as failed, in order.

For auditing a whole run; ended_on_failure is the outcome question.

usage_limits property

usage_limits: list[UsageLimit]

Every usage limit this run hit, in order.

A list and not a flag, because a long run may hit one more than once and the interesting number is how much of its wall-clock went on waiting. usage_limited stays the separate question of whether it ended against one.

total_usage property

total_usage: UsageStats

Accumulated usage: own + every separately-billed nested subagent.

A subagent flagged usage_in_parent is skipped whole (its own nested agents with it) because self.usage already covers it.

subagent_trajectories property

subagent_trajectories: list[Trajectory]

All subagent trajectories across all turns.

is_usage_limited property

is_usage_limited: bool

Whether the session ended due to a usage limit.

Set by Session.end() using the provider's detect_usage_limit.

is_complete property

is_complete: bool

Whether the session completed normally.

A trajectory is complete when it has been finalized (completed_at is set by Session.end()) and was not usage-limited. Mid-session snapshots written by append_turn have an empty completed_at and are therefore not considered complete.

Turn

Turn dataclass

Turn(input: str, output: list[ContentBlock] = list(), usage: UsageStats = UsageStats(), duration_ms: int = 0, failure_reason: str | None = None, usage_limit: UsageLimit | None = None)

A single turn: user sends a message, agent responds.

result property

result: str

Last text block's content.

failed property

failed: bool

Whether the CLI reported this turn as failed. See failure_reason.

tool_calls property

tool_calls: list[ToolUse]

All tool calls made during this turn.

Content blocks

TextBlock dataclass

TextBlock(text: str)

A block of text output from the agent.

ThinkingBlock dataclass

ThinkingBlock(text: str)

A block of thinking/reasoning output from the agent.

ToolUse dataclass

ToolUse(id: str, name: str, arguments: dict[str, Any] = dict(), output: str = '', is_error: bool = False, subagent_trajectory: Trajectory | None = None)

A tool invocation paired with its result.

ContentBlock module-attribute

ContentBlock = Union[TextBlock, ThinkingBlock, ToolUse]

UsageStats

UsageStats dataclass

UsageStats(input_tokens: int = 0, output_tokens: int = 0, cache_read_tokens: int = 0, cache_write_tokens: int = 0, cost_usd: float = 0.0)

Token usage and cost statistics.

total_tokens property

total_tokens: int

Total tokens consumed (input + output).

Model & tool selection

ModelTier

Bases: Enum

Abstract model selection tiers.

Each provider maps these to concrete model identifiers:

agent = Agent(model=ModelTier.STRONGEST)  # provider picks its best model
agent = Agent(model=ModelTier.FAST)        # provider picks its fast model
agent = Agent(model="claude-opus-4-6")     # explicit model string still works

ToolGroup

Bases: Flag

Abstract tool permission groups.

Combine with | (union) and - (subtract) to build permission sets:

ToolGroup.READER | ToolGroup.EXEC          # read + execute only
ToolGroup.ALL - ToolGroup.WRITER            # everything except writes
ToolGroup.ALL - ToolGroup.INTERACTION       # default for automated pipelines

Configuration types

AgentSpec dataclass

AgentSpec(name: str = '', description: str = '', system_prompt: str = '', model: str = '', reasoning: str = '', tools: ToolGroup | None = None, tool_servers: list[Any] = list(), mcp_servers: list[MCPServer] = list(), subagents: list['AgentSpec'] = list(), metadata: dict[str, Any] = dict())

Configuration for a subagent.

MCPServer dataclass

MCPServer(name: str, command: str = '', args: list[str] = list(), env: dict[str, str] = dict(), url: str = '')

Configuration for an MCP server.

For stdio transport, set command/args/env. For HTTP transport, set url (command/args/env are ignored).

MCPTool dataclass

MCPTool(name: str, description: str = '', server: str = '', input_schema: dict[str, Any] = dict())

Descriptor for a tool provided by an MCP server.

InteractiveResult dataclass

InteractiveResult(exit_code: int, output: str = '')

Result from an interactive agent session.

session_id property

session_id: str | None

Extract the session ID from Claude Code's exit output, if present.