Skip to content
Merged
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
19 changes: 15 additions & 4 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,25 @@ The Studio `环境` page stores each environment definition, generated Dockerfil
build version, log metadata, and resulting image reference in the private Studio
TOS bucket. Creating or saving an environment starts an asynchronous
CodePipeline build and pushes the resulting image to Container Registry.
New environments default to the AIO Sandbox base image, which keeps the inherited
`/opt/gem/run.sh` entrypoint and port `8080` so the Sandbox Shell API remains
available. Standard Ubuntu 22.04 and 24.04 images remain supported, and records
created before the base-environment field was introduced resolve to Ubuntu.
Custom and Dockerfile environments can select a preset environment: none, AIO
Sandbox, or Codex Sandbox. Selecting a preset pins its `FROM` instruction ahead
of the editable Dockerfile body; selecting none leaves the complete Dockerfile
under user control. AIO Sandbox keeps the inherited `/opt/gem/run.sh` entrypoint
and port `8080`, while Codex Sandbox exposes task delegation through its Codex
App Server instead of the generic Sandbox shell tool. Legacy records created
before preset environments were introduced remain compatible.
Each image version exposes a read-only Manifest at
`/web/environments/{environmentId}/builds/{versionId}/manifest`; the Studio
environment card opens the same version-bound contract as YAML for inspection
and copying.

When an environment is mounted to an Agent conversation, Studio assigns a new
`mount_instance_id`. Sandbox Tool Sessions are reused only while the Agent
session, mount instance, environment version, Tool ID, image, provider, and
region all remain unchanged. Unmounting and mounting again creates a new mount
instance and therefore a new Sandbox Tool Session. Codex Sandbox progress and
its Sandbox Session and Codex Thread identifiers are streamed into the normal
tool-call card and preserved in conversation history.
Volcengine builds use the Aliyun PyPI mirror, Huawei Cloud Python source mirror,
and npmmirror for Playwright browsers; BytePlus builds use the corresponding
official sources. Cross-version Python combinations are compiled from pinned
Expand Down
12 changes: 11 additions & 1 deletion frontend/server/environments/dockerfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from __future__ import annotations

from dataclasses import dataclass
import re

from .models import EnvironmentInput

Expand Down Expand Up @@ -201,6 +202,8 @@ def build_dockerfile(config: EnvironmentInput) -> str:
"""Build the canonical Dockerfile when the user did not provide one."""
if config.dockerfile:
return validate_dockerfile(config.dockerfile)
if config.base_environment == "codex-sandbox":
raise ValueError("Codex Sandbox 预制环境必须提供包含基础镜像的 Dockerfile。")

os_label, ubuntu_base_image = _OPERATING_SYSTEMS[config.operating_system]
uses_aio_python = config.base_environment == "aio-sandbox"
Expand Down Expand Up @@ -350,11 +353,18 @@ def build_dockerfile(config: EnvironmentInput) -> str:
def environment_base_image(config: EnvironmentInput) -> str:
if config.base_environment == "aio-sandbox":
return AIO_BASE_IMAGE
if config.base_environment == "codex-sandbox":
match = re.search(
r"^\s*FROM(?:\s+--platform=\S+)?\s+(\S+)",
config.dockerfile,
flags=re.IGNORECASE | re.MULTILINE,
)
return match.group(1) if match else ""
return _OPERATING_SYSTEMS[config.operating_system][1]


def environment_capabilities(config: EnvironmentInput) -> list[str]:
if config.base_environment == "aio-sandbox":
if config.base_environment in {"aio-sandbox", "codex-sandbox"}:
return ["shell-exec"]
return []

Expand Down
17 changes: 14 additions & 3 deletions frontend/server/environments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from pydantic import BaseModel, ConfigDict, Field, model_validator

EnvironmentOperatingSystem = Literal["ubuntu-22.04", "ubuntu-24.04"]
EnvironmentBaseEnvironment = Literal["ubuntu", "aio-sandbox"]
EnvironmentBaseEnvironment = Literal["ubuntu", "aio-sandbox", "codex-sandbox"]
EnvironmentLanguage = Literal["python-3.10", "python-3.12"]
EnvironmentBuildStatus = Literal[
"preparing",
Expand Down Expand Up @@ -250,11 +250,16 @@ def normalize(self) -> EnvironmentInput:
self.description = self.description.strip()
self.dockerfile = self.dockerfile.strip()
if (
"base_environment" not in self.model_fields_set
and "/codexenv:" in self.dockerfile.lower()
):
self.base_environment = "codex-sandbox"
elif (
"base_environment" not in self.model_fields_set
and "aio.sandbox" in self.dockerfile.lower()
):
self.base_environment = "aio-sandbox"
if self.base_environment == "aio-sandbox":
if self.base_environment in {"aio-sandbox", "codex-sandbox"}:
self.operating_system = "ubuntu-22.04"
self.language = "python-3.12"
if not self.name:
Expand Down Expand Up @@ -438,10 +443,16 @@ class EnvironmentManifestStatus(BaseModel):
updated_at: datetime = Field(alias="updatedAt")


EnvironmentManifestApiVersion = Literal[
"agentkit.studio/v3",
"agentkit.studio/v1alpha1",
]


class EnvironmentManifest(BaseModel):
model_config = ConfigDict(extra="forbid", populate_by_name=True)

api_version: Literal["agentkit.studio/v1alpha1"] = Field(alias="apiVersion")
api_version: EnvironmentManifestApiVersion = Field(alias="apiVersion")
kind: Literal["Environment"] = "Environment"
metadata: EnvironmentManifestMetadata
spec: EnvironmentManifestSpec
Expand Down
Loading
Loading