完成 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:
2026-06-17 21:22:41 +08:00
parent 2d91675ceb
commit b19b76af56
74 changed files with 9557 additions and 119 deletions

View File

@@ -21,7 +21,7 @@
微信小程序虚拟支付接入、`wechat_mp_virtual` 渠道、`wx.requestVirtualPayment` 承接页和后端签名配置见 [【技术方案】微信虚拟支付接入-2026-05-26.md](./%E3%80%90%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88%E3%80%91%E5%BE%AE%E4%BF%A1%E8%99%9A%E6%8B%9F%E6%94%AF%E4%BB%98%E6%8E%A5%E5%85%A5-2026-05-26.md)。
`/editor/agent` 浏览器内 AI Web 工程编辑器的静态 SPA 沙箱预览 MVP采用“平台编辑器壳 + api-server 控制面 + 独立 runner worker + 独立预览域”四层结构;技术方案、威胁模型和验收清单见 [【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md](./technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md)、[【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md](./technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md) 和 [【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md](./technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md)。P1 先用确定性 mock Agent 生成结构化 patch、真实打通项目 / 快照 / 构建 / artifact / 预览闭环,落地拆分见 [【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md](./technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md),可执行开发计划见 [【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md](./planning/【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md)。P2 先补持久 runtime job、worker / lease、日志恢复和 SandboxRuntime 抽象,计划见 [【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md](./planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md)。
`/editor/agent` 浏览器内 AI Web 工程编辑器的静态 SPA 沙箱预览 MVP采用“平台编辑器壳 + api-server 控制面 + 独立 runner worker + 独立预览域”四层结构;技术方案、威胁模型和验收清单见 [【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md](./technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md)、[【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md](./technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md) 和 [【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md](./technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md)。P1 先用确定性 mock Agent 生成结构化 patch、真实打通项目 / 快照 / 构建 / artifact / 预览闭环,落地拆分见 [【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md](./technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md),可执行开发计划见 [【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md](./planning/【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md)。P2 先补持久 runtime job、worker / lease、日志恢复和 SandboxRuntime 抽象,计划见 [【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md](./planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md)。P2 后真实 Agent、用户工作区、Agent 私有 shell、ShellPolicy / ToolBridge、LLM Broker 和后续容器拆分的长期架构见 [【技术方案】EditorAgent远程Agent与工作区沙箱架构-2026-06-17.md](./technical/【技术方案】EditorAgent远程Agent与工作区沙箱架构-2026-06-17.md)P3 采用 Agent + workspace 同 Remote Container 的 MVP 方案,计划见 [【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md](./planning/【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md)。
`/editor/canvas` 图片画布编辑器的画布素材 ZIP 导出能力,入口放在右上角标题栏下载图标内,第一版采用前端 JSZip 打包画布中有效图层引用的上传图、生成图和修改结果,方案见 [【前端架构】图片画布素材导出方案-2026-06-15.md](./technical/【前端架构】图片画布素材导出方案-2026-06-15.md)。

View File

@@ -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 接口以便后续拆分隔离。
## 维护规则

View File

@@ -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-01Runtime 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-02Runtime 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-03Spacetime Client Facade 与 API 契约
目标:
-`spacetime-client` 封装 runtime job facadeapi-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-04Preview 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-05Runtime 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-06TempDirBuildRuntime 接入 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-07Active 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` 组合 procedurerunner 成功后的 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 bindingsWindows 长路径格式化失败由脚本短路径 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、previewbuild 进入 `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 后再被终态保护拦住。
刷新恢复要求:

View File

@@ -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-01Job kind 与 HTTP 契约
- 在 shared contracts / packages shared 中新增 `agent_turn` job kind 和响应 DTO。
- api-server 增加 `POST /agent-turns` skeleton。
- 复用 P2 runtime job 状态机和日志分页。
### P3-02Remote Agent Server skeleton
- 新增远程 worker 进程或服务。
- 支持 claim / renew / append log / fail。
- 不跑真实 Agent只 dry-run 失败或返回固定 patch。
### P3-03Remote Container 执行
- worker 启动单 job container。
- hydrate snapshot 到 `/workspace`
- 执行最小命令并收集日志。
- 任务结束销毁 container 或清理 workspace。
### P3-04ShellPolicy 接口
- 所有 shell 命令必须经过 `ShellPolicy`
- MVP 实现 `AllowAllShellPolicy`
- 接入 OutputFilter 和 AuditLogger。
### P3-05真实 Agent 接入
- 接 LLM Broker。
- Agent 读取需求、snapshot 摘要和构建上下文。
- Agent 修改文件并可调用 shell。
- 输出 diff / patch而不是直接落库。
### P3-06Patch 保存与预览联动
- 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 BrokerContainer 只拿短期 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 主链路。

View File

@@ -0,0 +1,387 @@
# Editor Agent 远程 Agent 与工作区沙箱架构
更新时间:`2026-06-17`
## 背景
`/editor/agent` 已按 P1 / P2 跑通 Web Project 的 snapshot、patch、preview build、runtime job、worker / lease、日志恢复和独立 preview。下一步需要接入真实 Agent并把用户工作区与 Agent 执行从 `api-server` 进程中移到专门服务器。
本文记录长期架构边界不是单轮任务拆分。P3 的执行计划见 [../planning/【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md](../planning/【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md)。
## 结论
P2 后的 WebIDE + AI Agent 架构采用“控制面 / 执行面 / 预览面”分层:
```text
前端 WebIDE /editor/agent
-> api-server 控制面
-> SpacetimeDB 事实源
-> Remote Agent Server 执行面
-> Remote Container
-> Agent runtime
-> workspace
-> shell tool
-> ShellPolicy / ToolBridge
-> Preview Gateway 预览面
-> iframe sandbox
```
第一版为了尽快落地,允许 `Agent runtime` 与用户 `workspace` 位于同一个 Remote Container但接口必须按未来拆分为 `Agent Container + Workspace Container + ToolBridge sidecar` 的形态设计。
核心不变约束:
- 前端不直连 Container。
- api-server 不跑 Agent、不执行 shell、不运行用户代码。
- 用户不能使用 shell。
- Agent 可以使用 shell但必须经过 `ShellPolicy` / `CommandGuard`
- Container 不直接写 SpacetimeDB。
- workspace 不是事实源snapshot 才是事实源。
- 文件变更必须转成 `WebProjectPatch`,经 api-server 校验后保存 snapshot。
- preview 只服务 immutable artifact不服务 Container 临时目录。
## 组件职责
### 前端 WebIDE
前端负责编辑体验和状态展示:
- 文件树、当前文件查看或轻编辑。
- Agent 输入。
- agent job / preview build 状态。
- 脱敏日志展示。
- iframe 预览。
前端只连接 api-server / gateway
```text
POST agent_turn
GET job events
GET job logs
GET project / snapshot
GET preview build
iframe previewUrl
```
前端不得:
- 直接连接 Remote Container。
- 直接连接 Remote Agent Server 私有执行端口。
- 持有 container token、SSH key、Docker API 凭据或 job lease token。
- 暴露用户可交互 shell。
- 自行拼接 preview URL。
### api-server 控制面
api-server 是所有业务状态和权限的入口:
- 鉴权和 project owner 校验。
- 创建 `agent_turn` / `preview_build` runtime job。
- 读取 project、snapshot、job、logs、preview build。
- 为 Remote Agent Server 发放短期任务能力。
- 接收 Agent 产出的 patch、summary、日志摘要和结果状态。
- 执行 patch 校验。
- 保存新 snapshot。
- 创建 preview build。
- 提供取消、刷新恢复和 SSE / 日志分页。
api-server 不做:
- 不启动用户 Container。
- 不运行 Agent。
- 不执行 shell。
- 不把平台 `.env`、SpacetimeDB token、OSS 写权限、LLM provider key 下发给 workspace。
### SpacetimeDB 事实源
长期事实源包括:
```text
web_project
web_project_snapshot
web_project_runtime_job
web_project_runtime_job_log
web_project_preview_build
```
事实源规则:
- `web_project_snapshot` 是工程文件的版本化事实源。
- `web_project_runtime_job` 描述执行生命周期,不替代 snapshot。
- `web_project_runtime_job_log` 保存可恢复日志,不依赖前端内存。
- `web_project_preview_build` 保存 preview artifact、token 和 preview URL。
- Container 内 `/workspace` 只是一份可销毁执行副本。
### Remote Agent Server
Remote Agent Server 是专门服务器上的执行控制器,建议由常驻 worker 主动拉任务:
```text
claim job
renew lease
append log
run container
collect patch
complete / fail / cancel
```
它不直接写 SpacetimeDB 表,不绕过 api-server / spacetime-client facade。
推荐不要让 api-server 通过 SSH、Docker API 或远程 shell 主动控制专门服务器Remote Agent Server 应主动 claim job减少入站控制面。
### Remote Container
P3 MVP 的同容器形态:
```text
Remote Container
/workspace
/agent
/run/secrets/job_token
```
最低运行限制:
- 非 root。
- 无 Docker socket。
- 无宿主源码挂载。
- 无平台 `.env`
- 无 SpacetimeDB token。
- 无长期 LLM provider key。
- 仅挂载当前 job / project 工作区。
- CPU、内存、磁盘、进程数、打开文件数、任务时长和日志大小受限。
- 网络最小化。
后续隔离形态:
```text
Agent Container
-> ToolBridge sidecar
-> Workspace Container
```
拆分后Agent 的模型调用、规划和工具请求与 Workspace 的不可信代码执行隔离;但 snapshot / patch / job 状态机不变。
## Agent 与工作区关系
Agent 是用户开发助手workspace 是不可信代码执行区。即使每个用户都有独立 Agent 实例和独立 workspace也不应让 workspace 直接获得 Agent 的任务权限。
风险来自:
- 用户代码伪造构建、测试、日志或 patch 结果。
- 用户代码读取短期 job token 后伪造回调或滥用 LLM 配额。
- 用户代码污染 Agent 工具链,让 Agent 基于假现场继续生成。
- 用户代码把后门写回 snapshot。
P3 MVP 接受同容器的工程速度收益,但必须保留以下防线:
- job token 最小权限、短期有效。
- Agent 和 shell 输出都要审计和脱敏。
- 最终文件变更只通过 patch 保存。
- api-server patch validator 是唯一落库门。
- 后续可拆容器,不改控制面契约。
## Shell 工具与过滤接口
用户不使用 shell。shell 只作为 Agent 的内部工具。
Agent 不能直接调用底层 `exec` / `spawn`。所有命令必须经过:
```text
Agent
-> ShellTool.run(command)
-> ShellPolicy.evaluate(command, context)
-> CommandExecutor.exec(command)
-> OutputFilter.redact(output)
-> AuditLogger.record(attempt/result)
```
### ShellPolicy 接口
建议抽象:
```text
ShellCommandContext
jobId
projectId
snapshotId
workingDirectory
agentTurnId
userId
ShellPolicyDecision
allow
deny
require_approval
ShellPolicy.evaluate(command, context) -> decision
```
P3 MVP 可实现:
```text
AllowAllShellPolicy
```
但必须记录 `policyVersion=allow-all-v0`,并保留后续规则升级入口。
### 后续规则方向
ShellPolicy 后续可以加入:
- 命令 allowlist / denylist。
- 禁止读取 `/run/secrets`
- 禁止访问 `.env``.npmrc``.ssh``.git`
- 禁止后台进程。
- 限制工作目录必须在 `/workspace`
- 限制执行时长、输出大小和并发数。
- 命令风险分级。
- 高风险命令要求人工批准。
- 与 Container 网络策略联动。
### 日志与脱敏
内部审计日志保存完整结构化事件,但必须脱敏敏感值。用户可见日志只展示必要摘要。
每次命令至少记录:
- `jobId`
- `projectId`
- `snapshotId`
- `command`
- `workingDirectory`
- `decision`
- `policyVersion`
- `startedAt`
- `finishedAt`
- `exitCode`
- 输出摘要
## LLM Broker
Agent Container 不持有长期 provider key。模型调用统一走平台 `LLM Broker`
```text
Agent Container
-> short-lived job token
-> LLM Broker
-> OpenAI / Claude / other providers
```
LLM Broker 负责:
- 校验 job token。
- 校验 worker lease。
- 校验 project / owner / job kind。
- 模型路由。
- 配额、速率限制和成本归因。
- 审计与脱敏。
- provider key 管理。
如果短期内必须让 Container 直连 provider也只能注入单 job 短期 token且必须可撤销、可审计不得注入长期平台级 key。
## 前端与 Container 连接模型
前端不直接连接 Container。所有交互走平台控制面
```text
前端
-> api-server HTTP: 创建 agent_turn
-> api-server SSE: 订阅 job events
-> api-server HTTP: 读取持久日志
-> api-server HTTP: 读取 project / snapshot / build
-> Preview Gateway: iframe 预览
```
不提供用户 shell WebSocket。若未来确实需要 shell UI必须重新补安全模型和人工权限策略不能复用 Agent 内部 shell 工具直接暴露。
## Patch 与 Snapshot 流
Agent 修改文件的正式流:
```text
hydrate snapshot 到 workspace
-> Agent 修改文件
-> collect diff
-> 转成 WebProjectPatch
-> api-server validate_web_project_patch
-> 保存新 snapshot
-> 创建 preview_build job
```
禁止:
- Container 直接写 SpacetimeDB。
- Container 直接修改 `web_project_snapshot`
- 将 workspace 路径写入业务数据。
- 将临时 workspace 作为回滚来源。
- failed / cancelled / expired / stale 覆盖上一版 active preview。
## Preview 与 iframe sandbox
Preview 仍是独立面:
```text
preview build job
-> runner / sandbox build
-> immutable artifact
-> Preview Gateway
-> iframe sandbox
```
iframe sandbox 是浏览器侧限制Preview Gateway 是后端独立 origin / 独立 path 服务。二者共同保证 AI 生成代码不进入主站 JS 上下文,也拿不到主站 cookie / localStorage。
当前默认:
```text
iframe sandbox="allow-scripts"
CSP connect-src 'none'
```
## 与 Game SDK 方向的关系
`/editor/agent` 后续收敛到“AI 快速生成小游戏”,应在现有架构上增加 Game SDK Runtime Profile而不是绕过沙箱
```text
AI 生成 JS / TS + Canvas 2D + Web Audio + 平台 Game SDK
-> export createGame(ctx)
-> parent 提供 GameContext
-> ctx.assets / ctx.audio / ctx.events / ctx.finishCandidate / ctx.storage / ctx.random
-> 后端正式裁决
```
生成代码不得直接 fetch、读 cookie、连外网、开摄像头或调用主站 API。Game SDK 约束属于 P3 之后的模板 / runtime profile 收敛,不是 P3 MVP 的阻塞项。
## 迁移策略
P3 同容器 MVP 到隔离形态的迁移目标:
```text
同容器 Agent + workspace
-> AgentRuntime + ToolBridge 接口稳定
-> ToolBridge 从本地函数换成 sidecar RPC
-> WorkspaceRuntime 从本地目录换成独立 Workspace Container
-> Agent Container 不再直接接触不可信 shell 环境
```
低迁移成本依赖:
- 不让 Agent 代码直接依赖宿主绝对路径。
- 不把 shell executor 散落到业务代码里。
- 不让 Container 直接落库。
- 不让前端直连 Container。
- 不把 workspace 当事实源。
- 不把 provider key 放进 workspace。
## 验收要点
架构验收至少覆盖:
- 前端只连 api-server / gateway。
- `agent_turn` job 可恢复。
- Remote Agent Server 主动 claim job。
- Agent shell 全部经过 ShellPolicy。
- ShellPolicy allow-all 也能产生命令审计记录。
- LLM 调用不需要 Container 长期 provider key。
- Container 产出 patch 后由 api-server 校验保存 snapshot。
- preview build 独立触发,失败不覆盖上一版 preview。
- 刷新后 project、snapshot、agent job、logs 和 active preview 均可恢复。

View File

@@ -340,15 +340,23 @@ draft snapshot
Phase 2 的可执行计划见 [../planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md](../planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md)。执行顺序以“runtime job / worker / lease 先行SandboxRuntime 抽象随后接入”为准Docker 工作区、Shell 和真实 coding agent 都应挂在该任务系统之后。
### Phase 3受控依赖安装
### Phase 3远程 Agent Container MVP
在 P2 的 runtime job、worker / lease、日志恢复和 `SandboxRuntime` 抽象稳定后,先加速接入真实 Agent 与远程 Container。第一版采用 `Agent + workspace` 同 Remote Container 的 MVP前端仍只连 api-serverapi-server 创建 `agent_turn` jobRemote Agent Server 主动 claim jobContainer 内运行真实 Agent、工作区和 Agent 私有 shell最终收集 diff 转 `WebProjectPatch`,再由 api-server 校验并保存 snapshot。
Phase 3 不向用户开放 shellAgent 可使用完整 shell但必须经过 `ShellPolicy` / `CommandGuard` 接口。MVP policy 可以先是 allow-all但所有命令都要审计、脱敏和归入 job 日志。Container 不持有长期 LLM provider key真实模型调用通过 `LLM Broker`Container 也不能直接写 SpacetimeDB 或把 `/workspace` 提升为事实源。
真实 Agent、用户工作区、Agent 私有 shell、ShellPolicy / CommandGuard、ToolBridge、LLM Broker 和后续容器拆分的长期架构见 [【技术方案】EditorAgent远程Agent与工作区沙箱架构-2026-06-17.md](./【技术方案】EditorAgent远程Agent与工作区沙箱架构-2026-06-17.md)。P3 可执行计划见 [../planning/【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md](../planning/【开发计划】EditorAgentP3远程AgentContainerMVP计划-2026-06-17.md)。后续将同容器 MVP 替换为 `Agent Container + Workspace Container + ToolBridge sidecar` 时,只替换执行面适配器,不推翻 snapshot / patch / preview 控制面。
### Phase 4受控依赖安装
支持白名单依赖、lockfile 固化、依赖层缓存、安装失败可读错误、依赖封禁和审计。
### Phase 4:实时体验增强
### Phase 5:实时体验增强
在安全评审后再做长驻 dev server、HMR、WebSocket 代理、端口租约和空闲回收。
### Phase 5:作品化发布
### Phase 6:作品化发布
将通过安全门禁的 Web project 接入平台作品类型。发布必须重新构建 immutable artifact不直接提升临时预览 artifact。

View File

@@ -205,28 +205,38 @@ MVP 不支持:
```bash
cargo test -p api-server web_project --manifest-path server-rs/Cargo.toml
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 web-project-runner --manifest-path server-rs/Cargo.toml
```
前端:
```bash
npm run test -- src/services/sseStream.test.ts
npm run test -- src/components/editor/agent
npm run test -- src/services/web-project src/components/editor/agent --reporter verbose
npm run typecheck
npm run check:encoding
git diff --check
```
P2 schema 与 smoke 前置检查:
```bash
npm run spacetime:generate
npm run check:spacetime-schema
npm run check:editor-agent-p2-smoke
```
浏览器 smoke
```text
打开 /editor/agent
创建模板项目
提交一次 AI patch
等待静态构建成功
提交“做一个蓝色计数按钮页面”
等待 runtime job succeeded
确认 iframe 展示新预览
点击预览中的计数按钮,文案从 `已点击 0 次` 变为 `已点击 1 次`
提交一次故意破坏构建的 patch
确认错误出现且上一版预览仍保留
刷新页面确认项目、日志和 active preview 可恢复
提交“破坏构建”
确认 failed job 不覆盖上一版 preview
连续提交两次并确认旧 job 不覆盖新 preview
取消一个未完成 job
刷新页面确认 project、active snapshot、active preview、job 状态和日志恢复
```

View File

@@ -429,6 +429,46 @@ npm run check:server-rs-ddd
- Rust 结构体:`DatabaseMigrationOperator`
- 源码:`server-rs/crates/spacetime-module/src/migration.rs`
### `web_project`
- Rust 结构体:`WebProject`
- 源码:`server-rs/crates/spacetime-module/src/web_project.rs`
- 说明:`/editor/agent` Web 工程项目真相表,保存 owner、标题、固定模板 key、active snapshot 和 active preview buildsnapshot 仍是工程文件事实源artifact / runner 临时目录不得反向成为业务真相。
- 索引:`by_web_project_owner_user_id` 用于当前用户工程读取。
### `web_project_snapshot`
- Rust 结构体:`WebProjectSnapshotRow`
- 源码:`server-rs/crates/spacetime-module/src/web_project.rs`
- 说明Web 工程版本化 snapshot 表,保存固定模板工程文件 JSON、父 snapshot、创建来源和补丁摘要。P2 之后 sandbox workspace 只能从 snapshot hydratesandbox diff 必须转为 `WebProjectPatch` 并经 api-server 校验后保存为新 snapshot。
- 索引:`by_web_project_snapshot_project_id``by_web_project_snapshot_owner_user_id`
### `web_project_preview_build`
- Rust 结构体:`WebProjectPreviewBuildRow`
- 源码:`server-rs/crates/spacetime-module/src/web_project.rs`
- 说明P1 preview build 记录表,保存构建状态、短日志 JSON、artifact、preview token 和 preview URL。P2-04 起创建 preview build 的 HTTP 入口只负责创建该记录并入队 `web_project_runtime_job(kind=preview_build)`,不再在请求内占用进程 slot 或启动 runner该表继续作为 preview build 对外契约和 active preview 指针来源,`active_preview_build_id` 只应在后续 worker 成功写回 artifact / preview token 后推进。
- 索引:`by_web_project_preview_build_project_id``by_web_project_preview_build_snapshot_id``by_web_project_preview_build_owner_user_id`
### `web_project_runtime_job`
- Rust 结构体:`WebProjectRuntimeJobRow`
- 源码:`server-rs/crates/spacetime-module/src/web_project.rs`
- 说明Editor Agent P2 Web Project 专用 runtime job 表,首期承接 `preview_build` 后台任务生命周期,保存 `queued / running / succeeded / failed / cancelled / expired / stale` 状态、attempt、worker lease、取消请求、stale 原因、artifact / preview build 关联和错误摘要。该表只描述执行任务,不替代 `web_project_snapshot` 作为工程事实源。
- 状态机P2-02 已在 SpacetimeDB procedure 中收口 `create / get / claim / renew / complete / fail / cancel / stale / expire / append log`;所有入口必须由 Web Project service identity 调用worker 写回必须匹配 `worker_id + lease_token`,成功完成前必须校验 `snapshot_id == web_project.active_snapshot_id`,否则只能降级为 `stale`
- Facade/APIP2-03 起 `api-server` 只能通过 `spacetime-client` facade 操作 runtime jobHTTP 只暴露创建、读取、取消、未终态列表和日志分页,不直接下发 worker `lease_token`。新增只读 procedure `list_open_web_project_runtime_jobs_and_return` 用于刷新恢复时读取当前工程未终态任务。P2-04 起 preview build 创建响应在保留 `build` 的同时可返回 `runtimeJob`,用于前端恢复链路识别已入队任务;没有 worker 时任务保持 queued不由 api-server 假装构建成功。
- WorkerP2-05 起 `api-server` 支持 `GENARRATIVE_PROCESS_ROLE=web-project-runtime-worker` 独立运行 Web Project runtime worker`all` 角色也会并行启动该 worker。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`。P2-06 起绑定 `preview_build_id``preview_build` job 会进入 `TempDirBuildRuntime`hydrate snapshot 后调用受控 `web-project-runner` 子进程;无 preview build 绑定的内部 job 仍可按 dry-run 配置完成或失败。P2-07 起 runner 成功路径统一调用 `complete_web_project_preview_build_runtime_job_and_return` 组合 procedure在同一 SpacetimeDB transaction 中写 runtime job、preview build 和 `active_preview_build_id`;写入 succeeded 前必须重新校验 `job.snapshot_id == web_project.active_snapshot_id`,旧 snapshot job 即使 runner 成功也只能把 runtime job 与 preview build 写成 `stale`,不生成 preview URL、不覆盖 active preview。
- 取消 / 恢复 / smokeP2-08 起取消请求优先于 worker 成功或失败结果queued cancel 由 HTTP 同步收敛 linked preview buildrunning cancel 由 worker heartbeat 或最终续租处收敛;当前 `TempDirBuildRuntime` 不承诺强杀子进程。P2-09 起前端刷新和 SSE 断线恢复会先分页补拉 runtime job 持久日志,再恢复 build 状态和订阅。P2-10 起本地端到端 smoke 前置检查使用 `npm run check:editor-agent-p2-smoke` 读取 `.app/dev-stack.json` 并确认 dev 栈 ready真实浏览器仍需手工确认 happy path、失败保留预览、连续提交 stale guard、取消和刷新恢复。
- 索引:`by_web_project_runtime_job_project_id``by_web_project_runtime_job_snapshot_id``by_web_project_runtime_job_owner_user_id``by_web_project_runtime_job_status`
### `web_project_runtime_job_log`
- Rust 结构体:`WebProjectRuntimeJobLogRow`
- 源码:`server-rs/crates/spacetime-module/src/web_project.rs`
- 说明Editor Agent P2 runtime job 持久日志表,按 `job_id + sequence` 支持日志分页、SSE 断线补拉和刷新恢复;日志必须脱敏,不能包含平台 token、完整宿主路径或环境变量 dump。
- Facade/APIP2-03 起日志分页通过 `spacetime-client` facade 调用 `list_web_project_runtime_job_logs_and_return`HTTP `GET /api/runtime/web-project/runtime-jobs/{job_id}/logs?afterSequence=&limit=` 返回 `logs / nextAfterSequence / hasMore`
- 索引:`by_web_project_runtime_job_log_job_id``by_web_project_runtime_job_log_project_id``by_web_project_runtime_job_log_owner_user_id`
### `web_project_service_identity`
- Rust 结构体:`WebProjectServiceIdentity`

View File

@@ -50,6 +50,15 @@ Linux 本机多用户并发开发时,`npm run dev` 和 `npm run dev:*` 单模
后端日志默认写入 `logs/api-server/`。后端 API smoke 使用 `npm run dev:api-server` 并检查 `/healthz`;需要确认实例可接生产流量时检查 `/readyz`。不要使用旧 `api-server:maincloud` 或任何 `GENARRATIVE_SPACETIME_MAINCLOUD_*` 口径。
`/editor/agent` P2 端到端 smoke 不能从裸 `npm run dev:api-server` 开始。`dev:api-server` 只启动 Rust API 进程,会在启动前向已存在的 SpacetimeDB 请求 `/v1/identity` 并确认 Web Project service identity它不会自动启动 standalone也不会发布当前 `spacetime-module`。需要验证 Web Project runtime job、worker、preview gateway 和浏览器 iframe 时,优先运行:
```bash
npm run dev -- --no-interactive --api-timeout-seconds 900
npm run check:editor-agent-p2-smoke
```
完整 `npm run dev` 会为同一个 Rust api-server 进程注入 `GENARRATIVE_PROCESS_ROLE=all`,本地同时启动 HTTP、外部生成 worker 和 Web Project runtime worker但仍不会启动 external generation controller生产默认仍保持 `api` 或独立 worker 进程。若需要拆分终端,先运行 `npm run dev:spacetime` 等待模块发布完成,再运行 `npm run dev:api-server``npm run dev:web`;手动单独启动 `GENARRATIVE_PROCESS_ROLE=web-project-runtime-worker` 时必须复用本轮 dev 脚本创建的 `GENARRATIVE_SPACETIME_TOKEN`,否则订阅 / procedure 调用会 401。Windows native 首次冷编译 / 发布 `spacetime-module` 可能超过 5 分钟,应预留 10-15 分钟5 分钟内仍停在 publish 阶段通常不代表 P2 业务代码失败。`.app/dev-stack.json` 里的实际 web / api-server / spacetime / preview gateway URL 是 smoke 和浏览器访问的准确信息。
开发态 `npm run dev``npm run dev:api-server` 会默认注入 `GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED=true`,因此密码登录在本地开发环境可直接注册未知手机号账号;生产环境仍按 `api-server` 配置默认关闭该开关。
本地 `npm run dev``npm run dev:api-server` 默认保留 inline 开发体验:未显式设置 `GENARRATIVE_EXTERNAL_GENERATION_MODE=queue` 时,外部生成 handler 会同步复用 worker executor完成后返回 `completed`,便于快速确认 provider、OSS 和 SpacetimeDB 写回链路。inline 不创建 `external_generation_job`,也不能验证 worker lease、队列等待展示或动态扩缩容。