Confirmation Policy
A ready-to-run example is available here!Confirmation policy controls whether actions require user approval before execution. They provide a simple way to ensure safe agent operation by requiring explicit permission for actions.
Setting Confirmation Policy
Set the confirmation policy on your conversation:AlwaysConfirm()- Require approval for all actionsNeverConfirm()- Execute all actions without approvalConfirmRisky()- Only require approval for risky actions (requires security analyzer)
Custom Confirmation Handler
Implement your approval logic by checking conversation status:Rejecting Actions
Provide feedback when rejecting to help the agent try a different approach:Ready-to-run Example Confirmation
Full confirmation example: examples/01_standalone_sdk/04_confirmation_mode_example.py
examples/01_standalone_sdk/04_confirmation_mode_example.py
The model name should follow the LiteLLM convention:
provider/model_name (e.g., anthropic/claude-sonnet-4-5-20250929, openai/gpt-4o).
The LLM_API_KEY should be the API key for your chosen provider.Security Analyzer
Security analyzer evaluates the risk of agent actions before execution, helping protect against potentially dangerous operations. They analyze each action and assign a security risk level:- LOW - Safe operations with minimal security impact
- MEDIUM - Moderate security impact, review recommended
- HIGH - Significant security impact, requires confirmation
- UNKNOWN - Risk level could not be determined
ConfirmRisky()) to determine whether user approval is needed before executing an action. This provides an additional layer of safety for autonomous agent operations.
LLM Security Analyzer
A ready-to-run example is available here!The LLMSecurityAnalyzer is the default implementation provided in the agent-sdk. It leverages the LLM’s understanding of action context to provide lightweight security analysis. The LLM can annotate actions with security risk levels during generation, which the analyzer then uses to make security decisions.
Security Analyzer Configuration
Create an LLM-based security analyzer to review actions before execution:- Reviews each action before execution
- Flags potentially dangerous operations
- Can be configured with custom security policy
- Uses a separate LLM to avoid conflicts with the main agent
Ready-to-run Example Security Analyzer
Full security analyzer example: examples/01_standalone_sdk/16_llm_security_analyzer.py
examples/01_standalone_sdk/16_llm_security_analyzer.py
The model name should follow the LiteLLM convention:
provider/model_name (e.g., anthropic/claude-sonnet-4-5-20250929, openai/gpt-4o).
The LLM_API_KEY should be the API key for your chosen provider.Custom Security Analyzer Implementation
You can extend the security analyzer functionality by creating your own implementation that inherits from the SecurityAnalyzerBase class. This allows you to implement custom security logic tailored to your specific requirements.Creating a Custom Analyzer
To create a custom security analyzer, inherit fromSecurityAnalyzerBase and implement the security_risk() method:
Defense-in-Depth Security Analyzer
The problem
Your agent is about to run a tool call. Is it safe? TheLLMSecurityAnalyzer asks the model itself — but the model can be
manipulated, and encoding tricks can hide dangerous commands from it.
You need a layer that does not depend on model judgment: something
deterministic, local, and fast.
What this gives you
Three composable analyzers that classify actions at the boundary — before the tool runs, not after. No network calls, no model inference, no extra dependencies. They return aSecurityRisk level; your
ConfirmRisky policy decides whether to prompt the user.
Quick start
You must configure both the analyzer and the confirmation policy. Setting an analyzer does not automatically change confirmation behavior.confirm_unknown=True).
For security-sensitive environments, lower the threshold to catch more:
Adding the LLM analyzer for deeper coverage
The pattern analyzer catches known threats instantly. The LLM analyzer can catch novel or ambiguous cases. Composing both gives you speed and breadth:Why it works this way
Two corpora, not one. An agent that runsls /tmp but thinks
“I should avoid rm -rf /” is not flagged — shell patterns only see
the ls /tmp that will actually execute. Injection patterns like
“ignore all previous instructions” scan everything, because they
target the model’s instruction-following regardless of where they
appear.
Max-severity, not averaging. The analyzers scan the same input —
they are correlated, not independent. The highest concrete risk wins.
That is simpler and more auditable than probabilistic fusion.
UNKNOWN means “I don’t know,” not “safe.” By default, if all
analyzers return UNKNOWN the ensemble preserves it, and ConfirmRisky
triggers confirmation. If any analyzer returns a concrete level,
UNKNOWN results are filtered out. For stricter environments, set
propagate_unknown=True so that any single UNKNOWN triggers
confirmation regardless of other results.
Confirm, don’t block. The analyzers return a risk level. The
confirmation policy decides what happens. The analyzer does not
prevent execution — it classifies risk for the policy layer to act on.
Pair with Docker isolation for stronger safety guarantees.
What this does not do
This is a deterministic action-boundary control. It is not:- A complete prompt-injection solution
- A full shell parser or AST interpreter
- A sandbox replacement
- A guarantee against novel threats the patterns do not cover
LLMSecurityAnalyzer and GraySwanAnalyzer, not a
replacement for either.
Known limitations
Extraction budget and primary-surface-first ordering
The 30k-character cap is applied per scanning corpus, not per field: every field competes for one shared budget (the_BoundedSegments buffer in
defense_in_depth/utils.py). That creates a secondary risk — a single
oversized field could consume the whole budget and leave higher-value
fields unscanned. tool_name has no length validation in the SDK, so a 30k
hallucinated name is a real starvation vector, not just a theoretical one.
The analyzer addresses this by extraction order, not a per-field cap:
the primary attack surface is added first, so it always receives budget
even when a later field is adversarially large.
- Executable corpus:
tool_call.arguments(the primary prompt-injection surface) →tool_name→tool_call.name. - Reasoning corpus:
summary(what the agent is about to do) →reasoning_content→thought.
arguments payload cannot
crowd summary out of the injection scan.
Remaining boundary (a strict xfail in the test suite): a payload past
30k characters within a single field is still truncated and invisible.
That is the deliberate ReDoS trade-off already listed above; extraction
order does not change it.
Ready-to-run example: examples/01_standalone_sdk/47_defense_in_depth_security.py
Configurable Security Policy
A ready-to-run example is available here!Agents use security policies to guide their risk assessment of actions. The SDK provides a default security policy template, but you can customize it to match your specific security requirements and guidelines.
Using Custom Security Policies
You can provide a custom security policy template when creating an agent:- Define organization-specific risk assessment guidelines
- Set custom thresholds for security risk levels
- Add domain-specific security rules
- Tailor risk evaluation to your use case
Ready-to-run Example Security Policy
Full configurable security policy example: examples/01_standalone_sdk/32_configurable_security_policy.py
examples/01_standalone_sdk/32_configurable_security_policy.py
The model name should follow the LiteLLM convention:
provider/model_name (e.g., anthropic/claude-sonnet-4-5-20250929, openai/gpt-4o).
The LLM_API_KEY should be the API key for your chosen provider.Next Steps
- Custom Tools - Build secure custom tools
- Custom Secrets - Secure credential management

