diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md index 854c60bd..704e04a0 100644 --- a/.hermes/shared-memory/decision-log.md +++ b/.hermes/shared-memory/decision-log.md @@ -2246,3 +2246,10 @@ - 影响范围:`server-rs/crates/spacetime-module/src/editor_project_storage.rs`、`server-rs/crates/spacetime-client/src/editor_project.rs`、`server-rs/crates/api-server/src/editor_project.rs`、`src/services/image-editor/editorProjectClient.ts`、`src/components/image-editor/ImageCanvasEditorView.tsx`、后端数据契约文档和图片画布前端技术方案。 - 验证方式:`npm run spacetime:generate -- --rust-only`、`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx src/services/image-editor/editorProjectClient.test.ts`、`npm run typecheck`、`npm run check:spacetime-schema`、`npm run check:encoding`、`cargo check -p spacetime-client -p api-server --manifest-path server-rs/Cargo.toml`、`git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 + +## 2026-06-16 Editor Agent P2 先补持久任务再接 SandboxRuntime + +- 背景:`/editor/agent` P1 已用 Mock Agent 跑通 snapshot、patch、runner、artifact 和独立 preview;后续讨论需要支持 Docker sandbox、Shell 和开源 coding agent,但这些能力都依赖可靠的任务生命周期、日志恢复、取消和崩溃恢复。 +- 决策:P2 不直接跳到长驻 Docker 工作区或 Shell,而是先新增 `web_project_runtime_job`、worker / lease / controller、日志分页和 SSE 重连,再抽象 `SandboxRuntime`。第一版 SandboxRuntime 可继续复用当前 temp-dir runner,Docker / gVisor / Kata / microVM 作为后续执行面实现;snapshot 仍是唯一事实源,sandbox diff 必须转为 `WebProjectPatch` 并经过 api-server 校验后落库。 +- 影响范围:`docs/planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md`、`docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md`、`docs/technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md`、Web Project runtime job 后续实现。 +- 验证方式:P2 落地时需覆盖 runtime job 状态机、worker claim / renew / complete / fail、failed / cancelled / expired / stale 不覆盖 active preview、日志分页 / SSE 重连、真实浏览器 happy path 和破坏构建保留上一版预览。 diff --git a/docs/README.md b/docs/README.md index fe216f9e..c01096fd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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)。 +`/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/canvas` 图片画布编辑器的画布素材 ZIP 导出能力,入口放在右上角标题栏下载图标内,第一版采用前端 JSZip 打包画布中有效图层引用的上传图、生成图和修改结果,方案见 [【前端架构】图片画布素材导出方案-2026-06-15.md](./technical/【前端架构】图片画布素材导出方案-2026-06-15.md)。 diff --git a/docs/planning/README.md b/docs/planning/README.md index a97874e0..49cf0d9c 100644 --- a/docs/planning/README.md +++ b/docs/planning/README.md @@ -6,6 +6,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 抽象计划。 ## 维护规则 diff --git a/docs/planning/【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md b/docs/planning/【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md index 3f047864..849f9df9 100644 --- a/docs/planning/【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md +++ b/docs/planning/【开发计划】EditorAgentMockAgentP1可执行开发计划-2026-06-15.md @@ -638,4 +638,6 @@ P2 优先接: - [ ] 构建日志分页与可重连 SSE。 - [ ] 真实 Agent 接入,但继续产出同一结构化 patch。 +P2 的可执行拆分已经单独沉淀到 [【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md](./【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md)。后续应先按该计划补 runtime job / worker / lease,再接 Docker sandbox、Shell 或真实 coding agent,避免跳过任务生命周期基础设施。 + 真实 Agent 接入前不得扩大 P1 的执行权限;任意依赖安装、HMR、dev server 和作品化发布必须进入后续独立评审。 diff --git a/docs/planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md b/docs/planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md new file mode 100644 index 00000000..ebc62080 --- /dev/null +++ b/docs/planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md @@ -0,0 +1,337 @@ +# Editor Agent P2 持久任务与 SandboxRuntime 计划 + +更新时间:`2026-06-16` + +## 计划定位 + +本文承接 `/editor/agent` Mock Agent P1。P1 已经跑通“用户输入 -> 结构化 patch -> snapshot -> runner 构建 -> artifact -> 独立 preview”的最小闭环;P2 的目标不是先做更聪明的 AI,也不是直接开放 Shell,而是把 P1 的一次性构建升级为可恢复、可取消、可审计的后台任务系统,并为后续 Docker sandbox、Shell 和真实 coding agent 留出稳定接口。 + +一句话: + +```text +P2 先把执行任务做可靠,再把执行环境逐步替换成更强沙箱。 +``` + +## 当前基线 + +P1 当前形态: + +```text +前端 /editor/agent + -> Mock Agent 返回 WebProjectPatch + -> api-server 校验 patch + -> 保存 WebProjectSnapshot + -> 创建 preview build + -> api-server 触发一次性 web-project-runner + -> runner 临时展开 snapshot 构建 + -> 写入 immutable artifact + -> preview gateway 签发 previewUrl +``` + +当前工作区关系: + +- `snapshot` 是事实源,保存在 SpacetimeDB。 +- runner 构建时创建一次性临时目录,构建完成后删除。 +- artifact root 只保存构建产物,不作为用户工作区。 +- Agent 通过结构化 patch 连接工作区,不直接读写本机文件,也不执行 Shell。 + +## P2 目标 + +- 新增 Web Project 专用持久任务模型,支撑 preview build、后续 sandbox 命令和同步任务。 +- 将 preview build 从“请求内触发一次性 runner”迁移到 `api-server -> job -> worker` 模式。 +- 支持任务取消、stale、expired、runner crash 恢复。 +- 持久化构建日志,支持分页读取和 SSE 断线重连。 +- 抽象 `SandboxRuntime` 接口,为后续 Docker / gVisor / Kata / microVM 工作区替换留口。 +- 保持 snapshot 作为唯一事实源,sandbox 文件变化必须通过 diff / patch 校验后保存为新 snapshot。 + +## P2 非目标 + +P2 明确不做: + +- 不开放用户或 Agent 的交互式 Shell。 +- 不允许 Agent 直接执行宿主机命令。 +- 不开放任意 npm 依赖、用户 registry、用户 lockfile 或自定义 build script。 +- 不做 HMR、长驻 dev server、WebSocket 端口代理。 +- 不做 Web project 作品化发布。 +- 不把 Docker workspace 直接提升为事实源。 +- 不把真实 coding agent 深度绑定到宿主文件系统。 + +这些能力进入后续阶段前必须单独补安全模型、验收清单和资源隔离方案。 + +## Snapshot 与 Sandbox 的关系 + +P2 继续保持以下关系: + +```text +snapshot = 项目版本化事实源 +sandbox = 某个 snapshot 展开后的隔离执行环境 +``` + +标准流转: + +```text +读取 active snapshot + -> hydrate 到 sandbox workspace + -> 在 sandbox 中 build / test / 后续 shell + -> 收集 sandbox diff + -> 转成 WebProjectPatch + -> api-server 校验 patch + -> 保存新 snapshot +``` + +因此 P2 的第一步不是先做长驻 Docker workspace,而是先做能管理执行生命周期的持久 job。没有 job / worker / lease,sandbox 会缺少创建、回收、取消、超时、崩溃恢复和日志恢复的基础设施。 + +## 总体执行顺序 + +| 波次 | 主题 | 目标 | +| --- | --- | --- | +| P2a | 持久任务表与状态机 | 新增 `web_project_runtime_job`,固化状态、日志和 owner 校验 | +| P2b | Worker / lease / controller | 让任务由 worker 领取、续租、完成、失败和过期重领 | +| P2c | Preview build 迁移 | 将 P1 preview build 接到 runtime job,保留现有 runner 和 artifact 语义 | +| P2d | SandboxRuntime 抽象 | 定义执行环境接口,第一版可用当前 temp-dir runner 实现 | +| P2e | 日志和恢复 | 日志分页、SSE 重连、刷新恢复、取消和 stale 验收 | +| P2f | 文档与门禁 | 更新架构文档、验收清单和 smoke 流程 | + +## P2a:持久任务表与状态机 + +新增 SpacetimeDB 表建议: + +```text +web_project_runtime_job +web_project_runtime_job_log +``` + +`web_project_runtime_job` 建议字段: + +- `job_id` +- `project_id` +- `snapshot_id` +- `owner_user_id` +- `job_kind`:P2 先支持 `preview_build` +- `status`:`queued | running | succeeded | failed | cancelled | expired | stale` +- `attempt` +- `worker_id` +- `lease_token` +- `lease_expires_at` +- `cancel_requested_at` +- `stale_reason` +- `artifact_id` +- `preview_build_id` +- `error_summary` +- `created_at` +- `started_at` +- `finished_at` +- `updated_at` + +`web_project_runtime_job_log` 建议字段: + +- `log_id` +- `job_id` +- `project_id` +- `owner_user_id` +- `sequence` +- `level` +- `message` +- `created_at` + +状态机规则: + +- `queued` 只能被 worker claim 为 `running`,或被用户取消为 `cancelled`。 +- `running` 只能由持有当前 `worker_id + lease_token` 的 worker 完成、失败或续租。 +- lease 过期的 `running` 可以被重新 claim,`attempt` 递增。 +- 新 snapshot 创建后,旧 snapshot 的未终态 preview job 标记为 `stale` 或保持旧日志但不得推进 active preview。 +- `succeeded` 只有在 `snapshot_id == project.active_snapshot_id` 时才能推进 active preview。 +- `failed / cancelled / expired / stale` 永远不能覆盖上一版成功 preview。 + +## P2b:Worker / Lease / Controller + +新增 Web Project runtime worker 角色,避免把 Web 工程构建塞进 `external_generation_job`。 + +建议职责: + +- `api-server`:鉴权、创建 job、读取状态、取消任务、SSE / 日志查询。 +- `web-project-runtime-worker`:claim job、hydrate snapshot、调用 runner、写日志、回写状态。 +- `web-project-runtime-controller`:后续按队列压力管理 worker 数量;P2 本地可先手动或随 `api-server` 开发态启动。 + +worker claim 规则: + +- 只领取 `queued` 或 lease 已过期的 `running`。 +- claim 时写入 `worker_id`、新的 `lease_token`、`lease_expires_at`、`started_at` 和 `attempt`。 +- renew / complete / fail 必须带同一个 `worker_id + lease_token`。 +- SpacetimeDB 事务使用数据库时间判断 lease,不信任 worker 本机绝对时间。 + +## P2c:Preview Build 迁移 + +P2 不推翻 P1 runner,而是把触发方式迁移为持久 job: + +```text +POST /api/runtime/web-project/projects/{projectId}/preview-builds + -> 创建 web_project_preview_build + -> 创建 web_project_runtime_job(kind=preview_build) + -> worker claim job + -> worker 调 web-project-runner + -> 写 artifact / preview token / previewUrl + -> 成功时推进 active preview +``` + +兼容要求: + +- 前端仍通过现有 preview build API 创建构建。 +- SSE 事件类型保持 P1 口径,底层事件来源改为 runtime job log / status。 +- preview URL 仍由后端返回,前端不拼接。 +- preview gateway 的独立 origin、CSP、CORS 和 iframe `sandbox="allow-scripts"` 保持不变。 + +## P2d:SandboxRuntime 抽象 + +P2 应先抽象接口,不急于直接开放 Docker Shell。 + +建议接口: + +```text +SandboxRuntime + createWorkspace(projectId, snapshotId) + hydrateSnapshot(workspaceId, snapshot) + runBuild(workspaceId, jobContext) + execCommand(workspaceId, commandSpec) + collectDiff(workspaceId) + destroyWorkspace(workspaceId) +``` + +第一版实现: + +- `TempDirBuildRuntime`:复用 P1 runner 的一次性临时目录构建。 + +后续实现: + +- `DockerSandboxRuntime`:每个 project/session 独立容器或 volume。 +- `GVisorSandboxRuntime` / `KataSandboxRuntime` / `MicroVmSandboxRuntime`:生产安全要求提高后替换。 + +接口边界: + +- sandbox 不持有平台密钥、用户 cookie、SpacetimeDB 直连 token 或 OSS 写权限。 +- sandbox 只能访问当前项目 workspace。 +- sandbox 文件变化不能直接落库,必须转成 patch 后走现有校验。 +- sandbox 网络默认 deny,后续如需 npm mirror 必须走白名单。 + +## P2e:日志、取消与恢复 + +日志要求: + +- worker 写入持久日志表。 +- API 支持按 `jobId + cursor/sequence` 分页读取日志。 +- SSE 断线后,前端先读取 job 状态和缺失日志,再重新订阅。 +- 日志限长、脱敏,不能包含平台 token、完整宿主路径和环境变量 dump。 + +取消要求: + +- 用户取消时写 `cancel_requested_at` 并把未开始 job 标为 `cancelled`。 +- running job 由 worker 检测取消请求后停止 runner 并回写 `cancelled`。 +- P2 若暂不支持强杀子进程,也必须明确返回“取消请求已记录 / 等待 runner 收敛”的状态。 + +刷新恢复要求: + +- 打开 `/editor/agent?projectId=...` 后读取 project、active snapshot、active preview 和未终态 jobs。 +- 前端不会因为旧 job 成功覆盖新 snapshot 的 preview。 +- 页面刷新后日志和 build 状态以后端为准。 + +## 真实 Agent 与 Shell 的后续接入 + +P2 可以为真实 Agent 预留工具接口,但不默认接真实 coding agent。 + +推荐后续形态: + +```text +Agent service + -> sandbox file/shell tools + -> WebProjectPatch + -> api-server 校验 + -> snapshot +``` + +如果后续改编 Codex、opencode 等开源 coding agent: + +- Agent 不能直接以宿主仓库为 cwd。 +- 读写文件工具必须指向 sandbox workspace 或 Sandbox FS API。 +- shell tool 必须通过 runtime job 在 sandbox worker 中执行。 +- agent 修改后的文件 diff 必须转成 `WebProjectPatch` 并校验后保存。 + +## 验收场景 + +P2 最小验收: + +1. 打开 `/editor/agent`,创建或恢复 Web Project。 +2. 输入“做一个蓝色计数按钮页面”。 +3. 后端创建 `web_project_runtime_job(kind=preview_build)`。 +4. worker claim job 并写入 running 日志。 +5. runner 构建成功,job 进入 `succeeded`。 +6. preview URL 可访问,iframe 切到成功预览。 +7. 输入“破坏构建”,新 job 进入 `failed`。 +8. 上一版成功 preview 保留。 +9. 连续提交两次,旧 job 不覆盖新 snapshot preview。 +10. 刷新页面后恢复 project、active snapshot、active preview、job 状态和日志。 +11. 取消 queued job 后状态为 `cancelled`。 +12. 模拟 worker crash / lease 过期后任务可重领或进入确定失败 / expired 状态。 + +## 验证命令 + +涉及 schema: + +```bash +npm run spacetime:generate +npm run check:spacetime-schema +``` + +后端: + +```bash +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 +cargo check -p api-server --manifest-path server-rs/Cargo.toml +``` + +前端: + +```bash +npm run test -- src/services/web-project src/components/editor/agent --reporter verbose +npm run typecheck +npm run check:encoding +git diff --check +``` + +浏览器 smoke: + +```text +打开 /editor/agent +提交计数按钮指令 +等待 runtime job succeeded +确认 iframe 预览和按钮点击 +提交破坏构建 +确认 failed job 不覆盖上一版 preview +刷新页面确认状态和日志恢复 +``` + +## 风险与处理 + +| 风险 | 处理 | +| --- | --- | +| 直接先做 Docker workspace 导致生命周期失控 | 先做 runtime job / worker / lease,再接 SandboxRuntime | +| sandbox 文件成为第二事实源 | snapshot 仍为事实源,sandbox diff 必须转 patch 并校验 | +| 真实 Agent 绕过 patch 校验 | Agent 只产出 patch 或工具结果,保存仍走 api-server 校验 | +| 旧 job 成功覆盖新 preview | 成功推进 active preview 必须校验 active snapshot | +| worker crash 后 job 永久 running | lease 过期可重领或标记 expired | +| 日志丢失导致刷新不可恢复 | 日志进入持久表,SSE 只做实时补充 | +| 把 Web 工程任务塞进 external_generation_job | 使用 Web Project 专用 runtime job,避免语义污染 | + +## 完成定义 + +P2 完成必须满足: + +- `web_project_runtime_job` 和日志持久化落地。 +- preview build 已由 runtime worker 执行。 +- lease / claim / renew / complete / fail / expired 基础流转可测。 +- failed / cancelled / expired / stale 不覆盖 active preview。 +- 日志分页和 SSE 重连可恢复。 +- P1 的真实浏览器 happy path 和破坏构建保留预览继续通过。 +- `SandboxRuntime` 接口已形成,后续 Docker / Shell / coding agent 不需要推翻 P1/P2 控制面。 diff --git a/docs/technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md b/docs/technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md index 8060abb5..1a8c341c 100644 --- a/docs/technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md +++ b/docs/technical/【技术方案】EditorAgentMockAgentP1落地计划-2026-06-15.md @@ -527,4 +527,6 @@ P1 完成后进入 P2 时,优先补: - 构建日志分页与可重连 SSE。 - 真实 Agent 接入,但继续产出同一结构化 patch。 +P2 的执行计划见 [../planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md](../planning/【开发计划】EditorAgentP2持久任务与SandboxRuntime计划-2026-06-16.md)。该计划明确 P2 先补持久任务、worker / lease 和日志恢复,并提前抽象 SandboxRuntime;Docker sandbox、Shell 和开源 coding agent 改编应作为后续执行面接入,不应绕过 snapshot / patch / preview 控制面。 + 真实 Agent 接入前不得扩大 P1 的执行权限;任意依赖安装、HMR、dev server 和作品化发布必须进入后续独立评审。 diff --git a/docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md b/docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md index 694bdb77..582ae6e1 100644 --- a/docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md +++ b/docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md @@ -338,6 +338,8 @@ draft snapshot 引入 `web_project_runtime_job`,按 lease / controller / worker 模式实现任务队列、取消、stale、刷新恢复和日志 SSE。 +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:受控依赖安装 支持白名单依赖、lockfile 固化、依赖层缓存、安装失败可读错误、依赖封禁和审计。