dogwood.integrations.strands#
Strands Agents integration for Dogwood policy enforcement.
The integration follows the Strands extension model:
DogwoodInterventionimplements typed intervention decisions for before-tool-call authorization.DogwoodPluginsubclassesstrands.plugins.Pluginand uses a decorated hook for automatic plugin registration.lifecycle_hook()exposes lifecycle-aware hooks directly.
All three surfaces use a persistent Dogwood native authorizer so policy parsing and lowering happen once per integration object.
- class dogwood.integrations.strands.DogwoodIntervention[source]#
Strands intervention handler backed by Dogwood authorization.
Interventions are the preferred integration point for agent control flows because they return typed Strands decisions instead of mutating hook events directly.
DogwoodInterventioncan evaluate Dogwood policies across the primary Strands lifecycle methods:before_invocationbefore_model_callbefore_tool_callafter_tool_callafter_model_call
For
before_tool_callit returns:Proceedwhen Dogwood allows the tool call.Denywhen Dogwood denies the tool call.Confirmwhen Dogwood denies andconfirm_whenrequests human approval.
Without Strands installed, the class returns small local stand-ins so the mapping behavior remains testable.
Constructor inputs:
policy_sourceandpolicy_schema_sourcebuild a persistent native Dogwood authorizer.event_schema_sourcesupplies an explicit Dogwood.dwschema.authorizerreuses an existingdogwood.native.NativeAuthorizerinstead of building one.principal,resource, andinput_mappercustomize how Strands events are mapped into Dogwood authorization requests.actionmay be a fixed Cedar action string or a callback that resolves the action from a Strands event, which supports one action per tool.modeis"enforce"by default."log_only"records the Dogwood decision on the event and proceeds without blocking.confirm_whenturns Dogwood denials into StrandsConfirmactions for selected tool calls.lifecycle_eventsselects which lifecycle methods invoke Dogwood. The default is("before_tool_call",)for backward compatibility. Use"all"to evaluate every supported intervention lifecycle.
- name = 'dogwood-policy'#
- before_invocation(event, **kwargs)[source]#
Authorize the start of an agent invocation.
- Parameters:
event (Any)
kwargs (Any)
- Return type:
Any
- before_model_call(event, **kwargs)[source]#
Authorize a model call before the request is sent.
- Parameters:
event (Any)
kwargs (Any)
- Return type:
Any
- before_tool_call(event, **kwargs)[source]#
Authorize a Strands tool call and return a typed control decision.
This method is called by Strands for
BeforeToolCallEvent. Dogwood receives the selected tool name, tool input, and tool-use identifier via the configuredinput_mapper.- Parameters:
event (Any)
kwargs (Any)
- Return type:
Any
- after_tool_call(event, **kwargs)[source]#
Observe a completed tool call and continue.
Strands after-tool-call interventions support
ProceedandTransform. Dogwood denials are therefore observational here; hard enforcement belongs inbefore_tool_call.- Parameters:
event (Any)
kwargs (Any)
- Return type:
Any
- class dogwood.integrations.strands.DogwoodPlugin[source]#
Strands plugin for lifecycle-wide Dogwood policy enforcement.
DogwoodPluginfollows the Strands plugin pattern: it subclassesstrands.plugins.Pluginand marks lifecycle methods with@hookso Strands can discover and register them automatically.Before-events can be cancelled when Dogwood denies. After-events are observed by Dogwood and continue. Use
DogwoodInterventionwhen you want Strands’ typedProceed/Deny/Confirm/Guide/Transformcontrol flow instead.Constructor inputs mirror
DogwoodIntervention, except plugins use hook mutation semantics and do not return typed intervention actions.- name = 'dogwood-policy'#
- init_agent(agent)[source]#
Store the Strands agent instance during plugin initialization.
- Parameters:
agent (Any)
- Return type:
None
- on_before_invocation(event)[source]#
Authorize the start of an agent invocation.
- Parameters:
event (Any)
- Return type:
None
- on_after_invocation(event)[source]#
Observe the end of an agent invocation.
- Parameters:
event (Any)
- Return type:
None
- on_message_added(event)[source]#
Observe messages added to Strands conversation history.
- Parameters:
event (Any)
- Return type:
None
- on_before_model_call(event)[source]#
Authorize a model call before Strands sends it.
- Parameters:
event (Any)
- Return type:
None
- on_after_model_call(event)[source]#
Observe a model response after Strands receives it.
- Parameters:
event (Any)
- Return type:
None
- class dogwood.integrations.strands.StrandsLifecyclePolicyHook[source]#
Lifecycle-aware Strands hook backed by Dogwood authorization.
StrandsLifecyclePolicyHooksupports every primary Strands agent lifecycle event. It is opt-in per lifecycle to avoid accidentally applying a tool policy to invocation or model events.Before-events can be cancelled when Dogwood denies. After-events are observed by calling Dogwood and then continue, because Strands does not support hard denial after work has already happened.
- authorizer: NativeAuthorizer#
- action: str | Callable[[Any], str] = 'Agent::Action::CallTool'#
- principal()#
Resolve the Cedar principal from Strands
invocation_state.- Parameters:
event (Any)
- Return type:
str
- resource()#
Resolve the Cedar resource from Strands
invocation_state.- Parameters:
event (Any)
- Return type:
str
- tool_input_mapper()#
Build Dogwood tool-call input from a Strands tool event.
The default input shape is framework-neutral so the same Dogwood policy can be reused by future agent integrations. It works with a generic
CallToolaction and with schemas that map each tool name to a concrete Cedar action:{"tool": name, "input": tool_input, "toolUseId": id}- Parameters:
event (Any)
- Return type:
dict[str, Any]
- lifecycle_input_mapper()#
Build Dogwood input from any supported Strands lifecycle event.
This mapper intentionally keeps the payload small and JSON-safe. It records the lifecycle name plus a few stable fields that policies commonly need. Tool-call events still use
default_tool_input()by default.- Parameters:
event (Any)
- Return type:
dict[str, Any]
- lifecycle_events: tuple[str, ...] | str = ('before_tool_call',)#
- mode: Literal['enforce', 'log_only'] = 'enforce'#
- deny_message: str = 'Dogwood policy denied this lifecycle event.'#
- class dogwood.integrations.strands.StrandsPolicyHook[source]#
Low-level Strands hook backed by a Dogwood native authorizer.
This is the lowest-level integration surface. Register it for
BeforeToolCallEventwhen you want direct hook behavior instead of a Strands plugin or intervention.If Dogwood denies the request, the hook sets
event.cancel_toolwithdeny_message. Strands then skips the selected tool.Use this class when you need direct hook registration. Otherwise prefer
DogwoodInterventionfor typed decisions orDogwoodPluginfor Strands plugin auto-discovery.- authorizer: NativeAuthorizer#
- action: str | Callable[[Any], str] = 'Agent::Action::CallTool'#
- principal()#
Resolve the Cedar principal from Strands
invocation_state.- Parameters:
event (Any)
- Return type:
str
- resource()#
Resolve the Cedar resource from Strands
invocation_state.- Parameters:
event (Any)
- Return type:
str
- input_mapper()#
Build Dogwood tool-call input from a Strands tool event.
The default input shape is framework-neutral so the same Dogwood policy can be reused by future agent integrations. It works with a generic
CallToolaction and with schemas that map each tool name to a concrete Cedar action:{"tool": name, "input": tool_input, "toolUseId": id}- Parameters:
event (Any)
- Return type:
dict[str, Any]
- mode: Literal['enforce', 'log_only'] = 'enforce'#
- deny_message: str = 'Dogwood policy denied this tool call.'#
- dogwood.integrations.strands.attach_before_tool_call_hook(agent, hook)[source]#
Attach a Dogwood policy hook to a Strands agent.
This imports Strands only when called, keeping
dogwood-pyfree of a hard Strands dependency.- Parameters:
agent (Any)
hook (StrandsPolicyHook)
- Return type:
None
- dogwood.integrations.strands.before_tool_call_hook(policy_source, policy_schema_source, *, event_schema_source=None, action='Agent::Action::CallTool', principal=<function default_principal>, resource=<function default_resource>, input_mapper=<function default_tool_input>, mode='enforce', deny_message='Dogwood policy denied this tool call.')[source]#
Create a low-level
BeforeToolCallEventhook backed by Dogwood.The native Dogwood authorizer is persistent, so policy parsing and lowering happen once when this hook is constructed. Prefer
dogwood.integrations.strands.DogwoodInterventionfor new integrations that need typed Strands decisions.- Parameters:
policy_source (str)
policy_schema_source (str)
event_schema_source (str | None)
action (str | Callable[[Any], str])
principal (str | Callable[[Any], str])
resource (str | Callable[[Any], str])
input_mapper (Callable[[Any], dict[str, Any]])
mode (Literal['enforce', 'log_only'])
deny_message (str)
- Return type:
- dogwood.integrations.strands.confirm(prompt)[source]#
Return a Strands
Confirmaction with a human approval prompt.- Parameters:
prompt (str)
- Return type:
Any
- dogwood.integrations.strands.default_lifecycle_input(event)[source]#
Build Dogwood input from any supported Strands lifecycle event.
This mapper intentionally keeps the payload small and JSON-safe. It records the lifecycle name plus a few stable fields that policies commonly need. Tool-call events still use
default_tool_input()by default.- Parameters:
event (Any)
- Return type:
dict[str, Any]
- dogwood.integrations.strands.default_principal(event)[source]#
Resolve the Cedar principal from Strands
invocation_state.- Parameters:
event (Any)
- Return type:
str
- dogwood.integrations.strands.default_resource(event)[source]#
Resolve the Cedar resource from Strands
invocation_state.- Parameters:
event (Any)
- Return type:
str
- dogwood.integrations.strands.default_tool_input(event)[source]#
Build Dogwood tool-call input from a Strands tool event.
The default input shape is framework-neutral so the same Dogwood policy can be reused by future agent integrations. It works with a generic
CallToolaction and with schemas that map each tool name to a concrete Cedar action:{"tool": name, "input": tool_input, "toolUseId": id}- Parameters:
event (Any)
- Return type:
dict[str, Any]
- dogwood.integrations.strands.deny(message)[source]#
Return a Strands
Denyaction with a denial reason.- Parameters:
message (str)
- Return type:
Any
- dogwood.integrations.strands.guide(feedback)[source]#
Return a Strands
Guideaction with corrective model feedback.- Parameters:
feedback (str)
- Return type:
Any
- dogwood.integrations.strands.lifecycle_hook(policy_source, policy_schema_source, *, event_schema_source=None, action='Agent::Action::CallTool', principal=<function default_principal>, resource=<function default_resource>, tool_input_mapper=<function default_tool_input>, lifecycle_input_mapper=<function default_lifecycle_input>, lifecycle_events=('before_tool_call', ), mode='enforce', deny_message='Dogwood policy denied this lifecycle event.')[source]#
Create a lifecycle-aware Strands hook backed by Dogwood.
Pass
lifecycle_events="all"to evaluate every supported lifecycle event, or pass a tuple such as("before_invocation", "before_tool_call", "after_tool_call").- Parameters:
policy_source (str)
policy_schema_source (str)
event_schema_source (str | None)
action (str | Callable[[Any], str])
principal (str | Callable[[Any], str])
resource (str | Callable[[Any], str])
tool_input_mapper (Callable[[Any], dict[str, Any]])
lifecycle_input_mapper (Callable[[Any], dict[str, Any]])
lifecycle_events (tuple[str, ...] | str)
mode (Literal['enforce', 'log_only'])
deny_message (str)
- Return type: