架构
T3 Code 把执行留在拥有该工作区的环境(environment)里。Web、桌面和移动客户端(client)通过已认证的 RPC 控制它。远程客户端绝不能用自己的文件系统、提供方(provider)凭据或机器状态来代替环境的。桌面应用内置了一台服务器,但它的渲染进程遵循同一条边界。
所有权边界
提供方(provider)进程、终端、Git 和项目文件属于服务器。共享的连接状态和领域状态放在 packages/client-runtime;客户端(client)提供平台服务和界面。 把这段逻辑保持为共享,可以避免 Web 与移动端在重连和多环境行为上出现分歧。参见连接运行时和远程环境。
RPC 契约是彼此独立版本化的客户端与服务器之间的边界。订阅只发送客户端需要的状态,因此正在查看一个对话线程(thread)的客户端,不必为每个对话线程的历史付出代价。套接字通过认证,并不等于它上面的每个方法都得到授权。参见环境认证。
拉取请求关联兼容性
每个 protocol-2 环境(environment)都会宣告 threadPullRequests。某个环境的服务器配置加载完成之后,客户端才会使用已持久化的 pullRequests[]、多链接命令(command)、堆叠界面,以及反向的对话线程查找。在此之前,它们会隐藏关联操作。
环境会继续宣告 threadPullRequestLinking、接受旧版元数据命令,并发出派生的 linkedPullRequest 字段,因为较旧的客户端仍会读取它们。这个不含主机的字段只包含该对话线程所属项目自己仓库中的链接。保留旧版线路字段、投影列和回放支持。
随提供方而异的行为应当放在适配器(adapter)后面。编排使用的是规范化之后的命令和事件(event),因此新增一个提供方,不应当在领域层和各个客户端里到处增加分支。参见提供方约束。
设置的所有权
客户端(client)偏好留在当前客户端;环境(environment)默认值和项目覆盖留在拥有它们的服务器上。Web 和桌面的设置目标是 URL 状态,并对照当前连接和项目成员关系来解析。不可用的目标不得回退到另一个环境。 所有环境是对已连接且已加载的服务器所做的显式批量编辑,不是持久的全局默认值,也不承诺同步离线环境或将来的环境。项目组目标同样是选出已知的、位于环境本地的检出;组本身不存储继承而来的默认值。
持久意图与副作用
编排状态以事件(event)的形式被决定;投影一旦应用这些事件,就成为事实来源。v2 编排器串行处理命令(command),并在不执行提供方(provider)或文件系统工作的情况下决定事件。 EventSink在同一个数据库事务中提交事件、已持久化的投影、已接受命令的命令回执,以及发件箱效应(outbox effect)。订阅者在这次提交之后才收到事件。这使命令重试保持幂等,并防止已持久化的投影超前于事件日志。
效应工作器在意图记录之后执行副作用,再把结果送回编排。因此,命令确认意味着意图已经提交,而不是提供方、检查点(checkpoint)或其他后续工作已经完成。外部 I/O 不要放进命令决策,也不要放进数据库事务。绑定到已丢失的提供方进程上的效应不能直接重放;恢复会先把它们退役,再接纳新的工作。
事件日志只保留实时读取方需要的内容:大约一天的历史,用于重连回放和按命令 id 重试。 ProjectionMaintenance会删除投影已经覆盖的较旧事件,因此投影无法从日志重建。它保留每条流的最新事件(高水位、流版本、快照序列)、thread.created(旧版导入器的标记)、每一次 running 运行更新(Codex 用量历史读取每次尝试的第一条),以及被保留的子智能体行所引用的提示词锚点。如果客户端的游标同时低于其对话线程(thread)的最新事件和已记录的已剪裁至序列,就说明它错过了被剪裁的事件,于是从快照恢复;空闲对话线程的游标恢复时则不使用快照。恢复过程在读完回放之后会再次核对该序列,因为某一轮处理可能在读取期间进行剪裁。V2 命令回执为幂等而保留:早于所保留历史的重试仍会被去重,但其回放返回时已经没有那些被剪裁的事件。导入的旧版命令,其回执会在对应对话线程导入完成之后删除。
保留的事件在回放时必须仍然可以解码。更改模式既会影响旧环境的启动,也会影响实时 RPC 流量。兼容性工作必须把已存储的历史考虑在内,而不能只看最新客户端发送的内容。
回合完成与检查点
提供方(provider)回合(turn)结束,与其后续工作落定,是两个分开的里程碑。编排独立于运行收尾记录提供方回合状态和运行状态。迟到的检查点(checkpoint)或差异不得延长已记录的提供方耗时,也不得让客户端(client)继续把提供方工作显示为活动状态。完成后的拉取请求(pull request)发现还会检查:检出是否仍与该对话线程(thread)的非默认分支一致,以及是否没有更新的运行处于活动状态。
检查点使用隐藏的 Git 引用捕获工作区状态,而不向用户的分支添加提交。还原必须使工作区状态与提供方对话相互对齐。无法回滚自身对话的提供方,必须在更改文件系统之前拒绝该操作。
对话线程的落定归服务器所有。落定服务在没有已连接客户端的情况下评估拉取请求和不活动设置。合并通知会使缓存的拉取请求状态失效,并触发一次检查。发生在 T3 之外的合并,例如某个智能体运行 gh pr merge,不会发出通知,因此当一次执行了合并或关闭命令的运行结束时,拉取请求同步反应器会重新读取该对话线程仍处于打开状态的链接。每个代码托管平台(forge)的定义都在 changeRequestCloseCommand 中写明其 CLI 的命令。带防护的 thread.auto-settle 命令会拒绝更新的活动、显式的落定覆盖,以及进行中或被阻塞的工作。它记录活动时间戳以便稳定排序,并分离空闲的提供方会话(session)。客户端渲染已持久化的结果;它们不根据自己的时钟或拉取请求缓存来推导落定。
等待异步工作
测试使用可排空的工作器,一直等到队列及其当前条目都已完成。仅凭队列为空,并不能证明工作器处于空闲。
V2 测试还会排空效应工作器,或等待某一条已持久化的事件(event)或回执。测试信号独立于那些使分发保持幂等的持久命令回执。生产行为必须使用已持久化的状态和事件,而不是测试插桩,也不是对已经过时间的假设。
Electron 外壳会在异步服务之前同步取得 DesktopPreReadyPlatform.layer。在 Linux 上,这会在 Chromium 初始化其门户连接之前,设置桌面项身份和全局快捷键门户标志。等到 DesktopAppIdentity 再设置身份就太晚了:Chromium 会缓存第一次注册,失败的注册也包括在内。该身份必须与 DesktopLinuxUrlHandler 所管理的已安装条目一致。就绪前的准备还会在门户注册之前刷新该条目的 Exec 路径:AppImage 更新可能移除先前的可执行文件,这会使旧条目失效,即便其文件名仍然正确。稍后的 URL 处理器会避免在门户可能正在读取时,重写一条内容相同的条目。在 Wayland 上,Electron 同步的快捷键注册结果只确认提交已经发出;它不确认桌面环境已经同意,也不确认绑定已经生效。
在启动路径上,原生模块从不加载进 Electron 主进程,快照功能所保留的那两个模块是隔离的:@crowecawcaw/xa11y 只在派生的 Node 模式子进程(SnapShotAccessibilityWorker、RegionSnapShotWorker)和一个工作线程中运行,ffi-rs 则在 WindowsForeground.ts 内部按需加载,用于少数几次 Win32 调用。macOS 的窗口查找会转去调用 osascript,而不是使用原生插件。其中任何一个崩溃或停滞都不得拖垮应用,因此新的原生能力应放进带有截止时间的子进程,而不是主进程中的 import。