Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ These are the abstractions that span many files; understanding them is what make
productive here. The per-feature detail lives in `docs/architecture.md` and the specs
under `.trellis/spec/launchpad/`.

- **Three creation methods, one pipeline.** 方式A (Claude Agent SDK → ARM64 container),
方式B (managed Harness, no build), 方式C (Strands Studio canvas), plus `zip_runtime`,
- **Four creation methods, one pipeline.** 方式A (Claude Agent SDK → ARM64 container),
方式B (managed Harness, no build), 方式C (Strands Studio canvas), `byoc` (member-
uploaded code zip / Dockerfile context / existing ECR image), plus `zip_runtime`,
all converge into the ordered stages `generate → package → provision → deploy →
register` in `backend/app/deployer/pipeline.py`. Each method registers one callable
per stage (or omits it) via `register_method()`; the method modules
(`deployer/harness.py`, `zip_runtime.py`, `container.py`) are imported **for their
side effects** in `app/main.py`, so a new method must be imported there to exist.
(`deployer/harness.py`, `zip_runtime.py`, `container.py`, `byoc.py`) are imported
**for their side effects** in `app/main.py`, so a new method must be imported there
to exist.

- **Deploy is an async, resumable job.** `POST /api/agents` returns `202` with a
`job_id`; the job runs on a background thread, persisting per-stage status onto the
Expand Down
10 changes: 6 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,15 @@ These are the abstractions that span many files; understanding them is what make
productive here. The per-feature detail lives in `docs/architecture.md` and the specs
under `.trellis/spec/launchpad/`.

- **Three creation methods, one pipeline.** 方式A (Claude Agent SDK → ARM64 container),
方式B (managed Harness, no build), 方式C (Strands Studio canvas), plus `zip_runtime`,
- **Four creation methods, one pipeline.** 方式A (Claude Agent SDK → ARM64 container),
方式B (managed Harness, no build), 方式C (Strands Studio canvas), `byoc` (member-
uploaded code zip / Dockerfile context / existing ECR image), plus `zip_runtime`,
all converge into the ordered stages `generate → package → provision → deploy →
register` in `backend/app/deployer/pipeline.py`. Each method registers one callable
per stage (or omits it) via `register_method()`; the method modules
(`deployer/harness.py`, `zip_runtime.py`, `container.py`) are imported **for their
side effects** in `app/main.py`, so a new method must be imported there to exist.
(`deployer/harness.py`, `zip_runtime.py`, `container.py`, `byoc.py`) are imported
**for their side effects** in `app/main.py`, so a new method must be imported there
to exist.

- **Deploy is an async, resumable job.** `POST /api/agents` returns `202` with a
`job_id`; the job runs on a background thread, persisting per-stage status onto the
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ deploy it to AgentCore Runtime, and consume it** over chat or HTTP.
Launchpad is one console (React) over one FastAPI backend, plus shared AWS
infrastructure (CDK) and a vendored Strands Studio sub-app. It delivers:

- **Three creation methods, one deploy pipeline.** Users create agents via
- **Four creation methods, one deploy pipeline.** Users create agents via
**方式B — Managed Harness** (declarative `CreateHarness` — model, prompt,
tools, skills, memory; no code, no build), **方式C — Strands Studio**
(visual drag-and-drop canvas that generates Strands code), or
(visual drag-and-drop canvas that generates Strands code),
**方式A — Other Agent SDK** (bring your own agent SDK — the Claude Agent SDK
today — packaged into an ARM64 container image). All three
converge into the same five-stage pipeline and land on AgentCore Runtime
(方式A/C) or the managed Harness service (方式B).
today — packaged into an ARM64 container image), or **BYOC — Bring Your Own
Code** (upload a zip of your own agent code, or a Dockerfile build context,
or reference an existing private-ECR image; developers need no AWS access).
All of them converge into the same five-stage pipeline and land on AgentCore
Runtime (方式A/C/BYOC) or the managed Harness service (方式B).
- **Registry console.** A visual front end over AgentCore Registry for
cataloguing and discovering the three asset types — agents (A2A), MCP tools,
and skills — with submit → approve lifecycle actions.
Expand Down Expand Up @@ -243,7 +245,7 @@ For terminal-attached development, use `make dev` and stop it with `Ctrl+C`.
|---|---|
| `backend/` | FastAPI backend — deploy pipeline, invoke chain, evaluation & optimization, SQLite ledger |
| `backend/app/routers/` | Console `/api` + public `/v1` endpoints |
| `backend/app/deployer/` | Unified pipeline + per-method stages (harness, zip_runtime, container, studio) |
| `backend/app/deployer/` | Unified pipeline + per-method stages (harness, zip_runtime, container, studio, byoc) |
| `frontend/` | React console (Vite) — Overview, Create Agent, Registry, Chat, Observability, Evaluation, Skill Lab, Governance |
| `infra/` | AWS CDK app — the `launchpad-base` shared stack |
| `apps/studio/` | Vendored Strands Studio sub-app (方式C), rewired to the platform pipeline |
Expand Down
14 changes: 8 additions & 6 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ AgentCore Launchpad 是一套基于 Amazon Bedrock AgentCore 的**企业 Agent O
Launchpad 由 React 控制台、FastAPI 后端和一套 CDK 共享基础设施组成,另带一个
vendored Strands Studio 子应用。主要能力包括:

- **三种创建方式,共用一条部署流水线。** 用户可以选择**方式B(Managed Harness)**,
- **四种创建方式,共用一条部署流水线。** 用户可以选择**方式B(Managed Harness)**,
通过模型、提示词、工具、技能和记忆创建 Harness,无需编写代码或构建产物;
选择**方式C(Strands Studio)**,在可视化画布中生成 Strands 代码;
也可以选择**方式A(其他 Agent SDK)**,自带 Agent SDK(目前为 Claude Agent
SDK)并打包成 ARM64 容器镜像。
三种方式都进入同一条五阶段流水线,最终部署到 AgentCore Runtime(方式A/C)
或托管 Harness 服务(方式B)。
选择**方式A(其他 Agent SDK)**,自带 Agent SDK(目前为 Claude Agent
SDK)并打包成 ARM64 容器镜像;也可以选择**自带代码(BYOC)**,上传自己编写的
Agent 代码 zip 或 Dockerfile 构建上下文,或引用本账户私有 ECR 中的现有镜像,
开发者无需任何 AWS 权限。
所有方式都进入同一条五阶段流水线,最终部署到 AgentCore Runtime
(方式A/C/BYOC)或托管 Harness 服务(方式B)。
- **注册中心。** 通过 AgentCore Registry 登记和查找三类资产:Agent(A2A)、
MCP 工具和 Skill,并支持提交、审批等生命周期操作。
- **知识库。** 托管 Bedrock 知识库——全托管 RAG,向量库、嵌入与重排都由服务负责。
Expand Down Expand Up @@ -220,7 +222,7 @@ export LAUNCHPAD_AUTH_ALLOWED_EMAIL_DOMAINS='["your-company.com"]' # 白名单
|---|---|
| `backend/` | FastAPI 后端:部署流水线、调用链、评估与优化、SQLite 台账 |
| `backend/app/routers/` | 控制台 `/api` + 公开 `/v1` 接口 |
| `backend/app/deployer/` | 统一流水线 + 各方式的阶段实现(harness、zip_runtime、container、studio) |
| `backend/app/deployer/` | 统一流水线 + 各方式的阶段实现(harness、zip_runtime、container、studio、byoc) |
| `frontend/` | React 控制台(Vite):Overview、Create Agent、Registry、Chat、Governance、Evaluation |
| `infra/` | AWS CDK 应用:`launchpad-base` 共享栈 |
| `apps/studio/` | vendored Strands Studio 子应用(方式C),已接入平台流水线 |
Expand Down
10 changes: 10 additions & 0 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ class Settings(BaseSettings):
image_scan_block_severities: list[str] = ["CRITICAL"]
image_scan_timeout_s: int = Field(default=300, gt=0)

# The manylinux level the zip deploy paths resolve/install Python wheels for
# (consumed via app/core/runtime_target.py — see its module docstring for the
# measured evidence: AgentCore Runtime = AL2023, glibc 2.34, aarch64,
# 2026-09-18). manylinux_2_28 matches what current wheel builders publish;
# "manylinux2014" is the documented safe fallback (the official docs'
# conservative recommendation) if a runtime image ever reports older glibc.
runtime_python_platform: str = Field(
default="manylinux_2_28", pattern=r"^manylinux(2014|_2_\d+)$"
)

# AgentCore synchronous runtime requests may run for up to 15 minutes.
# Keep the SDK read timeout above that service limit so buffered agents can
# return their final response.
Expand Down
3 changes: 3 additions & 0 deletions backend/app/core/route_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
("POST", "/api/agents"): PERM_AGENT_DEPLOY,
("GET", "/api/agents/discovery"): MEMBER,
("POST", "/api/agents/discovery/import"): PERM_AGENT_IMPORT,
# BYOC artifact staging is one half of a deploy, so it carries deploy perms
("POST", "/api/agents/uploads"): PERM_AGENT_DEPLOY,
("GET", "/api/agents/uploads/{upload_id}"): MEMBER,
("GET", "/api/agents/{agent_id}"): MEMBER,
("GET", "/api/agents/{agent_id}/versions"): MEMBER, # read-only AWS view
("GET", "/api/agents/{agent_id}/conversions"): MEMBER, # ledger read: runtime twins
Expand Down
81 changes: 81 additions & 0 deletions backend/app/core/runtime_target.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""The Python platform the zip deploy paths resolve and install for.

One definition, three consumers that MUST agree: the `uv pip compile` resolve,
the `pip install` into the bundle (both in `app/deployer/`), and the
range→pin resolver (`app/schemas/requirements.py`). Resolving against one
platform and installing for another produces a lock that does not describe the
artifact.

The default is `manylinux_2_28` on aarch64. Evidence (measured 2026-09-18 from
inside a deployed AgentCore Runtime direct-code agent, PYTHON_3_13): the runtime
OS is Amazon Linux 2023.12.20260817 on aarch64 with glibc 2.34, so every
manylinux tag up to `manylinux_2_34` is loadable. The official docs recommend
`manylinux2014` (glibc 2.17), which is safe but strictly narrower — packages
that only publish `manylinux_2_26`/`2_28` aarch64 wheels (e.g. `google-re2`,
pulled in by `chromadb`) are unsolvable under it even though they run fine on
the runtime. `2_28` is what current manylinux images actually publish for;
`2_34` would buy nothing today and breaks the day the fleet moves to an older
glibc image, so the headroom stays unspent.

Configurable as `runtime_python_platform` (`LAUNCHPAD_RUNTIME_PYTHON_PLATFORM`),
value `manylinux_2_<minor>` or the legacy alias `manylinux2014`. `manylinux2014`
is the documented safe fallback if a future runtime image ever reports an older
glibc.
"""

import re

from app.core.config import get_settings

# AgentCore Runtime zips run on Python 3.13 regardless of the resolve platform.
TARGET_PYTHON = "3.13"

_PLATFORM_RE = re.compile(r"^manylinux(?:2014|_2_(?P<minor>\d+))$")

# The legacy aliases map onto PEP 600 glibc minors; pip expands neither
# direction on its own (measured: `--platform manylinux_2_28_aarch64` alone
# refuses a manylinux_2_17-only wheel), so the ladder below is built explicitly.
_MANYLINUX2014_MINOR = 17


def _glibc_minor(setting: str) -> int:
match = _PLATFORM_RE.match(setting)
if match is None:
raise ValueError(
f"runtime_python_platform {setting!r} is not a manylinux platform — "
"use manylinux_2_<minor> (e.g. manylinux_2_28) or manylinux2014"
)
return int(match.group("minor") or _MANYLINUX2014_MINOR)


def uv_platform() -> str:
"""The single `--python-platform` value for `uv pip compile`.

uv derives the whole compatible-tag set from one platform (a
`aarch64-manylinux_2_28` resolve accepts `manylinux_2_17` wheels), so no
ladder is needed on this side.
"""
return f"aarch64-{get_settings().runtime_python_platform}"


def pip_platforms() -> list[str]:
"""Every `--platform` value the `pip install` step must pass.

pip treats `--platform` tags as exact strings — it does NOT expand
`manylinux_2_28_aarch64` down to older glibc tags the way it expands the
*running* interpreter's platform. One flag would therefore reject the
`manylinux_2_17`-only wheels most projects publish, undoing the resolve.
"""
minor = _glibc_minor(get_settings().runtime_python_platform)
ladder = [f"manylinux_2_{m}_aarch64" for m in range(minor, _MANYLINUX2014_MINOR - 1, -1)]
# the pre-PEP600 alias many wheel filenames still use (manylinux2014 == 2_17)
ladder.append("manylinux2014_aarch64")
return ladder


def pip_platform_args() -> list[str]:
"""`--platform` argv fragments for a pip command line."""
args: list[str] = []
for platform in pip_platforms():
args += ["--platform", platform]
return args
Loading