git:20260821.c0c683c to git:20260824.c879529

123 added, 293 removed. Audit C to C.

---
title: Agent 平台编排
id: agent-orchestration
type: skill
tags: [agent, 外部Agent, 并行, 编排]
---
# Agent 平台编排
- > Leader 与外部 Agent 平台协作的通用指南。登记、区分、选协议、定策略。
+ > 外部 Agent(Claude Code / OpenCode / Hermes 等独立平台)协作指南。系统提示词已覆盖的(工具描述/annotation/Constitution)不重复,本技能只写外部 Agent 特有流程。
---
- ## 零、外部 Agent 登记(team.toml)
-
- 不做事前全量扫描。采用**渐进登记**:每使用一个外部 Agent,就在 `plugin/team.toml` 追加一段,逐渐积累出大王电脑上有哪些外部 Agent。
+ ## 1. 登记(team.toml)
- ### 段格式
+ 渐进登记:每用一个外部 Agent,在 `plugin/team.toml` 追加一段。只记稳定事实(type/open/process),禁止记 PID/窗口句柄/坐标(坐标走 ui-maps)。
```toml
- [claude-code]
- type = "terminal" # terminal | web-ui | desktop — 决定交互协议(§2)
- open = "终端执行 claude" # 打开方式:命令 / exe 路径 / URL
- process = "claude.exe" # 进程名特征,process_list 据此识别是否在跑
- note = "可选,一句话备注"
+ [opencode]
+ type = "terminal" # terminal | web-ui | desktop — 决定交互协议(§2)
+ open = "终端执行 opencode"
+ process = "opencode.exe"
```
- **只记稳定事实**:打开方式、窗口类型、进程名。**禁止记** PID / 窗口句柄 / 屏幕坐标等易变值(坐标参数走 `plugin/ui-maps/`,见 §6)。
-
- ### 调用决策(轻量,不过度投入认知资源)
-
- 1. Read `plugin/team.toml` — 有哪些已登记的 Agent
- 2. `process_list` 匹配各段 `process` — 哪些**现在活着**(活着优先,省去启动与窗口定位成本)
- 3. 能力匹配靠 Leader 自身对各平台的内部知识(如 Claude Code 长于代码任务),**不维护历史表现评分**——单次表现不固化成选择规则
- 4. 遇到未登记的新 Agent → 用完后补登记一段
+ 调用决策:Read team.toml → process_list 看谁活着(活着优先)→ 按平台能力匹配。不维护历史评分。
---
- ## 零.五、工具选择优先级
+ ## 2. 交互协议
- 任何任务,按以下优先级选择工具路径。上层可行则不降级:
+ ### 窗口定位(terminal 类,不可跳过)
```
- 1. 后台优先 — API / 系统调用 / 进程通信
- → system_shell、文件读写、process_list
- 2. 读数据优先 — 能读到的不截图
- → Read、Grep、FilesInfo、web_extract
- 3. 窗口级优先 — 必须视觉时限定窗口范围
- → desktop_window_screenshot + desktop_vision
- 4. 前台键鼠 — 最后手段
- → desktop_mouse、desktop_input
- 仅用于:浏览器交互(规避检测)、前三层都无法绕过的场景
+ 1. process_list 找 Agent 进程(按 team.toml process 字段)→ 记 PID
+ 2. 查父进程(Get-CimInstance Win32_Process)→ 父进程 MainWindowHandle → windows_list 定位
+ 3. 截图 + OCR 确认是 Agent(提示符/任务输出)——运行时标题常被覆写(如 OpenCode 显示「OC | 任务名」),不能靠标题
```
- 判断标准:每步问自己「这一步能不能用更上层的方式完成?」能 → 上移一层。
-
- ---
-
- ## 一、内外之分
+ 有 `plugin/ui-maps/{应用名}.json` 缓存 → 按缓存的 locate/default_pos 直接定位,校验窗口状态后使用(见 §6)。
- | | 内部 Agent | 外部 Agent 平台 |
- |---|---|---|
- | **是什么** | `task_dispatch` 创建的 ExecAgent 子任务 | 独立安装的第三方工具 |
- | **怎么调用** | `task_dispatch` 工具 | desktop_input / browser_* / 视觉链路 |
- | **验证能力** | 由 Leader 读取产出验证 |
+ ### 发送
- **禁止**:对外部 Agent 使用 `task_dispatch`。
+ - 短指令(≤200 字)→ desktop_input 直接输入,send 按目标:终端 `enter` / 即时通讯 `ctrl+enter` / 仅输入 `none`
+ - 长指令(>200 字)→ Write 文件,desktop_input 只发「读 {文件路径} 并执行」
- ---
+ ### 派发闭环(首次建立共识,后续直接派)
- ## 二、窗口类型 → 交互协议
+ **闭环 = 派发 → 门铃 → 验收**,每轮正式任务都走。读规则只做一次:
```
- 看到目标窗口 → 判断类型
- │
- ├─ 普通终端(非 Agent)
- │ └─ system_shell 后端执行
- │
- ├─ Agent 终端(Claude Code / Aider / OpenCode / Codex 等)
- │ ├─ 这些 Agent 运行在**独立终端窗口**,非 VS Code 集成终端
- │ │ ├─ Claude Code:claude.exe (node),窗口标题通常 "管理员: Windows PowerShell"
- │ │ ├─ Aider / OpenCode / Codex:同理,独立终端
- │ │ ⚠️ 不要被 VS Code 编辑器窗口标题误导——Agent 不在编辑器里
- │ │
- │ ├─ 定位方式(不可跳过):
- │ │ 1) process_list 找到 Agent 进程(已登记平台按 team.toml 的 process 字段匹配)→ 记下 PID
- │ │ 2) 查父进程:`Get-CimInstance Win32_Process` 查其 ParentProcessId
- │ │ → Agent 通常作为终端(powershell/cmd)的子进程运行
- │ │ 3) 查父进程 MainWindowHandle → 匹配 windows_list 定位窗口
- │ │ ⚠️ 运行时终端标题常被 Agent 覆写为任务名,不能靠标题匹配
- │ │ 4) ⚠️ 截图 + OCR 确认窗口内容确实是 Agent(提示符/任务输出)
- │ │ ❌ 仅凭进程名+窗口标题 → 大概率发错窗口
- │ │ ✅ 父进程链 + MainWindowHandle + OCR 三重确认
- │ │
- │ └─ 确认后三步法:windows_list → window_activate → desktop_input(text, send: "enter")
- │ ├─ 短指令(≤200 字)→ 直接输入自然语言
- │ ├─ 长指令(>200 字)→ Write 文件,desktop_input 告知文件路径让 Agent 读取
- │ ├─ 启动:优先 Leader 自启(按 team.toml 的 open 字段,system_shell Start-Process 开终端 → 输入启动命令);自启失败再请用户手动打开
- │ ⚠️ 部分 Agent 运行中弹出安全确认,Leader 持续留意
- │
- ├─ Web UI Agent 平台
- │ └─ browser_navigate → browser_wait_for(输入框) → snapshot → type/click
- │
- └─ 桌面应用 Agent
- ├─ 启动后等待加载(桌面应用远慢于终端)
- │ ├─ sleep 15s → 截图 → OCR 确认有界面内容
- │ └─ 无内容 → 等 10s 重试,最多 3 轮
- ├─ 加载完成 → OCR 扫描底部 20% 寻找输入框
- │ ├─ 找到 → 计算坐标 → desktop_mouse click → desktop_input
- │ └─ 未找到 → request_user_input 框选(首次)→ 存入缓存
- └─ 缓存机制:rel = 屏幕坐标 - 窗口起始位置,窗口移动不影响
+ 首次派发(新会话 / agent 无上下文):
+ 先走读规则环——派「读 {brief} 并回报关键理解」→ agent 门铃 progress(含理解要点)
+ → Leader 验收理解一致(不符 → 返工重读)→ 通过即建立共识
+
+ 同会话后续派发:
+ 共识已建立,直接派正式任务 → agent 执行 → 门铃 done/blocked → 验收(§7.4)
```
- ### send 参数
+ 首次通过后即共识,禁止:用 OCR 盯 Read 动作(盯梢不是闭环)。
- `desktop_input` 的 `send` **默认 `"enter"`**(实现:desktop_executors.rs `unwrap_or("enter")`)。⚠️ 只输入不发送必须**显式** `send: "none"`——省略 send 会直接回车发出。按目标应用类型指定:
+ ### 启动
- - 终端/命令行 → `send: "enter"`
- - 即时通讯 → `send: "ctrl+enter"` 或 `send: "shift+enter"`
- - 仅输入不发送(先填框观察/追加内容)→ `send: "none"`
+ Leader 自启(team.toml open 字段,system_shell Start-Process 开终端 → 输入启动命令);自启失败请用户手动打开。
---
- ## 三、并行调度策略
-
- **核心洞察**:Leader 自身串行,但外部 Agent 在独立进程中运行,天生并行。Leader 发完指令即走,多个外部 Agent 可同时运转。
-
- ```
- 时间线示例:
- │ 发指令给 Agent A ──→ 发指令给 Agent B ──→ 处理内部任务 ──→ 验证 A ──→ 验证 B │
- │ │ │ │ │ │
- │ └── A 后台跑 ──────┴── B 后台跑 ──────┴── 内部任务 ──┘ │
- ```
+ ## 3. 并行调度
- **决策树**:
+ 外部 Agent 独立进程天生并行。Leader 发完即走:
```
- 发指令给外部 Agent
- │
├─ 后续有内部任务 → 先发指令 → 立即 dispatch → 内部完成后验证外部
├─ 无需确认的结果 → 发送即止
- ├─ 需确认的结果(如源码修改)
- │ ├─ 非 Nuphus 自身源码 → 走 §7 门铃制交接(brief + report + 门铃回传),禁止 sleep 空等轮询
- │ └─ Nuphus 自身源码 → 发送即止(避免运行中被打断)
- └─ 多个 Agent → 依次发完所有指令 → 先完成的先验证
+ ├─ 需确认的结果(如源码修改)→ 一律走 §7 门铃交接(含 Nuphus 自身源码:发送即止不打断,完工经门铃/自然验收点收 report)
+ └─ 多个 Agent → 依次发完 → 先完成的先验证
```
---
- ## 四、交付验证
+ ## 4. 交付验证
- 外部 Agent 无 Checker,**验证责任在 Leader**:
+ 外部 Agent 无 Checker,验证责任在 Leader(只信产物不信摘要):
| 任务类型 | 验证方式 |
|----------|----------|
| 代码修改 | Read + Diff + cargo check |
| 文档产出 | Read 检查完整性 |
| 命令执行 | 读取日志或截图 |
| 视觉操作 | screenshot + OCR |
- 标准:**产出能被下游直接消费**,不只检查"任务是否执行了"。
+ 标准:产出能被下游直接消费。
---
- ## 五、失败回退
+ ## 5. 失败回退
- 1. 终端交互失败 → 检查是否实为普通终端 → 回退 `system_shell`
+ 1. 终端交互失败 → 确认是否实为普通终端 → 回退 system_shell
2. Web UI 找不到元素 → 延长 wait_for 超时 → 检查登录态
- 3. 桌面应用 UI 元素定位失败,按优先级回退:
- - 有文字 → 扩大截图区域重试 OCR
- - 纯图标 → hover + 小范围截图 OCR 获取 tooltip
- - tooltip OCR 不清晰 → `request_user_input(icon_confirm)` 让用户确认(OCR 推断结果预填,用户只需修正)
- - 用户无法确认 → `request_user_input(region)` 框选 → `request_user_input(text)` 填入功能名 → 写入 `ui-maps/`
- 4. UI 参数缓存失效(窗口布局实质变化)→ 通过验证锚点检测 → 重新执行完整识别流程并更新参数文件
- 5. 外部 Agent 产出不合格 → 走 §7.3 返工流程(brief 升版本重发,同一会话 ≤3 轮)→ 超限报告用户
+ 3. 桌面 UI 定位失败 → 有文字扩 OCR / 纯图标 hover+tooltip OCR / 不清晰走 icon_confirm 用户确认(见 §6)
+ 4. ui-maps 缓存失效(布局实质变化)→ 重新识别并更新参数文件
+ 5. 产出不合格 → §7.4 返工(brief 升版本重发 ≤3 轮)→ 超限报告用户
---
- ## 六、UI 元素识别与首次参数定义
-
- 桌面应用和网页的按钮、图标、输入框等 UI 元素,首次交互时通过视觉手段识别并**固化为参数文件**,后续自动化直接引用,避免重复分析。
+ ## 6. UI 识别与 ui-maps
- ### 6.1 识别策略分层
+ ### 识别策略
```
- 看到 UI 元素
- │
- ├─ 有文字(按钮文本、标签、菜单)
- │ └─ 窗口级截图 → desktop_perceive 定位 → 记录坐标+文字
- │
- ├─ 纯图标(无文字,如工具栏图标、Activity Bar)
- │ ├─ 第1优先:hover + 小范围截图 OCR
- │ │ └─ desktop_mouse hover(图标坐标) → sleep 2s
- │ │ → desktop_screenshot(以鼠标为中心,上下50px/左右100~200px)
- │ │ → desktop_vision → 从 tooltip 文字推断功能
- │ │ ⚠️ 截图范围不超出窗口客户区,避免截到其他窗口
- │ │ ⚠️ 已知 tooltip 显示方位时可缩小截图范围提升 OCR 精度
- │ │
- │ ├─ 第2优先(tooltip OCR 不清晰/不确定):
- │ │ → request_user_input(icon_confirm, icon_path, default_name, ...)
- │ │ → 一次表单确认名称+快捷键+坐标,返回结构化 JSON
- │ │ → 写入 ui-maps/{应用名}.json
- │ │
- │ └─ 最后手段(用户也无法确认时):
- │ → request_user_input(region, "框选图标区域")
- │ → request_user_input(text, "填入功能名称")
- │ → 手动组装后存入参数文件
- │
- └─ 浏览器网页元素
- └─ browser_snapshot → 获取 ref ID → type/click
- └─ CDP 失败 → 降级为桌面截图+OCR
+ 有文字 → 窗口级截图 → desktop_perceive 定位
+ 纯图标 → hover + 小范围截图 OCR tooltip → 不清晰 → request_user_input(icon_confirm)
+ 最后手段 → request_user_input(region/text) 用户框选
```
- ### 6.2 截图前置条件
-
- - **窗口置顶**:目标窗口必须在最前,且不被其他窗口遮挡
- - **空间隔离**:Nuphus 自身窗口移出目标区域,防止截到自己的 UI
- - **等待充足**:hover 后至少等待 1.5~3s,桌面应用 tooltip 渲染远慢于预期
- - **区域约束**:截图坐标 = 鼠标坐标 ± 偏移,且 `[x, y]` 不低于窗口客户区左上角
-
- ### 6.3 首次参数定义
-
- 首次接入一个新桌面应用/网页时,生成其 UI 布局参数文件,包含:
-
- - **窗口识别**:标题匹配模式、进程名、窗口大小特征
- - **按钮/图标**:功能名、屏幕相对坐标(rel = 屏幕坐标 - 窗口起始位置,窗口移动不失效)
- - **快捷键**:关联的键盘快捷键
- - **验证锚点**:特征颜色/像素,用于运行时校验 UI 未变化
+ 截图前置:窗口置顶、空间隔离(Nuphus 窗口移出目标区)、hover 后等 1.5~3s。
- 参数文件格式:JSON,存放策略分两种:
+ ### ui-maps 缓存
- - **通用应用**(VS Code、Chrome、终端等,多 workflow 共享)→ `plugin/ui-maps/{应用名}.json`
- - **Workflow 专属**(某 workflow 特有的 UI 参数)→ `plugin/workflows/{workflow-name}/` 与 .md/.json 同目录
+ - 位置:`plugin/ui-maps/{应用名}.json`(通用应用)或 `plugin/workflows/{workflow}/`(workflow 专属)
+ - 内容:window 定位(process/parent/locate/title_note)+ interact 步骤 + verify_anchor
+ - 使用:有缓存 → 恢复窗口尺寸/位置 → 锚点校验通过即用;布局实质变化才重新识别
+ - 核心:**窗口状态可控则坐标可信**。窗口移动/缩放不是缓存失效理由——恢复它即可
- 后续自动化流程中,Leader 先检查 `ui-maps/` 是否有目标应用的参数文件:
+ ### 纯图标确认
- - **有缓存** → 读取窗口尺寸/位置 → 校验当前窗口状态
- - 位置/大小匹配 → 直接使用相对坐标,跳过视觉分析
- - 位置/大小不匹配 → `desktop_window_move` + `desktop_window_resize` 恢复到参数文件中记录的尺寸和位置 → 验证锚点校验通过即生效
- - 恢复失败或锚点验证失败 → UI 布局已实质变化 → 重新执行识别流程并更新参数文件
- - **无缓存** → 执行本节识别流程,**首先记录窗口的尺寸(x, y, width, height)**,再逐元素定位,生成参数文件后执行任务
+ 走 `icon_confirm`(工具内置表单,字段级一次一问),返回值写 ui-maps。
- 核心原则:**窗口状态可控则坐标可信**。窗口移动/缩放不是缓存失效的理由——恢复它即可。只有 UI 布局实质变化(如主题切换、版本升级改变布局)才需要重新识别。
+ ---
- ### 6.4 与 request_user_input 的协作
+ ## 7. 任务交接闭环(Handoff Protocol)
- `request_user_input` 作为人类视觉锚定的接口,支持以下 `input_type`:
+ > 外部 Agent 无质检无回调,Leader 不能当看门人。解法 = **门铃制**:brief 定义标准,report 承载结果,HTTP 门铃承载完成信号。
- | 类型 | 用途 | 返回格式 |
- |------|------|---------|
- | `text` | 敏感文本(API Key/密码/验证码) | 纯文本 |
- | `screenshot` | 截取屏幕区域 | `{"path", "region":{x,y,w,h}}` |
- | `region` | 框选坐标区域 | `{"region":{x,y,w,h}}` |
- | `mouse_pos` | 鼠标点击定位 | `{"pos":{x,y}}` |
- | `color` | 取色器 | `{"color":{hex,rgb}}` |
- | **`icon_confirm`** | **图标功能确认(复合表单)** | **`{"name","shortcut?","rel_x?","rel_y?","note?"}`** |
+ **机制边界(先分清谁做什么)**:
- `icon_confirm` 专为纯图标功能确认设计,一次交互完成:
- - 显示图标预览(自动截取或 Leader 传入 `icon_path`)
- - 功能名称输入框(预填 OCR 推断结果 `default_name`)
- - 快捷键输入框(可选,预填 `default_shortcut`)
- - 相对坐标显示(可选,预填 `rel_x`/`rel_y`)
- - 备注输入框(可选)
- - 支持「重新截取」按钮更新图标预览
+ - **状态 = 机制自动**:agent 门铃 POST(progress/done/blocked)→ 后端自动写 `status.json` → 前端状态栏自动轮询显示。**Leader 零触发、不手动改 status.json**。
+ - **Leader 只做四件事**:①派发(走 dispatch 端点)②首次派发验收理解(读规则环)③每轮收门铃后验收产物(§7.4)④归档(§7.6)。
- 调用示例:
- ```
- request_user_input(
- title="确认图标功能",
- prompt="hover 到 Activity Bar 第3个图标,tooltip 显示 Ctrl+Shift+F",
- input_type="icon_confirm",
- icon_path="C:\\temp\\icon_preview.bmp",
- default_name="搜索",
- default_shortcut="Ctrl+Shift+F",
- rel_x=24,
- rel_y=120
- )
- → 返回 {"name":"搜索","shortcut":"Ctrl+Shift+F","rel_x":24,"rel_y":120}
- ```
+ ### 7.0 Leader 派发操作要求(无上下文时照此执行)
- **原则**:既然请求用户了就要获取精准信息。`icon_confirm` 用一次表单替代多次来回确认,减少认知负担。
+ **状态机制**:agent 门铃 POST → 后端自动写 status.json → 前端状态栏自动轮询。Leader 零触发、不手动改 status.json。
- Leader 收到返回值后的处理:
+ **Leader 完整操作序列**:
```
- result = request_user_input(input_type="icon_confirm", ...)
- // result.output 为 JSON 字符串: {"name":"搜索","shortcut":"Ctrl+Shift+F","rel_x":24,"rel_y":120}
-
- parsed = JSON.parse(result.output)
- 写入 ui-maps/{应用名}.json:
- icons[index] = { name: parsed.name, shortcut: parsed.shortcut, rel_x: parsed.rel_x, rel_y: parsed.rel_y, ... }
+ 1. 调 POST http://127.0.0.1:18771/handoff/dispatch(X-Handoff-Token: {prompt 环境信息})
+ body: {"agent":"{平台}","task_id":"MMDD-序号","brief":"","project":"可选"}
+ (brief 先传空串——契约由本步返回,避免鸡生蛋;brief 允许后补是既有能力)
+ → 返回 contract:门铃 URL / 令牌 / 上报 POST 示例 / 产物路径 / report_path
+ 2. Write brief 到 contract 给出的 briefs/{task_id}-brief.md(结构见 7.3)。
+ ★「门铃契约」段 = contract 原文原样嵌入(含 X-Handoff-Token 头 + 完整可复制的
+ POST 请求示例,id 为 {agent}::{task_id} 形态)——禁止手写转述、省略字段、
+ 只写文字描述不带示例(实测事故:手写门铃契约漏掉 token/header/schema,
+ agent 端 403/422 连环试错,闭环第一步即断)
+ 3. 首次派发:终端发「读 {contract 中 brief 路径} 并回报关键理解」→ 等门铃 progress(含理解要点)
+ → 验收理解一致(不符返工重读)→ 建立共识
+ 同会话后续派发:直接发正式任务指令(契约已生效,无需重复读规则)
+ 4. 每轮等门铃注入(progress/done/blocked)→ 状态栏自动反映,不轮询
+ 5. 收 done/blocked → Read report 全文 + 交叉验证产物(§4)→ 达标归档(§7.6)/ 不达标返工(≤3 轮)
```
- ---
-
- ## 七、任务交接闭环(Handoff Protocol)
-
- > 外部协作的核心矛盾:外部 Agent 无质检、无回调能力,Leader 串行不能当看门人。
- > 解法 = **门铃制交接**:brief 定义验收标准,report 文档承载结果,HTTP 门铃承载完成信号。
-
- ### 7.0 使用定位(先决判断)
-
- **默认内部自行处理**。外发仅三类场景:
-
- 1. 任务复杂、内部串行过慢 → 拆分一部分给外部**并行**(配合 §3)
- 2. 外部平台在某领域能力更强(能力互补)
- 3. 用户明确指定
-
- 决定外发后**选平台**:按 §0 调用决策——Read `plugin/team.toml` + `process_list` 看哪些活着 + 自身内部知识定能力匹配,不查历史评分。
+ **禁止**:手写 brief 文件绕过 dispatch;手动改 status.json;每轮重复读规则;用 OCR 盯 Read 动作;手写转述门铃契约(见步骤 2 ★)。
- **环节闭环总览**(终点即下次起点):
+ ### 7.1 派发前强制 Checklist(缺一不可)
- ```
- 决策(7.0) → 选平台(team.toml + process_list + 内部知识) → 写brief(7.2) → 派发(§2:发文件路径一句话)
- → 外部执行(开工health自检 + progress签收/中途报)
- → 回传(7.1:门铃三层传输 + 失败降级 + report兜底)
- → 验收(7.3:只信产物不信摘要)
- ├─ 达标 → 归档(7.4):新平台补登记 team.toml,经验入记忆
- └─ 不达标 → brief 升版本重发 ≤3 轮 → 超限报告用户
+ ```text
+ □ 1. 已走 POST /handoff/dispatch 派发(一次建 brief + 置 status + 取契约,见 7.0)——禁止手写 brief 文件绕过
+ □ 2. brief 门铃契约段 = dispatch 返回 contract 原文(含 X-Handoff-Token 头 + 可直接复制执行的 POST 示例),禁止手写转述/凭记忆写令牌
+ □ 3. 已要求接收方写 report 到契约给出的 report_path(固定四段)
+ □ 4. 已要求门铃契约:开工前 health 自检 → 读规则环 progress(含理解要点)→ 正式任务环 progress → 完工 done(含 report_path)
+ □ 5. 下发:长指令走文件(契约返回后终端只发「读 {brief 路径} 并执行」)
+ □ 6. 执行中不轮询不打断(禁止 sleep 空等;状态自动流转,看门铃注入即可)
+ □ 7. 首次派发先读规则环(验收理解建立共识);同会话后续直接派正式任务,见 §2 派发闭环
```
- ### 7.1 通信架构(三层传输 + 一条兜底)
+ ### 7.2 回传(门铃)——实证契约
```
- 派发(软通道,现状方式) 回传(硬信号,门铃)
- ──────────────────────────── ─────────────────────────────────
- Leader ─desktop_input/browser──→ 外部 Agent(交互会话,保有上下文)
- ↓ 完工/进度
- 终端类 Agent → curl 直调门铃(自服务)
- 本地 Web UI Agent → 页面 fetch 直调(localhost 同源不拦,如 OpenClaw)
- 公网 Web/桌面类 → 【中继】brief 要求其回复末尾输出结构化完成标记
- ```handoff {"id":"..","status":"done","summary":".."}```
- Leader 用 browser_evaluate/DOM 提取后代表其中转
- ⚠️ 公网页面无法直调 localhost(Chrome PNA 拦截,已实测)
- 兜底:门铃未响 → 自然验收点查 report 文件 / 终端 OCR(见 7.3)
+ POST http://127.0.0.1:{port}/handoff # 默认 18771,实际以 prompt 环境信息/contract 为准
+ Header: X-Handoff-Token: {token} # prompt 环境信息,每次运行随机,重启轮换;缺失或错误 → 403
+ Body: {"id":"{agent}::{task_id}","status":"done|progress|blocked","summary":"...","report_path":"..."}
+ GET /handoff/health → 免令牌自检
```
- **门铃端点**(运行时内嵌,仅绑 127.0.0.1,地址+令牌见 prompt 环境信息段):
+ **id 格式(关键,旧文档「MMDD-序号」为误)**:必须 `{agent}::{task_id}`(如 `opencode::0824-02`)。门铃按 `::` 前缀归组更新 status.json——不带前缀的事件不报错但**静默不归组**(状态栏永远不动)。
- ```
- POST http://127.0.0.1:{port}/handoff
- Header: X-Handoff-Token: {token} # 每次运行随机生成,从 prompt 环境信息获取
- Body: {"id":"0728-01","status":"done","summary":"一句话结果","report_path":".nuphus/handoff/0728-01-report.md"}
+ **错误码自诊断**(agent 与 Leader 排查共用):
- status ∈ progress | done | blocked
- GET /handoff/health → 免令牌自检连通
- ```
+ | 响应 | 含义 | 动作 |
+ |---|---|---|
+ | 200 | 已受理 | —(同 id 终态重复 POST 幂等忽略,仍回 200) |
+ | 403 | 缺/错 X-Handoff-Token 头(响应体无任何提示) | 核对 token 来源:prompt 环境信息 / contract 原文;重启后旧 token 一律失效 |
+ | 422 | JSON 缺必填字段(id/status/summary) | 补全四个字段:id/status/summary/report_path(可 null) |
+ | 400 | status 非法值 或 id/summary 为空 | status 只允许 done/progress/blocked |
- **令牌时效**:令牌每次运行随机生成,**Nuphus 重启即轮换**;门铃队列为内存态,重启清空。跨重启的在途任务处理 → 见 7.3 重启场景。
+ 语义细节:progress 同 id 折叠(只注入最新一条);done/blocked 终态幂等且到达后清除同 id 陈旧 progress;summary 换行压平、500 字截断。
- 约束不变:**执行中输入框锁定**(无法向用户澄清)+ **外部 Agent 无主动交互能力**(除门铃外无回调)→ brief 一次写全 + 内置歧义处理策略,验收全自助。
+ - 终端类:curl 直调;**PowerShell 必须发 UTF-8 字节体**(`-Body ([Text.Encoding]::UTF8.GetBytes($json))`),字符串 body 会把中文压成 `?`(实测);纯 ASCII body 才可用单引号直传
+ - 公网 Web 类:回复末尾输出 ```handoff 代码块,Leader 提取后代表中转
+ - 门铃失败降级:重试 1 次 → 仍失败记入 report ⚠️ 段 + 终端输出 handoff 标记
- ### 7.2 任务简报(brief)
+ ### 7.3 brief 结构
- 路径约定:`.nuphus/handoff/{id}-brief.md`({id} = MMDD-序号,如 `0728-01`)。
+ `.nuphus/handoff/{id}-brief.md`:任务定义(+成功标准)/ 上下文(文件:行号)/ 质量基线 / 反模式 / 歧义处理(按最合理假设推进并记入遗留,禁止停摆)/ 报告契约(四段:✅完成项 / 📄改动文件 / 🔍验证证据 / ⚠️遗留)/ 门铃契约(= dispatch 返回 contract 原文原样嵌入,见 7.0 步骤 2 ★——禁止手写转述)
- 结构 = 内部 dispatch 五段骨架 + 外发特有契约:
+ ### 7.4 验收(禁止轮询)
- 1. **任务定义** — 做什么 + 成功标准
- 2. **上下文** — 已核实事实(文件:行号)/ 约束 / 依赖
- 3. **质量基线** — 可验证的通过条件
- 4. **反模式** — 禁止清单
- 5. **歧义处理** — 遇到不确定时的默认策略(如:按最合理假设推进并记入报告遗留问题;禁止停摆等待提问)
- 6. **报告契约** — 结果写入 `.nuphus/handoff/{id}-report.md`,固定四段:✅完成项 / 📄改动文件清单 / 🔍验证证据(命令+输出摘要)/ ⚠️遗留问题
- 7. **门铃契约**(从 prompt 环境信息复制当前地址+令牌):
- - 终端类/本地 Web 类:「开工前先 `GET http://127.0.0.1:{port}/handoff/health` 自检连通;开工后报一条 `progress`("已开工")作签收;完工后执行 `curl -X POST http://127.0.0.1:{port}/handoff -H "X-Handoff-Token: {token}" -H "Content-Type: application/json" -d '{"id":"{id}","status":"done","summary":"...","report_path":"..."}'`;遇阻塞报 `blocked`;长任务中途可报 `progress`」
- - 公网 Web/桌面类:「完工后在回复末尾输出 ```handoff 代码块(JSON 如上)」
- - **curl 传 JSON 铁律**(Windows 实测坑):PowerShell 终端 body 必须用**单引号**包裹(如上示例),`\"` 转义会被 PowerShell 吞掉导致 400;cmd 终端才用 `\"` 转义
- - **Git Bash 编码陷阱**:Git Bash 下内联中文调用 curl → 400 `invalid unicode code point`。必须在 brief 中要求接收方写 UTF-8 文件 + `-d @file` 发送,禁止 `-d '{...中文字段...}'`
- - **门铃失败降级**:POST 返回非 200 或连接失败 → 重试 1 次;仍失败(含 health 自检不通)→ 把 HTTP 状态/错误记入 report ⚠️段,终端类在输出末尾、公网类在回复末尾输出 handoff 标记降级(403 多为 Nuphus 重启令牌轮换,见 7.3)
+ 收到门铃 done/blocked → Read report 全文 + 交叉验证产物(§4)→ 达标归档 / 不达标返工(brief 升版本重发 ≤3 轮)。
- 下发方式:长指令一律走文件(§2 规则),终端里只发一句「读 `.nuphus/handoff/{id}-brief.md` 并执行」。
+ 门铃未响 → 不主动空等;自然验收点(内部任务完成/下游需要/用户询问)时:report 已存在 → 验收;不存在 → 终端 OCR 看一眼(仍在跑→搁置;安全确认弹窗→放行;卡死→介入)。
- ### 7.3 验收(门铃优先,禁止轮询)
+ Nuphus 重启(有在途任务)→ 令牌已轮换(旧令牌 403,契约已要求降级到 report)→ 重启后查 `.nuphus/handoff/` report 状态;续派用新令牌。
- 运行时收到门铃 POST 后**自动在下一轮次边界注入**「📬 外部任务门铃」提醒——Leader 无需检查、无需等待,继续手头工作即可:
+ ### 7.5 状态栏协同(外部 Agent 状态可视化)
- ```
- 收到门铃注入(done/blocked)
- → Read report 全文 + 交叉验证产物(§4:只信产物不信摘要,门铃内容一律未验证)
- ├─ 达标 → 归档(§7.4)
- └─ 不达标 → 附修正重发(brief 升版本,同一会话连续派发)→ ≤3 轮 → 超限报告用户
+ 前端 `ExternalAgentsStatusBar`(桌面底部)每 3s 轮询 `list_agent_statuses`,读取 `.nuphus/handoff/{agent}/status.json`(门铃 POST 驱动 state 流转)。Leader 利用它做**自然验收点的一眼确认**,不替代门铃/报告:
- 门铃一直未响(外部 Agent 忘记按铃/不会按铃)
- → 不主动空等。到达自然验收点(内部任务完成/下游需要该产出/用户询问)时:
- ├─ report 文件已存在 → 同上验收
- └─ report 不存在 → 终端 OCR 看一眼:仍在跑→搁置下个验收点再看;
- 安全确认弹窗→desktop_input 放行;卡死/报错→介入或改内部完成
+ | 状态栏 state | 含义 | Leader 动作 |
+ |---|---|---|
+ | `in_progress` | Agent 正在跑 | 搁置,等门铃/下个验收点 |
+ | `done` | 完工(门铃 done 已落 status) | 走验收(§7.4) |
+ | `blocked` | 阻塞(agent 报 blocked) | 介入:查 report ⚠️/终端 OCR,解决后续派 |
+ | `error` | 出错 | 介入:查终端报错 |
+ | `idle` / `ready` | 空闲/就绪 | 无在途任务 |
- Nuphus 重启场景(有在途外发任务时)
- → 令牌已轮换 + 内存门铃队列已清空:外部 Agent 持旧令牌 POST 将 403(契约已要求其降级到 report + 回复标记)
- → 重启后首个自然验收点:主动 Read `.nuphus/handoff/` 查在途任务的 report 文件状态,不等门铃
- → 任务未完成需续派:brief 升版本 + 写入**新令牌**重新下发
- ```
+ 注意:状态栏**只读不写**,state 由外部 Agent 门铃 POST 驱动(`progress`/`done`/`blocked`);Leader 不要试图直接改 status.json。状态栏与门铃同源(status.json),门铃已响则状态栏必同步,二者互证。
- **禁止轮询**:Leader 串行,定时盯外部 = 自己变看门人,违背并行初衷。检查只发生在自然验收点。
+ **重启重置(设计意图)**:应用重启会把 status.json 重置为 idle/空 task_id(运行时态不跨重启)。在途任务经重启后,验收依据 = brief/report 文件(`.nuphus/handoff/`),状态栏只反映重启后的新事件;续派需重新 dispatch。
- ### 7.4 归档
+ **契约未送达的探测信号**:状态栏 `in_progress` 停留但 `last_event` 长时间为 null → agent 大概率没拿到可用契约或上报被门铃拒绝(403/422,见 7.2 错误码表)。不要干等:Read brief 检查门铃契约段是否为 contract 原文(含 token/header/示例)→ 缺失则补发正确契约并让 agent 重报;agent 在终端反复试错探测端点也是同一信号。
- 验收通过后:
+ ### 7.6 归档
- - brief + report 保留在 `.nuphus/handoff/` 备查(外部产出无 task_trace,这是唯一追溯链)
- - 关键结论与经验写入记忆(`leader_memory_update`)
- - **平台登记**(闭环终点 = 下次 7.0 起点):首次使用的平台 → 按 §0 格式补登 `plugin/team.toml`(只记稳定事实:type / open / process)。不记表现评分——单次表现不固化成选择规则
+ brief + report 保留 `.nuphus/handoff/`(外部产出无 task_trace,这是唯一追溯链);经验入记忆;新平台补登 team.toml。