☰
Warp 桌面端 Orchestration 自定义 Worker Host 选择器(Host Picker)实现解析
2026/10/5 10:28:56 网站建设 项目流程
  • 桌面应用
  • 开发者工具
  • 人工智能
  • AI 应用
  • AI Agent
  • 代码智能体

【免费下载链接】warp

Warp is an agentic development environment, born out of the terminal.

项目地址:https://gitcode.com/GitHub_Trending/wa/warp
点击查看免费下载

导读

Warp 的编排(Orchestration)能力允许用户从终端发起一次多智能体(Agent)协同任务,将子 Agent 派发到云端执行。在早期版本中,这些 Cloud Child Agent 的“执行主机”(worker host)被硬编码为默认的 Warp 集群(warp),使用自建 Worker(self-hosted worker)的团队无法从桌面客户端选择执行位置。本文基于仓库中 specs/QUALITY-701/PRODUCT.md 与配套技术设计 specs/QUALITY-701/TECH.md,完整拆解 QUALITY-701“自定义 Host 选择器”的产品行为、交互细节与源码实现,读完你将掌握该组件的两种渲染模式(列表模式 / 自定义模式)、持久化与最近使用(recency)策略、工作区默认主机的解析规则,以及它与既有编排 Picker 管线(OrchestrationPickerHandles)的协作方式。

背景:编排 UI 中缺失的“最后一公里”

编排 UI(Orchestrate 确认卡片与 Plan 卡片上的 Orchestration 区块)原本就有一行 Picker——模型(model)、harness、环境(environment)——用于驱动子 Agent 派发。真正派发时,RunAgents请求携带的worker_host字段却始终是写死的"warp",路由到默认 Warp 集群。这带来两个问题:

  1. 运行自建 Worker 的团队无法从桌面客户端指定执行主机,只能回到 Oz Web 应用(其HostSelector是当时唯一的入口);
  2. 桌面客户端与 Web 端能力不一致,缺少“选择执行位置”这一关键编排自由度。

有趣的是,worker_host字段在数据层早已贯通:它存在于OrchestrationEditState与RunAgentsExecutionMode::Remote上,缺少的只是一个让用户修改它的 UI 控件。QUALITY-701 补上的正是这一环,并刻意复用了编排 Picker 既有的一套外观管线(picker_styles()、Dropdown视图),而不是另起炉灶。

组件总览:一个 View、两种模式

QUALITY-701 的实现核心是一个非泛型视图HostPicker,位于 app/src/ai/blocklist/inline_action/host_picker.rs。它内部维护一个状态机,在两种渲染模式之间切换:

模式渲染内容触发方式代表行为
列表模式(List mode)复用Dropdown<InternalAction>,样式与其余编排 Picker 完全一致默认状态展示默认 /warp/ 最近自定义 / “Custom host…”
自定义模式(Custom mode)内联单行EditorView+ 右侧小型取消按钮点击 “Custom host…” 或外部注入未知 slug输入任意 slug,Enter / 失焦提交,Escape / 取消还原

两种模式共享同一个“当前选择”(current_slug),模式切换不丢失状态:进入自定义模式时会用slug_before_edit记录快照,取消时可精确回滚。该视图对外只暴露两个公共事件:

  • HostPickerEvent::HostChanged { slug }——任何一次选择变更(点选已知项或提交自定义值)都会发出;
  • HostPickerEvent::Closed——菜单关闭或编辑器失焦时发出,供父卡片把焦点交还给自己的输入框。

对应的公共 API 则包括set_options(default, recent, connected, ctx)(重建菜单行)、set_selected(slug, ctx)(设置显示值,未知 slug 自动切入自定义模式)、set_use_overlay_layer(...)(菜单浮层层级)与set_menu_position(...)(菜单锚点/朝向)。

列表模式:菜单项的排序、徽章与去重

列表模式的菜单内容由纯函数build_menu_items生成(位于 host_picker.rs),它接受default_host、recent_host与connected_hosts三路输入,按以下顺序产出菜单行:

  1. 工作区默认 slug(团队配置了defaultHostSlug时出现),带“Default”徽章,且固定在首位;
  2. warp(Warp 默认集群),无条件始终存在;
  3. 当前已连接的 Worker slug,带“Connected”徽章(按字母序、去重后插入);
  4. 最近使用的自定义 slug,无徽章渲染为纯文本;若该 slug 当前不在连接集合中,则额外标注“Disconnected”,使其可选中但状态明确;
  5. “Custom host…”条目,固定最后,选中后切换进入自定义模式。

去重规则(产品行为 5)有两层保障。首先,ConnectedSelfHostedWorkersModel::worker_hosts_excluding在数据来源侧就排除了warp与默认 slug(见 app/src/ai/connected_self_hosted_workers.rs);其次build_menu_items内部用known_slugs列表做大小写不敏感(eq_ignore_ascii_case)的二次去重,确保“最近自定义 == 默认”或“最近自定义 == warp”时不产生重复行。

build_host_snapshot(app/src/ai/orchestration/snapshots.rs)中的OptionBadge::Default / Connected / Recent徽章体系与上述菜单构建逻辑一一对应,而populate_host_picker(orchestration_controls.rs)正是读取该快照后把三类行分别灌入HostPicker::set_options。

自定义模式:内联编辑器的完整交互语义

点击 “Custom host…” 后,Picker 顶栏被替换为一个内联文本编辑器,其交互语义在产品文档中被拆成 5 条可测试的规则(行为 8–12),源码中分别对应:

  • 预填充:编辑器打开时以当前 slug 预填;若当前就是warp则预填为空(见enter_custom_mode_with_slug,host_picker.rs);
  • 提交:按 Enter 或编辑器失焦(EditorEvent::Blurred)触发commit_custom,提交前手动trim()去除首尾空白(handle_editor_event);
  • 空输入 = 还原:commit_custom对空 buffer 直接走cancel_custom,等价于“不做任何修改”(行为 10);
  • 大小写不敏感的 warp:输入warp/WARP并提交时,不把它持久化为自定义值,而是折叠回标准warp选中态(行为 11)——这避免了“current_slug是warp的大小写变体、与任何菜单标签都不匹配”的不对称情形;
  • 取消:Escape 或点击右侧的 ✕ 取消按钮(render_cancel_button,一个 16×16 的 X 图标Hoverable按钮)触发cancel_custom,依据slug_before_edit快照回滚到进入编辑前的选中值(行为 9)。

提交一个非空、非warp的 slug 时,还会发生两件事:该 slug 被提升进recent_host字段(!self.is_known_option(&raw)时才提升,避免与默认/已连接行重复),并在下一次打开菜单时以“最近使用”行出现;同时通过HostChanged事件交给父层持久化(行为 12,详见下一节)。

值得单独强调的两处实现细节:

  1. 垂直居中(行为 13):自定义模式的外壳用Flex::column+MainAxisAlignment::Center包裹编辑器,并在最外层套上与标准Dropdown相同的上下DROPDOWN_PADDING外边距,使编辑框文字垂直居中、且整盒与同一行其他 Picker 保持相同 y 偏移(render_custom_mode);
  2. Close 事件抑制:进入自定义模式期间,内层DropdownEvent::Close会被is_custom_mode检查吞掉。若不抑制,父卡片会在菜单关闭时抢回焦点,把刚聚焦的编辑器 blur,触发 commit-on-blur 立即退回列表模式——使 “Custom host…” 看起来像个无效操作。这是该功能在 UX 层面最容易翻车的一处防御(见 host_picker.rs 中 Dropdown 订阅逻辑)。

选择模型与工作区默认主机解析

产品文档的“Selection model”(行为 14–16)与“Persistence and recency”(行为 17–18)在源码层被提炼为三个纯函数(app/src/ai/orchestration/providers.rs):

  • resolve_default_host_slug(scope, ctx) -> Option<String>——返回团队配置的工作区默认主机 slug,优先读取开发者专用的WARP_CLOUD_MODE_DEFAULT_HOST环境变量(本地测试覆盖),否则回退到UserWorkspaces::default_host_slug(scope),并过滤空值;
  • resolve_recent_host_slug(scope, ctx) -> Option<String>——从CloudAgentSettings.last_selected_host读取用户最近一次自定义选择,但对"warp"(大小写不敏感)与当前团队默认值做双重排除,避免菜单出现重复行(行为 18);
  • persist_host_selection(worker_host, ctx)——把 slug 写回CloudAgentSettings.last_selected_host,跳过空值与warp(行为 17)。这条规则保证了“最近使用”行永远只收录真正有意义的自定义 slug。

三条产品行为在此得到印证:

  • 行为 14(永不空选):normalize_slug对输入trim()后,空串一律回退为warp;host_snapshot中current的解析同样在空worker_host时落到warp。两条路径共同保证 Picker 恒有非空选择;
  • 行为 15(未知 slug 进入自定义模式):HostPicker::set_selected先对 slug 做归一化,再调用is_known_option(对warp、默认、最近、已连接四类做大小写不敏感比对),不匹配即enter_custom_mode_with_slug,预填该 slug 而非显示一条“幽灵菜单项”;
  • 行为 16(默认预选):当工作区配置了默认主机且用户尚无显式选择(last_selected_host为空)时,HostSelector::set_default_host(app/src/terminal/view/ambient_agent/host_selector.rs)与编排侧的populate_host_picker都优先预选默认值而非warp;反之,若用户已有持久化选择,则尊重用户选择、仅把默认值作为菜单项提供。clear_default_host的注释还揭示了一个细节:窗口切换到“未配置默认主机”的团队时,同样以用户保存的选择优先,避免上一位团队的默认 Worker 被错误沿用。

双表面接入:确认卡片与 Plan 卡片

Picker 需要同时出现在两处 UI,且两处的菜单朝向策略不同(行为 20):

  • Orchestrate 确认卡片(app/src/ai/blocklist/inline_action/run_agents_card_view.rs):在ensure_pickers中为新的host_picker槽构建视图,调用picker.set_menu_position(PositionedElementAnchor::TopLeft, ChildAnchor::BottomLeft)让菜单向上翻开,与同卡片其他下拉保持一致,避免与下方的 Environment / Base model 行视觉碰撞;订阅HostChanged后重新派发既有的RunAgentsCardViewAction::WorkerHostChanged;
  • Plan 卡片(app/src/ai/document/orchestration_config_block.rs):调用picker.set_use_overlay_layer(true)让菜单绘制在**覆盖层(overlay layer)**之上、盖过同行的兄弟 Picker;WorkerHostChanged除更新编辑态与持久化外,还会调用apply_field_change把新值写进该 Plan 存储的OrchestrationConfig快照(行为 19 的“plan card 额外持久化”)。

两个WorkerHostChanged处理器的逻辑高度一致(见 run_agents_card_view.rs 与 orchestration_config_block.rs):先set_worker_host写入OrchestrationConfigState,再persist_host_selection写入最近使用设置,因此“任何以主机变更收尾的路径都会持久化 slug”。

共享 Picker 管线:OrchestrationPickerHandles的扩展

为了让两个卡片共享同一套初始化与同步逻辑,orchestration_controls.rs对既有管线做了四点增量改造:

  1. OrchestrationPickerHandles新增host_picker: Option<ViewHandle<HostPicker>>槽位;
  2. 新函数populate_host_picker(picker, initial_host, ctx)——读取host_snapshot(含默认/最近/已连接三类行),调用picker.set_options后按初始 host 调用picker.set_selected,空输入自动回退warp;两个卡片在ensure_pickers期间共用它完成播种;
  3. sync_picker_selections被扩展为在编辑态每次变化时调用picker.set_selected同步当前worker_host——这同时覆盖了初始填充与“其他 Picker 引发的级联变化”(例如模式切换把 host 重置回warp);
  4. 恢复 Remote 配置且 host 为空时,两个卡片都优先预填工作区默认值而非裸warp,与 Oz Web 应用保持一致。

由于worker_host字段在OrchestrationEditState与RunAgentsExecutionMode::Remote上本就存在,本次改造无需触碰派发(dispatch)、服务端序列化(server marshalling)与 auto-launch 匹配逻辑——之前写死的"warp"与用户选择的任意 slug 走的是同一条代码路径(见 TECH.md 第 5 节“No other call sites”)。

测试与验证策略

纯逻辑与视图行为采用了不同的验证方式:

单元测试(host_picker_tests.rs)直接测试无视图上下文的纯函数,覆盖产品行为 4/5/6/8/11/14/15/16/18,包括:

  • 无默认、无最近时菜单仅warp+ “Custom host…”两项;
  • 默认存在时置顶且带徽章;最近存在时排在warp之后、渲染为纯文本;
  • 最近 == 默认、最近 ==warp时的去重;
  • warp行派发SelectKnown("warp")、自定义行派发EnterCustomMode;
  • menu_label_for对默认 slug 打 “Default” 徽章、对未知值返回纯文本;
  • normalize_slug的 trim 与空输入回退warp。

菜单排序与去重的纯逻辑在快照层还有独立测试佐证:host_snapshot_orders_default_warp_connected_recent与host_snapshot_dedupes_connected_and_recent_against_known_rows(app/src/ai/orchestration/snapshots_tests.rs)。

手动冒烟测试覆盖视图驱动行为(TECH.md 附有逐条清单):自定义模式提交/失焦/焦点归还、与兄弟 Picker 的图层交互、端到端验证等。其中最关键的一条是行为 19 的端到端验证:选择非warpslug 后派发 Agent,Worker 日志中出现task_claimed worker_id:"local-dev"即证明自定义 slug 正确路由到了自建 Worker。

小结

QUALITY-701 是一个典型的“补齐数据链路最后一环”的 UI 功能:数据层(worker_host)早已贯通,工作量集中在一个双模式视图与两个卡片的薄接线(thin wiring)上。其工程价值在于三点:产品行为被精确拆解为可单元测试的纯函数;外观与交互复用既有 Picker 管线(同一套picker_styles()常量、Dropdown与 overlay 机制)而零散改;去重、默认预选、recency 与“warp 永不持久化”等边界规则被浓缩为三个纯函数,行为可预测且易测。对于希望在自建 Worker 环境下使用 Warp 编排能力的团队,这一功能补全了“在哪里执行”这一关键选择权;对于开发者而言,host_picker.rs 与其配套测试则是一份紧凑的“模式化 Picker 组件”参考实现。

  • 桌面应用
  • 开发者工具
  • 人工智能
  • AI 应用
  • AI Agent
  • 代码智能体

【免费下载链接】warp

Warp is an agentic development environment, born out of the terminal.

项目地址:https://gitcode.com/GitHub_Trending/wa/warp
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询