From 323f016eabc2bbef09932e6de18d11e5069ecb79 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:26:41 -0700 Subject: [PATCH 01/26] Document Serverless Workers on AgentCore Runtime --- .../workers/serverless-workers/index.mdx | 30 ++++--- .../serverless-workers-agentcore.mdx | 90 +++++++++++++++++++ sidebars.js | 1 + 3 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx diff --git a/docs/encyclopedia/workers/serverless-workers/index.mdx b/docs/encyclopedia/workers/serverless-workers/index.mdx index ff394ec70a..657fb5f378 100644 --- a/docs/encyclopedia/workers/serverless-workers/index.mdx +++ b/docs/encyclopedia/workers/serverless-workers/index.mdx @@ -14,10 +14,10 @@ tags: import { CaptionedImage, ReleaseNoteHeader } from '@site/src/components'; - AWS Lambda support is in Public Preview. GCP Cloud Run support is in Pre-release, and its APIs may change in - backwards-incompatible ways. To request Cloud Run access, create a [support ticket](/cloud/support#support-ticket) or - contact your account team, and [sign up for updates](https://temporal.io/pages/serverless-workers-updates) to hear - when Cloud Run reaches Public Preview. + AWS Lambda support is in Public Preview. Amazon Bedrock AgentCore Runtime and GCP Cloud Run support are in + Pre-release, and their APIs may change in backwards-incompatible ways. To request Cloud Run access, create a + [support ticket](/cloud/support#support-ticket) or contact your account team, and + [sign up for updates](https://temporal.io/pages/serverless-workers-updates) to hear when Cloud Run reaches Public Preview. This page covers the following: @@ -39,8 +39,9 @@ in response to work on a Task Queue. A Serverless Worker uses the same Temporal SDKs as a traditional long-lived Worker, and registers Workflows and Activities the same way. What differs is that Temporal manages the Worker's lifecycle rather than you running a Worker -process. How that lifecycle works depends on the compute provider: AWS Lambda runs short-lived invocations, while GCP -Cloud Run runs a pool of long-lived instances. See [Worker lifecycle](#worker-lifecycle). +process. How that lifecycle works depends on the compute provider: AWS Lambda runs short-lived invocations, Amazon +Bedrock AgentCore Runtime runs sessions with idle and maximum-lifetime limits, and GCP Cloud Run runs a pool of +long-lived instances. See [Worker lifecycle](#worker-lifecycle). Serverless Workers require [Worker Versioning](/worker-versioning). Each Serverless Worker must be associated with a [Worker Deployment Version](/worker-versioning#deployment-versions) that has a compute provider configured. @@ -69,12 +70,13 @@ Temporal impersonates to scale it. Compute providers are only needed for Serverless Workers. Traditional long-lived Workers do not require a compute provider because the Worker process lifecycle is not managed by the Temporal server. -Temporal supports two compute providers: +Temporal supports three compute providers: -| Provider | Description | -| ------------- | ----------------------------------------------------------------------------- | -| AWS Lambda | Temporal assumes an IAM role in your AWS account to invoke a Lambda function. | -| GCP Cloud Run | Temporal scales a Cloud Run [Worker Pool](https://cloud.google.com/run/docs/resource-model#worker-pools) through the Cloud Run admin API. A Worker Pool is its own Cloud Run resource type, distinct from a Service or a Job. | +| Provider | Description | +| ------------------------------ | ----------- | +| AWS Lambda | Temporal assumes an IAM role in your AWS account to invoke a Lambda function. | +| Amazon Bedrock AgentCore Runtime | Temporal assumes an IAM role in your AWS account to invoke an AgentCore Runtime endpoint. | +| GCP Cloud Run | Temporal scales a Cloud Run [Worker Pool](https://cloud.google.com/run/docs/resource-model#worker-pools) through the Cloud Run admin API. A Worker Pool is its own Cloud Run resource type, distinct from a Service or a Job. | ## How Serverless invocation works {/* #how-invocation-works */} @@ -167,6 +169,7 @@ short-lived invocations on AWS Lambda, or long-lived pool instances on GCP Cloud Refer to the lifecycle section for your compute provider: - [AWS Lambda lifecycle](/serverless-workers/aws-lambda#lifecycle) +- [Amazon Bedrock AgentCore Runtime lifecycle](/serverless-workers/agentcore#lifecycle) - [GCP Cloud Run lifecycle](/serverless-workers/cloud-run#lifecycle) ## Failure handling {/* #failure-handling */} @@ -207,9 +210,9 @@ With single-slot configuration, each Activity gets a dedicated execution environ | Constraint | Detail | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Activity duration | Depends on the compute provider. On AWS Lambda, an Activity must finish within the invocation limit (15 minutes maximum), minus the shutdown deadline buffer. On GCP Cloud Run, instances are long-lived, so no per-invocation limit applies. See [Worker lifecycle](#worker-lifecycle). | +| Activity duration | Depends on the compute provider. On AWS Lambda, an Activity must finish within the invocation limit (15 minutes maximum), minus the shutdown deadline buffer. On Amazon Bedrock AgentCore Runtime, a microVM session has a maximum lifetime of 8 hours. On GCP Cloud Run, instances are long-lived, so no per-invocation limit applies. See [Worker lifecycle](#worker-lifecycle). | | Workflow duration | No limit. Workflows of any duration work. A Workflow runs across as many Workers as needed. | -| Worker code | Same Temporal SDK Worker code, using the serverless Worker package for your SDK. | +| Worker code | Depends on the compute provider. AWS Lambda uses a serverless Worker package for your SDK. Amazon Bedrock AgentCore Runtime and GCP Cloud Run run standard long-lived Temporal Workers inside provider-specific runtime infrastructure. | | Versioning | [Worker Versioning](/worker-versioning) is required. Each Workflow must have an `AutoUpgrade` or `Pinned` behavior, set per-Workflow or as a Worker-level default. See [Worker Versioning](/worker-versioning) for rollout strategies such as ramping, and [Worker Versioning with Serverless Workers](#worker-versioning-with-serverless-workers) for how Worker Deployment Versions map to compute provider primitives. | | High Availability | On failover of a Namespace with [Multi-region or Multi-cloud Replication](/cloud/high-availability), the WCI keeps invoking Workers in the original region unless you manually repoint the compute provider. Compute provider configuration, such as a Lambda ARN or a Cloud Run Worker Pool, is scoped to a single region. See [Serverless Workers and High Availability](/cloud/high-availability#serverless-workers). | @@ -221,4 +224,5 @@ How Worker Deployment Versions map to compute provider primitives differs by pro Refer to the versioning section for your compute provider: - [AWS Lambda versioning](/serverless-workers/aws-lambda#worker-versioning) +- [Amazon Bedrock AgentCore Runtime versioning](/serverless-workers/agentcore#worker-versioning) - [GCP Cloud Run versioning](/serverless-workers/cloud-run#worker-versioning) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx new file mode 100644 index 0000000000..5d7abb99e2 --- /dev/null +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -0,0 +1,90 @@ +--- +id: serverless-workers-agentcore +title: Serverless Workers on Amazon Bedrock AgentCore Runtime +sidebar_label: Amazon Bedrock AgentCore +description: + How Serverless Workers run on Amazon Bedrock AgentCore Runtime, including Worker Versioning and Runtime session + lifecycle. +slug: /serverless-workers/agentcore +toc_max_heading_level: 4 +tags: + - Workers + - Concepts + - Serverless + - Amazon Bedrock AgentCore +--- + +import { ReleaseNoteHeader } from '@site/src/components'; + + + Amazon Bedrock AgentCore Runtime support is in Pre-release, and its APIs may change in backwards-incompatible ways. + + +This page covers how Serverless Workers run on Amazon Bedrock AgentCore Runtime, including Worker Versioning and the +Runtime session lifecycle. + +On AgentCore Runtime, a Serverless Worker is a standard long-running Temporal Worker that runs inside an AgentCore +Runtime session. When the [Worker Controller Instance (WCI)](/serverless-workers#worker-controller-instance) needs +capacity, it invokes an AgentCore Runtime endpoint. The Runtime starts a Worker, which connects to the Temporal Service +and polls its Task Queue. + +AgentCore Runtime uses the same event-driven autoscaling model as AWS Lambda. The WCI invokes individual Runtime +sessions when it needs more capacity; it does not manage a target-sized pool of Runtime sessions. + +## Worker Versioning {/* #worker-versioning */} + +Serverless Workers require [Worker Versioning](/worker-versioning). Associate each Worker Deployment Version with a +named AgentCore Runtime endpoint that points to one AgentCore Runtime version. + +AgentCore creates an immutable Runtime version when you create or update a Runtime. A named endpoint has a stable ARN +and points to a chosen Runtime version. Configure the endpoint ARN as the compute provider for the corresponding Worker +Deployment Version: + +```bash +temporal worker deployment create-version \ + --deployment-name my-worker \ + --build-id v1 \ + --aws-agentcore-endpoint-arn \ + --aws-agentcore-assume-role-arn \ + --aws-agentcore-assume-role-external-id +``` + +Use one named endpoint for each Worker Deployment Version. For example, point an endpoint named `temporal-v1` at +AgentCore Runtime version `1` and use its ARN for Temporal Worker Deployment Version `my-worker/v1`. + +When you deploy new Worker code, AgentCore creates a new Runtime version. Create another endpoint that points to that +new Runtime version and configure it on a new Worker Deployment Version. Keep the older endpoint while Pinned +Workflows can still need the older Worker code. + +:::caution + +Do not configure a live Worker Deployment Version with AgentCore's `DEFAULT` endpoint. That endpoint moves to the +latest Runtime version whenever you update the Runtime. Updating code behind a Worker Deployment Version can cause +non-determinism errors for in-flight Workflows, including Pinned Workflows. + +::: + +For details about AgentCore Runtime versions and endpoints, see [AgentCore Runtime versioning and +endpoints](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agent-runtime-versioning.html). + +## Lifecycle {/* #lifecycle */} + +The Runtime session provides the compute that hosts a Worker. It is not the durable identity of a Workflow, and Temporal +can route a later Workflow or Activity Task to another Worker. + +Unlike an AWS Lambda Worker, an AgentCore Worker does not run within a fixed Lambda invocation deadline. Your Runtime +handler starts the Worker as background work, and the Worker continues polling until it drains or AgentCore terminates +the Runtime session. + +AgentCore applies lifecycle settings to each microVM Runtime session: + +- **Idle Runtime session timeout**: Ends an idle session. The default is 15 minutes. +- **Maximum lifetime**: Ends a microVM session after a fixed lifetime. The default and maximum is 8 hours. + +Set the Worker’s own idle and graceful-shutdown policy so it stops polling and drains in-flight Tasks before its Runtime +session is no longer needed. The AgentCore maximum lifetime remains a hard termination boundary. Configure Activity +timeouts and, for long-running Activities, [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat) +so a retry can recover after a Worker is interrupted. + +For the lifecycle setting ranges and defaults, see [Configure Amazon Bedrock AgentCore lifecycle +settings](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-lifecycle-settings.html). diff --git a/sidebars.js b/sidebars.js index ba42163641..a939fb2209 100644 --- a/sidebars.js +++ b/sidebars.js @@ -2014,6 +2014,7 @@ module.exports = { link: { type: 'doc', id: 'encyclopedia/workers/serverless-workers/serverless-workers' }, items: [ 'encyclopedia/workers/serverless-workers/serverless-workers-aws-lambda', + 'encyclopedia/workers/serverless-workers/serverless-workers-agentcore', 'encyclopedia/workers/serverless-workers/serverless-workers-cloud-run', ], }, From c5f26a7c7bef9f0cdfaeb43f912b30d1794ec467 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:31:23 -0700 Subject: [PATCH 02/26] Clarify AgentCore Runtime session lifecycle --- .../serverless-workers/serverless-workers-agentcore.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 5d7abb99e2..1bf519fd50 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -69,8 +69,9 @@ endpoints](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agent-r ## Lifecycle {/* #lifecycle */} -The Runtime session provides the compute that hosts a Worker. It is not the durable identity of a Workflow, and Temporal -can route a later Workflow or Activity Task to another Worker. +An AgentCore Runtime session is only the compute that runs a Worker. It can end because of its idle timeout or maximum +lifetime. A later Task for the same Workflow can run on another Worker in another Runtime session. Keep state that the +Workflow needs in the Workflow or another durable store, not only in the Runtime session. Unlike an AWS Lambda Worker, an AgentCore Worker does not run within a fixed Lambda invocation deadline. Your Runtime handler starts the Worker as background work, and the Worker continues polling until it drains or AgentCore terminates From e8933a8455226d8233932cf4db1fea8c6b1d1c20 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:41:39 -0700 Subject: [PATCH 03/26] Explain AgentCore Worker idle shutdown --- .../serverless-workers-agentcore.mdx | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 1bf519fd50..52b1e8ca21 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -69,23 +69,29 @@ endpoints](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agent-r ## Lifecycle {/* #lifecycle */} -An AgentCore Runtime session is only the compute that runs a Worker. It can end because of its idle timeout or maximum -lifetime. A later Task for the same Workflow can run on another Worker in another Runtime session. Keep state that the -Workflow needs in the Workflow or another durable store, not only in the Runtime session. +An AgentCore Runtime session is the compute that runs a Worker, not a durable place to store Workflow state. AgentCore +can resume a session on new compute after the previous compute ends, and a later Task can run on another Worker. Keep +state that a Workflow needs in the Workflow or another durable store. -Unlike an AWS Lambda Worker, an AgentCore Worker does not run within a fixed Lambda invocation deadline. Your Runtime -handler starts the Worker as background work, and the Worker continues polling until it drains or AgentCore terminates -the Runtime session. +Unlike an AWS Lambda Worker, an AgentCore Worker does not have a fixed Lambda invocation deadline. Your Runtime handler +starts the Worker as background work. The Worker polls until it drains or AgentCore ends its compute. -AgentCore applies lifecycle settings to each microVM Runtime session: +AgentCore has two lifecycle settings: -- **Idle Runtime session timeout**: Ends an idle session. The default is 15 minutes. -- **Maximum lifetime**: Ends a microVM session after a fixed lifetime. The default and maximum is 8 hours. +- **Idle Runtime session timeout**: Ends a Runtime session after it has not received an AgentCore Runtime invocation for + the configured duration. The default is 15 minutes. This is not a Temporal Worker idle timer: polling the Temporal + Service does not reset it. +- **Maximum lifetime**: Ends the compute running a Runtime session after the configured duration. The default and + maximum is 8 hours. AgentCore can resume the session on new compute after that. -Set the Worker’s own idle and graceful-shutdown policy so it stops polling and drains in-flight Tasks before its Runtime -session is no longer needed. The AgentCore maximum lifetime remains a hard termination boundary. Configure Activity -timeouts and, for long-running Activities, [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat) -so a retry can recover after a Worker is interrupted. +Implement a separate idle policy in the Worker to release capacity before either AgentCore limit. The AgentCore Runtime +sample starts a Worker on invocation, then waits for 60 seconds with no Activity starting or completing. It stops +polling, allows up to two minutes for in-flight Activities to complete, and then returns from the Runtime handler. +Choose values that fit your workload, and keep them within the AgentCore maximum lifetime. + +Configure Activity timeouts and, for long-running Activities, +[Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat) so a retry can recover if AgentCore +ends the compute before an Activity completes. For the lifecycle setting ranges and defaults, see [Configure Amazon Bedrock AgentCore lifecycle settings](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-lifecycle-settings.html). From ae4df10671232aa3da034d9723f94cf1f7e105e5 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:45:05 -0700 Subject: [PATCH 04/26] Lead AgentCore lifecycle with Worker shutdown policy --- .../serverless-workers-agentcore.mdx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 52b1e8ca21..5ed2702157 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -76,7 +76,13 @@ state that a Workflow needs in the Workflow or another durable store. Unlike an AWS Lambda Worker, an AgentCore Worker does not have a fixed Lambda invocation deadline. Your Runtime handler starts the Worker as background work. The Worker polls until it drains or AgentCore ends its compute. -AgentCore has two lifecycle settings: +Two sets of controls determine when that Worker stops: + +- **Worker idle and graceful-shutdown policy**: Your Worker implementation decides when it has been idle, stops + polling, and waits for in-flight Activities to complete. +- **AgentCore lifecycle settings**: AgentCore can end the session or its compute before the Worker policy does. + +The AgentCore lifecycle settings are: - **Idle Runtime session timeout**: Ends a Runtime session after it has not received an AgentCore Runtime invocation for the configured duration. The default is 15 minutes. This is not a Temporal Worker idle timer: polling the Temporal @@ -84,10 +90,10 @@ AgentCore has two lifecycle settings: - **Maximum lifetime**: Ends the compute running a Runtime session after the configured duration. The default and maximum is 8 hours. AgentCore can resume the session on new compute after that. -Implement a separate idle policy in the Worker to release capacity before either AgentCore limit. The AgentCore Runtime -sample starts a Worker on invocation, then waits for 60 seconds with no Activity starting or completing. It stops -polling, allows up to two minutes for in-flight Activities to complete, and then returns from the Runtime handler. -Choose values that fit your workload, and keep them within the AgentCore maximum lifetime. +Implement the Worker idle policy to release capacity before either AgentCore limit. The AgentCore Runtime sample starts +a Worker on invocation, then waits for 60 seconds with no Activity starting or completing. It stops polling, allows up +to two minutes for in-flight Activities to complete, and then returns from the Runtime handler. Choose values that fit +your workload, and keep them within the AgentCore maximum lifetime. Configure Activity timeouts and, for long-running Activities, [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat) so a retry can recover if AgentCore From 159c66fec058c6bbacd5ae5756165bdd862410bd Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:46:30 -0700 Subject: [PATCH 05/26] Clarify AgentCore idle policy guidance --- .../serverless-workers/serverless-workers-agentcore.mdx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 5ed2702157..c1d43968ab 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -90,10 +90,11 @@ The AgentCore lifecycle settings are: - **Maximum lifetime**: Ends the compute running a Runtime session after the configured duration. The default and maximum is 8 hours. AgentCore can resume the session on new compute after that. -Implement the Worker idle policy to release capacity before either AgentCore limit. The AgentCore Runtime sample starts -a Worker on invocation, then waits for 60 seconds with no Activity starting or completing. It stops polling, allows up -to two minutes for in-flight Activities to complete, and then returns from the Runtime handler. Choose values that fit -your workload, and keep them within the AgentCore maximum lifetime. +The Worker can use an idle policy based on Temporal work rather than on AgentCore Runtime invocations. The AgentCore +Runtime sample starts a Worker on invocation, then waits for 60 seconds with no Activity starting or completing. It +stops polling, allows up to two minutes for in-flight Activities to complete, and then returns from the Runtime handler. +This is the sample's policy, not a required setting. Choose values that fit your workload and account for the AgentCore +maximum lifetime. Configure Activity timeouts and, for long-running Activities, [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat) so a retry can recover if AgentCore From 8ab5cf93647aca5417587822eddf9ca346538192 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:47:08 -0700 Subject: [PATCH 06/26] Document AgentCore Worker shutdown policy --- .../serverless-workers/serverless-workers-agentcore.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index c1d43968ab..197d2db711 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -90,11 +90,10 @@ The AgentCore lifecycle settings are: - **Maximum lifetime**: Ends the compute running a Runtime session after the configured duration. The default and maximum is 8 hours. AgentCore can resume the session on new compute after that. -The Worker can use an idle policy based on Temporal work rather than on AgentCore Runtime invocations. The AgentCore -Runtime sample starts a Worker on invocation, then waits for 60 seconds with no Activity starting or completing. It -stops polling, allows up to two minutes for in-flight Activities to complete, and then returns from the Runtime handler. -This is the sample's policy, not a required setting. Choose values that fit your workload and account for the AgentCore -maximum lifetime. +AgentCore's session idle timeout does not replace a Worker idle policy. It resets with AgentCore Runtime invocations +and does not measure Task Queue activity. To control how long an unused Worker polls, implement a separate shutdown +policy: when its idle condition is met, stop polling and drain in-flight Activities before the Runtime handler returns. +Choose the idle period and drain timeout for your workload, and account for the AgentCore maximum lifetime. Configure Activity timeouts and, for long-running Activities, [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat) so a retry can recover if AgentCore From 573b08df2557923755011d8c3a3863009d206fb6 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Wed, 9 Sep 2026 14:49:41 -0700 Subject: [PATCH 07/26] Share Serverless Worker autoscaling guidance --- .../workers/serverless-workers/aws-lambda.mdx | 36 ++++++++++++------- .../serverless-workers-agentcore.mdx | 5 ++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers/aws-lambda.mdx b/docs/encyclopedia/workers/serverless-workers/aws-lambda.mdx index 4547762d57..fca5bec7c3 100644 --- a/docs/encyclopedia/workers/serverless-workers/aws-lambda.mdx +++ b/docs/encyclopedia/workers/serverless-workers/aws-lambda.mdx @@ -24,26 +24,38 @@ For a step-by-step deployment guide, see [Deploy a Serverless Worker on AWS Lamb ## Autoscaling {/* #autoscaling */} -The Lambda autoscaling algorithm is event-driven and reactive. +The autoscaling algorithm in this section applies to Serverless Workers on AWS Lambda and Amazon Bedrock AgentCore +Runtime. The compute providers have different Worker lifecycles after a scale-out action. For AgentCore Runtime +lifecycle details, see [Serverless Workers on Amazon Bedrock AgentCore Runtime](/serverless-workers/agentcore#lifecycle). + +The autoscaling algorithm is event-driven and reactive. Sync match failure is the primary control signal, and backlog aids sizing. -When the [WCI](/serverless-workers#worker-controller-instance) needs more capacity, it calls the Lambda `InvokeFunction` API to start new Workers. -Each call is a discrete action ("invoke N more functions"), not a target state. -Temporal calls that API from outside your network, so no inbound connection to the function is needed. -The WCI does not manage a fleet of instances. +When the [WCI](/serverless-workers#worker-controller-instance) needs more capacity, it invokes the compute provider to +start new Workers: the Lambda `InvokeFunction` API for Lambda or an AgentCore Runtime endpoint for AgentCore Runtime. +Each call is a discrete action ("start N more Workers"), not a target state. Temporal calls the provider API from +outside your network, so no inbound connection to the Worker is needed. The WCI does not manage a fleet of instances. ### Scale-out {/* #scale-out */} -On sync match failure, the WCI invokes new Lambda functions. -Because Lambda cold start is sub-second to low single-digit seconds, reactive-only control does not create meaningful backlog overshoot. -The WCI can scale from zero with low latency. +On sync match failure, the WCI starts new Workers through the compute provider API. + +For Lambda, cold start is sub-second to low single-digit seconds, so reactive-only control does not create meaningful +backlog overshoot. The WCI can scale from zero with low latency. + +For AgentCore Runtime startup and session behavior, see +[Lifecycle](/serverless-workers/agentcore#lifecycle). ### Scale-in {/* #scale-in */} -Scale-in is automatic. -Each Lambda invocation runs until the Worker has finished processing available Tasks or approaches the 15-minute execution time limit, then shuts down. -There is no drain logic or stabilization window. -The WCI does not need to actively remove capacity. +The WCI does not maintain a target number of Workers or actively remove capacity. The provider and Worker lifecycle +determine when a Worker stops. + +On Lambda, each invocation runs until the Worker has finished processing available Tasks or approaches the 15-minute +execution time limit, then shuts down. There is no drain logic or stabilization window. + +On AgentCore Runtime, Worker shutdown and AgentCore session lifecycle settings determine when a Worker stops. See +[Lifecycle](/serverless-workers/agentcore#lifecycle). ### Instance model {/* #instance-model */} diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 197d2db711..5414e3a149 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -28,8 +28,11 @@ Runtime session. When the [Worker Controller Instance (WCI)](/serverless-workers capacity, it invokes an AgentCore Runtime endpoint. The Runtime starts a Worker, which connects to the Temporal Service and polls its Task Queue. +## Autoscaling {/* #autoscaling */} + AgentCore Runtime uses the same event-driven autoscaling model as AWS Lambda. The WCI invokes individual Runtime -sessions when it needs more capacity; it does not manage a target-sized pool of Runtime sessions. +sessions when it needs more capacity; it does not manage a target-sized pool of Runtime sessions. For the shared +autoscaling behavior, see [Autoscaling for Serverless Workers on AWS Lambda](/serverless-workers/aws-lambda#autoscaling). ## Worker Versioning {/* #worker-versioning */} From 0c051e26348fb347365af11612b3b991ec7db752 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 09:58:04 -0700 Subject: [PATCH 08/26] Add Python AgentCore Serverless Worker guide --- .../workers/serverless-workers/agentcore.mdx | 156 ++++++++++++++++++ .../workers/serverless-workers/index.mdx | 9 +- sidebars.js | 1 + 3 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 docs/develop/python/workers/serverless-workers/agentcore.mdx diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx new file mode 100644 index 0000000000..052ab68557 --- /dev/null +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -0,0 +1,156 @@ +--- +id: agentcore +title: Serverless Workers on Amazon Bedrock AgentCore Runtime - Python SDK +sidebar_label: Amazon Bedrock AgentCore +description: Run a Temporal Worker on Amazon Bedrock AgentCore Runtime using the Python SDK. +slug: /develop/python/workers/serverless-workers/agentcore +toc_max_heading_level: 4 +tags: + - Workers + - Python SDK + - Serverless + - Amazon Bedrock AgentCore +--- + +import { ReleaseNoteHeader } from '@site/src/components'; + + + Amazon Bedrock AgentCore Runtime support is in Pre-release, and its APIs may change in backwards-incompatible ways. + + +On Amazon Bedrock AgentCore Runtime, you run a standard long-lived Python Worker inside an AgentCore Runtime handler. +Temporal starts the handler when the Worker Controller Instance needs capacity. The handler starts a Worker that polls +the Task Queue, then stops it when your idle policy decides to release capacity. + +The Worker uses the normal Python SDK. The handler uses the `bedrock-agentcore` package to receive AgentCore Runtime +invocations. + +For the provider behavior, including autoscaling, Worker Versioning, and the Runtime session lifecycle, see +[Serverless Workers on Amazon Bedrock AgentCore Runtime](/serverless-workers/agentcore). + +## Install the AgentCore Runtime SDK {/* #install-agentcore-runtime-sdk */} + +Install the AgentCore Runtime SDK alongside the Temporal Python SDK: + +```bash +pip install bedrock-agentcore +``` + +## Create a versioned Worker {/* #versioned-worker */} + +Serverless Workers require [Worker Versioning](/worker-versioning). Create the Worker as you would any long-lived +Python Worker, then set `deployment_config` to declare its Worker Deployment Version and enable versioning: + +```python +import os + +from temporalio.client import Client +from temporalio.common import VersioningBehavior, WorkerDeploymentVersion +from temporalio.worker import Worker, WorkerDeploymentConfig + +from my_activities import my_activity +from my_workflows import MyWorkflow + + +def create_worker(client: Client) -> Worker: + return Worker( + client, + task_queue=os.environ["TEMPORAL_TASK_QUEUE"], + workflows=[MyWorkflow], + activities=[my_activity], + deployment_config=WorkerDeploymentConfig( + version=WorkerDeploymentVersion( + deployment_name=os.environ["TEMPORAL_DEPLOYMENT_NAME"], + build_id=os.environ["TEMPORAL_BUILD_ID"], + ), + use_worker_versioning=True, + default_versioning_behavior=VersioningBehavior.PINNED, + ), + ) +``` + +`TEMPORAL_DEPLOYMENT_NAME` and `TEMPORAL_BUILD_ID` must match the Worker Deployment Version that you create with +`temporal worker deployment create-version`. Configure that Worker Deployment Version with the AgentCore Runtime +endpoint that Temporal invokes. For the endpoint configuration, see +[Worker Versioning](/serverless-workers/agentcore#worker-versioning). + +Every Workflow needs a [versioning behavior](/worker-versioning#versioning-behaviors), either `PINNED` or +`AUTO_UPGRADE`. Setting `default_versioning_behavior` as shown applies `PINNED` behavior to every Workflow on the +Worker. To set the behavior per Workflow instead, pass `versioning_behavior` to the `@workflow.defn` decorator. + +## Start the Worker from the Runtime handler {/* #runtime-handler */} + +AgentCore Runtime invokes an HTTP handler. Use `BedrockAgentCoreApp` to provide that handler, and use `async_task` so +AgentCore keeps the Runtime active while the Worker polls. The following is a handler pattern; its idle policy is +application-specific: + +```python +from bedrock_agentcore.runtime import BedrockAgentCoreApp +from temporalio.client import Client +from temporalio.envconfig import ClientConfig + +app = BedrockAgentCoreApp() + + +@app.entrypoint +@app.async_task +async def invoke(_: dict) -> dict: + client = await Client.connect(**ClientConfig.load_client_connect_config()) + worker = create_worker(client) + + async with worker: + # Implement how your application decides that this Worker is idle. + await wait_until_idle() + + return {"message": "Worker drained"} +``` + +The payload does not represent a Workflow input. The Worker Controller Instance invokes the endpoint to add Worker +capacity. Applications start Workflows through the Temporal Client, as usual. + +`wait_until_idle()` represents the idle policy that you define for the Worker. See +[Stop and drain the Worker](#stop-and-drain-the-worker). + +## Configure the Temporal connection {/* #configure-connection */} + +The `temporalio.envconfig` package loads [Temporal Client](/develop/python/client/temporal-client) configuration from +environment variables and an optional TOML configuration file. Set the Temporal address, Namespace, Task Queue, and +Worker Deployment Version values as Runtime environment variables. Store a Temporal Cloud API key or TLS material in a +secret store rather than in the Runtime definition. + +For the supported connection variables, config-file format, and profiles, see +[Environment configuration](/develop/environment-configuration). + +## Stop and drain the Worker {/* #stop-and-drain-the-worker */} + +An AgentCore Runtime invocation is not a measure of Task Queue idleness. Add an idle policy that decides when the +Worker should stop polling. When that condition is met, leave the `async with worker` block. The Python SDK stops +polling and waits for in-flight Activities to finish before the handler returns. + +Set `graceful_shutdown_timeout` on `Worker()` to limit how long the Worker waits for in-flight Activities. Choose an +idle period and shutdown timeout that fit the workload, and account for AgentCore's maximum Runtime lifetime. For the +AgentCore lifecycle settings, see [Lifecycle](/serverless-workers/agentcore#lifecycle). + +## Keep Activities safe across Worker termination {/* #activity-recovery */} + +AgentCore can end the compute that runs a Worker. An Activity running at that time can be interrupted and retried. +Use [Activity Heartbeats](/develop/python/activities/timeouts#activity-heartbeats) so a retry resumes from its last +recorded progress instead of starting over: + +```python +from temporalio import activity + + +@activity.defn +async def my_activity(items: list[str]) -> str: + for i, item in enumerate(items): + activity.heartbeat(i) + # ... process item + return "done" +``` + +## Add observability {/* #add-observability */} + +An AgentCore Runtime Worker emits the same traces and metrics as a Worker on other compute. For metrics export and +OpenTelemetry tracing interceptors, see [Observability - Python SDK](/develop/python/platform/observability) and the +[SDK metrics reference](/references/sdk-metrics). diff --git a/docs/develop/python/workers/serverless-workers/index.mdx b/docs/develop/python/workers/serverless-workers/index.mdx index 6a1299e5cc..c0d1d55f52 100644 --- a/docs/develop/python/workers/serverless-workers/index.mdx +++ b/docs/develop/python/workers/serverless-workers/index.mdx @@ -14,10 +14,10 @@ tags: import { ReleaseNoteHeader } from '@site/src/components'; - AWS Lambda support is in Public Preview. GCP Cloud Run support is in Pre-release, and its APIs may change in - backwards-incompatible ways. To request Cloud Run access, create a [support ticket](/cloud/support#support-ticket) or - contact your account team, and [sign up for updates](https://temporal.io/pages/serverless-workers-updates) to hear - when Cloud Run reaches Public Preview. + AWS Lambda support is in Public Preview. Amazon Bedrock AgentCore Runtime and GCP Cloud Run support are in + Pre-release, and their APIs may change in backwards-incompatible ways. To request Cloud Run access, create a + [support ticket](/cloud/support#support-ticket) or contact your account team, and + [sign up for updates](https://temporal.io/pages/serverless-workers-updates) to hear when Cloud Run reaches Public Preview. Serverless Workers run on ephemeral, on-demand compute rather than long-lived processes. @@ -29,4 +29,5 @@ For the end-to-end deployment guide, see [Deploy a Serverless Worker](/productio ## Supported providers - [**AWS Lambda**](/develop/python/workers/serverless-workers/aws-lambda) - Use the `lambda_worker` contrib package to run a Worker as a Lambda function. Covers setup, configuration, Lambda-tuned defaults, and observability. +- [**Amazon Bedrock AgentCore Runtime**](/develop/python/workers/serverless-workers/agentcore) - Run a standard Worker from an AgentCore Runtime handler. Covers the handler, Worker Versioning, connection configuration, and Worker shutdown. - [**GCP Cloud Run**](/develop/python/workers/serverless-workers/cloud-run) - Run a standard Worker on a Cloud Run worker pool. Covers the versioned Worker setup, connection configuration, and handling scale-in. diff --git a/sidebars.js b/sidebars.js index a939fb2209..35f2165256 100644 --- a/sidebars.js +++ b/sidebars.js @@ -638,6 +638,7 @@ const developPythonCategory = { }, items: [ 'develop/python/workers/serverless-workers/aws-lambda', + 'develop/python/workers/serverless-workers/agentcore', 'develop/python/workers/serverless-workers/cloud-run', ], }, From 3a4455c8cad856301af89b0415f160544fdb4da0 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 11:22:49 -0700 Subject: [PATCH 09/26] Add AgentCore Worker idle policy example --- .../workers/serverless-workers/agentcore.mdx | 83 +++++++++++++++---- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 052ab68557..467d9170cd 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -12,7 +12,7 @@ tags: - Amazon Bedrock AgentCore --- -import { ReleaseNoteHeader } from '@site/src/components'; +import { ReleaseNoteHeader } from '@site/src/components' Amazon Bedrock AgentCore Runtime support is in Pre-release, and its APIs may change in backwards-incompatible ways. @@ -42,22 +42,70 @@ Serverless Workers require [Worker Versioning](/worker-versioning). Create the W Python Worker, then set `deployment_config` to declare its Worker Deployment Version and enable versioning: ```python +import asyncio import os +from datetime import timedelta from temporalio.client import Client from temporalio.common import VersioningBehavior, WorkerDeploymentVersion -from temporalio.worker import Worker, WorkerDeploymentConfig +from temporalio.worker import ( + ActivityInboundInterceptor, + ExecuteActivityInput, + Interceptor, + Worker, + WorkerDeploymentConfig, +) from my_activities import my_activity from my_workflows import MyWorkflow +DEBOUNCE = float(os.environ.get("AGENTCORE_DEBOUNCE_SECONDS", "60")) +DRAIN = timedelta(seconds=120) -def create_worker(client: Client) -> Worker: + +class ActivityTracker(Interceptor): + def __init__(self) -> None: + self.inflight = 0 + self.changed = asyncio.Event() + + def intercept_activity( + self, next: ActivityInboundInterceptor + ) -> ActivityInboundInterceptor: + return TrackedActivity(next, self) + + async def wait_until_idle(self, debounce: float) -> None: + while True: + self.changed.clear() + try: + await asyncio.wait_for(self.changed.wait(), timeout=debounce) + except asyncio.TimeoutError: + if self.inflight == 0: + return + + +class TrackedActivity(ActivityInboundInterceptor): + def __init__(self, next: ActivityInboundInterceptor, tracker: ActivityTracker): + super().__init__(next) + self.tracker = tracker + + async def execute_activity(self, input: ExecuteActivityInput): + self.tracker.inflight += 1 + self.tracker.changed.set() + try: + return await self.next.execute_activity(input) + finally: + self.tracker.inflight -= 1 + self.tracker.changed.set() + + +def create_worker(client: Client, tracker: ActivityTracker) -> Worker: return Worker( client, task_queue=os.environ["TEMPORAL_TASK_QUEUE"], workflows=[MyWorkflow], activities=[my_activity], + interceptors=[tracker], + graceful_shutdown_timeout=DRAIN, deployment_config=WorkerDeploymentConfig( version=WorkerDeploymentVersion( deployment_name=os.environ["TEMPORAL_DEPLOYMENT_NAME"], @@ -81,8 +129,7 @@ Worker. To set the behavior per Workflow instead, pass `versioning_behavior` to ## Start the Worker from the Runtime handler {/* #runtime-handler */} AgentCore Runtime invokes an HTTP handler. Use `BedrockAgentCoreApp` to provide that handler, and use `async_task` so -AgentCore keeps the Runtime active while the Worker polls. The following is a handler pattern; its idle policy is -application-specific: +AgentCore keeps the Runtime active while the Worker polls: ```python from bedrock_agentcore.runtime import BedrockAgentCoreApp @@ -96,11 +143,11 @@ app = BedrockAgentCoreApp() @app.async_task async def invoke(_: dict) -> dict: client = await Client.connect(**ClientConfig.load_client_connect_config()) - worker = create_worker(client) + tracker = ActivityTracker() + worker = create_worker(client, tracker) async with worker: - # Implement how your application decides that this Worker is idle. - await wait_until_idle() + await tracker.wait_until_idle(DEBOUNCE) return {"message": "Worker drained"} ``` @@ -108,9 +155,6 @@ async def invoke(_: dict) -> dict: The payload does not represent a Workflow input. The Worker Controller Instance invokes the endpoint to add Worker capacity. Applications start Workflows through the Temporal Client, as usual. -`wait_until_idle()` represents the idle policy that you define for the Worker. See -[Stop and drain the Worker](#stop-and-drain-the-worker). - ## Configure the Temporal connection {/* #configure-connection */} The `temporalio.envconfig` package loads [Temporal Client](/develop/python/client/temporal-client) configuration from @@ -123,13 +167,18 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} -An AgentCore Runtime invocation is not a measure of Task Queue idleness. Add an idle policy that decides when the -Worker should stop polling. When that condition is met, leave the `async with worker` block. The Python SDK stops -polling and waits for in-flight Activities to finish before the handler returns. +The example uses `ActivityTracker` as its idle policy. It starts a 60-second timer when no Activity is running. Starting +or completing an Activity resets the timer. When the timer expires, the Worker leaves the `async with` block, stops +polling, and waits up to two minutes for in-flight Activities to complete. + +This policy is appropriate when Activities represent the work that should keep the Worker available. For example, the +AgentCore sample runs model and tool calls as Activities. It is not a universal definition of idleness. If your Worker +has a different signal for useful work, use that signal instead. -Set `graceful_shutdown_timeout` on `Worker()` to limit how long the Worker waits for in-flight Activities. Choose an -idle period and shutdown timeout that fit the workload, and account for AgentCore's maximum Runtime lifetime. For the -AgentCore lifecycle settings, see [Lifecycle](/serverless-workers/agentcore#lifecycle). +`AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits +for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's +maximum Runtime lifetime. For the AgentCore lifecycle settings, see +[Lifecycle](/serverless-workers/agentcore#lifecycle). ## Keep Activities safe across Worker termination {/* #activity-recovery */} From 13fcfd41fa906087870251742ff43dc55971ae74 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 11:48:57 -0700 Subject: [PATCH 10/26] Use periods in AgentCore prose --- .../workers/serverless-workers/serverless-workers-agentcore.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 5414e3a149..9fd3328801 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -31,7 +31,7 @@ and polls its Task Queue. ## Autoscaling {/* #autoscaling */} AgentCore Runtime uses the same event-driven autoscaling model as AWS Lambda. The WCI invokes individual Runtime -sessions when it needs more capacity; it does not manage a target-sized pool of Runtime sessions. For the shared +sessions when it needs more capacity. It does not manage a target-sized pool of Runtime sessions. For the shared autoscaling behavior, see [Autoscaling for Serverless Workers on AWS Lambda](/serverless-workers/aws-lambda#autoscaling). ## Worker Versioning {/* #worker-versioning */} From 0817f1688acca8187cdd7f6497738c24cc369ad5 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:04:23 -0700 Subject: [PATCH 11/26] Make AgentCore idle policy self-contained --- .../workers/serverless-workers/agentcore.mdx | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 467d9170cd..6cf649d180 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -167,13 +167,27 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} -The example uses `ActivityTracker` as its idle policy. It starts a 60-second timer when no Activity is running. Starting -or completing an Activity resets the timer. When the timer expires, the Worker leaves the `async with` block, stops -polling, and waits up to two minutes for in-flight Activities to complete. +Use `ActivityTracker` to keep a Worker available while it runs Activities and stop it after a period with no Activity +work: -This policy is appropriate when Activities represent the work that should keep the Worker available. For example, the -AgentCore sample runs model and tool calls as Activities. It is not a universal definition of idleness. If your Worker -has a different signal for useful work, use that signal instead. +```python +DEBOUNCE = float(os.environ.get("AGENTCORE_DEBOUNCE_SECONDS", "60")) +DRAIN = timedelta(seconds=120) + +tracker = ActivityTracker() +worker = create_worker(client, tracker) + +async with worker: + await tracker.wait_until_idle(DEBOUNCE) +``` + +`ActivityTracker` starts a 60-second timer when no Activity is running. Starting or completing an Activity resets the +timer. When the timer expires, the Worker leaves the `async with` block, stops polling, and waits up to two minutes for +in-flight Activities to complete. + +This policy is appropriate when Activities represent the work that should keep the Worker available. The AgentCore +sample runs model and tool calls as Activities. It is not a universal definition of idleness. If your Worker has a +different signal for useful work, use that signal instead. `AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's From 9f1d16e4c4b6c4ef47683ec59335bb16ad1321a3 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:13:33 -0700 Subject: [PATCH 12/26] Give an AgentCore Worker idle signal example --- docs/develop/python/workers/serverless-workers/agentcore.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 6cf649d180..d30cf38927 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -187,7 +187,9 @@ in-flight Activities to complete. This policy is appropriate when Activities represent the work that should keep the Worker available. The AgentCore sample runs model and tool calls as Activities. It is not a universal definition of idleness. If your Worker has a -different signal for useful work, use that signal instead. +different signal, use that instead. For example, a Worker that performs meaningful Workflow Task work without +scheduling Activities can track active Workflow Task handlers as well as Activities. Start its idle timer only when +both counts reach zero. `AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's From 702f80186ec4d9db6b3e101fb375b50618b55d4f Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:14:34 -0700 Subject: [PATCH 13/26] Remove unsupported Workflow Task idle guidance --- docs/develop/python/workers/serverless-workers/agentcore.mdx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index d30cf38927..a2afe5734b 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -187,9 +187,7 @@ in-flight Activities to complete. This policy is appropriate when Activities represent the work that should keep the Worker available. The AgentCore sample runs model and tool calls as Activities. It is not a universal definition of idleness. If your Worker has a -different signal, use that instead. For example, a Worker that performs meaningful Workflow Task work without -scheduling Activities can track active Workflow Task handlers as well as Activities. Start its idle timer only when -both counts reach zero. +different signal, implement an idle policy based on that signal. `AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's From b0e0e5e9168627331a29da4dd70865cd1b5afd25 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:18:16 -0700 Subject: [PATCH 14/26] Add fixed lease idle policy example --- .../workers/serverless-workers/agentcore.mdx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index a2afe5734b..538b3134cc 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -186,8 +186,21 @@ timer. When the timer expires, the Worker leaves the `async with` block, stops p in-flight Activities to complete. This policy is appropriate when Activities represent the work that should keep the Worker available. The AgentCore -sample runs model and tool calls as Activities. It is not a universal definition of idleness. If your Worker has a -different signal, implement an idle policy based on that signal. +sample runs model and tool calls as Activities. It is not a universal definition of idleness. + +If your workload does not have a useful Activity-based signal, use a fixed Worker lease instead. Start a lease when +AgentCore invokes the Runtime. When it expires, stop polling and drain the Worker: + +```python +LEASE_SECONDS = 300 + +async with worker: + await asyncio.sleep(LEASE_SECONDS) +``` + +A shorter lease releases capacity sooner but causes more Worker starts. A longer lease keeps capacity available longer +but can leave it idle. After the Worker drains, the next Task that needs capacity causes the WCI to invoke another +Runtime session. `AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's From a47de75c1b179e409654234a7f9942a6a78f0e04 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:19:21 -0700 Subject: [PATCH 15/26] Remove unsupported AgentCore lease guidance --- .../workers/serverless-workers/agentcore.mdx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 538b3134cc..51557d0c0c 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -188,20 +188,6 @@ in-flight Activities to complete. This policy is appropriate when Activities represent the work that should keep the Worker available. The AgentCore sample runs model and tool calls as Activities. It is not a universal definition of idleness. -If your workload does not have a useful Activity-based signal, use a fixed Worker lease instead. Start a lease when -AgentCore invokes the Runtime. When it expires, stop polling and drain the Worker: - -```python -LEASE_SECONDS = 300 - -async with worker: - await asyncio.sleep(LEASE_SECONDS) -``` - -A shorter lease releases capacity sooner but causes more Worker starts. A longer lease keeps capacity available longer -but can leave it idle. After the Worker drains, the next Task that needs capacity causes the WCI to invoke another -Runtime session. - `AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's maximum Runtime lifetime. For the AgentCore lifecycle settings, see From ad43d78d623fa8212c5da928365669c778bdd740 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:21:09 -0700 Subject: [PATCH 16/26] Clarify Python Worker idle detection --- docs/develop/python/workers/serverless-workers/agentcore.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 51557d0c0c..fc8dc1258d 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -167,6 +167,10 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} +The Python SDK provides Worker shutdown and draining. It does not provide a general signal that a Worker is idle. +`ActivityTracker` is application code that uses an [Activity inbound Interceptor](/develop/python/workers/interceptors) +to define that signal. + Use `ActivityTracker` to keep a Worker available while it runs Activities and stop it after a period with no Activity work: From 9f07493df5cfbfb05126dff2a7df6bd7a1683f6e Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:26:59 -0700 Subject: [PATCH 17/26] Explain AgentCore sample ActivityTracker --- .../develop/python/workers/serverless-workers/agentcore.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index fc8dc1258d..30d597eb4e 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -167,9 +167,9 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} -The Python SDK provides Worker shutdown and draining. It does not provide a general signal that a Worker is idle. -`ActivityTracker` is application code that uses an [Activity inbound Interceptor](/develop/python/workers/interceptors) -to define that signal. +`ActivityTracker` is custom code from the AgentCore sample. It uses an +[Activity inbound Interceptor](/develop/python/workers/interceptors) to count Activity work and decide when the Worker +can stop. Use `ActivityTracker` to keep a Worker available while it runs Activities and stop it after a period with no Activity work: From 60d919a88616518b7e0063df785d6f0cff8275eb Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:30:14 -0700 Subject: [PATCH 18/26] Explain AgentCore Worker retirement policies --- .../workers/serverless-workers/agentcore.mdx | 148 ++++++++++-------- 1 file changed, 81 insertions(+), 67 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 30d597eb4e..54c478f9c5 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -41,13 +41,81 @@ pip install bedrock-agentcore Serverless Workers require [Worker Versioning](/worker-versioning). Create the Worker as you would any long-lived Python Worker, then set `deployment_config` to declare its Worker Deployment Version and enable versioning: +```python +import os + +from temporalio.client import Client +from temporalio.common import VersioningBehavior, WorkerDeploymentVersion +from temporalio.worker import Worker, WorkerDeploymentConfig + +from my_activities import my_activity +from my_workflows import MyWorkflow + +def create_worker(client: Client) -> Worker: + return Worker( + client, + task_queue=os.environ["TEMPORAL_TASK_QUEUE"], + workflows=[MyWorkflow], + activities=[my_activity], + deployment_config=WorkerDeploymentConfig( + version=WorkerDeploymentVersion( + deployment_name=os.environ["TEMPORAL_DEPLOYMENT_NAME"], + build_id=os.environ["TEMPORAL_BUILD_ID"], + ), + use_worker_versioning=True, + default_versioning_behavior=VersioningBehavior.PINNED, + ), + ) +``` + +`TEMPORAL_DEPLOYMENT_NAME` and `TEMPORAL_BUILD_ID` must match the Worker Deployment Version that you create with +`temporal worker deployment create-version`. Configure that Worker Deployment Version with the AgentCore Runtime +endpoint that Temporal invokes. For the endpoint configuration, see +[Worker Versioning](/serverless-workers/agentcore#worker-versioning). + +Every Workflow needs a [versioning behavior](/worker-versioning#versioning-behaviors), either `PINNED` or +`AUTO_UPGRADE`. Setting `default_versioning_behavior` as shown applies `PINNED` behavior to every Workflow on the +Worker. To set the behavior per Workflow instead, pass `versioning_behavior` to the `@workflow.defn` decorator. + +## Start the Worker from the Runtime handler {/* #runtime-handler */} + +AgentCore Runtime invokes an HTTP handler. Use `BedrockAgentCoreApp` to provide that handler, and use `async_task` so +AgentCore keeps the Runtime active while the Worker polls. The complete handler in +[Stop and drain the Worker](#stop-and-drain-the-worker) shows how to add a retirement policy. + +The payload does not represent a Workflow input. The Worker Controller Instance invokes the endpoint to add Worker +capacity. Applications start Workflows through the Temporal Client, as usual. + +## Configure the Temporal connection {/* #configure-connection */} + +The `temporalio.envconfig` package loads [Temporal Client](/develop/python/client/temporal-client) configuration from +environment variables and an optional TOML configuration file. Set the Temporal address, Namespace, Task Queue, and +Worker Deployment Version values as Runtime environment variables. Store a Temporal Cloud API key or TLS material in a +secret store rather than in the Runtime definition. + +For the supported connection variables, config-file format, and profiles, see +[Environment configuration](/develop/environment-configuration). + +## Stop and drain the Worker {/* #stop-and-drain-the-worker */} + +Decide what condition means that a Worker can retire. Observe that condition in the Runtime handler. When it remains +true for an idle period, stop polling and drain the Worker. The Temporal Python SDK handles the draining after you leave +the `async with worker` block. + +`ActivityTracker` is one example. It is custom code from the AgentCore sample. It uses an +[Activity inbound Interceptor](/develop/python/workers/interceptors) to count running Activities. This is appropriate +when Activities represent work that should keep the Worker available, such as the model and tool calls in the AgentCore +sample. + ```python import asyncio import os from datetime import timedelta +from bedrock_agentcore.runtime import BedrockAgentCoreApp from temporalio.client import Client from temporalio.common import VersioningBehavior, WorkerDeploymentVersion +from temporalio.envconfig import ClientConfig from temporalio.worker import ( ActivityInboundInterceptor, ExecuteActivityInput, @@ -98,8 +166,15 @@ class TrackedActivity(ActivityInboundInterceptor): self.tracker.changed.set() -def create_worker(client: Client, tracker: ActivityTracker) -> Worker: - return Worker( +app = BedrockAgentCoreApp() + + +@app.entrypoint +@app.async_task +async def invoke(_: dict) -> dict: + client = await Client.connect(**ClientConfig.load_client_connect_config()) + tracker = ActivityTracker() + worker = Worker( client, task_queue=os.environ["TEMPORAL_TASK_QUEUE"], workflows=[MyWorkflow], @@ -115,36 +190,6 @@ def create_worker(client: Client, tracker: ActivityTracker) -> Worker: default_versioning_behavior=VersioningBehavior.PINNED, ), ) -``` - -`TEMPORAL_DEPLOYMENT_NAME` and `TEMPORAL_BUILD_ID` must match the Worker Deployment Version that you create with -`temporal worker deployment create-version`. Configure that Worker Deployment Version with the AgentCore Runtime -endpoint that Temporal invokes. For the endpoint configuration, see -[Worker Versioning](/serverless-workers/agentcore#worker-versioning). - -Every Workflow needs a [versioning behavior](/worker-versioning#versioning-behaviors), either `PINNED` or -`AUTO_UPGRADE`. Setting `default_versioning_behavior` as shown applies `PINNED` behavior to every Workflow on the -Worker. To set the behavior per Workflow instead, pass `versioning_behavior` to the `@workflow.defn` decorator. - -## Start the Worker from the Runtime handler {/* #runtime-handler */} - -AgentCore Runtime invokes an HTTP handler. Use `BedrockAgentCoreApp` to provide that handler, and use `async_task` so -AgentCore keeps the Runtime active while the Worker polls: - -```python -from bedrock_agentcore.runtime import BedrockAgentCoreApp -from temporalio.client import Client -from temporalio.envconfig import ClientConfig - -app = BedrockAgentCoreApp() - - -@app.entrypoint -@app.async_task -async def invoke(_: dict) -> dict: - client = await Client.connect(**ClientConfig.load_client_connect_config()) - tracker = ActivityTracker() - worker = create_worker(client, tracker) async with worker: await tracker.wait_until_idle(DEBOUNCE) @@ -152,45 +197,14 @@ async def invoke(_: dict) -> dict: return {"message": "Worker drained"} ``` -The payload does not represent a Workflow input. The Worker Controller Instance invokes the endpoint to add Worker -capacity. Applications start Workflows through the Temporal Client, as usual. - -## Configure the Temporal connection {/* #configure-connection */} - -The `temporalio.envconfig` package loads [Temporal Client](/develop/python/client/temporal-client) configuration from -environment variables and an optional TOML configuration file. Set the Temporal address, Namespace, Task Queue, and -Worker Deployment Version values as Runtime environment variables. Store a Temporal Cloud API key or TLS material in a -secret store rather than in the Runtime definition. - -For the supported connection variables, config-file format, and profiles, see -[Environment configuration](/develop/environment-configuration). - -## Stop and drain the Worker {/* #stop-and-drain-the-worker */} - -`ActivityTracker` is custom code from the AgentCore sample. It uses an -[Activity inbound Interceptor](/develop/python/workers/interceptors) to count Activity work and decide when the Worker -can stop. - -Use `ActivityTracker` to keep a Worker available while it runs Activities and stop it after a period with no Activity -work: - -```python -DEBOUNCE = float(os.environ.get("AGENTCORE_DEBOUNCE_SECONDS", "60")) -DRAIN = timedelta(seconds=120) - -tracker = ActivityTracker() -worker = create_worker(client, tracker) - -async with worker: - await tracker.wait_until_idle(DEBOUNCE) -``` - `ActivityTracker` starts a 60-second timer when no Activity is running. Starting or completing an Activity resets the timer. When the timer expires, the Worker leaves the `async with` block, stops polling, and waits up to two minutes for in-flight Activities to complete. -This policy is appropriate when Activities represent the work that should keep the Worker available. The AgentCore -sample runs model and tool calls as Activities. It is not a universal definition of idleness. +Memory pressure can be another retirement condition. For example, the Runtime handler can monitor process memory and +initiate the same graceful shutdown when usage crosses a threshold. Memory usage is not an idle signal. It tells you +when to recycle a Worker, not whether it has work to do. Test any memory-based policy against the Runtime's memory +limit and your Activity retry behavior. `AGENTCORE_DEBOUNCE_SECONDS` controls the idle period. `graceful_shutdown_timeout` controls how long the Worker waits for in-flight Activities after it stops polling. Choose both values for your workload, and account for AgentCore's From 162b2379c31e408d55282245a55520857e90a0b4 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:33:29 -0700 Subject: [PATCH 19/26] Explain ActivityTracker idle behavior --- .../python/workers/serverless-workers/agentcore.mdx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 54c478f9c5..250d25464d 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -102,10 +102,8 @@ Decide what condition means that a Worker can retire. Observe that condition in true for an idle period, stop polling and drain the Worker. The Temporal Python SDK handles the draining after you leave the `async with worker` block. -`ActivityTracker` is one example. It is custom code from the AgentCore sample. It uses an -[Activity inbound Interceptor](/develop/python/workers/interceptors) to count running Activities. This is appropriate -when Activities represent work that should keep the Worker available, such as the model and tool calls in the AgentCore -sample. +The following example defines an `ActivityTracker`. It uses an +[Activity inbound Interceptor](/develop/python/workers/interceptors) to count running Activities. ```python import asyncio @@ -197,9 +195,10 @@ async def invoke(_: dict) -> dict: return {"message": "Worker drained"} ``` -`ActivityTracker` starts a 60-second timer when no Activity is running. Starting or completing an Activity resets the -timer. When the timer expires, the Worker leaves the `async with` block, stops polling, and waits up to two minutes for -in-flight Activities to complete. +`ActivityTracker` retires the Worker only after 60 seconds without an Activity starting or completing and with no +Activity running. A long-running Activity keeps the count above zero, so the Worker continues polling. After that +Activity completes, the tracker waits another 60 seconds before it retires the Worker. The two-minute +`graceful_shutdown_timeout` is a safety limit for any Activity still in flight when shutdown starts. Memory pressure can be another retirement condition. For example, the Runtime handler can monitor process memory and initiate the same graceful shutdown when usage crosses a threshold. Memory usage is not an idle signal. It tells you From a3752a61ac06f4a61fab271fe4a0c42525079a49 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:34:54 -0700 Subject: [PATCH 20/26] State exact AgentCore idle condition --- docs/develop/python/workers/serverless-workers/agentcore.mdx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 250d25464d..2abf7f8ec5 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -196,9 +196,8 @@ async def invoke(_: dict) -> dict: ``` `ActivityTracker` retires the Worker only after 60 seconds without an Activity starting or completing and with no -Activity running. A long-running Activity keeps the count above zero, so the Worker continues polling. After that -Activity completes, the tracker waits another 60 seconds before it retires the Worker. The two-minute -`graceful_shutdown_timeout` is a safety limit for any Activity still in flight when shutdown starts. +Activity running. A long-running Activity keeps the count above zero, so the idle policy does not interrupt it. The +two-minute `graceful_shutdown_timeout` is a safety limit for any Activity still in flight when shutdown starts. Memory pressure can be another retirement condition. For example, the Runtime handler can monitor process memory and initiate the same graceful shutdown when usage crosses a threshold. Memory usage is not an idle signal. It tells you From b5d85dc8afbf2a69ce6fa31db5d82c1f498f11b6 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 12:49:16 -0700 Subject: [PATCH 21/26] Focus AgentCore Worker lifecycle examples --- .../workers/serverless-workers/agentcore.mdx | 133 ++++++++---------- 1 file changed, 57 insertions(+), 76 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 2abf7f8ec5..5bb2d8ae9b 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -42,30 +42,17 @@ Serverless Workers require [Worker Versioning](/worker-versioning). Create the W Python Worker, then set `deployment_config` to declare its Worker Deployment Version and enable versioning: ```python -import os - -from temporalio.client import Client -from temporalio.common import VersioningBehavior, WorkerDeploymentVersion -from temporalio.worker import Worker, WorkerDeploymentConfig - -from my_activities import my_activity -from my_workflows import MyWorkflow - -def create_worker(client: Client) -> Worker: - return Worker( - client, - task_queue=os.environ["TEMPORAL_TASK_QUEUE"], - workflows=[MyWorkflow], - activities=[my_activity], - deployment_config=WorkerDeploymentConfig( - version=WorkerDeploymentVersion( - deployment_name=os.environ["TEMPORAL_DEPLOYMENT_NAME"], - build_id=os.environ["TEMPORAL_BUILD_ID"], - ), - use_worker_versioning=True, - default_versioning_behavior=VersioningBehavior.PINNED, +worker = Worker( + # ... + deployment_config=WorkerDeploymentConfig( + version=WorkerDeploymentVersion( + deployment_name=DEPLOYMENT_NAME, + build_id=BUILD_ID, ), - ) + use_worker_versioning=True, + default_versioning_behavior=VersioningBehavior.PINNED, + ), +) ``` `TEMPORAL_DEPLOYMENT_NAME` and `TEMPORAL_BUILD_ID` must match the Worker Deployment Version that you create with @@ -80,8 +67,29 @@ Worker. To set the behavior per Workflow instead, pass `versioning_behavior` to ## Start the Worker from the Runtime handler {/* #runtime-handler */} AgentCore Runtime invokes an HTTP handler. Use `BedrockAgentCoreApp` to provide that handler, and use `async_task` so -AgentCore keeps the Runtime active while the Worker polls. The complete handler in -[Stop and drain the Worker](#stop-and-drain-the-worker) shows how to add a retirement policy. +AgentCore keeps the Runtime active while the Worker polls: + +```python +@app.entrypoint +@app.async_task +async def invoke(payload: dict) -> dict: + client = await Client.connect(...) + tracker = ActivityTracker() + worker = Worker( + client, + task_queue=task_queue, + workflows=[MyWorkflow], + activities=[my_activity], + interceptors=[tracker], + graceful_shutdown_timeout=DRAIN, + deployment_config=deployment_config, + ) + + async with worker: + await tracker.wait_until_idle(DEBOUNCE) + + return {"message": "Worker drained"} +``` The payload does not represent a Workflow input. The Worker Controller Instance invokes the endpoint to add Worker capacity. Applications start Workflows through the Temporal Client, as usual. @@ -98,33 +106,22 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} -Decide what condition means that a Worker can retire. Observe that condition in the Runtime handler. When it remains -true for an idle period, stop polling and drain the Worker. The Temporal Python SDK handles the draining after you leave -the `async with worker` block. +The `async_task` decorator reports the Runtime as busy while the handler is running. If a Worker continues polling after +the available Temporal work is complete, AgentCore cannot tell from the handler status that the Worker is no longer +needed. The compute can remain active until it reaches its maximum lifetime, which is eight hours by default. -The following example defines an `ActivityTracker`. It uses an -[Activity inbound Interceptor](/develop/python/workers/interceptors) to count running Activities. +To release unused capacity sooner, decide how the Worker recognizes that it has no useful work. Observe that condition +in the Runtime handler. -```python -import asyncio -import os -from datetime import timedelta - -from bedrock_agentcore.runtime import BedrockAgentCoreApp -from temporalio.client import Client -from temporalio.common import VersioningBehavior, WorkerDeploymentVersion -from temporalio.envconfig import ClientConfig -from temporalio.worker import ( - ActivityInboundInterceptor, - ExecuteActivityInput, - Interceptor, - Worker, - WorkerDeploymentConfig, -) +When the condition remains true for an idle period, leave the `async with worker` block. The Worker stops polling for +new Tasks and gives in-flight Activities time to complete before the Runtime handler returns. -from my_activities import my_activity -from my_workflows import MyWorkflow +The following example from the +[AgentCore sample Worker](https://github.com/temporalio/samples-python/blob/77768ed87676359823736a40b68ca988a7344543/bedrock_agentcore/strands-agent/agentcore_worker.py) +defines an `ActivityTracker`. It uses an [Activity inbound Interceptor](/develop/python/workers/interceptors) to count +running Activities. +```python DEBOUNCE = float(os.environ.get("AGENTCORE_DEBOUNCE_SECONDS", "60")) DRAIN = timedelta(seconds=120) @@ -162,37 +159,21 @@ class TrackedActivity(ActivityInboundInterceptor): finally: self.tracker.inflight -= 1 self.tracker.changed.set() +``` +Register the tracker as a Worker Interceptor and wait for it inside the Worker context: -app = BedrockAgentCoreApp() - - -@app.entrypoint -@app.async_task -async def invoke(_: dict) -> dict: - client = await Client.connect(**ClientConfig.load_client_connect_config()) - tracker = ActivityTracker() - worker = Worker( - client, - task_queue=os.environ["TEMPORAL_TASK_QUEUE"], - workflows=[MyWorkflow], - activities=[my_activity], - interceptors=[tracker], - graceful_shutdown_timeout=DRAIN, - deployment_config=WorkerDeploymentConfig( - version=WorkerDeploymentVersion( - deployment_name=os.environ["TEMPORAL_DEPLOYMENT_NAME"], - build_id=os.environ["TEMPORAL_BUILD_ID"], - ), - use_worker_versioning=True, - default_versioning_behavior=VersioningBehavior.PINNED, - ), - ) - - async with worker: - await tracker.wait_until_idle(DEBOUNCE) +```python +tracker = ActivityTracker() +worker = Worker( + client, + # ... + interceptors=[tracker], + graceful_shutdown_timeout=DRAIN, +) - return {"message": "Worker drained"} +async with worker: + await tracker.wait_until_idle(DEBOUNCE) ``` `ActivityTracker` retires the Worker only after 60 seconds without an Activity starting or completing and with no From 167adcb1c5bc37c9b5a39e5cb037a42aa8ad40ec Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 13:01:06 -0700 Subject: [PATCH 22/26] Sync AgentCore Worker examples from sample --- .../workers/serverless-workers/agentcore.mdx | 86 +++++++++++++------ 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 5bb2d8ae9b..08ef2625d6 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -69,27 +69,50 @@ Worker. To set the behavior per Workflow instead, pass `versioning_behavior` to AgentCore Runtime invokes an HTTP handler. Use `BedrockAgentCoreApp` to provide that handler, and use `async_task` so AgentCore keeps the Runtime active while the Worker polls: -```python + +[bedrock_agentcore/strands-agent/agentcore_worker.py](https://github.com/temporalio/samples-python/blob/5b0fe65efe934388d35eda430fb11df6899ce1d3/bedrock_agentcore/strands-agent/agentcore_worker.py) +```py @app.entrypoint -@app.async_task +@app.async_task # keeps /ping on "HealthyBusy" until this returns async def invoke(payload: dict) -> dict: - client = await Client.connect(...) - tracker = ActivityTracker() - worker = Worker( - client, - task_queue=task_queue, - workflows=[MyWorkflow], - activities=[my_activity], - interceptors=[tracker], - graceful_shutdown_timeout=DRAIN, - deployment_config=deployment_config, + """Poll until idle, then drain. The payload is unused: every call is a new session and new worker.""" + api_key = os.environ.get("TEMPORAL_API_KEY") or None + client = await Client.connect( + os.environ.get("TEMPORAL_ADDRESS", "localhost:7233"), + namespace=os.environ.get("TEMPORAL_NAMESPACE", "default"), + api_key=api_key, + tls=bool(api_key), + plugins=[StrandsPlugin()], ) - async with worker: - await tracker.wait_until_idle(DEBOUNCE) + task_queue = os.environ.get("TEMPORAL_TASK_QUEUE", workflows.TASK_QUEUE) + tracker = ActivityTracker() - return {"message": "Worker drained"} + log.info("polling %s as %s/%s", task_queue, DEPLOYMENT_NAME, BUILD_ID) + # execute_code is a sync Activity, so it needs an executor to block on. + with ThreadPoolExecutor(max_workers=4) as activity_executor: + worker = Worker( + client, + task_queue=task_queue, + workflows=[workflows.StrandsAgentWorkflow], + activities=[execute_code], + activity_executor=activity_executor, + interceptors=[tracker], + deployment_config=WorkerDeploymentConfig( + version=WorkerDeploymentVersion( + deployment_name=DEPLOYMENT_NAME, build_id=BUILD_ID + ), + use_worker_versioning=True, + default_versioning_behavior=VersioningBehavior.PINNED, + ), + graceful_shutdown_timeout=DRAIN, + ) + async with worker: + await tracker.wait_until_idle(DEBOUNCE) + log.info("worker idle for %ss; drained", DEBOUNCE) + return {"message": "worker drained", "task_queue": task_queue} ``` + The payload does not represent a Workflow input. The Worker Controller Instance invokes the endpoint to add Worker capacity. Applications start Workflows through the Temporal Client, as usual. @@ -117,16 +140,22 @@ When the condition remains true for an idle period, leave the `async with worker new Tasks and gives in-flight Activities time to complete before the Runtime handler returns. The following example from the -[AgentCore sample Worker](https://github.com/temporalio/samples-python/blob/77768ed87676359823736a40b68ca988a7344543/bedrock_agentcore/strands-agent/agentcore_worker.py) +[AgentCore sample Worker](https://github.com/temporalio/samples-python/blob/5b0fe65efe934388d35eda430fb11df6899ce1d3/bedrock_agentcore/strands-agent/agentcore_worker.py) defines an `ActivityTracker`. It uses an [Activity inbound Interceptor](/develop/python/workers/interceptors) to count running Activities. -```python + +[bedrock_agentcore/strands-agent/agentcore_worker.py](https://github.com/temporalio/samples-python/blob/5b0fe65efe934388d35eda430fb11df6899ce1d3/bedrock_agentcore/strands-agent/agentcore_worker.py) +```py +# How long the Worker keeps polling after it goes idle. DEBOUNCE = float(os.environ.get("AGENTCORE_DEBOUNCE_SECONDS", "60")) +# How long the drain waits for in-flight Activities (a model or tool call). DRAIN = timedelta(seconds=120) class ActivityTracker(Interceptor): + """Tracks in-flight activities and blocks until AGENTCORE_DEBOUNCE_SECONDS elapses with no events.""" + def __init__(self) -> None: self.inflight = 0 self.changed = asyncio.Event() @@ -134,32 +163,39 @@ class ActivityTracker(Interceptor): def intercept_activity( self, next: ActivityInboundInterceptor ) -> ActivityInboundInterceptor: - return TrackedActivity(next, self) + return _TrackedActivity(next, self) async def wait_until_idle(self, debounce: float) -> None: + """Return once no Activity has run for ``debounce`` seconds.""" while True: self.changed.clear() try: + # Wake the moment an Activity starts or finishes; a timeout + # instead means nothing has happened for the whole window. await asyncio.wait_for(self.changed.wait(), timeout=debounce) except asyncio.TimeoutError: if self.inflight == 0: return -class TrackedActivity(ActivityInboundInterceptor): - def __init__(self, next: ActivityInboundInterceptor, tracker: ActivityTracker): +class _TrackedActivity(ActivityInboundInterceptor): + def __init__( + self, next: ActivityInboundInterceptor, tracker: ActivityTracker + ) -> None: super().__init__(next) - self.tracker = tracker + self._tracker = tracker async def execute_activity(self, input: ExecuteActivityInput): - self.tracker.inflight += 1 - self.tracker.changed.set() + self._tracker.inflight += 1 + self._tracker.changed.set() + log.info("activity in flight: %d", self._tracker.inflight) try: return await self.next.execute_activity(input) finally: - self.tracker.inflight -= 1 - self.tracker.changed.set() + self._tracker.inflight -= 1 + self._tracker.changed.set() ``` + Register the tracker as a Worker Interceptor and wait for it inside the Worker context: From ce637085897a88ed2551e6e7e46d9d2ede5325e5 Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 13:14:04 -0700 Subject: [PATCH 23/26] Tighten AgentCore idle policy context --- .../python/workers/serverless-workers/agentcore.mdx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index 08ef2625d6..f3cae542a4 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -129,12 +129,10 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} -The `async_task` decorator reports the Runtime as busy while the handler is running. If a Worker continues polling after -the available Temporal work is complete, AgentCore cannot tell from the handler status that the Worker is no longer -needed. The compute can remain active until it reaches its maximum lifetime, which is eight hours by default. - -To release unused capacity sooner, decide how the Worker recognizes that it has no useful work. Observe that condition -in the Runtime handler. +The `async_task` decorator reports the Runtime as busy while the handler is running, so AgentCore cannot tell when a +Worker that is still polling has no Temporal work. Without an application idle policy, the Runtime can remain active +until its maximum lifetime of eight hours. To release capacity sooner, have the Runtime handler detect when the Worker +has no useful work. When the condition remains true for an idle period, leave the `async with worker` block. The Worker stops polling for new Tasks and gives in-flight Activities time to complete before the Runtime handler returns. From b5c80b8e6e7f5b48664d5b0e29e960a1e88571fc Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 13:25:59 -0700 Subject: [PATCH 24/26] Lead AgentCore lifecycle guidance with impact --- .../python/workers/serverless-workers/agentcore.mdx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index f3cae542a4..fba823313e 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -129,10 +129,9 @@ For the supported connection variables, config-file format, and profiles, see ## Stop and drain the Worker {/* #stop-and-drain-the-worker */} -The `async_task` decorator reports the Runtime as busy while the handler is running, so AgentCore cannot tell when a -Worker that is still polling has no Temporal work. Without an application idle policy, the Runtime can remain active -until its maximum lifetime of eight hours. To release capacity sooner, have the Runtime handler detect when the Worker -has no useful work. +AgentCore cannot tell when a Worker that is still polling has no Temporal work. The Runtime remains busy while the +`async_task` handler runs, so it can remain active until its eight-hour maximum lifetime. To release capacity sooner, +have the handler detect when the Worker has no useful work and return. When the condition remains true for an idle period, leave the `async with worker` block. The Worker stops polling for new Tasks and gives in-flight Activities time to complete before the Runtime handler returns. From be60a1e32c9ea865ffddceddcfbee46ec74ee58a Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 13:40:18 -0700 Subject: [PATCH 25/26] Add AgentCore Serverless Worker deployment guide --- .../workers/serverless-workers/agentcore.mdx | 2 + .../serverless-workers-agentcore.mdx | 3 + .../worker-deployments/index.mdx | 2 +- .../serverless-workers/agentcore.mdx | 331 ++++++++++++++++++ .../serverless-workers/index.mdx | 15 +- sidebars.js | 1 + 6 files changed, 346 insertions(+), 8 deletions(-) create mode 100644 docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx diff --git a/docs/develop/python/workers/serverless-workers/agentcore.mdx b/docs/develop/python/workers/serverless-workers/agentcore.mdx index fba823313e..2c5605ec7a 100644 --- a/docs/develop/python/workers/serverless-workers/agentcore.mdx +++ b/docs/develop/python/workers/serverless-workers/agentcore.mdx @@ -27,6 +27,8 @@ invocations. For the provider behavior, including autoscaling, Worker Versioning, and the Runtime session lifecycle, see [Serverless Workers on Amazon Bedrock AgentCore Runtime](/serverless-workers/agentcore). +For the infrastructure procedure, see +[Deploy a Serverless Worker on Amazon Bedrock AgentCore Runtime](/production-deployment/worker-deployments/serverless-workers/agentcore). ## Install the AgentCore Runtime SDK {/* #install-agentcore-runtime-sdk */} diff --git a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx index 9fd3328801..6f0577e42a 100644 --- a/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx +++ b/docs/encyclopedia/workers/serverless-workers/serverless-workers-agentcore.mdx @@ -23,6 +23,9 @@ import { ReleaseNoteHeader } from '@site/src/components'; This page covers how Serverless Workers run on Amazon Bedrock AgentCore Runtime, including Worker Versioning and the Runtime session lifecycle. +To deploy a Worker, see +[Deploy a Serverless Worker on Amazon Bedrock AgentCore Runtime](/production-deployment/worker-deployments/serverless-workers/agentcore). + On AgentCore Runtime, a Serverless Worker is a standard long-running Temporal Worker that runs inside an AgentCore Runtime session. When the [Worker Controller Instance (WCI)](/serverless-workers#worker-controller-instance) needs capacity, it invokes an AgentCore Runtime endpoint. The Runtime starts a Worker, which connects to the Temporal Service diff --git a/docs/production-deployment/worker-deployments/index.mdx b/docs/production-deployment/worker-deployments/index.mdx index 4a4ecb9a1f..f70742770f 100644 --- a/docs/production-deployment/worker-deployments/index.mdx +++ b/docs/production-deployment/worker-deployments/index.mdx @@ -30,7 +30,7 @@ You can optionally use the Temporal [Worker Controller](/production-deployment/w This section also covers specific Worker Deployment examples: - [**Serverless Workers**](/production-deployment/worker-deployments/serverless-workers) - Deploy Serverless Workers on serverless compute like AWS Lambda. + Deploy Serverless Workers on AWS Lambda, GCP Cloud Run, or Amazon Bedrock AgentCore Runtime. Temporal invokes your Worker when Tasks arrive, with no long-lived processes to manage. - [**Deploy Workers to Amazon EKS**](/production-deployment/worker-deployments/deploy-workers-to-aws-eks) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx b/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx new file mode 100644 index 0000000000..3dc8c6ee45 --- /dev/null +++ b/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx @@ -0,0 +1,331 @@ +--- +id: agentcore +title: Deploy a Serverless Worker on Amazon Bedrock AgentCore Runtime +sidebar_label: Amazon Bedrock AgentCore +description: Deploy a Python Serverless Worker to AgentCore Runtime and configure Temporal Cloud to invoke its endpoint. +slug: /production-deployment/worker-deployments/serverless-workers/agentcore +toc_max_heading_level: 4 +tags: + - Workers + - Deploy + - Serverless + - Amazon Bedrock AgentCore +--- + +import { ReleaseNoteHeader } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + Amazon Bedrock AgentCore Runtime support is in Pre-release, and its APIs may change in backwards-incompatible ways. + + +This guide walks through deploying a Python [Serverless Worker](/serverless-workers) to Amazon Bedrock AgentCore +Runtime. Temporal invokes the Runtime endpoint when the Worker Controller Instance needs Worker capacity. + +The guide uses the +[Python Strands AgentCore sample](https://github.com/temporalio/samples-python/tree/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent). +The deployment procedure applies to other Python Workers that use the same AgentCore Runtime handler. For the Worker +implementation and lifecycle, see +[Serverless Workers on Amazon Bedrock AgentCore Runtime - Python SDK](/develop/python/workers/serverless-workers/agentcore). + +## Prerequisites {/* #prerequisites */} + +- A Temporal Cloud account with an AWS-hosted Namespace and access to the AgentCore Serverless Workers Pre-release. +- A Temporal Cloud API key that can connect to the Namespace. +- [Temporal CLI v1.8.3](https://github.com/temporalio/cli/releases/tag/v1.8.3) or later, configured for your Namespace. +- An AWS account in an [AgentCore-supported Region](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html). +- The [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed and configured + with credentials for that account. +- Node.js 20 or later and the [AgentCore CLI](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-get-started-cli.html) + installed with `npm install -g @aws/agentcore`. +- The [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) installed and bootstrapped in the target + account and Region. +- Python 3.10 or later and [`uv`](https://docs.astral.sh/uv/) installed. +- Permission to create AgentCore resources, CloudFormation stacks, and IAM roles. See + [IAM permissions for AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html). + +The sample agent calls an Amazon Bedrock model and uses AgentCore Code Interpreter. To run the sample without changing +it, make sure both services are available to your AWS account in the target Region. + +## 1. Prepare the Worker {/* #prepare-worker */} + +Clone the sample repository and check out the AgentCore sample branch: + +```bash +git clone https://github.com/temporalio/samples-python.git +cd samples-python +git checkout schoeff/strands-agent +cd bedrock_agentcore/strands-agent +``` + +The sample contains these deployment files: + +| File | Purpose | +| --- | --- | +| [`agentcore_worker.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/agentcore_worker.py) | Defines the AgentCore Runtime handler and starts the Temporal Worker. | +| [`workflows.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/workflows.py) | Defines the sample Workflow. | +| [`activities.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/activities.py) | Defines the sample Activity. | +| [`agentcore/agentcore.json`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/agentcore/agentcore.json) | Configures the Runtime, endpoint, environment, and lifecycle. | +| [`agentcore/aws-targets.json`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/agentcore/aws-targets.json) | Selects the AWS account and Region. | + +The Runtime handler registers `StrandsAgentWorkflow` and `execute_code` on the Worker. To deploy your own application, +replace those registrations with your Workflow and Activity Definitions. Keep the AgentCore handler, Worker Versioning +configuration, and shutdown policy. Every Workflow must declare a +[versioning behavior](/worker-versioning/#versioning-behaviors), or the Worker must set a default behavior. + +## 2. Configure the AgentCore project {/* #configure-project */} + +The AgentCore CLI reads the project from `agentcore/agentcore.json` and deploys it to a target in +`agentcore/aws-targets.json`. + +### Select the AWS account and Region {/* #select-account-region */} + +Replace the account and Region in `agentcore/aws-targets.json`: + +```json +[ + { + "name": "default", + "account": "", + "region": "" + } +] +``` + +The Region must support AgentCore Runtime and any AWS services that the Worker's Activities call. + +### Configure the Runtime {/* #configure-runtime */} + +In `agentcore/agentcore.json`, set the Temporal connection, Task Queue, Worker Deployment name, and Build ID under +`envVars`: + +```json +{ + "name": "TEMPORAL_ADDRESS", + "value": "..tmprl.cloud:7233" +}, +{ + "name": "TEMPORAL_NAMESPACE", + "value": "." +}, +{ + "name": "TEMPORAL_API_KEY", + "value": "" +}, +{ + "name": "TEMPORAL_TASK_QUEUE", + "value": "agentcore-strands-task-queue" +}, +{ + "name": "TEMPORAL_DEPLOYMENT_NAME", + "value": "agentcore-strands-agent-python" +}, +{ + "name": "TEMPORAL_BUILD_ID", + "value": "1.0.0" +} +``` + +The deployment name and Build ID must match the values you use when you create the Worker Deployment Version in +[Step 5](#create-worker-deployment-version). The Task Queue must match the Task Queue used to start the Workflow. +For the sample, also set `AWS_REGION` to the Region in `aws-targets.json` so its Bedrock model and Code Interpreter +calls stay in that Region. + +The sample stores the Temporal Cloud API key in `envVars` to keep the example short. Do not commit a populated API key. +For a production deployment, store the key in AWS Secrets Manager, grant the Runtime execution role permission to read +it, and load it in the Runtime handler. The AgentCore CLI creates the Runtime execution role during deployment. This +role is separate from the invocation role that Temporal assumes in [Step 4](#configure-iam). + +The sample uses a CodeZip build and a public network: + +```json +{ + "build": "CodeZip", + "entrypoint": "agentcore_worker.py", + "codeLocation": ".", + "runtimeVersion": "PYTHON_3_12", + "networkMode": "PUBLIC", + "protocol": "HTTP", + "authorizerType": "AWS_IAM" +} +``` + +`CodeZip` lets the AgentCore CLI package the Python code without Docker. `PUBLIC` gives the Worker outbound network +access to Temporal Cloud. The `AWS_IAM` authorizer lets Temporal invoke the endpoint by assuming an IAM role in your +AWS account. + +Keep the named `temporal` endpoint in the sample configuration. Temporal invokes this endpoint rather than AgentCore's +`DEFAULT` endpoint. For the endpoint and Runtime version relationship, see +[Worker Versioning on AgentCore Runtime](/serverless-workers/agentcore/#worker-versioning). + +## 3. Deploy the Runtime and endpoint {/* #deploy-runtime */} + +From the sample directory, run: + +```bash +./bin/create-runtime.sh +``` + +The script creates the AgentCore CDK scaffold on its first run. It then validates the project, packages the Worker and +its dependencies, and runs `agentcore deploy`. AgentCore creates the Runtime execution role, Runtime version, named +endpoint, and CloudWatch log group. + +Check the deployed resources: + +```bash +agentcore status --runtime temporal_strands_worker --json +agentcore status --type runtime-endpoint --json +``` + +Record both of these values from the output: + +- The **Runtime ARN**, which you use to scope the invocation role in [Step 4](#configure-iam). +- The **Runtime endpoint ARN** for the named `temporal` endpoint, which you give Temporal in + [Step 5](#create-worker-deployment-version). + +## 4. Grant Temporal permission to invoke the Runtime {/* #configure-iam */} + +Temporal Cloud assumes an IAM role in your AWS account to get the named endpoint and invoke the Runtime. The role is +not the Runtime execution role and does not run your Worker code. + +Choose an External ID of at least five characters. Use the same value in the role trust policy and the Worker +Deployment Version. The External ID prevents a +[confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) attack. + +The sample includes a +[CloudFormation template](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/iam-role-for-temporal-agentcore-invoke.yaml) +and a script that deploys it. Pass the Runtime ARN with a trailing wildcard so the policy covers the Runtime and its +endpoints: + +```bash +AWS_REGION= ./bin/mk-invoke-role.sh \ + temporal-agentcore-invoke \ + \ + '*' +``` + +Wait for the CloudFormation stack to finish: + +```bash +aws cloudformation wait stack-create-complete \ + --stack-name temporal-agentcore-invoke \ + --region +``` + +Then retrieve the invocation role ARN: + +```bash +aws cloudformation describe-stacks \ + --stack-name temporal-agentcore-invoke \ + --query 'Stacks[0].Outputs[?OutputKey==`RoleARN`].OutputValue' \ + --output text \ + --region +``` + +The role grants `bedrock-agentcore:InvokeAgentRuntime` and `bedrock-agentcore:GetAgentRuntimeEndpoint` on the configured +Runtime resources. Record the role ARN for the next step. + +## 5. Create the Worker Deployment Version {/* #create-worker-deployment-version */} + +Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) whose compute +configuration points to the named AgentCore Runtime endpoint. The deployment name and Build ID must match the Runtime +environment variables from [Step 2](#configure-runtime). + + + + +In the Temporal Cloud UI, open your Namespace and select **Workers** > **Create Worker Deployment**. Provide these +values: + +- **Name**: `agentcore-strands-agent-python`. +- **Build ID**: `1.0.0`. +- **Compute Provider**: select **Amazon Bedrock AgentCore Runtime**. +- **Runtime endpoint ARN**: the named endpoint ARN from [Step 3](#deploy-runtime). +- **IAM role ARN**: the invocation role ARN from [Step 4](#configure-iam). +- **External ID**: the External ID from [Step 4](#configure-iam). + +Save the Worker Deployment. When you create a version through the UI, the version is automatically current. Continue +to [Step 7](#verify-deployment). + + + + +First, create the Worker Deployment if it does not already exist: + +```bash +temporal worker deployment create \ + --namespace \ + --name agentcore-strands-agent-python +``` + +Then create the version with the AgentCore compute configuration: + +```bash +temporal worker deployment create-version \ + --namespace \ + --deployment-name agentcore-strands-agent-python \ + --build-id 1.0.0 \ + --aws-agentcore-endpoint-arn \ + --aws-agentcore-assume-role-arn \ + --aws-agentcore-assume-role-external-id +``` + +| Flag | Description | +| --- | --- | +| `--deployment-name` | Worker Deployment name. Must match `TEMPORAL_DEPLOYMENT_NAME` in the Runtime environment. | +| `--build-id` | Worker Deployment Version Build ID. Must match `TEMPORAL_BUILD_ID` in the Runtime environment. | +| `--aws-agentcore-endpoint-arn` | ARN of the named AgentCore Runtime endpoint that Temporal invokes. | +| `--aws-agentcore-assume-role-arn` | IAM role Temporal assumes to invoke the endpoint. This is the CloudFormation stack output from Step 4, not the Runtime execution role. | +| `--aws-agentcore-assume-role-external-id` | External ID configured in the invocation role trust policy. | + + + + +To check whether Temporal can reach the endpoint, open the Worker Deployment Version in the Temporal Cloud UI and +select **Actions** > **Validate Connection**. This checks that Temporal can assume the invocation role, get the named +endpoint, and invoke the Runtime. + +## 6. Set the version as current {/* #set-current-version */} + +If you used the Temporal CLI, set the version as current: + +```bash +temporal worker deployment set-current-version \ + --namespace \ + --deployment-name agentcore-strands-agent-python \ + --build-id 1.0.0 +``` + +This command asks you to confirm because it changes which version receives new Tasks. Pass `--yes` to skip the prompt. +If you created the version in the Temporal Cloud UI, it is already current. + +## 7. Verify the deployment {/* #verify-deployment */} + +Install the sample dependencies and export the same Temporal connection values that you configured for the Runtime: + +```bash +uv sync +export TEMPORAL_ADDRESS=..tmprl.cloud:7233 +export TEMPORAL_NAMESPACE=. +export TEMPORAL_API_KEY= +``` + +Start the sample Workflow from your local machine: + +```bash +uv run python starter.py "Calculate the first 10 Fibonacci numbers." +``` + +The starter sends the Workflow to `agentcore-strands-task-queue`. When no Worker is polling, Temporal invokes +the AgentCore Runtime endpoint. The Runtime starts the Worker, and the Worker processes the Workflow and its Activities. + +You can confirm the deployment in these places: + +- **Temporal Cloud UI**: Open the Workflow Execution and confirm that its Event History progresses. +- **AgentCore logs**: Run `agentcore logs --runtime temporal_strands_worker` to see the Worker start and process Tasks. +- **Temporal CLI**: Run `temporal workflow show --workflow-id agentcore-strands-workflow-id-1` to inspect the Event + History. + +For the sample Workflow and agent implementation, see +[`workflows.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/workflows.py). diff --git a/docs/production-deployment/worker-deployments/serverless-workers/index.mdx b/docs/production-deployment/worker-deployments/serverless-workers/index.mdx index 1a80f95fcc..8e15a56982 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/index.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/index.mdx @@ -15,10 +15,9 @@ tags: import { ReleaseNoteHeader } from '@site/src/components'; - AWS Lambda support is in Public Preview. GCP Cloud Run support is in Pre-release, and its APIs may change in - backwards-incompatible ways. To request Cloud Run access, create a [support ticket](/cloud/support#support-ticket) or - contact your account team, and [sign up for updates](https://temporal.io/pages/serverless-workers-updates) to hear - when Cloud Run reaches Public Preview. + AWS Lambda support is in Public Preview. Support for GCP Cloud Run and Amazon Bedrock AgentCore Runtime is in + Pre-release, and their APIs may change in backwards-incompatible ways. To request access, create a + [support ticket](/cloud/support#support-ticket) or contact your account team. Serverless Workers let you run Temporal Workers on serverless compute. Deploy your Worker code to a serverless provider, @@ -27,9 +26,8 @@ work on the Task Queue. There is no always-on Worker fleet to provision or scale Temporal monitors Task Queues that have a compute provider configured. When a Task arrives and no Worker is free to take it, the [Worker Controller Instance (WCI)](/serverless-workers#how-invocation-works) starts compute. How it starts -compute is where the providers differ. On AWS Lambda the WCI invokes a function per unit of work, and the Worker exits -when the invocation window ends. On GCP Cloud Run it resizes a Worker Pool of long-lived instances that poll -continuously. +compute is where the providers differ. On AWS Lambda and AgentCore Runtime, the WCI invokes compute in response to +unmet Task Queue demand. On GCP Cloud Run, it resizes a Worker Pool of long-lived instances that poll continuously. ## Supported providers @@ -38,3 +36,6 @@ continuously. - [**GCP Cloud Run**](/production-deployment/worker-deployments/serverless-workers/cloud-run) - Deploy a Serverless Worker to a Cloud Run Worker Pool. Temporal impersonates a service account in your GCP project to scale the pool as Tasks arrive and drain. +- [**Amazon Bedrock AgentCore Runtime**](/production-deployment/worker-deployments/serverless-workers/agentcore) - + Deploy a Serverless Worker to AgentCore Runtime. Temporal assumes an IAM role in your AWS account to invoke the + Runtime endpoint as Tasks arrive. diff --git a/sidebars.js b/sidebars.js index 35f2165256..f30fabd864 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1607,6 +1607,7 @@ module.exports = { 'production-deployment/worker-deployments/serverless-workers/aws-lambda/self-hosted-setup', ], }, + 'production-deployment/worker-deployments/serverless-workers/agentcore', { type: 'category', label: 'GCP Cloud Run', From cb56f06f158f1acdb9a74bc8bf7da05b3a9c89bb Mon Sep 17 00:00:00 2001 From: "Lenny (Temporal)" Date: Thu, 10 Sep 2026 16:56:51 -0700 Subject: [PATCH 26/26] Document durable agents on AgentCore --- docs/guides/durable-agent-on-agentcore.mdx | 428 ++++++++++++++++++ .../serverless-workers/agentcore.mdx | 245 ++++------ sidebars.js | 1 + snipsync.config.yaml | 3 + src/components/GuidesGrid/guides-data.json | 9 + ...loud-serverless-worker-agentcore-role.yaml | 69 +++ 6 files changed, 593 insertions(+), 162 deletions(-) create mode 100644 docs/guides/durable-agent-on-agentcore.mdx create mode 100644 static/files/temporal-cloud-serverless-worker-agentcore-role.yaml diff --git a/docs/guides/durable-agent-on-agentcore.mdx b/docs/guides/durable-agent-on-agentcore.mdx new file mode 100644 index 0000000000..b996fae4d7 --- /dev/null +++ b/docs/guides/durable-agent-on-agentcore.mdx @@ -0,0 +1,428 @@ +--- +id: durable-agent-on-agentcore +title: Build a durable agent on Amazon Bedrock AgentCore +sidebar_label: Durable agent on AgentCore +description: A Temporal Workflow preserves conversation state while AgentCore Runtime supplies serverless Worker compute for a Strands agent. +toc_max_heading_level: 3 +author: n/a +tags: + - Workflows + - Activities + - Workers + - Python SDK + - Strands Agents + - Serverless +--- + +import { ReleaseNoteHeader } from '@site/src/components'; + + + Amazon Bedrock AgentCore Runtime support is in Pre-release, and its APIs may change in backwards-incompatible ways. + + +This guide builds a data-analysis agent that can continue a conversation after the compute running it has stopped. A +Temporal Workflow holds the conversation and coordinates each turn. Strands defines how the agent uses a model and +tools. Amazon Bedrock AgentCore Runtime supplies serverless compute for the Temporal Worker, and AgentCore Code +Interpreter supplies an isolated environment for running code. + +The result separates the lifetime of the agent from the lifetime of its compute. The Workflow can remain open for days +or months without keeping an AgentCore Runtime active. + +## See what you will build + +The agent has one Workflow Execution for each conversation. A client sends prompts to an `ask` Update handler and +receives the agent's answer as the Update result. The Workflow waits without using Worker compute between prompts. + +When a prompt arrives and no Worker is polling, Temporal Cloud starts Worker capacity on AgentCore Runtime. The Worker +reconstructs the Workflow from its Event History, runs the next agent turn, and retires after it becomes idle. A later +prompt can run on a different Worker without starting a new conversation. + +```mermaid +sequenceDiagram + participant Client + participant Temporal as Temporal Cloud + participant Runtime1 as AgentCore Runtime A + participant AWS as Bedrock and Code Interpreter + participant Runtime2 as AgentCore Runtime B + + Client->>Temporal: Start conversation Workflow + Temporal->>Runtime1: Start Worker capacity + Client->>Temporal: Update: ask first question + Runtime1->>AWS: Model and tool Activities + AWS-->>Runtime1: Results + Runtime1-->>Temporal: Update result + Temporal-->>Client: First answer + Runtime1-->>Runtime1: Become idle and drain + Note over Temporal: Workflow waits without Worker compute + Client->>Temporal: Update: ask follow-up question + Temporal->>Runtime2: Start new Worker capacity + Runtime2->>Temporal: Replay Event History + Runtime2->>AWS: Model and tool Activities + AWS-->>Runtime2: Results + Runtime2-->>Temporal: Update result + Temporal-->>Client: Follow-up answer +``` + +Start with the +[durable AgentCore sample](https://github.com/temporalio/documentation-sdk-code-examples/tree/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent). +It contains the AgentCore project, Runtime handler, IAM policy, and Code Interpreter Activity used in this guide. + +You need Python 3.10 or later, `uv`, AWS credentials, access to a Bedrock model, and a local Temporal development server. +Follow [Set up your local Python environment](/develop/python/set-up-your-local-python) before continuing. + +Clone the sample repository and install the application dependencies: + +```bash +git clone https://github.com/temporalio/documentation-sdk-code-examples.git +cd documentation-sdk-code-examples/python-agentcore-durable-agent +uv sync +``` + +## Give each system one job + +The three systems operate at different levels: + +| System | Job in this application | +|---|---| +| Strands Agents | Defines the system prompt, tools, model interaction, and agent loop for one turn. | +| Temporal | Gives the conversation a durable identity, persists its progress, delivers later prompts, and retries model and tool calls as Activities. | +| AgentCore Runtime | Starts isolated AWS compute that hosts a Temporal Worker when the Task Queue needs capacity. | + +Amazon Bedrock performs model inference. AgentCore Code Interpreter runs code in a managed sandbox when the model +chooses that tool. + +The Workflow Id is the durable identity of the agent conversation. An AgentCore Runtime session is compute that can +host a Worker for part of that conversation. Do not require the same Runtime session or Worker process to handle every +turn. + +## Build the agent locally + +The [durable AgentCore sample](https://github.com/temporalio/documentation-sdk-code-examples/tree/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent) +defines `execute_code` as a Temporal Activity. It uses the Workflow Id as the Code Interpreter session name so two +Workflow Executions handled by the same process do not share a sandbox. The name does not make the sandbox durable +across Worker replacement. + + +[python-agentcore-durable-agent/activities.py](https://github.com/temporalio/documentation-sdk-code-examples/blob/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent/activities.py) +```py +@activity.defn +def execute_code( + code: str, language: LanguageType = LanguageType.PYTHON +) -> dict[str, Any]: + interpreter = AgentCoreCodeInterpreter( + region=os.environ.get("AWS_REGION", "us-west-2"), + session_name=activity.info().workflow_id, + ) + return interpreter.execute_code( + ExecuteCodeAction(type="executeCode", code=code, language=language) + ) + + +``` + + +The Activity boundary gives the tool call a separate timeout, Retry Policy, and result in Event History. It also keeps +AWS calls out of deterministic Workflow code. + +Define a Workflow that accepts multiple prompts: + + +[python-agentcore-durable-agent/workflows.py](https://github.com/temporalio/documentation-sdk-code-examples/blob/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent/workflows.py) +```py +@workflow.defn +class DurableAgentWorkflow: + def __init__(self) -> None: + self._done = False + self._lock = asyncio.Lock() + self._agent = TemporalAgent( + model="bedrock", + start_to_close_timeout=timedelta(seconds=60), + system_prompt=SYSTEM_PROMPT, + tools=[ + activity_as_tool( + execute_code, + start_to_close_timeout=timedelta(minutes=2), + ) + ], + ) + + @workflow.update + async def ask(self, prompt: str) -> str: + async with self._lock: + result = await self._agent.invoke_async(prompt) + return str(result).strip() + + @workflow.signal + def finish(self) -> None: + self._done = True + + @workflow.run + async def run(self) -> None: + await workflow.wait_condition(lambda: self._done) + await workflow.wait_condition(workflow.all_handlers_finished) + + +``` + + +`TemporalAgent` is a Strands `Agent` adapted to run inside a Workflow. It retains the Strands message list between +calls to `invoke_async`. The Temporal Strands plugin runs model calls as Activities, and `activity_as_tool` runs the +Code Interpreter tool as an Activity. Configure retries through Temporal Activity Retry Policies rather than a Strands +retry strategy. + +The lock makes the agent process one prompt at a time. The `run` method waits until the `finish` Signal arrives, so the +Workflow remains available between turns. This wait is durable and does not keep a Python process running. + +Register `DurableAgentWorkflow`, `execute_code`, and `StrandsPlugin` on a local Worker. The +[sample Worker](https://github.com/temporalio/documentation-sdk-code-examples/blob/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent/local_worker.py) +also creates the executor required by the synchronous `execute_code` Activity: + + +[python-agentcore-durable-agent/local_worker.py](https://github.com/temporalio/documentation-sdk-code-examples/blob/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent/local_worker.py) +```py +async def main() -> None: + client = await Client.connect( + "localhost:7233", + plugins=[StrandsPlugin()], + ) + + with ThreadPoolExecutor(max_workers=4) as activity_executor: + worker = Worker( + client, + task_queue=TASK_QUEUE, + workflows=[DurableAgentWorkflow], + activities=[execute_code], + activity_executor=activity_executor, + ) + await worker.run() + + +``` + + +Start the Temporal development server, then start the Worker in another terminal: + +```bash +temporal server start-dev +``` + +```bash +uv run python local_worker.py +``` + +The sample's chat client starts a Workflow and sends each prompt as an Update: + + +[python-agentcore-durable-agent/chat.py](https://github.com/temporalio/documentation-sdk-code-examples/blob/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent/chat.py) +```py +async def main() -> None: + client = await Client.connect( + "localhost:7233", + plugins=[StrandsPlugin()], + ) + handle = await client.start_workflow( + DurableAgentWorkflow.run, + id=f"durable-agent-{uuid.uuid4()}", + task_queue=TASK_QUEUE, + ) + + while prompt := input("You: "): + if prompt == "/finish": + await handle.signal(DurableAgentWorkflow.finish) + return + answer = await handle.execute_update(DurableAgentWorkflow.ask, prompt) + print(f"Agent: {answer}") + + +``` + + +Run the client in a third terminal: + +```bash +uv run python chat.py +``` + +Ask a question that requires calculation, then ask a follow-up that depends on the first answer. Enter `/finish` to +close the Workflow. In the Temporal Web UI, the Event History shows the `ask` Update, model Activities, and +`execute_code` Activity for each turn. + +## Run the Worker on AgentCore Runtime + +Local development uses a continuously running Worker. On AgentCore Runtime, the Worker starts inside the Runtime's HTTP +handler and returns when its idle policy decides to release the compute. + +The AgentCore Runtime handler registers the `DurableAgentWorkflow` and `execute_code` definitions from +[Build the agent locally](#build-the-agent-locally). It adds Worker Versioning and the Activity-based idle tracker from +the [Python AgentCore Worker guide](/develop/python/workers/serverless-workers/agentcore#stop-and-drain-the-worker), then +runs the Worker inside the Runtime handler: + + +[python-agentcore-durable-agent/agentcore_worker.py](https://github.com/temporalio/documentation-sdk-code-examples/blob/docs/durable-agent-agentcore-sample/python-agentcore-durable-agent/agentcore_worker.py) +```py +@app.entrypoint +@app.async_task +async def invoke(payload: dict) -> dict: + client = await Client.connect( + required_env("TEMPORAL_ADDRESS"), + namespace=required_env("TEMPORAL_NAMESPACE"), + api_key=required_env("TEMPORAL_API_KEY"), + tls=True, + plugins=[StrandsPlugin()], + ) + tracker = ActivityTracker() + + with ThreadPoolExecutor(max_workers=4) as activity_executor: + worker = Worker( + client, + task_queue=os.environ.get("TEMPORAL_TASK_QUEUE", TASK_QUEUE), + workflows=[DurableAgentWorkflow], + activities=[execute_code], + activity_executor=activity_executor, + interceptors=[tracker], + deployment_config=WorkerDeploymentConfig( + version=WorkerDeploymentVersion( + deployment_name=os.environ.get( + "TEMPORAL_DEPLOYMENT_NAME", DEPLOYMENT_NAME + ), + build_id=os.environ.get("TEMPORAL_BUILD_ID", BUILD_ID), + ), + use_worker_versioning=True, + default_versioning_behavior=VersioningBehavior.PINNED, + ), + graceful_shutdown_timeout=DRAIN, + ) + async with worker: + await tracker.wait_until_idle(DEBOUNCE) + + return {"message": "Worker drained"} + + +``` + + +The invocation payload does not contain a user prompt. Temporal invokes the Runtime endpoint to add Worker capacity. +Clients continue to start and message Workflows through the Temporal Client. + +The Runtime does not need a copy of the conversation in a local file or global variable. When a new Worker receives a +Workflow Task, Temporal replays the Workflow's Event History and restores the `TemporalAgent` message list before new +model or tool calls run. + +## Deploy the Serverless Worker + +Install the AgentCore CLI and generate the CDK project used by the sample's Runtime definition: + +```bash +npm install -g @aws/agentcore +./bootstrap-agentcore-project.sh +``` + +Follow [Deploy a Serverless Worker on Amazon Bedrock AgentCore Runtime](/production-deployment/worker-deployments/serverless-workers/agentcore) +to deploy the existing AgentCore project and configure its Worker Deployment Version. + +For this application, use the same values in each place: + +| Setting | Tutorial value | +|---|---| +| Runtime entrypoint | `agentcore_worker.py` | +| Task Queue | `durable-agent` | +| Worker Deployment name | `durable-agent-agentcore` | +| Build ID | A version for this code, such as `1.0.0` | + +The AgentCore Runtime execution role needs permission to invoke Bedrock and Code Interpreter. The separate role that +Temporal Cloud assumes needs permission to invoke the AgentCore Runtime endpoint. The deployment guide creates and +configures the second role. + +## Talk to the deployed agent + +Start one conversation Workflow. This command returns immediately while the Workflow remains open: + +```bash +temporal workflow start \ + --workflow-id durable-agent-alice \ + --type DurableAgentWorkflow \ + --task-queue durable-agent +``` + +Send the first prompt as an Update and wait for the reply: + +```bash +temporal workflow update execute \ + --workflow-id durable-agent-alice \ + --name ask \ + --input '"A film festival has 7 screens with 4 showings per screen. How many screenings can it schedule?"' +``` + +Temporal starts AgentCore Worker capacity because the Task Queue has work. After the turn completes and the idle period +expires, the Runtime handler drains the Worker and returns. Confirm this in the AgentCore logs: + +```bash +agentcore logs --runtime +``` + +After the Worker has retired, send a follow-up that depends on the first turn: + +```bash +temporal workflow update execute \ + --workflow-id durable-agent-alice \ + --name ask \ + --input '"If we add two screenings to the total you calculated, what is the new total?"' +``` + +Temporal starts capacity again. The new Worker reconstructs the existing Workflow and its Strands messages, so the +agent can interpret "the total you calculated" without depending on the previous Worker process. + +End the conversation when it no longer needs to accept prompts: + +```bash +temporal workflow signal \ + --workflow-id durable-agent-alice \ + --name finish +``` + +## Test recovery + +Worker retirement between turns tests one form of recovery. You can also interrupt compute while a model or tool +Activity is running. Start a prompt that takes long enough to observe, find the active Runtime session identifier in the +AgentCore logs, and stop that session: + +```bash +aws bedrock-agentcore stop-runtime-session \ + --agent-runtime-arn \ + --runtime-session-id \ + --region +``` + +For the required IAM permission and API behavior, see +[Stop a running session](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-stop-session.html). + +The Activity attempt running on that Worker is interrupted. Temporal keeps the Workflow state and schedules the +Activity again according to its Retry Policy. Serverless Workers starts new AgentCore capacity to process the Task. In +the Temporal Web UI, inspect the Activity attempts and confirm that the Workflow continues without restarting the +conversation. + +An Activity can run more than once if its Worker stops after making an external change but before reporting completion. +Use an idempotency key for tools that change external state. The Workflow Id plus a stable operation identifier is a +common choice. Code execution used only to calculate an answer does not make an external business change, so it is a +safe recovery demonstration. + +## Decide where state belongs + +Place state according to how long it must survive and which system uses it: + +| State | Location | Reason | +|---|---|---| +| Current conversation and agent progress | Temporal Workflow | It must survive Worker and Runtime replacement. | +| Completed model and tool call results | Temporal Event History | Activity results let replay restore completed progress without repeating successful calls. | +| Approvals, timers, and long waits | Temporal Workflow | These are part of the agent's durable control flow. | +| Knowledge shared across conversations | [AgentCore Memory](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory.html), accessed from an Activity | It belongs to the user or application rather than one Workflow Execution. | +| Credentials for AWS and external systems | [AgentCore Identity](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/identity.html) or an AWS secret store | Workflow state should not contain credentials. | +| Tool access and authorization | [AgentCore Gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html) and [Policy](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy.html) | These services control how tools are reached and whether a call is allowed. | +| Temporary Worker caches | AgentCore Runtime session | They can improve performance but must be safe to lose. | +| Code Interpreter variables and files | Code Interpreter session | They last only for that tool session. Store required outputs durably before relying on them later. | +| Large files and datasets | Object storage, with a reference in the Workflow | Event History is not intended for large application objects. | + +The Strands message list is Workflow state in this design. Temporal reconstructs it through Event History when another +Worker continues the Workflow. Do not use Event History as unlimited chat or object storage. For conversations that +accumulate many turns, use [Continue-As-New](/develop/python/integrations/strands-agents#handle-long-running-chat-sessions) +to start a new Event History while carrying forward the messages the next execution needs. diff --git a/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx b/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx index 3dc8c6ee45..4655bfde08 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/agentcore.mdx @@ -2,7 +2,7 @@ id: agentcore title: Deploy a Serverless Worker on Amazon Bedrock AgentCore Runtime sidebar_label: Amazon Bedrock AgentCore -description: Deploy a Python Serverless Worker to AgentCore Runtime and configure Temporal Cloud to invoke its endpoint. +description: Deploy an existing Python Worker to AgentCore Runtime and configure Temporal Cloud to start capacity when Task Queue demand increases. slug: /production-deployment/worker-deployments/serverless-workers/agentcore toc_max_heading_level: 4 tags: @@ -20,20 +20,24 @@ import TabItem from '@theme/TabItem'; Amazon Bedrock AgentCore Runtime support is in Pre-release, and its APIs may change in backwards-incompatible ways. -This guide walks through deploying a Python [Serverless Worker](/serverless-workers) to Amazon Bedrock AgentCore -Runtime. Temporal invokes the Runtime endpoint when the Worker Controller Instance needs Worker capacity. - -The guide uses the -[Python Strands AgentCore sample](https://github.com/temporalio/samples-python/tree/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent). -The deployment procedure applies to other Python Workers that use the same AgentCore Runtime handler. For the Worker -implementation and lifecycle, see +This guide deploys an existing Python [Serverless Worker](/serverless-workers) to Amazon Bedrock AgentCore Runtime and +configures Temporal Cloud to start Worker capacity. It assumes that you already have a Temporal Worker and an AgentCore +project. For the Worker implementation and lifecycle, see [Serverless Workers on Amazon Bedrock AgentCore Runtime - Python SDK](/develop/python/workers/serverless-workers/agentcore). +If you are still deciding how to structure your agent, Workflow, and Activities, see the +[Python Strands AgentCore sample](https://github.com/temporalio/samples-python/tree/schoeff/strands-agent/bedrock_agentcore/strands-agent) +for a complete application. + ## Prerequisites {/* #prerequisites */} - A Temporal Cloud account with an AWS-hosted Namespace and access to the AgentCore Serverless Workers Pre-release. - A Temporal Cloud API key that can connect to the Namespace. - [Temporal CLI v1.8.3](https://github.com/temporalio/cli/releases/tag/v1.8.3) or later, configured for your Namespace. +- An existing Python Temporal Worker with an + [AgentCore Runtime handler](/develop/python/workers/serverless-workers/agentcore#runtime-handler). +- An AgentCore project that packages the Worker and contains `agentcore/agentcore.json`, `agentcore/aws-targets.json`, + and the generated AgentCore CDK project. - An AWS account in an [AgentCore-supported Region](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html). - The [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed and configured with credentials for that account. @@ -41,64 +45,13 @@ implementation and lifecycle, see installed with `npm install -g @aws/agentcore`. - The [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) installed and bootstrapped in the target account and Region. -- Python 3.10 or later and [`uv`](https://docs.astral.sh/uv/) installed. - Permission to create AgentCore resources, CloudFormation stacks, and IAM roles. See [IAM permissions for AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html). -The sample agent calls an Amazon Bedrock model and uses AgentCore Code Interpreter. To run the sample without changing -it, make sure both services are available to your AWS account in the target Region. - -## 1. Prepare the Worker {/* #prepare-worker */} - -Clone the sample repository and check out the AgentCore sample branch: - -```bash -git clone https://github.com/temporalio/samples-python.git -cd samples-python -git checkout schoeff/strands-agent -cd bedrock_agentcore/strands-agent -``` - -The sample contains these deployment files: - -| File | Purpose | -| --- | --- | -| [`agentcore_worker.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/agentcore_worker.py) | Defines the AgentCore Runtime handler and starts the Temporal Worker. | -| [`workflows.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/workflows.py) | Defines the sample Workflow. | -| [`activities.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/activities.py) | Defines the sample Activity. | -| [`agentcore/agentcore.json`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/agentcore/agentcore.json) | Configures the Runtime, endpoint, environment, and lifecycle. | -| [`agentcore/aws-targets.json`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/agentcore/aws-targets.json) | Selects the AWS account and Region. | - -The Runtime handler registers `StrandsAgentWorkflow` and `execute_code` on the Worker. To deploy your own application, -replace those registrations with your Workflow and Activity Definitions. Keep the AgentCore handler, Worker Versioning -configuration, and shutdown policy. Every Workflow must declare a -[versioning behavior](/worker-versioning/#versioning-behaviors), or the Worker must set a default behavior. - -## 2. Configure the AgentCore project {/* #configure-project */} - -The AgentCore CLI reads the project from `agentcore/agentcore.json` and deploys it to a target in -`agentcore/aws-targets.json`. - -### Select the AWS account and Region {/* #select-account-region */} - -Replace the account and Region in `agentcore/aws-targets.json`: - -```json -[ - { - "name": "default", - "account": "", - "region": "" - } -] -``` - -The Region must support AgentCore Runtime and any AWS services that the Worker's Activities call. - -### Configure the Runtime {/* #configure-runtime */} +## 1. Configure the Worker Runtime {/* #configure-worker-runtime */} -In `agentcore/agentcore.json`, set the Temporal connection, Task Queue, Worker Deployment name, and Build ID under -`envVars`: +In `agentcore/agentcore.json`, configure the Runtime with the Temporal connection, Task Queue, Worker Deployment name, +and Build ID: ```json { @@ -115,101 +68,93 @@ In `agentcore/agentcore.json`, set the Temporal connection, Task Queue, Worker D }, { "name": "TEMPORAL_TASK_QUEUE", - "value": "agentcore-strands-task-queue" + "value": "" }, { "name": "TEMPORAL_DEPLOYMENT_NAME", - "value": "agentcore-strands-agent-python" + "value": "" }, { "name": "TEMPORAL_BUILD_ID", - "value": "1.0.0" + "value": "" } ``` -The deployment name and Build ID must match the values you use when you create the Worker Deployment Version in -[Step 5](#create-worker-deployment-version). The Task Queue must match the Task Queue used to start the Workflow. -For the sample, also set `AWS_REGION` to the Region in `aws-targets.json` so its Bedrock model and Code Interpreter -calls stay in that Region. +The Task Queue must match the Task Queue used by your application. The deployment name and Build ID must match the +Worker Deployment Version that you create in [Step 4](#create-worker-deployment-version). -The sample stores the Temporal Cloud API key in `envVars` to keep the example short. Do not commit a populated API key. -For a production deployment, store the key in AWS Secrets Manager, grant the Runtime execution role permission to read -it, and load it in the Runtime handler. The AgentCore CLI creates the Runtime execution role during deployment. This -role is separate from the invocation role that Temporal assumes in [Step 4](#configure-iam). - -The sample uses a CodeZip build and a public network: +The Runtime definition must use your Worker handler as its entrypoint and provide a named endpoint for Temporal. The +following fragment uses a public network so the Worker can reach Temporal Cloud: ```json { - "build": "CodeZip", "entrypoint": "agentcore_worker.py", - "codeLocation": ".", - "runtimeVersion": "PYTHON_3_12", "networkMode": "PUBLIC", "protocol": "HTTP", - "authorizerType": "AWS_IAM" + "authorizerType": "AWS_IAM", + "endpoints": { + "temporal": { + "version": 1, + "description": "Invoked by Temporal Cloud Serverless Workers" + } + } } ``` -`CodeZip` lets the AgentCore CLI package the Python code without Docker. `PUBLIC` gives the Worker outbound network -access to Temporal Cloud. The `AWS_IAM` authorizer lets Temporal invoke the endpoint by assuming an IAM role in your -AWS account. +If you use a VPC instead, configure outbound access from the VPC to your Temporal Cloud Namespace. Temporal invokes the +named endpoint by assuming the IAM role that you create in [Step 3](#configure-iam). -Keep the named `temporal` endpoint in the sample configuration. Temporal invokes this endpoint rather than AgentCore's -`DEFAULT` endpoint. For the endpoint and Runtime version relationship, see -[Worker Versioning on AgentCore Runtime](/serverless-workers/agentcore/#worker-versioning). +Do not commit a populated Temporal Cloud API key. For a production deployment, store it in AWS Secrets Manager, grant +the Runtime execution role permission to read it, and load it in the Runtime handler. The Runtime execution role is +separate from the invocation role that Temporal assumes. -## 3. Deploy the Runtime and endpoint {/* #deploy-runtime */} +## 2. Deploy the Worker Runtime {/* #deploy-runtime */} -From the sample directory, run: +From the AgentCore project directory, validate and deploy the project: ```bash -./bin/create-runtime.sh +agentcore validate +agentcore deploy --target -y ``` -The script creates the AgentCore CDK scaffold on its first run. It then validates the project, packages the Worker and -its dependencies, and runs `agentcore deploy`. AgentCore creates the Runtime execution role, Runtime version, named -endpoint, and CloudWatch log group. +AgentCore packages the Worker and its dependencies, deploys the Runtime, and creates the named endpoint. Check the deployed resources: ```bash -agentcore status --runtime temporal_strands_worker --json +agentcore status --runtime --json agentcore status --type runtime-endpoint --json ``` -Record both of these values from the output: +Record the Runtime ARN and the ARN of the named endpoint. You use the Runtime ARN to scope the invocation role and give +the endpoint ARN to Temporal Cloud. -- The **Runtime ARN**, which you use to scope the invocation role in [Step 4](#configure-iam). -- The **Runtime endpoint ARN** for the named `temporal` endpoint, which you give Temporal in - [Step 5](#create-worker-deployment-version). +## 3. Grant Temporal permission to invoke the Runtime {/* #configure-iam */} -## 4. Grant Temporal permission to invoke the Runtime {/* #configure-iam */} +Temporal Cloud assumes an IAM role in your AWS account to get the named endpoint and invoke the Runtime. Choose an +External ID of at least five characters. Use the same value in the role trust policy and the Worker Deployment Version. +The External ID prevents a [confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) +attack. -Temporal Cloud assumes an IAM role in your AWS account to get the named endpoint and invoke the Runtime. The role is -not the Runtime execution role and does not run your Worker code. - -Choose an External ID of at least five characters. Use the same value in the role trust policy and the Worker -Deployment Version. The External ID prevents a -[confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) attack. - -The sample includes a -[CloudFormation template](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/iam-role-for-temporal-agentcore-invoke.yaml) -and a script that deploys it. Pass the Runtime ARN with a trailing wildcard so the policy covers the Runtime and its -endpoints: +[Download the CloudFormation template](/files/temporal-cloud-serverless-worker-agentcore-role.yaml), then deploy it. +Pass the Runtime ARN with a trailing wildcard so the policy covers the Runtime and its endpoints: ```bash -AWS_REGION= ./bin/mk-invoke-role.sh \ - temporal-agentcore-invoke \ - \ - '*' +aws cloudformation create-stack \ + --stack-name \ + --template-body file://temporal-cloud-serverless-worker-agentcore-role.yaml \ + --parameters \ + ParameterKey=AssumeRoleExternalId,ParameterValue= \ + ParameterKey=AgentRuntimeARNs,ParameterValue='*' \ + --capabilities CAPABILITY_NAMED_IAM \ + --region ``` Wait for the CloudFormation stack to finish: ```bash aws cloudformation wait stack-create-complete \ - --stack-name temporal-agentcore-invoke \ + --stack-name \ --region ``` @@ -217,20 +162,19 @@ Then retrieve the invocation role ARN: ```bash aws cloudformation describe-stacks \ - --stack-name temporal-agentcore-invoke \ + --stack-name \ --query 'Stacks[0].Outputs[?OutputKey==`RoleARN`].OutputValue' \ --output text \ --region ``` The role grants `bedrock-agentcore:InvokeAgentRuntime` and `bedrock-agentcore:GetAgentRuntimeEndpoint` on the configured -Runtime resources. Record the role ARN for the next step. +Runtime resources. This role does not run the Worker code. -## 5. Create the Worker Deployment Version {/* #create-worker-deployment-version */} +## 4. Create the Worker Deployment Version {/* #create-worker-deployment-version */} Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) whose compute -configuration points to the named AgentCore Runtime endpoint. The deployment name and Build ID must match the Runtime -environment variables from [Step 2](#configure-runtime). +configuration points to the named AgentCore Runtime endpoint. @@ -238,15 +182,15 @@ environment variables from [Step 2](#configure-runtime). In the Temporal Cloud UI, open your Namespace and select **Workers** > **Create Worker Deployment**. Provide these values: -- **Name**: `agentcore-strands-agent-python`. -- **Build ID**: `1.0.0`. +- **Name**: the value of `TEMPORAL_DEPLOYMENT_NAME` in the Runtime environment. +- **Build ID**: the value of `TEMPORAL_BUILD_ID` in the Runtime environment. - **Compute Provider**: select **Amazon Bedrock AgentCore Runtime**. -- **Runtime endpoint ARN**: the named endpoint ARN from [Step 3](#deploy-runtime). -- **IAM role ARN**: the invocation role ARN from [Step 4](#configure-iam). -- **External ID**: the External ID from [Step 4](#configure-iam). +- **Runtime endpoint ARN**: the named endpoint ARN from [Step 2](#deploy-runtime). +- **IAM role ARN**: the invocation role ARN from [Step 3](#configure-iam). +- **External ID**: the External ID from [Step 3](#configure-iam). Save the Worker Deployment. When you create a version through the UI, the version is automatically current. Continue -to [Step 7](#verify-deployment). +to [Step 6](#verify-worker-startup). @@ -256,7 +200,7 @@ First, create the Worker Deployment if it does not already exist: ```bash temporal worker deployment create \ --namespace \ - --name agentcore-strands-agent-python + --name ``` Then create the version with the AgentCore compute configuration: @@ -264,20 +208,14 @@ Then create the version with the AgentCore compute configuration: ```bash temporal worker deployment create-version \ --namespace \ - --deployment-name agentcore-strands-agent-python \ - --build-id 1.0.0 \ + --deployment-name \ + --build-id \ --aws-agentcore-endpoint-arn \ --aws-agentcore-assume-role-arn \ --aws-agentcore-assume-role-external-id ``` -| Flag | Description | -| --- | --- | -| `--deployment-name` | Worker Deployment name. Must match `TEMPORAL_DEPLOYMENT_NAME` in the Runtime environment. | -| `--build-id` | Worker Deployment Version Build ID. Must match `TEMPORAL_BUILD_ID` in the Runtime environment. | -| `--aws-agentcore-endpoint-arn` | ARN of the named AgentCore Runtime endpoint that Temporal invokes. | -| `--aws-agentcore-assume-role-arn` | IAM role Temporal assumes to invoke the endpoint. This is the CloudFormation stack output from Step 4, not the Runtime execution role. | -| `--aws-agentcore-assume-role-external-id` | External ID configured in the invocation role trust policy. | +The deployment name and Build ID must match the values in the Runtime environment. @@ -286,46 +224,29 @@ To check whether Temporal can reach the endpoint, open the Worker Deployment Ver select **Actions** > **Validate Connection**. This checks that Temporal can assume the invocation role, get the named endpoint, and invoke the Runtime. -## 6. Set the version as current {/* #set-current-version */} +## 5. Set the version as current {/* #set-current-version */} If you used the Temporal CLI, set the version as current: ```bash temporal worker deployment set-current-version \ --namespace \ - --deployment-name agentcore-strands-agent-python \ - --build-id 1.0.0 + --deployment-name \ + --build-id ``` This command asks you to confirm because it changes which version receives new Tasks. Pass `--yes` to skip the prompt. If you created the version in the Temporal Cloud UI, it is already current. -## 7. Verify the deployment {/* #verify-deployment */} +## 6. Verify Worker startup {/* #verify-worker-startup */} -Install the sample dependencies and export the same Temporal connection values that you configured for the Runtime: - -```bash -uv sync -export TEMPORAL_ADDRESS=..tmprl.cloud:7233 -export TEMPORAL_NAMESPACE=. -export TEMPORAL_API_KEY= -``` - -Start the sample Workflow from your local machine: - -```bash -uv run python starter.py "Calculate the first 10 Fibonacci numbers." -``` - -The starter sends the Workflow to `agentcore-strands-task-queue`. When no Worker is polling, Temporal invokes -the AgentCore Runtime endpoint. The Runtime starts the Worker, and the Worker processes the Workflow and its Activities. +Submit work to the configured Task Queue using your application. When no Worker is polling, Temporal invokes the named +AgentCore Runtime endpoint. The Runtime starts the Worker, and the Worker polls and processes Tasks. You can confirm the deployment in these places: -- **Temporal Cloud UI**: Open the Workflow Execution and confirm that its Event History progresses. -- **AgentCore logs**: Run `agentcore logs --runtime temporal_strands_worker` to see the Worker start and process Tasks. -- **Temporal CLI**: Run `temporal workflow show --workflow-id agentcore-strands-workflow-id-1` to inspect the Event - History. - -For the sample Workflow and agent implementation, see -[`workflows.py`](https://github.com/temporalio/samples-python/blob/6fd0c9e879e85c4403bf8c1c591f7394fa60bed2/bedrock_agentcore/strands-agent/workflows.py). +- **Temporal Cloud UI**: Open the Worker Deployment Version and confirm that the connection is valid and a Worker has + polled the Task Queue. +- **AgentCore logs**: Run `agentcore logs --runtime ` to see the Worker start and process Tasks. +- **Temporal CLI**: Run `temporal worker deployment describe --name ` to inspect the deployment and + current version. diff --git a/sidebars.js b/sidebars.js index f30fabd864..459d22e436 100644 --- a/sidebars.js +++ b/sidebars.js @@ -2179,6 +2179,7 @@ module.exports = { id: 'guides/index', }, items: [ + 'guides/durable-agent-on-agentcore', 'guides/entity-pattern-loyalty-points', 'guides/recover-without-restart', 'guides/route-specialized-workloads', diff --git a/snipsync.config.yaml b/snipsync.config.yaml index a1b6ba5c4f..5968edbb38 100644 --- a/snipsync.config.yaml +++ b/snipsync.config.yaml @@ -39,6 +39,9 @@ origins: ref: 'main' - owner: temporalio repo: sdk-go + - owner: temporalio + repo: documentation-sdk-code-examples + ref: docs/durable-agent-agentcore-sample targets: - docs diff --git a/src/components/GuidesGrid/guides-data.json b/src/components/GuidesGrid/guides-data.json index 9f05d5194e..8e6641a888 100644 --- a/src/components/GuidesGrid/guides-data.json +++ b/src/components/GuidesGrid/guides-data.json @@ -1,4 +1,13 @@ [ + { + "name": "Durable agent on AgentCore", + "description": + "Run a long-lived Strands agent with Temporal and serverless Worker compute on Amazon Bedrock AgentCore.", + "tags": ["AI agents"], + "sdk": "Python", + "href": "/guides/durable-agent-on-agentcore" + }, + { "name": "Customer loyalty program", "description": diff --git a/static/files/temporal-cloud-serverless-worker-agentcore-role.yaml b/static/files/temporal-cloud-serverless-worker-agentcore-role.yaml new file mode 100644 index 0000000000..a4d5b16504 --- /dev/null +++ b/static/files/temporal-cloud-serverless-worker-agentcore-role.yaml @@ -0,0 +1,69 @@ +# CloudFormation template for creating an IAM role that Temporal Cloud can assume to invoke AgentCore runtimes. +AWSTemplateFormatVersion: '2010-09-09' +Description: + Creates an IAM role that Temporal Cloud can assume to invoke Amazon Bedrock AgentCore runtimes for Serverless Workers. + +Parameters: + AssumeRoleExternalId: + Type: String + Description: A string you choose. Use the same value when creating the Worker Deployment Version. + AllowedPattern: '[a-zA-Z0-9_+=,.@-]*' + MinLength: 5 + MaxLength: 45 + + AgentRuntimeARNs: + Type: CommaDelimitedList + Description: >- + Comma-separated list of AgentCore Runtime ARNs that Temporal may invoke. Append a wildcard to each Runtime ARN + to include its endpoints. + + RoleName: + Type: String + Default: 'Temporal-Cloud-Serverless-Worker' + +Resources: + TemporalCloudServerlessWorker: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${RoleName}-${AWS::StackName}' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + AWS: + - arn:aws:iam::902542641901:role/wci-lambda-invoke + - arn:aws:iam::160190466495:role/wci-lambda-invoke + - arn:aws:iam::819232936619:role/wci-lambda-invoke + - arn:aws:iam::829909441867:role/wci-lambda-invoke + - arn:aws:iam::354116250941:role/wci-lambda-invoke + Action: sts:AssumeRole + Condition: + StringEquals: + 'sts:ExternalId': !Ref AssumeRoleExternalId + Description: The role Temporal Cloud uses to invoke AgentCore runtimes for Serverless Workers + MaxSessionDuration: 3600 + + TemporalCloudAgentCoreInvokePermissions: + Type: AWS::IAM::Policy + Properties: + PolicyName: 'Temporal-Cloud-AgentCore-Invoke-Permissions' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - bedrock-agentcore:InvokeAgentRuntime + - bedrock-agentcore:GetAgentRuntimeEndpoint + Resource: !Ref AgentRuntimeARNs + Roles: + - !Ref TemporalCloudServerlessWorker + +Outputs: + RoleARN: + Description: The ARN of the IAM role created for Temporal Cloud + Value: !GetAtt TemporalCloudServerlessWorker.Arn + + AgentRuntimeARNs: + Description: The AgentCore Runtime ARNs that Temporal may invoke + Value: !Join [', ', !Ref AgentRuntimeARNs]