diff --git a/.github/workflows/real-agentenv.yml b/.github/workflows/real-agentenv.yml index a252eb691c..6121fea671 100644 --- a/.github/workflows/real-agentenv.yml +++ b/.github/workflows/real-agentenv.yml @@ -54,10 +54,11 @@ jobs: - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.12" - # Public ECR caps anonymous pulls by data per source IP and hosted runners share IPs, so the - # framework's image builds can fail here for hours. mirror.gcr.io serves the same Docker - # official images under the same digests, so each base image the pinned framework builds from - # is pulled there and named as its Dockerfile names it, as the framework's own CI does. + # The pinned framework builds its gateway and service-db images only when pulling them from + # ghcr.io fails, from public ECR base images; public ECR caps anonymous pulls by data per source + # IP and hosted runners share IPs, so such a build can fail here for hours. mirror.gcr.io serves + # the same Docker official images under the same digests, so each base image those Dockerfiles + # name is pulled there and named as its Dockerfile names it, as the framework's own CI does. # Digest-pinned names are set in containerd, whose images Docker reads only on the containerd # image store; switching stores restarts dockerd, so it happens before anything uses Docker. - name: Use the containerd image store @@ -72,16 +73,16 @@ jobs: run: | runner_root="${RUNNER_TEMP}/rea-agentenv-runner" python -m venv "${runner_root}" - "${runner_root}/bin/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1334' + "${runner_root}/bin/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1431' "${runner_root}/bin/agent-env" --version docker version --format '{{.Server.Version}}' echo "REA_AGENT_ENV_COMMAND=${runner_root}/bin/agent-env" >> "${GITHUB_ENV}" echo "REA_DOCKER_COMMAND=$(command -v docker)" >> "${GITHUB_ENV}" - - name: Pull the pinned framework's public ECR base images from mirror.gcr.io + - name: Pull the gateway and service-db public ECR base images from mirror.gcr.io shell: bash run: | package="$("${REA_AGENT_ENV_COMMAND%/agent-env}/python" -c 'import agent_env, pathlib; print(pathlib.Path(agent_env.__file__).parent)')" - grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}" | awk '{print $2}' | sort -u | + grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}/env/gateway" "${package}/env/envs/service_db" | awk '{print $2}' | sort -u | while read -r image; do name=${image#public.ecr.aws/docker/library/} source=mirror.gcr.io/library/$name diff --git a/docs/agent-environments-quickstart.md b/docs/agent-environments-quickstart.md index 251afd1447..4e64305714 100644 --- a/docs/agent-environments-quickstart.md +++ b/docs/agent-environments-quickstart.md @@ -19,7 +19,7 @@ records the app you provide; you start it and prepare its test data. To run the exported environment, install the [AgentEnv framework](https://github.com/scaleapi/agentenv-framework#install) and start Docker. REA's [compatibility check](testing.md#exported-environment-bundles) -uses AgentEnv version `0.9.1334` with Python 3.12. +uses AgentEnv version `0.9.1431` with Python 3.12. There are two ways to build an environment: ask your agent to record the app, or start from a recording you already have. diff --git a/docs/agent-environments.md b/docs/agent-environments.md index 71fd35bd65..be11df05db 100644 --- a/docs/agent-environments.md +++ b/docs/agent-environments.md @@ -1049,8 +1049,9 @@ unknown recorded status stays unknown; it is not borrowed from another record). A bundle with no such tool yields a verifier that checks the count only and says so. `tasks/smoke.json` deploys the env on the local sandbox and runs that verifier against the env's MCP endpoint; the first run -also builds the framework's own infrastructure, and the run tears everything -down when it ends. +also pulls the framework's published infrastructure images from `ghcr.io`, +building one only if its pull fails, and the run tears everything down when it +ends. The README written into the bundle records the capture artifact's SHA-256, the capture and manifest Evidence ids, the manifest view digest, the served tool diff --git a/docs/specs/agent-environments/environment-export/spec.md b/docs/specs/agent-environments/environment-export/spec.md index e36a4c57f5..2b1c2f98c7 100644 --- a/docs/specs/agent-environments/environment-export/spec.md +++ b/docs/specs/agent-environments/environment-export/spec.md @@ -69,7 +69,7 @@ previous increment. - Add the `agentenv` CI scope, the reusable `real-agentenv.yml` workflow (`workflow_dispatch` and `workflow_call` with `build_cache_artifact`, `ubuntu-24.04`, SHA-pinned actions, `setup-python` 3.12, a virtual - environment with `agentenv-framework==0.9.1334`, the runner's Docker, 30 + environment with `agentenv-framework==0.9.1431`, the runner's Docker, 30 minutes), the `verify:agentenv:environment` script and `scripts/verify-agentenv-environment.mjs`, which requires absolute `REA_AGENT_ENV_COMMAND` and `REA_DOCKER_COMMAND` like every other lane diff --git a/docs/testing-lanes.md b/docs/testing-lanes.md index d96cd9f629..ff3cd9d5ea 100644 --- a/docs/testing-lanes.md +++ b/docs/testing-lanes.md @@ -1172,9 +1172,10 @@ installed package entrypoint as the script's first argument for package coverage `npm run verify:agentenv:environment` is the `real-agentenv` lane. It requires the host's Docker CLI named by absolute `REA_DOCKER_COMMAND`, Python 3.12 with -`agentenv-framework==0.9.1334` installed in a virtual environment whose +`agentenv-framework==0.9.1431` installed in a virtual environment whose `agent-env` binary is named by absolute `REA_AGENT_ENV_COMMAND`, and outbound -network for `pip` on the runner and for `npm` inside the image build. Both +network for `pip` on the runner, for the framework's image pulls from `ghcr.io` +and for `npm` inside the image build. Both variables are required; neither upstream executable is resolved from `PATH`, and the verifier installs nothing. Packing uses the shared npm helper, honoring `npm_execpath` from the caller's selected npm. Direct Node invocations without @@ -1186,11 +1187,12 @@ storage, exports both paths and uses the runner's own Docker; no developer host configuration changes. `agent-env run` attempts share one 22-minute budget inside the 30-minute job (the first attempt at most 15 minutes), so a hung or retried run ends with the verifier's failed receipt rather than the runner's -kill. The framework builds its gateway and service-db -images from `public.ecr.aws` on every fresh runner, and hosted runners share -that registry's anonymous pull quota, so the lane first pulls the base images -the pinned framework's Dockerfiles name from `mirror.gcr.io`, which serves the -same Docker official images under the same digests, and names them as those +kill. The pinned framework pulls its gateway and service-db images from +`ghcr.io/scaleapi` by the digests its release records and builds one only when +its pull fails. Those builds start from `public.ecr.aws` base images, and hosted +runners share that registry's anonymous pull quota, so the lane first pulls the +base images the gateway and service-db Dockerfiles name from `mirror.gcr.io`, +which serves the same Docker official images under the same digests, and names them as those Dockerfiles do (on Docker's containerd image store, so digest-pinned names resolve), as the framework's own CI does. The quota handling below covers anything still pulled from that registry. The receipt's `agent_env_run` records the @@ -1218,9 +1220,11 @@ that served tool count, and that no container exists after the run that did not exist before; the container inventory runs even when the run fails or times out, so a failed receipt still lists what it left. The one exception is the framework's own image-store helper, `agentenv-registry` (or `agentenv-registry-`), -a `registry:2` container the local provider starts on first use and keeps -running across runs; a first run on a fresh host creates it, so the receipt -lists it under `image_store_helpers` instead of counting it as a leak. Images +a `registry:2` container the local provider starts when it first stores an image +it built and keeps running across runs. The pinned framework registers its +published infra images, and a deploy that cannot pull one builds it into the +host's Docker, so a fresh run does not normally create the helper; if one does, +the receipt lists it under `image_store_helpers` instead of counting it as a leak. Images built by the run remain and are counted in the receipt. Pass an installed package entrypoint as the script's first argument for package coverage. diff --git a/scripts/verify-agentenv-environment.mjs b/scripts/verify-agentenv-environment.mjs index 49e786ee7e..7d96a2ff29 100644 --- a/scripts/verify-agentenv-environment.mjs +++ b/scripts/verify-agentenv-environment.mjs @@ -8,10 +8,10 @@ import { PrivateRuntimeRoot } from "../dist/process/PrivateRuntimeRoot.js"; import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs"; import { createPackageVerifierCommands } from "./lib/verify-package-commands.mjs"; -const AGENTENV_VERSION = "0.9.1334"; +const AGENTENV_VERSION = "0.9.1431"; const AGENTENV_PIN = `agentenv-framework ${AGENTENV_VERSION}`; -// The framework builds its infra images from public ECR, whose anonymous pull quota hosted -// runners share; a rate refusal earns one retry, anything else fails at once. +// Only an infra image whose pull fails is built, from public ECR, whose anonymous pull quota +// hosted runners share; a rate refusal earns one retry, anything else fails at once. const PUBLIC_ECR_QUOTA = /public\.ecr\.aws[\s\S]{0,400}?(?:429 Too Many Requests|toomanyrequests)/u; // The anonymous data-transfer cap is counted per source IP, so a pause on the same runner @@ -31,8 +31,8 @@ for (const key of ["REA_AGENT_ENV_COMMAND", "REA_DOCKER_COMMAND"]) ); const agentEnv = process.env.REA_AGENT_ENV_COMMAND; const docker = process.env.REA_DOCKER_COMMAND; -// The framework's local image store keeps its registry:2 helper running across runs; a first -// run on a fresh host creates it, so it is reported separately rather than counted as a leak. +// The framework's local image store keeps its registry:2 helper running across runs; a run that +// first stores a build there creates it, so it is reported separately rather than counted as a leak. const IMAGE_STORE_HELPER = /^agentenv-registry(?:-\d+)?$/u; await Promise.all([access(agentEnv), access(docker)]); const entrypoint = diff --git a/tests/boundary/process/agentenvVerifierIdentity.test.ts b/tests/boundary/process/agentenvVerifierIdentity.test.ts index c6cf96541c..ecbd82353b 100644 --- a/tests/boundary/process/agentenvVerifierIdentity.test.ts +++ b/tests/boundary/process/agentenvVerifierIdentity.test.ts @@ -29,7 +29,7 @@ it.skipIf(process.platform === "win32")( ); await writeFile( runner, - "#!/usr/bin/env node\nprocess.stdout.write('agent-env 0.9.1334\\n');\n", + "#!/usr/bin/env node\nprocess.stdout.write('agent-env 0.9.1431\\n');\n", { mode: 0o755 }, ); await writeFile(docker, "#!/usr/bin/env node\nprocess.exit(0);\n", { @@ -70,8 +70,8 @@ it.skipIf(process.platform === "win32")( // The local framework and these executable shebang fixtures target POSIX hosts. it.skipIf(process.platform === "win32").each([ ["agent-env 0.9.9999", false], - ["other-runner 0.9.1334", false], - ["agent-env 0.9.1334", true], + ["other-runner 0.9.1431", false], + ["agent-env 0.9.1431", true], ] as const)( "admits the observed executable identity %s before Docker: %s", async (version, accepted) => {