完成 Editor Agent P2 持久任务与运行时收口
新增 Web Project runtime job、持久日志、lease、取消、expired、stale 和 active preview guard 状态机 接入 api-server Web Project runtime worker 与 TempDirBuildRuntime 构建执行链路 补齐 SpacetimeDB procedure、spacetime-client facade、shared contracts 和前端 web-project client 契约 更新 /editor/agent 的 runtime job 恢复、日志回填、SSE 重连、取消按钮和 active preview 刷新恢复 新增 P2 dev smoke 脚本,并让完整 npm run dev 默认以 all 角色启动 P2 worker 补充 P2 自动化测试、浏览器 smoke 验收记录、开发运维文档和 Hermes 踩坑记忆
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
- [【玩法创作】创作流程统一总计划-2026-05-30.md](./【玩法创作】创作流程统一总计划-2026-05-30.md):创作入口、统一创作页、统一生成页、结果页、发布、作品架、广场和运行态的阶段计划、进度记录、并行波次和可直接派发的任务包。
|
||||
- [【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md](./【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md):`/editor/agent` Mock Agent P1 的可执行波次、任务包、验收门禁和覆盖矩阵,完整承接技术落地计划。
|
||||
- [【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md](./【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md):`/editor/agent` P2 的持久 runtime job、worker / lease、日志恢复和 SandboxRuntime 抽象计划。
|
||||
- [【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md](./【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md):`/editor/agent` P2 后加速远程 Container 与真实 Agent 的 MVP 计划,第一版采用 Agent + workspace 同 Remote Container,保留 ShellPolicy / ToolBridge / SandboxRuntime 接口以便后续拆分隔离。
|
||||
|
||||
## 维护规则
|
||||
|
||||
|
||||
@@ -92,6 +92,541 @@ sandbox = 某个 snapshot 展开后的隔离执行环境
|
||||
| P2e | 日志和恢复 | 日志分页、SSE 重连、刷新恢复、取消和 stale 验收 |
|
||||
| P2f | 文档与门禁 | 更新架构文档、验收清单和 smoke 流程 |
|
||||
|
||||
## 可执行落地步骤
|
||||
|
||||
P2 落地按下面步骤推进。每一步都必须能独立验证,不把“建表、worker、runner、前端恢复、SandboxRuntime”混在同一个大提交里。
|
||||
|
||||
### P2-00:基线确认与任务入口冻结
|
||||
|
||||
目标:
|
||||
|
||||
- 确认 P1 的 project、snapshot、preview build、runner、artifact、preview gateway 和前端入口仍可用。
|
||||
- 冻结 P2 不扩权:不接真实 Shell、不开放任意 npm 依赖、不把 Docker workspace 当事实源。
|
||||
|
||||
改动范围:
|
||||
|
||||
- 只允许更新 P2 计划、P1 交接说明和验收清单。
|
||||
- 不改 schema,不改运行时代码。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p web-project-runner --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- P1 happy path 和“破坏构建保留上一版预览”测试仍通过。
|
||||
- 文档明确 P2 只做任务生命周期和 SandboxRuntime 抽象。
|
||||
|
||||
落地记录:
|
||||
|
||||
- 状态:已完成,时间 `2026-06-16`。
|
||||
- 已确认本步只做基线验证和文档冻结,不改 schema、不改运行时代码、不进入 worker / SandboxRuntime 实现。
|
||||
- `cargo test -p api-server web_project --manifest-path server-rs/Cargo.toml` 通过:5 个用例通过;存在 3 个既有 `dead_code` warning,不阻断 P2-00。
|
||||
- `cargo test -p web-project-runner --manifest-path server-rs/Cargo.toml` 通过:10 个用例通过。
|
||||
- `npm run test -- src/services/web-project src/components/editor/agent --reporter verbose` 通过:4 个测试文件、18 个用例通过;本机初次运行缺少 `node_modules/.bin/vitest`,已执行 `npm install` 恢复本地依赖后重跑通过。
|
||||
- `npm run check:encoding` 通过。
|
||||
|
||||
### P2-01:Runtime Job Schema 与迁移落点
|
||||
|
||||
目标:
|
||||
|
||||
- 在 SpacetimeDB 中新增 `web_project_runtime_job` 和 `web_project_runtime_job_log`。
|
||||
- 字段、索引、默认值、表目录、`migration.rs` 和生成绑定一次性对齐。
|
||||
|
||||
改动范围:
|
||||
|
||||
- `server-rs/crates/spacetime-module` 表结构、迁移和表目录。
|
||||
- `server-rs/crates/spacetime-client` / generated bindings 只做生成结果承接。
|
||||
- 不接 api-server 路由,不启动 worker。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
npm run spacetime:generate
|
||||
npm run check:spacetime-schema
|
||||
cargo test -p spacetime-module web_project --manifest-path server-rs/Cargo.toml
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- 新表通过 schema guard。
|
||||
- 新增字段若落在已有表,必须位于结构体末尾并带明确默认值;优先避免改已有表。
|
||||
|
||||
落地记录:
|
||||
|
||||
- 状态:已完成,时间 `2026-06-16`。
|
||||
- 已新增 `web_project_runtime_job` 与 `web_project_runtime_job_log` 两张 SpacetimeDB 表,未修改既有 `web_project_preview_build` 字段,避免引入已有表迁移风险。
|
||||
- 已同步 `server-rs/crates/spacetime-module/src/migration.rs`,两张 runtime job 表随业务迁移包导入导出;`web_project_service_identity` 继续作为环境级服务授权表排除在迁移数据外。
|
||||
- 已更新表目录,明确 runtime job 只描述执行生命周期,不替代 `web_project_snapshot` 作为工程文件事实源。
|
||||
- 已执行 `npm run spacetime:generate` 并生成 Rust bindings;首次运行因编译超时被工具截断,第二次长超时运行完成。SpacetimeDB CLI 自带格式化在 Windows 长路径下报 `os error 206`,脚本已按短路径分批 rustfmt 兜底并成功退出。
|
||||
- `npm run check:spacetime-schema` 通过。
|
||||
- `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 通过。
|
||||
- `npm run check:encoding` 通过。
|
||||
- 2026-06-17 复核后补齐原生测试口径:`spacetime-module` 已通过 `#[cfg(all(test, not(target_arch = "wasm32")))]` 引入 test-only SpacetimeDB ABI 桩,解决 Windows native `cargo test` 链接阶段缺少宿主符号的问题。`cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml` 已可执行到测试体并通过:6 个 runtime job 纯逻辑用例通过。该桩只服务不访问 SpacetimeDB 宿主 ABI 的 helper 单测,真实 module 行为仍以 wasm target check、bindings 生成、schema guard 和 SpacetimeDB publish/dev 验证为准。
|
||||
|
||||
### P2-02:Runtime Job 状态机 Reducer / Procedure
|
||||
|
||||
目标:
|
||||
|
||||
- 实现 create、claim、renew、complete、fail、cancel、mark stale / expired、append log 的原子流转。
|
||||
- 所有 owner 校验、snapshot 校验和 lease 校验都在 SpacetimeDB transaction 内完成。
|
||||
|
||||
改动范围:
|
||||
|
||||
- `spacetime-module` 的 Web Project runtime job 领域逻辑。
|
||||
- 不调用 runner,不写 artifact,不改前端。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml
|
||||
npm run check:spacetime-schema
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- `queued -> running -> succeeded` 正常流转。
|
||||
- 错误 `lease_token` 不能 complete / fail。
|
||||
- lease 过期后可重新 claim,`attempt` 递增。
|
||||
- `failed / cancelled / expired / stale` 不允许再推进 active preview。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- 状态机测试覆盖所有终态。
|
||||
- 旧 snapshot job 的成功回写在模块层被拒绝或降级为 stale。
|
||||
|
||||
落地记录:
|
||||
|
||||
- 状态:已完成,时间 `2026-06-16`。
|
||||
- 已在 `server-rs/crates/spacetime-module/src/web_project.rs` 按现有 Web Project `procedure + try_with_tx + service identity` 风格新增 runtime job 状态机 procedure。
|
||||
- 已覆盖 `create / get / claim / renew / complete / fail / cancel / stale / expire / append log` 的事务内流转;所有入口先校验 Web Project service identity,再在事务内校验 owner、project、snapshot、preview build 关联和 worker lease。
|
||||
- `claim` 只领取 `queued` 或 lease 已过期的 `running`,并写入 `worker_id`、`lease_token`、`lease_expires_at`、`started_at` 和递增后的 `attempt`。
|
||||
- `renew / complete / fail / append log` 在提供 worker lease 时必须匹配当前 `worker_id + lease_token`,且 lease 未过期。
|
||||
- `complete` 在 job 的 `snapshot_id` 不等于项目 `active_snapshot_id` 时不推进成功,直接将 job 降级为 `stale`;新 snapshot 保存时也会将同项目未终态 runtime job 标记为 `stale`。
|
||||
- `queued` 取消直接进入 `cancelled`;`running` 取消先记录 `cancel_requested_at`,等待后续 worker 阶段收敛。
|
||||
- `append log` 持久化到 `web_project_runtime_job_log`,按 `job_id + sequence` 做重复 sequence 防护,日志 level 和 message 做长度裁剪。
|
||||
- 已重新执行 `npm run spacetime:generate`,生成 runtime job 输入、快照、procedure result 和 procedure caller 绑定。
|
||||
- `npm run check:spacetime-schema` 通过。
|
||||
- `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 通过。
|
||||
- `cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml` 通过。
|
||||
- `npm run check:encoding` 通过。
|
||||
- `git diff --check` 通过。
|
||||
- 2026-06-17 已补齐 `server-rs/crates/spacetime-module/src/test_stubs.rs`,为 Windows native 测试链接阶段提供 25 个 SpacetimeDB WASM 宿主 ABI 符号桩;`console_log` 静默 no-op,其余桩仅用于暴露误调用。`cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml` 已通过:6 个测试通过。边界:这些用例只覆盖 runtime job helper 纯逻辑,不验证 `ctx.db`、表扫描 / 写入、procedure transaction 或真实 SpacetimeDB runtime。
|
||||
|
||||
### P2-03:Spacetime Client Facade 与 API 契约
|
||||
|
||||
目标:
|
||||
|
||||
- 在 `spacetime-client` 封装 runtime job facade,api-server 不直接拼 reducer / procedure 细节。
|
||||
- 定义 HTTP DTO:创建 job、查询 job、分页日志、取消 job、未终态 job 列表。
|
||||
|
||||
改动范围:
|
||||
|
||||
- `spacetime-client` Web Project facade。
|
||||
- `shared-contracts` / `packages/shared` 中必要 DTO。
|
||||
- `api-server` 可以加 handler skeleton,但不触发 runner。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p spacetime-client web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project_runtime_job --manifest-path server-rs/Cargo.toml
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- API handler 能创建 queued job、读取状态和分页日志。
|
||||
- API 不直接修改 SpacetimeDB 表,只经 facade。
|
||||
|
||||
落地记录:
|
||||
|
||||
- 状态:已完成,时间 `2026-06-17`。
|
||||
- 已在 `spacetime-module` 追加两个窄读 procedure:`list_open_web_project_runtime_jobs_and_return` 与 `list_web_project_runtime_job_logs_and_return`,用于 P2-03 API 的未终态 job 列表和日志分页;未修改 runtime job 表结构。
|
||||
- 已重新执行 `npm run spacetime:generate`,生成 `WebProjectRuntimeJobListOpenInput`、`WebProjectRuntimeJobListLogsInput` 和对应 procedure caller bindings。Windows 本机本次 release 生成约 9 分钟,`wasm-opt` 缺失与 SpacetimeDB CLI 长路径格式化失败均由脚本兜底处理,脚本最终成功退出。
|
||||
- 已在 `server-rs/crates/spacetime-client/src/web_project.rs` 封装 runtime job facade:`create / get / list open / claim / renew / complete / fail / cancel / stale / expire / append log / list logs`,api-server 不直接调用 generated procedure。
|
||||
- 已在 `shared-contracts` 与 `packages/shared` 定义 `WebProjectRuntimeJob`、`WebProjectRuntimeJobLog`、创建请求、单条响应、未终态列表响应和日志分页响应;HTTP DTO 不下发 worker `leaseToken`。
|
||||
- 已在 `api-server` 增加最小 runtime job skeleton:
|
||||
- `POST /api/runtime/web-project/projects/{project_id}/runtime-jobs`
|
||||
- `GET /api/runtime/web-project/projects/{project_id}/runtime-jobs`
|
||||
- `GET /api/runtime/web-project/runtime-jobs/{job_id}`
|
||||
- `POST /api/runtime/web-project/runtime-jobs/{job_id}/cancel`
|
||||
- `GET /api/runtime/web-project/runtime-jobs/{job_id}/logs`
|
||||
- 本步未改现有 `POST /api/runtime/web-project/projects/{projectId}/preview-builds` 行为,仍不触发 worker、不迁移 runner、不改前端主流程;这些留到 P2-04 之后。
|
||||
- `cargo check -p spacetime-module --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml` 通过。
|
||||
- `cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml` 通过。
|
||||
- `cargo check -p api-server --manifest-path server-rs/Cargo.toml` 通过;仍存在 3 个既有 puzzle `dead_code` warning。
|
||||
- `cargo test -p spacetime-client web_project --manifest-path server-rs/Cargo.toml` 通过:2 个相关用例通过。
|
||||
- `cargo test -p api-server web_project_runtime_job --manifest-path server-rs/Cargo.toml` 通过:2 个相关用例通过;仍存在 3 个既有 puzzle `dead_code` warning。
|
||||
- `npm run typecheck` 通过。
|
||||
- `npm run check:spacetime-schema` 通过。
|
||||
- `npm run check:encoding` 通过。
|
||||
- `git diff --check` 通过。
|
||||
|
||||
### P2-04:Preview Build 入队化
|
||||
|
||||
目标:
|
||||
|
||||
- 保持现有 `POST /api/runtime/web-project/projects/{projectId}/preview-builds` 外部入口。
|
||||
- 将 preview build 从请求内同步触发 runner 改为创建 `web_project_runtime_job(kind=preview_build)`。
|
||||
- 返回值带 `jobId` 和当前 build 状态,前端仍不拼 preview URL。
|
||||
|
||||
改动范围:
|
||||
|
||||
- `api-server` preview build handler。
|
||||
- 前端 service 类型只承接 `jobId`,不改 UI 主流程。
|
||||
- runner 仍可暂时不接 worker。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server web_project_preview_build --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project --reporter verbose
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- 创建 preview build 后能查到 queued runtime job。
|
||||
- 重复创建 build 不破坏 active preview。
|
||||
- API 仍兼容 P1 前端调用路径。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- 没有 worker 时,前端能看到“已入队 / 构建中”的后端状态,不假装成功。
|
||||
|
||||
落地记录(2026-06-17):
|
||||
|
||||
- 已将 `POST /api/runtime/web-project/projects/{projectId}/preview-builds` 从请求内 runner 执行改为“创建 preview build + 创建 `web_project_runtime_job(kind=preview_build)`”。
|
||||
- 该入口不再抢占 api-server 进程内 preview build slot,也不再调用 `spawn_preview_build_task`;旧 runner helper 保留为 P2-05 worker 接入时复用的过渡代码。
|
||||
- `WebProjectPreviewBuildResponse` 保留 `build` 字段,并新增可选 `runtimeJob` 字段;P1 前端继续只消费 `build`,不拼接 preview URL。
|
||||
- 若 runtime job 入队失败,api-server 会尽力把刚创建的 preview build 标记为 `failed` 并推送失败 SSE,避免留下没有 worker 可消费的永久 queued build。
|
||||
- 本步不推进 `web_project.active_preview_build_id`,active preview 仍只应在后续 worker 成功写回 artifact / preview token 后推进。
|
||||
|
||||
已验证:
|
||||
|
||||
```bash
|
||||
cargo check -p api-server --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project --reporter verbose
|
||||
npm run test -- src/components/editor/agent/WebProjectAgentEditorPage.test.tsx --reporter verbose
|
||||
npm run typecheck
|
||||
git diff --check
|
||||
```
|
||||
|
||||
备注:`cargo check -p api-server` 仍有 3 个既有 puzzle `dead_code` warning,和 P2-04 无关。
|
||||
|
||||
### P2-05:Runtime Worker 骨架与 Lease Loop
|
||||
|
||||
目标:
|
||||
|
||||
- 新增 Web Project runtime worker 角色,能 claim job、写 running 日志、续租、无构建 dry-run 完成或失败。
|
||||
- worker 进程身份和 `worker_id` 可观测。
|
||||
|
||||
改动范围:
|
||||
|
||||
- `api-server` 或独立 crate 中新增 worker loop,按现有工程进程组织方式接入。
|
||||
- 不复用 `external_generation_job`。
|
||||
- 不接真实 runner。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server web_project_runtime_worker --manifest-path server-rs/Cargo.toml
|
||||
cargo check -p api-server --manifest-path server-rs/Cargo.toml
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- worker 能 claim queued job。
|
||||
- renew 必须使用当前 `worker_id + lease_token`。
|
||||
- dry-run 成功 job 进入 `succeeded` 并产生日志。
|
||||
- dry-run 失败 job 进入 `failed` 并保留错误摘要。
|
||||
|
||||
落地记录(2026-06-17):
|
||||
|
||||
- 已在 `api-server` 内新增 `web-project-runtime-worker` 进程角色,配置项为 `GENARRATIVE_WEB_PROJECT_RUNTIME_WORKER_ID`、`GENARRATIVE_WEB_PROJECT_RUNTIME_WORKER_CONCURRENCY`、`GENARRATIVE_WEB_PROJECT_RUNTIME_WORKER_POLL_INTERVAL_MS`、`GENARRATIVE_WEB_PROJECT_RUNTIME_WORKER_LEASE_SECONDS` 和 `GENARRATIVE_WEB_PROJECT_RUNTIME_WORKER_DRY_RUN_RESULT`。
|
||||
- worker 通过 `spacetime-client` facade 领取 `web_project_runtime_job`,按当前 `worker_id + lease_token` 追加持久日志、续租、完成或失败;`all` 角色会与 HTTP、external generation worker 同进程并行运行。
|
||||
- 默认 dry-run 结果为 `fail`,不会为 `preview_build` 伪造 artifact、preview token 或 preview URL;显式配置 `success` 时只允许无 preview build 绑定的 runtime job 完成,用于骨架验证。
|
||||
- dry-run 失败会先失败 runtime job,再 best-effort 将绑定的 `web_project_preview_build` 标记 failed 并广播 SSE。两次 procedure 存在部分成功风险;P2-06 或后续可考虑补组合 procedure。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- worker crash 模拟后,lease 过期 job 可被新 worker 重领或被明确标记 `expired`。
|
||||
|
||||
### P2-06:TempDirBuildRuntime 接入 Runner
|
||||
|
||||
目标:
|
||||
|
||||
- 抽出第一版 `SandboxRuntime` / `TempDirBuildRuntime`,把 P1 runner 接到 worker。
|
||||
- worker hydrate snapshot,调用 runner,写 artifact、preview token 和 preview URL。
|
||||
|
||||
改动范围:
|
||||
|
||||
- web-project runner 调用适配。
|
||||
- api-server worker 执行面。
|
||||
- 不引入 Docker,不开放 Shell。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p web-project-runner --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project_runtime_worker --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project --manifest-path server-rs/Cargo.toml
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- “蓝色计数按钮页面” job 构建成功。
|
||||
- job 日志包含 hydrate、build、artifact、preview 四类关键阶段。
|
||||
- preview gateway 返回的 iframe 页面可加载。
|
||||
|
||||
落地记录(2026-06-17):
|
||||
|
||||
- 已新增 `web_project_preview_runtime` 内部模块,形成第一版 `SandboxRuntime` trait 与 `TempDirBuildRuntime` 实现;当前实现继续通过受控子进程调用 `web-project-runner`,不引入 Docker,不开放 Shell。
|
||||
- `web-project-runtime-worker` 对绑定 `preview_build_id` 的 `preview_build` job 不再走 dry-run,而是追加 hydrate / build 日志、hydrate snapshot、调用 `TempDirBuildRuntime`,并在 runner 成功时写入 artifact、preview token 和 preview URL。
|
||||
- 成功路径先通过 `complete_web_project_runtime_job` 写 runtime job,并借用现有 `snapshot_id == active_snapshot_id` 校验;如果返回 `stale`,worker best-effort 将 linked preview build 标记 stale,不推进 preview build succeeded。
|
||||
- runner 失败或缺少 artifact 时,worker 将 preview build 标记 failed 并失败当前 runtime job;失败不会生成新的 preview token / URL,也不会覆盖上一版 active preview。
|
||||
- P2-06 仍保留 runtime job 与 preview build 两次 procedure 写回,worker 在 runner 结束后先做一次最终续租,再进入终态写回,降低 lease 过期导致的部分写回风险;彻底收口为单事务组合 procedure 或更强 reconcile 放到 P2-07。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- runner 失败只让当前 job failed,不覆盖上一版 preview。
|
||||
|
||||
### P2-07:Active Preview Guard 与 Stale 收口
|
||||
|
||||
目标:
|
||||
|
||||
- 成功 job 推进 active preview 前必须校验 `snapshot_id == project.active_snapshot_id`。
|
||||
- 新 snapshot 产生后,旧 snapshot 未终态 job 标记为 stale,或至少保证旧 job 不能推进 active preview。
|
||||
|
||||
改动范围:
|
||||
|
||||
- SpacetimeDB 状态机和 api-server 成功回写路径。
|
||||
- 前端只显示 stale / failed / running 状态,不自行判断业务真相。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project_preview_build --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- 连续提交两次,第二次 snapshot 成为 active。
|
||||
- 第一次 job 后完成时不能覆盖第二次 active preview。
|
||||
- stale job 的日志仍可读取。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- “旧 job 成功覆盖新 preview”必须有自动测试拦截。
|
||||
|
||||
落地记录(2026-06-17):
|
||||
|
||||
- 已新增 `complete_web_project_preview_build_runtime_job_and_return` 组合 procedure;runner 成功后的 runtime job 终态、linked preview build 终态和 `web_project.active_preview_build_id` 推进在同一个 SpacetimeDB transaction 中完成。
|
||||
- 组合 procedure 在写入 succeeded 前重新校验 `job.snapshot_id == web_project.active_snapshot_id`;若旧 job 在新 snapshot 成为 active 后才完成,则 runtime job 与 preview build 一起写成 `stale`,不写 preview token / URL,也不改 active preview 指针。
|
||||
- `spacetime-client` 已封装组合 facade,并新增 `WebProjectRuntimeJobPreviewBuildMutationRecord` 映射;`web-project-runtime-worker` 成功路径改为生成 finish plan 后调用组合 facade,不再执行“complete job 后 best-effort update preview build succeeded”的两步写回。
|
||||
- 前端不新增业务判断,继续只在后端返回 `build.status=succeeded && previewUrl` 时切换 iframe;已补 `stale` SSE / 页面用例锁住 stale 不覆盖旧 iframe。
|
||||
- 已重新执行 `npm run spacetime:generate`,生成组合 procedure input / result / caller bindings;Windows 长路径格式化失败由脚本短路径 rustfmt 兜底成功。
|
||||
|
||||
已验证:
|
||||
|
||||
```bash
|
||||
npm run spacetime:generate
|
||||
cargo check -p spacetime-module --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml
|
||||
cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml
|
||||
cargo check -p api-server --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p spacetime-client web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project_runtime_worker --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project/webProjectSse.test.ts src/components/editor/agent/WebProjectAgentEditorPage.test.tsx --reporter verbose
|
||||
```
|
||||
|
||||
备注:`cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml` 当前依赖 test-only ABI 桩,仅用于验证不触碰 SpacetimeDB 宿主 API 的 runtime job 纯逻辑;它不能替代 wasm target check、bindings 生成、schema guard 或 SpacetimeDB publish/dev 对真实 module 行为的验证。
|
||||
|
||||
### P2-08:取消、Expired 与 Crash 恢复
|
||||
|
||||
目标:
|
||||
|
||||
- 支持用户取消 queued / running job。
|
||||
- running job crash 后通过 lease 过期重领,或进入确定 `expired`。
|
||||
- 取消请求可恢复、可查询、可展示。
|
||||
|
||||
实施进度(2026-06-17):
|
||||
|
||||
- 已在 SpacetimeDB runtime job 终态写回中加入取消优先级:`complete / fail / complete preview build` 在同一事务内发现 `cancel_requested_at` 后一律写成 `cancelled`,清理 worker lease,不生成 preview token / URL,也不推进 `active_preview_build_id`。
|
||||
- 已补 worker loop 的取消检测:worker 每次 heartbeat / 最终续租都会读取续租后的 job,若发现取消请求则停止后续成功或失败写回,并把 linked preview build 收敛为 `cancelled`。
|
||||
- 已补 queued cancel 的 HTTP 同步收敛:未开始任务取消后,api-server 会同步把 linked preview build 标记为 `cancelled` 并广播现有 preview build SSE。
|
||||
- 已补前端最小取消入口:`/editor/agent` 创建 preview build 时保留 `runtimeJob`,queued / running job 显示“取消”按钮;刷新时读取 open runtime jobs 恢复未终态任务上下文。
|
||||
- 当前 TempDir runner 仍不是强杀式取消;running job 的取消语义是“请求已记录,worker 在 heartbeat 或 runner 返回后的最终续租处收敛”。后续 Docker / gVisor / microVM runtime 接入时再把强杀子进程 / 容器作为执行面能力补齐。
|
||||
|
||||
改动范围:
|
||||
|
||||
- job cancel API:已存在,并补齐 queued linked preview build 的同步 `cancelled` 写回。
|
||||
- worker loop 的 cancel 检查:已完成 heartbeat / 最终续租检测。
|
||||
- 前端只加最小取消入口或状态展示,不做大 UI 重设计:已完成。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project_runtime_worker --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
本轮已完成验证:
|
||||
|
||||
```bash
|
||||
cargo check -p api-server --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p spacetime-module runtime_job --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- queued job 取消后直接 `cancelled`。
|
||||
- running job 记录 `cancel_requested_at`,worker 收敛后 `cancelled`。
|
||||
- crash 模拟后 job 不永久停在 running:沿用既有 running lease 过期后可重领,以及 `expire_web_project_runtime_job` 的显式 expired procedure。
|
||||
- `cancelled / expired` 不覆盖 active preview。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- 所有非成功终态都不影响上一版成功 preview。
|
||||
|
||||
### P2-09:日志分页、SSE 重连与刷新恢复
|
||||
|
||||
目标:
|
||||
|
||||
- worker 日志持久化后,API 支持 `jobId + cursor/sequence` 分页读取。
|
||||
- SSE 断线后,前端先补缺失日志,再重新订阅。
|
||||
- 刷新 `/editor/agent?projectId=...` 后恢复 project、active snapshot、active preview、未终态 jobs 和日志。
|
||||
|
||||
改动范围:
|
||||
|
||||
- API 日志分页和 SSE 事件源。
|
||||
- 前端 `sseStream.ts` 复用接入。
|
||||
- `/editor/agent` 恢复逻辑。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server web_project_runtime_logs --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
```
|
||||
|
||||
最小用例:
|
||||
|
||||
- 日志分页按 sequence 递增。
|
||||
- SSE 断线重连后不重复、不丢日志。
|
||||
- 页面刷新后显示后端真实 job 状态和 active preview。
|
||||
|
||||
进入下一步门槛:
|
||||
|
||||
- 前端没有用本地临时状态伪造 build 真相。
|
||||
|
||||
落地记录(2026-06-17):
|
||||
|
||||
- 已复用既有 `GET /api/runtime/web-project/runtime-jobs/{jobId}/logs?afterSequence=&limit=` 日志分页接口,并补 `web_project_runtime_logs_paginates_by_sequence` 单元测试,锁住 API 层多取一条判断 `hasMore`、按 requested limit 截断和 `nextAfterSequence` 指向最后返回日志 sequence 的分页语义。
|
||||
- `/editor/agent` 新增 runtime job 持久日志恢复:页面创建 preview build、刷新恢复 open runtime job、取消后刷新状态、SSE 断线恢复时都会按 `afterSequence` 补齐缺失日志;日志 entry 使用 `jobId + sequence` 稳定去重,不依赖临时时间戳判断是否重复。
|
||||
- SSE 断线后前端先 best-effort 补齐 runtime job 日志,再刷新 preview build 状态;若 build 仍未终态,则短延迟后重新订阅,终态仍以后端 build payload / reload 结果为准。
|
||||
- 刷新 `/editor/agent?projectId=...` 时继续恢复 project、active snapshot、active preview 和未终态 preview runtime job;若存在 open runtime job,会先读取持久日志,再恢复对应 preview build 订阅。
|
||||
- P2 收口补齐 active preview build 刷新恢复:若项目已有 `activePreviewBuildId`,页面初始化会读取后端 build、写入 `currentBuild`、合并 build 日志,并只在非终态时恢复 SSE 订阅;已成功 build 刷新后直接显示“已完成”和后端 preview URL,不再残留旧的“排队中”状态。
|
||||
|
||||
已验证:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server web_project_runtime_logs --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project --reporter verbose
|
||||
npm run test -- src/components/editor/agent/webProjectAgentViewModel.test.ts src/components/editor/agent/WebProjectAgentEditorPage.test.tsx --reporter verbose
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
备注:`cargo test -p api-server web_project_runtime_logs --manifest-path server-rs/Cargo.toml` 通过 1 个 P2-09 定向用例;仍存在既有 puzzle `dead_code` warning,和 P2-09 无关。
|
||||
|
||||
### P2-10:端到端 Smoke 与文档收口
|
||||
|
||||
目标:
|
||||
|
||||
- 用真实浏览器完成 P2 happy path、失败保留预览、连续提交、刷新恢复和取消场景。
|
||||
- 更新架构文档、测试用例和开发运维说明。
|
||||
|
||||
改动范围:
|
||||
|
||||
- `docs/technical`、`docs/planning`、必要的 `quality-gates`。
|
||||
- 自动 smoke 脚本或测试说明。
|
||||
|
||||
最小验证:
|
||||
|
||||
```bash
|
||||
npm run spacetime:generate
|
||||
npm run check:spacetime-schema
|
||||
cargo test -p spacetime-module web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p spacetime-client web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p api-server web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p web-project-runner --manifest-path server-rs/Cargo.toml
|
||||
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
npm run check:editor-agent-p2-smoke -- --checklist-only
|
||||
git diff --check
|
||||
```
|
||||
|
||||
浏览器最小 smoke:
|
||||
|
||||
```text
|
||||
打开 /editor/agent
|
||||
提交“做一个蓝色计数按钮页面”
|
||||
等待 runtime job succeeded
|
||||
确认 iframe 预览和按钮点击
|
||||
提交“破坏构建”
|
||||
确认 failed job 不覆盖上一版 preview
|
||||
连续提交两次并确认旧 job 不覆盖新 preview
|
||||
取消一个未完成 job
|
||||
刷新页面确认 active preview、job 状态和日志恢复
|
||||
```
|
||||
|
||||
落地记录(2026-06-17):
|
||||
|
||||
- 已新增 `npm run check:editor-agent-p2-smoke`,用于读取 `.app/dev-stack.json` 并检查完整 dev 栈的 SpacetimeDB `/v1/ping`、api-server `/healthz`、主站 `/editor/agent` 和 preview gateway 无效 token guard;该脚本不启动长驻进程,不杀本机已有 SpacetimeDB,只作为 P2 浏览器 smoke 前置 ready 检查。
|
||||
- 已支持 `npm run check:editor-agent-p2-smoke -- --checklist-only` 输出真实浏览器最小 smoke 步骤,便于在完整 dev 栈启动成功后逐项确认 happy path、失败保留预览、连续提交 stale guard、取消和刷新恢复。
|
||||
- 本地验证口径已明确:`npm run dev:api-server` 不会自动启动或发布 SpacetimeDB,直接运行会在请求 `/v1/identity` 前提缺失时失败;P2 端到端 smoke 应使用 `npm run dev -- --no-interactive --api-timeout-seconds 900`,完整 dev 脚本会为 api-server 注入 `GENARRATIVE_PROCESS_ROLE=all`,同进程启动 Web Project runtime worker。若拆分终端,需要先 `npm run dev:spacetime`,再 `npm run dev:api-server` / `npm run dev:web`,并确保手动 worker 复用本轮 `GENARRATIVE_SPACETIME_TOKEN`。Windows 首次冷发布 `spacetime-module` 可能超过 5 分钟,需给 10-15 分钟窗口。
|
||||
- P2-10 仍要求真实浏览器手工确认 iframe 交互和刷新恢复;当前仓库未引入 Playwright 等浏览器自动化依赖,本步没有为了 smoke 增加新重依赖。
|
||||
- 2026-06-17 已在隔离端口 `19000 / 19082 / 19101 / 19102 / 19104` 尝试完整 `npm run dev -- --no-interactive --api-timeout-seconds 900`:SpacetimeDB standalone 启动、`spacetime-module` publish、Web Project service identity 自动授权和 `web-project-runner` 构建均已完成;api-server 随后因本机私有短信配置仍为 `SMS_AUTH_PROVIDER=aliyun` 且缺少 `ALIYUN_SMS_ACCESS_KEY_ID / SECRET`,在初始化阶段报 `初始化应用状态失败:阿里云短信 AccessKey 未配置` 并退出,未进入 `/healthz`、Vite 和真实浏览器 smoke。该阻断属于本机 env 前提,不是 P2 runtime job / worker / preview build 代码失败;继续真实浏览器 smoke 前需把本机 `.env.local` 或更后加载的 `.env.secrets.local` 显式设为 `SMS_AUTH_PROVIDER=mock`。
|
||||
- 用户补齐短信配置后,完整 dev 栈 ready 检查和真实浏览器 happy path 已通过:SpacetimeDB `/v1/ping`、api-server `/healthz`、主站 `/editor/agent`、preview gateway invalid token guard 均可访问;浏览器提交“做一个蓝色计数按钮页面”后 runtime job / build 进入 `succeeded`,直接打开 preview URL 可交互,按钮从“已点击 0 次”变为“已点击 1 次”。收口阶段发现并修复完整 dev 默认角色和刷新恢复两个残留点。
|
||||
- 当前不新增“按 active preview build 反查终态 runtime job”的后端接口;刷新恢复已覆盖 active build 状态和 build 日志,终态 runtime job 的完整持久日志回填等后续确有产品需要时再作为独立小步追加,避免 P2 收口继续扩大范围。
|
||||
|
||||
最终验收确认(2026-06-17):
|
||||
|
||||
- 使用更新后的 `npm run dev -- --no-interactive --api-timeout-seconds 900 --web-port 19200 --api-port 19282 --spacetime-port 19301 --admin-web-port 19302 --web-project-preview-port 19304 --spacetime-data-dir .app/p2-final-dev-stack-smoke/spacetime-data --database genarrative-p2-final-smoke` 启动隔离完整栈;`npm run check:editor-agent-p2-smoke` 通过 SpacetimeDB、api-server、web 和 preview gateway ready 检查。
|
||||
- 自动化验收通过:`node --check scripts/dev.mjs`、`node --check scripts/check-editor-agent-p2-smoke.mjs`、`npm run test -- scripts/dev.test.ts src/services/web-project src/components/editor/agent --reporter verbose`、`npm run typecheck`、`npm run check:encoding`。
|
||||
- 真实浏览器 smoke 通过:登录本地 smoke 用户后打开 `/editor/agent`,提交“做一个蓝色计数按钮页面”,runtime worker 日志出现领取任务、hydrate、build、artifact、preview,build 进入 `succeeded`;直接打开 preview URL 后按钮从“已点击 0 次”变为“已点击 1 次”。
|
||||
- 失败与 stale guard 验收通过:提交“破坏构建”后新 build 进入 `failed`,iframe 仍保持上一版成功 preview URL;连续提交两次计数按钮改动后 active preview 更新到最新成功 preview URL,旧 job 没有覆盖当前 active preview。
|
||||
- 取消和刷新恢复验收通过:对新的 queued / running job 点击“取消”后状态收敛为 `cancelled`,上一版成功 preview URL 保持不变;刷新 `/editor/agent?projectid=...` 后 project、active snapshot、active preview、已完成状态和后端日志均从服务端恢复。
|
||||
|
||||
进入 P3 门槛:
|
||||
|
||||
- P2 完成定义全部满足。
|
||||
- Docker sandbox、Shell、真实 coding agent、任意依赖安装和 HMR 仍保持未开放状态。
|
||||
- P3 的远程 Agent Container MVP 从 `agent_turn` job 开始推进,第一版允许 Agent + workspace 同 Remote Container,但用户 shell 不开放,Agent shell 必须经过 `ShellPolicy` 接口,详见 [【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md](./【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md)。
|
||||
|
||||
## P2a:持久任务表与状态机
|
||||
|
||||
新增 SpacetimeDB 表建议:
|
||||
@@ -227,6 +762,8 @@ SandboxRuntime
|
||||
- 用户取消时写 `cancel_requested_at` 并把未开始 job 标为 `cancelled`。
|
||||
- running job 由 worker 检测取消请求后停止 runner 并回写 `cancelled`。
|
||||
- P2 若暂不支持强杀子进程,也必须明确返回“取消请求已记录 / 等待 runner 收敛”的状态。
|
||||
- `complete / fail / complete preview build` 必须让 `cancel_requested_at` 优先于 worker 结果;即使 runner 已经成功产出 artifact,取消请求也不能生成新 preview URL 或推进 active preview。
|
||||
- linked preview build 的取消写回分两类:queued cancel 由 HTTP cancel handler 同步标记 `cancelled`;running cancel 由 worker 收敛时标记 `cancelled`,避免先把 running build 写成 failed 后再被终态保护拦住。
|
||||
|
||||
刷新恢复要求:
|
||||
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
# Editor Agent P3 远程 Agent Container MVP 计划
|
||||
|
||||
更新时间:`2026-06-17`
|
||||
|
||||
## 计划定位
|
||||
|
||||
本文承接 `/editor/agent` P2。P2 完成后,`web_project_runtime_job`、worker / lease、日志分页、SSE 重连、取消 / stale 和 `SandboxRuntime` 抽象已经成为后续执行面的基础。
|
||||
|
||||
P3 的目标是尽快把真实 Agent 和远程 Container 跑通,但不推翻 P1 / P2 的控制面:
|
||||
|
||||
```text
|
||||
用户输入需求
|
||||
-> api-server 创建 agent_turn job
|
||||
-> Remote Agent Server claim job
|
||||
-> Remote Container 内运行真实 Agent 和工作区 shell
|
||||
-> 收集 diff 转 WebProjectPatch
|
||||
-> api-server 校验 patch 并保存 snapshot
|
||||
-> 自动触发 preview_build job
|
||||
-> Preview Gateway + iframe sandbox 展示结果
|
||||
```
|
||||
|
||||
一句话:
|
||||
|
||||
```text
|
||||
P3 先用同一个 Remote Container 快速跑通真实 Agent + 工作区 + shell,但接口按未来 Agent / Workspace 隔离形态设计。
|
||||
```
|
||||
|
||||
长期架构边界以 [../technical/【技术方案】EditorAgent远程Agent与工作区沙箱架构-2026-06-17.md](../technical/【技术方案】EditorAgent远程Agent与工作区沙箱架构-2026-06-17.md) 为准;本文只记录 P3 MVP 的实施顺序、验收和阶段边界。
|
||||
|
||||
## MVP 边界
|
||||
|
||||
P3 MVP 做:
|
||||
|
||||
- 新增 `agent_turn` runtime job。
|
||||
- 新增 Remote Agent Server / worker 执行面,主动 claim job。
|
||||
- 第一版采用 `Agent + workspace` 同 Remote Container。
|
||||
- Agent 可以使用完整 shell,但 shell 必须经过 `ShellPolicy` / `CommandGuard` 接口。
|
||||
- MVP shell policy 可先是 `AllowAllShellPolicy`,规则待定但接口必须存在。
|
||||
- Agent 调 LLM 走平台 `LLM Broker`,Container 不持有长期 provider key。
|
||||
- Container 内文件变更必须收集 diff,转成 `WebProjectPatch` 后回传 api-server。
|
||||
- api-server 继续作为唯一 patch 校验和 snapshot 落库入口。
|
||||
- Agent 成功保存 snapshot 后自动创建 `preview_build` job。
|
||||
|
||||
P3 MVP 不做:
|
||||
|
||||
- 用户可交互 shell。
|
||||
- Agent Container 与 Workspace Container 的强隔离拆分。
|
||||
- HMR / 长驻 dev server / WebSocket 端口代理。
|
||||
- 任意依赖安装的正式白名单治理。
|
||||
- Web Project 作品化发布。
|
||||
- Game SDK 强约束运行时。
|
||||
- Container 直接写 SpacetimeDB 或直接提升 workspace 为事实源。
|
||||
|
||||
## 总体架构
|
||||
|
||||
```text
|
||||
前端 /editor/agent
|
||||
-> api-server 控制面
|
||||
-> SpacetimeDB: project / snapshot / runtime job / logs / preview build
|
||||
-> Remote Agent Server
|
||||
-> agent-worker claim / renew / append log / complete / fail
|
||||
-> Remote Container
|
||||
-> Agent runtime
|
||||
-> /workspace
|
||||
-> shell tool
|
||||
-> ShellPolicy / OutputFilter / AuditLogger
|
||||
-> api-server 校验 WebProjectPatch 并保存 snapshot
|
||||
-> preview_build job
|
||||
-> Preview Gateway
|
||||
-> iframe sandbox
|
||||
```
|
||||
|
||||
### 前端边界
|
||||
|
||||
前端不直接连接 Container,不持有 Container token、SSH 信息或 Docker API。
|
||||
|
||||
前端只需要:
|
||||
|
||||
- `POST /api/runtime/web-project/projects/{projectId}/agent-turns` 提交用户需求。
|
||||
- 通过 job SSE / 日志分页读取 `agent_turn` 状态和脱敏日志。
|
||||
- 读取 project / snapshot / preview build。
|
||||
- 使用后端返回的 `previewUrl` 加载 iframe。
|
||||
|
||||
用户不能调用 shell。shell 是 Agent 的内部工具,不暴露为 WebIDE UI 能力。
|
||||
|
||||
### api-server 控制面
|
||||
|
||||
api-server 不运行 Agent、不执行 shell、不启动本机用户代码。它负责:
|
||||
|
||||
- 用户鉴权和 project owner 校验。
|
||||
- 创建 `agent_turn` job。
|
||||
- 下发短期任务能力给 Remote Agent Server。
|
||||
- 接收 Agent 产出的 patch、summary、日志摘要和结果状态。
|
||||
- 调用现有 patch validator。
|
||||
- 保存新 snapshot。
|
||||
- 创建 preview build。
|
||||
- 提供状态、日志、取消和刷新恢复接口。
|
||||
|
||||
api-server 不信任 Remote Container 的最终结论。构建成功、测试成功、Agent 成功都只能作为候选状态;正式工程文件事实以通过校验后的 snapshot 为准。
|
||||
|
||||
### SpacetimeDB 事实源
|
||||
|
||||
继续沿用 P2 的事实源口径:
|
||||
|
||||
```text
|
||||
web_project
|
||||
web_project_snapshot
|
||||
web_project_runtime_job
|
||||
web_project_runtime_job_log
|
||||
web_project_preview_build
|
||||
```
|
||||
|
||||
`/workspace` 不是事实源。Remote Container 内的文件修改必须在任务结束或阶段性保存时转成 `WebProjectPatch`,再由 api-server 校验后生成新 snapshot。
|
||||
|
||||
## Remote Agent Server
|
||||
|
||||
新增远程执行面,建议独立部署在专门服务器上,不放在 `api-server` 进程内。
|
||||
|
||||
职责:
|
||||
|
||||
- 主动 claim `agent_turn` job。
|
||||
- 按 `worker_id + lease_token` renew lease。
|
||||
- 启动或复用 Remote Container。
|
||||
- hydrate 当前 snapshot 到 `/workspace`。
|
||||
- 调用真实 Agent。
|
||||
- 记录工具调用、shell 命令、输出摘要和错误。
|
||||
- 收集 diff,生成 `WebProjectPatch`。
|
||||
- 回传 patch 和 job 结果。
|
||||
- complete / fail / cancel job。
|
||||
|
||||
Remote Agent Server 不直接写 SpacetimeDB 表,只通过受控 facade / API 回写任务状态和结果。
|
||||
|
||||
## Remote Container MVP
|
||||
|
||||
第一版为了速度采用同容器形态:
|
||||
|
||||
```text
|
||||
Remote Container
|
||||
/workspace
|
||||
/agent
|
||||
/run/secrets/job_token
|
||||
```
|
||||
|
||||
最低限制:
|
||||
|
||||
- 非 root 用户运行。
|
||||
- 不挂载 Docker socket。
|
||||
- 不挂载当前 Genarrative 仓库源码。
|
||||
- 不注入平台 `.env`。
|
||||
- 不注入 SpacetimeDB token。
|
||||
- 不注入长期 LLM provider key。
|
||||
- 只挂载当前 job / project 的工作区。
|
||||
- 设置 CPU、内存、磁盘、进程数、打开文件数、任务时长和日志大小限制。
|
||||
- 网络默认最小化,只允许控制面回调、LLM Broker、受控 npm registry / asset gateway。
|
||||
|
||||
这不是最终隔离形态。P3 代码结构必须保留后续迁移到以下结构的接口:
|
||||
|
||||
```text
|
||||
Agent Container
|
||||
-> Tool Bridge / sidecar
|
||||
-> Workspace Container
|
||||
```
|
||||
|
||||
## Agent 与 Shell
|
||||
|
||||
Agent 可以使用完整 shell,但必须经过固定工具链:
|
||||
|
||||
```text
|
||||
Agent
|
||||
-> ShellTool.run(command)
|
||||
-> ShellPolicy.evaluate(command, context)
|
||||
-> ContainerCommandExecutor.exec(command)
|
||||
-> OutputFilter.redact(output)
|
||||
-> AuditLogger.record(attempt/result)
|
||||
-> Agent receives result
|
||||
```
|
||||
|
||||
MVP policy:
|
||||
|
||||
```text
|
||||
AllowAllShellPolicy
|
||||
```
|
||||
|
||||
即使规则暂时放空,也必须记录:
|
||||
|
||||
- `jobId`
|
||||
- `projectId`
|
||||
- `snapshotId`
|
||||
- `command`
|
||||
- `workingDirectory`
|
||||
- `decision`
|
||||
- `policyVersion`
|
||||
- `startedAt`
|
||||
- `exitCode`
|
||||
- 输出摘要
|
||||
|
||||
后续可在同一接口上补:
|
||||
|
||||
- allowlist / denylist。
|
||||
- 禁止访问 `/run/secrets`。
|
||||
- 禁止后台进程。
|
||||
- 禁止危险文件名和隐藏配置。
|
||||
- 命令风险分级。
|
||||
- 需要人工批准的命令。
|
||||
- 网络策略联动。
|
||||
|
||||
## LLM Broker
|
||||
|
||||
Agent Container 不直接持有 OpenAI / Claude 等 provider key。真实模型调用通过平台 `LLM Broker`:
|
||||
|
||||
```text
|
||||
Agent Container
|
||||
-> short-lived job token
|
||||
-> LLM Broker
|
||||
-> provider
|
||||
```
|
||||
|
||||
Broker 负责:
|
||||
|
||||
- 校验 job token。
|
||||
- 校验 worker lease。
|
||||
- 校验 project / owner / job kind。
|
||||
- 模型路由。
|
||||
- 配额和速率限制。
|
||||
- 审计和日志脱敏。
|
||||
- provider key 管理。
|
||||
|
||||
## 接口与契约
|
||||
|
||||
P3 新增或扩展:
|
||||
|
||||
```text
|
||||
POST /api/runtime/web-project/projects/{projectId}/agent-turns
|
||||
GET /api/runtime/web-project/agent-turns/{jobId}
|
||||
GET /api/runtime/web-project/agent-turns/{jobId}/events
|
||||
GET /api/runtime/web-project/runtime-jobs/{jobId}/logs
|
||||
POST /api/runtime/web-project/runtime-jobs/{jobId}/cancel
|
||||
```
|
||||
|
||||
建议新增 job kind:
|
||||
|
||||
```text
|
||||
agent_turn
|
||||
preview_build
|
||||
```
|
||||
|
||||
后续可扩展:
|
||||
|
||||
```text
|
||||
agent_fix_build
|
||||
agent_shell_command
|
||||
game_runtime_build
|
||||
```
|
||||
|
||||
`agent_turn` 事件建议:
|
||||
|
||||
- `queued`
|
||||
- `running`
|
||||
- `thinking`
|
||||
- `tool_call_started`
|
||||
- `tool_call_finished`
|
||||
- `log`
|
||||
- `patch_ready`
|
||||
- `snapshot_saved`
|
||||
- `preview_build_queued`
|
||||
- `succeeded`
|
||||
- `failed`
|
||||
- `cancelled`
|
||||
- `expired`
|
||||
- `stale`
|
||||
|
||||
前端展示以服务端事件和持久日志为准;SSE 断线后按 `jobId + afterSequence` 补日志,再重新订阅。
|
||||
|
||||
## 工具抽象
|
||||
|
||||
P3 虽然同容器实现,但接口上必须拆开:
|
||||
|
||||
```text
|
||||
AgentRuntime
|
||||
runTurn(input) -> AgentTurnResult
|
||||
|
||||
WorkspaceRuntime
|
||||
hydrateSnapshot(snapshot)
|
||||
collectDiff(baseSnapshot)
|
||||
destroyWorkspace()
|
||||
|
||||
ToolBridge
|
||||
readFile(path)
|
||||
writeFile(path, content)
|
||||
runShell(command)
|
||||
|
||||
ShellPolicy
|
||||
evaluate(command, context)
|
||||
```
|
||||
|
||||
同容器 MVP 可以让 `ToolBridge` 直接调用本地文件系统和本地 shell。后续分容器时,替换为 RPC sidecar,不改 api-server、job 状态机、snapshot / patch 主链路。
|
||||
|
||||
## 工作流
|
||||
|
||||
```text
|
||||
1. 用户在 /editor/agent 输入需求
|
||||
2. 前端提交 agent_turn
|
||||
3. api-server 创建 runtime job
|
||||
4. Remote Agent Server claim job
|
||||
5. Remote Container hydrate snapshot
|
||||
6. Agent 调 LLM Broker
|
||||
7. Agent 通过 ToolBridge 读写文件并调用 shell
|
||||
8. ToolBridge 经 ShellPolicy 记录命令和输出
|
||||
9. 收集 diff 转 WebProjectPatch
|
||||
10. api-server 校验 patch
|
||||
11. 校验通过后保存新 snapshot
|
||||
12. api-server 创建 preview_build job
|
||||
13. preview worker 构建 artifact
|
||||
14. Preview Gateway 签发 previewUrl
|
||||
15. 前端 iframe sandbox 切换预览
|
||||
```
|
||||
|
||||
失败链路:
|
||||
|
||||
```text
|
||||
Agent 失败 / shell 超时 / Container 崩溃
|
||||
-> runtime job failed / expired
|
||||
-> 已存在 active preview 保持不变
|
||||
-> 日志和错误摘要可恢复
|
||||
```
|
||||
|
||||
## 可执行落地步骤
|
||||
|
||||
### P3-00:文档与边界冻结
|
||||
|
||||
- 新增本文。
|
||||
- 更新总方案、文档索引和团队决策记录。
|
||||
- 冻结 P3 MVP 不做用户 shell、不做双容器强隔离、不做 HMR、不做作品化发布。
|
||||
|
||||
### P3-01:Job kind 与 HTTP 契约
|
||||
|
||||
- 在 shared contracts / packages shared 中新增 `agent_turn` job kind 和响应 DTO。
|
||||
- api-server 增加 `POST /agent-turns` skeleton。
|
||||
- 复用 P2 runtime job 状态机和日志分页。
|
||||
|
||||
### P3-02:Remote Agent Server skeleton
|
||||
|
||||
- 新增远程 worker 进程或服务。
|
||||
- 支持 claim / renew / append log / fail。
|
||||
- 不跑真实 Agent,只 dry-run 失败或返回固定 patch。
|
||||
|
||||
### P3-03:Remote Container 执行
|
||||
|
||||
- worker 启动单 job container。
|
||||
- hydrate snapshot 到 `/workspace`。
|
||||
- 执行最小命令并收集日志。
|
||||
- 任务结束销毁 container 或清理 workspace。
|
||||
|
||||
### P3-04:ShellPolicy 接口
|
||||
|
||||
- 所有 shell 命令必须经过 `ShellPolicy`。
|
||||
- MVP 实现 `AllowAllShellPolicy`。
|
||||
- 接入 OutputFilter 和 AuditLogger。
|
||||
|
||||
### P3-05:真实 Agent 接入
|
||||
|
||||
- 接 LLM Broker。
|
||||
- Agent 读取需求、snapshot 摘要和构建上下文。
|
||||
- Agent 修改文件并可调用 shell。
|
||||
- 输出 diff / patch,而不是直接落库。
|
||||
|
||||
### P3-06:Patch 保存与预览联动
|
||||
|
||||
- Remote Agent Server 回传 `WebProjectPatch`。
|
||||
- api-server 校验并保存 snapshot。
|
||||
- 自动创建 preview build。
|
||||
- 前端展示 agent turn succeeded 和 preview build queued / running / succeeded。
|
||||
|
||||
### P3-07:构建失败修复闭环
|
||||
|
||||
- preview build failed 后,前端可触发修复。
|
||||
- Agent 基于脱敏构建日志生成修复 patch。
|
||||
- 修复仍走同一 patch 校验和 snapshot 保存链路。
|
||||
|
||||
## 验收场景
|
||||
|
||||
P3 MVP 最小验收:
|
||||
|
||||
1. 打开 `/editor/agent`。
|
||||
2. 输入“做一个蓝色计数按钮小游戏页面”。
|
||||
3. api-server 创建 `agent_turn` job。
|
||||
4. Remote Agent Server claim job 并启动 Remote Container。
|
||||
5. Agent 调 LLM Broker。
|
||||
6. Agent 通过 ShellPolicy 调用至少一个 shell 命令。
|
||||
7. shell 命令记录 policy decision 和脱敏日志。
|
||||
8. Container 产出 diff。
|
||||
9. api-server 校验 patch 并保存 snapshot。
|
||||
10. 自动创建 preview build。
|
||||
11. preview build succeeded 后 iframe 展示新预览。
|
||||
12. 刷新页面后恢复 agent job、日志、snapshot 和 active preview。
|
||||
13. Agent / container 失败时上一版 active preview 不被覆盖。
|
||||
|
||||
## 风险与处理
|
||||
|
||||
| 风险 | 处理 |
|
||||
| --- | --- |
|
||||
| 同容器让用户代码影响 Agent | P3 接受该 MVP 风险,但 Agent / shell token 最小化,最终只通过 patch 落库;P4 拆 Agent / Workspace 容器 |
|
||||
| 完整 shell 绕过后续治理 | P3 起所有 shell 都必须经过 ShellPolicy,即使当前 allow-all |
|
||||
| Container 成为事实源 | 禁止直接落库,workspace diff 必须转 patch 并由 api-server 校验 |
|
||||
| 前端直连 Container | 禁止,前端只连 api-server / gateway |
|
||||
| LLM provider key 泄露 | provider key 只在 LLM Broker,Container 只拿短期 job token |
|
||||
| 日志泄露 token 或内部路径 | OutputFilter 脱敏,用户可见日志摘要化 |
|
||||
| 远程 worker 崩溃后 job 卡住 | 复用 P2 lease / expired / reclaim 机制 |
|
||||
|
||||
## P4 预留
|
||||
|
||||
P3 跑通后再进入:
|
||||
|
||||
- Agent Container + Workspace Container + Tool Bridge sidecar 拆分。
|
||||
- ShellPolicy 正式规则和人工批准。
|
||||
- 受控依赖安装。
|
||||
- 构建失败自动修复循环增强。
|
||||
- Game SDK 模板与后端正式裁决。
|
||||
- Web Project 作品化发布。
|
||||
|
||||
## 完成定义
|
||||
|
||||
P3 完成必须满足:
|
||||
|
||||
- 真实 Agent 可在 Remote Container 内处理 `/editor/agent` 用户需求。
|
||||
- Agent shell 命令全部经过 ShellPolicy。
|
||||
- Container 变更通过 patch 校验保存为 snapshot。
|
||||
- 自动触发 preview build,并由 iframe sandbox 展示。
|
||||
- 前端不直连 Container,用户不能使用 shell。
|
||||
- Remote Agent Server 的日志、失败、取消和刷新恢复接入 P2 job 体系。
|
||||
- 后续拆成 Agent / Workspace 隔离容器时,不需要重写 api-server 控制面和 snapshot / patch 主链路。
|
||||
Reference in New Issue
Block a user