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,107 @@
## Context
当前 adapter interface 基于 spawn 本地进程模式无法满足跨机协同的核心目标。实际使用中暴露了通知机制不可靠的问题Codex 完成任务后 Jeeves 收不到通知)。
核心认知转变:
- **Orchestrator 不调用 AgentAgent 调用 Orchestrator**pull 模型)
- **Git/Forgejo 是状态追踪的 source of truth**PR 生命周期 = 任务生命周期)
- **通知 = Git 事件**push → 进度信号PR opened → 完成通知PR merged → receipt 验证)
## Goals / Non-Goals
**Goals:**
- Agent 通过 HTTP API 主动拉取任务、更新状态、提交 receipt任何机器、任何语言
- 利用 Forgejo PR webhook 作为可靠的状态追踪和通知机制
- 消除"Agent 完成但无人知道"的问题
- Agent client 可以是任何语言实现curl 就能交互)
**Non-Goals:**
- 不实现 Agent 侧的 client SDKAgent 自己决定用什么语言/方式调用 HTTP API
- 不实现 Orchestrator → Agent 的主动推送pull 模型足够Phase 2 可加 SSE
- 不修改 Forgejo webhook 已有的实现(复用现有 `POST /api/v1/webhooks/forgejo`
## Decisions
### Decision 1: Pull 模型Agent 主动拉取任务)
**选择**: Agent 通过 `POST /api/v1/tasks/dequeue` 主动拉取
**理由**:
- 跨机场景下 Orchestrator 无法主动连接 Agent防火墙、NAT、离线
- Agent 最清楚自己什么时候有空
- Pull 模型天然负载均衡——空闲 Agent 自然拉更多任务
- 无需长连接、无需消息队列
**替代方案**:
- Push 模型Orchestrator 推送):需要 Agent 暴露端点,复杂度高
- 消息队列NATS/Redis增加基础设施依赖
### Decision 2: Forgejo PR lifecycle 作为通知机制
**选择**: 利用 Git 事件push → PR opened → PR merged作为任务状态追踪
**理由**:
- Forgejo webhook 已经实现,可靠性由 Forgejo 保证
- PR 本身就是 code review 流程——天然对应任务的"完成→验证"流程
- 解决了实际遇到的通知丢失问题:即使 Agent 直接通知失败Forgejo webhook 仍然会触发
- PR merged = 自动 receipt 验证PR 存在且被 merge = 代码确实被接受)
**替代方案**:
- SSE/WebSocket 推送:需要保持长连接,跨网络不可靠
- Agent 回调 URL需要 Agent 暴露端点
- 轮询:可行但延迟高,且不解决"Agent 完成后通知谁"的问题
### Decision 3: Registry token 认证
**选择**: Agent 注册后获得 token后续请求需携带
**理由**:
- 轻量级,不需要 OAuth 或 JWT
- 防止未授权的 Agent 领取任务
- token 在重新注册时刷新
## Risks / Trade-offs
- **[Pull 延迟] Agent 需要定期 dequeue 轮询** → Agent 可以在 heartbeat 周期内顺便 dequeue不增加额外开销
- **[PR 必需] 强制要求 Agent 创建 PR** → 非 PR 任务通过 `/complete` 端点完成,两条路径在 receipt 验证层汇合
- **[Forgejo 单点] Forgejo 挂了通知就断了** → Forgejo 本身是 Git source of truth挂了整个流程都停可接受
- **[死循环] Review 循环可能无限** → max_retries 限制 + review_count 跟踪
## Key Design Principles (from community best practices)
基于 ChatGPT 讨论和社区最佳实践的几个核心原则:
1. **Agent 是纯函数 worker**:输入 task + artifact输出 artifact + state change。不保留跨任务状态。
2. **状态机驱动,不对话驱动**:所有 Agent 只对共享状态SQLite + Forgejo读写由 Orchestrator loop 推进流程
3. **结构化 handoff**Agent 之间不传 chat history只传结构化 artifactreceipt、plan、review
4. **Git worktree 隔离**:每个 task 独立分支,避免并发冲突
5. **Deterministic verification**:不依赖 Agent 自我判断,用 `cargo test` / `npm test` 等确定性验证
6. **80/20 原则**80% 自动运行20% 人类介入(失败、架构决策、优先级调整)
7. **外部调度 + 内部自治**agent-fleet 只管理任务状态和 artifact 流转Agent 的执行 runtimehooks、compaction、sandbox、tool loop、session 管理)由 Agent 自己负责。agent-fleet 是 KubernetesAgent 是 Pod。
8. **不替代 Agent runtime**Claude Code / Codex / OpenCode 的价值在于它们的 runtime不是模型本身。agent-fleet 不重新实现 tool loop、context compaction、patch management 等。
基于 ChatGPT 讨论和社区最佳实践的几个核心原则:
1. **Agent 是纯函数 worker**:输入 task + artifact输出 artifact + state change。不保留跨任务状态。
2. **状态机驱动,不对话驱动**:所有 Agent 只对共享状态SQLite + Forgejo读写由 Orchestrator loop 推进流程
3. **结构化 handoff**Agent 之间不传 chat history只传结构化 artifactreceipt、plan、review
4. **Git worktree 隔离**:每个 task 独立分支,避免并发冲突
5. **Deterministic verification**:不依赖 Agent 自我判断,用 `cargo test` / `npm test` 等确定性验证
6. **80/20 原则**80% 自动运行20% 人类介入(失败、架构决策、优先级调整)
## Migration Plan
1. 新增 `POST /api/v1/tasks/dequeue` 端点
2. 新增 `POST /api/v1/tasks/{task_id}/status` 端点
3. 新增 `GET /api/v1/tasks/{task_id}` 端点
4. 扩展 Forgejo webhook 处理:支持 `pull_request``push` 事件
5. 重写 `src/adapters/mod.rs`:从 AgentAdapter trait 改为 Agent Protocol 描述文档
6. 新增 token 认证中间件
7. 更新测试
## Open Questions
_(resolved — 见下方)_
- ~~非 PR 任务research、review如何触发完成通知~~ → 使用 `POST /api/v1/tasks/{id}/complete` + receipt与 PR 路径在 receipt 验证层汇合
- ~~Token 认证是否需要在 Phase 1 实现?~~ → 必须在 Phase 1 实现,否则无法安全对接远程 Agent