# agentto

`agentto` 是 [Agentto Web 控制台](https://dash.agentto.net) 的本地连接器。它会把电脑上已经安装的 Hermes、Claude Code、ClawClaude、OpenClaw、Codex、Gemini、Qwen Code 等 Agent CLI 连接到你的 Agentto 账号，让你可以通过网页或手机继续使用这些本地 Agent。

## 连接账号

首选扫码接入，不需要复制或输入账号密钥：

```bash
npx agentto@latest
# 在 TUI 中输入 /link，然后用已登录 Agentto 的手机扫码
```

无法扫码时，可以在 Agentto 的“我的 / Account”中复制账号密钥。不要把密钥放进命令
参数；命令参数可能出现在 shell 历史和进程列表中。请按当前 shell 隐藏输入，再通过环境
变量启动：

```bash
# zsh
(
  read -s 'AGENTTO_AUTH_KEY?Agentto account key: ' || exit
  printf '\n'
  export AGENTTO_AUTH_KEY
  npx agentto@latest
)
```

```bash
# bash（与上面的 zsh 示例二选一）
(
  read -rsp 'Agentto account key: ' AGENTTO_AUTH_KEY || exit
  printf '\n'
  export AGENTTO_AUTH_KEY
  npx agentto@latest
)
```

也可以先用可信编辑器把密钥保存到仅当前用户可读的临时文件，再读入环境变量：

```bash
(
  umask 077
  key_file="$(mktemp "${TMPDIR:-/tmp}/agentto-account-key.XXXXXX")" || exit
  trap 'rm -f -- "$key_file"' EXIT
  "${EDITOR:-vi}" "$key_file"
  IFS= read -r AGENTTO_AUTH_KEY < "$key_file" || exit
  export AGENTTO_AUTH_KEY
  npx agentto@latest
)
```

首次连接后，Connector 会把绑定状态保存到权限为 600 的 `~/.agentto-state.json`，后续重启
不需要再次把密钥放进 argv 或临时文件。环境变量和 600 权限文件可以避免密钥进入 shell
历史或命令参数，但不是同一 OS 用户内的强隔离：以同一用户运行的其他进程仍可能读取进程
环境或本机绑定状态。Connector 启动 Agent CLI、模型探测和定时任务子进程时会移除
Agentto 账号与消息凭据；OpenAI、Anthropic 等模型提供商密钥不受影响。

连接器会：

- 扫描本机已经安装的 Agent CLI；
- 使用账号密钥证明归属，并把候选 Agent 上报到你的账号；
- 在本机运行对应 CLI，把回复转发到 Web 或 iOS；
- 按模型和 CLI 能力转交图片、文档等附件。
- 把每个 Agentto 对话映射到 Codex / Claude Code 的原生会话，后续消息和 Connector
  重启后会继续同一上下文；删除 Agentto 对话时也会清除本机映射。

账号密钥等同于连接权限，请勿分享。若发生泄露，应立即在账号页面轮换密钥。

## 终端控制台

连接账号后，后台守护进程负责保持连接，当前终端只是控制台：

- `Tab`：聚焦 Agent 列表，再使用 `↑/↓` 切换 Agent。
- `/link`：显示二维码，用手机扫码绑定本机。
- `/img <路径>`：按模型能力添加图片或文档。
- `/stop`：停止后台连接；也可以在其他终端运行 `agentto stop`。
- `/unlink`：停止连接并清理本机绑定状态。
- `/quit` 或 `Ctrl-C`：只退出控制台，后台连接继续运行。

也可以从 Agentto 的“我的 / Account”页面选择一行设备发起安全断开。输入当前密码后，
服务端会轮换账号级密钥，并断开该账号的所有 Connector 设备和消息集成；所有安装都需要
重新运行 `/link` 并扫码确认。设备行的安装 ID 只用于分组和展示，不提供真正的单设备撤销。

## 本地模式

不登录、不连接服务器，直接在终端和本机 Agent 对话：

```bash
npx agentto@latest
```

安装完成后也可以直接运行 `agentto`。本地模式支持切换 Agent、连续上下文以及 `/img <路径>` 附件输入，所有调用都在本机完成。

## 守护进程

- `agentto --help`：显示命令帮助，不扫描或启动本机 Agent。
- `agentto --version`：显示当前 Connector 版本。
- `agentto status`：查看后台状态。
- `agentto restart`：重启连接器，使新版本或本地代码改动生效。
- `agentto stop`：停止后台连接。
- `agentto --daemon`：直接启动后台进程，不进入交互控制台。
- `agentto --no-tui`：在当前进程中连接，不显示完整 TUI。

日志位于 `~/.agentto.log`，PID 文件位于 `~/.agentto-daemon.pid`。

## Hermes / OpenClaw 原生同步与宿主机访问

Connector 可以把 Hermes REST/SSE 会话和 OpenClaw Gateway 会话直接映射到 Agentto。令牌只保存在 Connector 进程环境中，不会发送到浏览器或中转服务器。

Connector 会自动发现本机凭据：OpenClaw 从当前环境或 `~/.openclaw/openclaw.json` 的 `gateway.auth.token` 读取；Hermes 从当前环境或 `~/.hermes/.env` 的 `API_SERVER_KEY` 读取。以下 `AGENTTO_*` 变量是显式覆盖项，不再是必填项。发现过程不会把密钥写入日志、能力声明或 relay payload。

```bash
# Hermes API Server（可选覆盖；仍需先启动 Hermes API Server）
export AGENTTO_HERMES_API_URL=http://127.0.0.1:8642
export AGENTTO_HERMES_API_KEY='<Hermes API_SERVER_KEY>'

# OpenClaw Gateway（可选覆盖；标准本机配置通常可自动发现）
export AGENTTO_OPENCLAW_URL=ws://127.0.0.1:18789
export AGENTTO_OPENCLAW_TOKEN='<Gateway token>'
export AGENTTO_OPENCLAW_AGENT_ID=main

# 可选：覆盖自动开放的启动目录；macOS/Linux 多个目录用冒号分隔，Windows 用分号
export AGENTTO_HOST_ROOTS="$HOME/projects"

# 文件浏览默认只读开放启动目录；远程 Shell 默认关闭，必须显式选择开启
export AGENTTO_TERMINAL_ENABLED=1
export AGENTTO_TERMINAL_SHELL=/bin/zsh

agentto restart
```

原生凭据无法发现时，Hermes/OpenClaw 会继续走兼容的一次性 CLI 调用。Connector 默认把启动目录作为只读文件工作区；`AGENTTO_HOST_ROOTS` 可以覆盖这个默认值。工作区之外的路径不会出现在文件浏览器中，常见密钥文件和目录会被拒绝。终端只有在 `AGENTTO_TERMINAL_ENABLED=1` 时才会上报并接受请求。它是未沙箱化 Shell，即使启动目录受限，仍拥有当前系统用户的权限；只应在可信账号和隔离环境中按需开启。

### Web SSH Profile

Web SSH 与未沙箱化的本机 Shell 完全独立，不会开启或依赖
`AGENTTO_TERMINAL_ENABLED`，也不会读取 `~/.ssh/config`、SSH Agent 或网页传来的主机参数。
需要在 Connector 本机显式创建
`~/.agentto-ssh-profiles.json`；也可以用绝对路径
`AGENTTO_SSH_PROFILES_FILE=/path/to/profiles.json` 覆盖：

```json
{
  "version": 1,
  "profiles": [
    {
      "id": "production",
      "label": "Production",
      "hostLabel": "Primary server",
      "userLabel": "Deploy account",
      "host": "server.example.com",
      "port": 22,
      "user": "deploy",
      "identityFile": "/Users/you/.ssh/id_agentto",
      "knownHostsFile": "/Users/you/.ssh/agentto_known_hosts"
    }
  ]
}
```

`identityFile` 和 `knownHostsFile` 必须是 Connector 本机的绝对路径。请先通过可信渠道核对
服务器主机密钥，再把对应记录写入专用的 `knownHostsFile`；Connector 会强制严格主机密钥
检查，不会自动接受或更新陌生密钥。配置文件和私钥必须只允许当前用户访问：

```bash
chmod 600 ~/.agentto-ssh-profiles.json
chmod 600 ~/.ssh/id_agentto
chmod 644 ~/.ssh/agentto_known_hosts
agentto restart
```

建立连接前，Profile RPC 只会向 Web 返回 `id`、`label`、`hostLabel` 和 `userLabel`；
真实 SSH 主机、用户名、私钥路径和 known-hosts 路径不会作为 Profile 字段序列化，Web
也不能传入 SSH 密码、远程命令、ProxyCommand 或其他 SSH 选项。连接建立后，远程 Shell
输出或系统 `ssh` 的诊断信息仍可能显示主机名、用户名或本机路径等环境元数据，应把终端
画面视为用户可见内容。每次新建会话还需要当前 Agentto 账号密码签发的一次性短时授权。
Connector 直接启动系统 `ssh`，禁用密码/键盘交互认证、SSH Agent、X11、端口转发、
本地命令、代理命令和用户配置注入；只有本机 allowlist 中的 Profile 可以连接。删除配置文件或设置
`AGENTTO_SSH_ENABLED=0` 后重启 Connector，可以单独关闭 Web SSH，不影响文件浏览或本机
Shell 设置。每次新建会话还会重新安全读取私钥和 known-hosts，并只把内容复制到该会话
独有、退出后删除的私密临时目录；系统 `ssh` 不直接使用可被并发替换的原始路径。

可以随时运行只读体检（不会显示 token/API key）：

```bash
agentto native status
agentto native status --json
```

它会分别报告 CLI、自动发现的凭据、原生 API/Gateway、文件根目录和 PTY 状态。

新版浏览器与 Connector 会为每条通道执行临时 P-256 ECDH 握手，并使用双向独立的 AES-256-GCM 密钥。正常运行时中转服务器只转发公钥和密文。当前临时公钥尚未绑定设备身份，账号服务也属于受信控制面；此设计防被动读取，不承诺抵御被攻陷的中转服务主动替换公钥。完整边界见仓库的 `docs/native-runtime-and-host-access.md`。旧客户端只保留既有聊天/会话帧兼容，文件、终端和通用 runtime/host RPC 必须使用支持加密通道的新版本。

所有 WebSocket 帧硬限制为 256 KiB，E2E 加密前明文限制为 180 KiB。文件预览进一步
限制为 80 KiB 文本或 128 KiB 原始图片，并在 JSON 编码后再次检查；终端保留 128 KiB
字节回放。超限请求会返回 `frame_too_large` 或 `too_large`，不会静默发送不完整内容。
当前没有大文件/Artifact 分块协议，需要传输更大内容时请改用本机工作区或其他专用
文件通道。

文件页面会订阅当前目录的变更，Web、iOS 和本机工作区无需手动刷新即可保持一致。每个 Agent 同时只复用一份宿主机 CLI 会话；Web 或 iOS 暂时离开、网络重连后会按输出游标继续附着，不会重复输出或悄悄新开 Shell。主动点击“结束/关闭 PTY”会同步结束其他设备上的同一会话；仅关闭页面只会断开当前界面。

## 同账号 Agent-to-Agent 调用

同一个 Agentto 账号纳管的 Connector 可以直接打开 peer 通道，把任务从一台设备的 Agent 委派给另一台设备的 Agent。它依次探测同设备、同内网，再以中心 relay 兜底。调用角色来自服务器认证元数据；直连还需要短期单次签名票据和双向认证。Hermes/OpenClaw 的本机凭据不会离开各自设备。

先查看账号内的 Agent ID，再进行一次调用：

```bash
agentto agent list
agentto agent call <NAS上的OpenClaw-Agent-ID> "检查昨晚的备份是否完整"

# 显式指定调用方 Hermes，或供脚本读取结构化结果
agentto agent call <目标ID> "整理最新日志" --from <Hermes-Agent-ID> --json
```

`agent call` 默认选择当前账号中在线的 Hermes 作为调用方，并等待目标 Agent 的最终回复。`--timeout <毫秒>` 可覆盖默认五分钟超时。

Hermes 可以通过 Connector 自带的 MCP stdio server 自动发现这项能力：

```bash
hermes mcp add agentto --command agentto-mcp --env AGENTTO_CALLER_AGENT_ID=<Hermes-Agent-ID>
hermes mcp test agentto
```

配置后，Agent 会看到以下工具：

- `agentto_handoff`：只声明下一跳需要的能力，不指定 Agent ID；Agentto 自动选择在线且未访问过的 Agent。
- `agentto_run_chain`：启动自动接力。每位 Agent 完成自己的部分后，返回 `complete` 或声明下一跳的任务与能力，Agentto 自动续跑。
- `agentto_list_agents`、`agentto_call_agent`、`agentto_call_agents`：查看 Agent，或在确实需要固定目标时进行单次/批量调用。
- `agentto_get_task_status`：携带调用结果或进度事件返回的原 A2A `sessionId`，在 Connector 或链路重启后恢复确认任务终态；会话 ID 必填以防跨会话读取。

自动链路会携带 `traceId`、总目标、当前跳/最大跳数、已访问 Agent、能力要求、统一截止时间和可选的 `allowedRuntimes`。每一跳的真实调用方都是上一位 Agent；身份由 MCP 进程和已认证通道绑定，模型既不能冒充调用方，也不能指定或伪造下一位 Agent ID。需要把协作限制为 Codex 时，调用 `agentto_handoff` 或 `agentto_run_chain` 时传入 `"allowedRuntimes": ["codex"]`。

MCP server 从权限为 600 的 `~/.agentto-state.json` 读取已绑定账号，不需要把账号密钥写入 Agent 配置。

### Codex / Claude 消息 MCP

`agentto@0.5.38` 及更新版本已经包含 `agentto-message-mcp`。先用
`agentto agent list` 找到本机要处理的 Agent ID，再执行其中一条：

```bash
codex mcp add agentto-messages --env AGENTTO_AGENT_ID=<AGENT_ID> -- npx -y -p agentto@latest agentto-message-mcp
claude mcp add-json agentto-messages '{"type":"stdio","command":"npx","args":["-y","-p","agentto@latest","agentto-message-mcp"],"env":{"AGENTTO_AGENT_ID":"<AGENT_ID>"}}'
```

`agentto-message-mcp` 读取本机私密 linked state，并在客户端只列出、读取和回复
`AGENTTO_AGENT_ID` 选择的 Agent 会话。这里的账号密钥对账号内全部 Agent 有效，
`AGENTTO_AGENT_ID` 也是可修改的本机路由配置，并不是服务端签发的 Agent 专属凭据。
服务端仍会校验会话、目标 Agent 和账号归属的一致性，但持有账号密钥的可信本机可以改为
账号内另一个 Agent；真正不可切换的 Agent 级令牌需要后续单独设计。
自动事件消费与同一 Agent 的本机 Connector 路由会在消费入口互斥：Connector 在线时事件端点返回
`connector_agent_active`，Connector 激活也会立即结束已存在的事件流。已经取到事件且正在回写的
producer 与 Connector 定时任务目前都使用同一个账号级 Bearer，服务端不能安全区分，因此不会全局
阻断 `/send`。彻底消除这段在途切换窗口，需要后续增加服务端签发的 integration token 与
每条路由的短期 lease；不能依赖请求方可伪造的 `source` 字段。
事件流连接时仍用 `since` 选择严格晚于该时间的初始历史窗口；进入实时阶段后只按服务端
SQLite `AUTOINCREMENT` 插入序推进，不依赖设备时钟或随机消息 ID。

显式带幂等键的任务会先返回 `accepted/running`，终态由调用方回执，并原子写入权限为 600 的 `~/.agentto-task-journal.json`。中心连接重启不会中止正在直连执行的任务；Connector 进程重启后，已完成结果可查询/重放。对重启时仍在执行、结果无法判定的任务，Connector 会返回 `execution_interrupted_unknown`，不会自动重跑，以免重复产生外部副作用。

高风险操作的审批决定会先同步、原子写入权限为 600 的 `~/.agentto-approval-journal.json`，Connector 才会返回 `approval:decision:ack` 并继续执行。相同决定在五分钟内重送会返回成功；同一审批 ID 的冲突决定或日志写入失败会保守拒绝，不会启动危险操作。

对话历史使用 v2 `~/.agentto-sessions.json`，通过同目录临时文件原子替换并固定为权限
600。每个会话最多保留 100 条消息，最多保留 200 个会话，整体上限 32 MiB；会话读、
追加和删除都绑定到首次认领它的 Agent，其他 Agent 通道会收到
`session_owner_mismatch`。认证 A2A 与群组讨论的 transient 轮次只绑定当前通道，不读取
或写入其他 Agent 的持久历史。

安装身份保存在权限为 600 的 `~/.agentto-device-id`，用于路由、设备分组、显示名称和配对
请求关联。它与旧版使用的主机名一样都是客户端自报元数据，不能视为密钥或授权边界。
当前控制连接使用账号级 `accountKey`；安全断开会轮换该密钥，因此旧密钥即使换一个
device ID 也不能重连。SQLite 设备撤销行只保留为旧版本兼容提示。真正的单设备撤销需要
以后迁移到服务端签发的每设备独立凭据。

当前版本支持同账号、同步文本任务委派、能力路由、最多 8 跳的自动接力、循环保护、独立远程会话、结构化失败、截止时间、持久化幂等重试、取消、并发保护和分段耗时诊断；跨账号调用、自动处理远端高风险审批和大文件 Artifact 流仍不在本阶段范围内。

## 自定义 Agent 适配器

任何支持“一条命令接收 prompt 并输出文本”的 CLI 都可以加入扫描。创建 `~/.agentto-adapters.json`：

```json
{
  "agents": [
    {
      "key": "my-agent-default",
      "cli": "my-agent",
      "name": "My Agent",
      "tag": "MA",
      "model": "my-agent-model",
      "detectArgs": ["--version"],
      "args": ["run", "--prompt", "{prompt}"],
      "skills": ["coding", "testing", "review-specialist"],
      "commands": [
        { "name": "status", "desc": "查看本机 Agent 状态", "args": ["status"] }
      ]
    }
  ]
}
```

字段说明：

- `cli`：PATH 中的可执行命令名。
- `key`：可选的稳定本地身份；同一 CLI 配置多个 profile 时必须不同。
- `args`：调用参数模板，必须包含 `{prompt}`。
- `detectArgs`：检测安装状态使用的参数，默认 `['--version']`。
- `skills`：可选的能力标签，用于 `agentto_handoff` / `agentto_run_chain` 自动路由；同一 CLI 的不同 profile 可声明不同专长。
- `accepts`：可选附件能力，例如 `['image']`、`['document']`；只有 CLI 确实能读取文件路径时才声明。
- `commands`：可选的安全只读命令，例如 `help`、`status`、`version`、`models`、`info`、`whoami`、`doctor`、`list`。

保存配置后运行 `agentto restart`，新 Agent 会出现在“添加 Agent”列表中。

## 开发与验证

要求 Node.js 18 以上。在仓库根目录运行：

```bash
npm run check:connector
```

发布准备应在 `packages/connector/` 中执行 `npm pack`，检查 tarball 并在临时目录安装冒烟。`npm publish` 是单独的授权动作。

## 服务地址

- Connector：`wss://link.agentto.net`
- Web 控制台：`https://dash.agentto.net`
