teleportToRemote 决策树
这页回答什么
teleportToRemote()到底在决定什么?它如何在 GitHub clone、git bundle、empty sandbox、environment 选择之间做分支,并把本地工作区送到远端 session?
关键文件:
src/utils/teleport.tsxsrc/utils/teleport/gitBundle.tssrc/utils/teleport/environments.tssrc/tools/AgentTool/AgentTool.tsx
一句话结论
teleportToRemote()的本质不是“发一个远端请求”这么简单,而是在做一套 source-selection + environment-selection + session-creation 决策树:先决定远端容器如何拿到代码,再决定跑在哪个 environment,最后才创建 session。
这一步几乎决定了 remote agent 能不能正常工作。
1. 它要解决的真实问题
remote agent 启动时,远端 session 至少要知道三件事:
- 代码从哪里来
- 跑在哪个 environment
- 初始消息 / outcome branch / session metadata 是什么
所以 teleportToRemote() 本质上是在回答:
如何把当前本地上下文投送成一个远端可执行 session。
2. 整体决策可以分成两大分支
A. 显式 environmentId 分支
这是更“受控”的模式。
特点:
- 跳过普通 environment discovery
- 仍然会做 repo/source 决策
- 可注入
environmentVariables - 可显式要求
useBundle
B. 普通自动选择分支
这是默认 remote agent 路径。
特点:
- 自动决定 source ladder
- 自动拉 environments
- 自动选择 environment
- 再构造 create-session 请求
3. 显式 environmentId 分支:更像“定向投送”
当 options.environmentId 存在时:
先做什么
- 校验 OAuth / org UUID
- 准备 headers
- 构造
envVars- 自动带
CLAUDE_CODE_OAUTH_TOKEN - 再 merge
options.environmentVariables
- 自动带
再决定 source
如果 useBundle === true
- 直接
createAndUploadGitBundle(...) - 拿到
seedBundleFileId - 远端从 bundle 克隆
否则
detectCurrentRepositoryWithHost()- 如果能识别仓库,就构造
gitSource - 远端按 git repository source checkout
最后 create session
requestBody 的关键部分是:
session_context.sourcessession_context.seed_bundle_file_idsession_context.environment_variablesenvironment_id
这说明显式 env 分支的特点是:
environment 是调用方指定的,source 是在这个前提下再补进去。
4. 自动分支的第一步:source selection ladder
自动分支里最核心的注释已经把 ladder 说清楚了:
GitHub clone (if CCR can actually pull it)
→ bundle fallback (if .git exists)
→ empty sandbox这是整个 remote 设计最重要的路径。
5. GitHub clone 路径不是盲选,而是先 preflight
自动分支会先:
detectCurrentRepositoryWithHost()- 判断 repo host
- 如果是
github.com,先跑checkGithubAppInstalled(...)
它不是“看见 GitHub remote 就直接用”。
这是个很老练的判断,因为如果远端根本没有能 clone 这个 repo 的 token,直接起 session 只会得到一个必死容器。
所以它先把状态分成:
github_preflight_okgithub_preflight_failedghes_optimisticno_github_remoteforced_bundleno_git_at_all
这批 sourceReason 本质上就是 source 决策的解释码。
6. 什么时候走 Git source
如果满足:
- repoInfo 存在
- ghViable 为真
就构造:
gitSource = { type: 'git_repository', url, revision, ... }- 可选
allow_unrestricted_git_push - 同时构造
gitOutcome
这里还会决定:
revision = explicit branchName || default branchreuseOutcomeBranch时允许直接往调用方分支推
也就是说 Git source 路径不仅是在“带代码过去”,也是在给 remote session 定义一个 git outcome 策略。
7. 什么时候走 bundle fallback
如果:
- GitHub 路径没走通
- bundle gate 开着
- 本地存在
.git
就进入:
createAndUploadGitBundle(...)- 得到
seedBundleFileId
这里 bundle 的语义不是普通归档上传,而是:
把当前本地仓库状态(包括某些未提交工作树场景)封装成 seed bundle,交给远端作为 clone 起点。
这条路径特别重要,因为它让 remote agent 不再严格依赖:
- GitHub remote
- GitHub App 完整安装
- 远端可直接 clone 公私仓
所以 bundle 是 remote teleport 的真实兜底机制。
8. 什么时候会退化成 empty sandbox
如果最后:
- 没有
gitSource - 也没有
seedBundleFileId
代码会明确记录:
No repository detected — session will have an empty sandbox
这说明 remote session 仍然可以启动,只是:
- 没有 repo checkout
- 更像一个空工作区容器
所以 teleport 的 source 选择不是二元成败,而是三态:
- github
- bundle
- empty
这一点很像成熟系统的工程做法,不因为没有 repo 就整个 remote 功能报废。
9. 自动分支的第二步:environment selection
source 决策完成后,自动分支还要:
fetchEnvironments()- 决定选哪个 environment
优先顺序大致是:
- 配置中的
defaultEnvironmentId(如果允许) anthropic_cloud- 非 bridge 的第一个环境
- 实在不行就第一个环境
还有一个很细的保护:
- 如果调用方要求
useDefaultEnvironment但列表里没有anthropic_cloud - 会 retry 一次
fetchEnvironments() - 还不行就 fail fast
这说明 environment 选择也不是随便捡一个能用就上,而是尽量避免 silently 掉进死环境。
10. source 与 environment 是解耦但连续的两层决策
这个函数最值得注意的设计点之一是:
先决 source
- 远端怎么拿代码
再决 environment
- 代码在哪个远端环境执行
它们不是一回事,但被串成一个 session creation pipeline。
这让系统可以表达很多组合:
- GitHub source + anthropic cloud env
- bundle source + explicit code review env
- empty sandbox + default env
所以 teleport 不是单点判断,而是一个小型 deployment planner。
11. telemetry 暗示这其实是产品级关键路径
代码里有:
tengu_teleport_bundle_modetengu_teleport_source_decision
说明 source path:
- 走 GitHub
- 走 bundle
- 退空 sandbox
不是无足轻重的内部细节,而是产品上被持续观测的重要决策。
换句话说,团队自己也知道:
remote agent 好不好用,很大程度就取决于 teleport 这一步选得对不对。
12. 最简决策图
teleportToRemote()
→ OAuth / org UUID
→ if explicit environmentId:
→ build env vars
→ choose bundle or git source
→ create session directly
else:
→ detect repository
→ source ladder:
github clone if viable
else bundle if possible
else empty sandbox
→ fetch environments
→ choose environment
→ create session如果再压缩成 source 决策图,就是:
repo + github viable ? github source
: repo/.git + bundle gate ? seed bundle
: empty sandbox当前结论
teleportToRemote() 的工程意义在于:
它把 remote agent 启动前最容易失败的现实问题,收束成一条明确的决策树。
这条树至少处理了:
- GitHub auth 不可用
- 只有本地 git 仓库、没有可 clone remote
- 无 repo 场景
- 指定 environment 的特种流程
- environment list 不稳定
所以 remote agent 能跑起来,真正的前线不是 RemoteAgentTask,而是 teleport 阶段。
下一步最值得继续追
- 单独深挖
createAndUploadGitBundle()如何处理未提交工作区 - 追 create-session request body 里
outcomes/gitOutcome的语义 - 把
AgentTool local async / remote / sync三条路整理成统一总图