Skip to content

Capabilities API

Capabilities hook into the agent lifecycle via pydantic-ai's native AbstractCapability API. They are registered through the capabilities parameter of create_deep_agent, or enabled through dedicated feature flags. See Capabilities for the conceptual overview.

SkillsCapability

pydantic_deep.capabilities.SkillsCapability dataclass

Bases: AbstractCapability[DeepAgentDeps]

Capability providing skill discovery, loading, and execution.

Wraps SkillsToolset as a pydantic-ai capability with automatic instruction injection listing available skills.

Example
Python
from pydantic_ai import Agent
from pydantic_deep.features.skills import SkillsCapability

agent = Agent("anthropic:claude-sonnet-4-6", capabilities=[SkillsCapability(
    directories=["./skills"],
)])

ContextFilesCapability

pydantic_deep.capabilities.ContextFilesCapability dataclass

Bases: AbstractCapability[DeepAgentDeps]

Capability that injects project context files into the agent's system prompt.

Loads files like AGENTS.md, SOUL.md and injects their content as instructions.

Example
Python
from pydantic_ai import Agent
from pydantic_deep.features.context import ContextFilesCapability

agent = Agent("anthropic:claude-sonnet-4-6", capabilities=[ContextFilesCapability(
    context_files=["/workspace/AGENTS.md"],
)])

MemoryCapability

pydantic_deep.capabilities.MemoryCapability dataclass

Bases: AbstractCapability[DeepAgentDeps]

Capability providing persistent agent memory across sessions.

Provides read_memory, write_memory, update_memory tools and injects existing memory into the system prompt.

Example
Python
from pydantic_ai import Agent
from pydantic_deep.features.memory import MemoryCapability

agent = Agent("anthropic:claude-sonnet-4-6", capabilities=[MemoryCapability()])

BrowserCapability

pydantic_deep.capabilities.BrowserCapability dataclass

Bases: AbstractCapability[DeepAgentDeps]

Provides a real async Playwright browser to the agent.

Manages the full browser lifecycle: Playwright and Chromium are started lazily on the first browser-tool call and closed in a finally block, guaranteeing cleanup on both success and failure paths. Runs that never invoke a browser tool incur zero Playwright overhead.

Requires the browser optional extra::

Text Only
pip install 'pydantic-deep[browser]'
playwright install chromium

Parameters:

Name Type Description Default
headless bool

Run the browser without a visible window (default True).

True
allowed_domains list[str] | None

Domain allowlist. None (default) allows all domains. Example: ["docs.python.org", "github.com"].

None
screenshot_on_navigate bool

Append a base64 screenshot to every navigate response (default False).

False
max_content_tokens int

Maximum estimated tokens for page content (default 4000).

DEFAULT_MAX_CONTENT_TOKENS
timeout_ms int

Default Playwright navigation timeout in milliseconds (default 30000).

DEFAULT_TIMEOUT_MS
auto_install bool

Automatically run playwright install chromium when the Chromium binary is missing (default True). Uses the current Python interpreter so virtualenv installs are respected.

True

Example::

Text Only
from pydantic_ai import Agent
from pydantic_deep.features.browser.capability import BrowserCapability

agent = Agent(
    "anthropic:claude-sonnet-4-6",
    capabilities=[
        BrowserCapability(
            headless=True,
            allowed_domains=["docs.python.org"],
        )
    ],
)
result = await agent.run("What's new in Python 3.13?")

prepare_tools(ctx, tool_defs) async

Filter browser tools based on availability and approval state.

  • When Chromium is not installed (launch_error is set), browser tools are hidden from the model entirely - no point offering tools that always return an error.
  • When the browser is available, any browser tool marked as unapproved is reset to function so it never triggers approval dialogs.

wrap_run(ctx, *, handler) async

Install a lazy browser launcher and clean up after the run.

Both Playwright and Chromium are started only when the first browser tool is actually called. Runs that never use the browser incur zero Playwright overhead - no subprocess is spawned, no browser window appears.

A finally block guarantees cleanup of the browser and the Playwright driver whether the run succeeds, raises, or is cancelled.

If Chromium is not installed and auto_install is True (the default), playwright install chromium is run automatically on the first tool call, and the launch is retried once.

StuckLoopDetection

pydantic_deep.capabilities.StuckLoopDetection dataclass

Bases: AbstractCapability[DeepAgentDeps]

Capability that detects and breaks repetitive agent loops.

Tracks tool calls via after_tool_execute and detects three patterns of stuck behavior. Per-run state isolation via for_run() ensures concurrent runs don't interfere.

Parameters:

Name Type Description Default
max_repeated int

Number of repetitions before triggering (default 3).

3
action Literal['warn', 'error']

What to do when stuck - "warn" raises ModelRetry so the model can self-correct, "error" raises StuckLoopError to abort the run.

'warn'
detect_repeated bool

Enable repeated identical call detection.

True
detect_alternating bool

Enable A-B-A-B pattern detection.

True
detect_noop bool

Enable no-op (same result) detection.

True
ignore_tools set[str]

Tool names exempt from all stuck-loop checks. Use for polling primitives that are intentionally called many times with identical arguments - e.g. {"inspect_branches"} when forking is enabled.

set()

for_run(ctx) async

Return a fresh instance with isolated per-run state.

after_tool_execute(ctx, *, call, tool_def, args, result) async

Track tool calls and detect stuck patterns.

pydantic_deep.features.stuck_loop.StuckLoopError

Bases: Exception

Raised when the agent is stuck in a loop and action is "error".

Attributes:

Name Type Description
pattern

The detected pattern type ("repeated", "alternating", "noop").

message

Human-readable description of the stuck loop.

PeriodicReminderCapability

pydantic_deep.capabilities.PeriodicReminderCapability dataclass

Bases: AbstractCapability[DeepAgentDeps]

Capability that periodically reminds the agent of its original task.

Uses before_model_request to increment a turn counter and inject a reminder ModelRequest into the message history every N turns. Per-run state isolation via for_run() ensures concurrent runs don't share turn counters.

Parameters:

Name Type Description Default
config PeriodicReminderConfig

:class:PeriodicReminderConfig controlling firing cadence, render style, and generator. Pass PeriodicReminderConfig() for sensible defaults.

PeriodicReminderConfig()

for_run(ctx) async

Return a fresh instance with isolated per-run counters.

before_model_request(ctx, request_context) async

Increment turn counter and inject a reminder when it fires.

HooksCapability

See the Hooks API for HooksCapability and the related hook definitions.

EvictionCapability

pydantic_deep.features.eviction.EvictionCapability dataclass

Bases: AbstractCapability[DeepAgentDeps]

Capability that intercepts large tool outputs via after_tool_execute.

The oversized result is saved to a file via the backend and replaced with a compact preview plus a file reference, so the large output never enters the message list. For ToolReturn values only return_value is size-checked; multimodal content (e.g. BinaryContent screenshots) is preserved.

Multimodal binary parts accumulating across messages are bounded by max_binary_content in before_model_request: older binaries are written to the backend and replaced with a retrievable text reference.

Parameters:

Name Type Description Default
backend AsyncBackendProtocol | None

Fallback backend for writing evicted files.

None
token_limit int

Maximum estimated tokens before eviction (default: 20K).

DEFAULT_TOKEN_LIMIT
eviction_path str

Directory in the backend for evicted files.

DEFAULT_EVICTION_PATH
head_lines int

Lines from start in preview.

DEFAULT_HEAD_LINES
tail_lines int

Lines from end in preview.

DEFAULT_TAIL_LINES
max_binary_content int | None

Maximum multimodal binary parts to keep in history. None disables binary pruning.

DEFAULT_MAX_BINARY_CONTENT
on_eviction Callable[[str, str, int, int], Any] | None

Optional callback (tool_name, file_path, original_chars, preview_chars).

None

after_tool_execute(ctx, *, call, tool_def, args, result) async

Intercept large tool results before they enter message history.

before_model_request(ctx, request_context) async

Bound the number of multimodal binary parts in message history.

Walks messages newest-to-oldest, keeps the most recent max_binary_content BinaryContent parts, and replaces older ones with a retrievable text reference. Binaries are left untouched when no backend is available or a write fails.

PatchToolCallsCapability

pydantic_deep.features.patch.PatchToolCallsCapability dataclass

Bases: AbstractCapability[Any]

Capability that fixes orphaned tool calls/results via before_model_request.

Repairs two cases before each model request:

  1. Orphaned tool calls - ToolCallPart without a matching ToolReturnPart → injects a synthetic return with "Tool call was cancelled.".
  2. Orphaned tool results - ToolReturnPart without a matching ToolCallPart → removes the orphaned return.

This replaces the patch_tool_calls_processor history processor with a capability hook that runs at the same lifecycle point but integrates with the pydantic-ai capabilities system.

before_model_request(ctx, request_context) async

Patch orphaned tool calls/results before each model request.