Providers¶
The provider abstraction and the three built-in backends. See the Providers guide and Auto-provider mode for usage.
Abstract interface¶
Provider
¶
Bases: ABC
ABC that each coding agent backend implements.
binary_name
property
¶
Name of the backing CLI executable (as looked up on PATH).
Defaults to the provider name; override when the CLI binary is
named differently (e.g. claude_code → claude). Used by the
default find_binary.
resolve_model
¶
resolve_model(tier: ModelTier) -> str | None
Translate a ModelTier into a concrete model identifier, if needed.
Each provider must override this to map abstract tiers (e.g.
ModelTier.STRONGEST) to the actual model string it supports. Return
None when the provider's own default/config should select the tier.
Source code in caw/provider.py
resolve_tool_restrictions
¶
resolve_tool_restrictions(tools: ToolGroup) -> dict[str, Any]
Translate ToolGroup into provider-specific session kwargs.
Receives a concrete ToolGroup value (never None — the Agent layer applies the default before calling this).
Source code in caw/provider.py
check_limit
¶
Probe whether the provider's usage limit is currently active.
Sends a minimal test prompt and checks if the response indicates a
usage-limit. Returns the estimated number of minutes to wait before
the limit resets, or None if no limit is detected.
This incurs a small token cost for the probe request.
Source code in caw/provider.py
find_binary
¶
Resolve the CLI executable's path, or None if not installed.
Default looks up binary_name on PATH. Override to add
provider-specific fallback locations.
Source code in caw/provider.py
check_auth
¶
Best-effort, non-authoritative introspection of credentials.
Returns an AuthSignal, or None when the
provider cannot introspect its credentials at all. Override in
subclasses; the default returns None (unknown).
Source code in caw/provider.py
check_health
¶
Report raw health signals for this provider.
Fast by default: checks whether the CLI is installed and introspects
credentials. When live is True, additionally runs the
check_limit probe (one request) to confirm the provider
responds and whether it is currently rate-limited.
Forms no verdict on "availability" — see caw.health.ProviderHealth.
Source code in caw/provider.py
start_interactive
¶
start_interactive(initial_prompt: str, mcp_servers: list[MCPServer], capture_bytes: int = 0, **kwargs: Any) -> InteractiveResult
Launch the provider binary interactively with an initial prompt.
Hands control to the user's terminal — stdin/stdout/stderr are inherited so the user interacts with the agent directly. A copy of stdout is captured via a pty.
Returns an InteractiveResult with the exit code and
captured output.
Source code in caw/provider.py
start_session
abstractmethod
¶
start_session(mcp_servers: list[MCPServer], **kwargs: Any) -> ProviderSession
resume_key_from_trajectory
¶
resume_key_from_trajectory(trajectory: Trajectory) -> str | None
Extract the resume key from a persisted trajectory.
Mirrors ProviderSession.resume_key but reads from a stored
trajectory rather than a live session. Returns None if the
trajectory predates resume support or was never sent to.
Source code in caw/provider.py
resume_session
¶
resume_session(mcp_servers: list[MCPServer], *, session_id: str, resume_key: str, trajectory: Trajectory | None = None, **kwargs: Any) -> ProviderSession
Rebuild a live session ready to continue an existing conversation.
resume_key is the provider-side key the backend CLI needs to resume
(see ProviderSession.resume_key); session_id is caw's own
bookkeeping id (the on-disk directory name). The next
ProviderSession.send must resume rather than start fresh.
When trajectory is given, the prior history/usage is carried forward
via ProviderSession._restore_from_trajectory. When it is
None (e.g. resuming without a data_dir), the backend session is
still resumed but caw's trajectory starts empty.
Source code in caw/provider.py
ProviderSession
¶
Bases: ABC
ABC that each provider implements to manage a live session.
resume_key
property
¶
The provider-side key needed to resume this conversation.
This is whatever the backend CLI accepts to continue an existing
session (claude's session id, codex's thread id, opencode's session
id). None until the backend has assigned one — typically after the
first send.
last_raw_output
property
¶
Raw CLI stdout from the most recent send() call (if available).
end
abstractmethod
¶
end() -> Trajectory
detect_usage_limit
¶
detect_usage_limit(turn: Turn) -> int | None
Check whether turn indicates the provider's usage limit was hit.
Returns the number of minutes to wait before retrying, or None
if no limit was detected. Override in provider subclasses to
implement provider-specific detection logic.
Source code in caw/provider.py
set_step_callback
¶
set_logger
¶
set_logger(logger: AgentLogger | None) -> None
Built-in providers¶
CodexProvider
¶
Bases: Provider
Provider that delegates to the codex CLI.
start_interactive
¶
start_interactive(initial_prompt: str, mcp_servers: list[MCPServer], capture_bytes: int = 0, **kwargs: Any) -> InteractiveResult
Launch codex interactively (TUI) with an initial prompt.
Invoking the bare codex binary with a positional prompt starts its
full-screen interactive session. We run it through a pty so the user
drives the TUI directly while a copy of stdout is captured.
The sandbox mapping mirrors the headless exec path (see
CodexSession.send): an explicit restrictive sandbox is passed
through as --sandbox; otherwise codex runs with approvals and the
sandbox bypassed (--dangerously-bypass-approvals-and-sandbox).
Source code in caw/providers/codex.py
OpencodeProvider
¶
Bases: Provider
Provider that delegates to the opencode CLI.
start_interactive
¶
Launch opencode interactively (TUI) with an initial prompt.
opencode's interactive mode uses a full split-footer TUI. We launch it through a pty so stdin/stdout/stderr are inherited and a copy of stdout is captured.
Source code in caw/providers/opencode.py
Registry & fallback order¶
set_provider_order
¶
set_provider_order(order: list[str | tuple[str, str | ModelTier]] | None, *, models: dict[str, str | ModelTier] | None = None) -> None
Set the global provider fallback order, optionally with per-provider models.
Pass provider names/aliases in priority order, e.g.
set_provider_order(["claude", "codex", "opencode"]). Agents created
with provider="auto" (or with no provider and no CAW_PROVIDER)
will select the first installed provider in this order and gracefully
fall back to the next on a first-send failure. Pass None to clear it.
To also pin a model per provider, give (name, model) tuples and/or a
models mapping; both accept a concrete model string or a ModelTier::
set_provider_order([("claude", ModelTier.STRONGEST), ("codex", "gpt-5.5")])
set_provider_order(["claude", "codex"], models={"claude": "opus"})
A provider's order-model is applied only when the Agent itself sets no
model — an explicit model= (or CAW_MODEL) on the Agent wins.
Because the model is attached to a specific provider, it is honored even
when that provider is reached as a fallback (unlike a bare Agent-level model
string, which is provider-specific and dropped on fallback).
An explicit provider= argument on an Agent always overrides this order.