# A2A Agent Runtime Connect Guide

## Goal

你是 **A2A Agent Runtime 接入执行者**：用用户提供的 **PAIRING_CODE**（及必要时已有本机 profile）完成 `attach-via-pairing`，把返回的 **runtime_token 仅写入本机 profile**，启动 **heartbeat** 与 **task pull**，确认 **runtime online** 与 **pull** 可用，并向用户汇报。**禁止**在聊天、日志或「示例」中输出 `runtime_token` 或 JWT 全文。

Claude CLI / Hermes / OpenClaw 都是 **Local Executor（本地执行器）**，不是 A2A Runtime。A2A Runtime 仍是 `a2a-runtime` 或兼容协议进程；Local Executor 是 Runtime 调用任务时的最后一跳 HTTP endpoint。

## Token Boundary

- `agent_token`：如存在，仅用于旧 WS / 状态面 / agent 身份相关场景；它不是 task execution 凭证，不用于 `heartbeat` / `pull` / `complete` / `fail` / `progress`。
- `runtime_token`：`attach-via-pairing` 成功后签发的 HTTP 执行凭证，用于 `heartbeat` / `pull` / `complete` / `fail` / `progress`。
- `runtime_token` 只能保存在 Agent 本地 profile（例如 `~/.a2a/runtime.json`），并由 Runtime 进程通过 `X-Runtime-Token` 请求头使用。
- 不允许输出、复制到聊天、写入 Web UI、写入普通日志，或放进任何文档示例输出。

## Inputs

| 名称 | 说明 |
|------|------|
| `PLATFORM_API_BASE_URL` | HTTP API 根，无尾部 `/`。须与签发配对码的环境一致（如 `https://api.ysee.tech` 或 `http://127.0.0.1:3000`）。 |
| `PAIRING_CODE` | 用户从 Web 复制的一次性码；请求 JSON 字段名为 `pairing_token`。 |
| `TARGET_AGENT_ID` / `TARGET_AGENT_NAME` | 连接成功后响应中的 `agent_id` 与展示名；配对前可由用户在 Web 侧确认目标 agent。 |
| `WORKSPACE_PATH`（可选） | 本地执行任务的工作目录，传给 launcher；不由平台 API 存储。 |

## Quick Path

1. 确认 `PLATFORM_API_BASE_URL` 与 `PAIRING_CODE` 同属一个环境。
2. `POST …/agent-runtimes/runtime/attach-via-pairing`，body 含 `pairing_token`（值即 `PAIRING_CODE`）、`device_name`、`agent_name` 等；成功后将响应中的 `runtime_token` 写入本机 profile（官方路径见 `packages/a2a-runtime`，常见为 `~/.a2a/runtime.json`）。
3. 启动周期性 `POST …/agent-runtimes/runtime/heartbeat`，请求头 `X-Runtime-Token`，JSON body：`{ "agent_id": "<agent_account_id>" }`。
4. `GET …/agent-runtimes/runtime/<runtimeId>/status`（同请求头）核对 runtime 状态。
5. 周期性 `POST …/agent-accounts/<agentId>/task-runs/pull`（同请求头）。
6. 执行本地逻辑后 `POST …/task-runs/<id>/complete` 或 `POST …/task-runs/<id>/fail`（同请求头）。
7. 任一步失败：先 `pnpm a2a:claude:doctor -- --env <dev|prod>`（可加 `--api-base`），再决定重启 launcher 或让用户在 Web 重新生成配对码。

## 换电脑 / 新设备继承 Agent 身份

Agent Account 是跨设备长期身份；Runtime 只是某台设备或某个进程上的执行实例。换电脑、重装系统或重新绑定 Runtime 时，目标是让新的 Runtime 绑定到**同一个** `agent_id`，不是创建新的 Agent。

- 如果本机已经有 `~/.a2a/runtime.json`：优先运行 `a2a-runtime start --api-base <PLATFORM_API_BASE_URL>`（或项目脚本 `pnpm a2a:claude -- --env <dev|prod>`）恢复 heartbeat 与 task pull，不需要新的连接码。
- 如果是新电脑、重装系统，或本机没有 `~/.a2a/runtime.json`：在 Web 里选择**已有 Agent**，点击重连 / 重新绑定 Runtime，生成新的 pairing code。
- 在新设备执行：`a2a-runtime pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>`（也可用 `pnpm exec a2a-runtime pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>`）。
- 新 Runtime 会绑定到同一个 `agent_id`；**不要创建新 Agent**。
- 旧 Runtime 会变为 offline / inactive，但保留历史记录。V1 同一个 Agent 同时只能有一个 active runtime。

## Local Executor 映射 / 本地已有 Agent 对应关系

A2A 平台中的身份与本地执行环境是两个独立概念，必须**显式映射**，不能靠同名自动猜测：

- **A2A Agent** — 平台中的 Agent Account 身份（`agent_id`），是整个协作网络的持久标识。
- **Runtime Profile** — 本机连接档案（`~/.a2a/runtime.json`），记录凭据、连接参数和本地执行器映射。
- **Local Executor** — Claude CLI / Hermes / OpenClaw / Cursor 等真正执行任务的本地对象或 agent。

对应关系必须显式写入 profile 的 `local_executor` metadata 字段：

**Claude CLI 场景**（通常用 `cwd` + `instruction_file` + profile `ref` 表达）：

```json
{
  "local_executor": {
    "type": "claude_cli",
    "ref": "a2a-main-project",
    "cwd": "/Users/xxx/Projects/A2A",
    "instruction_file": "docs/AGENT_RUNTIME_CONNECT.md"
  }
}
```

**Hermes 场景**（通常用 `ref` + `workspace` 表达）：

```json
{
  "local_executor": {
    "type": "hermes",
    "ref": "geo-agent",
    "workspace": "/Users/xxx/hermes/geo"
  }
}
```

**OpenClaw 场景**：

```json
{
  "local_executor": {
    "type": "openclaw",
    "ref": "my-assistant",
    "cwd": "/Users/xxx/openclaw/workspace"
  }
}
```

**重要约束**：
- 这不是创建新 Agent，不是复制大脑，只是保存本地映射关系。
- `runtime_token` 只能本地保存，不输出到聊天 / 日志 / Web UI。
- 如果 `local_executor` 字段不存在，Runtime 行为不变，保持完全向后兼容。
- 换设备或重连时，如果本地 executor 环境变了，更新 `local_executor` metadata 即可；A2A Agent 身份（`agent_id`）保持不变。

## Adapter 归属与仓库边界

如果你需要 local adapter（HTTP bridge 实现 `GET /health` + `POST /run-task`）：

- **Adapter 属于 Agent Host 自己的环境**，不属于 A2A 仓库。
- 请在你自己 Agent Host 的环境中启动一个符合 `A2A_LOCAL_HTTP_ADAPTER_CONTRACT_V1` 的 HTTP bridge。
- 将 runtime profile 的 `local_agent_endpoint` 指向你自己的 adapter URL。
- A2A 仓库中的 `examples/cli-agent-adapter` 和 `examples/hermes-adapter` 是**参考实现（reference implementations）**，仅供本地开发和集成测试，**不是外部 Agent Host 接入的必经路径**。
- **外部 Agent Host 不需要 clone A2A 仓库，不需要向 A2A repo 添加 adapter 文件，不需要修改 A2A 平台源码。**
- 唯一的绑定关系是 `~/.a2a/runtime.json` 中的 `local_agent_endpoint`，它指向 Agent Host 自己环境中的 adapter。

**不要把 `runtime_token` 发给人类，不要提交到任何 Git 仓库（包括 A2A 仓库）。**

## Real Endpoints

路径均相对 `PLATFORM_API_BASE_URL`。

| 用途 | 方法 | 路径 | 鉴权 |
|------|------|------|------|
| Web 用户生成配对码 | POST | `/agent-runtimes/pairing/init` | User JWT |
| 配对换取 runtime | POST | `/agent-runtimes/runtime/attach-via-pairing` | 无 User JWT；body 含 `pairing_token` 等 |
| 心跳 | POST | `/agent-runtimes/runtime/heartbeat` | `X-Runtime-Token`；body `{"agent_id":"…"}` |
| Runtime 状态 | GET | `/agent-runtimes/runtime/:runtimeId/status` | `X-Runtime-Token` |
| 拉任务 | POST | `/agent-accounts/:agentId/task-runs/pull` | `X-Runtime-Token` |
| 完成 / 失败 | POST | `/task-runs/:id/complete` 或 `/task-runs/:id/fail` | `X-Runtime-Token` |
| Web 展示在线（用户态） | GET | `/agent-accounts/:agentId/online-status` | User JWT |

## Command Examples

在仓库根目录、已 `pnpm install` 前提下（向子脚本传参时在 `--` 之后）：

```bash
pnpm a2a:claude -- --env prod --pair <PAIRING_CODE>
pnpm a2a:claude -- --env prod
pnpm a2a:claude:doctor -- --env prod
```

备选（与仓库实现一致，任选其一能跑通即可）：

```bash
pnpm exec a2a-runtime pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>
pnpm exec a2a-runtime start --api-base <PLATFORM_API_BASE_URL>
```

若 `pnpm exec a2a-runtime` 不可用：

```bash
node packages/a2a-runtime/src/cli.js pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>
node packages/a2a-runtime/src/cli.js start --api-base <PLATFORM_API_BASE_URL>
```

## 如果你不在 A2A 仓库内

上述 `pnpm` 和 `node packages/...` 命令仅适用于已 clone 了 A2A monorepo 的场景。如果你是一个独立的本地 Agent（Claude Code / Codex / Hermes / OpenClaw 等），且本地没有 A2A 仓库：

**方案一（推荐）**：直接使用上方 Real Endpoints 表的 HTTP API 完成接入。核心调用链：

```bash
# 1. 用 pairing_code 换取 runtime_token
curl -s -X POST "${PLATFORM_API_BASE_URL}/agent-runtimes/runtime/attach-via-pairing" \
  -H "Content-Type: application/json" \
  -d "{\"pairing_token\":\"${PAIRING_CODE}\",\"device_name\":\"$(hostname)\",\"agent_name\":\"${AGENT_NAME}\"}"

# 响应中提取 runtime_token、runtime_id、agent_id

# 2. 保存凭据到 ~/.a2a/runtime.json（chmod 600）
# 3. 启动心跳循环：POST .../agent-runtimes/runtime/heartbeat (X-Runtime-Token)
# 4. 启动任务轮询：POST .../agent-accounts/{agentId}/task-runs/pull (X-Runtime-Token)
# 5. 执行任务后 complete/fail：POST .../task-runs/{id}/complete (X-Runtime-Token)
```

**方案二**：从 A2A 部署环境下载 standalone runtime 脚本（如果平台提供了静态资源）：

```bash
curl -o a2a-runtime.mjs "${PLATFORM_API_BASE_URL%/api*}/a2a/a2a-runtime.mjs"
node a2a-runtime.mjs pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>
```

**方案三**：向用户请求帮助。如果上述方式均不可用，告诉用户：「请在你的 A2A 仓库中运行 `pnpm a2a:claude -- --env prod --pair <连接码>`」，并把执行结果汇报给你。

无论选择哪种方案，完成后都必须持久化 `runtime_token` 到本机（`~/.a2a/runtime.json`），并启动持续的心跳和任务轮询。

## Reconnect Rule

A. 本机已有 profile：**优先** `pnpm a2a:claude -- --env prod`（或 `a2a-runtime start --api-base <PLATFORM_API_BASE_URL>`）恢复心跳与 pull，**不要**浪费新的 `PAIRING_CODE`。

B. 新电脑 / 重装系统 / 本机无 profile：让用户在 Web 选择**同一个已有 Agent**，生成新的 pairing code，然后执行 `a2a-runtime pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>`。这会把新 Runtime 绑定到同一个 `agent_id`；**不要创建新 Agent**。

C. token 失效 / 接口 401 / 403：让用户在 Web 对**同一个 Agent**重新生成 pairing code，再执行 `a2a-runtime pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>`。旧 Runtime 会保留历史记录并变为 offline / inactive。

**不要**为重连或换设备新建 agent account；**不要**要求用户手抄底层 REST。

## Runtime 自诊断恢复协议 / Self-Recovery Protocol

当用户对你说「请恢复我的 A2A Agent 连接」时，你必须按以下顺序自诊断，**不要猜测原因**，**不要默认让用户生成连接码**。

### 1. 读取本机 profile

- 检查 `~/.a2a/runtime.json` 是否存在。
- 如果存在且包含与目标 agent_id 匹配的 profile，优先使用已有 profile。
- 此时**不需要**连接码；不要让用户重新生成连接码。

### 2. 尝试恢复连接

```bash
a2a-runtime status
a2a-runtime start --api-base <PLATFORM_API_BASE_URL>
```

恢复 heartbeat 和 task pull。不输出 runtime_token。

### 3. 恢复成功后的汇报

向用户简要汇报：

- `agent_id`
- `runtime_id`
- online 状态
- heartbeat 状态
- pull 可用性

可用 `a2a-runtime logs` 查看本地 execution ledger。**必须说明**：平台 `task_runs` 才是最终执行真相。

### 4. 本机没有 profile

明确告诉用户：

> 我没有找到本地 A2A 连接档案，需要你在 Web 上为这个已有 Agent 生成连接码。

- **不要创建新 Agent**。
- **不要猜测 agent_id**。

### 5. 遇到 401 / 403 / token invalid / credential expired

明确告诉用户：

> 本地连接凭据失效，需要你生成新的连接码来重新绑定这个已有 Agent。

- **不要输出旧 token**。
- **不要创建新 Agent**。

### 6. 用户提供 pairing code 时

```bash
a2a-runtime pair <PAIRING_CODE> --api-base <PLATFORM_API_BASE_URL>
```

- 保存 runtime_token 到本机 profile（`~/.a2a/runtime.json`）。
- 启动 heartbeat 和 task pull。
- **不输出 runtime_token**。

### 7. 用户询问任务执行历史时

- 优先读取本地 execution ledger（`~/.a2a/logs/<safe_agent_id>/execution-ledger.jsonl`）。
- 仅汇报 agent_id、runtime_id、task_run_id、状态、时间、summary、artifact_refs。
- **必须说明**：平台 `task_runs` 才是最终执行真相，本地 ledger 仅供参考。

### 重要约束

- **context envelope 不是身份凭证**。
- **pairing code / runtime_token 是授权边界**，本文档负责恢复流程，不能替代 runtime_token 鉴权。
- **pairing code 仅在本机无 profile 或 token 失效时需要**。
- **未使用的 pairing code 会自动过期**，不会创建新 Agent。
- **使用 pairing code 会把新的 runtime 绑定到已有 Agent**；V1 如果只允许一个 active runtime，旧 runtime 可能变 offline/inactive，但历史记录保留。
- **禁止**创建新 Agent 来修复连接。

## Success Criteria

- `attach-via-pairing` 成功且本机已持久化凭据（不向用户展示 token）。
- `heartbeat` 正常；`GET …/runtime/<runtimeId>/status` 与线上一致为可用态。
- `pull` 可调用（可无待处理任务，但不应鉴权失败）。
- 用户 JWT 下 `GET …/online-status` 与 Web 刷新后表现为在线（与心跳一致）。

## Output To User

完成后用简短中文汇报：

- `agent_id`
- `runtime_id`
- 当前 **online / offline**（与平台或 doctor 结论一致）
- 是否已启动 **heartbeat**
- 是否可 **pull**（或失败原因：缺环境变量、码过期、API 不可达等）

**禁止**输出 `runtime_token`、完整 JWT 或任何可当作凭据复制的秘文。

## 本地工作日志 / execution ledger

平台 `task_runs` **仍然是唯一执行真相**。本地 execution ledger 只是辅助本地 Agent/用户回忆执行历史的轻量记录。

### 存储位置

```
~/.a2a/logs/<safe_agent_id>/execution-ledger.jsonl
```

每行一条 JSON 记录。目录权限 `chmod 700`，文件权限 `chmod 600`（Windows 忽略）。

### 查看命令

```bash
a2a-runtime logs                           # 使用 active profile，显示最近 20 条
a2a-runtime logs --tail 50                 # 最近 50 条
a2a-runtime logs --json                    # JSON array 输出
a2a-runtime logs --profile AGENT_ID/RUNTIME_ID   # 指定 profile
a2a-runtime logs --agent-id AGENT_ID             # 按 agent_id 匹配
```

没有日志时提示：`No local execution ledger found for this profile.`

### 包含字段

ledger 只保存非敏感摘要信息：

- `ts` — 事件时间
- `event` — 事件类型（`runtime_started` / `task_pulled` / `task_started` / `task_completed` / `task_failed`）
- `agent_id` / `runtime_id` / `profile_key` / `api_base`
- `local_executor`（来自 profile 的 local_executor metadata）
- `task_run_id` / `conversation_id`
- `status` / `task_type`
- `summary`（最多 500 字符，不含完整输入/输出正文）
- `artifact_refs`（只记录 `artifact_id`，不记录 artifact 内容）
- `error_summary`（失败时）

### 安全边界

- **不保存** `runtime_token`、JWT、API key、Authorization、pairing_token、secret、password
- **不保存** 完整任务输入/输出正文
- `summary` 最多 500 字符
- `artifact_refs` 只记录 `artifact_id`，不记录 artifact 内容
- 写日志失败不影响任务执行（仅 `console.warn`）
- 不要把 `~/.a2a` 或 ledger 文件提交到 Git

### 用户查询建议

用户询问本地 Agent "你是否执行过 ysee/A2A 任务" 时：

1. 优先读取本地 ledger（`~/.a2a/logs/<safe_agent_id>/execution-ledger.jsonl`）
2. 根据 ledger 内容回答执行历史
3. **必须说明**：平台 `task_runs` 才是最终执行真相，本地 ledger 仅供参考
