From 8cf50af4c241606de111a1d1f08689c3f598fc60 Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 14:45:24 +0000 Subject: [PATCH 01/10] fix(templates): allow patched MCP 1.x releases --- .../export-harness-python/pyproject.toml | 2 +- .../templates/mcp-python-fastmcp/pyproject.toml | 2 +- src/core/project/manager.export.test.ts | 10 ++++++++++ src/core/project/manager.test.ts | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/assets/templates/export-harness-python/pyproject.toml b/src/assets/templates/export-harness-python/pyproject.toml index 29262d7157..38b54210f6 100644 --- a/src/assets/templates/export-harness-python/pyproject.toml +++ b/src/assets/templates/export-harness-python/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", "bedrock-agentcore ~= 1.9.1", "botocore[crt] ~= 1.43.0", - "mcp >= 1.23.0, < 2.0.0", + "mcp >= 1.28.1, < 2.0.0", {{#if bedrockMantle}}"aws-bedrock-token-generator >= 1.1.0, < 2.0.0", {{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} ~= 1.54.0", diff --git a/src/assets/templates/mcp-python-fastmcp/pyproject.toml b/src/assets/templates/mcp-python-fastmcp/pyproject.toml index 960168bc5d..f9febaf96e 100644 --- a/src/assets/templates/mcp-python-fastmcp/pyproject.toml +++ b/src/assets/templates/mcp-python-fastmcp/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "mcp ~= 1.24.0", + "mcp >= 1.28.1, < 2.0.0", ] [tool.hatch.build.targets.wheel] diff --git a/src/core/project/manager.export.test.ts b/src/core/project/manager.export.test.ts index de3f458eca..c1fbc0436d 100644 --- a/src/core/project/manager.export.test.ts +++ b/src/core/project/manager.export.test.ts @@ -86,6 +86,16 @@ function exportInput(overrides: Partial = {}): ExportHarness } describe("FsProjectManager.exportHarness rendered tree", () => { + test("an exported harness permits patched MCP 1.x releases", async () => { + const { manager: subject } = manager(); + const project = await projectWithHarness(subject); + + const result = await drain(subject.exportHarness(project, exportInput())); + + const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); + expect(pyproject).toContain('"mcp >= 1.28.1, < 2.0.0"'); + }); + test("loads only the MCP tools allowedTools selects", async () => { const { manager: subject } = manager(); const project = await projectWithHarness(subject, { diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index fd7f490115..c419f74d0f 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -137,6 +137,21 @@ async function projectManifest(projectRoot: string): Promise { } describe("FsProjectManager.create", () => { + test("a generated FastMCP project permits patched MCP 1.x releases", async () => { + const directory = await inTempDirectory(); + const runtime = resolveRuntimeTemplateShortcut("mcp-python-fastmcp"); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: runtime, + }); + + const pyproject = await readFile( + join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), + "utf8", + ); + expect(pyproject).toContain('"mcp >= 1.28.1, < 2.0.0"'); + }); + test("scaffolds the expected file tree into a fresh directory", async () => { const directory = await inTempDirectory(); await runCreate(manager().manager, { From 2c8ccafe1f40f98e2570f3b36d4ae9353a1a64be Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 14:49:54 +0000 Subject: [PATCH 02/10] fix(templates): require patched AgentCore Python SDK versions --- .../a2a-python-strands/pyproject.toml | 2 +- .../agent-python-minimal/pyproject.toml | 2 +- .../agent-python-strands/pyproject.toml | 2 +- .../agui-python-strands/pyproject.toml | 2 +- .../export-harness-python/pyproject.toml | 2 +- src/core/project/manager.export.test.ts | 10 ++++++++ src/core/project/manager.test.ts | 24 +++++++++++++++++++ 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/assets/templates/a2a-python-strands/pyproject.toml b/src/assets/templates/a2a-python-strands/pyproject.toml index ede95b864b..6b2b79e750 100644 --- a/src/assets/templates/a2a-python-strands/pyproject.toml +++ b/src/assets/templates/a2a-python-strands/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.10" dependencies = [ "a2a-sdk[all] ~= 0.3.0", "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore[a2a] ~= 1.9.1", + "bedrock-agentcore[a2a] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", "strands-agents ~= 1.15.0", ] diff --git a/src/assets/templates/agent-python-minimal/pyproject.toml b/src/assets/templates/agent-python-minimal/pyproject.toml index c7b4e3ca28..381256f357 100644 --- a/src/assets/templates/agent-python-minimal/pyproject.toml +++ b/src/assets/templates/agent-python-minimal/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", ] diff --git a/src/assets/templates/agent-python-strands/pyproject.toml b/src/assets/templates/agent-python-strands/pyproject.toml index e214929654..b6ce3124c1 100644 --- a/src/assets/templates/agent-python-strands/pyproject.toml +++ b/src/assets/templates/agent-python-strands/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", {{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] ~= 1.15.0", {{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] ~= 1.15.0", diff --git a/src/assets/templates/agui-python-strands/pyproject.toml b/src/assets/templates/agui-python-strands/pyproject.toml index ab778db754..b69f58b296 100644 --- a/src/assets/templates/agui-python-strands/pyproject.toml +++ b/src/assets/templates/agui-python-strands/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "ag-ui-strands >= 0.3.0, < 0.4.0", "ag-ui-protocol >= 0.1.19, < 0.2.0", "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", "strands-agents ~= 1.15.0", "uvicorn >= 0.34.3, < 1.0.0", diff --git a/src/assets/templates/export-harness-python/pyproject.toml b/src/assets/templates/export-harness-python/pyproject.toml index 38b54210f6..dc1bddc3a6 100644 --- a/src/assets/templates/export-harness-python/pyproject.toml +++ b/src/assets/templates/export-harness-python/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", "mcp >= 1.28.1, < 2.0.0", {{#if bedrockMantle}}"aws-bedrock-token-generator >= 1.1.0, < 2.0.0", diff --git a/src/core/project/manager.export.test.ts b/src/core/project/manager.export.test.ts index c1fbc0436d..7732769195 100644 --- a/src/core/project/manager.export.test.ts +++ b/src/core/project/manager.export.test.ts @@ -86,6 +86,16 @@ function exportInput(overrides: Partial = {}): ExportHarness } describe("FsProjectManager.exportHarness rendered tree", () => { + test("an exported harness requires an SDK with the package-installation fix", async () => { + const { manager: subject } = manager(); + const project = await projectWithHarness(subject); + + const result = await drain(subject.exportHarness(project, exportInput())); + + const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); + expect(pyproject).toContain('"bedrock-agentcore >= 1.18.1, < 2.0.0"'); + }); + test("an exported harness permits patched MCP 1.x releases", async () => { const { manager: subject } = manager(); const project = await projectWithHarness(subject); diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index c419f74d0f..38dc17ce28 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -137,6 +137,30 @@ async function projectManifest(projectRoot: string): Promise { } describe("FsProjectManager.create", () => { + test.each([ + ["agent-python-minimal", "bedrock-agentcore"], + ["agent-python-strands", "bedrock-agentcore"], + ["agent-python-strands-container", "bedrock-agentcore"], + ["a2a-python-strands", "bedrock-agentcore[a2a]"], + ["agui-python-strands", "bedrock-agentcore"], + ] as const)( + "%s requires an SDK with the package-installation fix", + async (template, dependency) => { + const directory = await inTempDirectory(); + const runtime = resolveRuntimeTemplateShortcut(template); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: runtime, + }); + + const pyproject = await readFile( + join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), + "utf8", + ); + expect(pyproject).toContain(`"${dependency} >= 1.18.1, < 2.0.0"`); + }, + ); + test("a generated FastMCP project permits patched MCP 1.x releases", async () => { const directory = await inTempDirectory(); const runtime = resolveRuntimeTemplateShortcut("mcp-python-fastmcp"); From bec72a2f3c9d04d8269f7a66295a385ebc365bbd Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 14:52:23 +0000 Subject: [PATCH 03/10] fix(container): apply Debian package updates during builds --- .../agent-python-strands/Dockerfile.template | 2 ++ src/core/project/manager.test.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/assets/templates/agent-python-strands/Dockerfile.template b/src/assets/templates/agent-python-strands/Dockerfile.template index cb3569eff2..7eb3bafbdd 100644 --- a/src/assets/templates/agent-python-strands/Dockerfile.template +++ b/src/assets/templates/agent-python-strands/Dockerfile.template @@ -1,5 +1,7 @@ FROM public.ecr.aws/docker/library/python:3.12-slim-trixie +RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* + RUN pip install --no-cache-dir uv ARG UV_DEFAULT_INDEX diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index 38dc17ce28..0a1ca3f11b 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -329,6 +329,24 @@ describe("FsProjectManager.create", () => { expect(spec.runtimes[0]).toMatchObject({ build: "Container", dockerfile: "Dockerfile" }); }); + test("container scaffolds apply OS updates before switching to the runtime user", async () => { + const directory = await inTempDirectory(); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: AGENT_PYTHON_STRANDS_CONTAINER, + }); + + const dockerfile = await readFile( + join(directory, "example", "app", AGENT_PYTHON_STRANDS_CONTAINER.runtimeName, "Dockerfile"), + "utf8", + ); + expect(dockerfile).toContain("apt-get update"); + expect(dockerfile).toContain("apt-get upgrade -y"); + expect(dockerfile.indexOf("apt-get upgrade -y")).toBeLessThan( + dockerfile.indexOf("USER bedrock_agentcore"), + ); + }); + test("scaffolds the Bedrock Managed Agents environment and runtime defaults", async () => { const directory = await inTempDirectory(); const setup = manager(); From 91c4b20617d40dc3cfa7c3575d83ae4901b042e0 Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 14:54:29 +0000 Subject: [PATCH 04/10] fix(container): omit uv cache from runtime images --- .../agent-python-strands/Dockerfile.template | 1 + src/core/project/manager.test.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/assets/templates/agent-python-strands/Dockerfile.template b/src/assets/templates/agent-python-strands/Dockerfile.template index 7eb3bafbdd..2b7247f867 100644 --- a/src/assets/templates/agent-python-strands/Dockerfile.template +++ b/src/assets/templates/agent-python-strands/Dockerfile.template @@ -10,6 +10,7 @@ ARG UV_INDEX WORKDIR /app ENV UV_SYSTEM_PYTHON=1 \ + UV_NO_CACHE=1 \ UV_COMPILE_BYTECODE=1 \ UV_NO_PROGRESS=1 \ PYTHONUNBUFFERED=1 \ diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index 0a1ca3f11b..c62fd15874 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -347,6 +347,23 @@ describe("FsProjectManager.create", () => { ); }); + test("container scaffolds disable uv caching before installing locked dependencies", async () => { + const directory = await inTempDirectory(); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: AGENT_PYTHON_STRANDS_CONTAINER, + }); + + const dockerfile = await readFile( + join(directory, "example", "app", AGENT_PYTHON_STRANDS_CONTAINER.runtimeName, "Dockerfile"), + "utf8", + ); + expect(dockerfile).toContain("UV_NO_CACHE=1"); + expect(dockerfile.indexOf("UV_NO_CACHE=1")).toBeLessThan( + dockerfile.indexOf("RUN uv sync --frozen"), + ); + }); + test("scaffolds the Bedrock Managed Agents environment and runtime defaults", async () => { const directory = await inTempDirectory(); const setup = manager(); From df07597931cd097aafab87ff6eebfb017cdaee01 Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 14:59:51 +0000 Subject: [PATCH 05/10] fix(container): remove uv after dependency installation --- README.md | 1 + docs/python-project-updates.md | 32 +++++++++++++++++++ .../agent-python-strands/Dockerfile.template | 2 +- src/core/project/manager.test.ts | 19 +++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 docs/python-project-updates.md diff --git a/README.md b/README.md index ad159860f9..c43db9797c 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ declares, not the ones you add in the stack. - [Command reference](command.md): every command and flag. - [Harness project configuration](docs/harness-project-configuration.md): Harness YAML, prompts, tools, skills, and environment settings. +- [Python project updates](docs/python-project-updates.md): dependency upgrades and container rebuilds for existing generated projects. - [China regions](docs/china-regions.md): what is available there and how the restrictions are enforced. - [Amazon Bedrock AgentCore documentation](https://docs.aws.amazon.com/bedrock-agentcore/): service guides and API references. - [Contributing](CONTRIBUTING.md): development, builds, architecture, and testing. diff --git a/docs/python-project-updates.md b/docs/python-project-updates.md new file mode 100644 index 0000000000..f5eaee7d05 --- /dev/null +++ b/docs/python-project-updates.md @@ -0,0 +1,32 @@ +# Updating Generated Python Projects + +Updating the CLI does not rewrite an existing project's dependency constraints, lockfile, or +Dockerfile. Update affected entries in the Runtime's `pyproject.toml` dependency list, keeping its +other dependencies and package extras: + +```toml +dependencies = [ + "mcp >= 1.28.1, < 2.0.0", + "bedrock-agentcore >= 1.18.1, < 2.0.0", +] +``` + +Run this from the Runtime's code directory to refresh the locked versions: + +```bash +uv lock --upgrade +``` + +The Strands container template installs available Debian package updates, uses +`uv sync --frozen --no-dev`, disables uv caching, and removes the globally installed uv after the +final dependency installation. Its entrypoint runs Python directly. + +For an older generated Dockerfile, apply the equivalent changes before `USER bedrock_agentcore`. +Set `UV_NO_CACHE=1` before both sync steps. Remove uv only after the final sync, and keep it if your +custom application invokes it at runtime. The Bedrock Managed Agents template uses uv at startup +and is not covered by this cleanup. + +Rebuild and redeploy the changed image, test the application, and inspect its package versions and +scan findings. Docker can reuse cached base images and OS-update layers on later builds; when +building manually, use `docker build --pull --no-cache` to refresh them. An unchanged deployment +does not establish that packages were updated. diff --git a/src/assets/templates/agent-python-strands/Dockerfile.template b/src/assets/templates/agent-python-strands/Dockerfile.template index 2b7247f867..319ad9ba28 100644 --- a/src/assets/templates/agent-python-strands/Dockerfile.template +++ b/src/assets/templates/agent-python-strands/Dockerfile.template @@ -25,7 +25,7 @@ COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-dev --no-install-project COPY --chown=bedrock_agentcore:bedrock_agentcore . . -RUN uv sync --frozen --no-dev +RUN uv sync --frozen --no-dev && /usr/local/bin/python -m pip uninstall -y uv USER bedrock_agentcore diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index c62fd15874..12b09fa2df 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -364,6 +364,25 @@ describe("FsProjectManager.create", () => { ); }); + test("container scaffolds remove global uv after syncing and before changing users", async () => { + const directory = await inTempDirectory(); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: AGENT_PYTHON_STRANDS_CONTAINER, + }); + + const dockerfile = await readFile( + join(directory, "example", "app", AGENT_PYTHON_STRANDS_CONTAINER.runtimeName, "Dockerfile"), + "utf8", + ); + const uninstall = "/usr/local/bin/python -m pip uninstall -y uv"; + expect(dockerfile).toContain(uninstall); + expect(dockerfile.lastIndexOf("uv sync --frozen")).toBeLessThan(dockerfile.indexOf(uninstall)); + expect(dockerfile.indexOf(uninstall)).toBeLessThan( + dockerfile.indexOf("USER bedrock_agentcore"), + ); + }); + test("scaffolds the Bedrock Managed Agents environment and runtime defaults", async () => { const directory = await inTempDirectory(); const setup = manager(); From 4326700fcdc03810f1b820514f868b53fb579a9d Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 15:28:49 +0000 Subject: [PATCH 06/10] fix(templates): honor SDK Strands integration dependencies --- docs/python-project-updates.md | 5 ++ .../a2a-python-strands/pyproject.toml | 4 +- .../agent-python-strands/pyproject.toml | 12 ++--- .../agui-python-strands/pyproject.toml | 4 +- .../export-harness-python/pyproject.toml | 4 +- src/core/project/manager.export.test.ts | 20 +++++-- src/core/project/manager.test.ts | 53 +++++++++++++++++-- 7 files changed, 81 insertions(+), 21 deletions(-) diff --git a/docs/python-project-updates.md b/docs/python-project-updates.md index f5eaee7d05..9c6c522b42 100644 --- a/docs/python-project-updates.md +++ b/docs/python-project-updates.md @@ -17,6 +17,11 @@ Run this from the Runtime's code directory to refresh the locked versions: uv lock --upgrade ``` +Strands projects using the AgentCore Memory integration should enable the SDK's `strands-agents` +extra, for example `bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0`. This extra declares the +compatible Strands dependency. Replace older `strands-agents ~= 1.15.0` constraints with +`strands-agents >= 1.15.0, < 2.0.0`, preserving any model-provider extras, then refresh the lockfile. + The Strands container template installs available Debian package updates, uses `uv sync --frozen --no-dev`, disables uv caching, and removes the globally installed uv after the final dependency installation. Its entrypoint runs Python directly. diff --git a/src/assets/templates/a2a-python-strands/pyproject.toml b/src/assets/templates/a2a-python-strands/pyproject.toml index 6b2b79e750..fe05495bd4 100644 --- a/src/assets/templates/a2a-python-strands/pyproject.toml +++ b/src/assets/templates/a2a-python-strands/pyproject.toml @@ -11,9 +11,9 @@ requires-python = ">=3.10" dependencies = [ "a2a-sdk[all] ~= 0.3.0", "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore[a2a] >= 1.18.1, < 2.0.0", + "bedrock-agentcore[a2a,strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - "strands-agents ~= 1.15.0", + "strands-agents >= 1.15.0, < 2.0.0", ] [tool.hatch.build.targets.wheel] diff --git a/src/assets/templates/agent-python-strands/pyproject.toml b/src/assets/templates/agent-python-strands/pyproject.toml index b6ce3124c1..61ad4cf53d 100644 --- a/src/assets/templates/agent-python-strands/pyproject.toml +++ b/src/assets/templates/agent-python-strands/pyproject.toml @@ -10,13 +10,13 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore >= 1.18.1, < 2.0.0", + "bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - {{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] ~= 1.15.0", - {{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] ~= 1.15.0", - {{else}}{{#if (eq modelProvider "Gemini")}}"strands-agents[gemini] ~= 1.15.0", - {{else}}{{#if (eq modelProvider "LiteLLM")}}"strands-agents[litellm] ~= 1.15.0", - {{else}}"strands-agents ~= 1.15.0", + {{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] >= 1.15.0, < 2.0.0", + {{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] >= 1.15.0, < 2.0.0", + {{else}}{{#if (eq modelProvider "Gemini")}}"strands-agents[gemini] >= 1.15.0, < 2.0.0", + {{else}}{{#if (eq modelProvider "LiteLLM")}}"strands-agents[litellm] >= 1.15.0, < 2.0.0", + {{else}}"strands-agents >= 1.15.0, < 2.0.0", {{/if}}{{/if}}{{/if}}{{/if}} ] diff --git a/src/assets/templates/agui-python-strands/pyproject.toml b/src/assets/templates/agui-python-strands/pyproject.toml index b69f58b296..96eaa3e652 100644 --- a/src/assets/templates/agui-python-strands/pyproject.toml +++ b/src/assets/templates/agui-python-strands/pyproject.toml @@ -12,9 +12,9 @@ dependencies = [ "ag-ui-strands >= 0.3.0, < 0.4.0", "ag-ui-protocol >= 0.1.19, < 0.2.0", "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore >= 1.18.1, < 2.0.0", + "bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - "strands-agents ~= 1.15.0", + "strands-agents >= 1.15.0, < 2.0.0", "uvicorn >= 0.34.3, < 1.0.0", ] diff --git a/src/assets/templates/export-harness-python/pyproject.toml b/src/assets/templates/export-harness-python/pyproject.toml index dc1bddc3a6..3649109d27 100644 --- a/src/assets/templates/export-harness-python/pyproject.toml +++ b/src/assets/templates/export-harness-python/pyproject.toml @@ -10,11 +10,11 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore >= 1.18.1, < 2.0.0", + "bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", "mcp >= 1.28.1, < 2.0.0", {{#if bedrockMantle}}"aws-bedrock-token-generator >= 1.1.0, < 2.0.0", - {{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} ~= 1.54.0", + {{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} >= 1.54.0, < 2.0.0", ] diff --git a/src/core/project/manager.export.test.ts b/src/core/project/manager.export.test.ts index 7732769195..4e49af1b6b 100644 --- a/src/core/project/manager.export.test.ts +++ b/src/core/project/manager.export.test.ts @@ -93,7 +93,17 @@ describe("FsProjectManager.exportHarness rendered tree", () => { const result = await drain(subject.exportHarness(project, exportInput())); const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); - expect(pyproject).toContain('"bedrock-agentcore >= 1.18.1, < 2.0.0"'); + expect(pyproject).toContain('"bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0"'); + }); + + test("an exported harness permits the framework version selected by the SDK integration", async () => { + const { manager: subject } = manager(); + const project = await projectWithHarness(subject); + + const result = await drain(subject.exportHarness(project, exportInput())); + + const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); + expect(pyproject).toContain('"strands-agents >= 1.54.0, < 2.0.0"'); }); test("an exported harness permits patched MCP 1.x releases", async () => { @@ -258,7 +268,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(loadModel).toContain('params["temperature"] = 0.2'); expect(loadModel).toContain('params["top_p"] = 0.8'); const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); - expect(pyproject).toContain('"strands-agents[openai] ~= 1.54.0"'); + expect(pyproject).toContain('"strands-agents[openai] >= 1.54.0, < 2.0.0"'); expect(pyproject).not.toContain('"openai ~= 1.0.0"'); }); @@ -285,7 +295,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(loadModel).toContain('params["top_p"] = 0.9'); expect(loadModel).toContain('params["top_k"] = 20'); expect(await Bun.file(join(result.agentPath, "pyproject.toml")).text()).toContain( - '"strands-agents[gemini] ~= 1.54.0"', + '"strands-agents[gemini] >= 1.54.0, < 2.0.0"', ); }); @@ -310,7 +320,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(loadModel).toContain('params["top_p"] = 0.7'); expect(loadModel).toContain('json.loads("{\\"max_retries\\":2}")'); expect(await Bun.file(join(result.agentPath, "pyproject.toml")).text()).toContain( - '"strands-agents[litellm] ~= 1.54.0"', + '"strands-agents[litellm] >= 1.54.0, < 2.0.0"', ); }); @@ -330,7 +340,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(main).toContain("from strands import AgentSkills"); expect(main).toContain('SlidingWindowConversationManager(**{"window_size":12}, per_turn=True)'); expect(await Bun.file(join(result.agentPath, "pyproject.toml")).text()).toContain( - '"strands-agents ~= 1.54.0"', + '"strands-agents >= 1.54.0, < 2.0.0"', ); }); diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index 12b09fa2df..a3ab8853b8 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -137,12 +137,57 @@ async function projectManifest(projectRoot: string): Promise { } describe("FsProjectManager.create", () => { + test.each([ + ["Bedrock", "strands-agents"], + ["Anthropic", "strands-agents[anthropic]"], + ["OpenAI", "strands-agents[openai]"], + ["Gemini", "strands-agents[gemini]"], + ["LiteLLM", "strands-agents[litellm]"], + ] as const)( + "Strands with %s permits the framework version selected by the SDK integration", + async (modelProvider, dependency) => { + const directory = await inTempDirectory(); + const runtime = resolveRuntimeTemplateShortcut("agent-python-strands", { + modelProvider, + ...(modelProvider !== "Bedrock" && { apiKey: "test-model-key" }), + }); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: runtime, + }); + + const pyproject = await readFile( + join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), + "utf8", + ); + expect(pyproject).toContain(`"${dependency} >= 1.15.0, < 2.0.0"`); + }, + ); + + test.each(["a2a-python-strands", "agui-python-strands"] as const)( + "%s permits the framework version selected by the SDK integration", + async (template) => { + const directory = await inTempDirectory(); + const runtime = resolveRuntimeTemplateShortcut(template); + await runCreate(manager().manager, { + name: "example", + scaffoldRuntimeInput: runtime, + }); + + const pyproject = await readFile( + join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), + "utf8", + ); + expect(pyproject).toContain('"strands-agents >= 1.15.0, < 2.0.0"'); + }, + ); + test.each([ ["agent-python-minimal", "bedrock-agentcore"], - ["agent-python-strands", "bedrock-agentcore"], - ["agent-python-strands-container", "bedrock-agentcore"], - ["a2a-python-strands", "bedrock-agentcore[a2a]"], - ["agui-python-strands", "bedrock-agentcore"], + ["agent-python-strands", "bedrock-agentcore[strands-agents]"], + ["agent-python-strands-container", "bedrock-agentcore[strands-agents]"], + ["a2a-python-strands", "bedrock-agentcore[a2a,strands-agents]"], + ["agui-python-strands", "bedrock-agentcore[strands-agents]"], ] as const)( "%s requires an SDK with the package-installation fix", async (template, dependency) => { From 3671184c136452cfe4bd7342dfb5df1eb07e9bab Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 16:14:37 +0000 Subject: [PATCH 07/10] docs: remove unnecessary Python migration guide --- README.md | 1 - docs/python-project-updates.md | 37 ---------------------------------- 2 files changed, 38 deletions(-) delete mode 100644 docs/python-project-updates.md diff --git a/README.md b/README.md index c43db9797c..ad159860f9 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,6 @@ declares, not the ones you add in the stack. - [Command reference](command.md): every command and flag. - [Harness project configuration](docs/harness-project-configuration.md): Harness YAML, prompts, tools, skills, and environment settings. -- [Python project updates](docs/python-project-updates.md): dependency upgrades and container rebuilds for existing generated projects. - [China regions](docs/china-regions.md): what is available there and how the restrictions are enforced. - [Amazon Bedrock AgentCore documentation](https://docs.aws.amazon.com/bedrock-agentcore/): service guides and API references. - [Contributing](CONTRIBUTING.md): development, builds, architecture, and testing. diff --git a/docs/python-project-updates.md b/docs/python-project-updates.md deleted file mode 100644 index 9c6c522b42..0000000000 --- a/docs/python-project-updates.md +++ /dev/null @@ -1,37 +0,0 @@ -# Updating Generated Python Projects - -Updating the CLI does not rewrite an existing project's dependency constraints, lockfile, or -Dockerfile. Update affected entries in the Runtime's `pyproject.toml` dependency list, keeping its -other dependencies and package extras: - -```toml -dependencies = [ - "mcp >= 1.28.1, < 2.0.0", - "bedrock-agentcore >= 1.18.1, < 2.0.0", -] -``` - -Run this from the Runtime's code directory to refresh the locked versions: - -```bash -uv lock --upgrade -``` - -Strands projects using the AgentCore Memory integration should enable the SDK's `strands-agents` -extra, for example `bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0`. This extra declares the -compatible Strands dependency. Replace older `strands-agents ~= 1.15.0` constraints with -`strands-agents >= 1.15.0, < 2.0.0`, preserving any model-provider extras, then refresh the lockfile. - -The Strands container template installs available Debian package updates, uses -`uv sync --frozen --no-dev`, disables uv caching, and removes the globally installed uv after the -final dependency installation. Its entrypoint runs Python directly. - -For an older generated Dockerfile, apply the equivalent changes before `USER bedrock_agentcore`. -Set `UV_NO_CACHE=1` before both sync steps. Remove uv only after the final sync, and keep it if your -custom application invokes it at runtime. The Bedrock Managed Agents template uses uv at startup -and is not covered by this cleanup. - -Rebuild and redeploy the changed image, test the application, and inspect its package versions and -scan findings. Docker can reuse cached base images and OS-update layers on later builds; when -building manually, use `docker build --pull --no-cache` to refresh them. An unchanged deployment -does not establish that packages were updated. From 9c2ea88bdc2506bd190eea96980664ea2cb5bad1 Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 16:15:20 +0000 Subject: [PATCH 08/10] test(project): remove redundant dependency assertion tests --- src/core/project/manager.export.test.ts | 20 ---- src/core/project/manager.test.ts | 138 ------------------------ 2 files changed, 158 deletions(-) diff --git a/src/core/project/manager.export.test.ts b/src/core/project/manager.export.test.ts index 4e49af1b6b..8d70ddfef4 100644 --- a/src/core/project/manager.export.test.ts +++ b/src/core/project/manager.export.test.ts @@ -86,16 +86,6 @@ function exportInput(overrides: Partial = {}): ExportHarness } describe("FsProjectManager.exportHarness rendered tree", () => { - test("an exported harness requires an SDK with the package-installation fix", async () => { - const { manager: subject } = manager(); - const project = await projectWithHarness(subject); - - const result = await drain(subject.exportHarness(project, exportInput())); - - const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); - expect(pyproject).toContain('"bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0"'); - }); - test("an exported harness permits the framework version selected by the SDK integration", async () => { const { manager: subject } = manager(); const project = await projectWithHarness(subject); @@ -106,16 +96,6 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(pyproject).toContain('"strands-agents >= 1.54.0, < 2.0.0"'); }); - test("an exported harness permits patched MCP 1.x releases", async () => { - const { manager: subject } = manager(); - const project = await projectWithHarness(subject); - - const result = await drain(subject.exportHarness(project, exportInput())); - - const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); - expect(pyproject).toContain('"mcp >= 1.28.1, < 2.0.0"'); - }); - test("loads only the MCP tools allowedTools selects", async () => { const { manager: subject } = manager(); const project = await projectWithHarness(subject, { diff --git a/src/core/project/manager.test.ts b/src/core/project/manager.test.ts index a3ab8853b8..fd7f490115 100644 --- a/src/core/project/manager.test.ts +++ b/src/core/project/manager.test.ts @@ -137,90 +137,6 @@ async function projectManifest(projectRoot: string): Promise { } describe("FsProjectManager.create", () => { - test.each([ - ["Bedrock", "strands-agents"], - ["Anthropic", "strands-agents[anthropic]"], - ["OpenAI", "strands-agents[openai]"], - ["Gemini", "strands-agents[gemini]"], - ["LiteLLM", "strands-agents[litellm]"], - ] as const)( - "Strands with %s permits the framework version selected by the SDK integration", - async (modelProvider, dependency) => { - const directory = await inTempDirectory(); - const runtime = resolveRuntimeTemplateShortcut("agent-python-strands", { - modelProvider, - ...(modelProvider !== "Bedrock" && { apiKey: "test-model-key" }), - }); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: runtime, - }); - - const pyproject = await readFile( - join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), - "utf8", - ); - expect(pyproject).toContain(`"${dependency} >= 1.15.0, < 2.0.0"`); - }, - ); - - test.each(["a2a-python-strands", "agui-python-strands"] as const)( - "%s permits the framework version selected by the SDK integration", - async (template) => { - const directory = await inTempDirectory(); - const runtime = resolveRuntimeTemplateShortcut(template); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: runtime, - }); - - const pyproject = await readFile( - join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), - "utf8", - ); - expect(pyproject).toContain('"strands-agents >= 1.15.0, < 2.0.0"'); - }, - ); - - test.each([ - ["agent-python-minimal", "bedrock-agentcore"], - ["agent-python-strands", "bedrock-agentcore[strands-agents]"], - ["agent-python-strands-container", "bedrock-agentcore[strands-agents]"], - ["a2a-python-strands", "bedrock-agentcore[a2a,strands-agents]"], - ["agui-python-strands", "bedrock-agentcore[strands-agents]"], - ] as const)( - "%s requires an SDK with the package-installation fix", - async (template, dependency) => { - const directory = await inTempDirectory(); - const runtime = resolveRuntimeTemplateShortcut(template); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: runtime, - }); - - const pyproject = await readFile( - join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), - "utf8", - ); - expect(pyproject).toContain(`"${dependency} >= 1.18.1, < 2.0.0"`); - }, - ); - - test("a generated FastMCP project permits patched MCP 1.x releases", async () => { - const directory = await inTempDirectory(); - const runtime = resolveRuntimeTemplateShortcut("mcp-python-fastmcp"); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: runtime, - }); - - const pyproject = await readFile( - join(directory, "example", "app", runtime.runtimeName, "pyproject.toml"), - "utf8", - ); - expect(pyproject).toContain('"mcp >= 1.28.1, < 2.0.0"'); - }); - test("scaffolds the expected file tree into a fresh directory", async () => { const directory = await inTempDirectory(); await runCreate(manager().manager, { @@ -374,60 +290,6 @@ describe("FsProjectManager.create", () => { expect(spec.runtimes[0]).toMatchObject({ build: "Container", dockerfile: "Dockerfile" }); }); - test("container scaffolds apply OS updates before switching to the runtime user", async () => { - const directory = await inTempDirectory(); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: AGENT_PYTHON_STRANDS_CONTAINER, - }); - - const dockerfile = await readFile( - join(directory, "example", "app", AGENT_PYTHON_STRANDS_CONTAINER.runtimeName, "Dockerfile"), - "utf8", - ); - expect(dockerfile).toContain("apt-get update"); - expect(dockerfile).toContain("apt-get upgrade -y"); - expect(dockerfile.indexOf("apt-get upgrade -y")).toBeLessThan( - dockerfile.indexOf("USER bedrock_agentcore"), - ); - }); - - test("container scaffolds disable uv caching before installing locked dependencies", async () => { - const directory = await inTempDirectory(); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: AGENT_PYTHON_STRANDS_CONTAINER, - }); - - const dockerfile = await readFile( - join(directory, "example", "app", AGENT_PYTHON_STRANDS_CONTAINER.runtimeName, "Dockerfile"), - "utf8", - ); - expect(dockerfile).toContain("UV_NO_CACHE=1"); - expect(dockerfile.indexOf("UV_NO_CACHE=1")).toBeLessThan( - dockerfile.indexOf("RUN uv sync --frozen"), - ); - }); - - test("container scaffolds remove global uv after syncing and before changing users", async () => { - const directory = await inTempDirectory(); - await runCreate(manager().manager, { - name: "example", - scaffoldRuntimeInput: AGENT_PYTHON_STRANDS_CONTAINER, - }); - - const dockerfile = await readFile( - join(directory, "example", "app", AGENT_PYTHON_STRANDS_CONTAINER.runtimeName, "Dockerfile"), - "utf8", - ); - const uninstall = "/usr/local/bin/python -m pip uninstall -y uv"; - expect(dockerfile).toContain(uninstall); - expect(dockerfile.lastIndexOf("uv sync --frozen")).toBeLessThan(dockerfile.indexOf(uninstall)); - expect(dockerfile.indexOf(uninstall)).toBeLessThan( - dockerfile.indexOf("USER bedrock_agentcore"), - ); - }); - test("scaffolds the Bedrock Managed Agents environment and runtime defaults", async () => { const directory = await inTempDirectory(); const setup = manager(); From 79007665ac44880d643730194297ae32e86ceab3 Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 18:53:07 +0000 Subject: [PATCH 09/10] test(templates): guard Python security ranges and container cleanup --- .../project/templates/pythonSecurity.test.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/core/project/templates/pythonSecurity.test.ts diff --git a/src/core/project/templates/pythonSecurity.test.ts b/src/core/project/templates/pythonSecurity.test.ts new file mode 100644 index 0000000000..c5ff662258 --- /dev/null +++ b/src/core/project/templates/pythonSecurity.test.ts @@ -0,0 +1,44 @@ +import { expect, test } from "bun:test"; +import { FsAssetSource } from "../source"; +import { HandlebarsTemplateRenderer } from "./renderer"; + +const source = new FsAssetSource(); +const renderer = new HandlebarsTemplateRenderer(); + +test.each([ + ["agent-python-minimal", ["bedrock-agentcore >= 1.18.1, < 2.0.0"]], + ["agent-python-strands", ["bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0"]], + ["a2a-python-strands", ["bedrock-agentcore[a2a,strands-agents] >= 1.18.1, < 2.0.0"]], + ["agui-python-strands", ["bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0"]], + [ + "export-harness-python", + ["bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "mcp >= 1.28.1, < 2.0.0"], + ], + ["mcp-python-fastmcp", ["mcp >= 1.28.1, < 2.0.0"]], +] as const)("%s retains patched Python dependency ranges", async (template, dependencies) => { + const pyproject = renderer.render(await source.read(`templates/${template}/pyproject.toml`), { + name: "test_agent", + modelProvider: "Bedrock", + }); + + expect(Bun.TOML.parse(pyproject)).toMatchObject({ + project: { dependencies: expect.arrayContaining([...dependencies]) }, + }); +}); + +test("the Strands container updates OS packages and removes uv after installing dependencies", async () => { + const dockerfile = renderer.render( + await source.read("templates/agent-python-strands/Dockerfile.template"), + { entrypoint: "main" }, + ); + + expect(dockerfile).toMatch( + /RUN apt-get update && apt-get upgrade -y[\s\S]*USER bedrock_agentcore/, + ); + expect(dockerfile).toMatch( + /UV_NO_CACHE=1[\s\S]*RUN uv sync --frozen --no-dev --no-install-project/, + ); + expect(dockerfile).toMatch( + /RUN uv sync --frozen --no-dev && \/usr\/local\/bin\/python -m pip uninstall -y uv[\s\S]*USER bedrock_agentcore/, + ); +}); From caf73bc9190e4dc294021d151f1427abf9dc655a Mon Sep 17 00:00:00 2001 From: Aidan Daly Date: Thu, 1 Oct 2026 18:53:15 +0000 Subject: [PATCH 10/10] fix(deps): patch Fastify audit findings in the lockfile --- bun.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lock b/bun.lock index 8cc361b531..c0a6157ed2 100644 --- a/bun.lock +++ b/bun.lock @@ -835,7 +835,7 @@ "fast-uri": ["fast-uri@4.1.5", "", {}, "sha512-vZeoMRB4epNr7QfdHxel7te/RcX16CxyXI07JCCTFWZA2s4v1azGNESRj+2EoaHSaWFL/Z3GmKT2jF6A202jLg=="], - "fastify": ["fastify@5.12.1", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^7.0.0", "find-my-way": "^9.6.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.1.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-FWi+tQvwxR/PeRX7Z2mhfEF5ozJ3jn9asiiclzKXNSzJRHAYcU924aIOKAdHFJ+YIKieh3cqr1IwCOvTr41B3Q=="], + "fastify": ["fastify@5.12.5", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^7.0.0", "find-my-way": "^9.6.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.1.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-OB2k1dlxs5/NAABqeKV2FUHkSD2BbENsCak8yULVcymn3fHIPDVa9TI3SDnJSWYSllZmSYuZXy2gTnsT+Sut1A=="], "fastify-plugin": ["fastify-plugin@5.1.0", "", {}, "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw=="],