Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hooks and lifecycle controls (Events, limits, a...

Hooks and lifecycle controls (Events, limits, and cancellation) | Learn Strands Agents with Kiro 03

A deck for Python developers who have built a first agent with Strands Agents and now want to control each call. It caps one call with limits, stops it from outside with cancel_signal, reads stop_reason, and shows what happens to tool errors, hook errors, and overlapping calls. It then registers hooks with add_hook, HookProvider, and Plugin, changes event properties to redact input, swap or cap tools, retry a tool, and resume, and compares middleware (Input, Wrap, Output) with retry strategies, with diagrams, real code, and real output. The facts come from the official Strands Agents documentation only, and the examples use a help-desk agent with made-up customers and orders, made for this series and run on Amazon Bedrock in the Tokyo Region. As of November 2026 (Strands Agents 1.64, Level 300). This is deck 03 (Go deeper) of the Learn Strands Agents with Kiro series.

Avatar for Riku INADA

Riku INADA PRO

November 11, 2026

More Decks by Riku INADA

Other Decks in Technology

Transcript

  1. Learn Strands Agents with Kiro 03 Go deeper Hooks and

    lifecycle controls Events, limits, and cancellation Level 300 / November 2026 November 2026 Riku Inada Amazon Web Services Japan © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 03 Kiro + Strands Agents 1
  2. Introduction About me Riku Inada Amazon Web Services Japan G.K.

    Solutions Architect Works mainly with manufacturing customers Recent work Promoting the AI-Driven Development Life Cycle (AI-DLC) Writing and speaking about Kiro and AWS Amplify © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. X @inada_riku Speaker Deck speakerdeck.com/inariku Zenn zenn.dev/inariku GitHub github.com/inariku 2
  3. Introduction Where this deck fits You are here: 03 Hooks

    and lifecycle controls. Next: 04 Interventions and plugins 01–02 Go deeper Get started Get started with 01 Strands Agents Read first 02 Tools and MCP 03–07 Verify Hooks and lifecycle 03 controls Read next 04 Interventions and plugins Observability 09 and evals 09 Ship and extend 08–10 Deploy and secure 08 for production 10 Strands harness 05 Sessions and memory output 06 Structured and streaming 07 Multi-agent © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 3
  4. Introduction Four places in an Agent call that this deck

    covers The help desk: the help-desk agent of a made-up online electronics shop. desk() makes it (appendix A2) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 4
  5. 03 Hooks and lifecycle controls Part 1 Limits, stops, and

    failures © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. limit_turns 1 cancelled 1 EventLoopException 5
  6. 1 Limits, stops, and failures With no limits, a call

    can loop after you leave Still looping The caller has left One call with no limits can run longer than you expect and use more tokens than your budget Cap the amount with limits, and the time with cancel_signal One call that checked two customers' orders with no limits took 2 cycles and 2,051 tokens © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 6
  7. 1 Limits, stops, and failures Cap one call with limits

    limits.py lines 3-10 agent = desk() ask = "Check all orders for C-1001 and C-1002." r = agent(ask, limits={"turns": 1, "total_tokens": 20000}) print(r.stop_reason, r.metrics.cycle_count) r = agent("Go on.", limits={"turns": 6}) print(r.stop_reason) The keys are turns, output_tokens, total_tokens, and structured_output_attempts. Each is optional and a positive integer © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Output limit_turns 1 end_turn Limits are checked at the start of each cycle, so one cycle can go over. Tools the last cycle asked for still run to the end The next call counts from 0, and the history stays, so you can call again 7
  8. 1 Limits, stops, and failures What the app does next

    for each stop_reason stop_reason What to do next end_turn, stop_sequence Return the finished result limit_turns, limit_total_tokens, limit_output_tokens, limit_structured_output_attempts Say the budget ran out, or raise the limit and call again (not a failure) cancelled Treat it as stopped. Partial messages stay out of the result and history (deltas already streamed to the caller stay visible) content_filtered, guardrail_intervened Handle it by your app rules, and do not retry it as is interrupt Collect human input and resume (deck 04) Read it from result.stop_reason. Limits and cancellation raise no exception © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 8
  9. 1 Limits, stops, and failures Stop a call from outside

    with cancel_signal cancel.py lines 5-14 agent = desk() cancel_signal = threading.Event() deadline = threading.Timer(3.0, cancel_signal.set) deadline.start() try: r = agent("Check all orders for C-1001 and C-1002.", cancel_signal=cancel_signal) finally: deadline.cancel() print(r.stop_reason, r.metrics.cycle_count) Output cancelled 1 The Agent never sets or clears the Event you pass agent.cancel() on an idle Agent cancels the next call, so call it only while a call runs Whether a running tool stops is up to the tool © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 9
  10. 1 Limits, stops, and failures Tool errors go back to

    the model, hook errors to you Failure What happens An exception in a tool body Goes back to the model as an error result, and the loop goes on An exception in a hook Model retries run out A config error errors.py output tool failure: end_turn hook failure: EventLoopException cause: ValueError('hook error') In 1.64.0, an exception in a Reaches your app Handle the returned stop_reason and the raised BeforeToolCallEvent hook arrives as EventLoopException (strands.types.exceptions), with the original in __cause__ exception separately © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 10
  11. 1 Limits, stops, and failures A second call on a

    busy Agent is refused C-1001's orders? Each call changes the Agent history, so an overlapping call raises ConcurrencyException (Agent(concurrent_invocation_mode=...), default THROW) C-1002's orders? For parallel calls, make one Agent per request agent(ask, idempotency_token="q-1") twice: the second waits and returns the same AgentResult (True in the output) overlap.py output other call: ConcurrencyException same token: True © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 11
  12. 03 Hooks and lifecycle controls agent.add_hook(on_tool) Part 2 Register hooks

    for events © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. BeforeToolCallEvent tool: lookup_customer 12
  13. 2 Register hooks for events The events of one call,

    in order App Agent agent(...) AgentResult BeforeInvocationEvent MessageAddedEvent Model Tool user Agent loop BeforeModelCallEvent AfterModelCallEvent MessageAddedEvent BeforeToolsEvent BeforeToolCallEvent AfterToolCallEvent AfterToolsEvent MessageAddedEvent Repeats while tool_use AfterInvocationEvent end_turn © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. tool_use assistant Call user (toolResult) events.py output (9 event types) BeforeInvocationEvent MessageAddedEvent user BeforeModelCallEvent AfterModelCallEvent MessageAddedEvent assistant BeforeToolsEvent BeforeToolCallEvent AfterToolCallEvent AfterToolsEvent MessageAddedEvent user BeforeModelCallEvent AfterModelCallEvent MessageAddedEvent assistant AfterInvocationEvent 13
  14. 2 Register hooks for events Register hooks one at a

    time with add_hook add_hook.py lines 8-19 def start(event): print("start") Output start tool: lookup_customer agent.add_hook(start, BeforeInvocationEvent) def on_tool(event: BeforeToolCallEvent): print("tool:", event.tool_use["name"]) Hooks in Strands Agents are registered with add_hook(fn, Event) With a type hint on the argument, you can leave out the event type You can register any number of hooks agent.add_hook(on_tool) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. # uses the type hint for one event 14
  15. 2 Register hooks for events Register several at once with

    HookProvider provider.py lines 7-21 class RequestLogger(HookProvider): def register_hooks(self, registry: HookRegistry): registry.add_callback(BeforeInvocationEvent, self.start) registry.add_callback(AfterInvocationEvent, self.end) Output start: Strands Agents end: end_turn Inside register_hooks, call add_callback(Event, fn) def start(self, event): print("start:", event.agent.name) Pass it as Agent(hooks=[...]), def end(self, event): print("end:", event.result.stop_reason) agent.hooks.add_hook() or later with agent = desk(hooks=[RequestLogger()]) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 15
  16. 2 Register hooks for events A Plugin can register several

    hooks too plugin.py lines 7-19 class LoggingPlugin(Plugin): name = "logging-plugin" @hook def log_before(self, event: BeforeToolCallEvent): print("Calling:", event.tool_use["name"]) @hook def log_after(self, event: AfterToolCallEvent): print("Completed:", event.tool_use["name"]) Output Calling: lookup_customer Completed: lookup_customer Subclass Plugin, give it a name, and put @hook on its methods Each method is registered for the event type of its argument A plugin can also bring tools and settings (deck 04) agent = desk(plugins=[LoggingPlugin()]) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 16
  17. 2 Register hooks for events Set the order hooks run

    in with order order.py lines 13-19 (say(text) returns a hook that prints text) B, A = BeforeModelCallEvent, AfterModelCallEvent agent.add_hook(say("Before last"), B, order=HookOrder.SDK_LAST) agent.add_hook(say("Before 0 (1)"), B) agent.add_hook(say("Before 0 (2)"), B) agent.add_hook(say("Before first"), B, order=HookOrder.SDK_FIRST) agent.add_hook(say("After 0 (1)"), A) agent.add_hook(say("After 0 (2)"), A) Output Before first Before 0 (1) Before 0 (2) Before last After 0 (2) After 0 (1) HookOrder Value Smaller values run first. SDK_FIRST - 1 and float('-inf') work too SDK_FIRST -100 For equal values, Before hooks run in the order registered, and DEFAULT 0 (default) SDK_LAST 100 © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. After hooks in reverse 17
  18. 2 Register hooks for events Read call-time values in a

    hook state.py lines 8-13, 16-22 def audit(event: BeforeToolCallEvent): s = event.invocation_state db = s.get("database_connection") user = s.get("user_id") name = event.tool_use["name"] print(user, name, type(db).__name__) agent = desk() agent.add_hook(audit) db_conn = sqlite3.connect(":memory:") state = {"user_id": "staff-07", "database_connection": db_conn} agent("What is C-1001's account status?", invocation_state=state) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Output staff-07 lookup_customer Connection A hook reads them from event.invocation_state You can pass values that are not JSON, such as a DB connection Keyword arguments also work, but 1.64.0 prints a deprecation warning 18
  19. 03 Hooks and lifecycle controls BeforeToolCallEvent Part 3 selected_tool Change

    events to change behavior cancel_tool © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. refund_review 19
  20. 3 Change events to change behavior Change event properties to

    change behavior Event Property What changing it does BeforeInvocationEvent messages Changes the input before it enters history AfterModelCallEvent retry Retries the model call BeforeToolsEvent cancel Stops all the tools of one batch BeforeToolCallEvent cancel_tool Skips the tool and returns your text BeforeToolCallEvent selected_tool Swaps the tool that runs BeforeToolCallEvent tool_use Changes the arguments the tool gets AfterToolCallEvent result Changes the tool result AfterToolCallEvent retry Retries the tool AfterToolsEvent end_turn Ends the loop without calling the model AfterInvocationEvent resume Starts another call with new input © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 20
  21. 3 Change events to change behavior Hide the input before

    it enters the history redact.py lines 8-12, 15-16 def redact_input(event: BeforeInvocationEvent): if not event.messages: return event.messages[-1]["content"] = [ {"text": "[REDACTED]"}] agent.add_hook(redact_input) r = agent("My phone is 090-1234-5678.") Change messages in BeforeInvocationEvent, and that content enters the history and goes to the model Set event.messages to [] to drop the input Output (lines 17-18 print the history and the answer) history: [{'text': '[REDACTED]'}] answer: Could you share your question or issue? I'm happy to help right away. © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 21
  22. 3 Change events to change behavior Swap the tool being

    called for another one swap.py lines 13-17, 21 def intercept(event: BeforeToolCallEvent): if event.tool_use["name"] == "process_refund": event.selected_tool = refund_review event.tool_use["name"] = "refund_review" print("swapped:", event.tool_use["input"]) agent.add_hook(intercept) Put the other tool in selected_tool, and change the name in tool_use too It swaps on every process_refund call refund_review (lines 7-10) is a @tool that is not in the tools list of the Agent Output swapped: {'order_id': 'ORD-5390', 'yen': 15800} answer: Refund for ORD-5390 has been submitted for review. Ichiro Suzuki will get ¥15,800 back soon. © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 22
  23. 3 Change events to change behavior Cap how many times

    a tool can be called limit_tools.py lines 26-33, 41-43 def intercept_tool(self, event): name = event.tool_use["name"] with self._lock: limit = self.max_tool_counts.get(name) count = self.tool_counts.get(name, 0) + 1 self.tool_counts[name] = count if limit and count > limit: event.cancel_tool = f"{name}: limit reached" limit = LimitToolCounts({"get_order_history": 1}) agent = desk(hooks=[limit]) agent.add_hook(show) Output C-1002 error C-1001 success Over the limit, the HookProvider LimitToolCounts (lines 10-33) sets cancel_tool The count resets in BeforeInvocationEvent (lines 16-24) The second call counted (C-1002) was cancelled © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 23
  24. 3 Change events to change behavior Call a failed tool

    again tool_retry.py lines 22-28, 31-33 def retry_once(event: AfterToolCallEvent): tid = event.tool_use["toolUseId"] tries[tid] = tries.get(tid, 0) + 1 status = event.result["status"] print(tid[-6:], "try", tries[tid], status) if status == "error" and tries[tid] <= 1: event.retry = True agent = desk(tools=[get_stock]) agent.add_hook(retry_once) print("answer:", agent("USB-C hub in stock?")) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Output 9wuMln try 1 error 9wuMln try 2 success answer: Yes, we have 12 in stock. Let us know if you want to order. Set retry = True, and the result is dropped and the tool runs again with the same toolUseId Count the tries yourself. The caller still gets the ToolStreamEvent of a dropped try 24
  25. 3 Change events to change behavior Start one more call

    after the call ends resume.py lines 9-15, 22-26 def summarize(event: AfterInvocationEvent): global resume_count if resume_count or not event.result: return if event.result.stop_reason == "end_turn": resume_count += 1 event.resume = "Sum it up in 5 words." agent = desk() agent.add_hook(start) agent.add_hook(summarize) r = agent("What is C-1001's account status?") print(r) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Output BeforeInvocationEvent BeforeInvocationEvent Account status: active, no issues. Put input in resume, and the next call starts without returning to the caller It starts again from the first event, so the start hook (lines 18-19) printed twice Always write a stop condition 25
  26. 03 Hooks and lifecycle controls InvokeModelStage Part 4 Middleware and

    retry strategies © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. ModelThrottledException ModelRetryStrategy end_turn 26
  27. 4 Middleware and retry strategies Middleware can wrap two stages

    Stage Wraps Context Result InvokeModelStage One model call InvokeModelContext InvokeModelResult ExecuteToolStage One tool call ExecuteToolContext ExecuteToolResult Hooks watch events and react. Middleware wraps a stage and decides whether it runs, and how many times It can retry, cache, route to another model, or skip a stage In the tool stage, it can also use an interrupt that waits for human approval (deck 04) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 27
  28. 4 Middleware and retry strategies Input, Wrap, and Output always

    run in this order Input Before hook Outside Returns a new context Wrap The stage runs (the model call) Output Returns a new result After hook Outside next(context) next 0 times: skip the stage once: run it / twice or more: retry They run in this order whatever order you register them in. Input returns a copy of the context, and Output takes the result and returns it. Wrap sets the flow of the stage by how many times it calls next © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 28
  29. 4 Middleware and retry strategies Change the prompt for one

    call with Input mw_input.py lines 8-15 def in_capitals(context): return context.replace( system_prompt="Answer in capitals, 5 words max.") agent.add_middleware(InvokeModelStage.Input, in_capitals) print("answer:", agent("What are your business hours?")) print("system_prompt:", agent.system_prompt[:15]) Output answer: I'M AVAILABLE 24/7, ALWAYS system_prompt: You work at the © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Only that model call changes. The last line shows agent.system_prompt as before Do not edit context in place: return replace() 29
  30. 4 Middleware and retry strategies Write a Wrap as an

    async generator mw_wrap.py lines 11-21 async def timing(context, next_fn): start = time.monotonic() async for event in next_fn(context): if isinstance(event, ModelStopReason): print("result:", event.stop_reason) yield event took = time.monotonic() - start print(f"model call took {took:.1f}s") Output result: tool_use model call took 3.7s result: end_turn model call took 1.4s Register it on a stage with add_middleware() The result of the stage flows as agent.add_middleware(InvokeModelStage, timing) ModelStopReason for the model and ToolResultEvent for a tool. Tell them apart by type © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 30
  31. 4 Middleware and retry strategies Retries in a Wrap still

    fire one pair of hooks Hooks (one pair) BeforeModelCallEvent next twice Middleware Wrap Model call Model call Try 1 Try 2 mw_vs_hook.py output hook: BeforeModelCallEvent middleware: try 1 middleware: try 2 hook: AfterModelCallEvent The Wrap calls next twice, and the hook still fires one pair AfterModelCallEvent Hooks always fire, even when a stage is skipped. To see each try, use middleware © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 31
  32. 4 Middleware and retry strategies Call next twice in a

    Wrap, and the stage runs twice mw_vs_hook.py lines 10-15, 18-19, 22-24 async def run_twice(context, next_fn): for attempt in (1, 2): print(" middleware: try", attempt) events = [e async for e in next_fn(context)] for event in events: yield event def hook(event): print("hook:", type(event).__name__) agent.add_middleware(InvokeModelStage, run_twice) agent.add_hook(hook, [BeforeModelCallEvent, AfterModelCallEvent]) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Output hook: BeforeModelCallEvent middleware: try 1 middleware: try 2 hook: AfterModelCallEvent Each next_fn(context) call runs the stage (here, a model call) once This Wrap passes on only the second result. The hook fires one pair 32
  33. 4 Middleware and retry strategies By default, throttling is retried

    up to 5 times Try If it fails 1 Wait 4 seconds exceptions are raised at once 2 Wait 8 seconds To change it, pass a ModelRetryStrategy as 3 Wait 16 seconds Agent(retry_strategy=...) (next slide) 4 Wait 32 seconds 5 Wait 64 seconds 6 Exception raised © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. Only ModelThrottledException is retried. Other retry_strategy=None turns retries off In 1.64.0, ModelRetryStrategy() defaults to max_attempts=6 and initial_delay=4 33
  34. 4 Middleware and retry strategies Override is_retryable to retry more

    exceptions retry_sub.py lines 7-11, 14-15, 20-21 class MyRetryStrategy(ModelRetryStrategy): def is_retryable(self, exception): if isinstance(exception, TimeoutError): return True return super().is_retryable(exception) s = MyRetryStrategy(max_attempts=4, initial_delay=2, max_delay=60) agent = desk(retry_strategy=s) print(agent("Business hours?").stop_reason) Output ModelThrottledException True TimeoutError True ValueError False end_turn super().is_retryable(exception) keeps the default throttling retries Make one instance per Agent (it carries per-turn state) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 34
  35. 4 Middleware and retry strategies Retry a model call in

    an AfterModelCallEvent hook hook_retry.py lines 10, 21-26, 30 retries = [] # retries, counted by the hook async def retry_model(event: AfterModelCallEvent): print("exception:", repr(event.exception)) if event.exception is not None and len(retries) < 2: retries.append(1) event.retry = True await asyncio.sleep(2 ** len(retries)) agent.add_hook(retry_model) Output exception: TimeoutError('failed on purpose') exception: None retries: 1 end_turn © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. fail_once on lines 13-18 is a Wrap that makes the first model call raise TimeoutError On a failure, event.exception holds the exception. retry = True runs the same model call again The hook sets the count and the wait. To count again for each call, reset it in BeforeInvocationEvent 35
  36. 4 Middleware and retry strategies Subclass the strategy, or retry

    in a hook? Subclass ModelRetryStrategy AfterModelCallEvent hook Good for Owning the whole retry rule Just retrying on an error You write An is_retryable override event.retry = True Waiting The strategy backs off No wait unless you add one To retry in middleware, set retry_strategy=None The strategy runs outside the middleware chain, so each of its retries runs your retry middleware again © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 36
  37. Summary Summary 1 Bound one call with limits and cancel_signal,

    and choose the next step from stop_reason 2 Hooks receive events and change writable event properties to change behavior. Register them with add_hook, HookProvider, or Plugin 3 To wrap one model or tool call, use middleware. retry_strategy already retries throttling © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 37
  38. Summary Read next 04 Interventions and plugins An interrupt that

    waits for human approval before a tool runs, and plugins that bundle hooks 02 Tools and MCP How to make the tools this deck swapped and counted © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 38
  39. Appendix A1 The code this deck ran File Slide File

    Slide File Slide nolimit.py 6 provider.py 15 tool_retry.py 24 limits.py 7 plugin.py 16 resume.py 25 cancel.py 9 order.py 17 mw_input.py 29 errors.py 10 state.py 18 mw_wrap.py 30 overlap.py 11 redact.py 21 mw_vs_hook.py 31, 32 events.py 13 swap.py 22 retry_sub.py 34 add_hook.py 14 limit_tools.py 23 hook_retry.py 35 All are in d03/ of the help-desk agent and are not published. Run on 2026-11-11 on Amazon Bedrock in Tokyo (global.anthropic.claude-sonnet-5, global cross-Region inference) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 39
  40. Appendix A2 desk(), which makes the help desk base.py lines

    2, 4-5, 12, 15-19 from strands import Agent from agent import model from tools import get_order_history, lookup_customer, process_refund TOOLS = [lookup_customer, get_order_history, process_refund] def desk(**kw): """The help-desk agent. kw goes to Agent unchanged.""" kw.setdefault("tools", TOOLS) kw.setdefault("callback_handler", None) return Agent(model=model, system_prompt=PROMPT, **kw) desk() passes its arguments to Agent, except model and system_prompt. Use your own tools and PROMPT model comes from agent.py: a BedrockModel of global.anthropic.claude-sonnet-5 in Tokyo. Imports: A3 © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 40
  41. Appendix A3 The imports of this deck's code imports.py lines

    1-12 from strands import ModelRetryStrategy, tool from strands.hooks import (AfterInvocationEvent, AfterModelCallEvent, AfterToolCallEvent, AfterToolsEvent, BeforeInvocationEvent, BeforeModelCallEvent, BeforeToolCallEvent, BeforeToolsEvent, HookOrder, HookProvider, HookRegistry, MessageAddedEvent) from strands.middleware import (InvokeModelStage, ModelStopReason, ToolResultEvent) from strands.plugins import Plugin, hook from strands.types.exceptions import (ConcurrencyException, EventLoopException, ModelThrottledException) from d03.base import desk 22 names from strands and desk (threading, time, asyncio, sqlite3 left out). All 23 import in 1.64.0 © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 41
  42. Appendix Sources Strands Agents documentation, “Production Lifecycle Controls”, November 2026

    (slides 4, 6–11, 37) https://strandsagents.com/docs/user-guide/sdk/agents/lifecycle-controls/ Strands Agents documentation, “Structured output”, November 2026 (slide 7) https://strandsagents.com/docs/user-guide/sdk/agents/structured-output-behavior/ Strands Agents documentation, “Hook events”, November 2026 (slides 13, 17, 20) https://strandsagents.com/docs/user-guide/sdk/agents/hooks-events/ Strands Agents documentation, “Hooks”, November 2026 (slides 4, 10, 14–18, 21–25, 35, 37) https://strandsagents.com/docs/user-guide/sdk/agents/hooks/ Strands Agents documentation, “Middleware”, November 2026 (slides 4, 27–32, 36, 37) https://strandsagents.com/docs/user-guide/sdk/agents/middleware/ Strands Agents documentation, “Retry Strategies”, November 2026 (slides 4, 33, 34, 36, 37) https://strandsagents.com/docs/user-guide/sdk/agents/retry-strategies/ The code we ran and its output, from the help-desk agent (English copy, not published), strands-agents 1.64.0, Amazon Bedrock in Tokyo, 2026-11-11 (slides 6, 7, 9–11, 13–18, 21–25, 29–35) © 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved. 42