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[Any]

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.capabilities.skills import SkillsCapability

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

ContextFilesCapability

pydantic_deep.capabilities.ContextFilesCapability dataclass

Bases: AbstractCapability[Any]

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.capabilities.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[Any]

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.capabilities.memory import MemoryCapability

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

TeamCapability

pydantic_deep.capabilities.TeamCapability dataclass

Bases: AbstractCapability[Any]

Capability for multi-agent team coordination.

When registry and task_fn are provided, team members are registered as subagents and tasks execute via the subagent engine.

Example
Python
from pydantic_ai import Agent
from pydantic_deep.capabilities.teams import TeamCapability

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

PlanCapability

pydantic_deep.capabilities.PlanCapability dataclass

Bases: AbstractCapability[Any]

Capability providing interactive planning tools.

Provides ask_user and save_plan tools for structured planning workflow.

Example
Python
from pydantic_ai import Agent
from pydantic_deep.capabilities.plan import PlanCapability

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

BrowserCapability

pydantic_deep.capabilities.BrowserCapability dataclass

Bases: AbstractCapability[Any]

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.capabilities.browser 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[Any]

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 str

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.capabilities.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[Any]

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.processors.eviction.EvictionCapability dataclass

Bases: AbstractCapability[Any]

Capability that intercepts large tool outputs via after_tool_execute.

Unlike :class:EvictionProcessor (a history processor that runs after the result is already in message history), this capability intercepts the tool result before it enters the conversation - so the large output never bloats the message list.

The evicted content is saved to a file via the backend, and the tool result is replaced with a compact preview + file reference.

For tool results that are :class:pydantic_ai.messages.ToolReturn values, only the return_value is considered for size-based eviction; multimodal content (such as :class:BinaryContent screenshots) is preserved.

Multimodal binary parts that accumulate across messages are bounded by max_binary_content via :meth:before_model_request. Older binaries are written to the backend and replaced with a compact retrievable text reference, so the agent can still re-read them via read_file.

Parameters:

Name Type Description Default
backend BackendProtocol | 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. Older binaries are persisted to the backend and replaced with a text reference. 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.

ToolReturn results are handled specially: only return_value is considered for size-based text eviction so multimodal content (e.g. :class:BinaryContent screenshots) is never collapsed into a string.

before_model_request(ctx, request_context) async

Bound the number of multimodal binary parts in message history.

Walks request_context.messages newest-to-oldest and keeps the most recent max_binary_content :class:BinaryContent parts. Older binaries are written to the runtime backend and replaced with a compact text reference so the agent can still retrieve them via read_file. Binaries are left untouched when no backend is available or when a write fails, to avoid losing data.

PatchToolCallsCapability

pydantic_deep.processors.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.