teleportToRemote session_context 与 outcomes
这页回答什么
teleportToRemote()最终发出去的create session请求里,session_context到底装了什么?sources、seed_bundle_file_id、outcomes、environment_variables、reuse_outcome_branches、github_pr各自是什么意思?
关键文件:
src/utils/teleport.tsxsrc/bridge/createSession.tssrc/bridge/types.tssrc/skills/bundled/scheduleRemoteAgents.ts
一句话结论
teleportToRemote()真正交给后端的不是一个简单 prompt,而是一份 session bootstrap spec。session_context负责描述远端 session 的初始执行世界:代码来源、期望 git 产出、模型、环境变量、PR 关联,以及是否复用现有 outcome branch。
其中最关键的是两层:
sources/seed_bundle_file_id: 远端如何拿到代码outcomes: 远端完成后希望把 git 结果产出到哪里
1. 自动分支下最终的 session_context
自动 environment 选择路径里,teleportToRemote() 最后构造的是:
const sessionContext = {
sources: gitSource ? [gitSource] : [],
...(seedBundleFileId && { seed_bundle_file_id: seedBundleFileId }),
outcomes: gitOutcome ? [gitOutcome] : [],
model: options.model ?? getMainLoopModel(),
...(options.reuseOutcomeBranch && { reuse_outcome_branches: true }),
...(options.githubPr && { github_pr: options.githubPr })
}然后包进:
{
title,
events,
session_context: sessionContext,
environment_id
}这已经说明一个重要事实:
远端 session 的创建协议不是“只带 events”,而是 events + session_context + environment_id 三件套。
2. sources:远端从哪里获得仓库内容
结构
gitSource 形状是:
{
type: 'git_repository',
url: `https://${host}/${owner}/${name}`,
revision,
...(options.reuseOutcomeBranch && {
allow_unrestricted_git_push: true
})
}语义
sources 是给远端容器的 输入代码来源。
它告诉后端:
- clone 哪个 repo
- 以哪个 revision 作为起点
- 是否允许向某些分支直接 push
revision 是什么
它不是 outcome branch,而是:
options.branchName- 否则
getDefaultBranch()
也就是说:
revision决定远端 session 从哪个 base ref checkout 开始工作。
3. seed_bundle_file_id:当 sources 不够用时,改用 bundle seed
如果 GitHub source 不可行,且 bundle fallback 成功,会写入:
session_context.seed_bundle_file_id = seedBundleFileId这表示:
远端不是从 repo URL clone,而是从上传好的 git bundle 文件作为 seed 来初始化仓库。
这条路径的重要性很高,因为它覆盖了:
- 本地 repo 有
.git,但无可用 GitHub clone 权限 - GitHub App / token preflight 失败
- 只想把本地状态原样投过去
所以 sources 和 seed_bundle_file_id 是一组互补机制:
sources偏“远端自己拉代码”seed_bundle_file_id偏“本地把代码种子送过去”
4. outcomes:不是输入,而是远端 session 期望产出的 git 落点
gitOutcome 在代码里构造成:
{
type: 'git_repository',
git_info: {
type: 'github',
repo: `${owner}/${name}`,
branches: [sessionBranch]
}
}这和 sources 很不一样。
sources 解决
- 从哪拿代码
outcomes 解决
- 做完之后 git 结果应该往哪条 branch 产出
也就是说:
outcomes更像 session 的“预期交付物声明”,不是输入 checkout 配置。
这也是为什么它叫 outcome,而不是 destination source。
5. sessionBranch 的来源:通常不是输入 branch,而是新生成的 outcome branch
自动路径里:
- 若
options.title && options.reuseOutcomeBranch,直接复用 - 否则
generateTitleAndBranch(...) - 最后
sessionBranch = options.reuseOutcomeBranch || generated.branchName
所以默认情况下:
- source 的
revision可能是默认分支 / 显式 base branch - outcome 的
branches[0]则通常是新生成的claude/...
这说明 remote session 的 git 语义本来就是两层:
- 从哪条 base branch 开始
- 最终改动要落到哪条 outcome branch
这点非常关键,不能把 revision 和 outcomes.branches 混成一个东西。
6. reuseOutcomeBranch:让 remote 直接复用调用方现有分支
当传入 reuseOutcomeBranch 时,会发生两件事:
A. source 侧
gitSource 增加:
allow_unrestricted_git_push: trueB. session_context 侧
增加:
reuse_outcome_branches: true再加上:
sessionBranch = options.reuseOutcomeBranch
组合起来的效果是:
远端 session 不再新建一个
claude/...outcome branch,而是把当前 session 的 git 输出直接复用并推回调用方指定分支。
所以这里不是单一 flag 生效,而是:
- source 允许 push
- session_context 表明要复用 outcome branch
- outcomes 指明 branch 名
三者一起构成“直接写现有分支”的协议。
7. github_pr:把 session 绑定到某个 PR 上下文
若调用方提供:
githubPr: { owner, repo, number }则 session_context 里会带:
github_pr: { owner, repo, number }代码注释写得很直接:
Backend uses this to identify the PR associated with this session.
也就是说这不是本地逻辑消费字段,而是显式传给后端,让远端 session 带上 PR 语义。
它很可能影响:
- session 关联展示
- review / code review 类型工作流
- 后端如何理解这次 remote 任务的目标对象
8. model:session_context 里直接指定远端模型
自动路径里:
model: options.model ?? getMainLoopModel()说明 remote session 默认会继承当前主模型,除非调用方显式覆盖。
这也再次证明:
远端 session 创建不是只把 prompt 送走,而是把执行配置一起送走。
模型本身就是 session bootstrap spec 的一部分。
9. 显式 environmentId 分支:session_context 更简,但多了 environment_variables
当传入显式 environmentId 时,构造的是另一版:
session_context: {
sources: gitSource ? [gitSource] : [],
...(seedBundleFileId && { seed_bundle_file_id: seedBundleFileId }),
outcomes: [],
environment_variables: envVars
}和自动路径相比有几个明显差异:
有的
sourcesseed_bundle_file_idenvironment_variables
没有的
modelgitOutcomereuse_outcome_branchesgithub_pr
这意味着显式 env 分支更像:
把代码和运行变量定向投送到某个特定环境里执行,而不是完整走一套标准 remote coding branch outcome 流程。
尤其是:
outcomes: []
这点很说明问题,表示该流程不强绑定标准 git outcome 产出。
10. environment_variables:明确是 write-only,主要用于容器内执行环境注入
注释里写得很清楚:
- merged into
session_context.environment_variables - API layer 是 write-only
Get/List响应里会被 strip 掉
显式 env 分支里还会自动注入:
CLAUDE_CODE_OAUTH_TOKEN: accessToken然后再 merge:
options.environmentVariables
这说明:
environment_variables不是普通元数据,而是真正交给远端容器/运行时使用的环境变量输入。
并且它有个安全特征:
- 可写入创建请求
- 不会在后续 GET/LIST 里原样回显
11. events 和 session_context 是互补关系
在 create-session body 里,events 主要承载:
- 初始 user message
- 可选 control_request,比如
set_permission_mode
例如:
{
type: 'event',
data: {
type: 'control_request',
request: {
subtype: 'set_permission_mode',
mode,
ultraplan
}
}
}以及:
{
type: 'event',
data: {
type: 'user',
message: { role: 'user', content: initialMessage }
}
}所以两者分工很明确:
events
- session 一开始就写入的消息流/控制流
session_context
- session 的静态启动配置
如果硬要比喻:
session_context是容器启动规格events是启动后立刻喂进去的第一批输入
12. createSession.ts 侧验证:bridge 也在用同一套协议
src/bridge/createSession.ts 里也构造了近似结构:
session_context: {
sources: gitSource ? [gitSource] : [],
outcomes: gitOutcome ? [gitOutcome] : [],
model: getMainLoopModel(),
}这说明 session_context.sources/outcomes/model 不是 teleport 私货,而是更底层的 Sessions API 通用创建协议。
也就是说:
- teleport remote agent 在用它
- bridge remote-control 也在用它
这进一步增强了判断:
session_context是 Claude Code / CCR session 创建的标准 bootstrap contract。
13. types.ts 侧验证:environment_variables 会进入 work secret
src/bridge/types.ts 的 WorkSecret 里出现了:
environment_variables?: Record<string, string> | null这很关键,因为它说明:
session_context.environment_variables不是创建后就丢了,而是会进入后续 worker / environment runner 能看到的 secret 载荷里。
所以这个字段不是 UI 装饰,而是实际运行面会消费的配置。
14. scheduleRemoteAgents.ts 侧验证:定时 remote agent 也是围绕同类 session_context 组织的
scheduleRemoteAgents.ts 的 prompt 模板里也明确让 trigger body 组织:
environment_idjob_config.ccr.session_contextsourcesallowed_toolsevents
这说明“远端 session = environment + session_context + events”这套思路,不只用于即时 teleport,也用于 scheduled remote agents。
所以可以把它理解成一套通用 CCR session 描述语言。
15. 最简字段语义表
输入代码相关
sources: 远端从 repo URL 获取代码seed_bundle_file_id: 远端从 bundle seed 初始化代码
输出 git 相关
outcomes: 远端 session 期望把结果产出到哪些 repo branchreuse_outcome_branches: 复用已有 outcome branch,而不是新建allow_unrestricted_git_push: source 侧允许直接 push
执行环境相关
environment_id: 跑在哪个 environmentenvironment_variables: 容器内环境变量注入model: 远端 session 用哪个模型
工作对象关联
github_pr: 把 session 绑定到某个 PR 语义上下文
启动输入相关
events: 初始控制请求 + 初始用户消息
当前结论
teleportToRemote() 最关键的产物可以概括成一句:
它不是创建“一个带 prompt 的远端会话”,而是在创建“一个有代码输入来源、有 git 结果目标、有运行环境和控制事件的远端执行单元”。
其中最需要分清的是:
sources是输入outcomes是输出events是启动后的第一批消息/控制流environment_id+environment_variables是运行底座
把这四层分清,remote session 的协议就清楚了。
下一步最值得继续追
- 深挖
createAndUploadGitBundle()如何编码 WIP / stash seed - 细化
foreground -> background热切换为什么要重新启动 async lifecycle - 画一页
task-notification -> queued command -> attachment -> next query精细序列图