From 143edd6b0b87b8c1a92565597c1e5609bcfe02db Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 19 Jun 2026 18:27:53 +0200 Subject: [PATCH 1/4] chore(hosting): wire the agent runner sidecar into the dev compose stack --- .../docker-compose/ee/docker-compose.dev.yml | 83 ++++++++++++++----- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/hosting/docker-compose/ee/docker-compose.dev.yml b/hosting/docker-compose/ee/docker-compose.dev.yml index c08109d846..286d0d6aa7 100644 --- a/hosting/docker-compose/ee/docker-compose.dev.yml +++ b/hosting/docker-compose/ee/docker-compose.dev.yml @@ -46,9 +46,6 @@ services: - ../../../web/oss/src:/app/oss/src - ../../../web/oss/public:/app/oss/public - ../../../web/packages:/app/packages - - nextjs-ee-cache:/app/ee/.next/cache - - nextjs-oss-cache:/app/oss/.next/cache - - turbo-ee-cache:/app/.turbo # === CONFIGURATION ======================================== # env_file: - ${ENV_FILE:-./.env.ee.dev} @@ -82,12 +79,8 @@ services: "--reload-dir", "/app/ee/src", "--reload-dir", - "/app/ee/databases", - "--reload-dir", "/app/oss/src", "--reload-dir", - "/app/oss/databases", - "--reload-dir", "/app/entrypoints", "--reload-dir", "/sdks/python/agenta", @@ -150,9 +143,7 @@ services: image: agenta-ee-dev-api:latest # === EXECUTION ============================================ # command: > - watchmedo auto-restart --directory=/app/ee/src --directory=/app/ee/databases --directory=/app/oss/src - --directory=/app/oss/databases --directory=/app/entrypoints --directory=/sdks/python/agenta - --directory=/clients/python/agenta_client --pattern=*.py --recursive --ignore-patterns=*/tests/* -- + watchmedo auto-restart --directory=/app/ --pattern=*.py --recursive --ignore-patterns=*/tests/* -- python -m entrypoints.worker_evaluations # === STORAGE ============================================== # volumes: @@ -189,9 +180,7 @@ services: image: agenta-ee-dev-api:latest # === EXECUTION ============================================ # command: > - watchmedo auto-restart --directory=/app/ee/src --directory=/app/ee/databases --directory=/app/oss/src - --directory=/app/oss/databases --directory=/app/entrypoints --directory=/sdks/python/agenta - --directory=/clients/python/agenta_client --pattern=*.py --recursive --ignore-patterns=*/tests/* -- + watchmedo auto-restart --directory=/app/ --pattern=*.py --recursive --ignore-patterns=*/tests/* -- python -m entrypoints.worker_tracing # === STORAGE ============================================== # volumes: @@ -228,9 +217,7 @@ services: image: agenta-ee-dev-api:latest # === EXECUTION ============================================ # command: > - watchmedo auto-restart --directory=/app/ee/src --directory=/app/ee/databases --directory=/app/oss/src - --directory=/app/oss/databases --directory=/app/entrypoints --directory=/sdks/python/agenta - --directory=/clients/python/agenta_client --pattern=*.py --recursive --ignore-patterns=*/tests/* -- + watchmedo auto-restart --directory=/app/ --pattern=*.py --recursive --ignore-patterns=*/tests/* -- python -m entrypoints.worker_webhooks # === STORAGE ============================================== # volumes: @@ -273,9 +260,7 @@ services: image: agenta-ee-dev-api:latest # === EXECUTION ============================================ # command: > - watchmedo auto-restart --directory=/app/ee/src --directory=/app/ee/databases --directory=/app/oss/src - --directory=/app/oss/databases --directory=/app/entrypoints --directory=/sdks/python/agenta - --directory=/clients/python/agenta_client --pattern=*.py --recursive --ignore-patterns=*/tests/* -- + watchmedo auto-restart --directory=/app/ --pattern=*.py --recursive --ignore-patterns=*/tests/* -- python -m entrypoints.worker_events # === STORAGE ============================================== # volumes: @@ -409,6 +394,13 @@ services: - ${ENV_FILE:-./.env.ee.dev} environment: DOCKER_NETWORK_MODE: ${DOCKER_NETWORK_MODE:-bridge} + # Agent workflow: reach the agent runner sidecar in-network. + AGENTA_AGENT_PI_URL: http://agent-pi:8765 + # Agent runtime (WP-8): drive the harness over ACP via a rivet daemon. + # Harness (pi/claude) and sandbox (local/daytona) are independent axes. + AGENTA_AGENT_RUNTIME: ${AGENTA_AGENT_RUNTIME:-rivet} + AGENTA_AGENT_HARNESS: ${AGENTA_AGENT_HARNESS:-pi} + AGENTA_AGENT_SANDBOX: ${AGENTA_AGENT_SANDBOX:-local} # === NETWORK ============================================== # networks: - agenta-network @@ -426,6 +418,55 @@ services: # === LIFECYCLE ============================================ # restart: always + agent-pi: + # === IMAGE ================================================ # + # Agent runner sidecar. The services container + # calls it in-network at http://agent-pi:8765 (AGENTA_AGENT_PI_URL). + build: + context: ../../../services/agent + dockerfile: docker/Dockerfile.dev + # === EXECUTION ============================================ # + # No file watcher (the box's inotify limit is shared across stacks). Copy the + # read-only mounted Pi login into a writable path so OAuth refresh stays + # in-container. + command: > + sh -c "mkdir -p /pi-agent && cp -a /pi-agent-ro/. /pi-agent/ 2>/dev/null || true; + exec node_modules/.bin/tsx src/server.ts" + # === CONFIGURATION ======================================== # + # Deliberately NO env_file: the Pi sandbox must not inherit the stack's + # secrets (COMPOSIO_API_KEY, STRIPE/POSTHOG/GOOGLE keys, ...). Tools run + # server-side via /tools/call, so the sandbox only needs its own port, the Pi + # login (mounted below), the OTLP export fallback, and — for the rivet `daytona` + # sandbox axis (WP-8) — the Daytona credentials the SDK reads to create sandboxes. + environment: + PORT: "8765" + PI_CODING_AGENT_DIR: /pi-agent + # Tracing export fallback (used when a request carries no usable OTLP + # credential). Must be reachable from this container. + AGENTA_HOST: ${AGENTA_HOST:-http://144.76.237.122:8280} + AGENTA_API_KEY: ${AGENTA_API_KEY:-} + # Daytona sandbox axis: the rivet daytona provider's `new Daytona()` reads + # these. Scoped to Daytona only (not the full stack secret set). + DAYTONA_API_KEY: ${DAYTONA_API_KEY:-} + DAYTONA_API_URL: ${DAYTONA_API_URL:-} + DAYTONA_TARGET: ${DAYTONA_TARGET:-} + # Pre-baked snapshot (rivet daemon + Pi + certs; Claude inherited from rivet's + # -full base) so Daytona runs skip the ~150s per-invoke `npm install pi`. We + # ship the builder (poc/build_rivet_snapshot.py), not the snapshot itself: each + # operator builds their own, so we never distribute a Claude-containing image. + # See services/agent/docker/README.md for the licensing posture. + AGENTA_RIVET_DAYTONA_SNAPSHOT: ${AGENTA_RIVET_DAYTONA_SNAPSHOT:-agenta-rivet-pi} + AGENTA_RIVET_DAYTONA_INSTALL_PI: ${AGENTA_RIVET_DAYTONA_INSTALL_PI:-false} + # === STORAGE ============================================== # + volumes: + - ../../../services/agent/src:/app/src + - ${HOME}/.pi/agent:/pi-agent-ro:ro + # === NETWORK ============================================== # + networks: + - agenta-network + # === LIFECYCLE ============================================ # + restart: always + postgres: # === IMAGE ================================================ # image: postgres:17 @@ -593,6 +634,4 @@ volumes: postgres-data: redis-volatile-data: redis-durable-data: - nextjs-ee-cache: - nextjs-oss-cache: - turbo-ee-cache: + nextjs_cache: From 3017977d6d13ac2c754cbddbd5b5fca6250bfc6a Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 19 Jun 2026 22:16:38 +0200 Subject: [PATCH 2/4] chore(hosting): clarify agent sidecar dependency --- hosting/docker-compose/ee/docker-compose.dev.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hosting/docker-compose/ee/docker-compose.dev.yml b/hosting/docker-compose/ee/docker-compose.dev.yml index 286d0d6aa7..85f98ead68 100644 --- a/hosting/docker-compose/ee/docker-compose.dev.yml +++ b/hosting/docker-compose/ee/docker-compose.dev.yml @@ -396,8 +396,10 @@ services: DOCKER_NETWORK_MODE: ${DOCKER_NETWORK_MODE:-bridge} # Agent workflow: reach the agent runner sidecar in-network. AGENTA_AGENT_PI_URL: http://agent-pi:8765 - # Agent runtime (WP-8): drive the harness over ACP via a rivet daemon. + # Agent runtime: drive the harness over ACP via a rivet daemon. # Harness (pi/claude) and sandbox (local/daytona) are independent axes. + # The agent-pi sidecar files are introduced by PR #4778, so this compose + # wiring must land after #4778 or be reviewed with that branch applied. AGENTA_AGENT_RUNTIME: ${AGENTA_AGENT_RUNTIME:-rivet} AGENTA_AGENT_HARNESS: ${AGENTA_AGENT_HARNESS:-pi} AGENTA_AGENT_SANDBOX: ${AGENTA_AGENT_SANDBOX:-local} @@ -422,6 +424,7 @@ services: # === IMAGE ================================================ # # Agent runner sidecar. The services container # calls it in-network at http://agent-pi:8765 (AGENTA_AGENT_PI_URL). + # The build context and Dockerfile come from PR #4778. build: context: ../../../services/agent dockerfile: docker/Dockerfile.dev @@ -436,8 +439,8 @@ services: # Deliberately NO env_file: the Pi sandbox must not inherit the stack's # secrets (COMPOSIO_API_KEY, STRIPE/POSTHOG/GOOGLE keys, ...). Tools run # server-side via /tools/call, so the sandbox only needs its own port, the Pi - # login (mounted below), the OTLP export fallback, and — for the rivet `daytona` - # sandbox axis (WP-8) — the Daytona credentials the SDK reads to create sandboxes. + # login (mounted below), the OTLP export fallback, and the Daytona credentials + # the SDK reads for the rivet `daytona` sandbox axis. environment: PORT: "8765" PI_CODING_AGENT_DIR: /pi-agent From 76ad769be72c501012fe75589d0b0de555e18bcd Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Sat, 20 Jun 2026 14:17:12 +0200 Subject: [PATCH 3/4] fix(hosting): rebuild the Pi extension on agent-pi start The dev agent-pi compose command replaces the image CMD, so the extension bundle was never rebuilt and went stale, silently dropping custom tools on the Rivet path (QA finding F-005). Rebuild it from the mounted src on start. Claude-Session: https://claude.ai/code/session_01KsGSJQwsUdgWcNSEt2P2qD --- hosting/docker-compose/ee/docker-compose.dev.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hosting/docker-compose/ee/docker-compose.dev.yml b/hosting/docker-compose/ee/docker-compose.dev.yml index 85f98ead68..6d6c295d13 100644 --- a/hosting/docker-compose/ee/docker-compose.dev.yml +++ b/hosting/docker-compose/ee/docker-compose.dev.yml @@ -403,6 +403,8 @@ services: AGENTA_AGENT_RUNTIME: ${AGENTA_AGENT_RUNTIME:-rivet} AGENTA_AGENT_HARNESS: ${AGENTA_AGENT_HARNESS:-pi} AGENTA_AGENT_SANDBOX: ${AGENTA_AGENT_SANDBOX:-local} + # Gate user-declared MCP servers (resolve_mcp_servers). Off by default. + AGENTA_AGENT_ENABLE_MCP: ${AGENTA_AGENT_ENABLE_MCP:-false} # === NETWORK ============================================== # networks: - agenta-network @@ -431,9 +433,14 @@ services: # === EXECUTION ============================================ # # No file watcher (the box's inotify limit is shared across stacks). Copy the # read-only mounted Pi login into a writable path so OAuth refresh stays - # in-container. + # in-container. This command replaces the image CMD, so the Pi extension rebuild + # has to live here too: dist/ is not bind-mounted and src/extensions/agenta.ts is, + # so without this a restart keeps a stale bundle and custom tools silently stop + # being delivered on the Rivet path (QA finding F-005). Rebuild from the mounted + # src on start; fail loud if it cannot build rather than run a stale bundle. command: > sh -c "mkdir -p /pi-agent && cp -a /pi-agent-ro/. /pi-agent/ 2>/dev/null || true; + node scripts/build-extension.mjs && exec node_modules/.bin/tsx src/server.ts" # === CONFIGURATION ======================================== # # Deliberately NO env_file: the Pi sandbox must not inherit the stack's From 14ab328e6d246be2042920d94ad3a55f18326e07 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Mon, 22 Jun 2026 12:33:58 +0200 Subject: [PATCH 4/4] refactor(hosting): rename rivet service to sandbox-agent in dev compose --- .../docker-compose/ee/docker-compose.dev.yml | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/hosting/docker-compose/ee/docker-compose.dev.yml b/hosting/docker-compose/ee/docker-compose.dev.yml index 6d6c295d13..39703078a4 100644 --- a/hosting/docker-compose/ee/docker-compose.dev.yml +++ b/hosting/docker-compose/ee/docker-compose.dev.yml @@ -31,6 +31,15 @@ services: # === EXECUTION ============================================ # command: ["true"] + .sandbox-agent: + # === IMAGE ================================================ # + image: agenta-ee-dev-sandbox-agent:latest + build: + context: ../../../services/agent + dockerfile: docker/Dockerfile.dev + # === EXECUTION ============================================ # + command: ["true"] + web: # === ACTIVATION =========================================== # profiles: @@ -394,22 +403,17 @@ services: - ${ENV_FILE:-./.env.ee.dev} environment: DOCKER_NETWORK_MODE: ${DOCKER_NETWORK_MODE:-bridge} - # Agent workflow: reach the agent runner sidecar in-network. - AGENTA_AGENT_PI_URL: http://agent-pi:8765 - # Agent runtime: drive the harness over ACP via a rivet daemon. - # Harness (pi/claude) and sandbox (local/daytona) are independent axes. - # The agent-pi sidecar files are introduced by PR #4778, so this compose - # wiring must land after #4778 or be reviewed with that branch applied. - AGENTA_AGENT_RUNTIME: ${AGENTA_AGENT_RUNTIME:-rivet} - AGENTA_AGENT_HARNESS: ${AGENTA_AGENT_HARNESS:-pi} - AGENTA_AGENT_SANDBOX: ${AGENTA_AGENT_SANDBOX:-local} - # Gate user-declared MCP servers (resolve_mcp_servers). Off by default. + AGENTA_AGENT_RUNNER_URL: http://sandbox-agent:8765 AGENTA_AGENT_ENABLE_MCP: ${AGENTA_AGENT_ENABLE_MCP:-false} # === NETWORK ============================================== # networks: - agenta-network extra_hosts: - "host.docker.internal:host-gateway" + # === ORCHESTRATION ======================================== # + depends_on: + sandbox-agent: + condition: service_healthy # === LABELS =============================================== # labels: - "traefik.http.routers.services.rule=PathPrefix(`/services/`)" @@ -422,32 +426,27 @@ services: # === LIFECYCLE ============================================ # restart: always - agent-pi: + sandbox-agent: # === IMAGE ================================================ # - # Agent runner sidecar. The services container - # calls it in-network at http://agent-pi:8765 (AGENTA_AGENT_PI_URL). - # The build context and Dockerfile come from PR #4778. - build: - context: ../../../services/agent - dockerfile: docker/Dockerfile.dev + image: agenta-ee-dev-sandbox-agent:latest # === EXECUTION ============================================ # # No file watcher (the box's inotify limit is shared across stacks). Copy the # read-only mounted Pi login into a writable path so OAuth refresh stays # in-container. This command replaces the image CMD, so the Pi extension rebuild # has to live here too: dist/ is not bind-mounted and src/extensions/agenta.ts is, # so without this a restart keeps a stale bundle and custom tools silently stop - # being delivered on the Rivet path (QA finding F-005). Rebuild from the mounted + # being delivered on the sandbox-agent path. Rebuild from the mounted # src on start; fail loud if it cannot build rather than run a stale bundle. command: > sh -c "mkdir -p /pi-agent && cp -a /pi-agent-ro/. /pi-agent/ 2>/dev/null || true; node scripts/build-extension.mjs && exec node_modules/.bin/tsx src/server.ts" # === CONFIGURATION ======================================== # - # Deliberately NO env_file: the Pi sandbox must not inherit the stack's + # Deliberately no env_file: the harness sandbox must not inherit the stack's # secrets (COMPOSIO_API_KEY, STRIPE/POSTHOG/GOOGLE keys, ...). Tools run # server-side via /tools/call, so the sandbox only needs its own port, the Pi # login (mounted below), the OTLP export fallback, and the Daytona credentials - # the SDK reads for the rivet `daytona` sandbox axis. + # the runner reads for the `daytona` sandbox provider. environment: PORT: "8765" PI_CODING_AGENT_DIR: /pi-agent @@ -455,27 +454,32 @@ services: # credential). Must be reachable from this container. AGENTA_HOST: ${AGENTA_HOST:-http://144.76.237.122:8280} AGENTA_API_KEY: ${AGENTA_API_KEY:-} - # Daytona sandbox axis: the rivet daytona provider's `new Daytona()` reads - # these. Scoped to Daytona only (not the full stack secret set). - DAYTONA_API_KEY: ${DAYTONA_API_KEY:-} - DAYTONA_API_URL: ${DAYTONA_API_URL:-} - DAYTONA_TARGET: ${DAYTONA_TARGET:-} - # Pre-baked snapshot (rivet daemon + Pi + certs; Claude inherited from rivet's - # -full base) so Daytona runs skip the ~150s per-invoke `npm install pi`. We - # ship the builder (poc/build_rivet_snapshot.py), not the snapshot itself: each - # operator builds their own, so we never distribute a Claude-containing image. - # See services/agent/docker/README.md for the licensing posture. - AGENTA_RIVET_DAYTONA_SNAPSHOT: ${AGENTA_RIVET_DAYTONA_SNAPSHOT:-agenta-rivet-pi} - AGENTA_RIVET_DAYTONA_INSTALL_PI: ${AGENTA_RIVET_DAYTONA_INSTALL_PI:-false} + SANDBOX_AGENT_PROVIDER: ${SANDBOX_AGENT_PROVIDER:-local} + SANDBOX_AGENT_DAYTONA_API_KEY: ${SANDBOX_AGENT_DAYTONA_API_KEY:-} + SANDBOX_AGENT_DAYTONA_API_URL: ${SANDBOX_AGENT_DAYTONA_API_URL:-} + SANDBOX_AGENT_DAYTONA_TARGET: ${SANDBOX_AGENT_DAYTONA_TARGET:-} + SANDBOX_AGENT_DAYTONA_SNAPSHOT: ${SANDBOX_AGENT_DAYTONA_SNAPSHOT:-agenta-sandbox-pi} + SANDBOX_AGENT_DAYTONA_IMAGE: ${SANDBOX_AGENT_DAYTONA_IMAGE:-} + SANDBOX_AGENT_DAYTONA_INSTALL_PI: ${SANDBOX_AGENT_DAYTONA_INSTALL_PI:-false} # === STORAGE ============================================== # volumes: - ../../../services/agent/src:/app/src + # The Agenta harness's forced skills are real files the runner lays into the + # sandbox per run (resolved from /app/skills). Bind-mounted like src so edits are + # live; the prod image bakes them with `COPY skills ./skills`. + - ../../../services/agent/skills:/app/skills - ${HOME}/.pi/agent:/pi-agent-ro:ro # === NETWORK ============================================== # networks: - agenta-network # === LIFECYCLE ============================================ # restart: always + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8765/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s postgres: # === IMAGE ================================================ #