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
This commit is contained in:
Zer4tul 2026-05-12 14:07:56 +08:00
parent 1bc7580ecc
commit e39a16498c
34 changed files with 2541 additions and 1555 deletions

View file

@ -0,0 +1,41 @@
## MODIFIED Requirements
### Requirement: Agent adapter as CLI template
每个 Agent 类型 SHALL 定义为 CLI 命令模板 + 输出解析器,而非代码 trait。Orchestrator 根据模板构造命令、通过 SSH 执行、解析输出。
#### Scenario: Codex CLI adapter definition
- **WHEN** Agent 类型为 `codex-cli`
- **THEN** adapter 定义 SHALL 包含:
- `cli_template`: `codex exec --json '{prompt}'`
- `work_dir`: `{repo_path}`
- `output_format`: `json`
- `timeout`: 3600
- `output_parser`: codex_json_parser
#### Scenario: Claude Code adapter definition
- **WHEN** Agent 类型为 `claude-code`
- **THEN** adapter 定义 SHALL 包含:
- `cli_template`: `claude -p '{prompt}' --output-format json --dangerously-skip-permissions`
- `work_dir`: `{repo_path}`
- `output_format`: `json`
- `timeout`: 3600
- `output_parser`: claude_json_parser
#### Scenario: Custom adapter with custom template
- **WHEN** 用户定义新的 Agent 类型
- **THEN** SHALL 能通过配置文件指定 CLI 模板和输出格式
#### Scenario: http_pull mode — no adapter needed
- **WHEN** Agent 使用 http_pull 模式(如 OpenClaw/Jeeves
- **THEN** 不需要 CLI adapter 定义Agent 通过 HTTP API 自行交互
### Requirement: Adapter configuration
Agent 实例配置 SHALL 关联到具体主机,包含连接信息和执行参数。
#### Scenario: Remote Codex on host-worker-02
- **WHEN** 配置 host-worker-02 上的 Codex
- **THEN** 配置 SHALL 包含:`{host: "host-worker-02", agent_type: "codex-cli", max_concurrency: 2, model: "gpt-5.5", capabilities: ["code:rust"]}`
#### Scenario: Local Codex on same machine
- **WHEN** Agent 运行在 Orchestrator 同一台机器
- **THEN** SSH 可替换为本地 subprocess无需 SSH 开销

View file

@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: Remote host configuration
Orchestrator SHALL 支持配置多台远程主机,每台主机包含 SSH 连接信息和可用 Agent 列表。
#### Scenario: Host configuration format
- **WHEN** 配置远程主机
- **THEN** 配置 SHALL 包含:`{host_id, hostname, ssh_user, ssh_port, ssh_key_path, work_dir, agents: [{agent_type, max_concurrency}]}`
#### Scenario: Host with multiple agents
- **WHEN** 一台主机配置了多个 Agent例如同时有 Codex 和 Claude Code
- **THEN** Orchestrator SHALL 跟踪每个 Agent 的并发数,不超过 max_concurrency
### Requirement: Host health check
Orchestrator SHALL 能检查远程主机的 SSH 连通性和 Agent CLI 可用性。
#### Scenario: SSH connectivity check
- **WHEN** Orchestrator 检查 host-worker-02
- **THEN** SHALL 尝试 SSH 连接并执行 `echo ok`
- **AND** 连接失败时标记主机为 `unreachable`
#### Scenario: Agent CLI availability check
- **WHEN** Orchestrator 检查 host-worker-02 上的 Codex
- **THEN** SHALL 执行 `which codex``codex --version`
- **AND** CLI 不存在时标记该 Agent 为 `unavailable`
### Requirement: Host selection for task assignment
当任务的执行模式为 `ssh_cli`Orchestrator SHALL 选择合适的主机执行。
#### Scenario: Select host by capability and availability
- **WHEN** 任务需要 `code:rust` 能力
- **THEN** SHALL 选择配置了对应 Agent 且当前并发数未满的主机
- **AND** 多个候选主机时优先选择负载最低的
#### Scenario: No available host
- **WHEN** 没有可用主机匹配任务需求
- **THEN** 任务保持 `created` 状态,等待主机可用

View file

@ -0,0 +1,33 @@
## MODIFIED Requirements
### Requirement: Git branch as task execution unit
每个任务 SHALL 关联一个 Git 分支无论哪种执行模式。Agent 在该分支上工作,通过 PR 提交结果。
#### Scenario: ssh_cli mode — Orchestrator creates branch
- **WHEN** ssh_cli 模式任务开始执行
- **THEN** Orchestrator SHALL 在目标仓库创建分支 `task/{task_id}`(通过 SSH 在远程主机执行 `git checkout -b`
- **AND** 将分支名传入 Agent prompt
#### Scenario: http_pull mode — Agent creates branch
- **WHEN** http_pull 模式任务被 Agent 领取
- **THEN** Agent SHALL 自行创建分支 `task/{task_id}`
### Requirement: PR webhook as completion notification
无论哪种执行模式Agent 完成任务后 SHALL 通过 Forgejo PR 触发状态更新。
#### Scenario: ssh_cli mode — Agent creates PR via CLI
- **WHEN** ssh_cli 模式的 Agent 执行完成
- **THEN** Agent或 Orchestrator 通过 SSH SHALL push 到 task 分支并创建 PR
- **AND** Forgejo PR webhook 触发状态更新
#### Scenario: http_pull mode — same flow
- **WHEN** http_pull 模式的 Agent 执行完成
- **THEN** Agent SHALL push 到 task 分支并创建 PR
- **AND** Forgejo PR webhook 触发状态更新(与 ssh_cli 模式相同)
### Requirement: Push events as progress tracking
无论哪种执行模式Forgejo push webhook SHALL 作为进度信号。
#### Scenario: ssh_cli mode — push detected
- **WHEN** ssh_cli 模式执行中Agent push 到 task 分支
- **THEN** Orchestrator 更新 `last_activity_at`(与 http_pull 模式相同)

View file

@ -0,0 +1,63 @@
## ADDED Requirements
### Requirement: SSH CLI execution mode
Orchestrator SHALL 支持通过 SSH 在远程主机上执行 Agent CLI 命令。这是 subprocess CLI 的跨机等价Orchestrator 构造上下文、主动启动 Agent、等待完成、收集输出。
#### Scenario: Execute Codex on remote host
- **WHEN** 任务分配给 host-worker-02 上的 Codex Agent
- **THEN** Orchestrator SHALL 通过 SSH 连接 host-worker-02
- **AND** 执行 `codex exec --json '{structured_prompt}'`
- **AND** 等待命令完成,解析 JSON 输出为 receipt
#### Scenario: Execute Claude Code on remote host
- **WHEN** 任务分配给 host-worker-03 上的 Claude Code Agent
- **THEN** Orchestrator SHALL 通过 SSH 连接 host-worker-03
- **AND** 执行 `claude -p '{structured_prompt}' --output-format json --dangerously-skip-permissions`
- **AND** 等待命令完成,解析 JSON 输出为 receipt
#### Scenario: SSH connection fails
- **WHEN** Orchestrator 无法 SSH 连接到目标主机
- **THEN** 任务 SHALL 标记为 `failed`,记录连接错误
- **AND** 如果 retry_count < max_retries SHALL 自动重试
#### Scenario: Agent CLI returns non-zero exit code
- **WHEN** 远程 CLI 命令返回非零退出码
- **THEN** 任务 SHALL 标记为 `failed`,记录 stderr 输出
### Requirement: Structured prompt construction
Orchestrator SHALL 为每个任务构造结构化 prompt通过 CLI 参数传入 Agent。Prompt 内容包括:任务目标、约束条件、影响文件范围、验证命令。
#### Scenario: Prompt for code task
- **WHEN** 任务为代码实现类型
- **THEN** prompt SHALL 包含Issue 标题和描述、任务约束(从 Issue labels 提取)、预期输出格式、验证命令(`cargo test` / `npm test`
#### Scenario: Prompt for review task
- **WHEN** 任务为代码审查类型
- **THEN** prompt SHALL 包含PR diff、审查要点、审查结果格式要求
### Requirement: CLI command templates
每种 Agent 类型 SHALL 有可配置的 CLI 命令模板。模板支持变量替换:`{prompt}``{work_dir}``{task_id}``{branch}`
#### Scenario: Codex CLI template
- **WHEN** Agent 类型为 `codex-cli`
- **THEN** 命令模板 SHALL 为 `codex exec --json '{prompt}'`
- **AND**`{work_dir}` 目录下执行
#### Scenario: Custom template
- **WHEN** 用户配置自定义 CLI 模板
- **THEN** SHALL 支持变量替换:`{prompt}``{work_dir}``{task_id}``{branch}`
### Requirement: Output parsing
Orchestrator SHALL 解析 Agent CLI 的 JSON 输出为 receipt。
#### Scenario: Parse Codex JSON output
- **WHEN** Codex CLI 输出 JSON
- **THEN** SHALL 提取statuscompleted/failed、summary、artifactschanged files、PR URL、duration
#### Scenario: Parse Claude Code JSON output
- **WHEN** Claude Code CLI 输出 JSON
- **THEN** SHALL 提取status、summary、artifacts、duration
#### Scenario: Malformed output
- **WHEN** Agent 输出无法解析为有效 JSON
- **THEN** 任务 SHALL 标记为 `failed`,记录原始输出

View file

@ -0,0 +1,72 @@
## MODIFIED Requirements
### Requirement: Dual execution mode
Orchestrator SHALL 支持两种任务执行模式:
1. **ssh_cli**Orchestrator 主动通过 SSH 在远程主机执行 Agent CLI。控制权在 Orchestrator上下文由 Orchestrator 构造并传入。
2. **http_pull**Agent 自主通过 HTTP API 拉取任务、执行、提交 receipt。控制权在 Agent适用于外部 AgentOpenClaw/Jeeves、Hermes 等)。
每个任务 SHALL 有 `execution_mode` 字段,由任务来源决定(默认 `ssh_cli`)。
#### Scenario: Forgejo Issue → ssh_cli task
- **WHEN** 任务从 Forgejo Issue 创建
- **THEN** execution_mode SHALL 为 `ssh_cli`
- **AND** Orchestrator SHALL 选择主机并主动执行
#### Scenario: External Agent using http_pull
- **WHEN** 外部 AgentJeeves需要执行任务
- **THEN** 任务 execution_mode SHALL 为 `http_pull`
- **AND** Agent 通过 `POST /api/v1/tasks/dequeue` 拉取并自行执行
### Requirement: Agent task dequeue (pull model — for http_pull mode only)
`POST /api/v1/tasks/dequeue` SHALL 仅适用于 `http_pull` 模式的任务。`ssh_cli` 模式的任务 SHALL 由 Orchestrator 直接调度,不经过 dequeue。
#### Scenario: Agent dequeues an http_pull task
- **WHEN** Agent 发送 `POST /api/v1/tasks/dequeue`
- **THEN** SHALL 仅返回 execution_mode = `http_pull` 的任务
- **AND** 排除已被 Orchestrator 调度的 ssh_cli 任务
### Requirement: Agent task status update (for http_pull mode)
`POST /api/v1/tasks/{task_id}/status` SHALL 适用于 `http_pull` 模式。`ssh_cli` 模式的状态 SHALL 由 Orchestrator 直接管理。
#### Scenario: ssh_cli task status managed by Orchestrator
- **WHEN** 任务 execution_mode = `ssh_cli`
- **THEN** 状态变更 SHALL 由 Orchestrator 在 SSH 执行过程中自动更新
- **AND** Agent 不需要调用 status update API
### Requirement: Single task detail query
Orchestrator SHALL 提供 `GET /api/v1/tasks/{task_id}` 返回单个任务详情,两种执行模式通用。
#### Scenario: Query task detail
- **WHEN** 发送 `GET /api/v1/tasks/org/repo#42`
- **THEN** 返回任务完整信息 JSON包含 execution_mode、assigned_hostssh_cli或 assigned_agent_idhttp_pull
### Requirement: Agent authentication (http_pull mode)
`http_pull` 模式的 Agent 调用 API 时 SHALL 携带 token。`ssh_cli` 模式不需要 Agent 认证(由 Orchestrator 直接管理)。
#### Scenario: ssh_cli mode — no Agent auth needed
- **WHEN** 任务 execution_mode = `ssh_cli`
- **THEN** Agent 不参与 API 调用,无需认证
#### Scenario: http_pull mode — token required
- **WHEN** 任务 execution_mode = `http_pull`
- **THEN** Agent SHALL 携带有效 token 调用 API
### Requirement: Non-PR task completion endpoint
对于不产生 PR 的任务research、review 等),无论哪种执行模式,都 SHALL 可通过 `POST /api/v1/tasks/{task_id}/complete` 显式完成。
#### Scenario: ssh_cli mode — auto-complete from CLI output
- **WHEN** 任务 execution_mode = `ssh_cli` 且 Agent CLI 输出包含成功 receipt
- **THEN** Orchestrator SHALL 自动解析输出并完成任务
#### Scenario: http_pull mode — Agent calls complete
- **WHEN** 任务 execution_mode = `http_pull`
- **THEN** Agent SHALL 调用 `POST /api/v1/tasks/{task_id}/complete` + receipt
### Requirement: Review loop limit
无论哪种执行模式,任务的 review 循环 SHALL 有最大次数限制。
#### Scenario: ssh_cli review loop
- **WHEN** ssh_cli 模式下 review 不通过
- **THEN** Orchestrator SHALL 自动重新调度 Agent 修复
- **AND** 超过 max_retries 时标记 failed