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
2 changes: 1 addition & 1 deletion contracts/agents-api/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ stateDiagram-v2
- Each file, Skill or Plugin transfer has a two-minute staging budget. Configure, package installation, setup commands and snapshot finalization share the whole initialization’s 30-minute budget, whose remaining duration Core carries on each operation and the Runtime enforces. Initial input keeps its five-minute admission deadline, so large installations should start from an idle Session.
- A running initialization whose owner is lost, including across a Core restart, fails as unconfirmed; nothing is replayed. A completed Environment never reinstalls on reconnect or native recovery, so later user changes survive.
- Failure is terminal for the Session but destroys neither compute nor files.
- An `openai_hosted` Environment journals `agent.session.environment.pending` in the transaction that starts its initialization and `agent.session.environment.ready` in the one that completes it, each exactly once. A `self_hosted` Environment emits neither.
- Every `openai_hosted` Environment starts `pending`, including one with no files or setup, and journals `agent.session.environment.pending` in the transaction that starts its initialization and `agent.session.environment.ready` in the one that completes it, each exactly once. Core journals `agent.session.environment.connected` when the Runtime connects, which is usually before initialization starts. A `self_hosted` Environment emits neither `pending` nor `ready`, and starts `complete` unless it has files or setup.

A failed initialization records the events and Session state that [Environment initialization failure](./sessions-events.md#environment-initialization-failure) defines. The reason names only the step and its exit status:

Expand Down
1 change: 1 addition & 0 deletions contracts/agents-api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Each item is Core's deliberate or native behavior where the official service beh
- `env` cannot set `OPENAI_API_KEY`, which Core reserves ([env](./environments.md#env-and-setup-commands)); the official service accepts it.
- A `self_hosted` Session without pending input requests no connection ([activity](./environments.md#activity-and-required-actions)); one unused official Session switched to `requires_action` with `environment_connection` about five minutes after creation, and its deletion then returned 409.
- A hosted Environment emits `ready` once, when its initialization completes, and not again before each later Turn as one official Session did. It also emits `connected` and `disconnected`, which that Session did not.
- The usual hosted order is `connected`, `pending`, `ready`, because Core journals `connected` when the Runtime connects, before it starts initialization; the last Environment event then reads `ready` while the Environment is `connected`.

**Files, Skills, Environment files and Artifacts** ([Files and Skills](./source-files.md), [Environment files and Artifacts](./environment-files.md))

Expand Down
4 changes: 2 additions & 2 deletions contracts/agents-api/zh/environments.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Environment 与模板"
source: contracts/agents-api/environments.md
source_hash: fa17eba20ecb905da36dfb1c0ffb8d9a86f49560f0688ddc6b9a3b59009fc4b4
source_hash: 5cd6355e98456fc2b65dd8097080a8bce391d577bd9a746370d281c379d5e48b
---

Environment 是 Session 的执行资源,包括 Harness 运行所在的机器、工作区以及已完成准备的能力。Session 通过其 `environment` 配置创建 Environment;不存在独立的 create 调用。Environment 模板是 Session 创建时解析的可复用准备配置。本契约涵盖这两类资源、两种放置方式、输入接纳、能力准备、Skills、Plugins 和 MCP 连接来源。
Expand Down Expand Up @@ -257,7 +257,7 @@ stateDiagram-v2
- 每个文件、Skill 或 Plugin 传输的暂存预算为两分钟。configure、软件包安装、设置命令和快照最终确定共享整个初始化过程的 30 分钟预算,Core 在每项操作中携带剩余时长,由 Runtime 执行。初始输入仍保留其五分钟接纳期限,因此大型安装应从空闲 Session 开始。
- 正在运行且所有权丧失的初始化,包括跨 Core 重启丧失所有权,会作为未确认而失败;不会重播任何内容。已完成的 Environment 在重连或原生恢复时绝不会重新安装,因此用户后续更改会保留下来。
- 失败对 Session 而言是终止状态,但不会销毁计算资源或文件。
- `openai_hosted` Environment 在开始初始化的事务中记录 `agent.session.environment.pending`,在完成初始化的事务中记录 `agent.session.environment.ready`,各恰好一次。`self_hosted` Environment 两者都不发出。
- 每个 `openai_hosted` Environment 都从 `pending` 开始,包括没有文件和初始化设置的 Environment,并在开始初始化的事务中记录 `agent.session.environment.pending`,在完成初始化的事务中记录 `agent.session.environment.ready`,各恰好一次。Core 在 Runtime 连接时记录 `agent.session.environment.connected`,通常早于初始化开始。`self_hosted` Environment 不发出 `pending` 和 `ready`,且除非有文件或初始化设置,否则从 `complete` 开始。

初始化失败时记录的事件和 Session 状态由 [Environment 初始化失败](sessions-events.md#environment-initialization-failure)定义。原因只指明步骤及其退出状态:

Expand Down
3 changes: 2 additions & 1 deletion contracts/agents-api/zh/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "OpenAI 兼容性"
source: contracts/agents-api/index.md
source_hash: b6322e1f2733cd1e5b1486488c4341190ce72b3cd29bb4547ef3fc244893ff7c
source_hash: 27f86ff815bee7a3bfe69a5fb4706cea7f5c5103e5718b5a5b227822e3e259ac
---

Core 实现下方固定版本的 OpenAI Agents API。本台账列出各项资源的覆盖情况、相对于官方服务的所有差异以及已知缺口。
Expand Down Expand Up @@ -114,6 +114,7 @@ Core 自身字段位于 `x_agents_core` 中([Core extensions](../../../docs/zh
- `env` 不能设置 Core 保留的 `OPENAI_API_KEY`([env](environments.md#env-and-setup-commands));官方服务接受该变量。
- 没有待处理输入的 `self_hosted` Session 不会请求连接([活动状态](environments.md#activity-and-required-actions));一个未使用的官方 Session 在创建约五分钟后转为 `requires_action`,原因为 `environment_connection`,之后删除它返回 409。
- hosted Environment 只在初始化完成时发出一次 `ready`,不会像一个官方 Session 那样在之后每个 Turn 之前再次发出;它还会发出 `connected` 和 `disconnected`,而该官方 Session 没有。
- hosted Environment 通常的顺序是 `connected`、`pending`、`ready`,因为 Core 在 Runtime 连接时、开始初始化之前就记录 `connected`;此时最后一个 Environment 事件显示 `ready`,而 Environment 处于 `connected`。

**Files、Skills、Environment files 和 Artifacts**([Files and Skills](source-files.md)、[Environment files and Artifacts](environment-files.md))

Expand Down
3 changes: 2 additions & 1 deletion services/core/internal/db/queries/environments.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-- name: CreateEnvironment :exec
INSERT INTO environments (id, session_id, initialization)
VALUES ($1, $2, CASE WHEN EXISTS (SELECT 1 FROM initial_environment_files WHERE session_id = $2)
VALUES ($1, $2, CASE WHEN (SELECT configuration->'environment'->>'type' FROM sessions WHERE id = $2) = 'openai_hosted'
OR EXISTS (SELECT 1 FROM initial_environment_files WHERE session_id = $2)
OR EXISTS (SELECT 1 FROM environment_setups WHERE session_id = $2) THEN 'pending' ELSE 'complete' END);

-- name: GetEnvironment :one
Expand Down
3 changes: 2 additions & 1 deletion services/core/internal/db/sqlc/environments.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ func TestArchiveWaitingCleanupReceiptBarrier(t *testing.T) {
t.Fatal(err)
}
session := created.Session
if _, err := pool.Exec(t.Context(), `UPDATE environments SET initialization='complete' WHERE id=$1`, session.Environment.ID); err != nil {
t.Fatal(err)
}
secret := uuid.NewString()
key := deployment.AllocationKey{TenantID: project.TenantID, EnvironmentID: session.Environment.ID}
owner, err := leased.Deployment.ReserveAllocation(t.Context(), key, installation, runtimedevice.HashCredential(secret))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ func workspaceSettlementFixtureWithSetup(t *testing.T, provider sandbox.SandboxP
t.Fatal(err)
}
session := created.Session
// The fixture's Environment is already prepared.
if _, err := pool.Exec(t.Context(), `UPDATE environments SET initialization='complete' WHERE id=$1`, session.Environment.ID); err != nil {
t.Fatal(err)
}
if nodeID != "" {
if _, err := operations.EnsurePlacement(t.Context(), deployment.AllocationKey{TenantID: session.TenantID, EnvironmentID: session.Environment.ID}, installation); err != nil {
t.Fatal(err)
Expand Down
3 changes: 3 additions & 0 deletions services/core/tests/integration/archive_cancellation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ func TestArchiveWaitingCancellationReceipts(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if _, err := s.pool.Exec(t.Context(), `UPDATE environments SET initialization='complete' WHERE id=$1`, session.Environment.ID); err != nil {
t.Fatal(err)
}
secret := uuid.NewString()
owner, err := leased.Deployment.ReserveAllocation(t.Context(), deployment.AllocationKey{TenantID: project.TenantID, EnvironmentID: session.Environment.ID}, installation, runtimedevice.HashCredential(secret))
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ func localEnvironment(t *testing.T, s *Store, tenant string) (sessions.Session,
if err != nil {
t.Fatal(err)
}
// The fixture's Environment is already prepared.
if _, err := s.pool.Exec(t.Context(), `UPDATE environments SET initialization='complete' WHERE id=$1`, environment.ID); err != nil {
t.Fatal(err)
}
return session, environment
}

Expand Down
5 changes: 5 additions & 0 deletions services/core/tests/integration/root_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func (s *Store) SetPlacement(rules *placement.Rules) { s.placement = rules }
// CreateSession creates a Session through the Session service on s.
func (s *Store) CreateSession(ctx context.Context, tenant string, input sessions.CreateSession) (sessions.Session, error) {
creation, err := createSession(ctx, s, tenant, input)
// A hosted fixture with nothing to set up stands for a prepared Environment;
// the initialization tests give theirs files or a setup.
if err == nil && creation.Session.Environment != nil && len(input.InitialFiles) == 0 && input.Initialization.Empty() {
_, err = s.pool.Exec(ctx, `UPDATE environments SET initialization='complete' WHERE id=$1`, creation.Session.Environment.ID)
}
return creation.Session, err
}

Expand Down
39 changes: 39 additions & 0 deletions services/core/tests/integration/runtime_initialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,42 @@ func TestManagedRuntimePreparationAllOperationsUsePeer(t *testing.T) {
t.Fatal("initialization events", lifecycle)
}
}

func TestHostedEnvironmentWithoutSetupIsInitialized(t *testing.T) {
s, key := configuredStore(t)
tenant := uuid.NewString()
creation, err := createSession(t.Context(), s, tenant, WithFixtureModelProvider(sessions.CreateSession{
Creator: FixtureCreator(), Engine: "codex", IdempotencyKey: uuid.NewString(),
Configuration: json.RawMessage(`{"agent":{"model":"test-model"},"environment":{"type":"openai_hosted","network":{"access":"disabled"}}}`),
}))
if err != nil {
t.Fatal(err)
}
session := creation.Session
if initializationState(t, s, tenant, session.Environment.ID) != "pending" {
t.Fatal("hosted Environment without setup did not start pending")
}
provider := &initializingProvider{lifecycleProvider: lifecycleProvider{resources: map[string]sandbox.Info{}}}
provider.apply = func(request proto.RuntimePreparePayload, _ []byte) proto.RuntimePrepareResultPayload {
t.Error("empty initialization sent a Runtime operation", request.Action)
return completedInitialization(request, nil)
}
worker, _ := managedWorkerMode(t, s, key, provider, true)
if _, err := worker.ProvisionEnvironment(t.Context(), tenant, session.Environment.ID, key); err != nil {
t.Fatal(err)
}
awaitInitialization(t, s, tenant, session.Environment.ID, "complete")
changes, err := sessionAdapter(s).ListSessionEvents(t.Context(), tenant, session.ID, 0)
if err != nil {
t.Fatal(err)
}
var lifecycle []string
for _, change := range changes {
if change.Event.Type == "agent.session.environment.pending" || change.Event.Type == "agent.session.environment.ready" {
lifecycle = append(lifecycle, change.Event.Type)
}
}
if !reflect.DeepEqual(lifecycle, []string{"agent.session.environment.pending", "agent.session.environment.ready"}) {
t.Fatal("initialization events", lifecycle)
}
}
Loading