Skip to content

feat(hooks): add resume flag to AfterInvocationEvent#1767

Merged
mkmeral merged 7 commits intostrands-agents:mainfrom
mkmeral:agent-resume
Mar 10, 2026
Merged

feat(hooks): add resume flag to AfterInvocationEvent#1767
mkmeral merged 7 commits intostrands-agents:mainfrom
mkmeral:agent-resume

Conversation

@mkmeral
Copy link
Contributor

@mkmeral mkmeral commented Feb 25, 2026

Description

Motivation

Hooks that run after an agent invocation sometimes need to trigger a follow-up invocation automatically. For example, a coding agent hook could run tests and linting after each response, and if checks fail, resume the agent with the error output so it can fix the issues autonomously. Similarly, a validation hook could inspect the agent's output against acceptance criteria and loop until the result passes. Today there's no way to do this from a hook; callers have to build the retry loop externally, which duplicates lifecycle logic and bypasses the hook system entirely.

This PR adds a resume field to AfterInvocationEvent. When a hook sets resume to any valid AgentInput, the agent re-invokes itself with that input, firing a full new invocation cycle (including BeforeInvocationEvent). When resume is None (the default), behavior is unchanged.

Public API Changes

AfterInvocationEvent gains a writable resume attribute:

from strands import Agent
from strands.hooks.events import AfterInvocationEvent
import subprocess

async def validate_code(event: AfterInvocationEvent):
    result = subprocess.run(["hatch", "run", "prepare"], capture_output=True, text=True)
    if result.returncode != 0:
        event.resume = f"Tests/linting failed. Fix these errors:\n{result.stdout}\n{result.stderr}"

agent = Agent()
agent.add_hook(validate_code)
agent("implement a fibonacci function in utils.py")

Each resume triggers a complete invocation cycle: BeforeInvocationEvent → event loop → AfterInvocationEvent, so all existing hooks participate in every iteration. The resume input is converted to messages via the same _convert_prompt_to_messages path as normal agent calls.

Use Cases

  • Code quality loops: A hook runs tests and static analysis after each agent response, resuming with failure output until all checks pass
  • Output validation: Inspect the agent's result against acceptance criteria or schema constraints and loop until the output is correct
  • Multi-step workflows: Chain invocations where each step's output determines the next step's input, all within the hook lifecycle

Related Issues

Resolves: #1181

Documentation PR

TBD

Type of Change

New feature

Testing

  • 4 unit tests covering resume triggering, no-resume default, BeforeInvocationEvent firing on resume, and multi-step chaining

  • 3 unit tests for the resume field on AfterInvocationEvent (default value, writability, input type acceptance)

  • Manually tested with a Jupyter notebook demo

…-invocation

Add a writable 'resume' field (AgentInput | None) to AfterInvocationEvent.
When a hook sets resume to a non-None value, the agent automatically
re-invokes with that input, triggering a full new invocation cycle
including BeforeInvocationEvent.
@mkmeral mkmeral marked this pull request as draft February 25, 2026 20:09
@codecov
Copy link

codecov bot commented Feb 25, 2026

Codecov Report

❌ Patch coverage is 96.55172% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/strands/agent/agent.py 96.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@mkmeral mkmeral marked this pull request as ready for review March 2, 2026 15:26
When a hook sets resume on AfterInvocationEvent while an interrupt is
active, the resume loop now calls _interrupt_state.resume() to process
the interrupt responses before continuing. This enables hooks to
automatically handle interrupts by providing interruptResponse input
via event.resume, keeping the agent loop running without returning to
the caller.

If a hook provides invalid input (e.g. a plain string) while an
interrupt is active, _interrupt_state.resume() raises TypeError, same
as the normal caller-side interrupt resume path.

Added four tests covering:
- Hook handles interrupt automatically via resume with responses
- Invalid resume input during interrupt raises TypeError
- Interrupt without resume still returns to caller normally
- Interrupt during a resumed invocation handled end-to-end
@mkmeral
Copy link
Contributor Author

mkmeral commented Mar 6, 2026

/strands review

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

Question: The PR links to issue #1181 ("Add support for skills"), but that issue appears to be about loading knowledge files based on task context, which seems unrelated to the resume/re-invocation functionality.

Is there a different issue that this PR addresses, or is this feature part of the broader skills implementation? If this is a standalone feature, consider creating a dedicated issue or clarifying the relationship in the PR description.

@github-actions
Copy link

github-actions bot commented Mar 6, 2026

Issue: Documentation PR section is incomplete

The PR description lists "TBD" for the Documentation PR section. Per the contribution guidelines, PRs that introduce new public API features should either:

  1. Link to a corresponding documentation PR at https://github.com/strands-agents/docs/pull/..., OR
  2. Provide a justification explaining why documentation updates are not required

This feature adds a significant new capability (autonomous looping via hooks) that users need to understand. Documentation should cover:

  • The resume attribute usage and behavior
  • Example patterns for code validation loops, multi-step workflows
  • Best practices for implementing termination conditions

Action Required: Please either link a documentation PR or provide justification for why documentation is not needed.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: Request Changes

Well-designed feature that enables powerful autonomous looping patterns through the hooks system. The implementation is clean and follows existing SDK patterns.

Review Categories
  • Documentation (Blocking): The Documentation PR section is marked "TBD" - this must be addressed before merge
  • Safety: Consider adding guidance or guardrails for infinite loop prevention
  • API Design: Open discussion on resume vs continue naming should be resolved

Good test coverage with comprehensive edge case handling, including interrupt scenarios.

@mkmeral
Copy link
Contributor Author

mkmeral commented Mar 6, 2026

On agent review above: I wanted to keep the same logic for retrying for tools and model providers through hooks. Those "gaps" also exist there. I don't want to overload this feature for now

Docs is WIP

@mkmeral mkmeral merged commit bfe9d02 into strands-agents:main Mar 10, 2026
20 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add support for skills

2 participants