内容
# 代理任务交接
**让一个 AI 代理将工作交给另一个 — 从你的编辑器中,带有完整的任务跟踪。**
你的 AI 编码助手一次只能做一件事。agent-handoff 改变了这一点:一旦安装完成,你的 AI 可以启动 Claude、Codex、Gemini 或其他任何代理作为后台工作线程,将任务交给它,然后带着结果回来。它通过 MCP 工作,因此任何与 MCP 兼容的编辑器都可以使用它。
---
## 安装
```bash
npm install -g @daax-dev/agent-handoff
```
> **Bun 是必需的**,用于 MCP 服务器和 REST API — 它们使用 Bun 原生 API。
> 安装 Bun:`curl -fsSL https://bun.sh/install | bash`
这给了你三个命令:
| 命令 | 功能 |
|---------|-------------|
| `agent-handoff-mcp` | MCP 服务器 — 你的编辑器连接到的 |
| `agent-handoff-server` | REST API 后端(CLI 需要) |
| `agent-handoff` | 用于从终端创建和跟踪任务的 CLI |
---
## 连接到你的编辑器
### Claude Code
```bash
claude mcp add agent-handoff -- agent-handoff-mcp
```
重启 Claude Code,然后运行 `list_agents` 以确认它已连接并查看哪些代理在你的机器上可用。
### Cursor, Windsurf, Claude Desktop, Zed
将以下内容添加到你的 MCP 配置文件中:
```json
{
"mcpServers": {
"agent-handoff": {
"command": "agent-handoff-mcp"
}
}
}
```
每个客户端的配置文件位置:[docs/installation-guide.md](docs/installation-guide.md)
---
## 交接你的第一个任务
连接后,你的 AI 编辑器可以使用这些 MCP 工具:
**1. 查看机器上可用的代理**
```
list_agents
```
```
✓ claude 可用
✗ codex 未找到
✓ gemini 可用
```
**2. 交接一个任务**
```
handoff_task({
agent: "claude",
prompt: "为 src/routes/signup.ts 添加输入验证",
workingDirectory: "/path/to/your/project"
})
```
```
hnd_a1b2c3d4e5f6 已派发
```
**3. 检查任务状态**
```
check_status({ jobId: "hnd_a1b2c3d4e5f6" })
```
```
hnd_a1b2c3d4e5f6 claude ● 运行中 12.4s
```
**4. 获取结果**
```
get_result({ jobId: "hnd_a1b2c3d4e5f6" })
```
```
hnd_a1b2c3d4e5f6 claude ✓ 已完成 退出代码 0
──────────────────────────────────────────────
1 个文件已更改
src/routes/signup.ts +34 −2
```
**同时运行多个任务**
```
任务 代理 状态 时间 任务
─────────────── ──────── ────────────── ────── ──────────────────────────
hnd_a1b2c3d4 claude ● 运行中 12.4s 添加输入验证
hnd_e5f6g7h8 gemini ● 运行中 3.1s 总结 Q4 报告
hnd_i9j0k1l2 codex ✓ 已完成 8.2s 优化数据库查询
hnd_m3n4o5p6 claude ✗ 失败 5.0s 重构身份验证层
```
> 原始 JSON 响应形状:[附录](#附录--响应形状)
---
## 三种委托方式
```
本地启动(默认) A2A 远程 工作线程池
─────────────────────── ───────────────────────── ────────────────────
你的 AI 你的 AI 你的 AI
│ handoff_task │ handoff_task │ handoff_task
▼ │ agentUrl=… │ pool: true
agent-handoff ▼ ▼
│ fork/exec agent-handoff FIFO 队列
▼ │ message/send ┌────┬────┬────┐
claude -p "…" ▼ tasks/get w-1 w-2 w-3
codex exec "…" 远程代理 │ │
gemini -p "…" │ └─►pull_task
│ 标准输出 + 差异 ▼ │ 执行
▼ 结果 ▼
结果 提交结果
```
### 启动本地代理
默认模式。agent-handoff 以子进程的形式启动代理,并捕获其输出、退出代码和 git 差异。
```
handoff_task({
agent: "claude", // claude | codex | gemini | copilot | opencode
prompt: "将 src/db/queries.ts 重构为使用参数化查询",
workingDirectory: "/path/to/project",
model: "opus" // 可选
})
```
添加 `spawnMode: "tmux"` 以打开一个可见的 tmux 窗口(`daax-<agent>`),以便你可以实时查看代理的工作。
### 委托给远程 A2A 代理
通过 URL 注册任何符合 [A2A](https://google.github.io/A2A/) 标准的代理,然后将任务委托给它:
```
register_agent({ url: "https://research-agent.example.com", authToken: "tok_123" })
handoff_task({
agentUrl: "https://research-agent.example.com",
prompt: "总结 Q4 云计算定价趋势"
})
```
agent-handoff 发送 `message/send`,轮询 `tasks/get`,并在任务达到终端状态时返回。
> 跨机器设置和身份验证:[docs/cross-machine-and-auth.md](docs/cross-machine-and-auth.md)
### 使用工作线程池
将任务发布到 FIFO 队列;注册的工作线程认领并执行它们。适用于您想要多个代理并行运行或工作线程可用性动态变化的情况。
**队列一个任务:**
```
handoff_task({
agent: "claude",
prompt: "优化用户服务数据库查询",
pool: true,
requiredCapabilities: ["database"]
})
```
```
hnd_s1t2u3v4w5x6 排队 → 池 能力:[database]
```
**工作线程端:**
```
register_worker({ name: "db-specialist", capabilities: ["typescript", "database"] })
```
```
wkr_m3n4o5p6q7r8 db-specialist 在线 能力:[typescript, database]
```
```
pull_task({ workerId: "wkr_m3n4o5p6q7r8" })
```
```
hnd_s1t2u3v4w5x6 已认领 优化用户服务数据库查询
```
```
submit_result({ workerId: "wkr_m3n4o5p6q7r8", jobId: "hnd_s1t2u3v4w5x6",
status: "completed", output: "将 p95 延迟降低了 40%" })
```
```
hnd_s1t2u3v4w5x6 ✓ 已完成 将 p95 延迟降低了 40%
```
工作线程必须每 60 秒发送心跳信号(`worker_heartbeat`),否则它们将离线。
---
## CLI — 从终端管理任务
CLI 允许你创建和跟踪变更集。**变更集** 是一个跟踪的工作单元:一个任务规范、一个 git 分支和一个由 REST API 服务器管理的状态生命周期。
**启动服务器(终端 1):**
```bash
agent-handoff-server
# → API 服务器监听 http://localhost:4000
```
**使用 CLI(终端 2):**
```bash
export AGENT_HANDOFF_URL=http://localhost:4000
agent-handoff new "添加速率限制" # 创建任务 + 变更集
agent-handoff list # 列出所有活动变更集
agent-handoff status chg_000001 # 详细查看某个变更集
agent-handoff review chg_000001 # 检查评论 + CI 状态
agent-handoff approve chg_000001 # 批准
agent-handoff merge chg_000001 # 合并分支
agent-handoff export chg_000001 # 推送分支 + 打开 GitHub PR
```
### CLI 参考
| 命令 | 描述 |
|---------|-------------|
| `agent-handoff new <title>` | 创建任务 + 变更集,在 `$EDITOR` 中打开规范 |
| `agent-handoff list` | 列出所有活动变更集 |
| `agent-handoff status [id]` | 列出所有变更集,或详细查看某个变更集 |
| `agent-handoff review [id]` | 阻止评论 + 检查运行 |
| `agent-handoff approve <id>` | 批准变更集 |
| `agent-handoff merge <id>` | 合并到目标分支 |
| `agent-handoff export <id>` | 推送分支 + 打开 GitHub PR |
| `agent-handoff import-issue <url>` | 将 GitHub 问题导入为任务 |
| `agent-handoff workflow run <file>` | 运行代码编排工作流 |
| `agent-handoff workflow list` | 列出 `.claude/workflows/` 中的工作流 |
全局标志:
```bash
--url <url> # REST API URL(覆盖 AGENT_HANDOFF_URL)
--token <token> # Bearer token(覆盖 AGENT_HANDOFF_TOKEN)
--json # 机器可读输出(list, status, review)
```
---
## 工具参考
### 核心工具
| 工具 | 描述 |
|------|-------------|
| `handoff_task` | 分派任务(CLI 启动、A2A 或池) |
| `check_status` | 轮询作业状态 |
| `get_result` | 检索完整输出和 git 差异 |
| `cancel_task` | 杀死正在运行或排队的作业 |
| `list_agents` | 列出可用的 CLI 代理和注册的 A2A 代理 |
| `register_agent` | 注册 A2A 端点 |
### 池工具
| 工具 | 描述 |
|------|-------------|
| `register_worker` | 加入工作线程池,可选功能 |
| `pull_task` | 认领下一个兼容的排队任务 |
| `submit_result` | 将任务结果报告回池 |
| `worker_heartbeat` | 保持工作线程注册有效(60s TTL) |
| `list_workers` | 显示所有工作线程及其当前状态 |
### `handoff_task` 参数
| 参数 | 类型 | 描述 |
|-----------|------|-------------|
| `agent` | `string` | CLI 代理:`claude`、`codex`、`gemini`、`copilot`、`opencode` |
| `agentUrl` | `string` | A2A 端点 URL(与 `agent` 互斥) |
| `prompt` | `string` | 任务描述 |
| `workingDirectory` | `string` | 工作目录(CLI 代理) |
| `model` | `string` | 模型覆盖,例如 `"opus"`、`"o3"`、`"gemini-2.5-flash"` |
| `timeoutMs` | `number` | ms 超时(默认:300 000) |
| `spawnMode` | `"headless" \| "tmux"` | CLI 启动模式(默认:`"headless"`) |
| `pool` | `boolean` | 为工作线程池排队而不是直接启动 |
| `requiredCapabilities` | `string[]` | 池匹配所需功能 |
| `contextPayload` | `string` | 任务继续的序列化 HandoffContext |
| `dodCriteria` | `object[]` | 完成标准定义(两阶段握手) |
| `senderSpiffeId` | `string` | 发送者 SPIFFE ID(存储;尚未强制执行) |
`agent` 或 `agentUrl` 之一是必需的。
---
## 支持的 CLI 代理
| 代理 | 命令 | `"claude"` |
| `codex` | JSONL via stdin |
| `gemini` | JSON 输出,支持 `--model` |
| `copilot` | 纯文本输出 |
| OpenCode | JSON 输出,支持 `--model` |
所有这些都通过 PATH 检测。使用 `list_agents` 查看系统上安装的内容。
---
## 高级功能
### 工作流 — 代码编排
代替 LLM 会话编排子代理(每个结果重新进入其上下文窗口,直到它填满并降低质量),一个普通的 JavaScript 文件驱动编排。代理结果直接在代码中传递给阶段,从不重新进入模型上下文,因此工作流可以扩展到数十个代理,而编排器不会变得笨拙。
工作流文件导出一个默认的异步函数,该函数接收一个上下文对象:
```javascript
// .claude/workflows/triage.js
export default async function triage({ agent, pipeline, phaseLog, budget, args }) {
// 类型化交接:模式是阶段之间的契约。
const issues = await agent({
prompt: "将未解决的问题列为 JSON 数组 {issueId,title,userCount}。",
schema: { issueId: "string", title: "string", userCount: "number" },
array: true,
});
// 普通 JS 逻辑 — 无 LLM 决策,无令牌税。
const big = issues.filter((i) => i.userCount >= (args.threshold ?? )1);
if (big.length === 0) return { fixed: 0 };
// 管道将每个项目流经各个阶段,当它完成时(而不是并行 = 批处理 + 等待)。
phaseLog(`Fixing ${big.length} issues`);
const done = await pipeline(big, [
(issue) => agent({ prompt: `Investigate and fix ${issue.issueId}: ${issue.title}` }),
(fix, issue) => agent({ prompt: `Verify the fix for ${issue.issueId} is real: ${fix}` }),
]);
return { fixed: done.length, tokensRemaining: budget.remaining };
}
```
```bash
# 裸名称解析为 .claude/workflows/<name>.js …
agent-handoff workflow run triage --agent claude --arg threshold=100 --budget 200000
# … 或传递显式路径到任何 .js/.mjs 工作流文件。
agent-handoff workflow run examples/workflows/triage.workflow.js --dry-run # 仅验证
agent-handoff workflow run examples/workflows/triage.workflow.js --dry-run
agent-handoff workflow list
```
上下文基元:
| 基元 | 行为 |
|-----------|----------|
| `agent({ prompt, schema?, agent?, model?, array?, timeoutMs? })` | 启动一个子代理(通过 CLI 适配器层);返回模式验证的值,或原始文本。 `timeoutMs` 覆盖每个代理的超时。重试 3 次后出现故障。 |
| `parallel([...tasks])` | 并发运行 thunk/promises;等待所有(一个屏障)。 |
| `pipeline(items, [stages])` | 将每个项目流经各个阶段 — 项目 B 可以在项目 A 处于阶段 1 时处于阶段 2。 |
| `phaseLog(message, data?)` | 发出一个实时的进度条目。 |
| `budget` | `{ total, used, remaining }`,可在循环中查询(例如 `while (budget.remaining > 50000)`);一旦耗尽,`agent()` 抛出异常。 |
| `args` | 来自 `--arg key=value` 的运行时参数(值被 JSON 强制转换)。 |
模式接受字段映射简写(`{ field: "string" | "number" | "boolean" }`,加上 `"[]"` 后缀表示原始数组)或任何 Zod 模式。输出在交接边界处验证;不匹配项重试然后失败。
### 在代理之间传递状态
当交接部分完成的任务时,附加一个 `contextPayload` 以便接收代理知道已完成什么、做出了什么决定以及接下来是什么。
```typescript
import { createHandoffContext, serializeContext } from "@daax-dev/agent-handoff/handoff-context";
const payload = serializeContext(createHandoffContext({
sourceJobId: "hnd_prev123",
completed_tasks: [
{ id: "t-1", title: "Built auth scaffold", completedAt: new Date().toISOString() }
],
decisions: [
{ description: "Use HMAC-SHA256 for signing", rationale: "No SPIFFE lib available",
decidedAt: new Date().toISOString() }
],
modified_files: [{ path: "src/auth/spiffe.ts", changeType: "added" }],
next_steps: [
{ order: 1, description: "Write integration tests for signHandoff and verifyHandoff" }
],
workingContext: { gitBranch: "feat/spiffe-auth", gitHeadSha: "abc1234" }
}));
handoff_task({ agent: "claude", prompt: "Continue from where I left off", contextPayload: payload })
```
有效载荷经过 deflate 压缩和 base64 编码。限制:50 KB 压缩,500 KB 未压缩。
### 完成定义
要求接收代理人承诺遵守某些标准,然后再开始任务。如果无法满足所需标准,交接失败。
```
handoff_task({
agent: "claude",
prompt: "Add OAuth2 support",
dodCriteria: [
{ id: "tests_pass", description: "All tests pass after changes", required: true },
{ id: "type_check", description: "TypeScript compiles cleanly", required: true },
{ id: "docs_updated", description: "API docs updated", required: false }
]
})
```
`CAPABILITY_MISMATCH` 拒绝意味着接收者声明无法满足所需标准。`ACK_TIMEOUT` 拒绝自动重试,指数退火(5 次尝试,100 毫秒 → 800 毫秒)。
### SPIFFE 身份
附加发送者 SPIFFE ID 以进行身份跟踪。存储在作业中;尚未强制执行访问控制。
```
handoff_task({
agentUrl: "https://agent.example.com",
prompt: "...",
senderSpiffeId: "spiffe://trust-domain.example.com/agent/coordinator"
})
```
---
## 配置
### MCP 服务器环境变量
| 变量 | 默认值 | 用途 |
|----------|---------|---------|
| `RECEIVER_CAPABILITIES` | _(unset)_ | 本地 DoD 评估的能力 ID 以逗号分隔。当未设置时,接受 `dodCriteria` 握手。 |
| `HAWKEYE_URL` | _(unset)_ | 当 handshake 超时时发送警告的 HTTP 端点(POST)。尽力而为。 |
| `HANDOFF_LOG_PROMPTS` | `false` | 将提示文本(≤500 个字符)包含在日志中。默认情况下被截断。 |
| `HANDOFF_LOG_DIR` | `.logs/handoffs` | JSONL 事件日志的目录。 |
### REST API 服务器环境变量
| 变量 | 默认值 | 用途 |
|----------|---------|---------|
| `PORT` | `4000` | REST API 监听的端口 |
| `API_TOKEN` | _(unset)_ | 设置后,所有请求都需要 `Authorization: Bearer <token>`。健康端点始终免除。 |
| `API_TOKEN_ALLOW_UI_ORIGIN_BYPASS` | `0` | 将其设置为 `"1"` 以豁免 Vite 开发服务器源(端口 5173-5182)免于在回环上进行令牌身份验证。生产环境下不要在反向代理后面使用。 |
### CLI 客户端环境变量
| 变量 | 默认值 | 用途 |
|----------|---------|---------|
| `AGENT_HANDOFF_URL` | `http://localhost:4000` | REST API URL。被 `--url` 覆盖。 |
| `AGENT_HANDOFF_TOKEN` | _(unset)_ | Bearer token。被 `--token` 覆盖。 |
---
## 日志记录
交接事件记录到 `.logs/handoffs/YYYY-MM-DD.jsonl`(可通过 `HANDOFF_LOG_DIR` 修改):
```json
{
"timestamp": "2024-02-14T12:34:56.789Z",
"jobId": "hnd_a1b2c3d4e5f6",
"event": "task_created",
"transport": "cli",
"agent": "claude"
}
```
设置 `HANDOFF_LOG_PROMPTS=true` 以在日志条目中包含提示文本(截断为 500 个字符)。
---
## 架构
```
MCP 客户端(Claude Code,Cursor…)
│ stdio
agent-handoff MCP 服务器
├── handoff_task ──► CLI Runner(无头 / tmux)
│ ├── claude -p …
│ ├── codex exec …
│ └── gemini -p …
├── handoff_task ──► A2A 客户端(JSON-RPC over HTTP)
│ └── message/send → tasks/get → tasks/cancel
├── handoff_task ──► Worker Pool(FIFO 队列)
│ pull_task ◄── └── workers pull, execute, submit
│
└── Job Store(内存映射,hnd_* ID)
├── snapshot / rollback(DoD 重试安全性)
└── JSONL 日志(.logs/handoffs/)
```
所有作业状态都在内存中,服务器重启后重置。
| 模块 | 角色 |
|--------|------|
| `src/job-store.ts` | 内存作业存储,带有快照/回滚功能 |
| `src/handoff-context.ts` | HandoffContext 模式,规范序列化,deflate 编码 |
| `src/a2a/handshake.ts` | 两阶段 DoD 握手,30 秒超时,Hawkeye 升级 |
| `src/a2a/retry.ts` | 指数退火重试(仅 ACK_TIMEOUT,最大 5 次尝试) |
| `src/auth/spiffe.ts` | HMAC-SHA256 信封签名和验证 |
| `src/cli/` | 代理适配器 + tmux 启动器 |
| `src/pool/` | FIFO 作业队列 + 工作线程注册表,心跳存活 |
| `src/utils/logger.ts` | JSONL 事件日志(默认情况下提示被截断) |
---
## 开发
```bash
bun run test # 运行所有测试(vitest)
bun run typecheck # tsc --noEmit
```
测试文件位于 `tests/` 目录中。要添加 CLI 适配器:
1. 在 `src/cli/<name>.ts` 中创建 `BaseAdapter` 扩展 — 实现 `buildArgs()` 和 `parseOutput()`
2. 将代理名称添加到 `AgentName` 中的 `src/types.ts`
3. 在 `src/cli/registry.ts` 中注册
4. 在 `tests/cli-adapters.test.ts` 中添加测试
参见 [docs/faq.md](docs/faq.md) 获取非 MCP 协调器示例。
---
## 附录 — 响应形状
每个 MCP 工具返回的原始 JSON,用于构建集成或自定义客户端。
**`list_agents`**
```json
[
{ "name": "claude", "available": true, "command": "claude" },
{ "name": "codex", "available": false, "command": "codex" },
{ "name": "gemini", "available": true, "command": "gemini" }
]
```
**`handoff_task`**
```json
{ "jobId": "hnd_a1b2c3d4e5f6", "status": "queued", "transport": "cli" }
```
**`check_status`**
```json
{ "jobId": "hnd_a1b2c3d4e5f6", "status": "running", "durationMs": 12400 }
```
**`get_result`**
```json
{
"jobId": "hnd_a1b2c3d4e5f6",
"status": "completed",
"exitCode": 0,
"filesChanged": ["src/routes/signup.ts"],
"diffSummary": "1 file changed, 34 insertions(+), 2 deletions(-)",
"output": "…agent stdout…"
}
```
**`register_worker`**
```json
{ "workerId": "wkr_m3n4o5p6q7r8", "name": "db-specialist" }
```
**`pull_task`** (任务可用)
```json
{
"available": true,
"jobId": "hnd_s1t2u3v4w5x6",
"prompt": "Optimize the user-service database queries",
"requiredCapabilities": ["database"]
}
```
**`pull_task`** (队列为空)
```json
{ "available": false }
```
作业状态值:`queued` · `running` · `completed` · `failed` · `cancelled`
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器