编排器(orchestrator)MCP 服务器
用途
T3 通过应用自有的 MCP 端点暴露 V2 编排。提供方(provider)智能体可以用这个端点:
- 在任意受支持的提供方实例(provider instance)上,创建应用自有的子智能体(subagent);
- 等待或轮询该子智能体的持久结果;
- 取消一项活动中的委派任务;
- 创建一条或多条普通的顶层 T3 对话线程(thread);
- 列出一个项目(project)的对话线程,并增量读取其中任意一条;
- 重命名对话线程、重新生成标题,以及关联或取消关联拉取请求(pull request);
- 发送后续消息,或对后续消息做引导(steering);
- 等待或中断普通对话线程的运行;
- 调度在没有活动回合(turn)时仍会执行的重复工作。
这些是 T3 的编排操作,不是提供方原生的子智能体 API。委派任务总会创建一条 T3 子对话线程并运行它。子对话线程只收到所给的任务提示词,以及同一次工具调用里可选附上的角色说明。父级的对话历史不会复制到子对话线程里。
ThreadManagementService 是 V2 WebSocket 命令(command)与 MCP 共用的服务器应用边界。它负责对话线程的查找与列出、发送模式的选择、持久发送的后置条件、等待时的轮询,以及中断对象的选择;OrchestratorV2 仍然是更底层的命令与事件(event)处理器。传输适配器(adapter)只负责认证、解析各传输自己的输入,并构造响应。
传输与认证
编排工具共用现有的、已经过认证的 HTTP MCP 端点:
http://127.0.0.1:<server-port>/mcp提供方(provider)可见的服务器键是 t3-code。这个端点同时注册预览工具包和编排工具包。
ProviderSessionManager 打开新的 V2 提供方会话(session)之前,会向 McpSessionRegistry 申请一份凭据,范围限于:
- 该 T3 环境(environment);
- 父级 T3 对话线程(thread);
- 具体的提供方实例(provider instance);
- 该提供方会话。
这份凭据授予 preview 和 orchestration 能力。凭据达到最长有效期后会过期,空闲过久也会过期,并在提供方会话被释放时撤销。原始令牌不会写入编排状态。
MCP HTTP 服务器解析 bearer 令牌,并把得到的 McpInvocationScope 交给工具处理程序。编排处理程序在读取或修改状态之前,还会检查 orchestration 能力。
提供方(provider)注入
Codex V2
Codex 在每条对话线程(thread)的 thread/start、thread/resume 和 thread/fork 配置里得到 T3 的 MCP 服务器,提供方会话(session)令牌放在 Authorization 请求头中。同一台服务器会注册两次:
t3-code:除t3_thread_wait以外的全部工具。t3-code-direct:只有t3_thread_wait,并带上omit_tools_from = ["code_mode", "deferred"]。
Code Mode 把嵌套调用当作脚本的一部分来运行,因此阻塞式等待进行时,模型仍继续工作。第二条注册让这次等待成为一次直接的工具调用,和 Codex 自己的等待工具一样。Codex 把调用报告为 <server>.<tool>,所以 packages/shared/src/t3McpToolPresentation.ts 必须能解析这两个服务器名称。工具如何展示,以及待处理工作如何检测,都依赖这一点。
Claude Agent SDK V2
Claude 在查询选项里收到一台 HTTP MCP 服务器:
{
mcpServers: {
"t3-code": {
type: "http",
url: "http://127.0.0.1:<port>/mcp",
headers: {
Authorization: "Bearer <provider-session-token>",
},
},
},
allowedTools: [
// existing allowed tools
"mcp__t3-code__*",
],
}适配器(adapter)只记录 MCP 配置是否存在,不记录服务器的请求头或令牌。
Cursor Agent SDK V2
Cursor 通过 SDK 的 mcpServers,在 Agent.create / Agent.resume 以及每一个 send 上,接收同一个已认证的 HTTP MCP 端点。从 @cursor/sdk 1.0.39 起,发送时指定的服务器只替换创建时内联的那一组;来自 settingSources(project、user、team、mdm、plugins)的环境中原有服务器仍然可用。适配器会把授权请求头传给 SDK,但投影进协议诊断的只有脱敏后的选项元数据。
Grok ACP V2
Grok 通过 ACP 的 session/new、session/load 和 session/fork 上的 mcpServers 字段,接收已认证的 HTTP MCP 端点。共用的 ACP 适配器负责标准协议行为;Grok 这一变体额外加入 xAI 扩展请求,例如结构化的用户提问。
ACP 没有定义原生子智能体,也没有定义主动引导。因此 Grok 使用编排器(orchestrator)自有的子对话线程,并通过取消后重启来实现引导。它当前的驱动(driver)也缺少 session/fork,所以应用在分叉时使用可移植的上下文转移。这些是编排器的策略,不是某个提供方专属的 MCP 工具。
ACP Registry V2
acpRegistry 驱动是这同一套共用 ACP 适配器的通用变体。每个提供方实例(provider instance)都从官方 ACP Registry 里指定一个智能体。设置会通过已连接的服务器搜索注册表,然后在持久化该提供方实例之前,准备一份兼容的发行包。二进制发行包使用受管理、带版本的缓存;声明了校验和时会加以核验。固定版本的 npx 包通过 npm 全局安装;uvx 包使用 uv tool install。ACP 直接启动由此得到的全局命令(command),因此同一条命令也可以用来做终端认证。本地可执行文件可以覆盖已安装的命令,同时不改注册表所声明的参数或环境变量。
搜索、准备、提供方状态和提供方会话启动,共用一个以服务器为范围的目录服务。这样,平台选择和注册表校验在设置里和在运行时都完全一致。查看目录时不会启动 ACP 进程,不会探测模型,也不会做认证。受管理的提供方快照会在正常的提供方刷新生命周期里创建一个一次性的 session/new,把成功当作认证已经就绪的证明,并把对外公布的模型投影进快照。只能在终端完成的登录,仍然要在已连接的服务器上手动进行。
提供方会话加载、提供方会话分叉、模型、模式以及 MCP 传输这些能力,只有所选智能体对外公布了它们才会启用。缺少的功能按 V2 策略降级:引导改为中断后重启;原生 session/fork 不可用时,分叉改用可移植上下文;子智能体则使用编排器自有的子对话线程。注册表里的智能体不会得到提供方专属的扩展;那些扩展仍留在 Grok 这类变体中。
Pi V2
即使 Pi 支持原生 MCP,T3 仍保留一条提供方会话的 HTTP 桥接。mcp.json 里的条目会覆盖原生注册,而原生 MCP 默认的 60 秒请求超时可能会打断长时间运行的 T3 工具。这条桥接持有被注入的端点和凭据,并转发 Pi 的取消信号。
当提供方会话的凭据存在时,适配器会把一个 T3 自有的扩展写入服务器缓存,并按下面的参数启动 pi --mode rpc --extension <cache>/pi-t3-mcp-extension.ts:
T3_MCP_URL=http://127.0.0.1:<port>/mcp
T3_MCP_BEARER_TOKEN=<provider-session-token>该扩展把公开名称保存在 mcp__t3-code__ 之下,供已保存的配置组合和工具选择器使用。较新的 Pi 还会收到隐藏的 mcp__t3_code__ 别名。这些别名占住规范化之后的命名空间,免得和已配置的 MCP 服务器冲突,同时不增加声明,也不增加搜索结果。在 Pi 0.99 及以上版本中,orchestrator_capabilities、delegate_task 和 task_status 仍然可以直接使用;可选工具通过 Pi 内置的 tool_search 发现。在更早的 Pi 上,或者没有内置搜索时,全部工具仍然可以直接使用。桥接通过 HTTP 调用原来的 MCP 工具名。后续请求会带上 mcp-protocol-version: 2025-06-18;没有它时,Effect 的 MCP 传输会返回 400。一次会话的第一个回合(turn)还会收到共用的 T3 编排说明。
原生扩展的发现仍由 Pi 负责。T3 不会替换 Pi 的 subagent 工具,也不会复刻 Pi 的软件包与项目(project)信任加载器。持久的委派走带命名空间的 T3 MCP 工具 delegate_task,以及共用的编排子对话线程生命周期。如果安装了 Pi 的示例扩展 subagent,适配器会按文档中的 details.results 形态观察它,并投影出不带子对话线程 id 的任务卡片。认不出的结果形态,仍作为普通的动态工具输出。
提供方支持
提供方实例在其实时的 ProviderInstance 暴露出 V2 的 orchestrationAdapter 时,就可以运行子任务——当 delegated_task.request 执行时,编排器所解析的正是这同一项注册。覆盖范围包括 Codex、Claude Agent SDK、Cursor Agent SDK、Grok、经 ACP 接入的通用注册表智能体、OpenCode、OpenCode 2、Pi、Antigravity,以及今后任何会构建适配器的驱动。能力发现仍会报告其他已注册的提供方实例;解析不到适配器时,则把它们标为不能用于编排。这样,提供方选择对模型可见,一个跑不起来的请求也不会被放行。
工具面
OrchestratorToolkit 位于 toolkits/orchestrator/tools.ts 中,是编排工具的权威清单。下面各节说明委派工具和对话线程(thread)工具。t3_thread_launch 与它们写在一起,但由 toolkits/project/tools.ts 里的项目(project)工具包注册。
orchestrator_capabilities
返回:
- 继承到的提供方实例(provider instance)和模型;
- 父级的运行时模式(runtime mode)和交互模式(interaction mode);
- 已注册的提供方实例,以及对外公布的模型;
- 每个提供方(provider)能否运行子任务;
- 轮询、取消和批量创建对话线程所用的功能标志。
不可用的提供方会带上模型可见的约束,例如缺少 V2 适配器(adapter)支持、处于禁用状态、缺少可执行文件,或尚未认证。
模型列表故意保持很短:OpenCode 这类目录里可以有数百个模型,而每一次调用都会进入智能体的上下文。每个提供方会列出自定义模型、收藏模型,以及调用方自己的模型;Codex、Claude、Muse 和 Grok 还会列出全部当前(非旧版)模型,其他提供方则按用户的模型顺序,列出前 8 个当前模型。omittedModels 统计其余的数量,search 这个输入可以把它们找出来,旧版模型也包括在内。delegate_task 仍然接受目录中的任意模型。
delegate_task
创建一条 T3 自有的子对话线程,并立刻派发所给的任务提示词。
type DelegateTaskInput = {
task: string;
target?: {
providerInstanceId?: string;
driverKind?: string;
model?: string;
};
title?: string;
role?: "implementation" | "research" | "review" | "design" | "test" | "general";
mode?: "async" | "wait";
timeoutMs?: number;
clientRequestId?: string;
runtimeMode?: "inherit" | "approval-required" | "auto-accept-edits" | "full-access";
interactionMode?: "inherit" | "plan" | "default";
};省略时,提供方、模型、运行时模式和交互模式从父级继承。若目标只指定了驱动(driver),则在父级提供方实例能够运行子任务时继承该实例,否则选择该驱动的一个可用实例;显式给出的 providerInstanceId 会严格照办,不可用时则失败。选了另一个提供方却没有指定模型时,使用该提供方第一个对外公布的模型。
提供方会把自己不提供的运行时模式按 approval-required 来运行,于是子级会对每一条命令(command)请求审批。orchestrator_capabilities 只在提供方提供其中一部分模式时列出 runtimeModes;提供方若提供全部模式,则省略这一项。目标若列出 runtimeModes,不在该列表中的显式 runtimeMode 会被拒绝,继承来的模式会下调到目标所提供的最宽模式。
每一轮独立的委派审阅都使用一次新的 delegate_task 调用,并带上原始简报、先前的发现、回应,以及尚未解决的异议。每一轮用它自己的 taskId 来跟踪,并且每一轮使用不同的 clientRequestId;这个值在该轮的各次重试之间保持不变。需要同一条子级对话的后续消息,要在它的 childThreadId 上调用 t3_thread_send。原始任务完成之后,这条后续消息会作为子对话线程里的一项新任务运行;它不会重新打开已完成的任务,也不会改掉已经发布的结果。
委派要求 MCP 凭据所属的提供方会话(session)正拥有一次活动的父级运行。该请求会成为 V2 命令 delegated_task.request。
mode: "async" 立刻返回当前的持久状态。mode: "wait" 会等待任务结果,包括嵌套的工作以及完成时的后续消息,或者一直等到超时。等待超时不会取消子级;结果里会设置 waitTimedOut: true,调用方可以再用 task_status 继续。待处理的阻塞式提问也会结束这次等待,并带上 hasPendingQuestion: true 和 waitTimedOut: false。如果是 hasPendingQuestion,就把 threadId=childThreadId 传给 t3_pending_request,动作使用 list、read(附带 requestId)或 respond,或者去询问用户;该任务完成时仍会唤醒这条对话线程。针对该任务自己尚未完成的工作、仍在等待的实时审批,也会结束这次等待。它的身份出现在 pendingApproval 里;这并不授予审批权限。作出回应之前,先到子对话线程里读取最新的完整请求,因为这条持久记录可能比它的提供方回调存在得更久。异步提问不会结束这次等待。
type DelegateTaskResult = {
taskId: string;
childThreadId: string;
childRunId: string | null;
childNodeId: string;
status: "queued" | "running" | "waiting" | "completed" | "failed" | "cancelled" | "interrupted";
workState: "working" | "waiting_for_children" | "result_available";
hasPendingChildRuns: boolean;
providerInstanceId: string;
model: string | null;
summary: string | null;
resultContextTransferId: string | null;
latestTerminalRunId: string | null;
latestTerminalStatus: "completed" | "failed" | "cancelled" | "interrupted" | null;
latestTerminalSummary: string | null;
latestTerminalResultContextTransferId: string | null;
hasPendingQuestion: boolean;
pendingApproval: {
requestId: string;
kind: "command" | "file-read" | "file-change" | "mcp-elicitation" | "permission";
runId: string;
} | null;
waitTimedOut: boolean;
};task_status
从父对话线程的持久投影(projection)里读取一项委派任务。属于另一条父对话线程的任务 ID 会被拒绝。childRunId 标识最初的那次运行。workState 用来区分进行中的工作、已经结束但仍在等待子级的回合(turn),以及已经可以读取的结果。在已知的工作完成之前,任务一直处于非终态。在那之后,已经发布的 summary 和结果转移,在后来的后续消息之间保持不变。hasPendingChildRuns 报告后来排队或正在执行的回合;latestTerminal* 则暴露后来已经执行、且不属于监视的结果,同时不替换已经发布的任务结果。
task_cancel
先停掉这个子级委派出去的每一项任务,再用内部命令 thread.stop 停掉子对话线程,并结束自动向父级的投递。和用户的 Stop 一样,thread.stop 会中断正在运行的回合,挂起已排队的回合,并结束拉取请求(pull request)监视。一项非终态任务如果没有可中断的运行,就会被拒绝,除非它已经结束的回合仍在等待后台工作或委派任务。被取消的任务一旦回合结束,完成的结果会落定为 interrupted,失败的结果则保持 failed。已经处于终态的任务会返回它现有的状态,但它的子对话线程仍会停止,包括后来的运行和监视唤醒。已经发布的任务结果仍然可以读取。它还可以带一个可选的取消原因。
create_threads
创建一到二十条普通的顶层 T3 对话线程:
type CreateThreadsInput = {
threads: Array<{
prompt?: string;
title?: string;
target?: {
providerInstanceId?: string;
driverKind?: string;
model?: string;
};
runtimeMode?: "inherit" | "approval-required" | "auto-accept-edits" | "full-access";
interactionMode?: "inherit" | "plan" | "default";
}>;
clientRequestId?: string;
};每个条目各自解析提供方、模型和模式。新的对话线程继承父级的项目、分支和工作树(worktree)路径,但它们没有子智能体谱系。带提示词的条目会立刻派发一次运行;没有提示词的条目保持空闲。
t3_thread_launch
通过应用的启动服务,启动一条普通的顶层对话线程。用显式的 workspaceStrategy 来新建工作树(worktree 配合 baseRef)、挂上已有的检出(existing_worktree 配合 worktreePath),或使用项目根目录(root,这也是默认值)。智能体启动之前,这条对话线程就绑定到该工作区。在任务提示词里创建工作树,不会改掉这个绑定。
任务写在 message 里。项目、模型和模式在省略时继承;工作区不继承。scratch: true 不带项目启动,使用该环境(environment)下 Scratch 项目中它自己的一个文件夹。对于堆叠的 PR,把父分支当作 baseRef,并配合 startFromOrigin: false。新对话线程的运行时模式和交互模式都不得宽于调用方。对于 delegate_task,提供方不提供的运行时模式会被拒绝;继承来的或默认的模式,会下调到该提供方所提供的最宽模式。启动没有重试键,因此响应失败或丢失之后,再次启动之前要先查看已有的对话线程。共用同一个检出时,仍用 create_threads 做批量创建。
t3_thread_list
按从新到旧,列出一个项目里的持久对话线程外壳:给出了 projectId 就用它,否则用调用方对话线程所在的项目。调用方可以按标题、运行状态,以及是否包含应用自有的子智能体对话线程来过滤。结果有数量上限,并按偏移量分页。已删除的对话线程不会出现在列表里。
t3_thread_read
按对话线程 ID,读取该环境中任意对话线程的持久状态、最近的运行和可见时间线。已删除的对话线程返回 thread_not_found。默认的 messages 视图返回用户消息、助手消息和提出的计划。activity 视图还会返回经过摘要的工具、推理、检查点(checkpoint)、交接和运行时请求条目。过长的条目文本有长度上限,并会标明是否被截断。afterPosition 和 nextPosition 用来支持增量读取。
对话线程和消息的结果都带有必需的 createdBy 与 creationSource 来源。由 MCP 创建的对话线程和用户角色消息使用 createdBy: "agent" 与 creationSource: "mcp";提供方的输出使用 creationSource: "provider"。行动者和入口是分开的,这样智能体写成用户角色的消息,仍然可以和人写成的消息区分开。
智能体提及另一条对话线程时,使用结果里携带的 link,也就是 <t3-thread://v2/<environmentId>/<threadId>>。这样它们就不必自己排版一个 id,也不必写标题;用户给对话线程改名之后,标题就会过时。链接由拥有该对话线程的环境生成,因此被转发的结果,以及委派到某个已链接环境的任务,都会写上那个环境的名称。客户端(client)只会打开那个环境里的对话线程,并显示它当前的标题;指向客户端不认识的对话线程的链接,会渲染成一个不会打开任何内容的标签。较旧的 t3-thread://v1/<threadId> 链接不写环境名:客户端先在这条消息所在的环境里解析,再按另一个已启用环境中的唯一匹配来解析。外部 OAuth 客户端并不是已链接的环境,无法渲染 t3-thread:// 链接,因此对于这个环境中的对话线程,它得到的是「复制对话线程」会复制的那条链接,即 https://app.t3.codes/threads/<environmentId>/<threadId>,位于服务器的 T3CODE_HOSTED_APP_URL 上。托管应用可以给出桌面应用、移动应用,或它自己。
列表和读取的结果会给出 snoozed 和 snoozedUntil,t3_thread_list 则按 snoozed 过滤。服务器上的 isSnoozed 跟随客户端的 effectiveSnoozed,所以智能体和侧边栏看到的一致:一条已延后(snooze)的对话线程,只有在出现待处理请求,或延后之后发生失败时,才会提前唤醒。已经完成的运行、后台唤醒和委派完成,都会让它继续保持延后。
t3_thread_update
更新调用方这条对话线程的元数据,也可以更新该环境中的任意其他对话线程。带类型的动作有 rename、regenerate_title、link_pull_request 和 unlink_pull_request。用于关联的输入会给出仓库、编号和 URL;服务器会记下目标对话线程的项目 ID。分支和工作区的改动不由这个工具处理。
结果里有命令 ID 和持久事件(event)序列,以及得到的标题、标题重新生成标记和已关联的拉取请求。对同一个动作、同一条对话线程再次使用 clientRequestId,会重放同一份命令回执(command receipt)。对话线程的列表和读取结果会带上已关联的拉取请求,对话线程详情里还会带上正在进行的标题重新生成。
t3_thread_send
向该环境中任意普通对话线程或委派对话线程发送一条消息:
auto会启动一条空闲的对话线程,引导一个完全活动的回合,或排在一个还不能引导的回合后面;queue在活动工作结束之后,另开一次后续运行;steer要求当前有一个可以引导的活动提供方回合;restart要求当前有一个活动的提供方回合,并走编排器(orchestrator)的中断后重启路径。如果某个提供方能停止回合、也能把消息排队,但不能用新的输入重启回合,它就会停掉这个回合,并把这条消息排在其他已排队消息的前面;停止之后仍然还在、且未被挂起的委派任务结果,仍会先运行。两条路径都不适用时会被拒绝:提供方不能停止回合,Stop 已经到达这个回合,或者这个回合或这条新消息是/compact或/logout。
目标的运行时模式和交互模式都不得宽于调用方。稳定的命令 ID 和消息 ID 从 clientRequestId 派生,以便幂等重试。
t3_thread_wait
等待所选的一次运行变为 completed、failed、cancelled、interrupted 或 rolled_back。没有 runId 时,它在调用的那一刻固定当时最新的一次运行;对话线程如果空闲,会立刻返回。超时只报告最新的持久状态,不会取消工作。
t3_thread_interrupt
通过常规的 V2 命令 run.interrupt,中断所选的一次活动运行。没有 runId 时,它选择最新的一次可中断运行。已经终态的运行会原样返回;一条没有活动提供方回合的对话线程会返回 no_active_run。
计划任务
schedule_task、list_scheduled_tasks 和 delete_scheduled_task 在应用的调度器里管理重复执行的工作;schedule_task 带上 scheduledTaskId 时,用来更新已有任务。一项计划可以按间隔运行它的提示词,可以在服务器本地时区的某个固定时刻运行,也可以在每一个符合条件的 Webhook 请求上运行,即使当时没有任何活动回合。如果没有签名,任何拿到这个 Webhook URL 的人都能触发一次运行。签名校验、过滤和去重可以跳过某一次 Webhook 请求。只有当调用方在该任务的项目里拥有对话线程时,bindToCurrentThread 才默认是 true;否则默认是 false。已绑定的运行会投递到那条对话线程里;未绑定的运行则各自在该任务的项目中启动一条新的对话线程。如果显式指定了 bindToCurrentThread: true,但调用方在该项目里没有对话线程,请求会被拒绝。暂停时使用 schedule_task,并带上 scheduledTaskId 和 enabled: false;删除是永久性的。
委派任务的生命周期
MCP 服务器是进入 V2 的命令(command)入口。它不会直接调用提供方(provider)适配器(adapter)。
provider model
-> MCP tools/call delegate_task
-> authenticated OrchestratorMcpService
-> shared ThreadManagementService
-> V2 delegated_task.request command
-> child thread + child run
-> parent app_owned subagent projection
-> parent/child execution nodes
-> consumed subagent_spawn context transfer
-> normal provider effect and runtime ingestion
-> child run reaches a terminal state
-> parent subagent/node/turn item finalized
-> consumed subagent_result context transfer
-> wait result or later task_status result子对话线程(thread)的谱系关系是 subagent,并指回父节点。父级会得到一份 app_owned 子智能体投影(projection),以及一个子智能体回合(turn)条目,现有的调试界面就能据此画出进度。
终态的提供方事件(event)会触发收尾。事件流先重放已经持久化的事件,再跟随实时事件,所以服务器重启之后,收尾仍会执行。如果已经有 subagent_result 转移,收尾就是幂等的。
用户很少去看子对话线程,因此子级的提问本来会无人回答。子级打开一个待处理的 user_input 请求时,父级会收到一条通知消息,让它用待处理请求的工具来回答,或者去问用户。这条消息会说明子级是被堵住了,还是在 Codex 的异步提问里仍在继续工作。如果父级的回合正在运行,而且它的提供方可以在不打断工具的情况下引导,这条通知就会引导进那个回合,就像后台任务完成时那样。如果通知改为排队,它就会堵在一个可能永远不会结束的、长时间运行的编排器(orchestrator)回合后面。否则,它就进入队列。如果在排队的通知启动回合之前,这个提问已经被回答或关闭,那个排队的回合会被取消。审批不会被转交过去,因为那些工具回答不了审批。父级如果还堵在 delegate_task 的等待里,则会改由那次等待收到这个阻塞式提问。如果提供方提前结束了那次等待,一个仍未处理的实时提问会在启动这次等待的那次运行完成、失败,或转去等待回合之后的工作时,唤醒父级。停止或中断父级,就不会再补上这一次。
失败的运行会在任何进度文本之前,先给出提供方的错误。成功的结果使用最后一个工作回合里最新的助手内容。
策略与幂等
- 子级的运行时模式(runtime mode)可以与父级相同,也可以更窄。它不能提升权限。
- 子级的交互模式(interaction mode)可以保持不变,也可以从
default收窄到plan。它不能从plan提升到default。 - 对话线程(thread)工具可以把该环境(environment)中的任意对话线程当作目标。对一个对话线程调用方来说,列表和搜索只覆盖一个项目(project):默认是它自己的项目,除非给出了
projectId。 - 会改动另一条对话线程的工具,需要调用方对话线程有一次正在进行的运行,而且目标的运行时模式和交互模式都不得宽于调用方。这和创建子级时的权限上限相同。
- 提供方实例(provider instance)必须已启用、已安装、可用、已认证,并且背后有一个 V2 适配器(adapter)。
- 如果所选提供方(provider)公布了模型列表,请求的模型就必须是它对外公布的模型。
clientRequestId在该提供方会话(session)内派生出稳定的命令(command)、对话线程和消息 ID。用同样的调用重试,会得到同一份持久工作。- 没有
clientRequestId的调用会得到一个生成的请求键,并创建新的工作。
预期中的拒绝使用带类型的 OrchestratorMcpFailure 结果:
capability_denied
parent_not_active
provider_unavailable
model_unavailable
runtime_mode_escalation_denied
interaction_mode_escalation_denied
task_not_found
task_not_cancellable
thread_not_found
run_not_found
thread_not_sendable
thread_not_interruptible
invalid_request
orchestration_error代码归属
- 共用 schema:
packages/contracts/src/orchestratorMcp.ts和packages/contracts/src/threadMetadataMcp.ts - MCP 服务:
apps/server/src/mcp/OrchestratorMcpService.ts,以及专门的apps/server/src/mcp/ThreadMetadataMcpService.ts - 工具定义与处理程序:
apps/server/src/mcp/toolkits/orchestrator/ - HTTP 注册与认证:
apps/server/src/mcp/McpHttpServer.ts - 凭据生命周期:
apps/server/src/mcp/McpSessionRegistry.ts - 提供方(provider)注入:
apps/server/src/orchestration-v2/ProviderSessionManager.ts和各个 V2 适配器(adapter) - 持久的委派任务命令(command)与收尾:
apps/server/src/orchestration-v2/Orchestrator.ts
验证
集成测试使用真实的 MCP 工具包注册、V2 编排器(orchestrator)、SQL 持久化、事件(event)摄取、投影(projection)和检查点(checkpoint)。只有外部的提供方(provider)适配器(adapter)换成了确定性的测试实现。
覆盖范围包括:
- 能力发现;
- 跨提供方的委派完成;
- 子级上下文里只有提示词;
- 父级与子级的谱系投影;
- 启动时和结果时的上下文转移;
- 异步状态轮询;
- 取消;
- 批量创建普通对话线程(thread);
- 对话线程列表和时间线读取,包括另一个项目(project)的对话线程;
- 普通对话线程的发送、等待、引导和中断;
- 继承,以及按对话线程覆盖提供方;
- 幂等重试。
提供方适配器的测试另行验证 Codex、Claude、Cursor、Grok 和 ACP Registry 的行为,以及 MCP 注入。提供方会话(session)管理器的测试验证:适配器打开之前凭据已经存在,适配器关闭时凭据会被撤销。