View a markdown version of this page

Lifecycle hooks - Amazon Bedrock AgentCore
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Lifecycle hooks

Lifecycle hooks send an event to an Amazon Lambda function, Amazon SNS topic, or Amazon EventBridge event bus at a defined point in the harness agent loop. Use hooks to validate an invocation or tool call, record activity in another system, or start an asynchronous workflow.

You configure hooks on a harness with CreateHarness or UpdateHarness. Hook configuration is not available as an InvokeHarness override. A harness supports up to 20 hooks, and each hook name must be unique within the harness.

Choose a lifecycle event

The event type determines when the hook runs, the context sent to the target, and the effect of a Lambda deny decision.

Event Context Effect of a Lambda deny decision

before_invocation

The ordered messages supplied in the current InvokeHarness request, before the harness restores session state.

Stops the invocation before the agent starts. The response stream emits a messageStop event with stopReason set to hook_stopped.

before_tool_call

The tool name, tool type, tool-use ID, and model-generated tool input.

Skips the tool call. The agent loop continues.

after_tool_call

The tool name, tool type, tool-use ID, tool result, and an error string when a tool executed by the harness raised an exception.

Keeps the completed tool result, then stops the invocation. The response stream emits a messageStop event with stopReason set to hook_stopped.

after_invocation

The invocation stop reason and input and output token counts.

Reports the decision in the response stream. The invocation has already completed, so the hook cannot retract streamed output.

Use tool-call hooks with inline functions

Tool-call hooks also run for inline_function tools. The harness invokes before_tool_call after the model generates the tool request and before the harness hands the call off for client-side execution. Consume the complete response stream, then process the inline call only if the last messageStop event has stopReason set to tool_use. The model-generated request can appear in earlier stream events before the hook decision; before_tool_call controls execution, not visibility in the stream.

Return the matching toolResult in a later InvokeHarness request that uses the same runtimeSessionId. The harness resumes the pending call, invokes after_tool_call with the client-supplied result, and continues the agent loop if the hook allows the result. For the complete callback flow, see Inline function calls.

Tools in one model response retain sequential execution order. For harness-executed tool A, inline function B, and harness-executed tool C, the harness executes A before handing off B and waits for B’s result before executing C. Multiple inline functions are handed off one at a time. The follow-up request is a new invocation, so invocation hooks run again; before_tool_call for the pending inline function is not repeated, and after_tool_call runs once with the submitted result.

If before_tool_call denies B, the harness skips B and continues with C without handing B to the client. If after_tool_call denies B after the client returns its result, the harness retains B’s result and stops before C.

Validate client-supplied results

An after_tool_call event for an inline function confirms that the harness processed a result supplied by the client. It does not attest that the client performed the external action represented by that result. Authenticate the caller and validate the result before using the event as an authorization or audit signal.

Choose a target

Hooks support the following target types.

Target Delivery Decision behavior

Amazon Lambda

The harness invokes the function synchronously with RequestResponse and waits up to timeoutSeconds.

The function must return allow or deny. Lambda is the only target type that can change agent-loop behavior.

Amazon SNS

The harness publishes the hook payload as the SNS Message. Publishing does not block the agent loop.

Notification only. SNS does not return a hook decision.

Amazon EventBridge

The harness sends the hook payload in detail, with source bedrock-agentcore.harness and detail type Harness Lifecycle Hook. Sending does not block the agent loop.

Notification only. EventBridge does not return a hook decision.

The harness schedules SNS and EventBridge delivery without waiting for a response. A delivery error does not stop the invocation. For an SNS FIFO topic, the harness derives the message group ID from the harness ARN and session ID. This preserves publish order within that session. Standard SNS topics and EventBridge do not guarantee lifecycle event order.

Grant the harness execution role permission to call each target. For an example policy, see Lifecycle hook targets.

Configure hooks

The following example configures a Lambda decision hook before each invocation, an SNS notification after each tool call, and an EventBridge notification after each invocation. Define the hooks in hooks.json:

[ { "beforeInvocation": { "name": "validate_request", "target": { "lambda": { "arn": "arn:aws:lambda:us-west-2:123456789012:function:validate-harness-request", "timeoutSeconds": 5, "failureMode": "deny" } } } }, { "afterToolCall": { "name": "record_tool_result", "target": { "sns": { "arn": "arn:aws:sns:us-west-2:123456789012:harness-tool-events" } } } }, { "afterInvocation": { "name": "record_invocation", "target": { "eventBridge": { "arn": "arn:aws:events:us-west-2:123456789012:event-bus/harness-events" } } } } ]
Example
Amazon CLI

Apply the hooks:

aws bedrock-agentcore-control update-harness \ --harness-id "MyHarness-UuFdkQoXSL" \ --hooks file://hooks.json
Boto3

Load and apply the same configuration:

import json import boto3 client = boto3.client("bedrock-agentcore-control") with open("hooks.json", encoding="utf-8") as hooks_file: hooks = json.load(hooks_file) client.update_harness( harnessId="MyHarness-UuFdkQoXSL", hooks=hooks, )

When you pass hooks to UpdateHarness, the supplied list replaces the existing hook configuration. Pass an empty list to remove all hooks. Omit hooks to retain the existing configuration.

Return a Lambda decision

A Lambda target receives the hook payload directly. It must return a JSON object with a decision value of allow or deny. It can also return a reason. The harness includes this reason in the hookEvent response stream event and limits it to 1,024 characters.

The following handler denies a before_invocation event when the target cannot evaluate the complete hook context. Replace this logic with the rules for your application.

def lambda_handler(event, _context): hook_context = event["context"] if event["event"] == "before_invocation" and hook_context.get("truncated"): return { "decision": "deny", "reason": "The request context exceeded the evaluation limit.", } return {"decision": "allow"}

For Lambda targets, timeoutSeconds defaults to 60 seconds and accepts values from 1 through 900 seconds. failureMode determines the decision when the function times out, returns an error, or returns an invalid response:

  • deny – Apply a deny decision. This is the default.

  • allow – Apply an allow decision.

Configure long-running hook connections

A synchronous hook can keep an InvokeHarness stream idle until the hook returns or reaches timeoutSeconds. Configure the SDK read timeout to exceed the longest expected idle period. For a hook configured for 900 seconds, the following example uses a 1,000-second read timeout to provide additional margin:

import boto3 from botocore.config import Config client = boto3.client( "bedrock-agentcore", config=Config( read_timeout=1000, tcp_keepalive=True, ), )

If the connection passes through a NAT gateway, interface VPC endpoint, or Network Load Balancer, also configure the operating system TCP keepalive idle time to less than 350 seconds. On Linux, this is TCP_KEEPIDLE, configured by net.ipv4.tcp_keepalive_time. Setting tcp_keepalive=True alone enables TCP keepalive but does not shorten the operating system’s default idle time. For more information, see Connection timeout or reset on long-running or idle connections.

Context truncation does not activate failureMode. The Lambda function receives the truncated context and must return a decision.

When multiple Lambda hooks use the same event type, the harness invokes them concurrently. Any deny decision applies the event’s deny behavior. If more than one hook denies the event, the first denying hook in the configured list supplies the reason.

Process the hook payload

Every target receives the following common fields:

{ "hookEventId": "1234abcd-12ab-34cd-56ef-1234567890ab", "name": "validate_request", "event": "before_invocation", "harnessArn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:harness/MyHarness-UuFdkQoXSL", "sessionId": "12345678-1234-5678-9abc-123456789012", "context": { "messageCount": 1, "messages": [ { "role": "user", "content": [{"text": "Summarize this report."}] } ] } }

The context fields depend on the event type.

Event Field Description

before_invocation

messageCount

Number of messages in the current InvokeHarness request before hook-context truncation.

messages

Ordered messages from the current InvokeHarness request.

truncated

Present and set to true when the context exceeded 64 KiB.

before_tool_call

toolName

Model-facing tool name.

toolType

Harness tool type, such as builtin, remote_mcp, agentcore_gateway, or inline_function.

toolInput

Model-generated input for the tool.

toolUseId

ID that correlates the tool request with its result.

after_tool_call

toolName

Model-facing tool name.

toolType

Harness tool type.

toolResult

Result observed by the harness. For an inline function, this is the client-supplied result.

toolUseId

ID that correlates the tool request with its result.

error

Error text when a tool executed by the harness raised an exception. Omitted when no such exception occurred.

after_invocation

stopReason

Final invocation stop reason.

usage

Object containing inputTokens and outputTokens.

The before_invocation context does not include messages that the harness restores from session state after the hook runs. An after_tool_call Lambda deny decision does not remove the completed tool result.

Handle context truncation

The harness limits each hook context object to 64 KiB.

For before_invocation, the harness keeps the newest contiguous sequence of complete messages that fits within the limit. The payload preserves messageCount and keeps messages as a list. It also adds "truncated": true to the context. If the newest message alone exceeds the limit, messages is an empty list.

For other event types, the harness replaces the largest context fields until the context fits. Each replaced field uses the following form:

{ "truncated": true, "preview": "<prefix of the serialized field>" }

Check truncation markers before using a field. A Lambda target that requires complete context can return deny when it receives a truncation marker. An SNS or EventBridge consumer can record the event and obtain additional data from its own system of record.

Read hook events from the response stream

InvokeHarness emits a hookEvent for each configured hook that fires. A Lambda event includes the applied decision and optional reason:

{ "hookEvent": { "hookEventId": "1234abcd-12ab-34cd-56ef-1234567890ab", "name": "validate_request", "type": "before_invocation", "decision": "allow", "reason": "Request accepted." } }

For SNS and EventBridge targets, decision and reason are absent. Their hookEvent confirms that the harness scheduled the dispatch. It does not confirm that the target received the event.

Continue reading until the event stream closes. When the stream includes a terminal messageStop, any hookEvent with type set to after_invocation follows it. Some error paths emit after_invocation without a messageStop.