Bases: AbstractCapability[Any]
Capability providing filesystem tools with permission enforcement.
Bundles the console toolset (ls, read_file, write_file, edit_file, glob,
grep, execute) with dynamic instructions and per-tool permission control.
When a permission ruleset is provided:
- Tools for denied operations are hidden from the model entirely
- Per-path/command permissions are checked before each tool execution
- "ask" permissions can trigger an approval callback
Example
Pythonfrom pydantic_ai import Agent
from pydantic_ai_backends import ConsoleCapability
from pydantic_ai_backends.permissions import READONLY_RULESET
# Read-only agent — write/edit/execute tools are hidden
agent = Agent(
"openai:gpt-4.1",
capabilities=[ConsoleCapability(permissions=READONLY_RULESET)],
)
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| @dataclass
class ConsoleCapability(AbstractCapability[Any]):
"""Capability providing filesystem tools with permission enforcement.
Bundles the console toolset (ls, read_file, write_file, edit_file, glob,
grep, execute) with dynamic instructions and per-tool permission control.
When a permission ruleset is provided:
- Tools for denied operations are hidden from the model entirely
- Per-path/command permissions are checked before each tool execution
- "ask" permissions can trigger an approval callback
Example:
```python
from pydantic_ai import Agent
from pydantic_ai_backends import ConsoleCapability
from pydantic_ai_backends.permissions import READONLY_RULESET
# Read-only agent — write/edit/execute tools are hidden
agent = Agent(
"openai:gpt-4.1",
capabilities=[ConsoleCapability(permissions=READONLY_RULESET)],
)
```
"""
include_execute: bool = True
"""Whether to include the execute tool."""
edit_format: EditFormat = "str_replace"
"""Edit format: 'str_replace' or 'hashline'."""
permissions: PermissionRuleset | None = None
"""Permission ruleset for controlling tool access."""
_toolset: AbstractToolset[Any] | None = field(default=None, init=False, repr=False)
_checker: PermissionChecker | None = field(default=None, init=False, repr=False)
def __post_init__(self) -> None:
"""Create the underlying console toolset and permission checker."""
self._toolset = create_console_toolset(
include_execute=self.include_execute,
)
if self.permissions is not None:
self._checker = PermissionChecker(ruleset=self.permissions)
@classmethod
def get_serialization_name(cls) -> str:
"""Return name for AgentSpec YAML/JSON serialization."""
return "ConsoleCapability"
def get_toolset(self) -> AbstractToolset[Any] | None:
"""Return the console toolset."""
return self._toolset
def get_instructions(self) -> str:
"""Return console tool usage instructions."""
return get_console_system_prompt(edit_format=self.edit_format)
async def prepare_tools(
self,
ctx: RunContext[Any],
tool_defs: list[ToolDefinition],
) -> list[ToolDefinition]:
"""Hide tools for denied operations."""
if self._checker is None:
return tool_defs
result = []
for td in tool_defs:
operation = _TOOL_OPERATION_MAP.get(td.name)
if operation is None:
# Not a console tool — pass through
result.append(td)
continue
action = self._checker.check_sync(operation, "*")
if action != "deny":
result.append(td)
return result
async def before_tool_execute(
self,
ctx: RunContext[Any],
*,
call: ToolCallPart,
tool_def: ToolDefinition,
args: dict[str, Any],
) -> dict[str, Any]:
"""Check per-path permissions before tool execution."""
if self._checker is None:
return args
operation = _TOOL_OPERATION_MAP.get(call.tool_name)
if operation is None:
return args
# Determine target to check
if operation in _PATH_OPERATIONS:
target = args.get("path", args.get("file_path", "*"))
elif operation in _COMMAND_OPERATIONS:
target = args.get("command", "*")
else:
return args
# Check permission — raises PermissionDeniedError on deny
await self._checker.check(operation, str(target))
return args
|
include_execute = True
class-attribute
instance-attribute
Whether to include the execute tool.
Edit format: 'str_replace' or 'hashline'.
permissions = None
class-attribute
instance-attribute
Permission ruleset for controlling tool access.
__post_init__()
Create the underlying console toolset and permission checker.
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| def __post_init__(self) -> None:
"""Create the underlying console toolset and permission checker."""
self._toolset = create_console_toolset(
include_execute=self.include_execute,
)
if self.permissions is not None:
self._checker = PermissionChecker(ruleset=self.permissions)
|
get_serialization_name()
classmethod
Return name for AgentSpec YAML/JSON serialization.
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| @classmethod
def get_serialization_name(cls) -> str:
"""Return name for AgentSpec YAML/JSON serialization."""
return "ConsoleCapability"
|
Return the console toolset.
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| def get_toolset(self) -> AbstractToolset[Any] | None:
"""Return the console toolset."""
return self._toolset
|
get_instructions()
Return console tool usage instructions.
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| def get_instructions(self) -> str:
"""Return console tool usage instructions."""
return get_console_system_prompt(edit_format=self.edit_format)
|
Hide tools for denied operations.
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| async def prepare_tools(
self,
ctx: RunContext[Any],
tool_defs: list[ToolDefinition],
) -> list[ToolDefinition]:
"""Hide tools for denied operations."""
if self._checker is None:
return tool_defs
result = []
for td in tool_defs:
operation = _TOOL_OPERATION_MAP.get(td.name)
if operation is None:
# Not a console tool — pass through
result.append(td)
continue
action = self._checker.check_sync(operation, "*")
if action != "deny":
result.append(td)
return result
|
Check per-path permissions before tool execution.
Source code in src/pydantic_ai_backends/capability.py
| Python |
|---|
| async def before_tool_execute(
self,
ctx: RunContext[Any],
*,
call: ToolCallPart,
tool_def: ToolDefinition,
args: dict[str, Any],
) -> dict[str, Any]:
"""Check per-path permissions before tool execution."""
if self._checker is None:
return args
operation = _TOOL_OPERATION_MAP.get(call.tool_name)
if operation is None:
return args
# Determine target to check
if operation in _PATH_OPERATIONS:
target = args.get("path", args.get("file_path", "*"))
elif operation in _COMMAND_OPERATIONS:
target = args.get("command", "*")
else:
return args
# Check permission — raises PermissionDeniedError on deny
await self._checker.check(operation, str(target))
return args
|