雨天小六

读懂 Codex(3.11):TurnEnvironmentSnapshot 与环境选择

· 更新于 2026-08-02 · 专栏:读懂 Codex

#Codex#Agent Runtime#生命周期#软件架构

TurnEnvironmentSnapshot 不是环境变量字典。它保存这次 Turn 选中的执行环境、cwd、workspace roots、Shell 和 ShellSnapshot,以及环境尚在启动还是已经 Ready。它解决的是本地/远端执行位置的一致性。

从配置 selection 到可执行 TurnEnvironment

TurnEnvironmentSelection 只有 environment id、cwd 和 workspace roots。ThreadEnvironments 用 EnvironmentManager 解析 id,并异步等待环境 ready:本地直接采用本地 Shell;远端读取 EnvironmentInfo 中的 shell 描述;随后并发构建 shell snapshot。

Environment selection 解析为 Starting 或 Ready TurnEnvironment
图 3.11-1:selection 是配置意图,resolution 才得到可执行环境;非阻塞 snapshot 可以保留 Starting,让 Turn 不必为远端连接无限等待。
async def resolve_selection(selection):
    env = environment_manager.get(selection.environment_id)
    if env is None:
        return SKIP_UNKNOWN
    await env.wait_until_ready()
    shell = (await env.info()).shell if env.is_remote else local_shell
    turn_env = TurnEnvironment(env, selection.cwd,
                               selection.workspace_roots, shell)
    turn_env.shell_snapshot = spawn_shared(build_shell_snapshot(turn_env))
    return turn_env

ThreadEnvironments 使用 ArcSwap 发布 selection 列表。更新时按 environment id 去重,保留第一次出现;完全相同且未失败的 selection 复用旧 resolution;未知 id 记录 warning 后跳过;改变 cwd/roots 会启动新 resolution,但若 Environment Arc 相同,可以复用 connection listener。

被逻辑移除的 listener 会立即 abort。仅依赖 Arc drop 不够,因为旧 ArcSwap reader 可能长期持有旧 snapshot,使已经移除环境继续发送连接事件。

Blocking 与 non-blocking snapshot

Blocking 模式等待每个 selection resolution 完成;non-blocking 模式只 peek 当前结果,尚未完成的记录为 Starting。失败项不会进入 snapshot。Ready 与 Starting 都保持用户选择顺序。

TurnContext 捕获后,refresh_readiness() 只检查这些 Starting future 是否已经完成:成功则提升 Ready,失败则移除。它不会读取 ThreadEnvironments 后来发布的新 selections。

def refresh_readiness(old_snapshot):
    result = []
    for state in old_snapshot.in_selection_order:
        if state.is_ready:
            result.append(state)
        elif state.resolution.peek() == SUCCESS:
            result.append(Ready(state.resolution.value))
        elif state.resolution.peek() == PENDING:
            result.append(state)
        # failed: omit
    return TurnEnvironmentSnapshot(result)
Turn 内环境可以 Starting 到 Ready 但不采用新的 selection
图 3.11-2:Turn A 能等到自己选中的 remote 变 Ready,却不会在中途改用 Thread 后来为 Turn B 选择的新环境。

primary、local 与 single_local 的语义

primary() 是选择顺序中的第一个 Ready 环境,不保证是 local;local() 查找第一个非远端 Ready 环境。某些只能在单一本地 cwd 上成立的优化使用 single_local_environment:必须没有 Starting、恰好一个 Ready 且它是 local。只看 ready count 会误把“另一个远端仍在启动”当作单环境。

连接状态 Event 为什么 id 为空

Environment connection listener 是 Thread 级后台观察者,不属于某个 Submission 或 Turn,因此发送 EnvironmentConnected/Disconnected 时 Event id 为空字符串。客户端应按 environment_id 和事件类型处理,不能把所有 Event 都强行挂到 Turn ID。

评论


← 返回文章列表