agent-fleet/openspec/changes/dual-execution-model/design.md
Zer4tul e39a16498c feat: dual execution model (SSH CLI + HTTP pull)
- ExecutionMode enum: SshCli (orchestrator dispatches) | HttpPull (agent pulls)
- SSH CLI executor: spawn remote agents via ssh + CLI template
- Local subprocess as SSH special case (localhost)
- HostConfig with capability matching and load-based selection
- Dispatch loop: scan created tasks → select host → execute → update
- CliAdapterConfig: CLI templates for Codex and Claude Code
- Structured prompt construction (Issue → goal/constraints/validation)
- Output parsers: Codex JSON, Claude Code JSON, raw fallback
- TaskStatus::ReviewPending + review_count loop limit
- Forgejo webhook: pull_request (opened→review_pending, merged→completed)
- Forgejo webhook: push events (task/* branch → last_activity_at)
- HTTP API: dequeue only returns http_pull tasks
- HTTP API: status update only for http_pull mode
- Token auth config for http_pull agents
- Adapter module rewritten: AgentAdapter trait removed → config-driven CLI templates
- New fields: execution_mode, assigned_host, branch_name, pr_title, last_activity_at, review_count
- 30/30 tests pass
2026-05-12 14:07:56 +08:00

3.2 KiB
Raw Blame History

Context

adapter-cross-machine-revision change 设计了纯 Pull 模型Agent 主动调 HTTP API但分析后发现 Pull 模型不是 subprocess CLI 的跨机等价——控制权、上下文传递、生命周期管理都不同。

真正的跨机等价是 SSH + CLIOrchestrator 通过 SSH 在远程主机上 spawn Agent CLI与本地 subprocess 完全一致的执行模型。

同时HTTP Pull 对外部 AgentOpenClaw/Jeeves、Hermes仍有价值。因此需要双执行模型。

Goals / Non-Goals

Goals:

  • SSH + CLI 作为主执行模式Orchestrator 主动调度、构造上下文、控制生命周期)
  • HTTP API 保留给外部 Agent 自主接入
  • 每种 Agent 类型定义为 CLI 模板 + 输出解析器
  • 远程主机管理SSH 连接、CLI 可用性检查)

Non-Goals:

  • 不实现 Agent daemonPhase 1 用 SSH + CLI 足够)
  • 不实现动态主机发现Phase 1 静态配置)
  • 不实现容器化执行(不用 Docker/Kubernetes

Decisions

Decision 1: SSH + CLI 是 subprocess 的跨机等价

选择: Orchestrator 通过 SSH 执行远程 Agent CLI

理由:

  • 控制流与本地 subprocess 完全一致
  • 上下文由 Orchestrator 构造,通过 CLI 参数传入
  • Agent 不需要预运行,不需要 daemon
  • 唯一前提SSH 免密登录 + 目标机器装了 CLI

替代方案:

  • HTTP Pull控制权反转上下文传递难解决
  • Agent daemon复杂度高每台机器多一个服务
  • 容器化:更复杂,需要 container runtime

Decision 2: HTTP Pull 作为补充模式

选择: 保留 HTTP API 给外部 Agent

理由:

  • OpenClaw/Jeeves、Hermes 有自己的调度,不需要 Orchestrator 启动
  • 它们只需要查询/更新任务状态
  • 两种模式通过 execution_mode 字段区分

Decision 3: Adapter = CLI 模板 + 输出解析器

选择: 不实现 AgentAdapter trait而是配置驱动

理由:

  • CLI 模板可以通过配置文件定义,无需编译
  • 不同 Agent 的差异主要在 CLI 参数和输出格式
  • 新增 Agent 类型只需要加配置,不需要写代码

Decision 4: 本地执行作为 SSH 的特例

选择: Orchestrator 所在机器的 Agent 用本地 subprocess不经过 SSH

理由:

  • 避免 SSH loopback 的开销和配置
  • subprocess 是 SSH 的本地特例,逻辑统一

Risks / Trade-offs

  • [SSH 密钥管理] 需要配置免密 SSH → 用 SSH agent forwarding 或 deploy key标准运维实践
  • [长时间运行] SSH 连接可能超时 → 用 ssh -o ServerAliveInterval=60,或改用 SSH multiplexing
  • [CLI 版本差异] 不同机器可能装不同版本 → health check 时验证版本

Migration Plan

  1. 新增 src/execution/ 模块SSH executor、CLI template、output parser
  2. Task 模型添加 execution_modeassigned_host 字段
  3. 新增 [hosts] 配置 section
  4. 实现 Orchestrator dispatch loopssh_cli → SSH 执行http_pull → 等待 Agent dequeue
  5. 保留并调整 HTTP API 端点dequeue 仅限 http_pull 任务)
  6. 更新测试

Open Questions

  • SSH 库选择:ssh2 crate vs tokio::process::Command + 系统 ssh 命令?
  • 是否需要支持 SSH jump host通过跳板机连接目标机器