Skill: dym-harness-sdk-interventions — tool call guardrail
When to use
- When the agent needs to ask a human before running a dangerous tool, or to classify risk automatically, or to comply with a Cedar policy.
- Unlike a regular blocking hook: the handler returns a typed DECISION (Proceed · Deny · Guide · Interrupt · Transform), the registry merges by priority Deny > Interrupt > Transform > Guide > Proceed; Deny short-circuits the chain, Guide accumulates responses, there is a shared audit log (design
team/designs/0007-intervention-primitive.md).
Steps
- The chain grammar is DETERMINISTIC, do not guess content (
harness-py/src/strands_harness/interventions.py):
| value |
into |
"off" / None |
no handler |
"ask" |
HumanInTheLoop(ask=...) — ask the human on every call |
"smart" |
HumanInTheLoop(classifier=True) — the LLM classifies risk, only asks for flagged calls |
chain ends .cedar |
CedarAuthorization(policies=<file>) — need pip install 'strands-agents[cedar]' |
| another chain |
text policy → prompt of the risk classifier |
instance InterventionHandler |
passes straight through, unchanged |
- Combine multiple layers of different types:
create_harness(interventions=["policies/tools.cedar", "smart"])
- Check error behavior (measured 2026-09-28, rc 0):
create_harness(session=False, memory=False, interventions=["ask", "ask"])
# ValueError: Two interventions share the handler name 'strands:human-in-the-loop' ...
create_harness(session=False, memory=False, interventions=123)
# ValueError: Invalid interventions value 123; expected a preset name, ...
Rules
- Two handlers of the SAME type → raise (the SDK keeps only one handler per
name); combining DIFFERENT types (Cedar + a human-approval preset) is allowed.
- A Cedar policy written inline is NOT auto-detected (it looks like prose) → pass
CedarAuthorization(policies=...) directly.
- Child subagent inherits the parent's interventions; tool calls inside
programmatic_tool_caller still go through interventions — you cannot bypass it by calling indirectly.
- Order cheap handlers first (Cedar → service → LLM) because Deny short-circuits the chain.
"ask" with ask=None uses stdio — running headless/CI will hang waiting for input; pass a callback or use interrupt/resume.