dogwood.integrations.strands#

Strands Agents integration for Dogwood policy enforcement.

The integration follows the Strands extension model:

  • DogwoodIntervention implements typed intervention decisions for before-tool-call authorization.

  • DogwoodPlugin subclasses strands.plugins.Plugin and 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. DogwoodIntervention can evaluate Dogwood policies across the primary Strands lifecycle methods:

  • before_invocation

  • before_model_call

  • before_tool_call

  • after_tool_call

  • after_model_call

For before_tool_call it returns:

  • Proceed when Dogwood allows the tool call.

  • Deny when Dogwood denies the tool call.

  • Confirm when Dogwood denies and confirm_when requests human approval.

Without Strands installed, the class returns small local stand-ins so the mapping behavior remains testable.

Constructor inputs:

  • policy_source and policy_schema_source build a persistent native Dogwood authorizer.

  • event_schema_source supplies an explicit Dogwood .dwschema.

  • authorizer reuses an existing dogwood.native.NativeAuthorizer instead of building one.

  • principal, resource, and input_mapper customize how Strands events are mapped into Dogwood authorization requests.

  • action may be a fixed Cedar action string or a callback that resolves the action from a Strands event, which supports one action per tool.

  • mode is "enforce" by default. "log_only" records the Dogwood decision on the event and proceeds without blocking.

  • confirm_when turns Dogwood denials into Strands Confirm actions for selected tool calls.

  • lifecycle_events selects 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 configured input_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 Proceed and Transform. Dogwood denials are therefore observational here; hard enforcement belongs in before_tool_call.

Parameters:
  • event (Any)

  • kwargs (Any)

Return type:

Any

after_model_call(event, **kwargs)[source]#

Observe a model response and optionally guide on Dogwood denial.

Parameters:
  • event (Any)

  • kwargs (Any)

Return type:

Any

class dogwood.integrations.strands.DogwoodPlugin[source]#

Strands plugin for lifecycle-wide Dogwood policy enforcement.

DogwoodPlugin follows the Strands plugin pattern: it subclasses strands.plugins.Plugin and marks lifecycle methods with @hook so Strands can discover and register them automatically.

Before-events can be cancelled when Dogwood denies. After-events are observed by Dogwood and continue. Use DogwoodIntervention when you want Strands’ typed Proceed / Deny / Confirm / Guide / Transform control 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

on_before_tool_call(event)[source]#

Authorize a BeforeToolCallEvent before Strands executes a tool.

Parameters:

event (Any)

Return type:

None

on_after_tool_call(event)[source]#

Observe a completed tool call.

Parameters:

event (Any)

Return type:

None

class dogwood.integrations.strands.StrandsLifecyclePolicyHook[source]#

Lifecycle-aware Strands hook backed by Dogwood authorization.

StrandsLifecyclePolicyHook supports 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 CallTool action 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.'#
handle(lifecycle, event)[source]#

Evaluate a lifecycle event and return the Dogwood decision string.

Parameters:
  • lifecycle (str)

  • event (Any)

Return type:

str

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 BeforeToolCallEvent when you want direct hook behavior instead of a Strands plugin or intervention.

If Dogwood denies the request, the hook sets event.cancel_tool with deny_message. Strands then skips the selected tool.

Use this class when you need direct hook registration. Otherwise prefer DogwoodIntervention for typed decisions or DogwoodPlugin for 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 CallTool action 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.'#
__call__(event)[source]#

Authorize one tool event.

action may be a fixed Cedar action or a resolver callback that derives the action from the Strands tool event.

Parameters:

event (Any)

Return type:

None

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-py free of a hard Strands dependency.

Parameters:
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 BeforeToolCallEvent hook 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.DogwoodIntervention for 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:

StrandsPolicyHook

dogwood.integrations.strands.confirm(prompt)[source]#

Return a Strands Confirm action 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 CallTool action 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 Deny action with a denial reason.

Parameters:

message (str)

Return type:

Any

dogwood.integrations.strands.guide(feedback)[source]#

Return a Strands Guide action 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:

StrandsLifecyclePolicyHook

dogwood.integrations.strands.proceed()[source]#

Return a Strands Proceed action, or a local stand-in for tests.

Return type:

Any

dogwood.integrations.strands.transform(apply)[source]#

Return a Strands Transform action that mutates an event in place.

Parameters:

apply (Callable[[Any], Any])

Return type:

Any