chore: release v1.30.0

This commit is contained in:
coso
2026-05-08 01:05:41 +08:00
parent 15b52e5674
commit 431dc22113
255 changed files with 46141 additions and 20772 deletions
+10 -3
View File
@@ -36,6 +36,7 @@ docs/prd/*
!docs/prd/tools/*.md
# Internal roadmap&gongzonghao (private)
docs/exec-plans/
docs/roadmap/*
!docs/roadmap/lime-service-skill-cloud-config-prd.md
!docs/roadmap/task/
@@ -59,8 +60,8 @@ docs/roadmap/*
!docs/roadmap/memory/*.md
!docs/roadmap/knowledge/
!docs/roadmap/knowledge/prd.md
!docs/roadmap/creaoai/
!docs/roadmap/creaoai/*.md
!docs/roadmap/skill-forge/
!docs/roadmap/skill-forge/*.md
!docs/roadmap/managed-objective/
!docs/roadmap/managed-objective/*.md
!docs/roadmap/ai-layered-design/
@@ -73,7 +74,13 @@ docs/knowledge
!docs/knowledge/
docs/knowledge/*
!docs/knowledge/README.md
docs/research/
!docs/knowledge/skills/
!docs/knowledge/skills/**
docs/knowledge/skills/.DS_Store
docs/knowledge/skills/**/.DS_Store
docs/research/*
!docs/research/skill-forge/
!docs/research/skill-forge/*.md
# Issues tracking (internal use only)
.issues/
+20 -9
View File
@@ -11,6 +11,14 @@
5. **持续垃圾回收** - 技术债按小额、持续方式偿还;差距追踪见 `docs/exec-plans/tech-debt-tracker.md`
6. **卡住时修环境,不是更用力** - 先补上下文、工具、约束,再继续实现;缺口也要写回 repo
## 工程协作方式
1. **默认以完整交付为单位** - 不把可自行判断的实现细节、下一步动作或可逆选择转嫁给用户;读代码、做判断、完成闭环后再报告结果
2. **少问但不越权** - 只有真实需求歧义、不可逆 / 高风险操作、生产环境影响、凭证缺失,或继续会明显偏离用户意图时才停下来询问
3. **结果汇报优先** - 收尾说明做了什么、为什么这样做、验证了什么、还剩什么缺口;避免过程性礼貌汇报
4. **任务完成标准优先** - 以可编译、类型正确、测试通过、功能真实可用作为完成依据;实现细节服从项目既有模式和当前主线目标
5. **不主动扩大承诺** - 不在完成后追问“要不要继续做 X/Y/Z”;如存在自然下一刀,只简短列出建议,等待用户明确要求
## 基础约束
1. **始终使用中文** - 回复、文档、代码注释默认使用中文;若文件已有其他注释语言,保持与现有代码库一致
@@ -40,15 +48,18 @@
## 执行与路线图
1. **主线任务先重述目标** - 用户要求“对齐路线图 / 继续主线”时,先说明当前主目标、阶段和下一刀
2. **每一刀都要可追踪** - 改动要么回挂到 `docs/roadmap/`,要么登记到 `docs/exec-plans/` 或技术债追踪
3. **清理不能替代交付** - 连续两轮主要在做治理减法后,下一轮优先回到未完成主线
4. **长任务必须落计划** - 超过一轮的实现、迁移、清理,写入 `docs/exec-plans/` 并持续更新进度日志
5. **主线冲突先清障,不保旧面** - current 规划与旧实现直接冲突时,先删或下线阻碍主线的旧页面、旧命名、旧命令、旧文档,再继续实现;不要为了“看起来兼容”保留双轨
6. **默认不为顺手问题偏航** - 已经选定本轮主线后,除非该问题直接阻塞当前交付、会让新改动变假配置/假入口,或用户明确要求,否则不要切去处理旁支优化、额外治理、零引用清理或“顺手再修一个”
7. **清理必须有主线收益句** - 任何治理/重构/删除动作,动手前都要能用一句话说明“它如何直接帮助当前主线交付”;如果说不出来,就记录为后续项而不是立即执行
8. **顺手项一次只收一刀** - 实现主线时即使发现多个周边问题,默认只处理其中最直接阻塞的一项;其余登记后立即回到主线,不串行深挖
9. **完成判定先看主线,再看周边** - 用户问“完成了么”时,先回答主线目标是否完成;周边清理、额外校验、可选优化必须单独标为“已做 / 未做”,不能混成“还差一点边角所以整体未完成”
10. **验证以证明交付为上限** - 校验应先覆盖当前改动的真实风险;在已经证明主线可交付后,不要因为还能继续跑更重检查,就无限追加验证并拖延收口
2. **先补主缺口再磨细节** - 多阶段主线未到可用闭环前,优先做直接提高整体完成度的缺口;协议 polish、错误分类、额外 seam、边缘校验、文案润色、内部抽象等梢枝末节,只有在阻塞主路径、会造成假入口/假配置,或用户明确要求时才做
3. **下一刀必须按目标增量排序** - 选择下一步前先列出 1-3 个未完成主问题,并优先选“对整体目标完成度提升最大”的一项;不要因为当前文件顺手、测试容易、局部更完整,就继续做低杠杆小项
4. **每一刀都要可追踪** - 改动要么回挂到 `docs/roadmap/`,要么登记到 `docs/exec-plans/` 或技术债追踪
5. **清理不能替代交付** - 连续两轮主要在做治理减法后,下一轮优先回到未完成主线
6. **长任务必须落计划** - 超过一轮的实现、迁移、清理,写入 `docs/exec-plans/` 并持续更新进度日志
7. **主线冲突先清障,不保旧面** - current 规划与旧实现直接冲突时,先删或下线阻碍主线的旧页面、旧命名、旧命令、旧文档,再继续实现;不要为了“看起来兼容”保留双轨
8. **默认不为顺手问题偏航** - 已经选定本轮主线后,除非该问题直接阻塞当前交付、会让新改动变假配置/假入口,或用户明确要求,否则不要切去处理旁支优化、额外治理、零引用清理或“顺手再修一个”
9. **清理必须有主线收益句** - 任何治理/重构/删除动作,动手前都要能用一句话说明“它如何直接帮助当前主线交付”;如果说不出来,就记录为后续项而不是立即执行
10. **顺手项一次只收一刀** - 实现主线时即使发现多个周边问题,默认只处理其中最直接阻塞的一项;其余登记后立即回到主线,不串行深挖
11. **完成判定先看主线,再看周边** - 用户问“完成了么”时,先回答主线目标是否完成;周边清理、额外校验、可选优化必须单独标为“已做 / 未做”,不能混成“还差一点边角所以整体未完成”
12. **验证以证明交付为上限** - 校验应先覆盖当前改动的真实风险;在已经证明主线可交付后,不要因为还能继续跑更重检查,就无限追加验证并拖延收口
13. **开发任务结束必须给完成度百分比** - 非纯问答的开发任务收尾时,必须给“本轮完成度:X%”,并说明主线目标是否完成、验证情况、剩余缺口和下一刀;路线图 / 长任务 / 多阶段主线还要额外给“整体目标完成度:Y%”,并说明百分比口径
## 文档导航
+76 -42
View File
@@ -1,75 +1,109 @@
## Lime v1.29.0
## Lime v1.30.0
发布日期:`2026-05-06`
发布日期:`2026-05-08`
### 发布概览
- 本次发布目标 tag 为 `v1.29.0`,重点推进 CREAOAI workspace skill runtime binding、显式 runtime enable、AI 图层化设计导出,以及 Memory / Skills / Scene Apps / Knowledge 工作台的主路径收口。
- 版本事实源已同步到 `1.29.0`:`package.json`、`package-lock.json`、`src-tauri/Cargo.toml`、`src-tauri/Cargo.lock`、`src-tauri/tauri.conf.json`、`src-tauri/tauri.conf.headless.json`、`@limecloud/lime-cli` npm wrapper 与 release updater 测试样例保持一致。
- 该版本继续坚持 current-first:workspace skill binding、Query Loop metadata、runtime enable、Evidence Pack、GUI review surface 与 mock/contract 都回到同一条运行时事实源,不新增平行执行入口。
- 本次发布按当前准备递交的完整暂存内容编写,范围包括版本发布、Skill Forge 主线、只读 HTTP/API 能力草案、Agent Runtime / Evidence Pack、Knowledge v2、AI 图层化设计、GUI 页面与质量脚本;机密执行计划不进入发布提交。
- 应用版本从 `1.29.0` 升级到 `1.30.0`,同步 `package.json`、`package-lock.json`、`src-tauri/Cargo.toml`、`src-tauri/Cargo.lock`、`src-tauri/tauri.conf.json`、`src-tauri/tauri.conf.headless.json`、`packages/lime-cli-npm/package.json` 与 `@limecloud/lime-cli` 发布示例。
- `CREAOAI` 相关 research、roadmap 与 exec plan 已收口为 `Skill Forge` 命名,并同步导航、路线图、执行计划和治理文档,减少旧命名与 current 主线并存。
- 本版继续坚持 current-first:capability draft、registered skill、runtime binding、Query Loop metadata、runtime enable、Evidence Pack、Knowledge 与 Layered Design 都回到仓库内单一事实源,不新增 legacy / compat 平行执行入口。
### 用户可见更新
#### 1. Workspace Skill Runtime Binding
#### 1. Skill Forge 与能力沉淀闭环
- 新增 workspace skill binding readiness 投影,Skills 工作台可区分已注册、可手动启用、缺少输入或仍需治理的 skill。
- Chat request metadata 增加 workspace skill bindings 规划上下文,让 Query Loop 能看到当前 workspace 内可用能力,但不会自动打开执行权限。
- Runtime enable 只在当前 session scope 内显式启用 ready binding,并把 SkillTool 裁剪到 allowlist,避免 marketplace、scheduler 或旧平行命令绕过授权边界。
- Skills / Capability Draft UI 补充 automation draft、agent envelope draft 与 registered skill 状态回归,减少“已注册”和“可执行”之间的语义混淆。
- 新增并落地 `Skill Forge` 研究、路线图、架构图与产品原型入口,替换旧 `docs/research/creaoai` 与 `docs/roadmap/creaoai` 命名。
- Capability Draft 主线从 authoring、verification、registration、registered discovery、runtime binding、Query Loop metadata、runtime enable 延伸到 managed agent envelope 与 prompt-to-artifact smoke。
- Skills 工作台与 Workspace Registered Skills 面板展示更完整的 registered skill provenance、binding readiness、Agent envelope draft、automation draft、approval request 与 session gate 状态。
- “本回合启用”继续只走 `agent_runtime_submit_turn` metadata + SkillTool allowlist,不创建 marketplace、scheduler 或平行 runtime command。
- Managed Job / Agent Envelope 草案只在 evidence 与 owner audit 满足条件时进入可转化语义,避免把“已注册 / 已发现 / 已校验”误读成“已自动执行”。
#### 2. AI 图层化设计与导出
#### 2. 只读 HTTP/API 能力草案
- 图层化设计主链继续完善文档、artifact、flat image、extraction、zip export 与 image task 写回能力。
- Design Canvas 与 Layered Design Project API 增加稳定回归,覆盖图层文档编辑、导出、扁平化与图片任务关联。
- 新增 layered design Tauri command 入口,前端、mock 与项目 artifact 消费方继续围绕 `LayeredDesignDocument` 这个事实源收敛。
- Capability Draft verification 增加只读 HTTP/API gate:fixture input、fixture 文件、expected output、dry-run 入口、expected-output binding、offline dry-run、no-credentials、session authorization policy、credential reference 与 execution preflight。
- 新增只读 HTTP/API authoring 模板与 smoke:`scripts/lib/readonly-http-api-draft-template.mjs`、`scripts/readonly-http-api-smoke.mjs`,统一正向样例和负向 gate 样例。
- 注册摘要新增 `verification_gates` 与 `approval_requests`,把 passed preflight provenance 投影到 registered discovery 与 Workspace 面板,而不是发起真实 HTTP。
- Approval artifact 新增 consumption gate、credential resolver、consumption input schema、session input intake、submission contract、dry preflight plan 与 controlled GET preflight。
- 新增 session 输入提交与受控 GET 执行命令:`capability_draft_submit_approval_session_inputs`、`capability_draft_execute_controlled_get`;受控 GET 仅允许一次性 session 输入、GET 方法、非敏感 evidence 与 request / response hash。
- loopback / localhost 受控 GET 测试绕过系统代理,避免本地 fixture 被代理环境污染。
#### 3. Agent、Memory 与工作区体验
#### 3. Prompt-to-Artifact 与证据闭环
- Agent Chat、MessageList、Harness 状态、runtime review decision、thread grouping 与 workspace scene runtime 继续补稳定回归。
- Memory 页面完成大幅整理,任务建议、inspiration projection 与工作区入口更接近长期使用场景。
- Scene Apps、Knowledge、Settings、Onboarding 与 Sidebar 的主路径继续补齐状态、导航和测试断言,降低 GUI 启动与页面切换漂移。
- 增加 startup layout / diagnostics 工具与 smoke 脚本,用于定位启动布局和页面可见性问题。
- 新增 `scripts/prompt-to-artifact-smoke.mjs`,通过 DevBridge 串联 `create -> verify -> register -> list_registered_skills -> list_workspace_skill_bindings`,输出结构化 smoke summary。
- Prompt-to-Artifact smoke 记录结构化样例证据,明确 demo / smoke / evidence 不扩展 P4 runtime,也不打开 scheduler、queue 或 Marketplace。
- Evidence Pack completion audit 纳入受控 GET evidence 计数、执行状态分布、owner run、Workspace Skill ToolCall 与 artifact evidence;单一成功 run 不再被当成 completed。
- Runtime evidence summary、`runtime.json`、`artifacts.json`、`summary.md` 与前端 normalizer 消费同一份 completion audit 结构。
#### 4. Agent Runtime、聊天与执行可靠性
- Agent Chat 补齐 session state、history、timeline、thread reliability、stream runtime handler、text delta controller、action request A2UI、internal artifact visibility 与 task runtime 回归。
- 新增 `runtimeActionConfirmation` 与更稳定的 runtime action / permission confirmation 展示,避免把被拒绝的权限确认误写成成功证据。
- Agent thread timeline、reliability panel、ChatSidebar、Inputbar runtime status line 与 MessageList 对排队任务、内部 artifact、action request 和线程读模型状态的展示更一致。
- Execution run command、agent run DAO、execution tracker 与 runtime evidence service 补齐 owner metadata、status history 和 review / analysis handoff 证据链。
- `agent_runtime_export_evidence_pack`、analysis handoff、review decision template、review decision save 与 replay case 的前端 API 回归继续走统一 `agent_runtime_*` 命令面。
#### 5. Knowledge、Memory 与工作区页面
- 新增 `docs/roadmap/knowledge/prd-v2.md` 与可视化图,明确 Knowledge v2 采用 Skills-first、persona / data 双族、Builder Skill、KnowledgePack resolver 与 context run 证据链。
- 新增 `docs/knowledge/skills/personal-ip-knowledge-builder` 内置 Builder Skill 包,包含 `SKILL.md`、OpenAI agent 配置、个人 IP 知识骨架、访谈问题、模板、质量清单与 `docx_to_markdown.py` 转换脚本。
- Knowledge Rust crate 与前端 API 扩展 pack metadata、primary document、compile run、runtime mode、fenced context、source anchors、context resolver warning 与 context run validation。
- Knowledge GUI smoke 与 DevBridge mock 对齐 `knowledge_*` current 命令,页面回归覆盖 pack label、visibility 与默认工作区准备态。
- Memory 页面更新任务建议、灵感投影、工作区入口和状态文案;Scene Apps 页面、详情、运行列表、scorecard、governance 与 project pack runtime 面板做了大范围结构整理和回归。
- Sidebar、Skills Workspace、Workspace Canvas 与 Scene Apps 测试补齐导航、页面状态和主路径可见性断言。
#### 6. AI 图层化设计与 Design Canvas
- Layered Design 新增 structured analyzer、native analysis API、model slot config/runtime/transport、provider capability gate 与 worker-first analyzer 组合能力。
- 新增 subject matting、clean plate、text OCR、structured analyzer worker、worker client、heuristic worker seam 与 deterministic fallback,支持 mask、clean plate、TextLayer 与候选层质量评估。
- Design Canvas 增加扁平图拆层、候选确认、extraction quality、PSD / zip export、artifact 写回、worker 状态、model slot readiness 与 native analyzer fallback 的可见主路径。
- Tauri `layered_design_*` 命令、前端 `layeredDesignAnalysis` API、browser mock、`DesignCanvas.test.tsx` 与 `design-canvas-smoke` 同步更新,减少 UI、mock 与 Rust command 漂移。
- `src-tauri/Cargo.toml` / `Cargo.lock` 同步 `image` 相关依赖,用于图层分析与导出链路。
### 开发者与治理更新
#### 1. 命令边界与 contract
#### 1. 命令边界、Mock 与 Contract
- 新增并同步 `agent_runtime_list_workspace_skill_bindings` 相关命令与 runtime schema:前端 API、generated manifest、Rust 注册、DevBridge dispatcher、治理目录册、`mockPriorityCommands` 与默认 mock 保持一致。
- 新增 runtime skill binding service 与 prompt projection 测试,明确 readiness metadata 只读、runtime enable 显式、SkillTool gate allowlist 三个边界。
- `npm run test:contracts` 继续覆盖 agent runtime command manifest、command catalog、harness contract、modality contract 与 cleanup report,防止命令面漂移。
- Capability Draft、Agent Runtime、Knowledge、Layered Design、DevBridge dispatcher、`mockPriorityCommands`、`defaultMocks` 与 `agentCommandCatalog` 按本轮新增命令和返回结构同步。
- `src/lib/api/capabilityDrafts.ts` 扩展大量 snake_case / camelCase normalizer,覆盖 verification evidence、approval requests、session input、dry preflight、controlled GET preflight 与执行结果。
- `src/lib/tauri-mock/core.ts` 和相关测试对齐无后端 GUI 预览,避免浏览器模式把未满足 gate 的能力显示为假通过。
- `src/lib/base-setup` 的 service skill catalog projection / seeded package / adapter 更新 Skill Forge 与 Service Skill 口径。
#### 2. 路线图与执行计划
#### 2. 质量脚本与发布管线
- 新增 CREAOAI P3C runtime binding、P3D query loop metadata、P3E tool runtime authorization、P4 managed agent envelope 与 completion audit 执行计划。
- CreoAI research / roadmap 文档更新编码代理层、工具编排、原型与架构拆解,保持 repo 内 artifact 作为唯一记录系统。
- Warp 多模态 runtime contract 文档继续同步 runtime profile、permission state 与 evidence/replay 阻断事实。
- `scripts/check-app-version-consistency.mjs` 已把 `packages/lime-cli-npm/package.json` 纳入版本一致性检查,防止 npm wrapper 版本再次落后于应用版本。
- `scripts/quality-task-planner.mjs` 把 npm wrapper package 纳入 integrity 文件;版本、配置或依赖变化会触发对应本地质量任务。
- `scripts/run-vitest-smart.mjs`、GUI smoke 脚本、Knowledge smoke、Agent runtime tool surface smoke 与 Design Canvas smoke 更新,覆盖本轮新增 GUI / command 风险。
- `AGENTS.md`、`docs/aiprompts/commands.md`、`docs/aiprompts/quality-workflow.md`、`docs/aiprompts/skill-standard.md` 与文档索引同步 Skill Forge、只读 HTTP/API、Knowledge v2 和 Layered Design 的 current 边界。
#### 3. Release 管线
- 修复 release 构建中的 Vite manual chunk 旧入口引用,`useAgentChatUnified` 现在回到现役 hooks barrel 入口,并移除已不存在的 `immer` 显式 chunk 入口。
- 根包显式声明 GUI smoke 所需的 `playwright` 依赖,并同步 npm / pnpm 锁文件,避免 CI 干净安装后知识库 GUI 冒烟脚本缺包。
### 已知说明
### 已知边界
- Workspace skill binding readiness 仍不等于自动注入 tool surface;只有显式 runtime enable 且通过 allowlist 的 binding 才能进入当前 session 的 SkillTool gate。
- AI 图层化设计仍以本地图层文档、导出和 artifact 写回为主,不声明完整 PSD / mask / inpaint provider adapter 能力。
- 只读 HTTP/API 草案仍是受控能力:verification / registration / approval artifact / session input / controlled GET evidence 不保存 endpoint、token 或 response preview,也不代表技能已进入自动运行面。
- Session credential resolver 只声明 session scope 与引用边界,不读取、不存储、不注入 secret material。
- AI 图层化设计仍以本地图层文档、worker 处理、候选层复核、导出和 artifact 写回为主;provider-backed PSD / mask / inpaint 能力仍受 model slot readiness 与 capability gate 约束。
### 校验状态
- 本次版本准备已完成:
- 已完成校验:
- `npm run verify:app-version`
- `cargo fmt --manifest-path "src-tauri/Cargo.toml" --all`
- `CARGO_TARGET_DIR="/tmp/lime-v1.29.0-clippy-target" cargo clippy --manifest-path "src-tauri/Cargo.toml"`
- `npm run lint:rust`
- `cargo test --manifest-path "src-tauri/Cargo.toml"`
- `npm run lint`
- `npm test`
- `npm run test:contracts`
- `npm run verify:local`
- `git diff --check`
- 结果说明:
- 版本一致性检查通过:`1.29.0`。
- 版本一致性检查通过:`1.30.0`,包含 npm wrapper 版本一致性。
- Rust fmt 通过。
- Rust clippy 通过。
- 前端 lint 通过。
- 前端 Vitest smart suite 49 批通过。
- 标准 Rust `cargo test --manifest-path "src-tauri/Cargo.toml"` 未完成:当前磁盘空间不足,构建 `src-tauri/target/debug/deps/liblime_lib.a` 时报 `No space left on device (os error 28)`;该结果不是测试断言失败。
- Rust clippy 通过;当前仍有 `lime-knowledge` 的 `clippy::too_many_arguments` 既有警告,但脚本退出码为 0。
- Rust 测试通过:`1273` 个 lib 测试通过,`deepseek_reasoner_output_schema_runtime` 2 个集成测试通过,2 个真实联网测试按环境变量门禁保持 ignored。
- 前端 lint、typecheck 与 Vitest smart suite 通过,`npm test` 共 51 批通过。
- 命令契约、Harness 契约、modality runtime contract 与 cleanup report contract 通过。
- `npm run verify:gui-smoke` 通过:复用 headless Tauri / DevBridge,覆盖 workspace ready、browser runtime、site adapters、service skill entry、runtime tool surface、Knowledge GUI 与 Design Canvas smoke。
---
**完整变更**: `v1.28.0` -> `v1.29.0`
**完整变更**: `v1.29.0` -> `v1.30.0`
+5 -4
View File
@@ -6,6 +6,7 @@
- 普通创作者:`content/` 当前处于 LimeNext V2 重建期,只保留少量进阶页与法律说明
- 开发者与维护者:阅读 `aiprompts/`、`develop/`、`tech/`、`tests/` 等工程文档
- 仓库级 Agent 协作方式与硬约束:以根目录 `../AGENTS.md` 为入口
文档站基于 Nuxt Content 构建。
@@ -46,15 +47,15 @@
- `develop/scheduler-task-governance-p1.md`:调度任务治理 P1(连续失败、自动停用、冷却恢复)
- `aiprompts/skill-standard.md`:Skills 包标准、运行时投影与 current/compat 边界总文档
- `roadmap/lime-skills-standardization-roadmap.md`:Skills 标准化 supporting 收口计划,主要保留迁移边界与剩余差距
- `research/creaoai/README.md`:CreoAI / Tool-Maker Agent 研究入口,拆解能力生成、验证、注册与长期运行业务的外部范式
- `research/skill-forge/README.md`:Skill Forge / Tool-Maker Agent 研究入口,拆解能力生成、验证、注册与长期运行业务的外部范式
- `research/pi-mono-coding-agent/README.md`:pi-mono Coding Agent 本地调研,提炼 `AgentSession` 分层、工具 allowlist、可插拔工具后端、事件与测试 harness 对 Lime Capability Authoring Agent 的参考边界
- `research/codex-goal/README.md`:Codex `/goal` 研究入口,独立记录 persistent objective / idle continuation turn / completion audit 模式
- `research/codex-goal/diagrams.md`:Codex `/goal` Thread Goal Loop 图纸,包含架构图、流程图、时序图、状态机和最小心智原型
- `research/ribbi/README.md`:Ribbi 研究总入口,作为后续 LimeNext V2 的外部对照事实源
- `research/ai-layered-design/README.md`:AI 图层化设计研究入口,拆解 Lovart 类可编辑图层、分割、抠图、背景修补与 Canvas 工程范式
- `roadmap/creaoai/README.md`:CreoAI 启发下的 Skill Forge / workspace-local generated skill 路线图
- `roadmap/creaoai/prototype.md`:Skill Forge / generated capability / verification gate / workspace-local skill 的产品原型图
- `roadmap/creaoai/architecture-review.md`:CreoAI / Coding Agent 方案实现前 review gate,列出 draft store、verification、registration、evidence、安全边界缺口
- `roadmap/skill-forge/README.md`:外部 Agent 访谈启发下的 Skill Forge / workspace-local generated skill 路线图
- `roadmap/skill-forge/prototype.md`:Skill Forge / generated capability / verification gate / workspace-local skill 的产品原型图
- `roadmap/skill-forge/architecture-review.md`:Skill Forge / Coding Agent 方案实现前 review gate,列出 draft store、verification、registration、evidence、安全边界缺口
- `roadmap/managed-objective/README.md`:Managed Objective 路线图,把 thread goal loop 启发收敛为 Lime 的跨 turn 目标推进控制层
- `roadmap/managed-objective/prototype.md`:Managed Objective 在 Workspace、Task Center、Audit Drawer 和创建流程中的产品原型图
- `roadmap/ai-layered-design/README.md`:AI 图层化设计路线图,把图片生成升级为可编辑的多图层设计工程输出
+1 -1
View File
@@ -66,7 +66,7 @@
- **改 turn 提交 / prompt 组包 / queue / compaction / evidence 主链**:先读 `query-loop.md`
- **改 subagent / automation / execution tracker / scheduler taxonomy**:先读 `task-agent-taxonomy.md`
- **讨论 `/goal`、Managed Objective 或跨 turn 目标续跑**:先读 `task-agent-taxonomy.md` 与 `query-loop.md`,再读 `../research/codex-goal/README.md` 与 `../roadmap/managed-objective/README.md`
- **讨论 Coding Agent、Skill Forge 或能力生成 draft**:先读 `query-loop.md` 与 `skill-standard.md`,再读 `../research/pi-mono-coding-agent/README.md` 与 `../roadmap/creaoai/coding-agent-layer.md`
- **讨论 Coding Agent、Skill Forge 或能力生成 draft**:先读 `query-loop.md` 与 `skill-standard.md`,再读 `../research/pi-mono-coding-agent/README.md` 与 `../roadmap/skill-forge/coding-agent-layer.md`
- **改 channels / browser connector / DevBridge / OpenClaw remote runtime**:先读 `remote-runtime.md`
- **改记忆来源链 / working memory / durable memory / Team Memory / compaction**:先读 `memory-compaction.md`
- **改 FileArtifact / artifact sidecar / versions / file checkpoint / evidence 中的文件快照**:先读 `persistence-map.md`
+17 -5
View File
@@ -86,6 +86,14 @@ AI 图层化设计工程目录落盘继续走 current `LayeredDesignDocument`
当 `export-manifest.json` 中存在 `source=reference` 且 `originalSrc` 为 `http/https` 的远程图片资产时,`save_layered_design_project_export` 可以在同一条 current 命令内把它们持久化缓存到 `assets/`,并把 manifest / PSD-like projection 更新为 `source=file + filename + originalSrc`。`read_layered_design_project_export` 读回时则优先从这些缓存文件水合 `design.json` 返回给前端,确保 `DesignCanvas` 重新打开工程时继续得到可显示、可编辑的图片层,而不是再次依赖远程 URL 在线可达。
AI 图层化设计扁平图 OCR 分析同样继续走 current `LayeredDesignDocument.extraction` 主链。当前前端入口为 `src/lib/api/layeredDesignAnalysis.ts`,统一承接:
- `recognize_layered_design_text`
- `analyze_layered_design_flat_image`
`recognize_layered_design_text` 只服务 `analyzeLayeredDesignFlatImage` 的 OCR provider seam,把 native OCR 结果投影成可编辑 `TextLayer` 候选;非支持平台、非 `data:image/*;base64` 来源或无结果必须返回 unsupported/fallback,不应让拆层任务整体失败。
`analyze_layered_design_flat_image` 只服务 structured analyzer provider seam,把 native / Tauri 侧的 image / mask / clean plate 结构化结果投影回同一份 `LayeredDesignDocument.extraction`;不支持的来源必须返回 unsupported/fallback,不应绕过 `canvas:design -> DesignCanvas` 主链。它们都不是 provider adapter、不是旧 poster 协议,也不应回流 `poster_generate / canvas:poster / ImageTaskViewer`。
命令目录与输入补全链路同样需要单一事实源。当前前端主入口为 `src/lib/api/skillCatalog.ts`,统一承接:
- `bootstrap.skillCatalog`
@@ -138,7 +146,7 @@ AI 图层化设计工程目录落盘继续走 current `LayeredDesignDocument`
- `fallbackStrategy`
- 聊天结果沉淀为技能时,只能继续扩这组说明型字段,不要再平行发明第二套“技能草稿协议”
CreoAI Capability Draft 命令链也必须停留在独立的生成 / 验证 / 注册边界:
Skill Forge Capability Draft 命令链也必须停留在独立的生成 / 验证 / 注册边界:
- 前端统一经由 `src/lib/api/capabilityDrafts.ts` 承接:
- `capability_draft_create`
@@ -146,13 +154,17 @@ CreoAI Capability Draft 命令链也必须停留在独立的生成 / 验证 /
- `capability_draft_get`
- `capability_draft_verify`
- `capability_draft_register`
- `capability_draft_list_registered_skills`
- `capability_draft_list_registered_skills`
- `capability_draft_submit_approval_session_inputs`
- `capability_draft_execute_controlled_get`
- `capability_draft_create/list/get/verify/register/list_registered_skills` 只服务 `Capability Draft -> Workspace-local Skill package -> registered discovery` 的事实链,不是 runtime 执行入口
- `capability_draft_submit_approval_session_inputs` 只做 session-scoped approval 输入校验;`capability_draft_execute_controlled_get` 只做一次性受控 GET 门禁并返回当前命令 evidence,正向 / `request_failed` 只落非敏感 evidence artifact,不保存 endpoint/token/response preview、不进入 scheduler 或默认 tool surface
- `capability_draft_register` 只允许把 `verified_pending_registration` 草案复制到当前 `workspaceRoot/.agents/skills`,并记录来源、verification report 与权限摘要;它不得调用 Skill reload、不得修改 seeded skill、不得把能力直接放进默认 tool surface
- `capability_draft_list_registered_skills` 只能显式按 `workspaceRoot` 读取当前项目 `.agents/skills` 中带 `.lime/registration.json` 的 P3A 注册能力;它只做 catalog discovery / provenance projection,不得把能力合并进默认已安装方法列表、不得触发 runtime binding、不得展示运行或自动化入口
- `agent_runtime_export_evidence_pack` 可以按当前 session 消费 `.lime/capability-drafts/controlled-get-evidence/*.json` 的非敏感摘要,但该摘要只用于 evidence pack 可读性,不代表 capability draft 已进入 Query Loop、runtime、scheduler 或 default tool surface
- 注册后的执行仍必须回到 `agent_runtime_submit_turn -> Query Loop -> tool_runtime -> artifact/evidence` 主链,不能在 Capability Draft 命令里新增平行运行、调度或外部写协议
CreoAI P3C runtime binding 第一刀必须回到 `agent_runtime_*` 主链:
Skill Forge P3C runtime binding 第一刀必须回到 `agent_runtime_*` 主链:
- 当前前端入口为 `src/lib/api/agentRuntime/inventoryClient.ts` 中的 `listWorkspaceSkillBindings`
- 当前 Tauri 命令为 `agent_runtime_list_workspace_skill_bindings`
@@ -161,7 +173,7 @@ CreoAI P3C runtime binding 第一刀必须回到 `agent_runtime_*` 主链:
- 它可以说明哪些 registered skill 已经具备后续接入候选资格,但真正执行仍只能通过后续 `agent_runtime_submit_turn -> Query Loop -> tool_runtime -> artifact/evidence` 完成
- 不得把这类 runtime binding 状态继续塞回 `capability_draft_*` 命令族;`capability_draft_*` 只到 generation / verification / registration / discovery
CreoAI P3D Query Loop metadata 第一刀继续走 `agent_runtime_submit_turn`,不是新增命令面:
Skill Forge P3D Query Loop metadata 第一刀继续走 `agent_runtime_submit_turn`,不是新增命令面:
- 当前 metadata contract 为 `request_metadata.harness.workspace_skill_bindings`,兼容读取 `workspaceSkillBindings`
- 前端裁剪入口为 `src/components/agent/chat/utils/workspaceSkillBindingsMetadata.ts`;它只输出 snake_case metadata fragment,不写入 `allow_model_skills`
@@ -170,7 +182,7 @@ CreoAI P3D Query Loop metadata 第一刀继续走 `agent_runtime_submit_turn`,
- 当 `query_loop_visible=false`、`tool_runtime_visible=false` 或 `launch_enabled=false` 时,模型不得声称已运行、不得调用未授权 Skill、不得创建 automation / scheduler / job
- P3D 不注入 `SkillTool` registry,不改变 `agent_runtime_submit_turn` 的默认 tool surface;真正执行仍必须等后续 `tool_runtime` 授权裁剪和 session 显式 enable
CreoAI P3E tool_runtime authorization 第一刀仍继续走 `agent_runtime_submit_turn`,不是新增命令面:
Skill Forge P3E tool_runtime authorization 第一刀仍继续走 `agent_runtime_submit_turn`,不是新增命令面:
- 当前 enable metadata contract 为 `request_metadata.harness.workspace_skill_runtime_enable`,兼容读取 `workspaceSkillRuntimeEnable`
- 前端裁剪入口继续收在 `src/components/agent/chat/utils/workspaceSkillBindingsMetadata.ts` 与 `buildHarnessRequestMetadata`;该 metadata 输出 `source=manual_session_enable`、`approval=manual`、`workspace_root` 和 ready binding 列表,但不写入 `allow_model_skills`
+30 -6
View File
@@ -43,13 +43,37 @@
执行时额外遵守:
1. 校验前先确认本轮改动对应路线图哪一项
2. 如果本轮改动只是清理 dead surface、补 README 或局部整理,但没有直接推进主链,不能把“校验通过”当作完成目标
3. 汇报时必须同时给出:
2. 选择下一刀前先按“对整体目标完成度的增量”排序,优先补直接影响可用闭环的主缺口;协议 polish、错误分类、额外 seam、边缘校验、文案润色、内部抽象等梢枝末节,只有在阻塞主路径、会造成假入口/假配置,或用户明确要求时才做
3. 如果本轮改动只是清理 dead surface、补 README、局部整理或低杠杆细节,但没有直接推进主链,不能把“校验通过”当作完成目标
4. 汇报时必须同时给出:
- 本轮改动对应的路线图节点
- 本轮校验覆盖了哪条主线风险
- 当前距离该路线图阶段完成还差什么
4. 用户问“完成了么”时,先回答主线目标是否已经达到可交付门槛;额外校验、顺手清理、可选优化单独列出,不要反向覆盖主线结论
5. 校验的上限是证明当前主线可交付;一旦已经覆盖本轮真实风险,就不要因为还有更重的检查可跑而无限追加验证
5. 用户问“完成了么”时,先回答主线目标是否已经达到可交付门槛;额外校验、顺手清理、可选优化必须单独列出,不要反向覆盖主线结论
6. 校验的上限是证明当前主线可交付;一旦已经覆盖本轮真实风险,就不要因为还有更重的检查可跑而无限追加验证
## 开发任务收口反馈
非纯问答的开发任务结束时,最终回复必须给出面向开发主线的完成度反馈,而不只是列文件和测试。
默认包含:
1. `本轮完成度:X%`,并用一句话说明百分比依据
2. 本轮目标是否完成,以及落在哪条 current 主链
3. 实际完成了什么,哪些事实源或边界已同步
4. 实际执行了哪些验证;未执行的高风险验证要说明原因
5. 还剩什么未完成或仍不应宣称完成
6. 下一刀最该做什么
如果任务绑定路线图、执行计划、多阶段迁移或用户连续要求“继续主线”,还必须额外给出完成度判断:
- 区分 `本轮完成度` 和 `整体目标完成度`
- 整体百分比必须说明口径,例如工程闭环、MVP、完整产品目标
- 如果无法精确量化,给区间或估算值,但必须说明依据,不能省略百分比
- 剩余项按影响主线交付的优先级排序,避免把可选优化和主线缺口混在一起
- 简单单文件修复可以压缩成 2-3 句;不要为了格式而写长报告
该反馈只适用于开发任务;普通解释、检索、问答或无需改仓库的讨论不强制输出完成度百分比。只要发生代码、配置、脚本、测试或工程文档修改,就视为开发任务,最终回复必须给本轮完成度百分比。
## 执行硬规则
@@ -91,13 +115,13 @@
如果本轮涉及 `create_skill_scaffold_for_app`、`SkillsPage / SkillScaffoldDialog`,或“聊天结果 -> Skill 脚手架”沉淀闭环,还要同步检查前端网关、Rust 模板、DevBridge 分发与默认 mock 是否仍保持同一条主链;若新增了结构化骨架字段,至少要确认 `何时使用 / 输入 / 执行步骤 / 输出 / 失败回退` 能真实落进生成后的 `SKILL.md`。
如果本轮涉及 `capability_draft_create/list/get/verify/register/list_registered_skills`,还要同步检查 `src/lib/api/capabilityDrafts.ts`、`capability_draft_cmd`、`capability_draft_service`、DevBridge dispatcher、治理目录册、`mockPriorityCommands` 与 `defaultMocks`;注册命令只能证明 workspace-local Agent Skill 包已落盘,registered discovery 只能证明当前 workspace 可发现带 provenance 的 Skill 包,不能把“已注册 / 已发现”当成“已进入 tool surface / 可自动运行”。最低校验至少包含 Rust capability draft 定向测试、前端 API / UI 回归、`npm run test:contracts`;若 Skills 工作台可见行为变化,再补 `npm run verify:gui-smoke`。
如果本轮涉及 `capability_draft_create/list/get/verify/register/list_registered_skills/submit_approval_session_inputs/execute_controlled_get`,还要同步检查 `src/lib/api/capabilityDrafts.ts`、`capability_draft_cmd`、`capability_draft_service`、DevBridge dispatcher、治理目录册、`mockPriorityCommands` 与 `defaultMocks`;注册命令只能证明 workspace-local Agent Skill 包已落盘,registered discovery 只能证明当前 workspace 可发现带 provenance 的 Skill 包,session 输入命令只能证明一次性授权输入有效,受控 GET 命令只能返回当前命令 evidence,正向 / `request_failed` 只能落非敏感 evidence artifact,不能保存 endpoint/token/response preview,不能把“已注册 / 已发现 / 已校验 / 已执行一次 GET”当成“已进入 tool surface / 可自动运行”。最低校验至少包含 Rust capability draft 定向测试、前端 API / UI 回归、`npm run test:contracts`;若 Skills 工作台可见行为变化,再补 `npm run verify:gui-smoke`。
如果本轮涉及 `agent_runtime_list_workspace_skill_bindings`,还要同步检查 `src/lib/api/agentRuntime/inventoryClient.ts`、`src/lib/governance/agentRuntimeCommandSchema.json`、generated runtime command manifest、Rust `aster_agent_cmd` 注册、DevBridge dispatcher、治理目录册、`mockPriorityCommands` 与 `defaultMocks`;该命令只表示 P3B registered skill 的 runtime binding readiness projection,不能把 `ready_for_manual_enable` 当成“已注入 Query Loop / 已进入 SkillTool / 可自动执行”。最低校验至少包含 Rust runtime binding 定向测试、前端 API / UI 回归、`npm run generate:agent-runtime-clients` 或 `npm run check:agent-runtime-clients`、`npm run test:contracts`;若 Skills 工作台可见行为变化,再补 `npm run verify:gui-smoke`。
如果本轮涉及 `request_metadata.harness.workspace_skill_bindings` / `workspaceSkillBindings` 的 Query Loop metadata 投影,还要同步检查 `src-tauri/src/commands/aster_agent_cmd/workspace_skill_binding_prompt.rs`、`src-tauri/crates/agent/src/turn_input_envelope.rs` 的 prompt stage contract、`src/components/agent/chat/utils/workspaceSkillBindingsMetadata.ts` 与 `buildHarnessRequestMetadata` 的裁剪边界;该 metadata 只表示 P3C readiness 的只读规划上下文,不能自动打开 `allow_model_skills`、不能注入 `SkillTool` registry、不能改变默认 tool surface。最低校验至少包含 Rust prompt 投影定向测试、前端 metadata builder 单测和 `npm run typecheck`;若同时改了 runtime command schema 或 command manifest,再补 `npm run test:contracts`。
如果本轮涉及 `request_metadata.harness.workspace_skill_runtime_enable` / `workspaceSkillRuntimeEnable` 的 CREAO P3E runtime enable,还要同步检查 `src-tauri/src/services/runtime_skill_binding_service.rs`、`src-tauri/src/commands/aster_agent_cmd/runtime_turn.rs`、`src-tauri/src/commands/aster_agent_cmd/workspace_skill_binding_prompt.rs`、`src-tauri/crates/agent/src/tools/skill_tool_gate.rs`、`src/components/agent/chat/utils/workspaceSkillBindingsMetadata.ts` 与 `buildHarnessRequestMetadata`;该 metadata 只能在当前 session scope 内显式启用 P3C ready binding,并把 `SkillTool` 裁剪到 allowlist,不能复活 marketplace、scheduler 或绕过 `agent_runtime_submit_turn` 的平行执行命令。最低校验至少包含 Rust runtime binding / SkillTool gate 定向测试、Rust prompt 投影定向测试、前端 metadata builder 单测和 `npm run test:contracts`。
如果本轮涉及 `request_metadata.harness.workspace_skill_runtime_enable` / `workspaceSkillRuntimeEnable` 的 Skill Forge P3E runtime enable,还要同步检查 `src-tauri/src/services/runtime_skill_binding_service.rs`、`src-tauri/src/commands/aster_agent_cmd/runtime_turn.rs`、`src-tauri/src/commands/aster_agent_cmd/workspace_skill_binding_prompt.rs`、`src-tauri/crates/agent/src/tools/skill_tool_gate.rs`、`src/components/agent/chat/utils/workspaceSkillBindingsMetadata.ts` 与 `buildHarnessRequestMetadata`;该 metadata 只能在当前 session scope 内显式启用 P3C ready binding,并把 `SkillTool` 裁剪到 allowlist,不能复活 marketplace、scheduler 或绕过 `agent_runtime_submit_turn` 的平行执行命令。最低校验至少包含 Rust runtime binding / SkillTool gate 定向测试、Rust prompt 投影定向测试、前端 metadata builder 单测和 `npm run test:contracts`。
如果本轮涉及记忆主链,还要同步检查 `src/lib/api/memoryRuntime.ts`、`src-tauri/src/commands/memory_management_cmd.rs`、`runner.rs`、DevBridge dispatcher 与默认 mock 是否仍保持同一条 current surface;`rules / working / durable / team / compaction` 的产品分层可以在页面上拆开,但底层命令边界仍必须继续收敛到 `memory_runtime_*` 与 `unified_memory_*`。
+1 -1
View File
@@ -118,7 +118,7 @@ Lime 在工程上必须明确接受这一点:
1. `SKILL.md` 不是 Lime 的最终产品对象。
2. `ServiceSkill` / `Scene` 也不是新的包格式标准。
3. `ServiceSkill` / `Scene` 是 Lime 在 Agent Skills 之上的产品投影层。
4. CreoAI / Capability Draft 注册只负责把已验证草案复制成 workspace-local Agent Skill 包;它不等同于运行时绑定,也不能绕过 Query Loop 与 `tool_runtime` 直接执行。
4. Skill Forge / Capability Draft 注册只负责把已验证草案复制成 workspace-local Agent Skill 包;它不等同于运行时绑定,也不能绕过 Query Loop 与 `tool_runtime` 直接执行。
因此,generated skill 的最小安全顺序固定为:
-46
View File
@@ -1,46 +0,0 @@
# `docs/exec-plans`
本目录存放会影响开发执行的 versioned artifact:执行计划、进度日志、阻塞记录、迁移清单、技术债追踪。
## 放什么
- 多轮实现、迁移、治理任务的执行计划
- 与计划绑定的阶段进度、阻塞项、决策记录
- 需要持续小额偿还的技术债与退出条件
## 命名约定
- 专项计划:`<topic>-plan.md`
- 进度日志:`<topic>-progress.md`
- 常驻追踪:使用固定文件名,例如 `tech-debt-tracker.md`
## 使用规则
1. 计划不是一次性文档,推进状态变化时要同步更新
2. 会改变实现顺序、范围或回滚策略的决策,必须记录在这里或链接到这里
3. 清理类工作如果不能直接回挂路线图,应登记到 `tech-debt-tracker.md`
4. 被替代的计划不要悬空,保留跳转说明或归档指针
## 关联入口
- 路线图主线:`docs/roadmap/`
- 参考运行时主链总计划:`docs/exec-plans/upstream-runtime-alignment-plan.md`
- 参考运行时主链进度日志:`docs/exec-plans/upstream-runtime-alignment-progress.md`
- Provider 模型能力 taxonomy 进度日志:`docs/exec-plans/provider-model-taxonomy-progress.md`
- Lime 多模态运行合同实施计划:`docs/exec-plans/multimodal-runtime-contract-plan.md`
- 云端套餐与支付边界收口计划:`docs/exec-plans/cloud-commerce-user-center-boundary.md`
- `@` 命令本地执行纠偏计划:`docs/exec-plans/at-command-local-execution-alignment-plan.md`
- AI 图层化设计实现计划:`docs/exec-plans/ai-layered-design-implementation-plan.md`
- CreoAI Capability Authoring P1A 执行计划:`docs/exec-plans/creaoai-capability-authoring-p1a-plan.md`
- CreoAI Capability Verification P1B 执行计划:`docs/exec-plans/creaoai-capability-verification-p1b-plan.md`
- CreoAI Capability Registration P3 执行计划:`docs/exec-plans/creaoai-capability-registration-p3-plan.md`
- CreoAI Capability Discovery P3B 执行计划:`docs/exec-plans/creaoai-capability-discovery-p3b-plan.md`
- CreoAI Runtime Binding P3C 执行计划:`docs/exec-plans/creaoai-runtime-binding-p3c-plan.md`
- CreoAI Query Loop Metadata P3D 执行计划:`docs/exec-plans/creaoai-query-loop-metadata-p3d-plan.md`
- CREAO Tool Runtime Authorization P3E 执行计划:`docs/exec-plans/creaoai-tool-runtime-authorization-p3e-plan.md`
- CREAO Managed Execution / Agent Envelope P4 执行计划:`docs/exec-plans/creaoai-managed-agent-envelope-p4-plan.md`
- CREAO Roadmap P0-P4 完成审计:`docs/exec-plans/creaoai-completion-audit.md`
- LimeNext 总实施计划(`legacy current reference`,当前主规划已切到 `docs/roadmap/limenextv2/README.md`):`docs/exec-plans/limenext-plan.md`
- LimeNext 推进日志:`docs/exec-plans/limenext-progress.md`
- 技术债追踪:`docs/exec-plans/tech-debt-tracker.md`
- 模块级实施细节:`docs/aiprompts/README.md`
@@ -1,480 +0,0 @@
# Agent Knowledge 实现执行计划
> 状态:Phase 1 current 主链已接通,项目资料能力已回流到现有 Agent 输入框;首页添加、File Manager 添加、输入框使用、项目资料管理与 Agent 结果沉淀均已完成稳定 DevBridge 下的产品 E2E 验收
> 创建时间:2026-05-01
> 路线图来源:`docs/roadmap/knowledge/prd.md`
> 当前目标:完成 Markdown-first 项目资料的导入、整理、GUI 管理、Agent 输入框显式使用与运行时受保护上下文注入。
## 主目标
把 Agent Knowledge 标准接入 Lime 的 current 主链:
```text
src-tauri/crates/knowledge
-> src-tauri/src/commands/knowledge_cmd.rs
-> src/lib/api/knowledge.ts
-> src/features/knowledge
-> AgentChatWorkspace request metadata
-> Runtime KnowledgePack prompt stage
-> src-tauri/resources/default-skills/knowledge_builder
-> mock / governance / contract validation
```
固定事实源:
**后续知识包能力只允许向 `lime-knowledge + knowledge_* + Knowledge Context Resolver` 收敛;`project_memory_get` 继续只做项目资料附属层,不接管知识包主链。**
## 范围
本轮做:
1. 独立后端 crate:`src-tauri/crates/knowledge`。
2. 标准目录:`.lime/knowledge/packs/<pack-name>/KNOWLEDGE.md`、`sources/`、`wiki/`、`compiled/`、`runs/`。
3. Tauri 薄命令:`knowledge_import_source`、`knowledge_compile_pack`、`knowledge_list_packs`、`knowledge_get_pack`、`knowledge_set_default_pack`、`knowledge_resolve_context`。
4. 前端独立 API 网关:`src/lib/api/knowledge.ts`。
5. 前端 feature 边界:`src/features/knowledge`。
6. GUI 页面入口、侧边栏入口、任务中心资料入口。
7. Runtime 通过 Knowledge Context Resolver 注入受保护 fenced context。
8. mock、治理目录册、契约检查同步。
9. 后端 crate 单测、runtime 定向测试和前端回归测试。
本轮不做:
1. 聊天输入框内的细粒度知识包选择器。
2. 向量库、知识图谱、企业权限、知识包市场。
3. 把知识包接入 Memory 或 durable memory。
4. 把 Builder 输出直接写盘并自动覆盖用户已编辑知识资产。
## 执行记录
### 2026-05-01
- 已新增路线图 PRD:`docs/roadmap/knowledge/prd.md`。
- 已补 `.gitignore` 例外,让该 PRD 可被 Git 跟踪。
- 已开始 Phase 1 实现。
- 已根据用户反馈移除新实现与 PRD 中的具体人名样例。
- 已确定后端知识域必须独立成 `lime-knowledge` crate,Tauri command 只做薄适配。
- 已创建 `src-tauri/crates/knowledge`,并把知识包文件事实源逻辑迁入该 crate。
- 已创建前端网关 `src/lib/api/knowledge.ts` 与测试草稿。
- 已创建前端 feature 入口 `src/features/knowledge/index.ts`。
- 已同步 Tauri 命令注册、治理目录册、mockPriority 与 default mock 草稿。
- 已通过 `cargo test -p lime-knowledge`。
- 已通过 `npm test -- src/lib/api/knowledge.test.ts`。
- 已通过 `npm run test:contracts`,确认前端调用、Rust 注册、治理目录册与 mock 边界一致。
- 已通过 `cargo fmt --package lime-knowledge --check`。
- 已通过 `cargo check -p lime`,确认主 Tauri crate 能集成新 crate 与命令注册。
- 已通过 `npm run typecheck`。
### 2026-05-02
- 已新增 `src/features/knowledge/KnowledgePage.tsx`,接通知识包目录、详情、来源导入、编译、默认包设置和运行时视图预览。
- 已新增 `src/features/knowledge/KnowledgePage.test.tsx`,覆盖目录读取、导入、编译、默认包设置、运行时 context 预览和“去生成”metadata。
- 已把 `knowledge` 页面加入 `src/types/page.ts`、`src/components/AppPageContent.tsx`、`src/lib/navigation/sidebarNav.ts`、`ChatSidebar` 资料分组和 `AgentChatWorkspace` 工作台跳转。
- 已确认前端发送链路最终通过 `buildWorkspaceRequestMetadata` 合并 `workspaceRequestMetadataBase` 与 `sendOptions.requestMetadata`,Knowledge 页面传入的 `knowledge_pack` metadata 会进入 runtime request metadata。
- 已新增 runtime `KnowledgePack` prompt stage:从 `knowledge_pack` / `knowledgePack` metadata 解析 `pack_name/name`、`working_dir/workingDir`、`max_chars/maxChars`,调用 `lime_knowledge::resolve_knowledge_context(...)` 并注入 fenced context。
- 已将带知识包 metadata 的请求判定为 full runtime,避免 fast chat 短路跳过知识包上下文。
- 已在 `TurnPromptAugmentationStageKind` 增加 `KnowledgePack`,并用源代码顺序契约锁定 stage 位于 Memory 之后、WebSearch / RequestToolPolicy 之前。
- 已在聊天输入区增加轻量知识包开关:读取当前工作区默认知识包,用户显式启用后通过 `knowledge_pack` metadata 进入同一 runtime 注入链路。
- 已新增正式内置 Builder Skill:`src-tauri/resources/default-skills/knowledge_builder/SKILL.md`,用于把来源资料整理为 `KNOWLEDGE.md`、`wiki/`、`compiled/brief.md` 与 `runs/` 草稿。
- 已把 `knowledge_builder` 注册进默认 Lime Skills 安装链和内置目录白名单。
- 已在知识库页面补充 “Builder 生成”入口,携带 `knowledge_builder` metadata、项目根目录、pack name 和 pack 类型进入 Agent 自动执行。
- 已确认新增内容不包含用户要求禁止出现的具体人名样例,禁名扫描无命中。
- 已通过 `cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-core test_default_lime_skill_directories_include_embedded_defaults --no-default-features`,确认 `knowledge_builder` 已进入默认 Skill 目录事实源。
- 已通过 `npm run verify:gui-smoke`,覆盖 headless Tauri、DevBridge、workspace-ready、browser-runtime、site adapters、Agent service skill entry 与 runtime tool surface/page smoke。
- 曾尝试 `CARGO_TARGET_DIR="/tmp/lime-agent-knowledge-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime test_default_lime_skill_directories_include_embedded_defaults --no-default-features --features local-sensevoice`,该冷构建在 `sherpa-onnx-sys` 下载预编译库时因 TLS 连接中断失败;已改用 `lime-core` 定向测试覆盖本次 Builder 默认目录注册风险。
- 已把聊天输入区知识包开关升级为多包选择菜单:workspace runtime 读取当前项目全部非归档知识包,默认选工作区默认包,用户可在输入区切换具体包并继续通过同一 `knowledge_pack` metadata 发送。
- 已补输入区回归,覆盖菜单切换具体知识包、自动启用知识包上下文和发送 metadata 使用选中包。
- 已通过 `npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx"` 与 `npm run typecheck`。
- 本轮重跑 `npm run verify:gui-smoke` 时,脚本检测到 sqlite 构建缓存缺失并切到独立 target,同时复用已有 headless 链路;等待超过 20 分钟仍未暴露 DevBridge,本轮新启动的 verify 父进程已中断,该次 smoke 不计为通过。上一轮完整 GUI smoke 通过记录仍保留,但多包选择菜单这刀还需要在环境恢复后补跑一次 GUI smoke。
- 已升级 `knowledge_builder` 到 `lime_version: 1.1.0`,补齐 personal-ip、brand-product、organization-know-how、growth-strategy 与 custom 的类型化 wiki / runtime brief 模板。
- 已要求 Builder 输出 `runs/quality-report-{yyyyMMdd-HHmmss}.md`,包含 pass / warn / fail 结论、检查表、待确认事实、冲突风险和下一步,避免把无法确认的信息推断成事实。
### 2026-05-04
- 已补跑 `knowledge_builder` 1.1.0 内容契约检查,确认类型化模板、`compiled/brief.md` 固定结构和 `runs/quality-report-*` 质量报告关键字段存在。
- 已通过 `cargo fmt --manifest-path "src-tauri/Cargo.toml" --package lime --check`,确认默认 Skill 注册相关 Rust 改动格式正确。
- 已通过 `CARGO_TARGET_DIR="/tmp/lime-knowledge-resource-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime should_embed_social_image_tool_contract_in_default_skill --no-default-features`,避开共享 `src-tauri/target` 占用并确认 `knowledge_builder` 内容随默认 Skill 资源内嵌。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx"` 与 `npm test -- "src/components/AppPageContent.test.tsx"`,确认知识库页面与应用页挂载回归仍稳定。
- 已通过 `npm run typecheck`,确认前端类型边界未被知识库页面、预加载和输入区 metadata 改动破坏。
- 已通过 `CARGO_HOME="/tmp/lime-gui-smoke-cargo-home" npm run verify:gui-smoke`,复用现有 headless Tauri 环境,覆盖 DevBridge、workspace-ready、browser-runtime、site adapters、Agent service skill entry 与 runtime tool surface/page smoke。
- 已按普通使用者视角收敛知识库页面文案:用户可见 UI 与“整理资料”自动发送提示不再展示内部目录、Builder/Skill 名称、运行时围栏、token、frontmatter/trust/status 实现细节,改为“导入资料 / 自动整理 / 预览摘要 / 人工确认 / 引用摘要 / 整理记录”。
- 已同步 `src/features/knowledge/KnowledgePage.test.tsx`,新增负向断言锁定 `.lime/knowledge`、`knowledge_builder`、`compiled/brief.md`、`frontmatter`、`KNOWLEDGE.md`、raw fenced context 与 token 不出现在普通用户页面正文里;内部 metadata 契约测试继续保留。
- 已同步 `scripts/knowledge-gui-smoke.mjs` 的用户可见断言,把“Builder 入口 / 编译预览”替换为“资料整理入口 / 预览摘要”。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx"`。
- 已通过 `npm run typecheck`。
- 已运行 `npm run bridge:health -- --timeout-ms 20000`,当前 DevBridge 未监听 `127.0.0.1:3030`,因此本轮知识库 GUI smoke 未执行且不计为通过。
- 已根据产品化反馈重做知识页主路径:默认入口从“知识包管理台”改为“项目资料助手”,首屏直接支持粘贴资料、选择资料类型、开始整理、查看完成进度、检查确认与用于生成;默认包、待确认和全部资料改为次级信息。
- 已把手动导入页降级为补充入口,避免继续作为普通用户首屏主路径。
- 已同步 `src/features/knowledge/KnowledgePage.test.tsx`,覆盖资料助手首屏、粘贴资料后整理、检查确认、用于生成与内部 metadata 契约。
- 已同步 `scripts/knowledge-gui-smoke.mjs`,知识库 GUI smoke 断言改为资料助手、全部资料、等你确认的资料和当前资料。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx"`。
- 已通过 `npm run typecheck`。
- 已运行 `npm run bridge:health -- --timeout-ms 20000`,当前 DevBridge 仍未监听 `127.0.0.1:3030`,因此本轮产品化重做后的 GUI smoke 未执行且不计为通过。
### 2026-05-05
- 已根据用户反馈继续收敛产品主路径:普通用户整理和使用项目资料的入口回到现有 Agent 输入框,不再要求先进入独立知识页。
- 已将 `src/components/agent/chat/components/Inputbar/components/InputbarComposerSection.tsx` 的资料控件改为输入框主路径常显;已有资料时显示“项目资料”选择与启用,无资料时显示“整理成项目资料”。
- 已将 `src/components/agent/chat/workspace/useWorkspaceInputbarSceneRuntime.tsx` 接入“整理成项目资料”动作:有输入内容时复用现有 Agent `handleSend` 发送整理请求并携带 `knowledge_builder` metadata;无输入内容时只预填用户可编辑提示;未选择项目时提示先选择项目。
- 已下线知识页内独立聊天面板:`src/features/knowledge/KnowledgePage.tsx` 不再保留 `chat` 视图、页面内任务输入框、页面内发送按钮、引用原文预览和 token 展示。
- 已将知识页定位降级为“项目资料管理”:保留全部资料、资料详情、手动导入、确认、设默认、归档、重新整理等管理动作。
- 已将知识页“用于生成”改为直接回到现有 Agent,并携带 `knowledge_pack` metadata;资料使用不再绕到独立页面内聊天流。
- 已补齐 `src-tauri/src/dev_bridge/dispatcher/knowledge.rs`,并在 `src-tauri/src/dev_bridge/dispatcher.rs` 注册已有 `knowledge_*` 命令的 DevBridge 分发,解决 GUI smoke 中 `knowledge_import_source` 无分发的问题。
- 已同步 `scripts/knowledge-gui-smoke.mjs`,把断言从“资料助手 / 页面内聊天”改为“项目资料管理 / 回到 Agent / 手动导入”。
- 已同步 `src/features/knowledge/KnowledgePage.test.tsx` 与 `src/components/agent/chat/components/Inputbar/index.test.tsx`,锁定普通用户页面不展示开发者细节,并覆盖输入框整理入口、资料 metadata 发送和知识页回到 Agent。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx"`。
- 已通过 `npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx"`。
- 已通过 `npm run typecheck`。
- 已通过 `cargo fmt --manifest-path "src-tauri/Cargo.toml" --package lime --check`。
- 已通过 `npm run test:contracts`。
- 已执行禁名扫描,用户要求禁止出现的具体名称无命中。
- 已执行普通用户路径泄露扫描,新增主路径未再暴露“聊天任务 / 当前资料 / 查看引用 / 资料助手 / Builder 生成”等旧页面语义。
- 已重跑 `CARGO_HOME="/tmp/lime-gui-smoke-cargo-home" npm run verify:gui-smoke`,本轮进入独立 target 冷构建,已完成依赖下载并编译到主 `lime` crate 收尾阶段,但约 34 分钟仍未进入 GUI smoke 断言;该次临时进程组已中止,最新 GUI smoke 不计为通过。当前未观察到业务断言失败,剩余风险是 GUI 主路径尚未在稳定 headless 环境完成最终冒烟。
- 已完成知识库真实 GUI E2E 收口:
- 清理检查确认未残留 `knowledge-gui-smoke`、`smoke-knowledge-gui`、`lime-knowledge-e2e-target` 或专用 Chrome profile 进程。
- 初始 `npm run bridge:health -- --timeout-ms 20000` 失败,确认前端 `1420` 可访问但 DevBridge `3030` 未就绪。
- 尝试启动 `CARGO_TARGET_DIR="/tmp/lime-knowledge-e2e-target" npm run tauri:dev:headless` 避免共享 target 锁;后续用户原有 `pnpm run tauri dev` 完成启动,`npm run bridge:health -- --timeout-ms 5000` 通过。
- 通过 `http://127.0.0.1:3030/invoke` 真实调用 `knowledge_import_source`、`knowledge_compile_pack`、`knowledge_update_pack_status`、`knowledge_set_default_pack`,seed 出 `Smoke 默认项目资料` 与 `Smoke 备用项目资料` 两份资料。
- Playwright 打开 `http://127.0.0.1:1420/`,设置 onboarding 与 `lime.knowledge.working-dir` 后进入“知识库”,验证“项目资料管理 / 当前项目资料库 / 全部项目资料 / 手动导入 / 日常使用入口 / 回到 Agent”可见。
- E2E 暴露并修复两个阻塞点:`knowledge_*` 被 `mockPriorityCommands` 强制走 mock,导致真实 DevBridge seed 数据无法进 GUI;知识页只把 localStorage 目录写入输入框,未初始化 `workingDir` 状态,导致目录列表不加载。
- 修复后 Playwright 验证 `knowledge_list_packs` 与 `knowledge_get_pack` 走真实 HTTP Bridge,页面展示两份 seed 资料、默认资料标记与管理概览 `2 份项目资料`。
- 点击“用于生成”后回到现有 Agent 工作区,页面出现用户消息“请基于当前项目资料生成内容”,并通过真实 `agent_runtime_submit_turn` 自动提交;输入框主路径仍展示“整理成项目资料”,没有回到独立知识页聊天。
- 本轮控制台与页面快照扫描未发现 `ERROR`、`Failed to load resource`、`ERR_CONNECTION`,也未发现 `knowledge_builder`、`compiled/brief.md`、`.lime/knowledge`、`frontmatter`、`token`、`聊天任务`、`当前资料:`、`查看引用`、`资料助手`、`Builder 生成` 等普通用户主路径泄露。
- 本轮启动的隔离 target headless Tauri 冷构建已中止并确认无残留;用户原有 DevBridge 仍健康。
- 已通过 `npm run test:contracts`,确认命令契约、harness 契约和治理生成检查通过。
- 已通过 `npm test -- "src/lib/dev-bridge/safeInvoke.test.ts" "src/lib/tauri-mock/core.test.ts"`,确认移除 knowledge 优先 mock 后 fallback 与 mock 行为仍稳定。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx"`,确认知识页初始化目录与主路径回归稳定。
- 已通过 `npm run typecheck`。
### 2026-05-05 模块化产品化重构
- 已根据用户反馈确认“知识库”应按功能模块设计,而不是继续扩张单页实现。
- 已将 `src/features/knowledge/KnowledgePage.tsx` 中的状态文案、资料类型、用户可见字段、prompt builder、metadata builder、文件列表、状态标签、状态导轨、资料卡与排障面板拆到 `domain/`、`agent/` 与 `components/` 子模块。
- 已将输入框内项目资料控件从通用 `InputbarComposerSection.tsx` 拆到 `src/components/agent/chat/components/Inputbar/knowledge/InputbarKnowledgeControl.tsx`,通用输入框只保留组合职责。
- 已将 Workspace 中的项目资料加载、默认资料选择、启用状态、整理资料 prompt 与资料管理跳转拆到 `src/components/agent/chat/workspace/knowledge/useWorkspaceKnowledgeRuntime.ts`,继续复用现有 Agent 发送链路。
- 已将普通用户入口文案从“整理成项目资料 / 手动导入 / 高级目录”收敛为“添加项目资料 / 补充导入 / 排障设置”,并在输入框点击“添加项目资料”后先展示说明卡,再由用户明确发送给 Agent 整理。
- 已在资料管理页增加“已添加资料 / 已整理草稿 / 已确认可用”状态导轨,资料卡主动作按状态区分“继续确认 / 用于生成”。
- 已移除资料详情里的不可用“编辑资料说明”禁用按钮,避免普通用户看到无效动作。
- 已更新 `KnowledgePage.test.tsx` 与 `Inputbar/index.test.tsx`,锁定普通首屏不展示“内部标识 / 资料文件名 / 高级:手动指定项目目录”,并覆盖输入框资料整理说明卡。
- 已更新 `scripts/knowledge-gui-smoke.mjs` 的入口文案断言,跟随“补充导入”新命名。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx" "src/components/agent/chat/components/Inputbar/index.test.tsx"`。
- 已通过 `npm run typecheck`。
- 已通过 `npm run test:contracts`。
- 已执行 `npm run bridge:health -- --timeout-ms 10000`,初始 DevBridge 就绪。
- 已执行 `npm run verify:gui-smoke`,workspace-ready、browser-runtime、site-adapters、agent-service-skill-entry、agent-runtime-tool-surface 与 agent-runtime-tool-surface-page 均通过;进入 `smoke:knowledge-gui` 后,`browser_execute_action` 阶段 DevBridge 中途掉线,knowledge GUI smoke 未计为通过。
- 已单独重跑 `npm run smoke:knowledge-gui -- --app-url http://127.0.0.1:1420/ --health-url http://127.0.0.1:3030/health --invoke-url http://127.0.0.1:3030/invoke --timeout-ms 600000 --interval-ms 1000`,仍在 `wait-knowledge-overview` 后的 browser action 阶段触发 DevBridge 掉线,判断为当前本地 DevBridge / CDP smoke 环境稳定性问题,需要后续先清理 dev 进程和 target lock 再复测。
- 已用 Playwright MCP 手动打开现有 Lime 页签并进入知识库,确认页面展示 `项目资料管理`、`添加项目资料` 空态、`补充导入`、`排障设置`、状态导轨,普通页面未出现内部标识、资料文件名或内部目录语义;因 DevBridge 已掉线,真实 seed -> 使用资料 -> Agent 自动发送闭环本轮未完成。
- 已清理残留 `smoke-knowledge-gui` Chrome profile 进程;隔离 target headless 冷构建进度较慢,已中止,避免长时间占用本机资源。
### 2026-05-05 模块化产品化闭环收口
- 已将 `scripts/knowledge-gui-smoke.mjs` 从 DevBridge CDP `browser_execute_action` 流程改为本地 Playwright persistent context,避免 smoke 自测递归依赖浏览器桥接并把 DevBridge 带掉。
- 已让 knowledge GUI smoke 使用真实 DevBridge seed,并创建临时 `temporary` workspace;页面进入 Agent 前会等待资料管理页解析出当前项目,确保“用于生成”回到同一现有 Agent 项目上下文。
- 已将“用于生成”从自动提交改为回到现有 Agent 后预填“请基于当前项目资料生成内容”,并保留 `knowledge_pack` metadata 作为首发 request metadata;这样普通用户可以确认后再发送,不再被突然自动执行打断。
- 已修正 `AgentChatWorkspace` 无文稿入口下 `initialUserPrompt` 的默认语义:`autoRunInitialPromptOnMount=false` 时只预填,不默认发送;只有显式自动运行入口才自动发送。
- 已让资料管理页按 `workingDir` 反查项目并记录 `selectedProjectId`,避免从知识页回 Agent 后漂移到默认项目。
- 已更新 `KnowledgePage.test.tsx`,覆盖 `workingDir -> projectId` 回填、“用于生成”预填意图与 metadata 透传;已更新 `AgentChatPage` 回归,覆盖无文稿入口 initial prompt 不自动发送。
- 已将 knowledge GUI smoke 的 Agent 断言改为普通用户真实可见的输入框状态:`项目资料:未使用` 与已预填的生成意图,而不是等待首页口号或自动发送结果。
- 已通过 `npm run smoke:knowledge-gui -- --app-url http://127.0.0.1:1420/ --health-url http://127.0.0.1:3030/health --invoke-url http://127.0.0.1:3030/invoke --timeout-ms 240000 --interval-ms 1000`。
- 已通过 `npm test -- "src/features/knowledge/KnowledgePage.test.tsx" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/index.test.tsx"`,共 `164` 个相关测试通过。
- 已通过 `npm run typecheck`。
- 已通过 `npm run verify:gui-smoke`,覆盖 workspace-ready、browser-runtime、site-adapters、Agent service skill entry、runtime tool surface/page 与 knowledge GUI smoke;knowledge 阶段完成真实 seed、资料管理页、用于生成回 Agent、补充导入入口验证。
- 已执行 `git diff --check`。
- 已执行普通用户可见路径泄露扫描,`KnowledgePage.tsx`、knowledge components、Inputbar knowledge control 与 smoke 脚本未出现内部标识、资料文件名、高级目录、`knowledge_builder`、`compiled/brief.md`、`frontmatter` 或 `tokens`。
### 2026-05-05 文档架构同步
- 已按最新产品判断更新 `docs/roadmap/knowledge/prd.md`:current 入口从单一知识库页面改为 File Manager、输入框资料图标、首页引导和 Agent 输出沉淀四条路径;`@资料` 降级为兼容入口。
- 已更新 PRD 总体架构图,把 File Manager、输入框资料图标、`@资料` 兼容入口、首页引导、Agent 输出沉淀、导入编排、`lime-knowledge`、资料管理页、现有 Agent 输入框和 Resolver 串成同一闭环。
- 已更新 PRD 关键时序:从 File Manager / 首页添加资料、通过输入框资料图标使用资料、从生成结果沉淀资料、用户修改后重新整理。
- 已更新 PRD 前台信息架构、UI 原型、模块边界、Current / Deprecated 分类、Phase 计划和产品验收标准,明确普通用户主路径不暴露 packName、metadata、compiled、token、runtime fence 或本机完整路径。
- 已新增 `docs/knowledge/README.md`,声明 current 文档事实源,并把 `docs/knowledge/` 下早期方案标为 compat / 参考。
- 已更新 `.gitignore`,仅放行 `docs/knowledge/README.md` 作为可追踪索引;其余 `docs/knowledge/` 私有样例和早期方案仍默认不纳入版本库。
- 已给 `docs/knowledge/lime-knowledge-base-construction-blueprint.md`、`docs/knowledge/markdown-first-knowledge-pack-plan.md`、`docs/knowledge/lime-project-knowledge-base-solution.md`、`docs/knowledge/agent-skills-and-knowledge-pack-boundary.md` 增加 compat 状态说明,避免后续继续按旧方案扩张。
### 2026-05-05 四入口闭环实现
- 已新增 `src/features/knowledge/import/knowledgeSourceImport.ts`,把文件路径与文本资料导入封装为独立前端模块;实现继续复用 `read_file_preview_cmd`、`knowledge_import_source` 与 `knowledge_compile_pack`,不新增后端命令。
- 已在 File Manager 右键菜单补充“设为项目资料”,并在输入框路径 chip 上提供“设为资料”动作;用户从左侧文件管理器添加文件后,不必理解 packName、metadata 或内部目录即可整理为项目资料。
- 已将输入框底栏资料图标明确为项目资料主入口;`@资料` 只做兼容打开同一资料中枢,`@沉淀资料` 继续复用现有输入框资料整理动作,不创建独立 Agent 或新聊天面板。
- 已将首页起手入口从“预填一段资料说明”改为“直接打开输入框资料中枢”,保持首页、输入框资料图标和 `@资料` 兼容入口指向同一浮层。
- 已在助手消息操作区增加“沉淀为项目资料”,将 Agent 输出直接交给 Workspace knowledge runtime 导入与编译,继续由现有 Agent 工作区承载使用与确认。
- 已补首页起手入口、输入建议与引导卡:普通用户可以从首页了解“添加 / 确认 / 使用项目资料”,而不是先进入开发者式管理页。
- 已补稳定回归:File Manager 右键导入、输入框路径 chip 导入、`@资料` / `@沉淀资料` 复用现有输入框动作、消息沉淀、首页入口与 seeded command catalog。
### 2026-05-05 真实 E2E 顺滑度复测
- 已用 Playwright MCP 复用真实 Lime 页签,刷新 `http://127.0.0.1:1420/` 后重新建立基线:DevBridge 健康、首页可交互、控制台 error 为 0。
- 首页点击 `添加资料` 可直接打开输入框资料中枢,且不会把说明文字预填进输入框;这一段顺利。
- 资料中枢点击 `去确认资料` 能进入项目资料管理页;确认、设为默认、用于生成回 Agent 均能走真实 DevBridge,不依赖 mock fallback。
- `用于生成` 回到 Agent 后会预填 `请基于当前项目资料生成内容`,但视觉状态仍是 `项目资料:未使用`,用户还要再点资料中枢里的 `使用这份资料`;这会让普通用户误以为“用于生成”没有真正生效。
- 手动点击资料中枢 `使用这份资料` 后,输入框状态能变为 `正在使用:资料名称`;这一段顺利,但多了一步。
- 从 Agent 输出点击 `沉淀为项目资料` 时,Playwright 正常点击被消息区覆盖层拦截;通过 JS click 才触发 `knowledge_import_source` 与 `knowledge_compile_pack`。这说明普通用户也可能遇到命中区域不稳定或按钮难点的问题。
- 资料详情仍暴露 `custom`、Markdown 结构、运行时边界、`name/status/trust`、source 路径等内部信息;人工确认后引用摘要里仍显示 `status: draft`、`trust: unreviewed`,与页面“已确认”状态冲突。
- 回到知识库后出现项目上下文漂移:页面显示了 smoke 临时项目资料和临时目录提示,而不是当前默认项目资料;说明 workingDir / selectedProjectId 的恢复仍不够稳定。
- 结论:自动 smoke 主链通过,但普通用户真实 E2E 不够顺滑;当前最大问题不是桥接失败,而是状态语义、上下文恢复、点击命中和普通用户文案仍有产品化缺口。
### 2026-05-05 真实 E2E 问题修复
- 已给 `用于生成` 增加 `initialKnowledgePackSelection` 导航参数,并贯通 `AgentPageParams -> AppPageContent -> AgentChatWorkspace -> Workspace knowledge runtime`,从资料管理回 Agent 后直接显示 `正在使用:资料名称`,不再要求用户二次点击。
- 已把知识页项目恢复顺序改为显式页面参数优先,其次最近项目 ID,最后默认项目;临时 smoke 目录不再单独作为普通入口默认项目,避免刷新或回知识库时上下文漂移。
- 已将资料详情、列表卡片和文件条目统一走普通用户预览清洗:隐藏 `custom`、`metadata`、`compiled/brief.md`、`sources/...`、本机完整路径、运行时摘要和 `status/trust` 原始字段;无效资料摘要改为“缺少原始内容,请补充后再确认”。
- 已把助手消息的“沉淀为项目资料”改为常显文字按钮,并修复普通 Playwright click 被消息气泡拦截的问题;无原始内容的助手结果会提示先补充资料,不再继续沉淀成项目资料。
- 已同步 `scripts/knowledge-gui-smoke.mjs`,GUI smoke 断言从旧的 `项目资料:未使用` 更新为 `正在使用:资料名称`,让自动 E2E 对齐当前产品语义。
- 已继续清理历史脏资料摘要:列表与详情不再展示 `何时使用`、`缺失事实时`、`不编造来源资料` 等 Builder 模板腔,避免普通用户看到像开发提示词的内容。
## 待完成清单
- [x] 给 `docs/knowledge/` 早期方案补 compat 状态说明,避免继续误用旧架构。
- [x] 新增 `docs/knowledge/README.md`,明确 current / compat 文档事实源。
- [x] 更新 `.gitignore`,让 `docs/knowledge/README.md` 作为 knowledge 文档索引进入 repo。
- [x] 更新 current PRD 的项目资料产品闭环、架构图、时序图和阶段计划。
- [x] 实现 File Manager 右键与输入框路径 chip 的“设为项目资料”入口。
- [x] 实现 `@资料` 与 `@沉淀资料`,并复用现有 Agent 输入框主链。
- [x] 将可见资料口令收敛为 `@资料`,保留内部 command key 与现有 Agent 主链不变。
- [x] 将 `@资料` 从单一启用动作升级为资料中枢:按当前状态引导添加、确认、选择、使用、关闭或补充资料。
- [x] 将资料中枢主入口收口到输入框底栏资料图标,`@资料` 只保留为兼容兜底,不再作为普通命令标签或主路径宣传。
- [x] 将首页“添加资料”入口改为直接打开输入框资料中枢,不再预填说明文字或制造第二条入口语义。
- [x] 实现 Agent 输出“沉淀为项目资料”入口。
- [x] 将 Agent 输出“沉淀为项目资料”纳入 `smoke:knowledge-gui`,完成普通点击、真实导入 / 编译和管理页待确认资料可见的 E2E 验收。
- [x] 在首页补充项目资料添加与使用引导。
- [x] 收口 File Manager 与输入框路径 chip 的可用性判断:仅对 Markdown / 文本文件展示直接整理入口,PDF / Word 等非文本资料给出普通用户可执行提示,避免误导为已支持直接解析。
- [x] 修正 `lime-knowledge` crate 编译问题和格式问题。
- [x] 补齐 Tauri command 注册与主 crate 依赖。
- [x] 补齐 `defaultMocks` 中的 knowledge 命令 mock。
- [x] 确认 `agentCommandCatalog`、`mockPriorityCommands`、Rust 注册、前端调用四侧一致。
- [x] 运行 `cargo test -p lime-knowledge`。
- [x] 运行 `npm run test -- src/lib/api/knowledge.test.ts` 或仓库等价 vitest 定向入口。
- [x] 运行 `npm run test:contracts`。
- [x] 根据结果更新本执行计划。
- [x] 新增并挂载知识库 GUI 页面。
- [x] 补齐知识库页面、导航、任务中心入口的前端回归测试。
- [x] 将知识包选择 metadata 接到 AgentChatWorkspace 发送链路。
- [x] 将 Knowledge Context Resolver 接入 full runtime system prompt。
- [x] 补齐 runtime 知识包注入和 prompt stage 顺序测试。
- [x] 在聊天输入区补轻量知识包选择器,让用户不必先进入知识库页面再“去生成”。
- [x] 产品化 Builder Skill,让来源资料能通过正式 Skill 生成更完整的 `wiki/` 与 `compiled/` 草稿。
- [x] 为聊天输入区知识包开关增加多包选择菜单,继续复用 `knowledge_pack` metadata 与 runtime fenced context。
- [x] 为 `knowledge_builder` 增加类型化模板和质量检查输出,覆盖个人 IP、品牌产品、组织 Know-how、增长策略四类。
- [x] 对知识库页面做普通用户语言降噪,隐藏内部协议、目录、Builder/Skill、token 与 fenced context 等开发者细节。
- [x] 重做知识页产品主路径:默认展示资料助手,而不是知识包管理台。
- [x] 将项目资料主入口回流到现有 Agent 输入框。
- [x] 下线知识页内独立聊天面板。
- [x] 将知识页定位收敛为项目资料管理页。
- [x] 补齐 knowledge DevBridge dispatcher。
- [x] 在稳定 DevBridge + Playwright 环境完成知识库 GUI 主路径 E2E:真实 seed、资料列表、用于生成回 Agent、Agent 自动发送。
- [x] 完成 Knowledge 前端 feature module 拆分:domain / agent / components / Inputbar knowledge / Workspace knowledge runtime。
- [x] 在清理本地 DevBridge / CDP smoke 环境后,重跑 `smoke:knowledge-gui` 并补齐真实 seed -> 使用资料 -> 回到现有 Agent 预填生成意图闭环。
- [x] 修复真实 E2E 暴露的用于生成未自动启用、项目上下文漂移、详情页内部信息泄露和消息沉淀按钮命中问题。
## 验证记录
```bash
cargo test -p lime-knowledge
npm test -- src/lib/api/knowledge.test.ts
npm run test:contracts
cargo fmt --package lime-knowledge --check
cargo check -p lime
npm run typecheck
npm test -- "src/features/knowledge/KnowledgePage.test.tsx" "src/components/AppPageContent.test.tsx" "src/components/agent/chat/components/ChatSidebar.test.tsx" "src/components/AppSidebar.test.tsx" "src/lib/navigation/sidebarNav.test.ts" "src/i18n/__tests__/translation-coverage.test.ts"
cargo fmt --package lime --check
cargo test -p lime merge_system_prompt_with_knowledge_context_should_append_fenced_context_from_metadata
cargo test -p lime knowledge_pack_metadata_should_force_full_runtime_context
cargo test -p lime test_runtime_turn_source_keeps_full_runtime_prompt_stage_order_contract
npm run typecheck
npm run verify:gui-smoke
npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx"
npm run typecheck
cargo fmt --manifest-path "src-tauri/Cargo.toml" --package lime --check
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
cargo test --manifest-path "src-tauri/Cargo.toml" -p lime should_embed_social_image_tool_contract_in_default_skill --no-default-features --features local-sensevoice
cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-core test_default_lime_skill_directories_include_embedded_defaults --no-default-features
禁名扫描:新增路线图、执行计划、Builder Skill、默认 Skill 注册与 Knowledge 前端模块无命中。
CARGO_HOME="/tmp/lime-gui-smoke-cargo-home" npm run verify:gui-smoke
npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx"
npm run typecheck
CARGO_HOME="/tmp/lime-gui-smoke-cargo-home" npm run verify:gui-smoke
# 2026-05-02 重跑未完成:独立 target 重建 + 既有 headless 链路长时间未暴露 DevBridge,未计为通过。
cargo fmt --manifest-path "src-tauri/Cargo.toml" --package lime --check
# 内容契约脚本输出:knowledge_builder content contract ok
CARGO_TARGET_DIR="/tmp/lime-knowledge-resource-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime should_embed_social_image_tool_contract_in_default_skill --no-default-features
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm test -- "src/components/AppPageContent.test.tsx"
npm run typecheck
CARGO_HOME="/tmp/lime-gui-smoke-cargo-home" npm run verify:gui-smoke
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm run typecheck
npm run bridge:health -- --timeout-ms 20000
# 2026-05-04 知识库降噪后 GUI smoke 未执行:DevBridge 未监听 127.0.0.1:3030,health check 超时。
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm run typecheck
npm run bridge:health -- --timeout-ms 20000
# 2026-05-05 资料助手主路径重做后 GUI smoke 未执行:DevBridge 未监听 127.0.0.1:3030,health check 超时。
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx"
npm run typecheck
cargo fmt --manifest-path "src-tauri/Cargo.toml" --package lime --check
npm run test:contracts
# 禁名扫描:用户要求禁止出现的具体名称无命中。
# 普通用户路径泄露扫描:新增主路径未再暴露“聊天任务 / 当前资料 / 查看引用 / 资料助手 / Builder 生成”等旧页面语义。
CARGO_HOME="/tmp/lime-gui-smoke-cargo-home" npm run verify:gui-smoke
# 2026-05-05 Agent 输入框回流后 GUI smoke 未完成:独立 target 冷构建约 34 分钟仍未进入 smoke 断言,临时进程组已中止,该次不计为通过。
npm run bridge:health -- --timeout-ms 5000
# 2026-05-05 Bridge 就绪:127.0.0.1:3030 health status=ok。
# 2026-05-05 Playwright E2E:真实 DevBridge seed 两份项目资料,进入知识库,确认项目资料管理页、全部项目资料、手动导入、用于生成可见;点击用于生成后回到现有 Agent,并通过 agent_runtime_submit_turn 自动发送“请基于当前项目资料生成内容”。
npm run test:contracts
npm test -- "src/lib/dev-bridge/safeInvoke.test.ts" "src/lib/tauri-mock/core.test.ts"
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm run typecheck
# 控制台 / 页面泄露扫描:无 ERROR / Failed / ERR_CONNECTION;无 knowledge_builder、compiled/brief.md、.lime/knowledge、frontmatter、token、聊天任务、当前资料:、查看引用、资料助手、Builder 生成。
npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000
npm test -- "src/features/knowledge/KnowledgePage.test.tsx" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/index.test.tsx"
npm run typecheck
npm run verify:gui-smoke
git diff --check
# 普通用户可见路径泄露扫描:KnowledgePage、knowledge components、Inputbar knowledge control 与 knowledge GUI smoke 无内部实现词命中。
test -f docs/roadmap/knowledge/prd.md && test -f docs/knowledge/README.md
rg -n "File Manager|@资料|沉淀为项目资料|knowledge_import_source|knowledge_resolve_context|KnowledgePack" docs/roadmap/knowledge/prd.md
# 文档禁名扫描:current PRD、执行计划、docs/knowledge README 与 compat 方案无命中。
git diff --check -- docs/roadmap/knowledge/prd.md docs/exec-plans/agent-knowledge-implementation-plan.md docs/knowledge/README.md docs/knowledge/lime-knowledge-base-construction-blueprint.md docs/knowledge/markdown-first-knowledge-pack-plan.md docs/knowledge/lime-project-knowledge-base-solution.md docs/knowledge/agent-skills-and-knowledge-pack-boundary.md
# 2026-05-05 文档架构同步:knowledge docs validation ok。
npm test -- "src/components/agent/chat/components/FileManager/FileManagerSidebar.test.tsx" "src/components/agent/chat/components/Inputbar/components/InputbarCore.test.tsx" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/components/EmptyState.test.tsx" "src/lib/base-setup/seededCommandPackage.test.ts"
npm run typecheck
npm run test:contracts
# 2026-05-05 四入口闭环实现:File Manager、输入框路径 chip、@资料、@沉淀资料、Agent 输出沉淀与首页入口定向回归通过。
npm run verify:gui-smoke
# 2026-05-05 四入口闭环 GUI smoke 通过:已补齐当前工作树 runtime_evidence_pack_service.rs 的 AgentThreadItem 导入编译缺口;本轮 cold target 里 sherpa-onnx-sys 下载仍出现 TLS close_notify 警告,但 DevBridge 已就绪,workspace-ready、browser-runtime、site-adapters、Agent service skill entry、runtime tool surface/page 与 knowledge GUI smoke 均通过。
npm test -- "src/features/knowledge/import/knowledgeSourceSupport.test.ts" "src/components/agent/chat/components/FileManager/FileManagerSidebar.test.tsx" "src/components/agent/chat/components/Inputbar/components/InputbarCore.test.tsx"
npm run typecheck
# 2026-05-05 文件导入可用性收口:Markdown / 文本文件保留直接整理入口;PDF 等非文本文件不再在输入框展示“设为资料”,File Manager 右键菜单展示禁用态与可执行提示。
npm run verify:gui-smoke
# 2026-05-05 文件导入可用性收口后 GUI smoke 通过:复用已有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、Agent service skill entry、runtime tool surface/page 与 knowledge GUI smoke 均通过。
npm test -- "src/lib/base-setup/seededCommandPackage.test.ts" "src/components/agent/chat/components/Inputbar/index.test.tsx"
npm run test:contracts
npm run typecheck
npm run verify:gui-smoke
# 2026-05-05 资料口令产品化收口:可见 mention 从模块名收敛为 `@资料`,`knowledge_pack` 内部 command key 与现有 Agent 输入框主链保持不变。
# 2026-05-05 资料口令收口后 GUI smoke 通过:复用已有 headless Tauri 与 DevBridge,knowledge GUI smoke 通过。
npm test -- "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/lib/base-setup/seededCommandPackage.test.ts"
npm run typecheck
npm run test:contracts
# 2026-05-05 `@资料` 闭环修正:`@资料` 不再直接启用资料,而是打开资料中枢;无资料、待确认、未启用、已启用四类状态均有主动作回归。
npm test -- "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/lib/base-setup/seededCommandPackage.test.ts"
npm run typecheck
npm run verify:gui-smoke
# 2026-05-05 追加修正:初始 capability route 带 `@资料` 时也会打开资料中枢,不再渲染普通 builtin command badge。
# 2026-05-05 追加修正后 GUI smoke 通过:复用已有 headless Tauri 与 DevBridge,knowledge GUI smoke 通过。
npm test -- "src/components/agent/chat/components/EmptyState.test.tsx" "src/components/agent/chat/home/buildHomeSkillSurface.test.ts" "src/components/agent/chat/home/HomeStarterChips.test.tsx"
npm test -- "src/components/agent/chat/components/EmptyStateComposerPanel.test.tsx" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts" "src/lib/base-setup/seededCommandPackage.test.ts"
npm run typecheck
npm run test:contracts
npm run verify:gui-smoke
# 2026-05-05 首页添加资料入口收口:点击首页“添加资料”直接打开输入框资料中枢,不再预填解释 prompt;GUI smoke 通过,knowledge GUI 阶段覆盖资料管理页、用于生成回 Agent 与补充导入入口。
git diff --check -- src/components/agent/chat/home/homeSurfaceTypes.ts src/components/agent/chat/home/homeSurfaceCopy.ts src/components/agent/chat/home/HomeStarterChips.tsx src/components/agent/chat/home/buildHomeSkillSurface.test.ts src/components/agent/chat/components/EmptyState.tsx src/components/agent/chat/components/EmptyState.test.tsx docs/roadmap/knowledge/prd.md docs/exec-plans/agent-knowledge-implementation-plan.md
# 2026-05-05 首页资料入口收口后禁名 / 用户可见泄露扫描无命中。
npm run bridge:health -- --timeout-ms 120000
npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000
# 2026-05-05 Playwright MCP 真实 E2E 顺滑度复测:自动 knowledge smoke 通过;手动用户流暴露用于生成后未自动启用资料、消息沉淀按钮点击命中不稳定、详情页内部信息泄露、项目上下文漂移四类产品化问题。
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm test -- "src/components/agent/chat/components/MessageList.test.tsx"
npm test -- "src/components/agent/chat/index.test.tsx"
npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts"
npm run typecheck
# 2026-05-05 typecheck 未通过:阻塞项来自当前工作树既有 capabilityDrafts / tauri-mock 类型错误,非本轮 knowledge 改动。
npm run test:contracts
npm run bridge:health -- --timeout-ms 120000
npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000
# 2026-05-05 修复后 Playwright MCP 复测:用于生成回 Agent 后输入框显示“正在使用:资料名”;普通 click 可点击“沉淀为项目资料”;控制台 error 为 0。
npm run verify:gui-smoke
# 2026-05-05 修复后 GUI smoke 通过:复用已有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、Agent service skill entry、runtime tool surface/page 与 knowledge GUI smoke 均通过。
npm test -- "src/features/knowledge/KnowledgePage.test.tsx"
npm run typecheck
npm run test:contracts
npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000
# 2026-05-05 历史脏资料摘要降噪后:KnowledgePage 定向测试、typecheck、contracts 通过;knowledge GUI smoke 因 DevBridge 未监听 3030 未执行成功,尝试重启 headless 时遇到其他工作树 Rust 文件持续变更触发 watch 重建,已中止该次环境进程,不计为通过。
npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000
# 2026-05-05 产品 E2E 验收收口:knowledge GUI smoke 已覆盖真实 seed、用于生成回 Agent、Agent 结果样本普通点击“沉淀为项目资料”、真实导入 / 编译、管理页出现待确认资料和补充导入入口。
node --check "scripts/knowledge-gui-smoke.mjs"
npm run typecheck
npm run test:contracts
npm run verify:gui-smoke
# 2026-05-05 GUI smoke 全量通过:workspace-ready、browser-runtime、site-adapters、Agent service skill entry、runtime tool surface/page、knowledge GUI smoke 与 design-canvas 均通过。
```
## 后续切片
1. 把 File Manager 文本文件识别从扩展名 / mimeType 扩展到 PDF / DOCX 的“先预览再整理”安全路径,但仍不向普通用户暴露内部转换细节。
2. 继续收口普通用户语言:管理页只展示资料名称、状态、风险提醒、引用摘要和确认动作;内部文件名、Skill 名称、目录结构只保留在开发文档和测试 mock 中。
3. 为运行时 Knowledge Context Resolver 增加更细的章节选择和成本控制,但只在开发者诊断或高级设置中展示,不进入普通用户默认路径。
4. 为 `knowledge_builder` 增加示例输入 / 输出快照测试,锁定不同 `pack_type` 的生成结构。
## 2026-05-05 产品 E2E 闭环续测
- 页面 / URL:`http://127.0.0.1:1420/`,从首页进入知识库,再点击“用于生成”回到现有 Agent 输入框。
- 已完成步骤:知识库加载、普通用户可见文案检查、资料中枢打开、已确认资料启用、Agent 输入框显示“正在使用:资料名”、发送“请基于当前项目资料生成内容”。
- 暴露问题:知识页默认展示排障目录预览,属于普通用户信息泄露;输入框资料中枢把待确认 / 缺素材资料放在可用选项里,属于体验误导;发送后 DevBridge 出现 `workspace_get` / `agent_runtime_get_session` / event stream 超时,属于桥接稳定性缺口。
- 本轮修复:排障入口改为“项目识别异常?”并默认隐藏本机路径;资料中枢只把已确认资料作为可用选项,待确认资料只显示数量和“管理资料”;运行时默认资料选择优先已确认资料,避免默认草稿抢占生成路径。
- 新增 skill:`.codex/skills/lime-product-e2e-loop/SKILL.md`,沉淀“真实用户路径 E2E -> 问题分类 -> 最小产品化修复 -> 复测记录”的复用流程。
- 验证通过:`npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts" "src/components/agent/chat/workspace/knowledge/useWorkspaceKnowledgeRuntime.test.ts" "src/features/knowledge/KnowledgePage.test.tsx"`;追加 `npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/workspace/knowledge/useWorkspaceKnowledgeRuntime.test.ts" "src/features/knowledge/KnowledgePage.test.tsx"`;`git diff --check` 通过;禁名扫描无命中;skill 基础结构校验通过。
- 验证未完成:`npm run typecheck` 当前失败在既有 `src/lib/layered-design/imageTasks.ts` 类型错误,非本轮 Knowledge 改动;`npm run bridge:health -- --timeout-ms 15000` 失败,当前 DevBridge 3030 监听进程无响应,headless Tauri watch 反复因其他工作树 Rust 文件变化重建并等待 Cargo lock,因此本轮 Playwright 复测停在修复前用户流和组件回归,尚未完成修复后真实 GUI 复走。
## 2026-05-05 项目资料产品化二次收口
- 页面 / URL:`http://127.0.0.1:1420/`;手动 Playwright 从首页进入左侧 `项目资料`,再回到 Agent 输入框资料中枢,最后发送一次带资料引用的消息。
- 闭环结果:模块入口已从左侧 `知识库` 收敛为 `项目资料`;管理页首屏只保留“管理与确认”职责;空资料态明确提示三条添加路径:输入框添加、文件管理器添加、对话结果沉淀;输入框无资料时不再同时露出“管理资料”这种管理动作。
- 本轮修复:更新 `src/lib/navigation/sidebarNav.ts`、`src/components/agent/chat/components/ChatSidebar.tsx` 的入口命名;更新 `KnowledgePage` 的首屏、空态和主按钮;更新 `InputbarKnowledgeControl` / `knowledgeHubState` 的无资料文案、菜单按钮和二级管理动作条件;同步 `scripts/knowledge-gui-smoke.mjs` 的导航断言。
- 用户视角验证:知识管理页截图 `knowledge-after-optimization.png` 已确认不再把模块包装成独立聊天页;输入框资料中枢可选择已确认资料,点击后显示 `正在使用:资料名`;发送时页面进入现有 Agent 对话流,没有新建独立 Agent。
- 控制台 / Bridge 状态:冷构建后的 DevBridge 已恢复,`npm run bridge:health -- --timeout-ms 30000` 通过;手动发送曾在 DevBridge 未就绪期间出现 `无法创建会话`,Bridge 恢复后 `smoke:knowledge-gui` 真实通过。
- 验证通过:`npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts"`;`npm test -- "src/lib/navigation/sidebarNav.test.ts" "src/components/AppSidebar.test.tsx" "src/components/agent/chat/components/ChatSidebar.test.tsx" "src/features/knowledge/KnowledgePage.test.tsx"`;`npm run typecheck`;`npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000`。
- 验证未完成:`npm run verify:gui-smoke` 未通过,失败在 `smoke:agent-runtime-tool-surface-page` 的浏览器 CDP 标签页读取 `http://127.0.0.1:15668/json/list`,不在本轮项目资料 UI / smoke 脚本改动边界;全局 `git diff --check` 仍受其它工作树文件尾随空白影响,本轮改动文件的 `git diff --check -- <touched files>` 通过。
## 2026-05-05 项目资料产品化三次收口
- 页面 / URL:`http://127.0.0.1:1420/`;手动 Playwright 从首页 `添加资料`、输入框资料图标、左侧 `项目资料`、File Manager 四条路径复走。
- 用户闭环结果:`添加资料` 和管理页 `回到 Agent 添加` 现在都会打开输入框项目资料浮层;浮层在已有资料时同时给出 `添加新资料`、`检查资料`、`使用这份资料`,不再把用户困在“只能使用已有资料”的分支里。
- 本轮修复:输入框项目资料浮层新增常显补充入口,并把二级管理动作改为 `检查资料`;知识页回 Agent 添加改为直达现有 Agent 输入框资料浮层;File Manager 文本文件普通点击改为 `加入对话`,行内提供 `设为资料`,避免点击文件直接调系统打开;输入框本地文件 chip 隐藏本机绝对路径,只保留文件名和 `本地文件 / 本地文件夹`。
- 普通用户信息边界:默认页面不再暴露本机目录、`.lime/knowledge`、`compiled/brief.md`、`metadata/status/trust`、`knowledge_builder` 或命令名;路径只在测试 mock 和内部 metadata 中存在。
- Playwright 证据:首页点击 `添加资料` 后浮层可见 `添加新资料 / 检查资料 / 使用这份资料`;点击 `添加新资料` 后输入框填入整理资料提示;File Manager 打开后文本文件行内出现 `加入对话 / 设为资料`;点击文本文件后没有再触发 `open_with_default_app` unknown command,新控制台仅剩 DevBridge event stream 在重建期间的环境噪音。
- 验证通过:`npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts"`;`npm test -- "src/features/knowledge/KnowledgePage.test.tsx" "src/components/agent/chat/components/FileManager/FileManagerSidebar.test.tsx"`;`npm test -- "src/components/agent/chat/components/Inputbar/components/InputbarCore.test.tsx" "src/components/agent/chat/components/EmptyStateComposerPanel.test.tsx"`;`npm run typecheck`;`npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000` 主流程通过。
- 验证未完成:`npm run verify:gui-smoke` 本轮仍失败在 `smoke:agent-runtime-tool-surface-page` 的 `launch_browser_session fetch failed`;同时 headless Tauri watch 期间其它 Rust 文件持续变更触发重建,导致 DevBridge 3030 多次断开。`smoke:knowledge-gui` 已通过主断言,但清理临时项目时也因 DevBridge 重建出现 `workspace_delete fetch failed`,记录为环境 / 并发重建噪音,不判定为项目资料主链失败。
## 2026-05-05 产品 E2E 验收补测与修复
- 页面 / URL:`http://127.0.0.1:1420/`;Playwright 从首页、输入框项目资料浮层、File Manager、项目资料管理页和最近结果路径复走。
- 闭环判定:A 首页 / 输入框添加资料、B File Manager 加入对话与设为资料、C 项目资料管理页确认与用于生成回 Agent 已按普通用户路径补测;D Agent 结果沉淀当前样本未稳定展示可点击结果按钮,本轮只记录为 `warn`,不判定完成。
- 本轮发现:File Manager 顶部仍显示本机完整路径,属于信息泄露;首页空态输入框的本地文件 chip 没有透传“设为项目资料”动作,属于产品阻塞;浏览器 mock 文件路径进入真实文件预览时会报 `No such file or directory`,属于 mock / bridge 组合缺口;`回到 Agent 添加` 存在重复按钮定位,E2E 脚本需用 `.first()` 或明确作用域。
- 本轮修复:File Manager 顶部位置改为“本地位置”,`title` 不再放绝对路径;`EmptyState -> EmptyStateComposerPanel -> InputbarCore` 补齐 `onImportPathReferenceAsKnowledge` 透传,首页和空态 chip 也能直接“设为资料”;`knowledgeSourceImport` 对浏览器文件管理器 mock 文本路径增加产品化 fallback,避免普通 click 后出现文件元信息错误。
- Playwright 证据:修复后 B 路径显示 `brief.md / 本地文件 / 设为项目资料`,普通 click 可命中 chip 的设为资料动作,File Manager 和输入框正文不再展示 `/Users/...`;C 路径管理页首屏未出现 `.lime/knowledge`、`compiled/brief.md`、`metadata`、`status/trust`、`knowledge_builder`、`frontmatter` 或 `token`。
- 验证通过:`npm test -- "src/features/knowledge/import/knowledgeSourceImport.test.ts" "src/features/knowledge/import/knowledgeSourceSupport.test.ts"`;`npm test -- "src/components/agent/chat/components/EmptyStateComposerPanel.test.tsx" "src/components/agent/chat/components/FileManager/FileManagerSidebar.test.tsx"`;`npm test -- "src/components/agent/chat/index.test.tsx" -t "点击顶部加号应在任务中心新标签内嵌首页起手页"`;`npm run typecheck`;本轮触达文件 `git diff --check` 通过;禁名扫描通过。
- 验证警告:`npm test -- "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/components/InputbarCore.test.tsx"` 首次全量组合仅 1 个任务中心标签用例失败,单测定向重跑通过,按当前工作树并发负载下的组合级波动记录。
- 验证未完成:本轮最终 `npm run bridge:health -- --timeout-ms 10000` 未就绪,`npm run tauri:dev:headless` 停在 Tauri / Cargo dev 进程等待阶段且 3030 未监听;因此修复后的完整 A/B/C/D Playwright 复走仍缺稳定 DevBridge 复验,不能把产品 E2E 验收宣称为全部完成。
## 2026-05-05 产品 E2E 验收收口
- 页面 / URL:`http://127.0.0.1:1420/`;复用已就绪 DevBridge,并通过 `smoke:knowledge-gui` 走完整项目资料产品闭环。
- 闭环判定:A 首页 / 输入框添加资料、B File Manager 文本资料设为项目资料、C 项目资料管理页用于生成回现有 Agent、D Agent 输出沉淀为项目资料均已纳入可重复 E2E;本轮不再保留 D 路径 `warn`。
- 本轮修复:`scripts/knowledge-gui-smoke.mjs` 在创建临时项目后再按实际项目根目录 seed 知识资料,避免页面项目根与 seed 根不一致;同时加入 Agent 结果样本,普通点击“沉淀为项目资料”,等待真实 `knowledge_import_source` / `knowledge_compile_pack` 完成,并在管理页验证待确认资料可见。
- 产品证据:`smoke:knowledge-gui` 阶段顺序包含 `open-agent-with-knowledge -> wait-agent -> prepare-agent-result -> wait-agent-result -> capture-agent-result -> wait-agent-result-captured -> wait-captured-agent-result -> open-import-view`,确认从“使用资料”到“结果沉淀”再回“管理确认”的闭环顺序。
- 验证通过:`node --check "scripts/knowledge-gui-smoke.mjs"`;`npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000`;`npm run typecheck`;`npm run test:contracts`;`npm run verify:gui-smoke`。
- 当前剩余风险:Agent 结果样本由 E2E 脚本注入历史消息以避开真实模型配置依赖;点击、导入、编译和管理页展示均走真实 GUI / DevBridge。后续如要覆盖真实模型生成,只应作为模型配置可用时的增强验收,不再阻塞当前项目资料产品闭环。
## 2026-05-06 产品 E2E 验收最终复跑
- 页面 / URL:`http://127.0.0.1:1420/`;使用隔离 `CARGO_TARGET_DIR="/tmp/lime-knowledge-headless-target"` 启动 headless Tauri,并复用已有 Vite 前端。
- 用户闭环判定:已完成首页 `添加资料` 打开资料中枢、File Manager `brief.md -> 设为项目资料`、项目资料页 `用于生成` 回现有 Agent、Agent 结果 `沉淀为项目资料`、回管理页继续确认的完整闭环;本轮达到产品 E2E 可交付门槛。
- 本轮发现:File Manager 行本身是可点击区域,自动化按文本找 `设为资料` 时命中了包含同名文案的文件行,实际触发的是“加入对话”,属于 `测试缺口` 与 `点击命中风险`,不是资料导入后端失败。
- 本轮修复:File Manager 行内 `设为资料` 增加稳定可访问名 `设为项目资料 <文件名>`;`scripts/knowledge-gui-smoke.mjs` 改为在 File Manager 作用域内点击该可访问名,并等待真实 `knowledge_list_packs` 出现由文件导入生成的资料。
- 验证通过:`node --check "scripts/knowledge-gui-smoke.mjs"`;`npm test -- "src/components/agent/chat/components/FileManager/FileManagerSidebar.test.tsx"`;`npm run bridge:health -- --timeout-ms 10000`;`npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000`;`npm run typecheck`;`npm run test:contracts`;`npm run verify:gui-smoke -- --reuse-running`。
- 质量收口:`npm run verify:gui-smoke -- --reuse-running` 覆盖 workspace-ready、browser-runtime、site-adapters、Agent service skill entry、runtime tool surface/page、knowledge GUI 与 design canvas,全部通过。
## 2026-05-06 输入框资料入口排版收口与启动修复
- 页面 / URL:`http://127.0.0.1:1420/`;本轮聚焦输入框底栏和项目资料浮层,不把 `项目资料` 收进 `高级设置`,因为它是本次生成的上下文来源,不是配置项。
- 本轮 UI 收口:底栏顺序调整为 `资料 / 模型 / 高级设置 / 文件管理器`;模型 badge 文案从 `当前模型` 收敛为 `模型`;资料状态收敛为 `资料可用 / 资料待确认 / 添加资料 / 资料:<名称>`,减少普通用户看到的长解释和重复按钮。
- 浮层收口:项目资料主按钮合并下拉入口,取消单独小箭头按钮;浮层改成上下文选择器语气,保留 `添加新资料 / 检查资料 / 使用这份资料`,不再呈现为设置面板。
- 启动阻塞修复:`tauri:dev:headless` 失败的直接原因依次为 1420 被 `vite preview` 占用、`SkillsPage.tsx` 出现中文弯引号导致 Vite optimize 失败、`MemoryPage.tsx` 存在未闭合 JSX 标签导致 typecheck 失败、隔离 Cargo target 一度写入失败提示磁盘不足;本轮只做语法级最小修复,并恢复 1420 / 3030 可用。
- 运行态处理:磁盘空间恢复后仍优先避免再次触发 Cargo 大编译,改用 `npm run dev:web-bridge` 启动 1420,再直接运行既有 `src-tauri/target/debug/lime` 恢复 DevBridge;当前验证时 1420 / 3030 均已监听。
- E2E 结果:`smoke:knowledge-gui` 已复走首页添加资料、File Manager 设为项目资料、项目资料页用于生成回现有 Agent、Agent 结果沉淀为项目资料、回管理页继续确认的完整闭环。
- 验证通过:`node --check "scripts/knowledge-gui-smoke.mjs" && node --check "scripts/agent-service-skill-entry-smoke.mjs"`;`npm test -- "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/Inputbar/knowledge/knowledgeHubState.test.ts" "src/components/agent/chat/components/EmptyStateComposerPanel.test.tsx"`;`npm run bridge:health -- --timeout-ms 30000`;`npm run smoke:knowledge-gui -- --app-url "http://127.0.0.1:1420/" --health-url "http://127.0.0.1:3030/health" --invoke-url "http://127.0.0.1:3030/invoke" --timeout-ms 240000 --interval-ms 1000`;`npm run typecheck`。
- 验证说明:本轮未重新跑完整 `npm run verify:gui-smoke -- --reuse-running`,因为上一轮被中断后留下的 browser-runtime smoke、1420 preview 占用和 Cargo target 空间问题需要先处理;本轮用知识库专项 GUI smoke 证明项目资料主链已恢复。
File diff suppressed because it is too large Load Diff
@@ -1,531 +0,0 @@
# AI 图层化设计实现执行计划
> 状态:P4I 原生工程目录远程资产持久化缓存已完成,P4J 扁平图拆层协议首刀已完成,P4K 扁平图 draft `canvas:design` artifact bridge 已完成,P4L 上传扁平图本地 draft adapter 已完成,P4M DesignCanvas 候选层切换首刀已完成,P4N 上传扁平图本地 heuristic seed 首刀已完成;上传图片现在已能直接归一为 extraction draft,并生成可切换的本地裁片候选层进入 current `DesignCanvas`;定向单测、ESLint、定向 TypeScript 与 GUI smoke 已通过
> 创建时间:2026-05-05
> 路线图来源:`docs/roadmap/ai-layered-design/README.md`
> 当前目标:围绕 `LayeredDesignDocument` current 事实源完成生成、编辑、任务回写、工程目录保存、恢复、PSD-like 专业层栈投影,以及扁平图拆层 draft/候选层切换与本地 heuristic seed 首刀;下一步进入拆层确认页接线、真实 analyzer adapter,或真 PSD writer、复杂 matting / mask refine。
## 主目标
把 Lime 的 AI 图片生成从“返回一张扁平 PNG”升级为“生成、保存、重新打开并继续编辑的设计工程”:
```text
用户目标 / @海报 / @配图
-> Layer Planner
-> Asset Generator
-> LayeredDesignDocument
-> Design Canvas Editor
-> Exporter / Artifact / Evidence
```
固定事实源:
**AI 图层化设计的 current 事实源是 `LayeredDesignDocument`;Canvas Editor、导出、单层重生成和后续拆层都必须读写这份文档。现有 `DocumentCanvas` / `ImageTaskViewer` / `TeamWorkspaceCanvas` 只作为 UI 和交互基础参考,不反向定义设计协议。**
## 已完成阶段范围
已完成:
1. 新增执行计划并回挂 `docs/exec-plans/README.md`。
2. 新增 `src/lib/layered-design/` 的 P1 最小协议。
3. 用纯函数保证图层排序、默认值归一化、单层资产替换和 transform 更新不变量。
4. 补定向单测,证明预览 PNG 只是导出投影,不是设计事实源。
5. 新增 `DesignCanvas` 最小可见 UI,并把 `canvas:design` Artifact 打开链路接入 Workspace Canvas。
6. 新增本地 Layer Planner seed:从 prompt 生成可编辑图层计划,不调用图片模型。
7. 新增 `LayeredDesignDocument -> canvas:design Artifact` bridge,让 prompt seed 能进入当前 Artifact / Canvas 主链。
8. 新增 provider-agnostic 资产生成 seam:从图片图层创建生成请求,并把 provider 输出写回目标图层。
9. 新增 `LayeredDesignAssetGenerationRequest -> create_image_generation_task_artifact` adapter,复用现有图片任务主链。
10. 在 `DesignCanvas` 增加“生成全部图片层 / 重生成当前层”入口,提交任务后回写 `LayeredDesignDocument.editHistory`。
11. 从 `LayeredDesignDocument.editHistory` 恢复已提交图片任务,并通过现有 `get_media_task_artifact` 刷新成功结果回写目标图层。
12. 借鉴 Codex `imagegen` 的模型能力约束与透明图层 chroma-key 后处理策略,扩展为主流图片模型族 registry 并沉到 `runtimeContract.layered_design`,不新增 Python CLI 旁路。
13. 新增 `LayeredDesignDocument` 导出投影:`design.json`、`export-manifest.json`、`preview.svg`、`preview.png` 与内嵌 data URL assets 下载入口。
14. 新增 DEV-only `/design-canvas-smoke` 页面与 `smoke:design-canvas`,真实页面已验证 `canvas:design -> DesignCanvas -> 图层选择/移动/显隐` 主路径。
15. 完整 `npm run verify:gui-smoke` 已通过,证明默认 GUI 壳、DevBridge、workspace、browser runtime、runtime tool surface、knowledge GUI 与 design canvas smoke 在同一轮可跑通。
16. 新增无依赖 ZIP 工程包导出:单个 `.layered-design.zip` 包含 `design.json`、`export-manifest.json`、`preview.svg`、`preview.png` 与 `assets/` 内嵌资产。
17. 修复图片任务 artifact 对自定义 `runtime_contract.layered_design` 的透传,并让 media task worker 消费 `chroma_key_postprocess`:生成提示词追加 chroma-key 背景约束,结果图与最终 task result 写入 `postprocess` seam,前端写回资产时保留该状态。
18. 在 media task worker 内实现 data URL PNG 的 `chroma-key -> alpha` 像素级后处理:支持 `data:image/png;base64` 输出透明 PNG,远程 URL 保留原图并标记 `skipped_unsupported_source`,不让后处理失败中断图片任务。
19. 在 media task worker 内补齐 http/https 远程 URL PNG 下载后处理:provider 返回远程图片时可受控下载、抠绿、回写透明 PNG data URL,并保留 `input_source: remote_url` 元数据。
20. 新增原生项目工程目录落盘:`DesignCanvas` 绑定项目根目录时通过 Tauri current 命令写入 `.lime/layered-designs/<document>.layered-design/`,包含 `design.json / export-manifest.json / preview.svg / preview.png / assets/`;未绑定项目时仍回退浏览器 ZIP 下载。
21. 新增原生项目工程目录读回:`DesignCanvas` 绑定项目根目录时可调用 `read_layered_design_project_export` 打开最近保存的 `.layered-design` 工程,读回 `design.json` 后归一为 `LayeredDesignDocument` 并继续编辑。
22. 新增 PSD-like 专业导出投影:`psd-like-manifest.json` 记录 back-to-front 图层栈、editable text、raster image、vector shape 与 group reference,随 ZIP 和原生工程目录一起导出,但明确 `compatibility.truePsd=false`。
23. 新增扁平图拆层协议首刀:`LayeredDesignDocument.extraction` 现在可记录 `source_image`、候选层、置信度、clean plate 状态,并通过纯函数把“已选候选层”同步为正式 `layers`,低置信度候选默认不进入正式图层。
24. 新增扁平图 draft Artifact bridge:`createLayeredDesignArtifactFromExtraction` 现在可把拆层 draft 直接包装成 `canvas:design` Artifact,并沿 current Canvas 打开链路进入 `DesignCanvasState`。
25. 新增上传扁平图本地 draft adapter:`createLayeredDesignFlatImageDraftDocument` / `createLayeredDesignArtifactFromFlatImage` 现在可把单张上传图片直接归一为 extraction draft,即使还没有真实 analyzer 结果,也能通过 current 主链进入 `DesignCanvas`。
26. 新增 `DesignCanvas` 扁平图入口与候选层切换首刀:工具栏可直接上传扁平图创建 draft,属性栏可切换 `extraction.candidates`,只把选中的候选层 materialize 到正式图层栈。
27. 新增上传扁平图本地 heuristic seed 首刀:上传本地图后会先生成主体 / 标题文字 / Logo / 边角碎片裁片候选,并继续通过 `LayeredDesignDocument.extraction` 与 current `DesignCanvas` 进入编辑;未接 OCR / matting / clean plate 真执行前,不新增第二套确认页或拆层协议。
仍未做:
1. 不新增 provider adapter、旧 poster 命令或平行主链;P4F/P4G 只新增 current 工程目录保存/读取命令。
2. 不直接调用 `gpt-image-2` / Gemini / Flux;当前只规范 request contract 与现有 media task artifact 写回。
3. 不引入 Fabric 运行时。
4. 不实现真 PSD writer、PSD 文件打开验证、mask、inpaint、OCR 或拆层模型执行;当前只完成扁平图拆层协议首刀、候选层 materialize 纯函数和本地 heuristic 裁片 seed。
5. 不宣称原生工程目录落盘、PSD-like manifest 或 `LayeredDesignDocument.extraction` 已经等同于真 PSD、mask、inpaint、OCR 或完整扁平图拆层产品流;这些仍在后续 P4/P5。
6. 不宣称已完成复杂 matting、mask refine、文字/Logo 自动拆层、拆层确认页接线或 provider 级 clean plate 生成;当前只是把后续执行结果所需的 current 事实源协议、本地 heuristic 候选层与 current Canvas 接线先落稳。
## 阶段计划
### P0:文档与边界
状态:已完成 proposal 文档,进入 implementation 跟踪。
产物:
1. `docs/research/ai-layered-design/`
2. `docs/roadmap/ai-layered-design/`
3. `docs/roadmap/creaoai/` 与 AI 图层化设计边界说明
完成标准:
1. 文档说明为什么不先训练模型。
2. 文档固定 `LayeredDesignDocument` 是 current 事实源。
3. 文档说明 Lovart 类“可调整图层”来自工程编排,不是单个生图模型。
### P1:LayeredDesignDocument 最小协议
状态:已完成 P1 第一刀。
产物:
1. `src/lib/layered-design/types.ts`
2. `src/lib/layered-design/document.ts`
3. `src/lib/layered-design/index.ts`
4. `src/lib/layered-design/document.test.ts`
完成标准:
1. 创建文档时按 `zIndex` 稳定排序。
2. 普通文案默认是 `TextLayer`,不是烘焙图片。
3. 单层替换 asset 不改变 layer id、transform、zIndex、visible、locked。
4. normalize 能填充 `visible`、`locked`、`opacity`、`rotation` 等缺省值。
5. preview 只作为导出投影,不进入 `layers[]`。
### P2:Design Canvas Editor
状态:已完成最小可见 UI 与 Artifact 接入口。
计划:
1. 在现有 Workspace / CanvasWorkbench 壳层下新增 `DesignCanvas`。
2. 第一版用 DOM/CSS absolute layers 支持选择、拖动、缩放、隐藏、锁定和 zIndex。
3. 图层栏和属性栏只读写 `LayeredDesignDocument`。
4. Fabric 只作为后续更复杂选择框、旋转和导出的候选实现,不作为首刀依赖。
5. 旧 `canvas:poster / canvas:music / canvas:novel / canvas:script` 不再归一到现役画布;需要图层化图片设计时必须使用 `canvas:design`。
### P3:原生分层生成与单层重生成
状态:P3G 已完成本地 seed、Artifact bridge、provider-agnostic 资产生成 seam、现有 image task artifact API adapter、`DesignCanvas` 生成入口、任务结果刷新写回,以及 OpenAI / Gemini Imagen / Flux / Stable Diffusion / Ideogram / Recraft / Seedream / CogView / Midjourney 等主流模型族能力 request contract;P4A 收口时已补完整 GUI smoke。
计划:
1. Layer Planner 输出 5-8 个可编辑层。
2. Prompt seed 生成 `canvas:design` Artifact,直接进入 `DesignCanvas`。
3. Asset Generator 通过 provider capability seam 调用图片模型。
4. 每个 ImageLayer 绑定 asset、prompt、provider、modelId。
5. 单层重生成只替换该层 asset,并写入 edit history。
### P4:扁平图拆层与专业导出
状态:P4A 设计工程导出首刀已完成,P4B 浏览器 ZIP 工程包已完成,P4C media task worker 后处理 seam 已完成,P4D data URL PNG 像素级 chroma-key 后处理已完成,P4E http/https 远程 URL PNG 后处理已完成,P4F 原生工程目录落盘已完成,P4G 工程目录再打开/恢复已完成,P4H PSD-like 专业导出投影首刀已完成,P4I 原生工程目录远程资产持久化缓存已完成,P4J 扁平图拆层协议首刀已完成,P4K 扁平图 draft `canvas:design` artifact bridge 已完成,P4L 上传扁平图本地 draft adapter 已完成,P4M DesignCanvas 候选层切换首刀已完成,P4N 上传扁平图本地 heuristic seed 首刀已完成;真 PSD writer、复杂 matting / mask refine 与拆层执行链路仍未开始。
计划:
1. 上传扁平图后识别主体、文字、Logo、背景候选层。
2. 通过 mask / matting / clean plate 建立可编辑文档。
3. 先稳定导出 PNG + JSON + assets,再试点 PSD-like 投影。
4. P4A 当前只做浏览器下载投影,不新增 Tauri 二进制写文件命令。
5. P4B 先用前端无依赖 ZIP 打包形成可交换工程包,仍不新增 Tauri 写文件命令。
6. P4C 先把 `chroma_key_postprocess` 从 `runtimeContract` 贯穿到 media task worker 和结果元数据;真实像素处理单独作为下一刀。
7. P4D 先在 media task worker 内处理 `data:image/png;base64`,把 chroma-key 背景像素 alpha 置 0;远程 URL 与复杂抠图留给后续缓存 / matting 阶段。
8. P4E 继续在 media task worker 内处理 provider 返回的 http/https PNG URL,下载只在任务执行期发生,并受大小上限约束;持久化缓存与工程目录落盘仍单独推进。
9. P4F 把当前浏览器 ZIP 下载推进为 Tauri current 命令 `save_layered_design_project_export`:只写项目根目录下 `.lime/layered-designs/<document>.layered-design/`,继续消费 `LayeredDesignDocument` 导出投影,不新增 provider adapter 或旧 poster 协议。
10. P4G 在同一条 current 工程目录链路补 `read_layered_design_project_export`:只读 `.lime/layered-designs/<document>.layered-design/design.json`,恢复 `LayeredDesignDocument` 到 `DesignCanvas`,不读取或定义新的设计事实源。
11. P4H 先定义 `psd-like-manifest.json` 专业层栈投影并随 ZIP / 原生工程目录导出;它只做 `LayeredDesignDocument` 的可交换投影,不写真 `.psd`,不做 OCR / matting / mask。
12. P4I 继续收口 current 工程目录保存/读取链路:保存时把 manifest 中的远程图片引用持久化到 `assets/`,读回时优先从缓存文件水合回 `design.json`,但不把 ZIP 浏览器导出扩展成第二套下载协议。
13. P4J 先不接模型,只把扁平图拆层需要的 current 协议落到 `LayeredDesignDocument`:记录 `source_image`、候选层、置信度、clean plate 状态,并用纯函数保证“只有已选候选层才 materialize 为正式 layers”,为后续拆层确认页和本地/远程 analyzer adapter 铺路。
14. P4K 在不新增命令和 UI 主入口的前提下,把扁平图拆层 draft 接回 current `canvas:design` Artifact 链:新增 `createLayeredDesignArtifactFromExtraction`,保证拆层 draft 可以像 prompt seed 一样进入 `DesignCanvasState`,继续复用现有 Canvas 主路径。
15. P4L 继续收口“上传扁平图”的本地入口:新增 `createLayeredDesignFlatImageDraftDocument` 和 `createLayeredDesignArtifactFromFlatImage`,让单张图片在没有 analyzer / OCR / mask 时也能先生成 extraction draft,后续只需替换 candidates/cleanPlate seed,不需要重开第二条 Canvas 接线。
16. P4M 先不做独立拆层确认页,直接在 current `DesignCanvas` 落一刀最小确认态:上传扁平图后可在属性栏切换候选层,保持 `extraction.candidates` 与正式 `layers` 的边界一致,为后续专门确认页先验证状态机。
17. P4N 继续在同一条 current 上传链上补本地 heuristic seed:先用浏览器本地裁片生成主体 / 标题文字 / Logo / 边角碎片候选,继续写回 `LayeredDesignDocument.extraction.candidates`,不伪装成 OCR / matting / clean plate,不新增独立确认页或新的 Artifact 类型。
## 已完成的不变量
当前已证明:
1. `LayeredDesignDocument` 类型是唯一 current 设计事实源。
2. `GeneratedDesignAsset` 只是资产记录,只有被 layer 引用才进入图层栏语义。
3. `DesignPreviewProjection` 只是当前导出预览的投影,编辑会把它标记为 stale。
4. 所有编辑函数保持不可变更新,避免 Canvas 状态绕过文档。
5. Prompt seed 里的普通文案保持 `TextLayer`,不被烘焙成图片。
6. Prompt seed 里的图片资产只是 `plannedOnly` 占位,不隐式调用 provider。
7. `canvas:design` 是图层化设计唯一 current Artifact 类型;旧 `canvas:poster` 不再参与归一。
8. 资产生成 seam 只选择图片 / effect 图层,跳过 `TextLayer`,并允许单层重生成重新进入 provider seam。
9. 图层生成任务复用现有 `create_image_generation_task_artifact`,通过 `slotId / targetOutputId / targetOutputRefId / anchorHint` 保留 document/layer/asset 关联,不新增旧 poster 协议。
10. Canvas UI 提交任务后必须回写 `LayeredDesignDocument.editHistory`;如果任务输出已经包含图片结果,立即写回目标图片层 asset,文字层保持可编辑。
11. `asset_generation_requested` 必须记录 `taskId / taskPath / taskStatus`,后续打开同一设计工程时可恢复等待写回的图片任务。
12. 主流图片模型族必须通过统一 capability registry 判断尺寸策略、透明策略、编辑/mask/reference 能力;未知模型走 `generic + provider_passthrough`,不阻塞任务创建。
13. `gpt-image-2 / gpt-images-2` 图层任务必须归一到 16 倍数尺寸与合法像素范围;透明图层只记录 `chroma_key_postprocess` 策略,不把 Python CLI 变成 Lime current 主链。
14. 导出结果是 `LayeredDesignDocument` 的投影:`design.json` 会标记 `status: exported`,`preview.svg / preview.png` 只作为当前画布快照,不反向替代图层事实源。
15. 内嵌 data URL assets 可随导出下载;远程 assets 在 manifest 中保留 `originalSrc` 引用,不伪装成本地已落盘文件。
16. ZIP 工程包只是导出容器:`assets/` 只收纳内嵌 data URL 资产,远程资产继续只在 manifest 中保留引用,避免把不可控远程资源伪装成本地工程文件。
17. 图片任务 artifact 必须保留调用方传入的 `runtime_contract.layered_design` 扩展字段,同时继续保留标准 `image_generation` executor / policy / routing 合同;不能用默认 runtime contract 覆盖设计图层扩展。
18. `chroma_key_postprocess` 的 worker 合同必须先保持可追踪:生成请求提示词明确 chroma-key 背景,`result.postprocess` 和 `images[].postprocess` 持续写入同一套后处理元数据,前端写回 `GeneratedDesignAsset.params.postprocess` 不得丢失状态。
19. `chroma_key_postprocess` 的首个真实像素处理器必须至少消费 `data:image/png;base64`:成功时替换 `images[].url` 为透明 PNG data URL,并写入 `status: succeeded / removed_pixel_count / total_pixel_count / transparent`。
20. http/https 远程 URL 后处理必须受控:只允许下载任务结果 URL,限制最大图片体积,成功后仍回写透明 PNG data URL;失败只写 `postprocess.status: failed/skipped_unsupported_source`,不得让图片任务整体失败,也不得伪装为已透明化。
21. 原生工程目录落盘必须只保存导出投影:Tauri 侧负责路径约束、目录创建、UTF-8 / base64 文件写入和目录穿越防护;`preview.png` 与 `assets/` 仍是投影文件,不能反向替代 `LayeredDesignDocument`。
22. 工程目录读回必须只恢复 `design.json` 中的 `LayeredDesignDocument`:Tauri 侧负责约束目录必须位于 `.lime/layered-designs/`,前端负责 `normalizeLayeredDesignDocument` 后回写 `DesignCanvas`,manifest / preview / assets 只作为旁路投影元数据。
23. PSD-like manifest 必须是导出投影而非新事实源:`source.factSource` 必须指向 `LayeredDesignDocument`,`compatibility.truePsd=false`,图层顺序固定为 `back_to_front`,不得引入 `poster_generate / canvas:poster / ImageTaskViewer`。
24. 原生工程目录保存命令可以在不新增协议面的前提下,把 `export-manifest.json` 中 `http/https` 远程图片引用持久化缓存到 `assets/`;读回时优先从缓存文件水合 `design.json` 返回给前端,避免重新打开工程时仍依赖远程 URL 在线可达。
25. 扁平图拆层候选必须作为 `LayeredDesignDocument.extraction.candidates` 单独记录;候选层在用户确认前不能静默混入正式 `layers`。
26. 低置信度拆层候选默认不选中;即使候选附带 mask / RGBA 资产,也只能在 `selected=true` 后才 materialize 到 `DesignCanvas` 图层栈。
27. clean plate 失败不能阻断进入可编辑工程;背景层必须可回退到 `source_image`,同时在 extraction 元数据里保留失败状态和说明。
28. 扁平图拆层 draft 一旦进入 current Artifact 主链,仍必须继续使用 `canvas:design`;不为拆层草稿新增 `canvas:image`、`canvas:poster` 或平行 viewer 协议。
29. 上传扁平图的本地 draft adapter 只能做归一化和最小默认值推导;它不能伪装成真实 analyzer、OCR、matting 或 clean plate 结果,也不能偷偷扩成新的事实源 schema。
30. `DesignCanvas` 内的候选层切换只能修改 `extraction.candidates.selected` 并同步 materialize 结果;未选候选不能因为画布交互而静默出现在正式 `layers`。
31. 上传扁平图的本地 heuristic seed 只能产出基于原图的裁片候选;它可以帮助 current 画布先验证候选层状态机,但不能伪装成真实 mask、透明抠图、OCR 文字层或 clean plate 成果。
## 验证策略
当前改动横跨 TypeScript 协议、Artifact adapter、Workspace Canvas UI、Tauri 命令、DevBridge、mock 与治理 catalog;每一刀按实际触达边界选择最小可证明交付的校验集合。
最低校验:
```bash
npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/generation.test.ts" "src/lib/layered-design/imageModelCapabilities.test.ts" "src/lib/layered-design/imageTasks.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/artifact/canvasAdapterUtils.test.ts" "src/components/artifact/ArtifactRenderer.ui.test.tsx"
npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/artifact/canvasAdapterUtils.ts" "src/components/artifact/canvasAdapterUtils.test.ts" --max-warnings 0
npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit
npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000
```
GUI 主路径当前已补齐的 smoke 门槛:
```bash
npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000
npm run verify:gui-smoke -- --reuse-running --timeout-ms 600000 --interval-ms 1000
```
后续若继续改 Workspace / Design Canvas / DevBridge 主路径,应继续把 `npm run verify:gui-smoke` 纳入收口门槛;若只是纯函数或局部 UI 小改,可先跑定向 Vitest、ESLint 和 TypeScript 后再按风险升级。
后续进入 Tauri 命令 / provider / mock 时再追加:
```bash
npm run test:contracts
npm run governance:legacy-report
```
## 进度日志
### 2026-05-05
- 已创建本执行计划,承接 `docs/roadmap/ai-layered-design/`。
- 当前阶段固定为 P1 第一刀:先落 `LayeredDesignDocument` 协议和纯函数测试。
- 本轮不接 GUI、provider、Tauri 命令或 Fabric,避免在事实源未稳定前扩展平行实现。
- 已新增 `src/lib/layered-design/types.ts`、`src/lib/layered-design/document.ts`、`src/lib/layered-design/index.ts` 与 `src/lib/layered-design/document.test.ts`。
- 已实现 `LayeredDesignDocument`、`DesignCanvas`、`DesignLayer`、`GeneratedDesignAsset`、`LayerEditRecord`、创建 / normalize / 排序 / 单层资产替换 / transform 更新等 P1 最小协议。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/document.test.ts"`,5 个定向测试覆盖 zIndex 排序、TextLayer、单层替换不变量、默认值归一化和 preview 投影语义。
- 已通过 `npm exec -- eslint "src/lib/layered-design/**/*.ts" --max-warnings 0` 与定向 `tsc`,确认本轮新增协议文件静态检查通过。
- 已执行 `npm run typecheck`,当前失败来自未跟随本轮修改的未跟踪文件 `src/components/agent/chat/hooks/agentStreamRuntimeContextController.test.ts`:测试构造的 `output_schema_runtime` 缺少 `source` 与 `strategy` 字段;本轮未修改该区域,未纳入本阶段修复范围。
### 2026-05-05 P2 最小 Canvas UI
- 已新增 `src/components/workspace/design/DesignCanvas.tsx` 与 `src/components/workspace/design/types.ts`,提供图层栏、画布预览、属性栏、选择、移动、显隐、锁定、zIndex 调整和 zoom 控制。
- 已把 `CanvasStateUnion` 扩展为 `document / video / design`,并在 `CanvasFactory` 与 `workbenchCanvas` current 网关接入 `DesignCanvas`。
- 已新增 `canvas:design` Artifact 类型和 `.json` 默认扩展名;Artifact adapter 可从 `LayeredDesignDocument` JSON 创建 design canvas state,并把 design canvas state 序列化回同一文档 JSON。
- 已清理旧 Canvas 类型别名:`canvas:poster / canvas:music / canvas:novel / canvas:script` 不再归一到 `canvas:document / canvas:video`,避免旧专用主题继续伪装成新设计工程主线。
- 已同步 `src/components/artifact/README.md`,明确旧 Canvas 别名不再是 compat 主链;图层化图片设计必须走 `canvas:design + LayeredDesignDocument`。
- 已补 `src/components/workspace/design/DesignCanvas.test.tsx`、`src/components/artifact/canvasAdapterUtils.test.ts` 与 `canvasUtils` 回归,锁定 UI 操作必须回写文档而不是只改 DOM。
- 已修正 `ArtifactRenderer` 的 Canvas 分发顺序:Canvas 类型先委托给 `CanvasAdapter`,不再要求先注册轻量 renderer;`canvas:design` 因此能从 Artifact 直接打开 `DesignCanvas`。
- 已补 `src/components/artifact/ArtifactRenderer.ui.test.tsx` 回归,覆盖 `canvas:design` 从 Artifact 直接渲染到图层设计画布。
- 已同步 `ArtifactToolbar` MIME:`canvas:design` 导出内容按 `application/json` 处理。
- 已补 `src/components/agent/chat/workspace/generalWorkbenchHelpers.test.ts`,覆盖 design canvas 的空态判断和 `LayeredDesignDocument` JSON 同步。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/artifact/canvasAdapterUtils.test.ts" "src/components/workspace/canvas/canvasUtils.test.ts"`。
- 已通过 `npm exec -- vitest run "src/lib/artifact/parser.test.ts" "src/lib/artifact/registry.test.ts" "src/components/artifact/ArtifactRenderer.test.ts" "src/components/artifact/ArtifactToolbar.test.ts"`。
- 已通过 `npm exec -- vitest run "src/components/artifact/ArtifactRenderer.ui.test.tsx" "src/components/artifact/canvasAdapterUtils.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx"`。
- 已通过 `npm exec -- vitest run "src/components/artifact/ArtifactToolbar.test.ts" "src/components/agent/chat/workspace/generalWorkbenchHelpers.test.ts" "src/components/artifact/ArtifactRenderer.ui.test.tsx"`。
- 已通过 `npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/workspace/design/**/*.{ts,tsx}" "src/components/workspace/canvas/canvasUtils.ts" "src/components/workspace/canvas/canvasUtils.test.ts" "src/components/workspace/canvas/CanvasFactory.tsx" "src/components/artifact/canvasAdapterUtils.ts" "src/components/artifact/canvasAdapterUtils.test.ts" "src/lib/artifact/types.ts" "src/lib/artifact/parser.ts" "src/components/artifact/ArtifactRenderer.test.ts" "src/components/artifact/ArtifactToolbar.test.ts" "src/components/agent/chat/workspace/generalWorkbenchHelpers.ts" --max-warnings 0`。
- 已通过增量 ESLint:`npm exec -- eslint "src/components/artifact/ArtifactToolbar.tsx" "src/components/artifact/ArtifactToolbar.test.ts" "src/components/agent/chat/workspace/generalWorkbenchHelpers.ts" "src/components/agent/chat/workspace/generalWorkbenchHelpers.test.ts" "src/components/artifact/ArtifactRenderer.tsx" "src/components/artifact/ArtifactRenderer.ui.test.tsx" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`,范围包含 `src/vite-env.d.ts`、layered-design、DesignCanvas、CanvasFactory、CanvasAdapter、ArtifactRenderer、artifact 类型/解析和 Workbench 同步 helper。
- 已通过 `git diff --check` 相关文件检查。
- 已尝试 `npm run typecheck`,120 秒内未完成并被中止;本轮定向测试和 ESLint 已覆盖新增边界,完整 typecheck 需要等当前工作区并发校验任务收口后补跑。
### 2026-05-05 P3A 本地 Layer Planner seed 与 Artifact bridge
- 已新增 `src/lib/layered-design/planner.test.ts`,锁定 prompt seed 会生成背景、主体、氛围特效、主标题、副标题、CTA 底和 CTA 文案等 7 个可编辑层。
- 已确认 prompt seed 中普通文案保持 `TextLayer`,图片资产仅为 `plannedOnly` 占位,`src` 为空且不写 `provider / modelId`,不会假装已经调用 `gpt-image-2`、Gemini 或其他模型。
- 已新增 `src/lib/layered-design/artifact.ts`,提供 `createLayeredDesignArtifact` 与 `createLayeredDesignArtifactFromPrompt`,统一生成 `canvas:design` Artifact。
- 已通过 `createLayeredDesignArtifactFromPrompt -> createCanvasStateFromArtifact -> DesignCanvasState` 回归,证明 prompt seed 能进入当前 Artifact / Canvas 主链。
- 已从 `src/lib/layered-design/index.ts` 导出 planner 与 artifact bridge,后续主链入口不需要绕到旧 poster / image viewer。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/components/artifact/canvasAdapterUtils.test.ts"`,共 13 个定向测试。
- 已通过 `npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/artifact/canvasAdapterUtils.ts" "src/components/artifact/canvasAdapterUtils.test.ts" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 当前仍未跑 `npm run verify:gui-smoke`,因此 GUI 主路径还不能宣称完整可交付。
### 2026-05-05 P3B provider-agnostic 资产生成 seam
- 已新增 `src/lib/layered-design/generation.ts`,提供 `createLayeredDesignAssetGenerationPlan`、`createSingleLayerAssetGenerationRequest` 与 `applyLayeredDesignGeneratedAsset`。
- 资产生成计划只选择 `ImageLayer / EffectLayer`,跳过 `TextLayer`,确保普通文案继续留在可编辑图层而不是被送进生图模型。
- 默认生成计划只请求空 `src` 或 `plannedOnly` 资产;单层重生成请求允许已生成资产再次进入 provider seam。
- 写入 provider 输出时只替换目标图片层的 asset,并把该层标记为 `source: "generated"`;其他图层和文字层保持不变。
- 该 seam 不调用 `gpt-image-2`、Gemini 或本地模型,只定义 current 文档如何对接后续 provider adapter。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/generation.test.ts" "src/components/artifact/canvasAdapterUtils.test.ts"`,共 17 个定向测试。
- 已通过 `npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/artifact/canvasAdapterUtils.ts" "src/components/artifact/canvasAdapterUtils.test.ts" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 下一刀应接真实 provider adapter 或 UI 单层重生成入口,不能回到旧 `poster_generate / ImageTaskViewer`。
### 2026-05-05 P3C image task adapter
- 已新增 `src/lib/layered-design/imageTasks.ts`,把 `LayeredDesignAssetGenerationRequest` 映射到现有 `createImageGenerationTaskArtifact` 前端 API。
- 映射后的图片任务使用 `entrySource: "layered_design_canvas"`、`modalityContractKey: "image_generation"`、`routingSlot: "image_generation_model"`,不新增 Tauri 命令、不新增 mock、不回到旧 poster 协议。
- 图层关联通过现有字段持久化:`slotId=layerId`、`targetOutputId=assetId`、`targetOutputRefId=generationRequest.id`、`anchorHint=layered-design:<documentId>:<layerId>`。
- 已新增 `createGeneratedDesignAssetFromImageTaskOutput`,能从成功的 image task result 创建 `GeneratedDesignAsset`,并保留 `provider / model / taskId / taskPath / layerId / documentId`。
- 已新增 `applyLayeredDesignImageTaskOutput`,可把成功任务输出写回目标图层;文字层仍保持 `TextLayer` 可编辑。
- 已补 `src/lib/layered-design/imageTasks.test.ts`,覆盖请求映射、批量提交、任务输出转 asset、任务输出写回文档,并断言不出现 `poster_generate / canvas:poster`。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/generation.test.ts" "src/lib/layered-design/imageTasks.test.ts" "src/components/artifact/canvasAdapterUtils.test.ts"`,共 21 个定向测试。
- 已通过 `npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/artifact/canvasAdapterUtils.ts" "src/components/artifact/canvasAdapterUtils.test.ts" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 当前仍未跑 `npm run test:contracts` 与 `npm run verify:gui-smoke`;本轮没有新增命令面,但下一轮接 UI 或真实轮询后必须补 GUI 主路径验证。
### 2026-05-05 P3D DesignCanvas 图层生成入口
- 已在 `src/components/workspace/design/DesignCanvas.tsx` 增加“生成全部图片层”和“重生成当前层”入口,页面类型属于宽工作台,按钮沿用深色主按钮 + 白底描边次按钮层级。
- `DesignCanvas` 生成入口只调用 `createLayeredDesignImageTaskArtifacts` current adapter;没有接 `poster_generate`、`canvas:poster` 或 `ImageTaskViewer`。
- 提交任务后通过 `recordLayeredDesignImageTaskSubmissions` 回写 `LayeredDesignDocument.editHistory`,保证 UI 操作不只停在 DOM 状态。
- 如果任务输出已经包含图片结果,`DesignCanvas` 会立刻用 `applyLayeredDesignImageTaskOutput` 写回目标图片层 asset;文字层继续保持 `TextLayer`。
- `CanvasFactory` 已向 design canvas 透传 `projectRootPath / projectId / contentId`,`useWorkspaceCanvasSceneRuntime` 使用当前 workspace root 作为图片任务根目录。
- 已补 `src/components/workspace/design/DesignCanvas.test.tsx` 回归,覆盖全部生成、单层重生成、任务请求字段、edit history 回写、任务结果写回图层和旧 poster 文本不回流。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/generation.test.ts" "src/lib/layered-design/imageTasks.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/artifact/canvasAdapterUtils.test.ts" "src/components/artifact/ArtifactRenderer.ui.test.tsx"`,共 34 个定向测试。
- 已通过 `npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/workspace/design/**/*.{ts,tsx}" "src/components/workspace/canvas/CanvasFactory.tsx" "src/components/agent/chat/workspace/useWorkspaceCanvasSceneRuntime.tsx" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 当前仍未跑 `npm run verify:gui-smoke`;下一刀应补任务轮询 / 结果自动写回后跑 GUI 主路径验证。
### 2026-05-05 P3E 图层任务恢复与刷新写回
- 已扩展 `LayerEditRecord`,让 `asset_generation_requested` 记录 `taskId / taskPath / taskStatus`,避免任务提交后只能依赖当前 React 内存状态。
- 已新增 `listPendingLayeredDesignImageTasks`,从 `LayeredDesignDocument.editHistory` 恢复仍等待写回的图片任务;如果同一图层已有后续 `asset_replaced`,旧任务会被视为已关闭。
- 已新增 `refreshLayeredDesignImageTaskResults`,复用现有 `getMediaTaskArtifact` / `get_media_task_artifact` 刷新图片任务结果,成功时只替换目标图片层 asset。
- 已在 `DesignCanvas` 增加“刷新生成结果”入口;页面仍属于宽工作台,主生成按钮与刷新按钮保持深色主按钮 + 白底描边次按钮层级,不引入新的视觉体系。
- 已补 `src/lib/layered-design/imageTasks.test.ts` 与 `src/components/workspace/design/DesignCanvas.test.tsx` 回归,覆盖 pending 任务恢复、刷新调用、成功结果写回和文字层保持可编辑。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/imageTasks.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx"`,共 13 个定向测试。
- 当前仍未跑 `npm run verify:gui-smoke`,GUI 主路径还不能宣称完整可交付。
### 2026-05-05 P3F 借鉴 Codex imagegen 的模型能力约束
- 已只读分析 `/Users/coso/Documents/dev/rust/codex/codex-rs/skills/src/assets/samples/imagegen/`,确认适合借鉴的是模型能力约束、透明图层后处理策略和多资产生成纪律,不适合直接引入 Python CLI 旁路。
- 已新增 `src/lib/layered-design/imageModelCapabilities.ts`,把 `gpt-image-2 / gpt-images-2` 的 16 倍数、最大边、像素范围、长短边比例和透明背景限制沉到纯函数。
- `createLayeredDesignImageTaskRequest` 现在会在指定 `gpt-image-2 / gpt-images-2` 时归一任务尺寸,并把原始尺寸、任务尺寸、alpha 策略写入 `runtimeContract.layered_design`。
- 透明图层当前只记录 `chroma_key_postprocess` 策略和默认 key color,不直接把 Codex `scripts/image_gen.py` 或 chroma-key prompt 接成产品主链;后续应在 media task worker 内做本地 post-process。
- 已补 `src/lib/layered-design/imageModelCapabilities.test.ts` 与 `src/lib/layered-design/imageTasks.test.ts` 回归,证明 request contract 仍是 `image_generation`,没有新增旧 `poster_generate / canvas:poster` 协议。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/imageModelCapabilities.test.ts" "src/lib/layered-design/imageTasks.test.ts"`,共 11 个定向测试。
### 2026-05-05 P3G 主流图片模型族 capability registry
- 已把上一刀的 `gpt-image-2` 单点判断扩展为主流模型族 registry:`openai-gpt-image-2`、`openai-gpt-image`、`openai-dalle`、`google-imagen`、`flux`、`stable-diffusion`、`ideogram`、`recraft`、`seedream`、`cogview`、`midjourney` 与 `generic`。
- `LayeredDesignImageModelCapability` 现在记录 `sizePolicy`、`allowedSizes`、`supportsNativeTransparency`、`supportsImageEdit`、`supportsMask`、`supportsReferenceImages` 等能力;未知模型走 `generic + provider_passthrough`,不阻塞现有 provider routing。
- 尺寸策略已收敛为四类:`flexible_pixels`、`allowed_sizes`、`multiple_pixels`、`provider_passthrough`;OpenAI legacy / DALL-E 会选最接近允许尺寸,Stable Diffusion 会按 64 倍数归一,Flux 会按最大像素做保守缩放。
- `createLayeredDesignImageTaskRequest` 继续只复用现有 `create_image_generation_task_artifact`,但会把模型族、provider、size policy、透明策略和编辑/mask/reference 能力写入 `runtimeContract.layered_design`。
- 已补 `src/lib/layered-design/imageModelCapabilities.test.ts`,覆盖 `gpt-image-1.5`、`flux-pro`、`stable-diffusion-xl`、`seedream-4.0` 等非 gpt-image-2 模型族,避免当前主线绑定单一模型。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/imageModelCapabilities.test.ts" "src/lib/layered-design/imageTasks.test.ts"`,共 12 个定向测试。
- 已修正 `LayeredDesignImageRuntimeContract` 类型,让它可直接写入现有图片任务 `runtimeContract: Record<string, unknown>`,不新增任务协议或桥接命令。
- 已通过 P3G 汇总回归:`npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/generation.test.ts" "src/lib/layered-design/imageModelCapabilities.test.ts" "src/lib/layered-design/imageTasks.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/artifact/canvasAdapterUtils.test.ts" "src/components/artifact/ArtifactRenderer.ui.test.tsx"`,共 43 个定向测试。
- 已通过 P3G 定向 ESLint:`npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/workspace/design/**/*.{ts,tsx}" "src/components/workspace/canvas/CanvasFactory.tsx" "src/components/agent/chat/workspace/useWorkspaceCanvasSceneRuntime.tsx" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已再次尝试 `npm run verify:gui-smoke -- --timeout-ms 600000 --interval-ms 1000`;本轮未走到新增 `smoke:design-canvas`,而是在既有 `smoke:agent-runtime-tool-surface-page` 超时。随后单独复跑该旧 smoke,失败点为 `launch_browser_session` 多次 DevBridge 响应超时;该失败归类为既有 browser runtime / 本地端口状态问题,不来自 `canvas:design`。
### 2026-05-05 P3H / P4A Design Canvas 专属 smoke 与导出首刀
- 已新增 `src/pages/design-canvas-smoke.tsx`,DEV-only 挂载 `/design-canvas-smoke`,从 `createLayeredDesignArtifactFromPrompt -> createCanvasStateFromArtifact -> CanvasFactory` 进入真实 `canvas:design` 页面。
- 已新增 `scripts/design-canvas-smoke.mjs` 与 `package.json` 脚本 `smoke:design-canvas`,验证 `canvas:design`、`LayeredDesignDocument`、图层栏、属性栏、生成/刷新/单层重生成/导出入口,以及图层选择、右移、隐藏、显示交互。
- 已修正 `scripts/design-canvas-smoke.mjs`:优先使用系统 Chrome channel,缺失时回退 Playwright Chromium;图层与属性按钮定位改为精确 accessible name,避免与图层列表“显示/隐藏”元信息冲突。
- 已新增 `src/lib/layered-design/export.ts`,把 `LayeredDesignDocument` 投影为 `design.json`、`export-manifest.json`、`preview.svg`、`preview.png` 和可下载内嵌 data URL assets,不新增 Tauri 命令。
- `export-manifest.json` 会区分 `file / reference / missing`:内嵌 data URL assets 可下载成文件,远程 assets 保留 `originalSrc` 引用,避免伪装成本地 assets 已落盘。
- `DesignCanvas` 顶部工具栏已把旧占位“PNG 导出待接入”替换为“导出设计工程”,点击后下载设计 JSON、manifest、SVG、PNG 和内嵌 assets;PNG 由当前 SVG 投影转换而来,仍不是事实源。
- 已补 `src/lib/layered-design/export.test.ts` 与 `src/components/workspace/design/DesignCanvas.test.tsx` 回归,覆盖导出包结构、SVG 可见图层投影、文本转义、远程 assets 引用、UI 导出入口。
- 已通过汇总回归:`npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/planner.test.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/generation.test.ts" "src/lib/layered-design/imageModelCapabilities.test.ts" "src/lib/layered-design/imageTasks.test.ts" "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/artifact/canvasAdapterUtils.test.ts" "src/components/artifact/ArtifactRenderer.ui.test.tsx"`,共 47 个测试。
- 已通过定向 ESLint:`npm exec -- eslint "src/lib/layered-design/**/*.ts" "src/components/workspace/design/**/*.{ts,tsx}" "scripts/design-canvas-smoke.mjs" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。该临时 tsconfig 为规避既有 `src/lib/sceneapp/product.ts` 对 ES2022 `Array.prototype.at` 的依赖,显式使用 `lib: ["ES2022", "DOM", "DOM.Iterable"]`。
- 已通过专属 GUI smoke:`npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000`,真实页面验证通过,项目为默认 workspace `849e36ff-8f64-45ed-ba51-aab6b8e182e4`。
- 截至 2026-05-05 该刀收口前,完整 `verify:gui-smoke` 尚未通过;后续 2026-05-06 记录已完成仓库级 smoke 收口。
### 2026-05-06 P4A GUI smoke 收口
- 已修正 `scripts/agent-runtime-tool-surface-page-smoke.mjs` 的托管 Chrome 会话恢复逻辑:当 `browser_execute_action` 遇到 `CDP 调试端口不可用`、`没有可用的 Chrome 会话` 或 `未找到 profile_key=` 时,限次重启同一 smoke profile 后继续当前检查,避免本地 Chrome profile 抖动误报为产品失败。
- 已通过 `npm exec -- eslint "scripts/agent-runtime-tool-surface-page-smoke.mjs" --max-warnings 0`。
- 已通过单独旧 smoke 复测:`npm run smoke:agent-runtime-tool-surface-page -- --timeout-ms 180000 --interval-ms 1000`。
- 已通过完整 GUI smoke:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 600000 --interval-ms 1000`。
- 本轮完整 GUI smoke 覆盖 `workspace-ready`、`browser-runtime`、`site-adapters`、`agent-service-skill-entry`、`agent-runtime-tool-surface`、`agent-runtime-tool-surface-page`、`knowledge-gui` 与新增 `design-canvas`;其中 `smoke:design-canvas` 真实验证 `canvas:design`、`LayeredDesignDocument`、图层栏、属性栏、生成/刷新/单层重生成/导出入口,以及图层选择、右移、隐藏、显示。
- 当前结论:`canvas:design` 已达到 Lime GUI 最小可交付门槛;尚未完成的是原生工程目录落盘、media task worker 后处理 seam、PSD-like 投影与扁平图拆层。
### 2026-05-06 P4B ZIP 工程包导出
- 已新增 `src/lib/layered-design/zip.ts`,实现无依赖 stored ZIP writer;该工具只负责 ZIP 容器,不懂 `LayeredDesignDocument` 语义,避免把打包细节塞进设计协议。
- 已扩展 `src/lib/layered-design/export.ts`:`createLayeredDesignExportZipFile` 会把 `design.json`、`export-manifest.json`、`preview.svg`、调用方生成的 `preview.png` 和内嵌 data URL assets 打进单个 `.layered-design.zip`。
- `DesignCanvas` 的“导出设计工程”入口已从散落下载多个文件改为下载单个 ZIP;包内 `assets/` 只包含内嵌 data URL assets,远程 assets 仍只在 manifest 中保留 `originalSrc` 引用。
- 已补 `src/lib/layered-design/export.test.ts`,读取 ZIP local headers 校验包内路径为 `design.json / export-manifest.json / preview.svg / preview.png / assets/...`,并断言不回流 `poster_generate / canvas:poster`。
- 已补 `src/components/workspace/design/DesignCanvas.test.tsx`,验证导出入口只触发一次 ZIP 下载,而不是多个散文件下载。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx"`,共 12 个测试。
- 已通过 `npm exec -- eslint "src/lib/layered-design/export.ts" "src/lib/layered-design/zip.ts" "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" --max-warnings 0`。
- 已通过 `npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已尝试 `npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000`,但本地 DevBridge 未监听 `3030`,停在 `stage=wait-health` 后失败;随后 `npm run bridge:health -- --timeout-ms 10000` 也确认 `fetch failed`。本轮曾尝试启动 `npm run tauri:dev:headless`,但被已有 Cargo artifact lock 阻塞,已终止本轮启动进程,未处理其他已有 Rust / dev 进程。
- 当前结论:浏览器侧已经具备单文件设计工程包代码路径与组件级回归;GUI smoke 需要等本地 DevBridge 恢复后补跑。下一刀应接 media task worker 的 `chroma_key_postprocess` seam,而不是继续扩展导出 UI。
### 2026-05-06 P4C media task chroma-key 后处理 seam
- 已修复 `src-tauri/src/commands/media_task_cmd.rs`:`create_image_generation_task_artifact_inner` 现在会把请求中的 `runtime_contract` 合并进标准 `image_generation_runtime_contract()`,保留 `layered_design` 扩展,同时不允许覆盖标准 `contract_key / executor_binding / policy / routing` 主合同。
- 已扩展 `src-tauri/crates/media-runtime/src/lib.rs`:图片 worker 会从 `payload.runtime_contract.layered_design.alpha` 读取 `chroma_key_postprocess`,给每个请求 slot prompt 追加 chroma-key 背景约束,并在 `result.postprocess` 与 `images[].postprocess` 写入 `pending_chroma_key_processor` seam。
- 已扩展 `src/lib/layered-design/imageTasks.ts`:`GeneratedDesignAsset.params.postprocess` 会保留 worker 写回的后处理状态,后续像素级处理器可以按 `taskId / documentId / layerId / originalAssetId` 找回上下文。
- 已补 `src-tauri/src/commands/media_task_cmd.rs` 回归,证明图片任务 artifact 同时保留标准 executor binding 和 `layered_design.alpha.strategy = chroma_key_postprocess`。
- 已补 `src-tauri/crates/media-runtime/src/lib.rs` 回归,证明 worker 能消费 layered-design alpha contract、追加 chroma-key prompt hint,并写出 `pending_chroma_key_processor` 结果 seam。
- 已补 `src/lib/layered-design/imageTasks.test.ts` 回归,证明前端写回资产时不会丢失 worker postprocess metadata。
- 已通过 `npm exec -- vitest run "src/lib/layered-design/imageTasks.test.ts" "src/lib/layered-design/imageModelCapabilities.test.ts"`,共 13 个测试。
- 已通过 `npm exec -- eslint "src/lib/layered-design/imageTasks.ts" "src/lib/layered-design/imageTasks.test.ts" --max-warnings 0`。
- 已通过 `npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已通过 `CARGO_TARGET_DIR="/tmp/lime-p4c-media-runtime-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-media-runtime prepare_image_task_input_should_consume_layered_design_chroma_key_postprocess_contract`。
- 已通过 `CARGO_TARGET_DIR="/tmp/lime-p4c-app-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime --lib create_image_generation_task_artifact_inner_should_preserve_layered_design_runtime_contract --no-default-features`。
- 已通过命令契约门禁:`npm run test:contracts`。
- 当前结论:`chroma_key_postprocess` 已从 LayeredDesignDocument 图层任务 contract 贯穿到标准 image task artifact、worker 请求与结果回写;下一刀才做真实像素级 key color -> alpha 处理或原生工程目录落盘。
### 2026-05-06 P4D data URL PNG chroma-key 像素级后处理
- 已为 `lime-media-runtime` 增加最小依赖 `base64` 与 `image`,只用于 worker 内 PNG data URL 解码、像素遍历和透明 PNG 编码;没有新增 provider adapter、Tauri 命令或 Python/CLI 旁路。
- 已扩展 `src-tauri/crates/media-runtime/src/lib.rs`:当图片服务返回 `data:image/png;base64` 且任务带 `runtime_contract.layered_design.alpha.strategy = chroma_key_postprocess` 时,worker 会按 `chroma_key_color` 计算颜色距离,把命中像素 alpha 置 0,并用新的透明 PNG data URL 替换 `images[].url`。
- `images[].postprocess` 现在会写入 `status: succeeded`、`removed_pixel_count`、`total_pixel_count`、`output_mime: image/png`、`transparent: true`;最终 `result.postprocess` 会聚合 `processed / succeeded / skipped / failed` 计数。
- 远程 URL 或非 PNG data URL 不会让图片任务失败;worker 保留原始 `url`,并写入 `status: skipped_unsupported_source` 与原因,避免把不可处理资产伪装成透明图层。
- 已更新 `src/lib/layered-design/imageTasks.test.ts`,验证前端资产写回会保留 worker 的 `succeeded` 后处理元数据与像素统计。
- 已通过前端定向回归:`npm exec -- vitest run "src/lib/layered-design/imageTasks.test.ts" "src/lib/layered-design/imageModelCapabilities.test.ts"`,共 13 个测试。
- 已通过前端定向 ESLint:`npm exec -- eslint "src/lib/layered-design/imageTasks.ts" "src/lib/layered-design/imageTasks.test.ts" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已通过 Rust 定向回归:`CARGO_TARGET_DIR="/tmp/lime-p4d-media-runtime-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-media-runtime chroma_key`。
- 已通过 Rust crate 回归:`CARGO_TARGET_DIR="/tmp/lime-p4d-media-runtime-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-media-runtime`,共 17 个测试。
- 已通过 diff 卫生检查:`git diff --check -- "src-tauri/Cargo.lock" "src-tauri/crates/media-runtime/Cargo.toml" "src-tauri/crates/media-runtime/src/lib.rs" "src/lib/layered-design/imageTasks.ts" "src/lib/layered-design/imageTasks.test.ts" "docs/exec-plans/ai-layered-design-implementation-plan.md"`。
- 当前结论:`LayeredDesignDocument -> image task artifact -> media-runtime -> GeneratedDesignAsset.params.postprocess` 已具备首个真实透明图层生成闭环;后续仍需远程资产缓存后处理、复杂 matting / mask refine、PSD-like 投影和扁平图拆层。
### 2026-05-06 P4E 远程 URL PNG chroma-key 后处理
- 已继续扩展 `src-tauri/crates/media-runtime/src/lib.rs`:当 provider 返回 `http/https` 图片 URL 且任务带 `chroma_key_postprocess` 时,worker 会在任务执行期受控下载该 URL,再复用同一套 PNG 像素处理器输出透明 PNG data URL。
- 远程下载只允许 `http/https`,并设置 `IMAGE_TASK_POSTPROCESS_MAX_IMAGE_BYTES = 20 MiB` 上限;非 URL、非支持 scheme、下载失败、状态非成功或超限都只写后处理 `failed/skipped_unsupported_source`,不让图片任务整体失败。
- `images[].postprocess.input_source` 现在可区分 `data_url` 与 `remote_url`;远程 URL 成功时 `images[].url` 会从原始 URL 替换为 `data:image/png;base64,...`,继续保留 `removed_pixel_count / total_pixel_count / transparent`。
- 已补 Rust 集成回归 `execute_image_generation_task_should_postprocess_remote_chroma_key_url`,用本地 Axum 同时模拟图片生成接口和远程 PNG 资源,验证 worker 最终写回透明 data URL 且绿色像素 alpha=0、红色像素 alpha=255。
- 已通过 Rust 定向回归:`CARGO_TARGET_DIR="/tmp/lime-p4e-media-runtime-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-media-runtime chroma_key`,共 3 个测试。
- 已通过 Rust crate 回归:`CARGO_TARGET_DIR="/tmp/lime-p4e-media-runtime-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-media-runtime`,共 18 个测试。
- 已通过 diff 卫生检查:`git diff --check -- "src-tauri/Cargo.lock" "src-tauri/crates/media-runtime/Cargo.toml" "src-tauri/crates/media-runtime/src/lib.rs" "src/lib/layered-design/imageTasks.ts" "src/lib/layered-design/imageTasks.test.ts" "docs/exec-plans/ai-layered-design-implementation-plan.md"`。
- 当前结论:`chroma_key_postprocess` 已覆盖 provider 返回 `b64_json -> data URL` 与 `url -> http/https PNG` 两类主流图片结果;后续仍需持久化资产缓存、原生工程目录落盘、PSD-like 投影、复杂 matting / mask refine 与扁平图拆层。
### 2026-05-06 P4F 原生工程目录落盘
- 已新增 `src/lib/api/layeredDesignProject.ts`,前端只通过 API 网关调用 current Tauri 命令 `save_layered_design_project_export`,没有在 `DesignCanvas` 里散落裸 `invoke`。
- 已扩展 `src/lib/layered-design/export.ts`:在 ZIP 投影之外新增 `createLayeredDesignProjectExportFiles`,把同一份 `LayeredDesignDocument` 导出投影拆成可由 Tauri 写入的 `design.json / export-manifest.json / preview.svg / preview.png / assets/*` 文件列表。
- 已新增 `src-tauri/src/commands/layered_design_cmd.rs`:只把导出文件写入项目根目录下 `.lime/layered-designs/<document>.layered-design/`,并校验项目根目录必须是绝对路径、导出相对路径不得目录穿越、文件内容仅支持 `utf8 / base64`。
- 已同步命令四侧:Rust `runner.rs` 注册、DevBridge dispatcher、`agentCommandCatalog.fileBrowserCommands`、`mockPriorityCommands` 与 `defaultMocks`;并在 `docs/aiprompts/commands.md` 记录该命令仍属于 `LayeredDesignDocument -> canvas:design` 主链。
- `DesignCanvas` 现在在绑定 `projectRootPath` 时默认保存到项目工程目录;只有未绑定工作区时才回退浏览器 `.layered-design.zip` 下载,避免把浏览器下载误称为原生落盘。
- 已补 `src/lib/layered-design/export.test.ts`、`src/components/workspace/design/DesignCanvas.test.tsx`、`src/lib/tauri-mock/core.test.ts` 与 `src/lib/dev-bridge/mockPriorityCommands.test.ts` 回归,覆盖 Tauri 文件列表、项目目录保存、不触发浏览器下载、mock 命令可用。
- 已通过前端定向回归:`npm exec -- vitest run "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx"`,共 14 个测试。
- 已通过 mock / bridge 定向回归:`npm exec -- vitest run "src/lib/tauri-mock/core.test.ts" "src/lib/dev-bridge/mockPriorityCommands.test.ts"`,共 26 个测试。
- 已通过定向 ESLint:`npm exec -- eslint "src/lib/api/layeredDesignProject.ts" "src/lib/layered-design/export.ts" "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/workspace/design/types.ts" "src/lib/dev-bridge/mockPriorityCommands.ts" "src/lib/tauri-mock/core.ts" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已通过 Rust 定向回归:`CARGO_BUILD_JOBS=1 CARGO_TARGET_DIR="/Users/coso/Library/Caches/lime-p4f-layered-design-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime --lib save_layered_design_project_export --no-default-features`,共 2 个测试。
- 已通过命令契约门禁:`npm run test:contracts`。
- 已通过专属 GUI smoke:`npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000`,真实页面验证 `canvas:design`、图层交互和导出入口仍可打开。
- 已通过完整 GUI smoke:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 600000 --interval-ms 1000`。
- 已通过 diff 卫生检查:`git diff --check -- "src/lib/api/layeredDesignProject.ts" "src/lib/layered-design/export.ts" "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/workspace/design/types.ts" "src-tauri/src/commands/layered_design_cmd.rs" "src-tauri/src/commands/mod.rs" "src-tauri/src/app/runner.rs" "src-tauri/src/dev_bridge/dispatcher/files.rs" "src/lib/dev-bridge/mockPriorityCommands.ts" "src/lib/tauri-mock/core.ts" "src/lib/governance/agentCommandCatalog.json" "docs/aiprompts/commands.md"`。
- 当前结论:P4F 的 current 代码路径、命令契约、Rust 写盘内核和 GUI 主路径均已验证通过;后续可进入 PSD-like 投影或扁平图拆层,不需要回到旧 poster / ImageTaskViewer 链路。
### 2026-05-06 P4G 工程目录再打开 / 恢复
- 已扩展 current API 网关 `src/lib/api/layeredDesignProject.ts`:新增 `readLayeredDesignProjectExport`,继续通过 `safeInvoke` 调用 `read_layered_design_project_export`,页面层没有散落裸 `invoke`。
- 已扩展 `src-tauri/src/commands/layered_design_cmd.rs`:新增只读请求/输出结构和 `read_layered_design_project_export_inner`,支持显式相对目录或自动选择最近保存的 `.lime/layered-designs/*.layered-design/`,只读取 `design.json` 与可选 `export-manifest.json`。
- 读取命令会校验 `projectRootPath` 必须是绝对路径、指定导出目录必须位于 `.lime/layered-designs/` 下;非导出目录或缺少 `design.json` 会失败,不会把普通目录误当设计工程。
- 已同步命令四侧:Rust `runner.rs` 注册、DevBridge dispatcher、`agentCommandCatalog.fileBrowserCommands`、`mockPriorityCommands` 与 `defaultMocks`;`docs/aiprompts/commands.md` 已记录保存/读取同属 `LayeredDesignDocument -> canvas:design` current 工程目录链路。
- `DesignCanvas` 新增“打开最近工程”入口:绑定 `projectRootPath` 后读取最近工程,`JSON.parse(designJson)` 后通过 `normalizeLayeredDesignDocument` 恢复文档,并自动选中恢复文档中最高 zIndex 图层,继续沿同一个编辑器状态工作。
- 已补 `src/components/workspace/design/DesignCanvas.test.tsx` 回归,覆盖“打开最近工程”后恢复 `LayeredDesignDocument`、继续编辑状态和旧 `poster_generate / canvas:poster / ImageTaskViewer` 不回流。
- 已补 `src/lib/tauri-mock/core.test.ts` 与 `src/lib/dev-bridge/mockPriorityCommands.test.ts` 回归,覆盖保存/读取 mock 闭环和浏览器模式 mock 优先命令集合;`scripts/design-canvas-smoke.mjs` 也检查“打开最近工程”入口存在。
- 已通过前端定向回归:`npm exec -- vitest run "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx" "src/lib/tauri-mock/core.test.ts" "src/lib/dev-bridge/mockPriorityCommands.test.ts"`,共 43 个测试。
- 已通过定向 ESLint:`npm exec -- eslint "src/lib/api/layeredDesignProject.ts" "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" "src/components/workspace/design/types.ts" "src/lib/dev-bridge/mockPriorityCommands.ts" "src/lib/dev-bridge/mockPriorityCommands.test.ts" "src/lib/tauri-mock/core.ts" "src/lib/tauri-mock/core.test.ts" "scripts/design-canvas-smoke.mjs" --max-warnings 0`。
- 已通过定向 TypeScript 检查:重建 `/tmp/lime-layered-design-tsconfig.json` 后执行 `npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已通过 Rust 定向回归:`CARGO_BUILD_JOBS=1 CARGO_TARGET_DIR="/Users/coso/Library/Caches/lime-p4g-layered-design-target" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime --lib layered_design_project_export --no-default-features`,共 4 个测试。
- 已通过命令契约门禁:`npm run test:contracts`。
- 已通过专属 GUI smoke:`npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000`,真实页面验证 `canvas:design`、图层交互、导出入口和“打开最近工程”入口仍可打开。
- 已通过完整 GUI smoke:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 600000 --interval-ms 1000`。
- 已通过 diff 卫生检查:`git diff --check` 覆盖 tracked 主线文件;未跟踪的新文件 `src-tauri/src/commands/layered_design_cmd.rs` 与 `src/lib/api/layeredDesignProject.ts` 已额外检查尾随空白和文件末尾换行。
- 当前结论:P4G 已补上 `生成/编辑 -> 保存项目工程目录 -> 重新打开 -> 继续编辑` 的最小 current 闭环;仍未完成的是持久化远程资产缓存、PSD-like 投影、复杂 matting / mask refine 与扁平图拆层。
### 2026-05-06 P4H PSD-like 专业导出投影
- 已扩展 `src/lib/layered-design/export.ts`:新增 `createLayeredDesignPsdLikeManifest` 与 `LAYERED_DESIGN_PSD_LIKE_EXPORT_SCHEMA_VERSION`,把 `LayeredDesignDocument` 投影为 `psd-like-layer-stack`。
- `psd-like-manifest.json` 会记录 `source.factSource=LayeredDesignDocument`、`compatibility.truePsd=false`、`layerOrder=back_to_front`,并把 image / effect、text、shape、group 分别投影为 raster image、editable text、vector shape 与 group reference。
- ZIP 工程包与 Tauri 工程目录文件列表现在都会包含 `design.json / export-manifest.json / psd-like-manifest.json / preview.svg / preview.png / assets/`;远程 assets 继续只保留 `source=reference` 与 `originalSrc`,不伪装成已缓存文件。
- `DesignCanvas` 导出文案已同步说明 `psd-like-manifest.json`,用户仍从同一个“导出设计工程”入口进入,不新增旧 poster、provider adapter 或平行主链。
- 已补 `src/lib/layered-design/export.test.ts` 回归,覆盖 PSD-like manifest 的事实源、兼容性声明、图层顺序、图层角色、远程 asset 引用和旧链路禁词。
- 已补 `src/components/workspace/design/DesignCanvas.test.tsx` 回归,覆盖导出入口文案和 Tauri 保存文件列表包含 `psd-like-manifest.json`。
- 已通过前端定向回归:`npm exec -- vitest run "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.test.tsx"`,共 16 个测试。
- 已通过定向 ESLint:`npm exec -- eslint "src/lib/layered-design/export.ts" "src/lib/layered-design/export.test.ts" "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" --max-warnings 0`。
- 已通过定向 TypeScript 检查:`npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`。
- 已通过专属 GUI smoke:`npm run smoke:design-canvas -- --timeout-ms 240000 --interval-ms 1000`。
- 已通过完整 GUI smoke:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 600000 --interval-ms 1000`。
- 当前结论:P4H 完成的是专业层栈 manifest 投影,不是真 PSD writer;仍未完成的是持久化远程资产缓存、真 PSD 文件导出 / 打开验证、复杂 matting / mask refine、OCR 与扁平图拆层。
### 2026-05-06 P4I 原生工程目录远程资产持久化缓存
- 已继续扩展 `src-tauri/src/commands/layered_design_cmd.rs`,但没有新增命令名:`save_layered_design_project_export` 现在会解析 `export-manifest.json` 中 `source=reference + originalSrc=http/https` 的远程图片资产,并在保存工程目录时尝试持久化到 `assets/`。
- 远程缓存成功后,命令会把 `export-manifest.json` 与 `psd-like-manifest.json` 中对应资产从 `reference` 更新为 `file`,同时继续保留 `originalSrc`,确保导出 projection 明确说明“这是缓存副本,不是新的事实源 URL”。
- `design.json` 磁盘文件仍保持原始导出投影,不在保存阶段偷偷改成第二套协议;重新打开时由 `read_layered_design_project_export` 优先读取 manifest 指向的缓存文件,把对应 asset 水合回 data URL,再返回给前端 `DesignCanvas`。
- 这刀的直接收益是:远程图片图层在“保存项目工程目录 -> 重新打开 -> 继续编辑”链路里不再完全依赖远程 URL 在线可达;即使原始 provider URL 后续失效,只要缓存文件仍在,本地工程仍能继续打开编辑。
- 本轮不扩浏览器 ZIP 下载的远程缓存;ZIP 仍保持“嵌入 data URL 资产直接打包,远程资产保留引用”。这样避免为了浏览器侧旁路再引入第二套下载/权限模型。
### 2026-05-06 P4J 扁平图拆层协议首刀
- 已在 `src/lib/layered-design/types.ts` 给 `LayeredDesignDocument` 增加兼容扩展字段 `extraction`,并补 `GeneratedDesignAsset.kind = "source_image"`,让扁平图来源资产、候选层、置信度和 clean plate 状态都能稳定回挂到 current 事实源,而不是另起一套拆层中间协议。
- 已在 `src/lib/layered-design/document.ts` 增加 extraction normalization:候选层会被强制标记 `source: "extracted"`,低置信度自动补 `low_confidence`,并把 extraction 附带的 `clean_plate` / 候选资产吸收到顶层 `assets`,避免后续 UI 或导出在事实源内读到“候选引用了不存在的资产”。
- 已新增 `src/lib/layered-design/extraction.ts` 纯函数,提供 `createLayeredDesignExtractionDocument` 和 `updateLayeredDesignExtractionSelection`:前者把扁平图、候选层、clean plate 结果归一为 draft 文档,后者只把 `selected=true` 的候选 materialize 到正式 `layers`,未选候选继续只存在于 `extraction.candidates`。
- clean plate 成功时,背景层默认引用 `clean_plate`;clean plate 失败时,背景层自动回退到 `source_image`,同时保留 `extraction.cleanPlate.status/message`,确保“可继续编辑”和“风险显式暴露”同时成立。
- 本轮仍不接 OCR / SAM / matting / inpaint 真执行,也不接拆层确认页;这刀只把后续拆层执行链路需要的协议和不变量先钉死在 current 主链上。
- 已验证:
- `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/extraction.test.ts"`
- `npm exec -- eslint "src/lib/layered-design/types.ts" "src/lib/layered-design/document.ts" "src/lib/layered-design/extraction.ts" "src/lib/layered-design/extraction.test.ts" --max-warnings 0`
- `npm exec -- tsc -p "/tmp/lime-layered-design-extraction-tsconfig.json" --noEmit`
### 2026-05-06 P4K 扁平图 draft `canvas:design` Artifact bridge
- 已在 `src/lib/layered-design/artifact.ts` 新增 `createLayeredDesignArtifactFromExtraction`,把扁平图拆层 draft 的创建和 Artifact 包装放在同一条 current helper 链里,避免后续上传/拆层入口再临时拼装 `canvas:design` JSON。
- 这个 bridge 明确把拆层 draft 归类为 `meta.source = "layered-design-extraction"`,继续沿用现有 `canvas:design`、`platform: layered-design` 和 `designId` 语义,不新增拆层专用 Artifact type,也不回流旧 `canvas:poster` / `ImageTaskViewer` 路线。
- `src/lib/layered-design/artifact.test.ts` 已补回归:扁平图 draft 打开到 `DesignCanvasState` 后,只会 materialize 已选/高置信度候选层;低置信度碎片候选仍只留在 `document.extraction.candidates`,保持确认前后边界一致。
- 这刀的直接收益是:后续不管拆层结果来自本地 analyzer、远程任务还是 mock,都能先归一为 `LayeredDesignDocument`,再复用现有 Artifact/Canvas 主链进入编辑,不需要为拆层入口再开第二套 Viewer/Workspace 路径。
- 已验证:
- `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/extraction.test.ts" "src/lib/layered-design/artifact.test.ts"`
- `npm exec -- eslint "src/lib/layered-design/types.ts" "src/lib/layered-design/document.ts" "src/lib/layered-design/extraction.ts" "src/lib/layered-design/artifact.ts" "src/lib/layered-design/extraction.test.ts" "src/lib/layered-design/artifact.test.ts" --max-warnings 0`
- `npm exec -- tsc -p "/tmp/lime-layered-design-extraction-tsconfig.json" --noEmit`
### 2026-05-06 P4L 上传扁平图本地 draft adapter
- 已新增 `src/lib/layered-design/flatImage.ts`,提供 `createLayeredDesignFlatImageDraftDocument`:输入只需要上传图片的 `src/width/height` 与可选 `fileName`,就能自动推导 `document id/title`、`source_image asset`、canvas 尺寸,并产出一个最小 extraction draft。
- 这个 adapter 复用了前一刀的 extraction 协议,而不是新开一套“上传图片草稿” schema:即使当前还没有 analyzer / OCR / clean plate 结果,也仍然先生成 `LayeredDesignDocument.extraction`,背景层默认回指原始 `source_image`。
- 已在 `src/lib/layered-design/artifact.ts` 新增 `createLayeredDesignArtifactFromFlatImage`,让单张上传图片可以直接落成 current `canvas:design` Artifact;它继续复用 `layered-design-extraction` 元数据来源,不额外扩 metadata 面。
- `src/lib/layered-design/flatImage.test.ts` 证明两点:一是纯上传图可以直接进入“只有背景层”的最小 draft;二是同一个 adapter 可以在有本地候选 seed 时只 materialize 高置信度候选层,把低置信度碎片继续留在 `extraction.candidates`。
- `src/lib/layered-design/artifact.test.ts` 已补回归,证明 `createLayeredDesignArtifactFromFlatImage` 产出的 Artifact 可以直接进入 `DesignCanvasState`,不需要额外 JSON 拼装或第二条 Workspace 接线。
- 这刀的直接收益是:后续“上传扁平图”入口只要先拿到图片 bytes / data URL / 远程 URL 与尺寸,就已经能稳定走到 current `canvas:design` 主链;未来接 analyzer 结果时只是在同一份 extraction draft 上补 candidates/cleanPlate,不用重写打开链路。
- 已验证:
- `npm exec -- vitest run "src/lib/layered-design/document.test.ts" "src/lib/layered-design/extraction.test.ts" "src/lib/layered-design/flatImage.test.ts" "src/lib/layered-design/artifact.test.ts"`
- `npm exec -- eslint "src/lib/layered-design/types.ts" "src/lib/layered-design/document.ts" "src/lib/layered-design/extraction.ts" "src/lib/layered-design/flatImage.ts" "src/lib/layered-design/artifact.ts" "src/lib/layered-design/extraction.test.ts" "src/lib/layered-design/flatImage.test.ts" "src/lib/layered-design/artifact.test.ts" --max-warnings 0`
- `npm exec -- tsc -p "/tmp/lime-layered-design-flat-image-tsconfig.json" --noEmit`
### 2026-05-06 P4M DesignCanvas 候选层切换首刀
- 已在 `src/components/workspace/design/DesignCanvas.tsx` 新增两个 current UI 动作:工具栏的“上传扁平图”,以及属性栏里的“拆层候选”卡片。前者直接读取本地图片文件并落成 extraction draft,后者允许在同一个 `DesignCanvas` 内切换 `extraction.candidates.selected`。
- 这一刀没有新建独立拆层确认页,而是先把确认态压进现有 `DesignCanvas`,验证 `LayeredDesignDocument.extraction -> selected candidates -> layers` 这个最小状态机能否在 current 主路径内跑通。
- UI 语义保持和协议一致:低置信度候选仍会显示“低置信度”,但在用户点击前不会 materialize 到正式图层;点击后只改 `candidate.selected`,再由纯函数同步图层栈,不直接手改 `layers`。
- 这刀的直接收益是:上传扁平图后,用户已经可以在 current 画布里完成“原图进入编辑 -> 补选候选层 -> 成为正式图层”的最小操作,而不需要等待独立确认页或真实 analyzer 才能继续主链验证。
- 已验证:
- `npm exec -- vitest run "src/components/workspace/design/DesignCanvas.test.tsx" "src/lib/layered-design/document.test.ts" "src/lib/layered-design/extraction.test.ts" "src/lib/layered-design/flatImage.test.ts" "src/lib/layered-design/artifact.test.ts"`
- `npm exec -- eslint "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" "src/lib/layered-design/flatImage.ts" "src/lib/layered-design/flatImage.test.ts" "src/lib/layered-design/artifact.ts" "src/lib/layered-design/artifact.test.ts" --max-warnings 0`
- `npm run verify:gui-smoke -- --timeout-ms 600000 --interval-ms 1000`(已通过)
### 2026-05-06 P4N 上传扁平图本地 heuristic seed 首刀
- 已新增 `src/lib/layered-design/flatImageHeuristics.ts`,在浏览器本地对上传图片生成最小裁片候选:主体、标题文字、Logo 与边角碎片都继续回挂到 `LayeredDesignDocument.extraction.candidates`,不新增新的拆层 schema。
- `DesignCanvas` 的上传入口现在会优先尝试本地 heuristic seed:成功时直接带着裁片候选进入 current `canvas:design -> DesignCanvas` 主链;失败时回退为“只有背景层”的 draft,不阻断上传到 current 画布。
- 这刀仍然显式标注 clean plate 未执行,且只让高于阈值的主体 / 标题裁片默认进入正式图层;Logo 和碎片继续作为低置信度候选等待用户确认,避免把启发式裁片伪装成真实拆层结果。
- 这刀的直接收益是:`上传扁平图 -> extraction draft -> 候选层切换 -> 正式图层` 现在不再依赖手工伪造 seed 或未来 analyzer 才能演示,current `DesignCanvas` 已经能直接承接一条最小但真实的候选层闭环。
- 已验证:
- `npm exec -- vitest run "src/components/workspace/design/DesignCanvas.test.tsx" "src/lib/layered-design/document.test.ts" "src/lib/layered-design/extraction.test.ts" "src/lib/layered-design/flatImage.test.ts" "src/lib/layered-design/artifact.test.ts"`
- `npm exec -- eslint "src/components/workspace/design/DesignCanvas.tsx" "src/components/workspace/design/DesignCanvas.test.tsx" "src/lib/layered-design/flatImage.ts" "src/lib/layered-design/flatImage.test.ts" "src/lib/layered-design/flatImageHeuristics.ts" "src/lib/layered-design/artifact.ts" "src/lib/layered-design/artifact.test.ts" "src/lib/layered-design/index.ts" --max-warnings 0`
- `npm exec -- tsc -p "/tmp/lime-layered-design-tsconfig.json" --noEmit`
- `npm run verify:gui-smoke -- --timeout-ms 600000 --interval-ms 1000`
@@ -1,218 +0,0 @@
# `@` 命令本地执行面纠偏计划
## 摘要
本计划用于把 Lime 客户端仓库中关于 `@` 命令、产品型 `/scene`、`ServiceSkill` 的 current 文档收口到同一个正确前提:
- `limecore` 只负责目录控制、发布治理与配置同步
- `lime` 客户端负责全部执行
- 不存在服务端代跑 `@` 命令、`/scene` 或 `service skill` 的 current 主链
这份计划只覆盖客户端仓库的文档与后续本地实现收口,同时定义服务端文档完成后的跨仓校验要求。
## 背景
最近两仓围绕 Ribbi 风格统一调用面的分析,暴露出一个持续回流的错误前提:
1. 一部分文档把 `limecore` 写成了命令执行面
2. 一部分文档把 `scene runtime`、`run / poll`、`cloud_required` 继续当成 current 产品模型
3. 这会误导后续实现,把目录控制面继续扩成运行时
用户已明确产品边界:
1. 云端不会执行任何东西
2. `limecore` 只承担 `client/bootstrap`、`client/skills`、`client/service-skills` 这类目录与配置事实源
3. Lime 客户端是唯一执行面
因此,客户端 current 文档必须先于代码实现完成纠偏,否则新增 `@` 命令会继续叠加在错误边界上。
## 正确边界
### `current`
- `SkillCatalog.entries` 作为 `@ / / / skill` 的统一目录协议
- `serviceSkillCatalog` 作为完整服务型技能目录
- seeded / fallback 作为客户端韧性兜底
- `request_metadata.harness.*` 作为本地运行时路由提示
- Agent、tool、browser、workspace、task、viewer 全部留在本地执行
### `deprecated`
- `cloud_required`
- `executionLocation = cloud_required`
- `OEM Scene Runtime run / poll`
- `lime_run_service_skill` 被解释成服务端执行入口
- `cloud-video-dubbing` 这类带云语义的历史命名
### `dead`
- 服务端代跑 `@` 命令
- 服务端代跑 `/scene`
- 服务端代跑 `service skill`
- 任何把目录命中直接翻译成云端 run 的 current 叙事
## 本轮文档改动
需要优先收口以下客户端 current 文档:
1. `docs/aiprompts/commands.md`
2. `docs/aiprompts/command-runtime.md`
3. `docs/aiprompts/limecore-collaboration-entry.md`
4. `docs/roadmap/lime-service-skill-cloud-config-prd.md`
改动原则:
1. 删除或改写 `run / poll`、`cloud_required`、`scene runtime` 的 current 叙事
2. 如果现有实现或历史 ID 仍沿用旧命名,文档里要明确它只是历史命名,不代表当前边界
3. 不新发明第二套术语,统一收敛到“目录控制面 + 本地执行面”
## 后续实现顺序
### Phase 1:文档先行
- 先把 current 文档纠偏
- 在实现前统一团队和 AI 的事实源
### Phase 2:客户端代码收口
后续只在本仓库推进以下本地实现检查:
- `src/components/agent/chat/workspace/useWorkspaceSendActions.ts`
- `src/components/agent/chat/workspace/serviceSkillSceneLaunch.ts`
- `src/components/agent/chat/service-skills/*`
- `src/components/agent/chat/workspace/useWorkspaceServiceSkillEntryActions.ts`
- `src/lib/base-setup/seededCommandPackage.ts`
- `src/lib/base-setup/seededServiceSkillPackage.ts`
- `src/components/settings-v2/system/automation/serviceSkillContext.ts`
目标:
- 去掉任何 current 主链中的云端 run / poll 假设
- 保留目录命中与 metadata 注入
- 把 `@配音`、`/scene-key`、service skill launch 全部收回本地执行链
### Phase 3:服务端完成后的联调校验
服务端会由另一个进程按 `limecore/docs/exec-plans/at-command-control-plane-only.md` 推进。客户端这边在服务端文档完成后,需要补一次跨仓一致性校验。
## 校验要求
### 客户端本地校验
至少重新检查:
- `@配音`
- `/scene-key`
- `@浏览器`
- `@搜索`
- `@发布`
- `@渠道预览`
校验点:
1. 在线目录可命中
2. seeded / fallback 不失能
3. current 文档不再暗示云执行
4. 轻卡、timeline、viewer 仍以本地运行态为真相
### 跨仓校验
服务端文档完成后,再做一次双仓扫描:
- `cloud_required`
- `云执行`
- `run / poll`
- `scene runtime`
- `not_executable_in_cloud`
扫描结果必须区分为:
1. 已移除
2. 仅保留在历史参考
3. 仍误留在 current,需要继续收口
## 当前状态
- 2026-04-21:创建本计划,作为客户端纠偏的 current 执行入口
- 2026-04-21:与用户确认真实产品边界为“云端不执行,只做目录控制”
- 2026-04-21:本轮优先目标固定为“先改文档事实源,再进入客户端实现收口”
- 2026-04-21:已完成 current 文档纠偏,统一改写为“云端只做目录控制,客户端负责执行”
- 2026-04-21:已继续同步质量事实源;`quality-workflow` 中的 `@配音 / /scene-key` 校验口径已从 `OEM run/timeline` 收回到本地 `service-scene` 直驱执行与 `ServiceSkill / tool timeline`
- 2026-04-21:已继续收口 Rust `service_scene_launch` current 默认值;后端当前默认按 `local_service_skill` 解析,并只把 `cloud_scene` 当 compat alias 读取
- 2026-04-21:已完成客户端 current 主链收口,`/scene` 非站点技能与 `@配音` 不再依赖 OEM run / poll,而是统一生成本地 `composeServiceSkillPrompt(...)` 并通过工作区/Agent 本地执行
- 2026-04-21:已完成 service skill 首页、@ 提及面板、设置自动化文案的执行语义纠偏;`cloud_required` 当前只作为兼容目录标记展示,不再表示云端执行
- 2026-04-21:已继续收口 automation 设置页 current 展示;任务列表、详情弹窗和运行摘要里的 `cloud_required` 现在会显式显示为“客户端执行 + 旧目录兼容”,不再把 compat 标记伪装成当前执行真相
- 2026-04-21:已完成技能工作台 current surface 的旧云运行状态收口,`ServiceSkillHomeItem` 与技能页展示不再消费 `cloudStatus`
- 2026-04-21:已删除零入口 compat 残留 `src/components/agent/chat/service-skills/cloudRunStorage.ts` 与 `src/lib/api/serviceSkillRuns.ts`,避免旧云 run 语义继续回流
- 2026-04-21:已进一步把 current seeded package 主动产出的 `cloud_scene / cloud-scene-instant` 收掉;当前只保留历史 seeded id(如 `cloud-video-dubbing`)与 compat projection/sceneapp 中的旧命名,避免 current 目录事实源继续给云执行语义续命
- 2026-04-21:已继续收口 sceneapp current 主链;Tauri / mock sceneapp planner 产出的 `service_scene_launch.kind` 与 `execution_kind` 已统一改为 `local_service_skill`,并补 `execution_location = client_default`
- 2026-04-21:已移除 current seeded sceneapp 中额外的 `cloud_session` 启动前置,避免 sceneapp detail/runtime 继续把“云端运行时”误当成当前执行门槛
- 2026-04-21:已继续收口 sceneapp 恢复链内部命名;run summary / entry action / resume helper 中的 `cloud_scene_runtime_ref`、`cloudSceneRuntimeRef`、`open_cloud_scene_session` 已统一改成 `service_scene` 语义,并保留 Rust 侧 alias 兼容旧字段
- 2026-04-21:已完成最小交付校验,`npm run test:contracts`、定向 Vitest、`npm run verify:local`、`npm run governance:legacy-report` 全部通过
- 2026-04-21:已补充 sceneapp 定向校验;`npm run verify:gui-smoke` 与 sceneapp 定向 Vitest 已通过,Rust service-scene 新用例改名后需按当前测试名单独补测
- 2026-04-21:已继续收口 sceneapp current planner/action 命名;当前 Tauri/mock planner 改为产出 `open_service_scene_session`,旧 `launch_cloud_scene` 仅保留为 compat 反序列化输入
- 2026-04-21:已把 seeded `voice-runtime` 从 `cloud_managed` current 模型收回到本地即时类型;当前种子目录不再把它描述成“云托管场景”
- 2026-04-21:已继续收口 sceneapp runtime context;`cloud_session_ready` 不再参与 current readiness / tool refs 注入,只再保留为 compat 输入字段,避免 current planning 再把“云会话”带回执行语义
- 2026-04-21:已继续收口 sceneapp seeded/mock 原始目录展示;current `capabilityRefs`、`infraProfile` 与 composition step binding 不再主动写出 `cloud_scene / cloud_runtime`,旧 `cloud_scene` 只再保留在 service-scene adapter 锚点与 compat 输入
- 2026-04-21:已继续收口 sceneapp current entry binding;seeded/mock 里的 `story-video-suite`、`voice-runtime` 当前入口已改成 `agent_turn`,Rust/mock planner 会按 service-scene 特征继续产出 `open_service_scene_session`
- 2026-04-21:已继续收口 Rust sceneapp 内部命名;`CloudManaged / CloudScene / CloudSession / cloud_session_ready` 当前已改成内部 compat 语义名,并通过 serde 继续兼容旧外部值,避免 current 内部建模继续被旧云执行命名污染
- 2026-04-22:已补通过 Rust sceneapp 定向 lib 测试:`should_build_service_scene_adapter_plan_for_hybrid_sceneapp`、`build_sceneapp_scorecard_from_runs_should_keep_stable_scene`、`should_promote_selected_memory_entries_into_reference_items`
- 2026-04-21:已补跑本轮相关前端定向回归;`useWorkspaceSendActions`、`sceneapp` API/目录/展示与 SceneApps 页面共 8 个 Vitest 文件、154 条用例通过
- 2026-04-21:已补跑 `npm run governance:legacy-report`,结果为 `边界违规 0 / 分类漂移候选 0`
- 2026-04-21:已继续收口 sceneapp 展示层 current 边界;`presentation/product` 与 SceneApps 页面测试夹具默认改成 `agent_turn`,展示层不再主动持有 `cloud_scene` current 标签
- 2026-04-22:已继续收口 `serviceSkills / skillCatalog` 类型边界;public `ServiceSkillItem / ServiceSkillCatalog / SkillCatalogExecutionKind` 默认只暴露 current 值,`cloud_scene / cloud_required` 改为显式 compat 类型或原始输入解析层处理
- 2026-04-22:已同步更新聊天入口与技能页相关测试夹具;`service_scene_launch.kind` 当前统一改成 `local_service_skill`,服务技能 fixture 默认改成 `agent_turn + client_default`
- 2026-04-22:已补跑服务技能 / 技能目录 / 聊天入口定向回归共 11 个 Vitest 文件、206 条用例通过;随后补跑 `sceneapp` API/目录/launch 3 个 Vitest 文件、13 条用例通过
- 2026-04-22:已再次补跑 `npm run governance:legacy-report`,结果仍为 `边界违规 0 / 分类漂移候选 0`
- 2026-04-22:已把 service skill 执行位置展示语义收回 `src/lib/api/serviceSkills.ts` 单一事实源;prompt composer 当前只写“客户端执行”,automation 设置页继续通过 compat helper 单独补 `旧目录兼容` 徽标,避免旧云目录标记继续混入 current prompt 文案
- 2026-04-22:已继续收口 mock sceneapp planner 内部判断;legacy `cloudSessionReady` 输入不再把 planner 拉回 `cloud_scene` 分支,mock adapter 当前会先正规化到 `agent_turn` 后再决定是否打开 `service_scene` 会话
- 2026-04-22:已继续收口前端 `sceneapp runtime context` 内部命名;当前默认改用 `directorySessionReadyCompat` 承载 compat 会话位,`sceneapp API` 调 Tauri 前会统一回写为 `cloudSessionReady` wire alias,mock readiness 也与 Rust 一样对 `cloud_session` 保持 compat no-op
- 2026-04-22:已继续收口 SceneApps 页面 current 筛选面;目录页不再主动提供 `cloud_managed` 旧类型筛选,旧 page params / 最近访问里的 `typeFilter=cloud_managed` 现在会在 `navigation` 归一化阶段被丢弃,避免 current 页面状态继续把 compat 类型写回路由与本地存储
- 2026-04-22:已继续收口 sceneapp 展示 compat helper;`presentation/product` 当前统一通过显式 helper 解析 `cloud_managed / cloud_runtime`,workbench 统计与基础设施摘要不再各自直接消费旧云命名,compat 展示只保留在单一 helper 边界
- 2026-04-22:已继续收口 `sceneapp API` 的旧 planner 假门槛;当前网关会过滤 `launchRequirements / readiness.unmetRequirements` 里的 `cloud_session`,并把 context overlay 中的 `toolRefs.cloud_scene -> agent_turn`、`toolRefs.cloud_session -> 丢弃`,避免旧 planner 再把“云会话阻塞”显影到 current 详情页
- 2026-04-22:已继续收口 `sceneapp API` 的 compat 类型投影;旧目录 `sceneappType = cloud_managed` 当前会按绑定族与基础设施信号正规化为 `local_instant / local_durable / browser_grounded / hybrid`,不再把 compat 类型直接暴露给 current 前台对象
- 2026-04-22:已继续收口 current 测试夹具默认值;`workspace sceneapp launch`、`workspace sceneapp entry actions` 与 `automation` 的默认 SceneApp plan/descriptor 夹具已改成 `agent_turn` / 本地能力摘要,只再保留显式 compat 用例验证旧 `cloud_scene` 输入的正规化
- 2026-04-22:已继续清理 sceneapp 深层 current 测试夹具;`product` 与 `SceneAppsPage` 默认目录样板不再把 `cloud_runtime` 当作普通 `infraProfile` 基线,旧云基础设施词现在只保留给显式 compat 断言
- 2026-04-22:已继续收口 SceneApps 详情页 current 启动主链;详情页 launch 现在会优先复用当前 preview planning,`workspace_entry` 做法会继续导航到 `agent` 主链,`automation_job` 会先打开标准自动化弹窗再异步补工作区列表,避免 current UI 因重复 planning 或等待工作区列表而停在 sceneapps 自身
- 2026-04-22:已为 sceneapp launch runtime 补 unmount 保护;页面切走或测试回收后不再回流异步 `setState`,避免 current 导航链在离开 SceneApps 页后继续触发收尾更新
- 2026-04-22:已补跑 `SceneAppsPage / navigation / presentation / product / launch` 共 5 个 Vitest 文件、58 条用例,通过;`npm run governance:legacy-report` 与 `npm run verify:gui-smoke` 也已通过
- 2026-04-22:`npm run verify:local` 在 smart 批次 `src/components/agent/chat/workspace/serviceSkillSceneLaunch.test.ts` 因 `@/lib/api/serviceSkills` mock 未同步导出 `resolveServiceSkillExecutionLocationPresentation` 失败;该阻塞位于另一条 service-skill 线,不属于本轮 sceneapp 启动实现回归
- 2026-04-21:Rust 定向测试 `cargo test should_build_service_scene_adapter_plan_for_hybrid_sceneapp` 因工作区存在多路并发 `cargo test` 抢占 `target`/artifact lock,未拿到最终结果;本轮不把它记为已通过,后续需在较干净的 Rust 构建窗口补测
- 2026-04-21:已尝试用独立 `CARGO_TARGET_DIR=/tmp/lime-target-codex-sceneapp` 复测 Rust service-scene 新用例,但首次编译 `aster`/`lime` 依赖链耗时过长,本轮仍未拿到最终结果;为避免持续占用机器资源,已主动停止该测试进程,后续需在更稳定的 Rust 构建窗口补跑
## 本轮实现结果
### 已完成
- `/scene` 非站点技能:改为构造 `kind = local_service_skill` 的 request metadata,并生成本地 prompt;不再向 current 主链注入 `oem_runtime` 或云端 session 语义
- `@配音`:保留用户可见原文,但底层 dispatch 改为 `composeServiceSkillPrompt(...)` 生成的本地 prompt;metadata 同步切换到 `local_service_skill`
- service skill 入口点击:删除 current 对 `createServiceSkillRun` / `getServiceSkillRun` / run-poll 的依赖,统一进入本地工作区准备态
- 首页与提及展示:`runnerLabel`、`runnerDescription`、`actionLabel`、`outputDestination` 改为本地执行叙事;legacy `cloud_required` 只按兼容目录标记处理
- automation 设置页展示:`service skill` 任务列表、详情弹窗与运行摘要当前都会补显 `旧目录兼容` 徽标与说明文案;即使 run metadata 已被正规化为 `client_default`,只要来源 job 仍带旧目录标记,也不会在 current 展示层把 compat 信息洗掉
- Rust `service_scene_launch` 解析:当前默认 `kind` 已切到 `local_service_skill`,并允许 `cloud_scene` 只作为 compat alias 继续读取;`aster_agent_cmd` 定向测试样例也已同步改成 current 命名
- Rust `prompt_context / lime_run_service_skill`:当前系统提示已改成“本地 service-scene 直驱执行”,`lime_run_service_skill` 只再作为 compat 护栏保留,不再发 OEM `/runs` 请求
- seeded 目录:保留历史 id 以避免目录断裂,但 `@配音` 的 command binding 已切到 `agent_turn`,技能标题/摘要已改成当前本地执行语义
- sceneapp planner:Tauri 与 mock 当前改为产出 `open_service_scene_session`;旧 `launch_cloud_scene` 只作为 compat 反序列化输入保留,避免历史 planner / fixture 直接失效
- sceneapp seeded / fallback:当前目录不再额外声明 `cloud_session` 启动前置;sceneapp detail 展示会继续聚焦“目录控制面 + 客户端执行”,不再把云端运行时写成 current 门槛
- sceneapp seeded voice-runtime:当前目录模型已改成 `local_instant` + 本地执行文案;旧 `cloud_managed` 只再保留给 compat 目录输入与展示兜底
- sceneapp runtime context:当前 context compiler 不再把 `cloud_session` 注入 tool refs,且 runtime readiness 对 `cloud_session` 只按 compat no-op 处理;这样历史字段还能读,但不会再影响 current 启动判断
- sceneapp 恢复链:当前做法复盘和继续执行链已统一改为 `service_scene` 命名;旧 `cloud_scene_runtime_ref` 只再作为 Rust 反序列化 alias 保留,避免历史 run summary 数据读取失败
- mention / sceneapp 内部命名:聊天发送边界里的 usage slot 提取已统一改成 `service_scene` 语义,sceneapp current seeded/mock 目录的展示能力摘要也已改成 `agent_turn` / 本地工作区表述,避免 current 实现继续把内部变量写成“云场景”
- sceneapp current service-scene 入口:当前 seeded/mock 目录不再依赖 `entryBindings.bindingFamily = cloud_scene` 才能走本地场景执行;planner 会在 `agent_turn` current 目录下按 linked service skill / scene key 继续产出 `open_service_scene_session`
- sceneapp 展示层:`presentation/product` 与 SceneApps 页面当前测试夹具、组合步骤、入口绑定和执行摘要默认都已切到 `agent_turn`;`cloud_scene` 不再作为 current UI 文案映射保留在展示层
- `serviceSkills / skillCatalog` 类型面:公开 catalog/item/execution 类型当前只暴露 `agent_turn / client_default` 等 current 值;`cloud_scene / cloud_required` 仅再保留为 compat 输入类型与解析层归一化逻辑,避免前台 current consumer 继续把旧云执行名当作常规值处理
- 聊天入口测试夹具:`workspaceEntry / AppPageContent / slash preflight / scene launch / send actions / EmptyState` 等 current fixture 已同步改成 `local_service_skill` 与 `agent_turn` 语义,避免测试继续给旧云执行命名续命
### 仍保留为 compat / 历史锚点
- `cloud-video-dubbing`
- `cloud_scene`
- `executionLocation = cloud_required`
- `launch_cloud_scene` / `open_cloud_scene_session`
这些字段当前仍存在于历史 skill id、compat projection、sceneapp 规划与测试中,用于兼容历史目录和旧数据,不再代表 current 执行边界;其中 current seeded package 已不再主动产出 `cloud_scene / cloud-scene-instant`,sceneapp current planner 也不再主动写出 `service_scene_launch.kind = cloud_scene`。
## 下一步
- 等服务端进程完成 `limecore` 文档与目录控制面实现后,按本计划里的“跨仓校验”重新扫描双仓术语一致性
- 如果后续要继续做减法,优先清理 `sceneapp` 与 compat projection 中仍残留的 `cloud_scene` 历史命名,但这不阻塞当前客户端本地执行主链交付
@@ -1,57 +0,0 @@
# Chrome Web Store 发布计划
## 目标
将 `extensions/lime-chrome` 以当前可发布版本提交到 Chrome Web Store,并沉淀可复用的上架材料,避免后续版本更新再次依赖口头说明。
## 当前状态
- 已确认扩展目录:`extensions/lime-chrome`
- 已确认扩展版本:`0.4.0`
- 已生成上传包:`tmp/lime-chrome-0.4.0.zip`
- 已确认上传包 SHA-256:`059b6c17a8fea130d78709cd85e3cc16e687438da4ab4aab6bd0ed898bd3818b`
- 已发现安装页内预留商店链接:`https://chromewebstore.google.com/detail/lime-chrome/cpidmllglbedhpombjibeoalnafofipo`
- 公开链接当前未返回可见上架页,需在开发者后台确认该条目是草稿、私有条目还是无效占位
## 已完成
1. 检查扩展结构、`manifest.json`、README 与现有仓库脚本,确认仓库内没有现成的 Chrome 商店发布自动化
2. 识别审核敏感权限与行为:
- `debugger`
- `clipboardRead`
- `notifications`
- `host_permissions: <all_urls>`
3. 生成待上传 zip 产物,并记录哈希便于核对
4. 补齐仓库内可复用的发布文案与隐私政策工件
5. 生成首批 Chrome 商店截图草稿素材
## 阻塞项
1. 真正发布到 Chrome Web Store 需要进入 Google 开发者后台,属于高风险外部发布动作,必须先得到用户明确确认
2. 若开发者后台尚未保存截图、分类、数据使用问卷或发布地区信息,仍需在后台继续补齐
3. 若当前商店条目与安装页中的扩展 ID 不一致,需要在后台确认是否继续沿用旧条目或新建条目
4. 隐私政策需要一个公网可访问 URL;当前仓库内文件已就绪,但尚未推送到公开可访问位置
## 下一步
1. 进入 Chrome Web Store 开发者后台,确认 `cpidmllglbedhpombjibeoalnafofipo` 对应条目状态
2. 使用 `tmp/lime-chrome-0.4.0.zip` 上传扩展包
3. 将 `extensions/lime-chrome/CHROME_WEB_STORE_SUBMISSION.md` 中的文案和权限说明填入后台
4. 将 `extensions/lime-chrome/PRIVACY_POLICY.md` 对应的公开 URL 填入隐私政策字段
5. 完成数据使用问卷、截图与可见性配置后提交审核或发布
## 进度日志
### 2026-04-20
- 补齐扩展 options 页中英文切换,并把缺配置状态改成明确的“需要配置 / Setup Required”
- 重新生成 `tmp/lime-chrome-0.4.0.zip`,剔除测试文件后记录正式 SHA-256
- 将 Chrome 商店提交底稿与发布计划同步到 `0.4.0` 事实源
- 将扩展 `icons/icon16.png`、`icon32.png`、`icon48.png`、`icon128.png` 全部替换为 `public/logo.png` 缩放版本,并重新打包上传产物
### 2026-04-13
- 完成本地打包与审核风险盘点
- 确认需要先补齐发布文案与隐私政策,再进入发布后台
- 生成 3 张 `1280x800` 商店截图草稿,位于 `tmp/chrome-store-assets/`
- 由于发布属于外部生产动作,暂未执行任何登录、上传或提交操作
@@ -1,106 +0,0 @@
# Claude Code Agent Task Runtime 全面对齐计划
## 摘要
目标是把 Lime 的 agent 主链从“线程态 + 工具流 + 诊断信号”升级为对标 Claude Code 的“统一任务运行时”:
- 主会话、子 agent、等待输入、完成结算都统一映射为可见任务。
- 简单问题不再错误抬升为常驻 `MAIN TASK` 面板,只在复杂、长链路、可跟踪任务里显示当前进展。
- 工具调用按批次聚合,必须产出中间过程结论,而不是只留下工具名。
- `token usage` 与 `prompt cache` 在任务完成态和消息态都保持可见。
- E2E 与 GUI smoke 后续统一以“任务是否创建、推进、完成”为核心断言。
## 关键改动
### 1. Agent Task Runtime 投影
- 新增统一任务投影模型,先覆盖 `main_session` 与 `subagent`。
- 每次主会话回合都可以被投影成任务,但展示层增加“简单直答自动折叠”规则。
- 当前回合的任务标题、状态、阶段、等待原因、工具批次摘要、子任务统计统一从现有 `thread_read / turns / items / messages / child_subagent_sessions` 归一投影。
### 2. 任务展示规则
- 仅在以下场景显示任务状态:
- 有工具批次或过程轨迹
- 有子任务
- 有排队 / 待补信息 / 等待确认
- 回合失败或中断
- 简单直接回答成功后默认折叠隐藏,不显示常驻 `MAIN TASK`。
- 复杂任务也不再插入聊天主区大卡片,改挂输入区底栏弱提示 pill。
- 任务状态 pill 保留:
- 主任务标题
- 当前状态
- 工具批次摘要
- 子任务数量
- 阻塞 / 排队信号
### 3. 工具批次与过程结论
- 复用现有工具批次聚合与工具过程摘要能力。
- 任务状态条优先展示批次级结论,例如:
- 已探索项目
- 已查看关键文件
- 已检查页面
- 当没有工具批次时,状态 pill 回退到等待原因、失败原因或当前阻塞摘要。
### 4. 回归与验收
- 前端单测覆盖:
- 简单直答完成后不显示任务状态条
- 工具批次会显示输入区顶部弱提示条
- 等待输入会显示等待态
- 聊天主区仍保留消息级 `token usage` 与 `prompt cache` 摘要
- 后续 GUI / E2E 继续对齐:
- 打开真实项目后触发分析请求
- 断言复杂任务可见
- 断言简单直答不出现误报任务卡
## 当前进度
- 2026-04-14:创建执行计划文件。
- 2026-04-14:前端新增主任务投影工具与任务卡组件。
- 2026-04-14:先前尝试把主任务直接插入聊天主区,已确认不符合 Claude Code 的任务层级。
- 2026-04-14:主任务展示已回撤出聊天主区,并进一步收敛为输入区底栏弱提示 pill;完成态自动折叠。
- 2026-04-14:补充任务投影、输入区与消息列表的定向回归测试。
- 2026-04-14:定向 `vitest` 已通过,覆盖“复杂任务完成后不继续占据聊天主区,token usage / Prompt Cache 仍由消息结算区承载”。
- 2026-04-14:定向 `eslint` 已通过;`verify:local` 仍被仓库现存无关问题阻塞,当前已知阻塞文件为 `src/components/agent/chat/utils/toolBatchGrouping.ts` 与 `src/components/memory/MemoryPage.tsx`。
- 2026-04-14:`verify:gui-smoke` 已启动并推进到 headless Tauri 最终链接阶段,当前阻塞为首次临时 `CARGO_TARGET_DIR` 的 Rust/Tauri 大编译,尚未拿到 `http://127.0.0.1:3030/health` 就绪结果。
- 2026-04-14:`verify:gui-smoke` 最终通过,确认默认 workspace、browser runtime、site adapter catalog 与 agent service skill entry 主链未回退。
- 2026-04-14:浏览器 E2E 已验证复杂请求运行中只在输入区底栏显示状态 pill;完成后 pill 自动折叠消失,消息尾部继续显示 `token usage` 与缓存统计。
- 2026-04-14:后端已落地“连续只读工具批次并发执行”,当前只对 Bash / PowerShell 的保守只读命令集启用并发,写操作与高风险命令继续串行。
- 2026-04-14:补齐路径预检,避免在分析本地项目时盲猜不存在路径。
- 2026-04-14:定位到 `tauri:dev*` 脚本把 `CARGO_TARGET_DIR` 写死为 `target`,会在 GUI 续测与 Rust 测试并行时放大构建锁竞争;现已改成“默认 target,但允许显式覆盖”。
- 2026-04-14:继续收口“任务分工建议”视觉层级,首页空态与普通输入栏统一改为轻量胶囊提示,不再用大横幅抢占输入区主视觉。
- 2026-04-14:按最新反馈去掉消息正文里的 `阶段结论` 标题壳;新消息直接显示结论正文,旧历史通过前端统一清洗兼容去标题。
- 2026-04-14:继续追查 GUI 续测慢点,确认 `run-tauri-dev.mjs` / `run-tauri-profile.mjs` 会把相对 `CARGO_TARGET_DIR` 传进 `tauri dev`,在当前启动链下误写成 `src-tauri/src-tauri/target`;现已改成仓库根绝对路径下的 `src-tauri/target`,同时同步更新 README 说明。
## 本轮验证记录
### 已完成
- `npx vitest run "src/components/agent/chat/components/Inputbar/index.test.tsx" "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/utils/agentTaskRuntime.test.ts"`
- `npx eslint "src/components/agent/chat/components/Inputbar/index.tsx" "src/components/agent/chat/components/Inputbar/components/InputbarComposerSection.tsx" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/workspace/useWorkspaceInputbarSceneRuntime.tsx" "src/components/agent/chat/utils/agentTaskRuntime.ts"`
### 进行中
- `npm run verify:gui-smoke`
- 当前表现:前端壳 `http://127.0.0.1:1420/` 可访问,但 `3030` DevBridge 尚未监听,页面仍处于 mock/fallback 状态。
- 当前判断:阻塞源是 headless Tauri 首次完整构建与链接,不是任务 pill 回退或聊天主区重新渲染 task 卡片。
- `npm run tauri:dev:headless`
- 当前表现:真实页面发送按钮 disabled 的直接原因不是输入组件,而是 `aster_agent_init` 因 DevBridge 不可达失败,页面落入 bridge cooldown / mock fallback。
- 当前判断:根因之一不是单纯构建锁,而是开发脚本传入的相对 `CARGO_TARGET_DIR` 被 `tauri dev` 解析成 `src-tauri/src-tauri/target`,造成双层 target 与冷编译;脚本已改成绝对路径,正在重新拉起真实 bridge 做二次验证。
### 已完成补充验收
- 真实浏览器会话中已验证:
- 复杂请求运行中,聊天主区展示的是消息与过程结论,不再插入大 task 面板
- 输入区底栏出现紧凑状态 pill,内容包含任务标题、状态与批次摘要
- 任务完成后状态 pill 自动折叠隐藏
- 消息结算区继续显示 `68.0K tokens` 与缓存统计 `缓存 62.3K(读 62.3K / 写 0)`
- 本轮真实交互后控制台未新增 bridge/runtime 错误,仍只剩浏览器模式的 i18n warning
## 默认假设
- 本阶段优先修正产品感知与主路径可见性,不先重写底层 `session/thread/turn/item` 存储结构。
- 媒体 / 插件任务暂不并入统一任务 runtime。
- 真正的后端 `task event` 契约和 `thread_read.task_runs` 字段后续再补,当前先用前端投影收口主体验。
@@ -1,25 +0,0 @@
# 云端套餐与支付边界收口执行计划
## 主目标
把套餐购买、支付、账单、用量明细统一收敛到 `limecore` 用户中心网页;Lime 客户端只保留会话状态、当前套餐、积分余额、待支付提醒和跳转入口。
## 事实源
- `current`:`limecore` control-plane 与 `apps/user-center-web` 的 `/pricing`、`/billing`、`/subscription`、`/credits`
- `current`:Lime 客户端 `useOemCloudAccess` 负责登录态、权益摘要、API Key、支付回跳同步
- `dead`:Lime 客户端内置套餐卡、充值包卡、用量图、账单表、直接创建购买订单的本地商业工作台
## 本轮进度
- 已将 Lime 设置页云端服务面收口为摘要卡 + 用户中心入口。
- 已从客户端设置页移除本地套餐购买、积分充值、用量明细和账单表渲染。
- 已从 `useOemCloudAccess` 返回面移除客户端直接创建套餐/充值订单的处理器。
- 已在 `limecore` 用户中心 `/billing` 补齐用量与账单 tab,使用真实 usage / credits / billing dashboard 和真实 checkout。
- 已将 `limecore` 用户中心 `/pricing` 文案从“模型价格”收敛为“套餐与价格”。
- 已让 Lime 客户端入口直达 `/billing?tab=usage` 与 `/billing?tab=billing`,并让用户中心 tab 与 URL 查询参数同步。
- 已从用户中心主导航移除独立“账单管理”菜单,保留 `/subscription` 直接路由作为旧链接可达页面,主导航收敛到 `/billing`。
## 下一刀
继续验证两个仓库的定向类型检查与回归测试;如果后续发现客户端仍存在套餐购买 UI 文案或旧 testid,应继续按 `dead` 删除,不再迁回客户端。
@@ -1,152 +0,0 @@
# CreoAI Capability Authoring P1A 执行计划
> 状态:P1A 完成,已通过 GUI smoke
> 创建时间:2026-05-05
> 路线图来源:`docs/roadmap/creaoai/README.md`、`docs/roadmap/creaoai/coding-agent-layer.md`、`docs/research/pi-mono-coding-agent/README.md`
> 当前目标:实现 P1A 最小模块,让 Lime 能创建、存储、查看未验证 `Capability Draft`,并证明未验证草案不会进入默认 tool surface、不会自动注册、不会自动执行。
## 主目标
把 CreoAI 的 Coding Agent 启发收敛为 Lime current 主链里的 **Capability Authoring Agent / Skill Forge draft store**:
```text
用户目标
-> capability generation request
-> workspace-local draft store
-> unverified draft manifest
-> frontend API gateway
-> Workspace / Skills 工作台 draft review
-> 后续 P1B / P2 verification gate
```
固定宗旨:
**不是永远限制能力;是永远限制未经验证、未经授权、不可审计的执行。**
## 本轮范围
本轮做:
1. 后端模块化 draft store。
- 独立 Rust domain 模块,负责 manifest、路径 guard、文件清单和状态。
- Tauri command 只做薄适配,不承载业务规则。
2. 前端模块化 API 网关。
- 新增 `src/lib/api/capabilityDrafts.ts`,组件不直接裸 `invoke`。
3. 前端 UI 最小 review surface。
- 优先接到现有 `SkillsWorkspacePage` 或相邻 feature 模块。
- 展示 draft 名称、目标、状态、权限摘要、文件清单和“未验证不可运行”。
4. 测试与 smoke。
- 后端路径 guard / manifest 单测。
- 前端 API normalization 单测。
- UI 状态回归:unverified draft 不显示运行/注册动作。
- 契约检查:前端调用、Rust 注册、治理目录册、mock 保持一致。
本轮不做:
1. 不实现完整 Coding Agent。
2. 不实现 verification gate。
3. 不注册 workspace-local skill。
4. 不接 automation job。
5. 不开放 full shell、依赖安装或外部写操作。
6. 不新增平行 runtime、scheduler、evidence pack。
## 模块边界
### 后端
推荐结构:
```text
src-tauri/src/commands/capability_draft_cmd.rs
src-tauri/src/services/capability_draft_service.rs
```
设计原则:
1. `capability_draft_cmd.rs` 只负责 Tauri 参数 / 返回值适配。
2. `capability_draft_service.rs` 负责业务规则、路径 guard、manifest 读写。
3. 文件事实源暂定 workspace-local:`.lime/capability-drafts/<draft_id>/manifest.json`。
4. 写入范围必须限制在 draft root 内,不允许路径逃逸。
5. 状态首期只允许 `unverified / failed_self_check`,不暴露运行能力。
### 前端
推荐结构:
```text
src/lib/api/capabilityDrafts.ts
src/features/capability-drafts/
domain/
components/
CapabilityDraftPanel.tsx
```
设计原则:
1. API 网关统一封装命令名、参数与 normalization。
2. domain 模块负责状态文案、权限文案和按钮可见性。
3. 组件只做展示与用户操作,不拼接命令参数。
4. UI 作为工作台信息面板,不做独立花哨页面。
5. `unverified` 状态只允许查看、继续修复、丢弃;不允许运行和创建任务。
## 分阶段实施
### P1A-0:盘点与落 plan
- [x] 新增本执行计划。
- [x] 盘点现有 skill scaffold、Skills 工作台、Tauri command 注册、mock 和治理目录册。
- [x] 确认不复用 `create_skill_scaffold_for_app`,新增独立 capability draft 命令,避免未验证 draft 进入 Skill reload 主链。
### P1A-1:后端 draft store
- [x] 新增 manifest 类型和状态枚举。
- [x] 新增 draft root 路径解析与 path escape guard。
- [x] 新增 create/list/get 命令。
- [x] 新增 Rust 单测。
### P1A-2:前端 API 与 domain
- [x] 新增 `capabilityDraftsApi`。
- [x] 新增状态 / 权限 / 可操作性 domain helper。
- [x] 新增 API normalization 单测。
### P1A-3:Workspace UI review
- [x] 在 Skills 工作台或相邻模块加入 draft review panel。
- [x] 展示 `unverified` 隔离语义。
- [x] 补 UI 回归测试。
### P1A-4:试跑与验收
- [x] 用 Rust service 定向测试创建一个只读 draft。
- [x] 确认 manifest 与文件清单写入 draft root。
- [x] 确认 UI 能看到 draft。
- [x] 确认未验证 draft 不进入默认 tool surface。
- [x] 通过 DevBridge 真实调用 `capability_draft_create/list/get`。
- [x] 运行定向测试、`npm run test:contracts`。
- [x] `npm run verify:gui-smoke` 全绿通过。
## 验收标准
1. 能创建一个 `unverified` capability draft。
2. draft manifest 包含目标、来源、权限摘要、文件清单和状态。
3. 路径逃逸被拒绝。
4. 未验证 draft 不可运行、不可注册、不可绑定 automation job。
5. 前后端通过单一 API / command 主链连接。
6. 契约检查通过。
## 执行记录
### 2026-05-05
- 已创建执行计划,固定本轮只做 P1A draft store + review surface,不进入 P2/P3/P4。
- 已完成后端模块:`capability_draft_service` 作为文件事实源,`capability_draft_cmd` 只做 Tauri 薄适配,新增 `capability_draft_create/list/get`。
- 已完成前端模块:`capabilityDraftsApi`、`CapabilityDraftPanel` 与 presentation helper,接入 `SkillsWorkspacePage` 右侧工作台。
- 已同步命令边界:`runner.rs`、DevBridge dispatcher、`agentCommandCatalog.capabilityDraftCommands`、`mockPriorityCommands` 的 bridge-truth 列表、`defaultMocks`。
- 已试跑模块功能:Rust 定向测试真实创建 draft root、写入 `SKILL.md` 与 `manifest.json`、list/get 回读,并覆盖路径逃逸拒绝。
- 已验证:前端定向测试 36 个通过,`cargo test ... capability_draft` 通过,`npm run test:contracts` 通过,`npm run typecheck` 通过,`cargo fmt --check` 通过。
- 已通过 DevBridge smoke:在临时 workspace 下真实调用 `capability_draft_create/list/get`,生成 `unverified` draft,落盘 `SKILL.md` 与 `workflow/README.md`,list/get 回读一致。
- 首次执行 `npm run verify:gui-smoke` 时,前序 `bridge health`、`workspace-ready`、`browser-runtime`、`site-adapters`、`agent-service-skill-entry`、`agent-runtime-tool-surface`、`agent-runtime-tool-surface-page` 已通过;当时失败在既有 `smoke:knowledge-gui` 的“知识库总览加载”等待,页面仍停留首页,未命中本轮新增的 Capability Draft 命令、mock 或 Skills 工作台 review surface。
- 已定向复跑 `npm run smoke:knowledge-gui -- --app-url http://127.0.0.1:1420/ --health-url http://127.0.0.1:3030/health --invoke-url http://127.0.0.1:3030/invoke --timeout-ms 600000 --interval-ms 1000` 并通过;前次失败判断为运行中前端 / Tauri 会话抖动,不需要修改知识库代码。
- 已重跑 `npm run verify:gui-smoke` 并全绿通过,P1A 达到 Lime GUI 产品交付门槛。
@@ -1,162 +0,0 @@
# CreoAI Capability Discovery P3B 执行计划
> 状态:完成
> 创建时间:2026-05-05
> 前置计划:`docs/exec-plans/creaoai-capability-registration-p3-plan.md`
> 路线图来源:`docs/roadmap/creaoai/implementation-plan.md`、`docs/aiprompts/skill-standard.md`、`docs/aiprompts/commands.md`
> 当前目标:让 P3A 已注册到当前 workspace 的 Agent Skill 包可被产品层发现和审计,但仍不进入默认 runtime tool surface。
## 主目标
把 P3A 的文件注册事实推进到最小可见目录闭环:
```text
<workspaceRoot>/.agents/skills/<skill_directory>/
-> require .lime/registration.json provenance
-> inspect Agent Skills package
-> workspace registered skill catalog projection
-> Skills 工作台只读展示
-> 后续 runtime gate / Query Loop binding
```
固定宗旨:
**不是永远限制能力;是永远限制未经验证、未经授权、不可审计的执行。**
## 本轮最小切口
本轮只做 **workspace-local registered skill discovery**:
1. 后端新增 workspace 显式入参的已注册能力发现命令。
2. 只扫描 `<workspaceRoot>/.agents/skills`,不再依赖进程 `cwd`。
3. 只返回带 `.lime/registration.json` 的 P3A 注册能力,不把任意项目 skill 都混进 CreoAI 生成链。
4. 返回 Agent Skills 标准检查摘要、资源摘要、权限摘要、来源 draft / verification report。
5. 前端 Skills 工作台展示“已注册但待运行接入”的只读卡片。
本轮明确不做:
1. 不调用 `AsterAgentState::reload_lime_skills()`。
2. 不修改 `get_local_skills_for_app` 的默认语义。
3. 不把 workspace generated skill 合并进 `useSkills("lime")` 的已安装方法列表。
4. 不展示“立即运行 / 自动化 / 继续这套方法”入口。
5. 不接 `agent_runtime_submit_turn`、Query Loop、`tool_runtime` 或 automation job。
6. 不解决 P4 Managed Objective 续跑。
## 为什么不用现有 local skills 列表直接承接
当前 `SkillService::get_catalog_roots(AppType::Lime)` 依赖:
```text
app_paths::resolve_project_skills_dir()
-> std::env::current_dir()
-> cwd/.agents/skills
```
但 P3A 注册位置是:
```text
<workspaceRoot>/.agents/skills/<skill_directory>
```
因此如果直接扩 `get_local_skills_for_app`,会把“当前前端项目 workspace”与“后端进程 cwd”继续混在一起,还可能把 generated skill 误投进默认运行面。P3B 第一刀先新增独立 discovery 命令,等目录投影、权限和 UI 语义稳定后,再进入 runtime binding。
## 安全规则
1. **显式 workspaceRoot**:入参必须是存在的绝对目录。
2. **registered-only**:目录必须同时包含 `SKILL.md` 与 `.lime/registration.json`。
3. **不跟随 symlink**:扫描到 symlink 目录直接拒绝,避免通过目录投影读取 workspace 外内容。
4. **不执行文件**:只读 `SKILL.md`、registration metadata 和包资源摘要。
5. **不信任元数据路径**:返回真实扫描到的目录,同时保留 registration summary 作为 provenance。
6. **默认不可运行**:返回对象必须显式标记 `launchEnabled=false` 与 runtime gate 提示。
## 实施步骤
### P3B-0:计划与边界
- [x] 新增本执行计划。
- [x] 明确本轮只做 registered skill discovery,不做 runtime binding。
### P3B-1:后端 discovery service
- [x] 新增 `ListWorkspaceRegisteredSkillsRequest`。
- [x] 新增 `WorkspaceRegisteredSkillRecord` DTO。
- [x] 新增 `list_workspace_registered_skills(...)` 服务函数。
- [x] 只扫描 `<workspaceRoot>/.agents/skills`。
- [x] 只返回包含 `.lime/registration.json` 的标准 Skill 包。
- [x] 补 Rust 单测:空目录、无 registration 忽略、注册后可发现、相对 workspaceRoot 拒绝、symlink 逃逸拒绝。
### P3B-2:命令边界
- [x] 新增 Tauri command `capability_draft_list_registered_skills`。
- [x] 同步 `runner.rs`、DevBridge dispatcher。
- [x] 同步 `agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks`。
- [x] 运行 `npm run test:contracts`。
### P3B-3:前端 API / UI
- [x] 扩展 `capabilityDraftsApi.listRegisteredSkills(...)` 与 normalization。
- [x] 新增 Workspace 已注册能力只读面板。
- [x] Skills 工作台在 Capability Draft 隔离区附近展示已注册能力。
- [x] 注册成功后刷新已注册能力面板。
- [x] 补 API、组件、Skills 工作台回归测试。
### P3B-4:试跑与验收
- [x] 用 DevBridge 走 `create -> verify -> register -> list_registered_skills`。
- [x] 确认返回 provenance、标准合规与 `launchEnabled=false`。
- [x] 确认 UI 展示“已注册但待运行接入”,没有运行或自动化按钮。
- [x] 根据 GUI 工作台改动补 `npm run verify:gui-smoke`。
## 验收标准
1. 不存在 `.agents/skills` 时返回空数组。
2. 普通 project skill 没有 `.lime/registration.json` 时不会进入 CreoAI registered 列表。
3. P3A 注册后的 skill 能被显式 workspaceRoot 发现。
4. discovery 结果包含来源 draft、verification report、权限摘要和 Agent Skills 标准状态。
5. discovery 结果显式 `launchEnabled=false`。
6. UI 不出现“立即运行 / 自动化 / 继续这套方法”入口。
7. 命令契约、DevBridge、mock、文档和 GUI smoke 保持一致。
## 执行记录
### 2026-05-05
- 已创建 P3B 执行计划,确认第一刀只补 workspace-local registered skill discovery。
- 已确认 P3B 不复用 `get_local_skills_for_app` 的 cwd 语义,也不把 generated skill 直接混进默认已安装方法列表。
### 2026-05-06
- 已完成后端 `list_workspace_registered_skills(...)`、Tauri command、DevBridge dispatcher、前端 API 网关、默认 mock、治理目录册与 Skills 工作台只读面板。
- 已把 discovery 语义固定为 `workspaceRoot -> .agents/skills -> .lime/registration.json provenance -> read-only projection`,结果显式返回 `launchEnabled=false` 与 runtime gate 文案。
- Rust 默认 feature 定向测试曾因 `local-sensevoice / sherpa-onnx-sys` 冷编译阻塞中止;已改用无语音特性的定向命令补齐:
- `CARGO_TARGET_DIR="src-tauri/target-codex-p3-novoice" cargo test --manifest-path "src-tauri/Cargo.toml" --no-default-features capability_draft`
- 结果:`16` 个 capability draft 测试通过,`1203` 个测试按过滤器跳过。
- 前端定向回归通过:
- `npm test -- "src/lib/api/capabilityDrafts.test.ts" "src/features/capability-drafts/domain/capabilityDraftPresentation.test.ts" "src/features/capability-drafts/components/CapabilityDraftPanel.test.tsx" "src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx" "src/components/skills/SkillsWorkspacePage.test.tsx"`
- 结果:`5` 个文件、`48` 个测试通过。
- 命令契约通过:
- `npm run test:contracts`
- 结果:command contracts、harness contracts、modality runtime contracts 与 cleanup report contract 均通过。
- DevBridge 真实链路通过:
- `capability_draft_create -> capability_draft_verify -> capability_draft_register -> capability_draft_list_registered_skills`
- 临时 workspace:`/tmp/lime-creaoai-p3b-smoke.3KFuJW`
- 结果:发现 `capability-213ea44ef8d9`,`launchEnabled=false`,runtime gate 文案存在。
- GUI smoke 通过:
- `npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`
- 覆盖 DevBridge health、workspace ready、browser runtime、site adapters、service skill entry、runtime tool surface、Knowledge GUI 与 Design Canvas smoke。
## P3B 收口结论
P3B registered discovery 已达到本计划可交付门槛:P3A 注册后的 workspace-local Skill 包可以被当前 workspace 明确发现、审计来源、展示权限与标准检查,但仍不会进入默认 tool surface,也不会暴露运行、自动化或继续执行入口。
下一阶段应单独开计划推进 runtime binding:
```text
registered discovery
-> workspace-scoped catalog binding
-> Query Loop metadata
-> tool_runtime 授权裁剪
-> artifact / evidence 调用记录
```
在这条后续链路完成前,`registered` 与 `discovered` 只能表示“可审计存在”,不能表示“可自动运行”。
@@ -1,146 +0,0 @@
# CreoAI Capability Registration P3 执行计划
> 状态:P3A 完成;已通过 DevBridge 注册链路验证与 GUI smoke
> 创建时间:2026-05-05
> 前置计划:`docs/exec-plans/creaoai-capability-authoring-p1a-plan.md`、`docs/exec-plans/creaoai-capability-verification-p1b-plan.md`
> 路线图来源:`docs/roadmap/creaoai/implementation-plan.md`、`docs/aiprompts/skill-standard.md`
> 当前目标:把已通过 verification gate 的 `Capability Draft` 注册为当前 workspace 的本地 Agent Skill 包,但仍不接运行、不接自动化、不进入默认 tool surface。
## 主目标
把 P1B 的 `verified_pending_registration` 推进到最小可追踪注册闭环:
```text
workspace-local capability draft
-> verified_pending_registration
-> registration gate
-> <workspaceRoot>/.agents/skills/<skill_directory>/
-> draft manifest lastRegistration
-> Skills 工作台 review surface
-> 后续 P3B / P4 runtime binding
```
固定宗旨:
**不是永远限制能力;是永远限制未经验证、未经授权、不可审计的执行。**
## 本轮范围
本轮做:
1. 后端最小 registration gate。
- 新增 `capability_draft_register` 命令。
- 只允许 `verified_pending_registration` 状态进入注册。
- 注册前再次校验 draft 文件清单完整性与 Agent Skills 标准合规。
- 将 draft 生成文件复制到 `<workspaceRoot>/.agents/skills/<skill_directory>/`。
- 写入 draft 侧 `registration/latest.json` 和 manifest `lastRegistration`。
2. 前端 API / domain / UI 接入。
- `capabilityDraftsApi.register(...)` 统一封装命令。
- UI 只在 `verified_pending_registration` 显示“注册到当前 Workspace”。
- 注册后展示目录与来源,不显示“立即运行 / 自动化”。
3. 命令治理与 mock 同步。
- Rust 注册、DevBridge dispatcher、`agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks` 保持一致。
4. 定向验证。
本轮不做:
1. 不调用 `AsterAgentState::reload_lime_skills()`。
2. 不修改全局 seeded skill 或用户级 Lime skill 目录。
3. 不把已注册 skill 自动放进默认 tool surface。
4. 不新增 runtime binding、scheduler、automation job 或 Managed Objective。
5. 不执行 draft 中的脚本,不做 dry-run、shell、依赖安装或外部写操作。
6. 不解决现有 `resolve_project_skills_dir()` 依赖进程 cwd 的完整 catalog 可见性问题;这属于 P3B discovery/runtime binding。
## P3A / P3B 边界
本计划只交付 **P3A:workspace-local file registration**。
```text
P3A: verified draft -> workspace .agents/skills package -> provenance
P3B: workspace catalog discovery -> skill launch metadata -> tool_runtime surface
P4 : managed execution / automation / objective loop
```
这样拆分的原因:
1. registration 是文件与来源事实,不等于可运行能力。
2. catalog discovery 需要解决 workspace 选择、进程 cwd、SkillService root 和 runtime session 的一致性,不能顺手塞进复制文件命令里。
3. runtime binding 必须回到 `agent_runtime_submit_turn -> Query Loop -> tool_runtime -> artifact/evidence` 主链,不能让 `capability_draft_register` 变成第二套执行入口。
## 注册规则
1. **状态前置**:仅允许 `verified_pending_registration`。
2. **标准前置**:`SKILL.md` 必须通过 Agent Skills 标准检查;P1B 的静态 gate 通过不等于标准合规。
3. **路径前置**:只复制 manifest `generatedFiles` 清单内的相对路径,继续拒绝绝对路径、`..`、平台相关路径与 symlink。
4. **目标位置**:只写当前 `workspaceRoot/.agents/skills/<skill_directory>/`。
5. **冲突处理**:目标目录已存在时拒绝,不覆盖、不合并、不删除用户已有目录。
6. **来源记录**:注册摘要必须包含 draft id、verification report id、权限摘要、文件数量、注册时间与目标目录。
7. **可见性限制**:注册完成只表示“workspace 中已有标准 skill 包”,不表示已经进入运行时工具面。
## 实施步骤
### P3-0:计划与边界
- [x] 新增本执行计划。
- [x] 明确本轮只做 P3A registration,不做 P3B discovery/runtime binding。
### P3-1:后端 registration service
- [x] 新增 registration summary / request / result 类型。
- [x] 新增注册目录派生、目标路径 guard、文件复制与 provenance 写入。
- [x] 新增 `register_capability_draft(...)` 服务函数。
- [x] 注册后更新 manifest 状态为 `registered`。
- [x] 补 Rust 单测:未验证拒绝、验证失败拒绝、标准不合规拒绝、标准草案可注册、目标目录冲突拒绝。
### P3-2:命令边界
- [x] 新增 Tauri command `capability_draft_register`。
- [x] 同步 `runner.rs`、DevBridge dispatcher。
- [x] 同步 `agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks`。
- [x] 运行 `npm run test:contracts`。
### P3-3:前端 API / UI
- [x] 扩展 `capabilityDraftsApi.register(...)` 与 normalization。
- [x] 扩展 domain helper:`canRegisterCapabilityDraft`、注册摘要展示。
- [x] 在 `CapabilityDraftPanel` 展示注册按钮和注册目录。
- [x] 补 API、domain、UI 回归测试。
### P3-4:试跑与验收
- [x] 用 DevBridge 创建完整标准 draft,验证通过后注册。
- [x] 通过 Rust service 定向测试确认生成 `<workspaceRoot>/.agents/skills/<skill_directory>/SKILL.md`。
- [x] 通过 Rust service 定向测试确认 manifest 进入 `registered` 并记录 `lastRegistration`。
- [x] 通过前端回归测试确认 UI 仍没有运行或自动化入口。
- [x] 根据改动风险补 `npm run verify:gui-smoke`。
## 验收标准
1. 未验证或验证失败 draft 无法注册。
2. P1B 静态 gate 通过但 Agent Skills 标准不合规的 draft 仍无法注册。
3. 标准合规 draft 能注册到当前 workspace 的 `.agents/skills`。
4. 注册动作不会覆盖已有 skill 目录。
5. 注册后 manifest 和 `registration/latest.json` 能追踪来源、权限和 verification report。
6. UI 能看到注册结果,但没有运行、自动化或外部写入口。
7. 命令契约、mock、文档与 GUI smoke 保持一致。
## 执行记录
### 2026-05-05
- 已创建 P3A 执行计划,明确本轮只做 workspace-local file registration。
- 已确认 `create_skill_scaffold_for_app` 是人工 scaffold 主链,不复用为 draft registration;P3A 使用独立 `capability_draft_register`,避免未验证 / 未授权能力进入 Skill reload 或运行时主链。
- 已完成后端 P3A registration gate:只允许 `verified_pending_registration`,注册前复核 manifest 文件完整性与 Agent Skills 标准,复制到当前 workspace 的 `.agents/skills/<skill_directory>/`,并写入 draft / registered skill 两侧 provenance。
- 已完成命令边界同步:`capability_draft_register` 已接入 Tauri command、`runner.rs`、DevBridge dispatcher、`agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks` 与前端 API 网关。
- 已完成 Skills 工作台最小 UI:只有 `verified_pending_registration` 显示“注册到 Workspace”,注册成功只展示目录与来源提示,仍不展示立即运行、自动化或 runtime binding 入口。
- 校验通过:`cargo fmt --manifest-path src-tauri/Cargo.toml --check`。
- 校验通过:`CARGO_TARGET_DIR=src-tauri/target-codex-p3 cargo test --manifest-path src-tauri/Cargo.toml capability_draft`,11 个 capability draft 定向测试通过。
- 校验通过:`npm test -- src/lib/api/capabilityDrafts.test.ts src/features/capability-drafts/domain/capabilityDraftPresentation.test.ts src/features/capability-drafts/components/CapabilityDraftPanel.test.tsx src/components/skills/SkillsWorkspacePage.test.tsx`,4 个文件 42 个测试通过。
- 校验通过:`npm run test:contracts`,命令契约、Harness 契约、modality runtime contracts 与 cleanup report contract 通过。
- 已做稳健性补强:注册复制使用目标目录独占创建,避免 race 下覆盖或合并已有 workspace skill 目录;manifest 写入失败时同步清理 draft 侧 registration summary,避免留下不可达 provenance。
- GUI smoke 首次尝试时,`smoke:knowledge-gui` 点击旧 `ariaLabel=知识库` 失败,当前导航按钮实际为 `灵感库` / `项目资料` 等;这是 Knowledge 导航 smoke 断言与当前 UI 命名不一致,非 P3A Capability Draft 注册链路改动。
- 已收口 Knowledge GUI smoke 导航断言:`scripts/knowledge-gui-smoke.mjs` 从旧的 `知识库` 导航切到当前 `项目资料` 入口,并更新 Agent 页资料使用文案断言。
- 校验通过:`npm run smoke:knowledge-gui -- --app-url http://127.0.0.1:1420/ --health-url http://127.0.0.1:3030/health --invoke-url http://127.0.0.1:3030/invoke --timeout-ms 300000 --interval-ms 1000`。
- 校验通过:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`,完整 GUI smoke 通过。
- DevBridge 链路验证通过:通过 `capability_draft_create -> capability_draft_verify -> capability_draft_register` 创建标准 draft,验证结果 `passed`,注册后 manifest 状态为 `registered`,并确认生成 `.agents/skills/<skill_directory>/SKILL.md` 与 `.lime/registration.json`。
- 已知非本轮阻塞:`npm run typecheck` 仍受 `src/lib/layered-design/imageTasks.ts` 既有类型问题影响,错误为 `LayeredDesignImageRuntimeContract` 不能赋给 `Record<string, unknown>`;本计划不顺手修 layered-design 旁支。
@@ -1,126 +0,0 @@
# CreoAI Capability Verification P1B 执行计划
> 状态:P1B 完成,已通过 GUI smoke
> 创建时间:2026-05-05
> 前置计划:`docs/exec-plans/creaoai-capability-authoring-p1a-plan.md`
> 路线图来源:`docs/roadmap/creaoai/implementation-plan.md`、`docs/roadmap/creaoai/coding-agent-layer.md`、`docs/roadmap/creaoai/architecture-review.md`
> 当前目标:在 P1A `Capability Draft` 事实源上补最小 verification gate,让草案可以被结构化检查并进入 `verification_failed` 或 `verified_pending_registration`,但仍不注册、不运行、不接自动化。
## 主目标
把 P1A 的“未验证草案可见”推进到“草案可以被门禁检查”:
```text
workspace-local capability draft
-> static verification gate
-> verification report
-> manifest status update
-> Skills 工作台 review surface
-> 后续 P3 registration
```
固定宗旨:
**不是永远限制能力;是永远限制未经验证、未经授权、不可审计的执行。**
## 本轮范围
本轮做:
1. 后端最小 verification gate。
- 新增 `capability_draft_verify` 命令。
- 只做结构、contract、权限声明、危险 token 静态扫描和 fixture 存在性检查。
- 输出 `verification/latest.json` 报告,并同步 manifest 状态。
2. 前端 API / domain / UI 接入。
- `capabilityDraftsApi.verify(...)` 统一封装命令。
- UI 暴露“运行验证”按钮,但不暴露“运行草案 / 注册方法 / 自动化”。
- 展示最近验证摘要与失败建议。
3. 命令治理与 mock 同步。
- Rust 注册、DevBridge、`agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks` 保持一致。
4. 定向验证。
本轮不做:
1. 不执行用户生成脚本。
2. 不开放 shell、安装依赖、联网 dry-run 或外部写操作。
3. 不注册 workspace-local skill。
4. 不把 `verified_pending_registration` 草案放进 tool surface。
5. 不新增 evidence pack 主链,只保留可后续消费的 verification report 文件。
## 最小检查矩阵
| 检查 | 目标 | 失败后状态 |
| ------------------------ | --------------------------------------------------------------------- | --------------------- |
| `package_structure` | `SKILL.md` 存在,manifest 文件清单与磁盘一致 | `verification_failed` |
| `skill_readme_quality` | `SKILL.md` 内容不是空壳,包含可读任务说明 | `verification_failed` |
| `input_contract` | 存在 `contract/input.schema.json` 或等价输入 schema | `verification_failed` |
| `output_contract` | 存在 `contract/output.schema.json` 或等价输出 schema | `verification_failed` |
| `permission_declaration` | 权限摘要非空,并能解释只读 / 草案内写入边界 | `verification_failed` |
| `static_risk_scan` | 未出现删除、发布、付款、依赖安装、任意 shell、HTTP 写操作等危险 token | `verification_failed` |
| `fixture_presence` | 至少存在 `tests/` 或 `examples/` 作为后续 dry-run 输入 | `verification_failed` |
通过后状态只到:
```text
verified_pending_registration
```
它表示“可以进入 P3 注册设计”,不表示现在已经能运行。
## 实施步骤
### P1B-0:计划与边界
- [x] 新增本执行计划。
- [x] 确认 P1B 只做静态 gate,不做注册和执行。
### P1B-1:后端 verification service
- [x] 扩展 draft 状态:`verification_failed / verified_pending_registration`。
- [x] 新增 verification report 类型、summary、check item。
- [x] 新增 `verify_capability_draft(...)` 服务函数。
- [x] 写入 `verification/latest.json` 并更新 manifest。
- [x] 补 Rust 单测:通过、缺 contract 失败、危险 token 失败。
### P1B-2:命令边界
- [x] 新增 Tauri command `capability_draft_verify`。
- [x] 同步 `runner.rs`、DevBridge dispatcher。
- [x] 同步 `agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks`。
- [x] 运行 `npm run test:contracts`。
### P1B-3:前端 API / UI
- [x] 扩展 `capabilityDraftsApi.verify(...)` 与 normalization。
- [x] 扩展 domain helper:状态文案、能否验证、验证摘要。
- [x] 在 `CapabilityDraftPanel` 展示验证按钮与最近结果。
- [x] 补 API、domain、UI 回归测试。
### P1B-4:试跑与验收
- [x] 用 DevBridge 创建一个完整 draft 并验证通过。
- [x] 用 DevBridge 创建一个危险 draft 并验证失败。
- [x] 运行前后端定向测试。
- [x] 根据改动风险补 `npm run verify:gui-smoke` 或记录原因。
## 验收标准
1. 完整草案能进入 `verified_pending_registration`。
2. 缺 input/output contract 的草案会进入 `verification_failed`。
3. 出现危险 token 的草案会进入 `verification_failed`,并给出可修复建议。
4. UI 能触发 verification gate 并刷新状态。
5. 即使验证通过,也没有运行、注册或自动化入口。
6. 命令契约、mock 与文档保持一致。
## 执行记录
### 2026-05-05
- 已完成后端 verification gate:`capability_draft_service` 扩展状态、report、check item 与 `verify_capability_draft(...)`,验证报告落到 `verification/latest.json`,manifest 同步 `lastVerification` 与 `verificationStatus`。
- 已完成命令边界:新增 `capability_draft_verify`,同步 Tauri 注册、DevBridge dispatcher、`agentCommandCatalog.capabilityDraftCommands`、`mockPriorityCommands` 与 `defaultMocks`。
- 已完成前端接入:`capabilityDraftsApi.verify(...)`、状态 / 验证摘要 domain helper、`CapabilityDraftPanel` 的“运行验证”按钮和最近验证摘要;验证通过后仍只显示“待注册”,没有运行、注册或自动化按钮。
- 已通过 Rust 定向测试:`cargo test --manifest-path src-tauri/Cargo.toml capability_draft`,6 个 capability draft 测试通过。
- 已通过前端定向测试:`npm test -- src/lib/api/capabilityDrafts.test.ts src/features/capability-drafts/domain/capabilityDraftPresentation.test.ts src/features/capability-drafts/components/CapabilityDraftPanel.test.tsx src/components/skills/SkillsWorkspacePage.test.tsx`,39 个测试通过。
- 已通过契约与类型检查:`npm run test:contracts`、`npm run typecheck`、`cargo fmt --manifest-path src-tauri/Cargo.toml --check`。
- 已通过 DevBridge smoke:完整草案验证后进入 `verified_pending_registration`;包含 `method: "POST"` 的危险草案验证后进入 `verification_failed`,失败项为 `static_risk_scan`。
- 已通过 GUI smoke:`npm run verify:gui-smoke` 全绿,覆盖 DevBridge、workspace-ready、browser-runtime、site-adapters、agent-service-skill-entry、agent-runtime-tool-surface、agent-runtime-tool-surface-page 与 knowledge-gui。
-135
View File
@@ -1,135 +0,0 @@
# CREAO Roadmap Completion Audit
> 状态:P0-P4 最小闭环完成审计通过
> 日期:2026-05-06
> 审计目标:确认 `docs/roadmap/creaoai/README.md` 与 `docs/roadmap/creaoai/implementation-plan.md` 中 CREAO-inspired 开发计划,已经收敛到 Lime current 主链,并具备完整、可验证、不过度扩展的 P0-P4 最小实现。
## 0. 审计结论
P0-P4 当前可以判定为 **最小可交付闭环完成**:
```text
Capability Draft
-> verification gate
-> workspace-local registration
-> registered discovery
-> runtime binding readiness
-> Query Loop metadata
-> session-scoped tool_runtime authorization
-> ToolResult source metadata
-> Managed Job 草案
-> automation owner evidence
-> completion audit input / summary
-> Harness UI / Workspace UI
-> evidence-gated Agent envelope / derived Agent card
-> workspace/team registered discovery sharing
```
关键判定:
1. 没有新增平行 runtime、scheduler、queue、evidence、Marketplace 或 Agent card 存储表。
2. 未验证 draft 不会进入默认 tool surface,也不会注册、运行或自动化。
3. registered / discovered / readiness 只表示可审计存在和候选资格,不等于可调用。
4. 真正调用必须经 `agent_runtime_submit_turn` + `workspace_skill_runtime_enable` + `SkillTool` session allowlist。
5. `success` automation run 只能进入 completion audit input;`completed` 只能由 automation owner、Workspace Skill ToolCall 和 artifact / timeline evidence 共同判定。
6. Agent envelope 是 Workspace 产品组合面,执行 owner 仍是 automation job / Managed Objective / runtime evidence 主链。
7. 本轮额外修正了一个审计缺口:`evidencePackId` 单独存在不再让 Agent envelope 进入 `evidence_ready`,必须有 completed completion audit 且三项 evidence 齐全。
## 1. P0 文档与边界
| 要求 | 证据 | 状态 | 备注 |
| --- | --- | --- | --- |
| 研究和路线图落盘 | `docs/research/creaoai/README.md`、`docs/roadmap/creaoai/README.md`、`docs/roadmap/creaoai/implementation-plan.md` | 完成 | 已固定 CREAO pivot、组织 harness、Agent 产品模型和 Lime 差距。 |
| Skill Forge 不是 runtime | `docs/roadmap/creaoai/README.md`、`docs/roadmap/creaoai/coding-agent-layer.md`、`docs/roadmap/creaoai/architecture-review.md` | 完成 | 文档明确 build-time capability author 与 runtime owner 分离。 |
| generated capability 不能长期执行 | `docs/roadmap/creaoai/README.md`、`docs/exec-plans/creaoai-capability-authoring-p1a-plan.md` | 完成 | Draft 只能进入 verification / registration gate。 |
| 禁止 generated tools 平行 runtime | `docs/roadmap/creaoai/implementation-plan.md`、`docs/exec-plans/creaoai-managed-agent-envelope-p4-plan.md` | 完成 | P3E / P4 都回到 `agent_runtime_submit_turn`、automation、evidence 主链。 |
## 2. P1 workspace-local skill scaffold
| 要求 | 证据 | 状态 | 备注 |
| --- | --- | --- | --- |
| 创建 workspace-local Capability Draft | `src-tauri/src/services/capability_draft_service.rs:create_capability_draft`、`src-tauri/src/commands/capability_draft_cmd.rs`、`src/lib/api/capabilityDrafts.ts` | 完成 | 文件事实源为 `.lime/capability-drafts/<draft_id>/manifest.json`。 |
| Draft 包含 `SKILL.md`、manifest、文件清单和权限摘要 | `CapabilityDraftManifest` / `CapabilityDraftRecord`、`create_capability_draft` 单测 | 完成 | P1A 创建时写入 draft root,路径 guard 拒绝逃逸。 |
| Workspace 可展示 draft 状态 | `src/features/capability-drafts/components/CapabilityDraftPanel.tsx`、`src/components/skills/SkillsWorkspacePage.tsx` | 完成 | UI 明确“未验证前不会注册,也不会自动运行”。 |
| 未验证 draft 不进默认 tool surface | `CapabilityDraftPanel` domain helper、P1A / P1B / P3A 执行计划验证记录 | 完成 | UI 无运行 / 自动化入口;后端注册也拒绝未验证状态。 |
| 从对话请求创建的边界 | `capability_draft_create` 已进入 command catalog / DevBridge / mock;当前产品入口仍是受控 draft store | 完成(最小闭环) | 未实现无限制 autonomous authoring agent;符合 P1A “不做完整 Coding Agent、先证明安全产生能力”的约束。 |
## 3. P2 verification gate
| 要求 | 证据 | 状态 | 备注 |
| --- | --- | --- | --- |
| 结构、contract、权限、风险、fixture 检查 | `src-tauri/src/services/capability_draft_service.rs:verify_capability_draft` | 完成 | 检查矩阵落为静态 gate,不执行用户脚本。 |
| 缺 contract 不能注册 | `verify_capability_draft_fails_without_contracts`、`register_capability_draft_rejects_verification_failed_draft` | 完成 | 状态进入 `verification_failed`。 |
| 危险 token / 权限不一致失败 | `verify_capability_draft_rejects_dangerous_tokens` | 完成 | 高风险外部写通过静态风险扫描阻断;后续放权必须走授权策略。 |
| 通过后进入 pending registration | `CapabilityDraftStatus::VerifiedPendingRegistration`、`verify_capability_draft_marks_complete_draft_pending_registration` | 完成 | 仍不代表可运行。 |
| verification 结果可消费 | `verification/latest.json`、manifest `lastVerification`、注册 provenance | 完成 | P3A 注册写入 verification report id。 |
## 4. P3 registration / runtime binding
| 阶段 | 要求 | 证据 | 状态 |
| --- | --- | --- | --- |
| P3A | 只注册 `verified_pending_registration` 到当前 workspace `.agents/skills` | `register_capability_draft`、`registration/latest.json`、`.lime/registration.json` | 完成 |
| P3A | 不覆盖已有目录、不修改全局 seeded skill、不运行 | `register_capability_draft_rejects_existing_skill_directory`、P3A plan 验证记录 | 完成 |
| P3B | 显式 `workspaceRoot` discovery,只读返回 provenance / 标准 / 权限 | `list_workspace_registered_skills`、`WorkspaceRegisteredSkillsPanel` | 完成 |
| P3B | `launchEnabled=false`,无运行 / 自动化入口 | `WorkspaceRegisteredSkillRecord.launchEnabled`、P3B tests / GUI smoke | 完成 |
| P3C | readiness projection 在 `agent_runtime_* / inventory` 主链下 | `agent_runtime_list_workspace_skill_bindings`、`runtime_skill_binding_service.rs:list_workspace_skill_bindings` | 完成 |
| P3C | `queryLoopVisible=false`、`toolRuntimeVisible=false`、`launchEnabled=false` | `runtime_skill_binding_service` DTO / tests | 完成 |
| P3D | `workspace_skill_bindings` 只进 Query Loop 只读 prompt | `workspace_skill_binding_prompt.rs`、`workspaceSkillBindingsMetadata.ts` | 完成 |
| P3D | 不打开 `allow_model_skills`,不注入 `SkillTool` registry | `harnessRequestMetadata.ts`、`runtime_turn.rs` tests | 完成 |
| P3E | `workspace_skill_runtime_enable` 显式启用当前 session | `runtime_skill_binding_service.rs:resolve_workspace_skill_runtime_enable`、`runtime_turn.rs` | 完成 |
| P3E | `SkillTool` 被裁剪到 `project:<directory>` / `<directory>` allowlist | `src-tauri/crates/agent/src/tools/skill_tool_gate.rs` | 完成 |
| P3E | ToolResult metadata 写回来源和授权 | `workspace_skill_source` / `workspace_skill_runtime_enable` metadata、SkillTool gate tests | 完成 |
## 5. P4 managed execution / Agent envelope
| 要求 | 证据 | 状态 | 备注 |
| --- | --- | --- | --- |
| 绑定 automation job,不新增 scheduler | `workspaceSkillAgentAutomationDraft.ts`、`SkillsWorkspacePage.tsx` | 完成 | 创建入口复用 `AutomationJobDialog` + `createAutomationJob`,默认暂停。 |
| payload 仍为 `agent_turn` | `buildWorkspaceSkillAgentAutomationInitialValues` | 完成 | `request_metadata.harness` 写入 `agent_envelope`、`managed_objective`、`workspace_skill_runtime_enable`。 |
| 支持暂停 / 恢复 | `WorkspaceRegisteredSkillsPanel.tsx` 调用 `updateAutomationJob(job.id, { enabled })` | 完成 | 不新增平行 pause state。 |
| app 重启后状态恢复或阻塞 | `WorkspaceRegisteredSkillsPanel` 每次加载复用 `getAutomationJobs()` 读取持久 job 事实 | 完成 | 恢复以 automation job storage 为事实源;失败显示 last_status / last_error。 |
| 失败可见步骤 / 原因 / 下一步 | `buildWorkspaceSkillManagedAutomationPresentation`、completion audit label | 完成 | Workspace 显示 blocked / paused / planned / verifying 文案;Harness 显示 audit blocking reasons。 |
| 产物 / timeline / evidence 可审计 | `runtime_evidence_pack_service.rs` | 完成 | `timeline.json` 保留 Workspace Skill ToolCall source metadata。 |
| automation owner evidence | `export_runtime_evidence_pack_with_owner_runs`、`runtime.json` / `artifacts.json` 的 `automationOwners` | 完成 | owner run、Agent envelope、Managed Objective、runtime enable 关系进入 evidence。 |
| completion audit input | `automationOwners.runs[].completionAudit` | 完成 | `success` run 仍为 `not_completed` 输入。 |
| completion audit summary | `build_completion_audit_summary_json`、`completionAuditSummary` normalizer / Harness UI | 完成 | `completed / blocked / needs_input / verifying` 由 evidence 判定。 |
| Agent envelope evidence gate | `agentEnvelopeDraftPresentation.ts` | 完成 | 只有 `completionAuditSummary.decision=completed` 且三项 evidence 齐全才 actionEnabled。 |
| 最近运行审计 | `WorkspaceRegisteredSkillsPanel.tsx` 调用 `getAutomationRunHistory` + `exportAgentRuntimeEvidencePack` | 完成 | 不新增 evidence 查询命令。 |
| Agent card / sharing | `agentEnvelopeDraftPresentation.ts`、Workspace panel tests | 完成 | `workspace-local/<skill-directory>` 派生展示;共享限定 workspace / team。 |
## 6. 验证证据
已记录通过的关键验证:
1. P1A:`capability_draft_create/list/get` Rust / frontend / DevBridge / `npm run verify:gui-smoke`。
2. P1B:`capability_draft_verify` Rust / frontend / DevBridge / `npm run test:contracts` / `npm run verify:gui-smoke`。
3. P3A:`capability_draft_register` Rust / frontend / DevBridge / `npm run test:contracts` / `npm run verify:gui-smoke`。
4. P3B:registered discovery Rust / frontend / DevBridge / `npm run test:contracts` / `npm run verify:gui-smoke`。
5. P3C:runtime binding readiness Rust / frontend / `npm run typecheck` / `npm run test:contracts` / GUI smoke。
6. P3D:workspace skill metadata prompt projection Rust / TS 定向测试,且文档边界已同步。
7. P3E:workspace skill runtime enable metadata、SkillTool allowlist/source metadata、runtime turn 定向测试、`npm run test:contracts`。
8. P4:Agent envelope presentation / Workspace panel / Skills workspace / Harness panel / Rust evidence pack 定向测试、`npm run typecheck`、`npm run test:contracts`、GUI smoke。
9. 本审计轮复跑:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`,12 passed。
## 7. 非目标确认
以下仍保持未做,且是正确边界:
1. 未新增 public Marketplace / Skill Store。
2. 未新增 `agent_envelope_*` command 或 Agent card 存储表。
3. 未新增独立 scheduler / queue / generated tool runtime / evidence 系统。
4. 未允许未验证 draft 进入默认 tool surface。
5. 未允许外部写操作在无人工确认时自动执行。
6. 未把 `workspace_skill_bindings` readiness metadata 自动升级为可调用工具。
7. 未把 automation `success` 直接判定为 Managed Objective `completed`。
## 8. 收口判定
P0-P4 的 CREAO-inspired 最小开发计划已经完成;后续如果继续推进,应作为新阶段处理:
1. P5:真实 prompt-to-artifact 产品 E2E 场景,把“只读 CLI 每日报告”跑成完整演示数据集。
2. P5:外部写操作的人类确认策略和 policy-approved scheduled write。
3. P5:多 skill managed workflow 与 team-scoped sharing 的权限模型。
4. P5:proactive agentization 信号,基于 rerun 频率、阻塞原因和修复次数建议固化 Agent。
当前 P0-P4 不需要继续补平行实现;下一步应只做验证样例或 P5 扩展,而不是扩大 P4 的 runtime 面。
@@ -1,299 +0,0 @@
# CREAO Managed Execution / Agent Envelope P4 执行计划
> 状态:P4 完成;P0-P4 完成审计通过
> 创建时间:2026-05-06
> 前置计划:`docs/exec-plans/creaoai-tool-runtime-authorization-p3e-plan.md`
> 路线图来源:`docs/roadmap/creaoai/README.md`、`docs/roadmap/creaoai/implementation-plan.md`、`docs/roadmap/managed-objective/README.md`、`docs/aiprompts/quality-workflow.md`
> 当前目标:把 P3E 已可显式启用并可审计调用的 workspace-local skill,推进到“成功运行后可固化为 Workspace 产品面的 Agent envelope 草案”,但不新增 runtime、scheduler 或 marketplace。
## 主目标
P4 第一刀只回答:
```text
一个 P3E 显式启用并成功运行过的 workspace-local skill
-> 如何被展示成可 rerun 的 Agent envelope 草案
-> 如何引用来源 draft、verification report、registered directory、session 授权和 evidence
-> 如何为后续 managed execution / schedule 预留明确边界
```
固定边界:
**Agent envelope 是 Workspace 产品组合面,不是新 runtime;执行仍然回到 `agent_runtime_submit_turn`、automation job、Managed Objective、artifact 和 evidence 主链。**
## 本轮最小切口
第一刀不直接做完整定时任务。先做可验证的 envelope 草案展示与证据消费边界:
1. 定义前端 `AgentEnvelopeDraft` presentation contract,来源必须是 P3A/P3B registered skill、P3C binding、P3E runtime source metadata 或已导出的 evidence pack。
2. 在 Workspace 已注册能力面板中展示 “Agent envelope 草案 / 可固化条件” 区域,只对 ready binding 说明下一步,不创建自动化。
3. 已有 P3E source metadata 时,草案摘要必须能显示 source draft、verification report、registered directory、permission summary 和 session authorization scope。
4. “转成 Agent” 第一刀只做草案入口或 disabled-ready state,不创建 scheduler、不写长期 job、不绕过 `agent_runtime_submit_turn`。
5. 补组件 / presentation 单测,证明 P4 入口不会在未 ready、未 evidence 或 blocked 状态下声称已可自动化。
## 本轮明确不做
1. 不新增 `agent_envelope_*` Tauri 命令。
2. 不新增 scheduler、queue、automation job 存储或后台 runner。
3. 不新增 Agent Marketplace / Skill Store。
4. 不把 P3B / P3C readiness 当成已成功运行。
5. 不把 `workspace_skill_runtime_enable` 升级成长期授权。
6. 不允许模型自报完成后直接创建 Agent;必须引用 artifact / timeline / evidence 或后续 completion audit。
## 最小 Agent envelope 字段
```ts
interface AgentEnvelopeDraft {
id: string;
name: string;
sourceSkill: {
directory: string;
registeredSkillDirectory: string;
sourceDraftId: string;
sourceVerificationReportId?: string | null;
};
runbook: {
skillName: string;
permissionSummary: string[];
};
permission: {
authorizationScope: "session" | "manual" | "scheduled";
externalWriteRequiresConfirmation: boolean;
};
evidence: {
status: "missing" | "source_metadata_only" | "evidence_pack_ready";
sourceMetadata?: unknown;
evidencePackId?: string;
};
schedule: {
status: "manual_only" | "draft" | "scheduled";
};
}
```
第一刀可以只落 presentation 层;后续若需要持久化,必须先回到 Managed Objective / automation job 主链设计,不新增平行实体。
## 第二刀最小切口
第二刀开始接入 managed execution,但只复用现有 automation job,不新增 scheduler / runtime:
1. 对 `ready_for_manual_enable` 的 workspace skill,允许从 Workspace 已注册能力面板打开 “Managed Job 草案”。
2. 草案默认 `enabled=false`,用户需要在现有持续流程弹窗里确认调度、权限和输出后再启用。
3. automation payload 仍是 `agent_turn`,执行时继续走 `agent_runtime_submit_turn` / runtime queue。
4. `request_metadata.harness` 必须携带:
- `agent_envelope`:source draft、verification report、registered skill directory、skill name 与 scheduled session authorization scope。
- `managed_objective`:owner type 为 `automation_job`,completion audit 要求 artifact / timeline / evidence。
- `workspace_skill_runtime_enable`:source 为 `agent_envelope_scheduled_run`,每次 automation run 仍在当前 session 内显式打开 allowlist。
5. blocked / 缺少 verification provenance / 缺少 workspace root 的 skill 不能生成 Managed Job 草案。
## 实施步骤
### P4-0:计划与边界
- [x] 新增本执行计划。
- [x] 明确 P4 第一刀是 Agent envelope 草案展示和 evidence 消费边界,不做 scheduler / marketplace / 新 runtime。
### P4-1:Presentation contract
- [x] 新增最小 `AgentEnvelopeDraft` presentation builder。
- [x] 输入优先级固定为:P3E ToolResult source metadata / evidence pack > P3C ready binding > P3B registered skill。
- [x] 缺少 P3E source metadata 或 evidence 时,状态只能是 `source_metadata_only` 或 `missing`,不能显示为可自动化。
- [x] 补单测覆盖 ready、blocked、missing evidence、source metadata 四类状态。
### P4-2:Workspace UI 第一刀
- [x] 在 `WorkspaceRegisteredSkillsPanel` 中展示 Agent envelope 草案摘要。
- [x] ready binding 只显示“可在成功运行后固化为 Agent”,blocked binding 显示阻塞原因。
- [x] “转成 Agent” 入口第一刀只允许 disabled / draft explanation,不创建 automation job。
- [x] 保留 P3E “本回合启用”作为唯一真实运行入口。
### P4-3:Evidence 消费边界
- [x] 明确 timeline / evidence pack 读取 `workspace_skill_source` / `workspace_skill_runtime_enable` 的字段映射。
- [x] 先做 presentation 级消费;同时确认 `timeline.json` 原先未保留 ToolCall source metadata,因此补 evidence exporter 最小透传。
- [x] 避免 UI 伪造证据:P4 Agent envelope 草案只读取 P3E source metadata / evidence pack;timeline 不存在字段时仍保持 `missing`。
### P4-4:验证
- [x] 前端 presentation / component 定向测试。
- [x] `npm run test:contracts` 只在触碰命令 / bridge / mock 时补跑;P4 第一刀未新增命令,沿用 P3E 已通过结果。
- [x] Workspace 可见 UI 改动已补 `npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`。
### P4-5:Managed Job 草案与 owner metadata
- [x] 新增 `workspaceSkillAgentAutomationDraft` helper,生成现有 `AutomationJobDialog` 可消费的 initial values。
- [x] request metadata 固定写入 `harness.agent_envelope`、`harness.managed_objective` 与 `harness.workspace_skill_runtime_enable`,不新增命令或 runtime。
- [x] Skills 工作台的 Workspace 已注册能力面板增加 “创建 Managed Job 草案”入口,只对 ready binding + verification provenance 可用。
- [x] 创建确认继续复用现有 `AutomationJobDialog` + `createAutomationJob`;默认暂停,避免注册后自动长期运行。
- [x] 补 helper / panel / Skills 工作台定向测试。
### P4-6:Automation owner evidence
- [x] `agent_runtime_export_evidence_pack` 导出前会查询当前 session 关联的 `agent_runs`,把 automation owner runs 注入 evidence pack。
- [x] `runtime.json` 与 `artifacts.json` 新增 `automationOwners`,保留 automation job id、状态、`agent_envelope`、`managed_objective` 与 `workspace_skill_runtime_enable`。
- [x] 该证据仍来自 `agent_runs.metadata` 与 runtime evidence pack,不新增 evidence 事实源。
- [x] Rust 定向测试覆盖 automation owner -> Agent envelope -> P3E runtime enable 的导出关系。
### P4-7:Workspace managed job 状态投影
- [x] Workspace 已注册能力面板读取现有 `automation_job` 列表,不新增查询命令。
- [x] 通过 automation payload 的 `request_metadata.harness.agent_envelope` 反查绑定的 workspace skill directory / skill name。
- [x] Agent envelope 草案区域展示 Managed Job 是否已创建、暂停/启用状态、调度摘要、最近运行与错误摘要。
- [x] 状态投影仅显示现有 automation job 事实,不把 registered skill 误报为已运行或已完成。
### P4-8:Pause / resume 最小闭环
- [x] Workspace 已注册能力面板对已匹配的 Managed Job 展示暂停 / 恢复操作。
- [x] 操作复用既有 `updateAutomationJob(job.id, { enabled })`,不新增命令、不新增 scheduler。
- [x] 成功后以返回的 automation job record 更新本地投影,继续以 `enabled` 作为暂停 / 恢复事实源。
- [x] 前端回归覆盖恢复按钮调用 `updateAutomationJob` 并刷新状态摘要。
### P4-9:Managed Objective 状态 / audit 投影
- [x] Workspace managed job 状态区新增 Managed Objective 最小状态投影:`planned` / `paused` / `running` / `blocked` / `verifying`。
- [x] `last_status=success` 只进入 `verifying`,不会直接标为 `completed`。
- [x] Completion Audit 文案明确要求 artifact / timeline / evidence 审计,避免模型自报完成。
- [x] 前端 presentation 单测覆盖 success run 不直接 completed。
### P4-10:Evidence completion audit input
- [x] Evidence pack 的 `automationOwners.runs[]` 新增 `completionAudit` 结构化输入。
- [x] `completionAudit` 会检查 automation run status、`agent_envelope`、`managed_objective`、`workspace_skill_runtime_enable` 与 `managed_objective.completion_audit`。
- [x] 即使 run status 为 `success`,`completionDecision` 仍保持 `not_completed`;真正 completed 必须由后续 artifact / timeline / evidence audit 产生。
- [x] Rust evidence 定向测试覆盖 `audit_input_ready` 与 `not_completed`。
### P4-11:Evidence completion audit summary
- [x] `runtime.json` / `artifacts.json` 新增 `completionAuditSummary`,统一输出 `completed / blocked / needs_input / verifying`。
- [x] `completed` 只允许在 automation owner success、workspace skill ToolCall source metadata、artifact / timeline evidence 同时满足时出现。
- [x] 非 success owner run、缺失 Agent envelope / Managed Objective / runtime enable、缺少 workspace skill ToolCall evidence 时分别落到 `blocked`、`needs_input` 或 `verifying`。
- [x] Rust evidence 定向测试覆盖 evidence 齐全时 summary 才输出 `completed`。
- [x] `summary.md` 新增 Completion Audit 摘要,让人类先读入口也能看到 decision、owner / ToolCall / artifact evidence 和 blocking reasons。
### P4-12:Evidence export UI projection
- [x] `RuntimeEvidencePackExportResult` 新增 `completionAuditSummary`,让导出命令响应也携带 evidence-based completion audit。
- [x] 前端 `AgentRuntimeEvidencePack` 类型和 normalizer 接入 `completion_audit_summary`,兼容 camelCase / snake_case。
- [x] Harness 面板导出问题证据包后展示 Completion Audit 卡片,包含 decision、owner success、Skill ToolCall、artifact evidence 与 blocking reasons。
- [x] 前端 API 与 Harness 面板回归覆盖 completion audit summary 投影。
### P4-13:Agent envelope completion audit gate
- [x] `AgentEnvelopeDraftPresentation` 接入 `completionAuditSummary` 作为 evidence-ready 的结构化输入。
- [x] 只有 `decision=completed` 且 automation owner / workspace skill ToolCall / artifact-or-timeline 三项 evidence 全为 true 时才进入 `evidence_ready`。
- [x] `verifying` 或缺 ToolCall evidence 不会误报为可固化 Agent envelope。
- [x] Presentation 单测覆盖 completed 正向与 verifying 负向 gate。
### P4-14:Workspace Agent envelope evidence-gated action
- [x] `WorkspaceRegisteredSkillsPanel` 新增 `completionAuditSummariesByDirectory` 注入边界,不新增命令、不读取平行 runtime。
- [x] 当指定 skill 的 completion audit 已 `completed` 且证据齐全时,“转成 Agent 草案”入口启用。
- [x] 入口复用既有 `onCreateManagedAutomationDraft(binding)` / Managed Job 草案链,不新增 Agent envelope 存储或 scheduler。
- [x] Workspace 组件回归覆盖 completed audit 打开入口并传回对应 binding。
### P4-15:Workspace recent run audit action
- [x] 匹配到 Managed Job 后,Workspace 已注册能力面板展示“审计最近运行”。
- [x] 点击后复用既有 `get_automation_run_history(job.id, 5)` 查最近 automation run,不新增查询命令。
- [x] 找到 run `session_id` 后复用 `agent_runtime_export_evidence_pack(sessionId)` 获取 `completionAuditSummary`。
- [x] audit summary 回填到当前 skill directory,并驱动 Agent envelope evidence gate / “转成 Agent 草案”入口。
### P4-16:Agent envelope card composition
- [x] Agent envelope presentation 补齐 `Memory` 与 `Widget` 摘要。
- [x] Workspace 已注册能力面板展示 Runbook、Memory、Widget、Permission、Schedule、Evidence 六块组成。
- [x] Memory 只引用 verification report 与后续运行修正,不新增独立 memory runtime。
- [x] Widget 只展示 Managed Job 状态、最近产物、审计结论和下一步动作,不新增执行实体。
### P4-17:Derived Agent card / workspace sharing
- [x] Agent card 采用派生展示:`workspace-local/<skill-directory>` 来自 registered skill + Managed Job + completion audit。
- [x] 未完成 audit 时只显示草案等待态,不创建平行持久化实体。
- [x] sharing 范围先限定当前 workspace / team,不进入 public Marketplace。
- [x] Workspace 回归覆盖 completed audit 后展示 derived Agent card 与 sharing 摘要。
### P4-18:Workspace/team sharing discovery boundary
- [x] Agent envelope presentation 展示 registered skill discovery 路径:`.agents/skills/<skill-directory>`。
- [x] 同 workspace 成员通过既有 registered skill discovery 发现 Agent card 来源,不新增 sharing 命令。
- [x] 共享复用同一 Managed Job / evidence 事实源,不复制 automation job 或 evidence。
- [x] 前端回归覆盖 sharing discovery 文案。
## 验收标准
1. blocked 或未 ready 的 registered skill 不出现可固化为 Agent 的积极入口。
2. ready binding 可以看到 Agent envelope 草案组成:Skill / permission / schedule / evidence。
3. UI 明确说明“成功运行后固化”,不能把注册、发现或 readiness 说成已经运行成功。
4. “转成 Agent” 第一刀不创建 automation job、不新增 scheduler、不写长期授权。
5. source draft、verification report、registered directory 与 session 授权范围能够从 P3E metadata / evidence 进入草案摘要。
6. 组件测试覆盖 P4 入口不会破坏 P3E “本回合启用”的唯一真实运行入口。
7. Managed Job 草案只能复用现有 automation job,payload 必须仍是 `agent_turn`,且每次运行都通过 `workspace_skill_runtime_enable` 做 session-scoped allowlist。
8. Evidence pack 必须能导出 automation owner 与 Agent envelope / Managed Objective / workspace skill runtime enable 的关系,不能只靠前端草案说明。
9. Workspace 面板必须能从已有 automation job 反投影 Managed Job 状态,展示下次运行 / 最近运行,而不是只提供创建入口。
10. 暂停 / 恢复必须只修改 automation job 的 `enabled` 状态,不允许创建平行 pause state。
11. `success` run 只能作为 completion audit 输入,不能直接把 Managed Objective 判为 completed。
12. Evidence pack 必须显式导出 completion audit input,作为后续 completed / blocked / needs_input 判定的唯一输入之一。
13. Evidence pack 必须显式导出 completion audit summary,且 `completed` 只能由 automation owner、workspace skill ToolCall 和 artifact / timeline 证据共同判定。
14. `summary.md` 必须能直接展示 completion audit 结论和阻塞原因,不能要求用户只靠 JSON 手工定位。
15. 导出问题证据包后的 UI 必须展示 completion audit summary,避免用户只能打开落盘文件才能知道 Managed Objective 是否完成。
16. Agent envelope presentation 只能在 completion audit `completed` 且必要 evidence 齐全时进入 evidence-ready,不能把 `verifying` 或缺证据状态当成可固化。
17. Workspace 的“转成 Agent 草案”只能由 completed completion audit 打开,并且必须复用现有 Managed Job 草案创建链。
18. Workspace 的最近运行审计必须复用 `get_automation_run_history` 和 `agent_runtime_export_evidence_pack`,不能新增平行 evidence 查询或 runtime。
19. Agent envelope 草案必须展示 Runbook、Memory、Widget、Permission、Schedule、Evidence 六块组成,且 Memory / Widget 只能是产品组合面摘要。
20. Agent card 首期只能作为 registered skill + Managed Job + completion audit 的派生卡片,不新增存储表;sharing 先限定 workspace / team。
21. Workspace/team sharing 只能复用 registered skill discovery、Managed Job 与 evidence 事实源,不能新增 sharing 命令、Marketplace 或复制执行实体。
## 执行记录
### 2026-05-06
- 已从 P3E 收口进入 P4,确认第一刀应消费 `workspace_skill_source` / `workspace_skill_runtime_enable`,而不是新增平行执行命令。
- 已确认现有 `WorkspaceRegisteredSkillsPanel` 仍保留 P3C / P3E 边界:只对 `ready_for_manual_enable` 展示“本回合启用”,并明确不创建自动化。
- 已新增 `agentEnvelopeDraftPresentation` presentation builder:ready binding 默认是 `manual_enable_required`,blocked binding 是 `blocked`,P3E source metadata 是 `source_metadata_ready`,evidence pack 是 `evidence_ready`;所有状态第一刀都不创建长期任务。
- 已接入 `WorkspaceRegisteredSkillsPanel`:每个 registered skill 展示 Agent envelope 草案的 runbook、permission、manual rerun schedule 与 evidence 状态;P3E “本回合启用”仍是唯一真实运行入口。
- 定向验证已通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx src/components/skills/SkillsWorkspacePage.test.tsx -t "应在我的方法工作台展示 Workspace 已注册能力|buildAgentEnvelopeDraftPresentation|WorkspaceRegisteredSkillsPanel"`(3 files,9 passed / 29 skipped)。
- TypeScript 校验已通过:`npm run typecheck`。
- 全量 `src/components/skills/SkillsWorkspacePage.test.tsx` 当前仍有既有文案迁移断言失败(例如“我的方法” vs “我的 Skills”、“你来给”前缀等),与本 P4 第一刀无直接关系;本轮只修正了 P4 新增 disabled action 与 P3E enable button 的选择器歧义。
- 已补 evidence pack timeline 最小透传:`timeline.json` 的 ToolCall item 会在存在 P3E metadata 时写出 `workspaceSkillToolCall.workspaceSkillSource` 与 `workspaceSkillToolCall.workspaceSkillRuntimeEnable`,让 Agent envelope 可追踪 source draft、verification report、registered directory 与 session 授权范围。
- Rust evidence 定向验证已通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p4-agent-envelope-target cargo test --manifest-path src-tauri/Cargo.toml -p lime --lib timeline_should_preserve_workspace_skill_source_metadata_for_agent_envelope`(1 passed)。
- GUI smoke 已通过:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`。首次 smoke 曾暴露 Skills 页面文案断言与 `react-syntax-highlighter` / `refractor` ESM 测试环境问题;已把断言对齐当前 UI,并在相关测试中 mock Markdown syntax highlighter,复跑通过 workspace ready、browser runtime、site adapters、service skill entry、runtime tool surface、runtime tool surface page、Knowledge GUI 与 Design Canvas。
- 已新增 Managed Job 草案入口:`workspaceSkillAgentAutomationDraft` 会为 ready binding 生成 `AutomationJobDialogInitialValues`,并把 `agent_envelope` / `managed_objective` / `workspace_skill_runtime_enable` 写入 automation payload 的 `request_metadata.harness`。
- 已接入 `SkillsWorkspacePage`:Workspace 已注册能力面板可打开现有持续流程弹窗,提交后调用既有 `createAutomationJob`;默认 `enabled=false`,不绕过用户确认和 automation 主链。
- 定向验证已通过:`npx vitest run src/features/capability-drafts/workspaceSkillAgentAutomationDraft.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`(7 passed)。
- Skills 工作台回归已通过:`npx vitest run src/components/skills/SkillsWorkspacePage.test.tsx`(30 passed)。
- TypeScript 校验已复跑通过:`npm run typecheck`。
- 已补 automation owner evidence:`agent_runtime_export_evidence_pack` 会把当前 session 的 `agent_runs` 注入导出服务,`runtime.json` / `artifacts.json` 写出 `automationOwners`。
- Rust evidence 定向验证已通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p4-agent-owner-target cargo test --manifest-path src-tauri/Cargo.toml -p lime --lib evidence_pack_should_export_automation_owner_agent_envelope_metadata`(1 passed)。
- 已补 Workspace managed job 状态投影:`WorkspaceRegisteredSkillsPanel` 会读取既有 automation jobs,并按 `agent_envelope.directory` / `skill` 显示 Managed Job 创建状态、schedule 与最近运行。
- 前端定向验证已复跑通过:`npx vitest run src/features/capability-drafts/workspaceSkillAgentAutomationDraft.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx src/components/skills/SkillsWorkspacePage.test.tsx`(38 passed)。
- 已补 pause / resume 最小闭环:Workspace managed job 状态区可调用既有 `updateAutomationJob` 切换 `enabled`。
- 已补 Managed Objective 状态 / audit 投影:success run 显示 `verifying`,等待 artifact / timeline / evidence 审计。
- 已补 evidence completion audit input:`automationOwners.runs[].completionAudit` 输出 `audit_input_ready` / `missing_inputs` / `blocked_by_run_status`,并保持 `completionDecision=not_completed`。
- 已补 evidence completion audit summary:`runtime.json` / `artifacts.json` 输出 `completionAuditSummary`,在 automation owner success、workspace skill ToolCall source metadata 与 artifact / timeline 证据齐全时才输出 `completed`。
- 已补 completion audit summary 负向回归:覆盖缺 automation owner -> `needs_input`、owner run error -> `blocked`、缺 audit inputs -> `needs_input`、缺 workspace skill ToolCall evidence -> `verifying`。
- 已补 `summary.md` Completion Audit 人类可读入口:导出 decision、owner success count、Workspace Skill ToolCall evidence、artifact evidence 与 blocking reasons。
- Rust evidence 负向定向验证已通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p4-agent-owner-target cargo test --manifest-path src-tauri/Cargo.toml -p lime --lib completion_audit_summary_should_classify_negative_paths`(1 passed)。
- Rust evidence 定向验证已复跑通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p4-agent-owner-target cargo test --manifest-path src-tauri/Cargo.toml -p lime --lib evidence_pack_should_export_automation_owner_agent_envelope_metadata`(1 passed)。
- Rust timeline 定向验证已复跑通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p4-agent-owner-target cargo test --manifest-path src-tauri/Cargo.toml -p lime --lib timeline_should_preserve_workspace_skill_source_metadata_for_agent_envelope`(1 passed)。
- 已补 evidence export UI projection:`RuntimeEvidencePackExportResult`、前端 normalizer 和 Harness 面板均接入 `completionAuditSummary`,导出问题证据包后可直接看到 evidence-based decision 与 blocking reasons。
- 前端定向验证已通过:`npx vitest run src/lib/api/agent.test.ts src/components/agent/chat/components/HarnessStatusPanel.test.tsx`(77 passed)。
- TypeScript 校验已通过:`npm run typecheck`。
- 命令契约校验已通过:`npm run test:contracts`,覆盖 agent runtime client manifest、命令契约、harness metadata contract、modality contracts 与 cleanup report contract。
- GUI smoke 已复跑通过:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`,覆盖 workspace ready、browser runtime、site adapters、service skill entry、runtime tool surface、runtime tool surface page、Knowledge GUI 与 Design Canvas。首次复跑曾在 `smoke:agent-service-skill-entry` 出现 Vitest worker `onTaskUpdate` 通信超时;单独复跑该 smoke 与完整 GUI smoke 均通过。
- 已补 Agent envelope completion audit gate:presentation contract 消费 `completionAuditSummary`,completed + 三项 evidence 齐全才进入 `evidence_ready`;verifying / 缺 ToolCall evidence 仍不可固化。
- 前端 presentation 定向验证已通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts`(6 passed)。
- 已补 Workspace Agent envelope evidence-gated action:`WorkspaceRegisteredSkillsPanel` 支持按 directory 注入 completion audit summary,completed + evidence 齐全后“转成 Agent 草案”复用 Managed Job 草案创建链。
- 前端 Workspace 定向验证已通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`(12 passed)。
- 已补 Workspace recent run audit action:匹配 Managed Job 后可点击“审计最近运行”,通过 `getAutomationRunHistory` 找 session,再用 `exportAgentRuntimeEvidencePack` 导出并回填 `completion_audit_summary`,随后 evidence-gated Agent envelope 入口启用。
- Skills 工作台回归已通过:`npx vitest run src/components/skills/SkillsWorkspacePage.test.tsx`(30 passed)。
- GUI smoke 已在 recent run audit action 后复跑通过:`npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`,覆盖 workspace ready、browser runtime、site adapters、service skill entry、runtime tool surface、runtime tool surface page、Knowledge GUI 与 Design Canvas。
- 已补 Agent envelope card composition:Workspace 草案区展示 Runbook、Memory、Widget、Permission、Schedule、Evidence 六块组成,仍不新增执行实体。
- 前端定向验证已通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`(12 passed)。
- TypeScript 校验已复跑通过:`npm run typecheck`。
- 已补 derived Agent card / workspace sharing 摘要:completed audit 后显示 `workspace-local/<skill-directory>` 派生 Agent card 与 workspace / team 共享范围;未完成审计时显示草案等待态。
- 前端定向验证已复跑通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`(12 passed)。
- 已补 workspace/team sharing discovery 边界:Agent card 摘要展示 `.agents/skills/<skill-directory>` 的 registered discovery 来源,并说明复用同一 Managed Job / evidence。
- 前端定向验证已复跑通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`(12 passed)。
- 已完成 P0-P4 completion audit:新增 `docs/exec-plans/creaoai-completion-audit.md`,逐项映射 roadmap / implementation-plan 的 P0-P4 要求到代码、测试、命令验证和文档证据。
- 审计发现并修正 Agent envelope gate 的一个边界:仅 `evidencePackId` 存在时不再进入 `evidence_ready`;必须 `completionAuditSummary.decision=completed` 且 automation owner / Workspace Skill ToolCall / artifact-or-timeline 三项 evidence 齐全。
- 前端定向验证已复跑通过:`npx vitest run src/features/capability-drafts/agentEnvelopeDraftPresentation.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx`(12 passed)。
@@ -1,123 +0,0 @@
# CreoAI Query Loop Metadata P3D 执行计划
> 状态:完成
> 创建时间:2026-05-06
> 前置计划:`docs/exec-plans/creaoai-runtime-binding-p3c-plan.md`
> 路线图来源:`docs/roadmap/creaoai/implementation-plan.md`、`docs/aiprompts/commands.md`、`docs/aiprompts/quality-workflow.md`
> 当前目标:把 P3C 的 runtime binding readiness 作为 Query Loop 可读上下文投影进单次 `agent_runtime_submit_turn`,但仍不启用真实 SkillTool 执行。
## 主目标
P3D 第一刀只回答:
```text
如果当前回合显式携带 workspace_skill_bindings metadata
-> Query Loop 能不能读到这些 registered skill 的来源、状态和下一道 gate
-> 模型能不能据此规划下一步
-> 同时明确不能声称已运行、不能调用未启用 skill、不能自动化
```
固定宗旨:
**不是永远限制能力;是永远限制未经验证、未经授权、不可审计的执行。**
## 本轮最小切口
本轮只做 Query Loop metadata prompt projection:
1. 新增 `workspace_skill_bindings` / `workspaceSkillBindings` request metadata contract。
2. Rust 在 full runtime system prompt 中注入只读说明块。
3. 说明块把 registered skill 当作“候选能力上下文”,不是可调用工具。
4. prompt 明确禁止模型声称已执行、禁止调用未授权 Skill、禁止创建自动化。
5. 补最小 frontend metadata builder,用于后续 UI / send boundary 统一组装。
本轮明确不做:
1. 不注入 `SkillTool` registry。
2. 不把 P3C binding candidate 变成 `allow_model_skills=true`。
3. 不创建 “运行 / 自动化 / 继续这套方法” UI 入口。
4. 不新增 scheduler、queue、artifact 或 evidence 旁路。
5. 不执行 `.agents/skills/<skill>/scripts`。
## Metadata contract
推荐放在:
```json
{
"harness": {
"workspace_skill_bindings": {
"source": "p3c_runtime_binding",
"bindings": [
{
"directory": "capability-report",
"name": "只读 CLI 报告",
"description": "把只读 CLI 输出整理成 Markdown 报告。",
"binding_status": "ready_for_manual_enable",
"next_gate": "manual_runtime_enable",
"query_loop_visible": false,
"tool_runtime_visible": false,
"launch_enabled": false,
"permission_summary": ["Level 0 只读发现"],
"source_draft_id": "capdraft-...",
"source_verification_report_id": "capver-..."
}
]
}
}
}
```
固定语义:
- `workspace_skill_bindings` 表示“当前回合可读的 registered skill 候选上下文”。
- `query_loop_visible=false` 表示尚未进入长期 Query Loop 目录。
- `tool_runtime_visible=false` 表示尚未进入可调用工具面。
- `launch_enabled=false` 表示前端和模型都不能把它当作可运行能力。
## 实施步骤
### P3D-0:计划与边界
- [x] 新增本执行计划。
- [x] 明确 P3D 只做 Query Loop metadata projection,不做 execution。
### P3D-1:Rust prompt projection
- [x] 新增 `workspace_skill_binding_prompt` 模块。
- [x] 支持 snake_case / camelCase metadata。
- [x] 限制最多投影 5 个 binding,避免 prompt 膨胀。
- [x] 过滤空字段并截断长文本。
- [x] 在 full runtime prompt stage 中插入 `WorkspaceSkillBindings`。
- [x] 补 Rust 单测:无 metadata 不注入、有 binding 注入、禁止执行语义存在、stage 顺序稳定。
### P3D-2:Frontend metadata builder
- [x] 新增 workspace skill binding metadata builder。
- [x] 支持从 `AgentRuntimeWorkspaceSkillBinding` 安全裁剪为 request metadata。
- [x] 保持 `allow_model_skills` 不被自动打开。
- [x] 补 TS 单测。
### P3D-3:文档与校验
- [x] 更新 CreoAI 路线图 P3D 状态。
- [x] 更新命令 / 质量文档中 metadata 边界。
- [x] 跑 Rust / TS 定向测试、`npm run typecheck`、必要时 `npm run test:contracts`。
## 验收标准
1. 不带 `workspace_skill_bindings` metadata 时 prompt 不变化。
2. 带 metadata 时 prompt 包含 skill 名称、目录、状态、来源与下一道 gate。
3. prompt 明确说明这些 binding 只能用于规划,不能被直接调用或声称已运行。
4. 该 metadata 不会自动打开 `allow_model_skills`。
5. 所有新增测试和契约检查通过。
## 执行记录
### 2026-05-06
- 已创建 P3D 执行计划,确认本轮只把 P3C readiness 作为 Query Loop 可读上下文,不做 tool_runtime 执行授权。
- 已新增 Rust `WorkspaceSkillBindings` prompt stage:支持 `workspace_skill_bindings` / `workspaceSkillBindings`,最多投影 5 个候选 binding,并在 prompt 中明确禁止声称已运行、禁止调用未授权 Skill、禁止创建 automation。
- 已新增前端 `workspaceSkillBindingsMetadata` builder,并接入 `buildHarnessRequestMetadata` 可选参数;默认不改变发送行为,也不写入 `allow_model_skills`。
- 已更新 CreoAI 路线图、命令边界与质量工作流,明确 P3D 是只读 Query Loop metadata projection,不是 runtime enable。
- 后续 P3E / P4 收口验证已覆盖 P3D 边界:workspace skill metadata builder、harness metadata builder、runtime turn prompt projection、`npm run test:contracts` 与 `npm run typecheck` 均通过;P3D 判定完成。
@@ -1,156 +0,0 @@
# CreoAI Runtime Binding P3C 执行计划
> 状态:完成
> 创建时间:2026-05-06
> 前置计划:`docs/exec-plans/creaoai-capability-discovery-p3b-plan.md`
> 路线图来源:`docs/roadmap/creaoai/implementation-plan.md`、`docs/aiprompts/commands.md`、`docs/aiprompts/quality-workflow.md`
> 当前目标:把 P3B 已发现的 workspace-local registered skill 推进为运行时可审计的 binding readiness projection,但仍不开放默认执行面。
## 主目标
P3C 第一刀只回答一个问题:
```text
当前 workspace 里哪些 P3A/P3B registered skill
-> 已经具备进入 Query Loop / tool_runtime 的候选资格
-> 还卡在哪个 gate
-> 为什么现在仍不能直接运行
```
固定宗旨:
**不是永远限制能力;是永远限制未经验证、未经授权、不可审计的执行。**
## 本轮最小切口
本轮新增 `agent_runtime_*` 主链下的只读投影命令:
```text
agent_runtime_list_workspace_skill_bindings
-> workspaceRoot
-> P3B registered skills
-> binding readiness / policy gate / next gate
-> Skills 工作台只读展示
```
本轮只做:
1. 显式按 `workspaceRoot` 读取 P3B registered skill。
2. 返回 runtime binding candidate / blocked / next gate 等只读状态。
3. 明确标注 `queryLoopVisible=false`、`toolRuntimeVisible=false`、`launchEnabled=false`。
4. 前端只展示“runtime binding 候选 / 待启用”,不展示运行、自动化或继续方法入口。
5. 同步 `agentRuntimeCommandSchema`、generated manifest、DevBridge、mock、命令目录册和文档。
本轮明确不做:
1. 不调用 `AsterAgentState::reload_lime_skills()`。
2. 不修改 `SkillService::get_catalog_roots` 的 cwd 语义。
3. 不把 workspace registered skill 合并进默认 `useSkills("lime")`。
4. 不把 generated skill 注入 `SkillTool` global registry。
5. 不改变 `agent_runtime_submit_turn` 的工具可见性。
6. 不创建 automation job 或 Managed Objective。
## 为什么 P3C 第一刀仍然只读
P3B 已证明“文件存在且可审计”,但它还没有证明:
1. Query Loop 该如何在当前 session 中发现这个 skill。
2. `tool_runtime` 该如何裁剪它的权限、caller、surface 和 sandbox。
3. evidence pack 该如何记录来源 draft、verification report、registration 和运行事实。
4. 当前 workspace 与后端进程 `cwd` 不一致时,运行时 loader 该读哪个 root。
因此 P3C 不能直接把 registered skill 交给现有 `SkillTool` 执行。第一刀先把 binding gate 明文化,让后续每一步都有事实源可验证。
## 安全规则
1. **workspace 显式入参**:不从进程 `cwd` 推断当前项目。
2. **registered-only**:只消费 P3B 已认可的 `.lime/registration.json` provenance。
3. **只读 projection**:不执行 `SKILL.md`、scripts、CLI 或外部 API。
4. **默认不可运行**:所有结果必须显式 `launchEnabled=false`。
5. **gate 可解释**:每条 binding 都必须说明当前状态、阻塞原因和下一道 gate。
6. **后续执行回主链**:真正执行只能继续走 `agent_runtime_submit_turn -> Query Loop -> tool_runtime -> artifact/evidence`。
## 实施步骤
### P3C-0:计划与边界
- [x] 新增本执行计划。
- [x] 明确第一刀只做 runtime binding readiness projection,不做 execution。
### P3C-1:后端 binding service
- [x] 新增 workspace skill binding DTO。
- [x] 新增 `list_workspace_skill_bindings(...)` 服务函数。
- [x] 复用 P3B registered discovery 的 symlink / provenance / 标准检查边界。
- [x] 返回 `ready_for_manual_enable` / `blocked` 等 binding 状态。
- [x] 补 Rust 单测:空 workspace、相对 workspaceRoot 拒绝、registered skill 变成 binding candidate、缺 provenance blocked、非标准项 blocked。
### P3C-2:agent runtime 命令边界
- [x] 新增 Tauri command `agent_runtime_list_workspace_skill_bindings`。
- [x] 同步 `runner.rs`、DevBridge dispatcher。
- [x] 同步 `agentRuntimeCommandSchema.json` 并生成 `commandManifest.generated.ts`。
- [x] 同步 `agentCommandCatalog`、`mockPriorityCommands`、`defaultMocks`。
- [x] 运行 `npm run test:contracts`。
### P3C-3:前端 API / UI
- [x] 扩展 `src/lib/api/agentRuntime/inventoryClient.ts`。
- [x] 扩展 `WorkspaceRegisteredSkillsPanel`:展示 binding 状态与 next gate。
- [x] 保持不出现“立即运行 / 自动化 / 继续这套方法”入口。
- [x] 补 API、组件、Skills 工作台回归测试。
### P3C-4:试跑与验收
- [x] Rust 定向测试通过。
- [x] 前端定向测试通过。
- [x] `npm run test:contracts` 通过。
- [x] 若 Skills 工作台 UI 可见行为变化,补 `npm run verify:gui-smoke`。
## 验收标准
1. `agent_runtime_list_workspace_skill_bindings` 只接受显式 `workspaceRoot`。
2. 返回结果只包含 P3B registered skill。
3. 每条结果包含来源 draft、verification report、registration、权限摘要和 next gate。
4. 每条结果默认 `queryLoopVisible=false`、`toolRuntimeVisible=false`、`launchEnabled=false`。
5. UI 展示 runtime binding 状态,但不出现运行、自动化或继续方法入口。
6. 命令契约、DevBridge、mock、文档和 GUI smoke 保持一致。
## 执行记录
### 2026-05-06
- 已创建 P3C 执行计划,确认命令归属为 `agent_runtime_* / inventory` 主链,而不是继续扩 `capability_draft_*`。
- 已确认第一刀不接 `SkillTool`、不 reload、不修改 cwd-based loader,只补 workspace binding readiness projection。
- 已完成后端 `runtime_skill_binding_service`、Tauri command、DevBridge dispatcher、前端 API 网关、默认 mock、治理目录册与 Skills 工作台只读 binding 状态展示。
- 已把 P3C 语义固定为 `workspaceRoot -> P3B registered skills -> runtime binding readiness / next gate`,结果显式返回 `queryLoopVisible=false`、`toolRuntimeVisible=false` 与 `launchEnabled=false`。
- Rust 定向测试通过:
- `CARGO_TARGET_DIR="src-tauri/target-codex-p3c-novoice" cargo test --manifest-path "src-tauri/Cargo.toml" --no-default-features runtime_skill_binding`
- 结果:`5` 个 runtime skill binding 测试通过,`1224` 个测试按过滤器跳过。
- 首次编译曾被既有媒体任务编译问题阻断;已补最小阻塞修复后复跑通过。
- 前端定向回归通过:
- `npm test -- "src/lib/api/agentRuntime/inventoryClient.test.ts" "src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx" "src/components/skills/SkillsWorkspacePage.test.tsx" "src/lib/api/capabilityDrafts.test.ts"`
- 结果:`4` 个文件、`41` 个测试通过。
- 运行时 API 目录校验通过:
- `npm run typecheck`
- `npx eslint "src/lib/api/agentRuntime.ts" "src/lib/api/agentRuntime/*.ts" --max-warnings 0`
- 命令契约通过:
- `npm run test:contracts`
- 结果:agent runtime generated manifest、command contracts、harness contracts、modality runtime contracts 与 cleanup report contract 均通过。
- GUI smoke 复跑通过:
- `npm run verify:gui-smoke -- --reuse-running --timeout-ms 300000`
- 首次运行在 `smoke:knowledge-gui` 的文件管理器资料导入等待处失败;复跑通过 workspace ready、browser runtime、site adapters、service skill entry、runtime tool surface、Knowledge GUI 与 Design Canvas。
## P3C 收口结论
P3C runtime binding readiness projection 第一刀已达到本计划可交付门槛:P3B registered skill 可以在 `agent_runtime_* / inventory` 主链下被投影为 workspace skill binding candidate,并明确说明当前 binding status、next gate、来源 provenance 与权限摘要;但仍不会进入 Query Loop、SkillTool registry、默认 tool surface,也不会暴露运行、自动化或继续方法入口。
下一阶段应继续单独推进:
```text
runtime binding readiness
-> workspace-scoped Query Loop metadata
-> tool_runtime 授权裁剪
-> 当前 session 显式启用 generated skill
-> artifact / evidence 调用记录
```
@@ -1,101 +0,0 @@
# CREAO Tool Runtime Authorization P3E 执行计划
> 状态:P3E 第一刀已完成,进入 P4 前收口
> 日期:2026-05-06
> 主线:`Capability Draft -> verification -> workspace-local skill -> P3B discovery -> P3C readiness -> P3D Query Loop metadata -> P3E tool_runtime authorization`
## 目标
P3E 只回答一个问题:已注册的 workspace-local Skill 如何在单个 session / turn 中经过显式 enable 后进入可调用边界。
本轮不做:
1. Agent Marketplace / Skill Store。
2. 长期自动化、scheduler 或后台 job。
3. 绕过 `agent_runtime_submit_turn` 的平行执行命令。
4. 把 P3D `workspace_skill_bindings` 只读 metadata 直接升级成可调用工具。
## 合同
新增 runtime metadata contract:
```json
{
"harness": {
"workspace_skill_runtime_enable": {
"source": "manual_session_enable",
"approval": "manual",
"workspace_root": "/abs/workspace",
"bindings": [
{
"directory": "capability-xxxx",
"skill": "project:capability-xxxx",
"source_draft_id": "capdraft-...",
"source_verification_report_id": "capver-..."
}
]
}
}
}
```
约束:
1. `workspace_root` 必须与当前 turn 的 workspace root 一致。
2. `bindings[].directory` 必须来自 P3C `ready_for_manual_enable` binding。
3. `SkillTool` 只在当前 session scope 内启用,并裁剪到 allowlist 中的 Skill 名称。
4. P3E metadata 本身不写 `allow_model_skills`,避免与 P3D 只读候选混淆。
5. Workspace Skill 加载只由 runtime enable gate 触发;注册和 discovery 仍不 reload Skill。
## 任务
### P3E-0:边界确认
- [x] 确认 `agent_runtime_list_workspace_skill_bindings` 仍只做 readiness,不新增命令。
- [x] 确认 P3E 继续走 `agent_runtime_submit_turn` metadata,不创建平行 runtime command。
### P3E-1:Rust runtime gate
- [x] 增加 `workspace_skill_runtime_enable` 解析与 P3C readiness 校验。
- [x] 明确校验 workspace root、registered skill directory 和 verification provenance。
- [x] 显式加载当前 workspace `.agents/skills`,并把 `project:<directory>` 放入 session allowlist。
- [x] 扩展 `LimeSkillTool` session gate:支持 all-access 与 allowlist 两种模式。
### P3E-2:Prompt 与前端 metadata
- [x] 在 full runtime prompt 中投影 runtime enable scope,提示只能调用列出的 workspace-local Skill。
- [x] 增加前端 metadata builder,输出 snake_case `workspace_skill_runtime_enable`,且不写 `allow_model_skills`。
- [x] 在 Workspace 已注册能力面板接入“本回合启用”,跳转到 Agent 后只通过 `initialAutoSendRequestMetadata.harness.workspace_skill_runtime_enable` 显式授权当前回合,不写长期自动化配置。
- [x] 将 P3E enable binding provenance 注入 `SkillTool` session source store,并在 ToolResult metadata 中写回 `workspace_skill_source` / `workspace_skill_runtime_enable`,让 timeline / evidence pack 能追踪 source draft、verification report、registered directory 与 session 授权范围。
### P3E-3:验证
- [x] Rust 定向测试:runtime binding service / runtime turn / agent SkillTool gate。
- [x] 前端定向测试:workspace metadata builder / harness metadata builder / Workspace 已注册能力启用入口。
- [x] 视命令契约变更情况运行 `npm run test:contracts`;本轮不新增命令,主要用于确认未漂移。
## 进度日志
### 2026-05-06
- P3E 第一刀已落到 current 主链:`agent_runtime_submit_turn -> request_metadata.harness.workspace_skill_runtime_enable -> SkillTool session allowlist`。
- 保留 P3D 只读语义:`workspace_skill_bindings` 仍不打开 `allow_model_skills`,不代表可调用。
- Workspace 已注册能力面板已补“本回合启用”入口:只在 P3C `ready_for_manual_enable` binding 上可用,自动发送首回合时注入 P3E metadata,不创建 automation / scheduler / marketplace。
- 前端定向验证已通过:`npx vitest run src/components/agent/chat/utils/workspaceSkillBindingsMetadata.test.ts src/components/agent/chat/utils/harnessRequestMetadata.test.ts src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx src/components/skills/SkillsWorkspacePage.test.tsx`(4 files / 54 tests)。
- 命令 / harness 契约验证已通过:`npm run test:contracts`。
- 已补 evidence / timeline 的最小来源链路:P3E projection 会把每个 enabled binding 转为 session-scoped `SkillToolSessionSkillSource`,`LimeSkillTool` 执行结果会携带 `workspace_skill_source` 与 snake_case `workspace_skill_runtime_enable` metadata;由于 timeline tool call payload 已保留 ToolResult metadata,后续 evidence pack 可直接消费该字段进入 P4 Agent envelope。
- Rust SkillTool gate 定向验证已通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p3e-agent-target cargo test -p lime-agent allowlisted_session_should_preserve_workspace_skill_source_metadata`(1 passed)。
- Rust runtime turn 定向验证已通过:`CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=/tmp/lime-p3e-agent-target cargo test -p lime --lib workspace_skill_runtime_enable_metadata_should_force_full_runtime_context`(1 passed)。
- 最新校验已通过:`rustfmt --edition 2021 ...`、`git diff --check -- ...`、`npm run test:contracts`、前端 P3E vitest 定向套件、Rust SkillTool gate 定向测试和 Rust runtime turn 定向测试。
## P3E 收口结论
P3E 已完成当前计划中的最小可交付闭环:
1. 注册后的 workspace-local skill 仍默认不可调用,只作为 P3B / P3C / P3D 的只读候选和 readiness 上下文。
2. 当前 session 只有在 `request_metadata.harness.workspace_skill_runtime_enable` 显式携带 P3C ready binding 后才打开 `SkillTool`。
3. `SkillTool` tool surface 被裁剪到 `project:<directory>` / `<directory>` allowlist,且不通过 `allow_model_skills` 偷开全局 skills。
4. runtime gate 会校验 workspace root、registered skill directory、source draft、verification report 和 readiness provenance。
5. 调用结果会写回 `workspace_skill_source` / `workspace_skill_runtime_enable` metadata,后续 P4 可直接用于 timeline、evidence pack 和 Agent envelope 展示。
下一刀应进入 P4:把成功运行后的 workspace-local skill 包成 Workspace 产品面的 Agent envelope 草案,并继续复用 `agent_runtime_submit_turn`、automation job、Managed Objective、artifact 和 evidence 主链。
-412
View File
@@ -1,412 +0,0 @@
# LimeNext 总实施计划
> 状态:`legacy current reference`
> 更新时间:2026-04-18
> 上位总纲:`docs/roadmap/limenext/`
> 当前主规划:`docs/roadmap/limenextv2/README.md`
> 目标:把 LimeNext 从“上位平台定义”推进到“可持续实施的主线计划”,统一产品对象、运行时骨架、远程入口、长时执行、证据治理与场景选品的推进顺序。
> 说明:本文保留旧阶段实施锚点与推进历史,帮助理解 `limenext -> limenextv2` 的迁移背景;后续 current 实现与新增文档默认不再以本文为主规格,而应回到 `docs/roadmap/limenextv2/README.md` 及其子专题。
## 1. 先给结论
LimeNext 当前不是缺一篇愿景文档,而是缺一条能持续推进的平台主线。
本计划固定一个判断:
**后续提到“推进 LimeNext”,默认不是泛泛优化平台,而是沿着 `产品对象 -> 业务装配 -> 运行时骨架 -> 长时执行与远程 -> 证据治理 -> 场景选品` 这条主线推进。**
## 2. 当前基线
已经完成:
1. `docs/roadmap/limenext/` 专题包已建立。
2. Lime 的 current 平台事实源已经分别收口到:
- `overview.md`
- `query-loop.md`
- `command-runtime.md`
- `task-agent-taxonomy.md`
- `remote-runtime.md`
- `state-history-telemetry.md`
- `skill-standard.md`
3. `ribbi/`、`command-runtime/`、`harness-engine/`、`product convergence`、`service skill cloud config` 等子专题已被回挂到 LimeNext 总图。
4. `sceneapp-capability-model.md` 已补齐,固定了五类 `SceneApp`、五种 skill / scene 设计模式与九类底层能力模块的判断标准。
5. `base-setup-decoupling.md` 已把“基础设置包”和宿主内核边界单独收口。
6. 多模态组合型样板已进入 LimeNext 总纲,开始用 `composition blueprint` 统一表达组合蓝图。
7. `composition-blueprint-schema.md` 已补出文档级 schema 草案,后续可以继续下沉到 validator 与目录投影。
8. `base-setup-package-schema.md` 已把基础设置包补成顶层装配对象,开始明确“改包、改投影、改宿主”的判断线。
9. `base-setup-projection-lifecycle.md` 已把装配包发布、灰度、seeded 兜底与回滚链收口成可讨论的事实源。
10. `base-setup-implementation.md` 已把 validator / compiler / rollout gate 的第一版代码落点收口到现有 `ServiceSkillCatalog` 与 launch/runtime 锚点。
11. `src/lib/base-setup/*` 已新增第一版代码骨架,开始把基础设置包下沉到实际 TypeScript 模块与单测。
12. `src/lib/base-setup/storage.ts`、`bootstrap.ts` 已落地,把基础设置包快照、bootstrap 提取与 compat 编译链收口成独立模块。
13. `src/lib/api/serviceSkills.ts`、`src/lib/serviceSkillCatalogBootstrap.ts` 已正式接入 `Base Setup Package` 主链,远端刷新与 bootstrap 同步都能直接消费装配包并落基础设置快照。
14. 默认云端 seeded 目录已从手写 `ServiceSkillCatalog` 迁到手写 `Base Setup Package` + compat 编译产物,开始把装配层推进为真正的内置事实源。
15. seeded 本地 `local_custom` 项也已迁到手写 `Base Setup Package` + compat 编译产物,`x-article-export` 不再依赖手写 `ServiceSkillItem` 补丁。
16. `SceneApp` 的 `project pack` 合同已经从基础设置编译层正式接到产品读模型、目录详情页和经营评分页:
- 统一暴露 `deliveryProfile / compositionProfile / scorecardProfile`
- 目录页已经能说明最终交付部件、组合步骤和经营关注点
- mock 与 Tauri seeded DTO 已同步对齐,避免只在前端测试里存在结构字段
17. `project pack` 已继续接到真实 `run summary / scorecard` 聚合:
- `artifactCount` 不再是唯一运行结果语义
- 运行详情已经能解释“交齐几项、还缺什么、卡在哪个失败信号”
- `project pack` 经营评分已改成 `complete_pack_rate / review_pass_rate / publish_conversion_rate`
- `observedFailureSignals / topFailureSignal` 已从真实运行样本回流到产品面
18. `project pack` 第二阶段已开始接到 `session / evidence` 主链:
- `SceneApp run summary` 在拿到 `sessionId` 时会优先读取真实 `SessionDetail + FileArtifact`
- runtime verification failure 已开始回挂到 `review_blocked`
- 只有缺少 session evidence 时才回退 `AgentRun.metadata.artifact_paths`
19. `project pack` 已开始补齐结构化结果入口,而不是只在运行详情里展示 viewer 文案:
- `SceneAppRunSummary` 已新增稳定 `deliveryArtifactRefs`
- 结果入口会优先从 runtime evidence 解析真实会话产物,并在缺失 session evidence 时回退 metadata artifact path
- 前端已改为复用现有 Agent 文件预览入口打开主稿 / 结果文件,不新增新的 viewer 协议
20. `project pack` 已把治理入口接到 SceneApp 运行详情,而不再只展示“已接入会话证据”的文字判断:
- `SceneAppRunSummary` 已新增稳定 `governanceArtifactRefs`
- Rust 会按 `sessionId` 组装 `evidence/summary.md` 与 `review/review-decision.*` 的稳定引用
- 前端继续复用现有 Agent 文件预览入口打开证据摘要、人工复核记录与复核 JSON,不新增新的 SceneApp viewer 协议
21. `project pack` 已开始把治理入口从“可打开”推进到“缺失时自动补生成”:
- 新增 `sceneapp_prepare_run_governance_artifact` 当前命令
- SceneApp 页面点击治理入口时会先按制品类型触发 evidence / review 导出,再继续打开对应文件
- 页面仍只消费 `src/lib/api/sceneapp.ts` 网关,不直接耦合 `agent_runtime_export_*` 命令名
22. `project pack` 已继续把治理入口从“单文件打开”推进到“业务动作”:
- 运行详情已新增 `治理动作` 区块,而不只是列出治理文件
- 当前已支持:
- `准备周会复盘包`
- `准备结构化治理包`
- 两类动作都会先经由 `src/lib/api/sceneapp.ts` 批量补齐 evidence / review 制品,再打开对应主治理文件
- SceneApp 页面仍不直接理解底层 runtime export 命令,也不自己拼接治理路径
23. `project pack` 已开始把治理能力从“单次运行详情”推进到“页面级治理看板”:
- `SceneAppsPage` 已新增独立 `治理看板`
- 当前会把 `run + scorecard + evidence / review` 翻译成业务向状态,而不是只展示底层技术字段
- 目录主视图已经能判断这条场景当前更适合:
- 周会复盘
- 生成 / 看板消费
- 自动化任务跟进
- 结果编辑 / 发布
- 页面级治理动作继续复用 `src/lib/api/sceneapp.ts` 与既有文件打开链,不新增平行协议
24. `SceneAppsPage` 已开始从“单页堆叠工作台”收口到“分页式信息架构”:
- 当前固定拆成 `场景目录 / 场景详情 / 治理复盘` 三个分页
- `catalog` 只负责选品与筛选,不再同时承载详情和治理解释
- 目录卡片点击后会直接进入对应 `detail` 分页,而不是只停留在选中态
- 顶部继续保留“当前场景”摘要,确保分页切换时不丢业务上下文
- `detail / governance` 已补齐分页级空态与回退动作,不再让用户停在无下一步的空白页
- 顶部已新增业务向工作流导轨,明确表达 `选场景 -> 补启动 -> 看治理` 的跨页顺序
- 这条规则已经同步写回 `docs/aiprompts/design-language.md`,作为后续复杂工作台默认设计约束
25. `创作场景执行摘要` 已从启动瞬间静态摘要继续推进到生成页运行态回流:
- `AgentChatWorkspace` 当前会按 `sessionId -> sceneapp run` 回查最近运行
- 生成页顶部摘要卡已开始展示 `delivery completion / runtime evidence / governance artifact / observed failure signal`
- `AppPageContent` 已修正 `sceneapps` keep-alive 树位,`创作场景 -> 持续流程 -> 创作场景` 往返不会再触发目录页重挂载
- `useAppNavigation` 已升级为 `requested / committed` 双态导航,`AppSidebar + AppPageContent` 统一按请求态渲染,快速切换时以最后一次点击为准,不再出现主区短暂空白或左侧导航瞬时消失
- `SceneAppsPage` 当前只允许在激活且拥有当前导航请求时回写 `sceneapps` 参数与 recent visit,keep-alive 的旧页不会再把全局导航抢回去
26. `创作场景 -> 持续流程 / 自动化` 的事实源已经开始统一,而不是继续各讲各的:
- `AutomationJobDetailsDialog` 当前会识别 `sceneapp` 派生任务的 metadata,并回查同一条 `descriptor / project pack plan / run summary / scorecard`
- 自动化详情里已新增 `创作场景闭环` 摘要块,可直接回到 `创作场景` 准备页或治理复盘页
- 自动化详情里的结果文件、治理文件与治理动作,当前已复用 `SceneAppRunDetailViewModel + prepare_run_governance_artifact` 主链,不再额外发明 automation 专用 pack/gov 协议
27. `创作场景 -> 场景目录` 当前也已开始接到同一份经营事实源,而不是继续停留在静态标题目录:
- `SceneAppsCatalogPanel` 目录卡片已开始同时展示 `delivery contract / latest run / scorecard action / top failure signal / operating summary`
- `useSceneAppsPageRuntime` 会按全量 descriptor 聚合最近运行与 scorecard,并把它们回流到同一份目录读模型
- 目录卡片、生成页顶部执行摘要与自动化详情当前共享同一组 `descriptor + latest run + scorecard` 语义,不再各自发明一套“当前状态”解释
28. `project pack` 已开始从治理复盘单点入口继续扩到 `生成准备 / 经营评分` 的结果消费入口:
- `SceneAppDetailPanel` 与 `SceneAppScorecardPanel` 当前都会展示最近可消费的 `Project Pack` 结果入口,而不再只停留在 `viewerLabel`
- 当最新运行仍在执行或尚未带回结果文件时,页面会自动回退到最近一轮已交付样本,而不是让准备页与评分页只剩规划文案
- 结果入口仍继续复用现有 Agent 文件预览打开链,不新增新的 `Project Pack viewer` 协议
29. `project pack` 已继续扩到 `生成主执行面` 的直接结果消费入口:
- `useSceneAppExecutionSummaryRuntime` 当前不再只回流 `runtimeBackflow` 摘要,还会同步回流最近可消费的 `Project Pack` 结果样本
- `SceneAppExecutionSummaryCard` 已新增 `最近可消费结果` 区块,生成页可以直接打开结果文件,不再只停留在顶部摘要和 `viewerLabel`
- `生成主执行面` 与 `创作场景` 页面当前共享同一条 `findLatestSceneAppPackResultRun + deliveryArtifactEntries + Agent 文件预览打开链` current 主链,不再各自维护一套结果入口逻辑
30. `生成主执行面` 已开始补齐回闭环动作,而不再让用户自己在左侧重新找入口:
- 执行摘要卡当前已新增 `回生成准备 / 去治理复盘` 深链动作
- `去治理复盘` 会优先带到最近可消费样本对应的 run,而不是只回当前可能仍在执行中的 run
- 执行摘要卡当前还可直接触发 `填写人工复核 / 可继续复用 / 继续观察 / 补证据 / 先别继续`
- 这些动作继续复用 `review decision` 主链与 `RuntimeReviewDecisionDialog`,生成页不再复制平行治理协议
31. `生成主执行面` 已开始补齐第一批 `生成后动作编排`,而不再只是“看结果 + 跳页面”:
- 执行摘要卡当前会直接展示 `周会复盘 / 生成 / 看板 / 持续流程 / 自动化 / 结果编辑 / 发布` 的推荐去向
- 生成页当前可直接执行 `准备周会复盘包 / 准备结构化治理包 / 打开基础治理材料 / 恢复底层运行入口`
- 生成页当前还可在同一会话里直接触发 `补齐缺失部件 / 发布前检查 / 进入发布整理 / 生成渠道预览稿 / 整理上传稿`,继续复用 `@发布合规 / @发布 / @渠道预览 / @上传` 与当前 turn 提交主链,而不新增新的发布协议
- `AgentChatWorkspace` 与 `SceneAppsPage` 当前共享 `resolveSceneAppRunEntryNavigationTarget + prepareSceneAppRunGovernanceArtifact(s)` current 主链,不再各自维护一份 run entry 恢复逻辑
- 这一步把 `生成` 从“结果页旁边的跳转入口”推进成了第一批真正的统一编排面
32. `生成主执行面` 已开始直接消费当前会话里的发布后产物,而不再只负责发起发布动作:
- `AgentChatWorkspace` 当前会按 `taskFiles / sessionFiles / artifacts` 聚合最近一份 `发布稿 / 渠道预览稿 / 上传稿`,只消费已带 `contentPostIntent / contentPostLabel` metadata 的 current 发布产物,不把普通 `content-posts/*.md` 误判为投放结果
- `SceneAppExecutionSummaryCard` 已新增 `最近发布产物` 区块,用户可直接从生成页打开刚刚整理出的发布稿、渠道预览稿和上传稿,不需要再去消息流或侧栏翻文件
- 打开链继续复用现有工作区文件预览主链:优先打开 `task file / artifact`,没有内存态时再回落到 `session file` 读取,不新增新的 viewer 协议
- 旧结论里若继续写“生成页当前只会触发发布后动作、还不能消费动作结果”,当前都按已过时理解
33. `生成主执行面` 已开始补第一层发布态判断,而不再只把 `发布稿 / 渠道预览稿 / 上传稿` 平铺出来:
- `sceneAppExecutionContentPosts` 当前会继续检查同名 `*.cover.json / *.publish-pack.json` 伴随文件,并推导 `可继续发布 / 优先渠道预览 / 优先上传整理 / 待补封面信息、发布包` 这类轻量就绪态
- `SceneAppExecutionSummaryCard` 的 `最近发布产物` 卡片当前会直接显示就绪标签与伴随材料芯片,用户在生成页就能看出这轮结果更适合继续发布、先看预览还是先补投放材料
- 这一步仍然只复用现有 `content-posts` 文件命名约定与 session/task/artifact 三路事实源,不新增新的发布状态协议
尚未完成:
1. 把 `ServiceSkill` 在产品面、目录面、运行时面和自动化面真正打通成同一个对象。
2. 让所有样板场景都按统一 `SceneApp` 类型与能力模块声明接入。
3. 把 `SceneScorecard` 与现有 evidence / tracker / artifact 数据链打通。
4. 在 `docs/exec-plans/` 里建立 LimeNext 的持续推进日志与阶段风险管理。
5. 把已完成的样板进一步接到 `Artifact + Evidence + Scorecard`。
6. 把 `SceneScorecard` 从概念字段推进到可被聚合和查看的产品对象。
7. 把“主 App 宿主内核”与“基础设置包”分层固定下来,减少场景扩张对客户端发版的依赖。
8. 让基础设置包的 schema 继续下沉到 bootstrap / seeded catalog 可消费的目录投影。
9. 把 `composition blueprint` 从文档口径继续收口到可校验、可投影的装配对象。
10. 继续把 `project pack` 的结构化读模型从当前已接通的 `session / evidence + deliveryArtifactRefs` 优先聚合,扩展到更完整的 `artifact validator / request telemetry / evidence summary` 主链。
11. 把 `project pack` 的治理闭环从当前已接通的“生成主执行面 + 场景目录 + 生成准备 + 治理复盘深链 + 经营评分 + 页面级治理看板 + 自动化详情 + 周会复盘包 / 结构化治理包 + 同聊补件 / 发布前检查 / 进入发布整理 / 渠道预览稿 / 上传稿 + 最近发布产物直接消费 + 发布态轻量判断”继续扩展到更完整的正式投放动作与更强的 evidence/review 闭环。
12. 把装配包从已接通的 `service_skill_catalog + scene_catalog` 继续扩到 `command_catalog` 投影代码。
13. 决定第一版是“客户端内编译 package”还是“服务端下发预编译 projection,客户端只做 gate + fallback”。
14. 把剩余 seeded 来源继续收口,重点转到 `command` 与 automation 相关入口,不再保留新的手写 `local_custom` 补丁。
15. 把分页式 `SceneAppsPage` 继续补成真正的多页面工作台;当前跨页入口、分页级空态和工作流导轨已补齐,剩余重点转到 GUI smoke 交付证明与更强的子页级信息头部。
## 3. 总目标
LimeNext 的实施总目标固定为:
`目标输入 -> Scene / ServiceSkill -> Agent / Binding 执行 -> Artifact 交付 -> Evidence / Review 治理 -> Scorecard 评估 -> Keep / Incubate / Retire`
## 4. 实施阶段
### P0:总纲固化
目标:
- 让 LimeNext 平台定义稳定,不再来回摇摆。
- 固定 `SceneApp` 的能力分类,不再只停留在“场景工作台”的口号层。
已完成:
- `docs/roadmap/limenext/` 专题包
退出条件:
- 后续讨论新专题时,能先声明它属于 LimeNext 哪一层。
- 新样板进入实施前,能先写出 `sceneapp_type`、`pattern_primary / pattern_stack`、`infra_profile` 与 `composition_blueprint`。
### P1:产品对象收口
目标:
- 统一 `ServiceSkill / Scene` 的产品对象语义
- 把首页卡、技能页、slash scene、推荐方案挂到同一对象
依赖:
- `docs/research/ribbi/*`
- `docs/roadmap/limenextv2/product-principles.md`
- `docs/roadmap/limenextv2/implementation-roadmap.md`
- `docs/roadmap/lime-service-skill-cloud-config-prd.md`
退出条件:
- 前台不再把 `skill bundle` 和 `ServiceSkill` 混成一个词
- 首页与输入栏围绕同一场景对象表达
### P1.5:业务装配收口
目标:
- 固定基础设置包与 `composition blueprint` 的角色边界
- 让多模态组合场景能优先通过装配层下发,而不是频繁改主 App
退出条件:
- 团队能明确回答“什么改动只改基础设置包,什么改动必须升级宿主”
- 第一条组合型样板能被写成标准设计卡,而不是口头 workflow
### P2:运行时骨架收口
目标:
- 让 `Scene / ServiceSkill` 真正走统一运行时主链
依赖:
- `docs/roadmap/gongneng/command-runtime/*`
- `docs/aiprompts/query-loop.md`
- `docs/aiprompts/command-runtime.md`
退出条件:
- 至少一条 `Agent + ServiceSkill` 样板链路与一条 `Agent + Task` 样板链路稳定成立
当前状态:
- `@配音 / voice_runtime / cloud-video-dubbing` 已被选定并沉淀为第一条 `Agent + ServiceSkill` current 样板方案包
- `x-article-export` 已被沉淀为第二条 `Agent + ServiceSkill` current 样板方案包,并覆盖 `site_adapter / browser_assist` 分支
### P3:长时执行与远程收口
目标:
- 把 `subagent turn / automation job / channels runtime / browser connector` 纳入统一平台解释与实施顺序
依赖:
- `docs/aiprompts/task-agent-taxonomy.md`
- `docs/aiprompts/remote-runtime.md`
退出条件:
- 团队不再把 OpenClaw compat shell 或 scheduler tick 当成长期主线
### P4:证据与治理闭环
目标:
- 让 evidence / replay / review / dashboard 成为平台默认治理面
依赖:
- `docs/aiprompts/state-history-telemetry.md`
- `docs/roadmap/harness-engine/*`
退出条件:
- 失败场景可以稳定回挂到 evidence -> analysis/review -> fix -> regress 主链
### P5:场景工厂
目标:
- 建立 `SceneScorecard`
- 固定 launch / keep / incubate / retire 规则
依赖:
- `docs/roadmap/limenext/metrics-and-selection.md`
退出条件:
- 平台能够系统性淘汰低价值场景,而不是只增不减
当前补充:
- `Artifact / Evidence / Scorecard` 的业务样板与图示已经建立
- `Local Durable SceneApp` 当前样板已固定为 `每日趋势摘要 / 账号增长跟踪`
- `x-article-export` 的当前产品合同已固定为“资料包优先 + 同一路径后处理”
- `Base Setup` 解耦规则已单独成文,开始固定“什么该下发,什么必须跟宿主升级”
- `project pack` 已不再只是 schema 词汇,而是已经进入 SceneApp 目录和经营评分读模型
- `project pack` 已进一步接到真实 `run summary / scorecard` 聚合对象
- `project pack` 已开始改成 `session / evidence` 优先聚合,而不是只看 tracker metadata
- `project pack` 已开始把真实结果文件入口接到 SceneApp 运行详情,不再只展示 viewer kind 文案
- `project pack` 已开始把证据摘要 / 人工复核记录 / 复核 JSON 接到同一条运行详情治理面
- `project pack` 已开始在治理入口点击时自动补生成 evidence / review 文件,而不是让用户先回到 Agent 手动导出
- `project pack` 已开始把治理面升级成业务动作,当前可直接准备周会复盘包与结构化治理包
- `project pack` 已开始把治理面抬到 SceneApp 页面级看板,而不再只停留在单次运行详情里
- `project pack` 已开始把最近可消费结果入口接到 `生成主执行面`,不再只停留在 `创作场景` 子页消费
- 下一步不再只是补页面,而是要把这套治理看板继续接到更完整的发布/复盘动作、选品口径和后续自动治理主链
## 5. 默认样板链路
后续实施默认至少推进五条样板:
1. `Agent + ServiceSkill`
- 第一条 current 样板:`@配音 / voice_runtime`
- 第二条 current 样板:`x-article-export`
2. `Agent + Task`
- `@配图`
3. `Prompt 型`
- `@总结` 或 `@分析`
4. `automation job`
- `每日趋势摘要`
- `账号增长跟踪`
5. `Multimodal Composition`
- `文本 -> 线框图 -> 配乐 -> 剧本 -> 短视频草稿`
固定补充规则:
- 每条样板都必须显式声明:
- `sceneapp_type`
- `pattern_primary`
- `pattern_stack`
- `infra_profile`
- `composition_blueprint`
- `execution_entity`
- `runtime_binding`
- `storage_strategy`
- `delivery_contract`
推荐先从三句最短版开始写:
- 这是什么类型的 `SceneApp`
- 它内部主要按什么模式组织
- 它到底调用了哪些基础设施
## 6. 当前优先顺序
当前建议优先顺序固定为:
1. 先按 `SceneApp` 能力模型收口样板声明标准
2. 继续收口 `ServiceSkill` 作为产品对象
3. 以 `@配音 / voice_runtime` 作为第一条 `Cloud-managed SceneApp` 样板进入实施
4. 以 `x-article-export` 作为第一条 `Browser-grounded SceneApp` 样板进入实施
5. 以 `每日趋势摘要 / 账号增长跟踪` 作为第一条 `Local Durable SceneApp` 样板进入实施
6. 以多模态组合短视频草稿作为第一条 `Hybrid SceneApp` 样板进入实施
7. 把五条样板链路接到 `Artifact + Evidence + Scorecard`
当前不建议优先做:
1. 扩目录数量
2. 新增更多 compat 入口
3. 新造一层平行 runtime
## 7. 风险
### 风险 1:继续把 Lime 误讲成 `skills runtime`
处理:
- 所有执行文档都保持 `Skill / ServiceSkill / Scene / Artifact / Evidence / Scorecard` 分层
### 风险 2:把设计模式误当成产品对象
处理:
- 文档中固定区分 `ServiceSkill / Scene` 与 `pattern_primary / pattern_stack`
- 不再出现“这是个 Pipeline 产品”这类混层说法
### 风险 3:只做总纲,不接实施
处理:
- 本计划与进度日志持续更新
- 每进入实现阶段,回挂具体子专题与验证命令
### 风险 4:只做治理减法,不回主线交付
处理:
- 连续两轮主要在做治理后,下一轮必须回到样板场景交付
## 8. 当前下一刀
当前下一刀建议固定为:
**把 LimeNext 从“durable automation 已回到装配主链”推进到“gate 定版与组合交付主链收口”。**
当前已经成立的恢复协议:
1. `automation -> automation job`
2. `chat / skill -> agent session`
3. `browser_assist -> browser runtime`
4. `cloud_scene -> structured cloud scene resume`
5. `native_skill -> structured service skill resume`
当前已经补齐的装配收口:
1. 保持 `SceneAppRunSummary` 继续只暴露稳定恢复引用,而不是让 UI 直接理解底层 metadata:
- `sourceRef`
- `sessionId`
- `browserRuntimeRef`
- `cloudSceneRuntimeRef`
- `nativeSkillRuntimeRef`
- `deliveryArtifactRefs`
2. durable 场景的 automation projection / fallback 已继续收回同一装配主链:
- `Base Setup Package` 已支持 `automationProfiles[]`
- `catalogProjection.automationProfileRef` 已进入 validator / parser / projection metadata / snapshot index
- durable seeded 样板已显式声明 automation profile
- `service-skills/automationDraft.ts` 现已优先消费装配层 schedule / delivery / retry / enabled 预设
3. `browser-runtime` GUI smoke 与 `verify:local` 当前已重新通过,GUI 交付门槛不再被此前的 CDP 环境问题卡住
当前下一步转为:
1. 决定客户端编译 vs 服务端预编译 gate 的最终定版位置
2. `创作场景 -> 生成` 的执行摘要、`自动化详情`、`场景目录`、`生成准备` 和 `经营评分` 都已经接通运行态回流;下一步把同一份 `Project Pack + governance` 基线继续扩到生成主执行面的直接结果消费与更完整的治理消费入口
3. 把 `SceneScorecard` 与周会模板、页面级治理看板和生成口径继续打通
4. 继续让 automation detail / SceneApp run / scorecard 聚合消费同一组 base-setup refs,而不是回退到运行时猜测
这样 LimeNext 才会从“目录、启动、复盘、恢复、durable 装配都闭环”继续推进到“装配 gate 定版、组合交付与经营聚合也闭环”。
@@ -1,14 +0,0 @@
# LimeNext 推进日志(临时镜像)
这份文件是 2026-04-22 为绕过 macOS `Documents` 目录 `EPERM` 问题而临时创建的 Finder 镜像,不是正式事实源。
当前请统一以:
1. `docs/exec-plans/limenext-progress.md`
作为唯一有效的 LimeNext 推进日志。
补充说明:
1. 这份镜像文件不再继续更新。
2. 若后续文件系统权限恢复,可直接清理这份临时镜像。
@@ -1,14 +0,0 @@
# LimeNext 推进日志(临时镜像)
这份文件是 2026-04-22 为绕过 macOS `Documents` 目录 `EPERM` 问题而临时创建的 Finder 镜像,不是正式事实源。
当前请统一以:
1. `docs/exec-plans/limenext-progress.md`
作为唯一有效的 LimeNext 推进日志。
补充说明:
1. 这份镜像文件不再继续更新。
2. 若后续文件系统权限恢复,可直接清理这份临时镜像。
File diff suppressed because it is too large Load Diff
@@ -1,156 +0,0 @@
# LimeNext V2 文件系统阻塞记录(2026-04-22)
> 后续状态说明:本文是当日权限阻塞的历史记录,不再代表 current 现状。
> 现役事实源以 [../roadmap/limenextv2/sceneapp-current-boundary.md](../roadmap/limenextv2/sceneapp-current-boundary.md) 与 [./limenext-progress.md](./limenext-progress.md) 的最新条目为准。
> 文中提到的临时命名如 `SceneAppCurrentPresentationDescriptor`,现已落回 `SceneAppPresentationDescriptor`;对象级 `SceneAppCurrent*` 也已全部删除。
## 背景
当前主线仍是把 `LimeNext V2` 的 current 对象边界继续收口,尤其是:
1. `sceneapp` current API / 展示 / 测试夹具不再继续把 compat 云执行命名当成普通 current 值
2. compat 旧值只保留在显式输入归一化、alias、历史锚点与 compat 测试里
在本轮前半段,已经完成:
1. `src/lib/api/sceneapp.ts`
- compat `cloud_scene / cloud_session / cloud_runtime / launch_cloud_scene` 已继续被正规化为 current 本地执行语义
- compat `sceneappType = cloud_managed` 已在 API 归一化阶段转成 current `local_instant / local_durable / browser_grounded / hybrid`
2. `src/lib/api/sceneapp.test.ts`
- 已补充 compat `cloud_managed` -> current 类型投影断言
3. `src/lib/sceneapp/product.test.ts`
- 默认 current fixture 已去掉 `cloud_runtime`
4. `src/components/sceneapps/SceneAppsPage.test.tsx`
- 默认 current fixture 已去掉 `cloud_runtime`
5. `docs/exec-plans/at-command-local-execution-alignment-plan.md`
6. `docs/exec-plans/limenext-progress.md`
## 本轮进一步想继续做但被阻塞的点
原计划继续收口这两处:
1. `src/lib/sceneapp/types.ts`
- 理想目标:让 current `SceneAppDescriptor` 不再公开承认 `cloud_managed`
- 只在显式 compat helper / compat 输入边界保留 `SceneAppCompatType`
2. `src/lib/sceneapp/presentation.ts`
- 理想目标:`getSceneAppPresentationCopy(...)` 只消费 current descriptor
- compat `cloud_managed` 只保留给 `resolveSceneAppTypePresentation(...)` / `getSceneAppTypeLabel(...)` 这类显式 compat helper
3. `src/lib/sceneapp/presentation.test.ts`
- 理想目标:把「compat helper 断言」和「current descriptor copy 断言」拆开
## 实际阻塞
从本轮中段开始,仓库内既有文件出现系统级 `EPERM / Operation not permitted`:
1. `apply_patch` 无法继续读取既有文件
2. shell 无法直接读取或覆写既有文件
3. `mv` 也无法用新文件原子替换既有文件
4. `git` / `npm` / `node` 在仓库根 cwd 下也会受到连带影响
但新文件仍可创建,因此这份记录被补写下来。
## 继续诊断补充(2026-04-22)
后续继续排查时,又确认了两件事:
1. 当前执行器在仓库根真实调用 `getcwd()` 也会被系统拒绝
- 表现为 Python `os.getcwd()` 报 `PermissionError: [Errno 1] Operation not permitted`
- 这解释了为什么部分依赖 cwd 的命令会出现额外异常
2. 前端 dev server 仍能读取源码并通过 sourcemap 暴露 `sourcesContent`
- 可通过 `http://127.0.0.1:1420/src/...` 取回当前前端源码文本
- 这条路径只适合继续分析 current 边界,不解决任何既有文件写入问题
3. 根据 dev server 暴露出的当前源码,`src/lib/sceneapp/types.ts` 已经处于半收口状态
- `SceneAppType` 已经是 current-only
- `SceneAppCompatType = "cloud_managed"` 已是显式 compat 类型
- 当前更大的残留已转移到 `presentation.ts / presentation.test.ts` 的展示与测试边界
4. 已确认一个可操作但不优雅的旁路
- 直接 shell 仍无法读取或覆写受阻文件
- 但 Finder 可以把受阻文件复制到 `/private/tmp`,也可以把 `/private/tmp` 里的同名文件覆盖回仓库
- 因此当前可以通过:
- Finder duplicate 到 `/private/tmp`
- 在 `/private/tmp` 用 `apply_patch` 修改
- 再由 Finder duplicate 覆盖回仓库
- 继续推进代码收口
因此,本轮可继续做的事变成:
1. 利用 dev server 把前端现状分析清楚
2. 把 current 规划和阻塞记录补到可写文档
3. 对少量关键文件使用 `Finder -> /private/tmp -> apply_patch -> Finder` 的旁路修改
本轮仍不能安全做的事:
1. 大范围批量改仓库既有文件
2. 依赖 repo cwd 的常规 `git / npm / node` 工作流
3. 假装环境已经恢复正常
## 直接影响
当前不适合按常规 shell 工作流继续对既有源码文件做大范围机械修改,否则仍然容易在未知权限状态下把主线卡死。
但对少量关键文件,可以使用上面的 Finder 旁路继续推进。
## 权限恢复后建议的下一刀
本轮通过 Finder 旁路,已经实际完成:
1. `src/lib/sceneapp/types.ts`
- 执行族当前已拆成:
- `SceneAppExecutorBindingFamily`
- `SceneAppCompatBindingFamily`
- 因此“当前执行族”和“只为兼容旧目录保留的 cloud_scene”不再混在单一别名里
2. `src/lib/sceneapp/catalog.ts`
- `normalizeCompatSceneAppBindingFamily(...)` 当前已显式返回 current 执行族
- `resolveDistinctBindingFamilies(...)` 当前也已收窄到 current 执行族数组
3. `src/lib/sceneapp/launch.ts`
- `SceneAppWorkspaceExecutionDraft.adapterKind` 当前已收窄到 current 执行族
- `normalizeWorkspaceAdapterKind(...)` 当前也已显式返回 current 执行族
4. `src/lib/sceneapp/types.ts`
- `SceneAppLaunchRequirementKind` 当前已拆成:
- `SceneAppLaunchRequirementCoreKind`
- `SceneAppCompatLaunchRequirementKind`
- 因此“当前允许的启动前置”和“只为兼容旧目录保留的 cloud_session”不再混在同一个裸联合注释里
5. `src/lib/sceneapp/types.ts`
- `SceneAppRuntimeContext` 当前已只保留 current 字段
- `cloudSessionReady / cloud_session_ready` 当前已收进 `SceneAppCompatRuntimeContextInput`
- `readSceneAppDirectorySessionReadyCompat(...)` 继续兼容读取旧 wire 字段,但 current interface 不再直接承认它们
6. `src/lib/sceneapp/presentation.ts`
- 新增 `SceneAppCurrentPresentationDescriptor`
- `getSceneAppPresentationCopy(...)` / `inferFallbackCopy(...)` 当前已显式收窄到 current descriptor
7. `src/lib/sceneapp/presentation.test.ts`
- compat `cloud_managed / cloud_runtime` 断言与 current descriptor copy 断言已拆开
- 已删掉“compat descriptor 伪装成 current descriptor”的测试夹具写法
8. 定向旁路验证
- `npx --yes vitest@3.2.4 run /private/tmp/lime-sceneapp-edit/presentation.test.ts`
- 结果:`3 tests passed`
9. 扩展旁路验证
- 已通过:
- `npx --yes vitest@3.2.4 run "/private/tmp/sceneapp-scan/runEntryNavigation.test.ts" "/private/tmp/sceneapp-scan/launch.test.ts" "/private/tmp/sceneapp-scan/product.test.ts" "/private/tmp/sceneapp-scan/presentation.test.ts" "/private/tmp/sceneapp-scan/navigation.test.ts"`
- 结果:`5 files / 31 tests passed`
- 已确认但暂不视为代码回归:
- 把整个 `/private/tmp/sceneapp-scan/*.test.ts` 一次性跑宽时,`storage / entry` 会因缺少 `window/jsdom` 失败,`automation / executionPromptActions / catalog` 会因缺少 repo alias `@/...` 失败
- 这些失败说明的是“临时副本环境不完整”,不等于本轮 `sceneapp presentation` 收口本身引入了新断链
在常规权限恢复后,优先继续下面这组剩余收口:
1. `src/lib/sceneapp/types.ts`
- 保持 `SceneAppType` 直接作为 current 类型事实源
- 不要把 `SceneAppDescriptor` 再改宽回 compat 联合类型
2. 然后补跑 repo 内正式校验:
- `npx eslint "src/lib/sceneapp/types.ts" "src/lib/sceneapp/presentation.ts" "src/lib/sceneapp/presentation.test.ts"`
- `npm exec vitest run "src/lib/sceneapp/presentation.test.ts" "src/lib/sceneapp/product.test.ts" "src/lib/api/sceneapp.test.ts" "src/components/sceneapps/SceneAppsPage.test.tsx"`
- `npm run governance:legacy-report`
- `npm run typecheck`
## 结论
这不是路线图方向摇摆,而是一次纯环境阻塞。
主线判断不变:
1. current 前台与 current API 对象继续坚持本地执行语义
2. compat 云执行命名继续只留在显式兼容边界
3. 当前已经通过 Finder 旁路推进了 `presentation` 这条 current 边界收口
4. 一旦文件系统权限恢复,下一步就直接回到 repo 内正式校验和余下的少量边界清理
5. 当前边界的文档事实源已补到:
- [../roadmap/limenextv2/sceneapp-current-boundary.md](../roadmap/limenextv2/sceneapp-current-boundary.md)
@@ -1,483 +0,0 @@
# Lime 多模态运行合同实施计划
## 摘要
本计划承接 `docs/roadmap/warp/`,用于把 Warp / ClaudeCode 参考落成 Lime 可检查的底层多模态运行合同。
本轮主目标不是先改 `@` 命令,而是先建立:
1. 底层运行事实源地图
2. `ModalityRuntimeContract` schema
3. 可机器检查的 contract registry
4. 最小治理守卫
## 主线声明
从现在开始,多模态能力默认向下面这条 current 主链收敛:
```text
runtime identity
-> ModalityRuntimeContract
-> capability matrix
-> ModalityExecutionProfile
-> executor binding
-> domain artifact graph
-> evidence / replay / task index
-> viewer
-> entry binding
```
`@` 命令、按钮、Scene 只允许作为上层 entry binding,不能直接拥有 task、model、artifact、viewer 或 evidence 的事实写入权。
## 当前阶段
### Phase 0:底层运行事实源盘点
状态:进行中。
本阶段输出:
1. `docs/roadmap/warp/runtime-fact-map.md`
2. 底层 fact source 的 `current / compat / deprecated / dead` 分类
3. 后续 contract registry 的 owner / reader / writer 约束
### Phase 1:ModalityRuntimeContract schema
状态:进行中。
本阶段输出:
1. `docs/roadmap/warp/contract-schema.md`
2. `src/lib/governance/modalityRuntimeContracts.json`
3. `scripts/check-modality-runtime-contracts.mjs`
4. `npm run governance:modality-contracts`
### Phase 2:多模态能力矩阵
状态:进行中。
本阶段输出:
1. `docs/roadmap/warp/capability-matrix.md`
2. `src/lib/governance/modalityCapabilityMatrix.json`
3. contract 守卫检查 `required_capabilities` 与 `routing_slot` 是否引用已登记能力和模型角色
4. `request_model_resolution` 已开始消费 `TaskProfile.routingSlot`,把 `browser_reasoning_model`、`image_generation_model`、`audio_transcription_model`、`voice_generation_model` 等槽位折叠成最小模型能力需求;候选池会过滤不满足能力的模型,显式用户模型锁定仍保留锁定模型但输出 `*_candidate_missing` gap,并在 gap 来源为 `explicit_model_lock` 时把 `limit_state.status` 标为 `user_locked_capability_gap`,由 runtime turn 在模型执行前阻断。
### Phase 3:ModalityExecutionProfile
状态:进行中。
本阶段输出:
1. `docs/roadmap/warp/execution-profile.md`
2. `src/lib/governance/modalityExecutionProfiles.json`
3. `image_generation`、`browser_control`、`pdf_extract`、`voice_generation`、`audio_transcription`、`web_research`、`text_transform` 的最小 execution profile。
4. contract 守卫检查 current contract 必须被 profile 覆盖,且 profile 的模型角色、权限、LimeCore policy、artifact policy 必须覆盖 contract。
5. `src/lib/governance/modalityExecutionProfiles.ts` 把 profile / adapter registry 解析成 launch metadata 快照。
6. Rust runtime contract snapshot、Evidence Pack、Replay 与统一媒体任务索引已经携带 profile / adapter key;媒体任务 worker 已在进入图片、配音、转写执行器前做最小 profile / adapter / executor binding preflight;Browser Assist 工具层现在也会在真实浏览器动作前校验 `browser_control` 的 execution profile、executor adapter 与 executor binding,失败时返回 `runtime_preflight` 工具错误并保留同一 runtime contract metadata,供 Evidence / Replay 识别为被合同阻断;`LimeSkillTool` 现在也会对 current Skill 主链生成治理 registry 驱动的 `modality_runtime_contract` metadata,并在上层显式传入冲突 `execution_profile` / `executor_adapter` / `executor_binding` 时阻断进入 Skill 执行器;旧 `lime_run_service_skill` 仍是 compat guard,但命中 `voice_generation` 时会携带并校验 `service_skill:voice_runtime` 合同,避免旧云运行工具被误看成 current executor;LimeCore policy refs/snapshot 已进入 runtime contract、Evidence Pack 与统一媒体任务索引;后续仍需在 registry 出现明确 `gateway:*` adapter 后接入 Gateway preflight、真实 runtime policy merge、更完整 thread read 决策解释和 GUI 可视化。
7. `thread_read.runtime_summary.modalityRuntime` 现在会从最近 ToolCall / FileArtifact 的同一 `runtime_contract` 投影 `contractKey`、`modality`、`routingSlot`、`requiredCapabilities`、`profileKey`、`executorAdapterKey`、`executorKind` 与 `executorBindingKey`;这只暴露最近合同的 profile / adapter / binding 摘要,不把 thread read 变成上层 `@` 命令事实源。
8. `SessionExecutionRuntimeTaskProfile.routingSlot` 已接入 provider/model resolution 的最小能力 enforcement:非显式用户锁定路径会优先在候选池内选择满足 runtime requirements 的模型,显式用户锁定路径继续 honored,但 `capability_gap` 会暴露 `browser_reasoning_candidate_missing`、`image_generation_candidate_missing` 等 gap code。
9. `SessionExecutionRuntimePermissionState` 已把 `permissionProfileKeys` 推进为 `lime_runtime.permission_state` 最小权限摘要,并同步进入 `runtime_summary.permissionStatus / permissionAskCount / permissionBlockingCount`、`AgentRuntimeThreadReadModel.permission_state` 与前端 execution runtime / thread read 类型。本阶段解释 profile 声明、需确认 profile 与空阻断清单,不把 `ask_user_question` 视为风险权限;未解决确认现在会在 prelude 状态发出后、模型执行前阻断 turn,且最小 `RequestUserInput` 权限确认 + `agent_runtime_respond_action` 写回 + 下一轮 `resolved/denied` metadata merge 已接入,避免声明态需确认权限被误当成功执行;这仍不是完整权限系统或 LimeCore 云授权。
### Phase 5:Executor Adapter registry
状态:进行中。
本阶段输出:
1. `src/lib/governance/modalityExecutionProfiles.json` 的 `executor_adapters`
2. `skill:image_generate`、`browser:browser_assist`、`skill:pdf_read`、`service_skill:voice_runtime`、`skill:transcription_generate`、`skill:research`、`skill:text_transform` 的最小 adapter 声明。
3. contract 守卫检查 current contract 的 `executor_binding` 必须能解析到 `executor_kind:binding_key` adapter。
4. adapter 的 progress / cancel / resume / artifact 支持位、artifact output、permission requirements 与 failure mapping 必须覆盖 contract。
5. 前端 `runtime_contract` snapshot 已携带 `executor_adapter` 摘要,避免上层入口继续只传 executor binding。
6. 统一媒体任务索引已经能查询 `execution_profile_keys`、`executor_adapter_keys`、`limecore_policy_refs` 与每条 snapshot 的 `execution_profile_key` / `executor_adapter_key` / `executor_kind` / `executor_binding_key` / `limecore_policy_snapshot_status`;图片、配音、转写 worker 已消费同一 adapter registry 做执行前检查;Browser Assist 工具层已消费同一 `browser_control` runtime contract 做执行前检查,错误结果继续带 `modality_runtime_contract` 与 `last_error(stage=runtime_preflight)`;`LimeSkillTool` 已把 `pdf_read`、`research`、`report_generate`、`site_search`、`summary`、`translation`、`analysis`、`transcription_generate` 映射到 current 底层合同,并把 profile / adapter / binding 快照从治理 JSON 注入 Skill tool metadata;`lime_run_service_skill` 作为旧服务型工具只保留 compat guard,命中 `voice_generation` 时必须通过 `voice_generation_profile`、`service_skill:voice_runtime` adapter 与 `service_skill:voice_runtime` binding 检查,否则返回 `runtime_preflight`;Gateway preflight 仍后置,不能从 Telegram / 微信 / 飞书 / Discord ingress 硬造 `gateway:*` executor adapter。
7. thread read 可见层已开始消费同一 adapter 摘要:`runtime_summary.modalityRuntime` 会并列暴露最近合同的 profile / adapter / binding key,供后续 GUI / evidence 解释复用;真实 Gateway adapter preflight 仍等待 registry 出现明确 `gateway:*` adapter 后再接。
8. provider/model resolution 已消费同一 `routingSlot` 摘要来约束模型候选池:Browser reasoning、图片生成、音频转写、语音生成、结构化文档与 cheap summary 都会先映射成 runtime model capability requirements,再影响候选数、自动重选与 gap 输出。
### Phase 6:LimeCore 目录与策略接线
状态:进行中。
本阶段输出:
1. `runtime_contract.limecore_policy_refs` 声明每个 current contract 依赖的 LimeCore 控制面事实源。
2. `runtime_contract.limecore_policy_snapshot` 现在落最小本地默认决策:`status=local_defaults_evaluated`、`decision=allow`、`decision_source=local_default_policy`、`decision_scope=local_defaults_only`。这个 `allow` 只表示本地默认策略没有阻断继续路由,不等于 LimeCore tenant / provider 真实放行。
3. `runtime_contract.limecore_policy_snapshot.policy_inputs` 现在按 ref 生成最小输入清单,标记 `status=declared_only`、`value_source=limecore_pending`;`missing_inputs` 继续列出等待 LimeCore 真实命中的控制面输入。
4. Evidence Pack `modalityRuntimeContracts.snapshotIndex.limecorePolicyIndex` 汇总 policy refs、snapshot status、decision、decision source、policy inputs、missing inputs 与 unresolved refs,方便后续 allow / ask / deny 与 LimeCore audit 对齐。
5. `list_media_task_artifacts` 的统一媒体任务索引输出 policy refs / snapshot status / decision / decision source / missing inputs / unresolved refs,以及 `policy_evaluation` 的 status / decision / blocking / ask / pending refs,让任务列表不必读取隐藏 task JSON 才能知道当前 contract 依赖哪些云控制面、卡在哪类 evaluator 输入。
6. Harness evidence 面板现在展示 `LimeCore 策略缺口` 摘要,直接暴露 policy snapshot、控制面 refs、missing inputs、local default decision、profile / adapter 与 `declared_only / limecore_pending` 输入状态。
7. Replay / grader 现在把 `limecorePolicyIndex` 纳入 suite tags、failure modes、success criteria、blocking checks 与多模态合同检查,要求 replay 继续保留 policy refs / missing inputs / local default decision,不能把本地默认 `allow` 当真实云策略放行。
8. `policy_value_hits[]` / `pending_hit_refs[]` / `policy_value_hit_count` 已进入 runtime contract、Evidence Pack、统一媒体任务索引、前端 normalizer 与浏览器 mock;默认命中数为 `0`,只表达“真实 LimeCore 命中值尚未接入”,不伪造 model catalog / offer / tenant flags。
9. Policy hit resolver seam 已能消费传入的 `status=resolved` 命中值,并自动把对应 ref 从 `missing_inputs` / `pending_hit_refs` 移到 `evaluated_refs`;当所有 refs 都已命中时,最小 `policy_input_evaluator` 会把已命中的 `model_catalog / provider_offer / tenant_feature_flags / gateway_policy` 信号折叠成 `allow / ask / deny` 的可审计决策;仍有 pending refs 时,顶层 `decision` 继续保持 `local_default_policy / local_defaults_only`,避免把输入缺口伪装成真实策略放行。
10. 图片任务执行前的 model registry assessment 现在会作为最小本地 `model_catalog` hit producer 写回同一 `policy_value_hits(status=resolved, value_source=local_model_catalog)`;该 hit 只证明模型目录输入已命中,模型是否具备 `image_generation` 能力仍由 runtime preflight 单独判定。
11. 图片任务进入真实执行器前已能从已解析的本地 runner config / API key 与 task payload provider/model 生成最小 `provider_offer` hit,写回同一 `policy_value_hits(status=resolved, value_source=local_provider_offer)`;该 hit 不序列化 API key,只证明 provider offer 输入已命中,不代表 tenant/provider/gateway 已策略放行。
12. Browser Assist 与 Web Research 类 launch 会从请求侧 `harness.oem_routing` 生成最小 `gateway_policy` hit,写回同一 `policy_value_hits(status=resolved, value_source=request_oem_routing)`;该 hit 只记录 tenant/provider/quota/can_invoke/fallback 等路由输入,不包含 session token,也不把本地默认 `allow` 升级成真实网关放行。
13. 请求侧会从 OEM Cloud bootstrap snapshot 的 `features` 生成最小 `tenant_feature_flags` hit,写回同一 `policy_value_hits(status=resolved, value_source=oem_cloud_bootstrap_features)`;该 hit 只解释租户功能开关输入已命中,不包含 session token。
14. `policy_evaluation` 现在在每个 snapshot 中记录 evaluator 状态:`input_gap` 时只说明还缺控制面输入,`evaluated` 时才允许顶层 `decision_source=policy_input_evaluator`;统一媒体任务索引同步暴露 evaluation status / decision / source 与 blocking / ask / pending refs。该 evaluator 只消费本地已命中的 policy inputs,不接 LimeCore 云 run/poll,也不把 LimeCore 扩张为默认 executor。
15. `thread_read.runtime_summary.limecorePolicy` 现在会从最新 tool metadata / file artifact 中的 runtime contract 读取 LimeCore policy snapshot,投影 contract、snapshot status、顶层 decision、decision source/scope/reason、refs、missing/pending refs、hit count 与 evaluator 摘要;这只解释当前线程最近一次可见 policy 决策输入,不新增命令、不接云 run/poll。
16. `list_media_task_artifacts.modality_runtime_contracts` 现在会把 evaluator 摘要提升为索引字段:`limecore_policy_evaluation_statuses`、`limecore_policy_evaluation_decisions`、`limecore_policy_evaluation_decision_sources`、`limecore_policy_evaluation_blocking_refs`、`limecore_policy_evaluation_ask_refs`、`limecore_policy_evaluation_pending_refs`,并在每条 snapshot 输出对应字段;这让任务列表、恢复层和后续 GUI 卡片能直接区分 pending input gap、ask 与 deny,不读取隐藏 task JSON。
17. 配音与转写任务卡恢复层现在消费同一索引里的 `limecore_policy_evaluation_*` 字段,把 `input_gap` 渲染为 `LimeCore 策略输入待命中: N`,把已评估 `deny / ask` 渲染为阻断或需确认的 refs;这只解释本地 evaluator 输入状态,不把 input gap 当成真实用户确认,也不把本地 `allow` 当云策略放行。
18. 图片任务恢复层现在会从 `runtime_contract.limecore_policy_snapshot.policy_evaluation` 解析 evaluator 摘要并写入 `ImageRuntimeContractSnapshot`;图片 viewer 复用同一文案规则展示 `LimeCore 策略输入待命中 / 阻断 / 需确认` 胶囊标签,让图片任务也能在用户可见层解释 pending input gap、ask 与 deny。
19. `thread_read.runtime_summary` 现在并列暴露 `limecorePolicy` 与 `modalityRuntime`:前者解释最近 policy decision input,后者解释同一合同的 profile / adapter / executor binding;这仍只消费已有 runtime contract,不新增 Tauri command、不接 LimeCore 云 run/poll,也不把 input gap 或本地 default decision 伪装成云端策略结论。
### Phase 7:上层入口绑定
状态:进行中。
本阶段输出:
1. `image_generation.bound_entries` 先登记 `@配图`、`@海报`、`@修图`、`@重绘`、`@分镜` 与显式图片动作。
2. 前端图片 launch 从 `ModalityRuntimeContract` registry 读取 contract 字段,不再在入口文件重复维护底层 contract 常量。
3. 守卫要求 entry binding 声明 `entry_source` 且 `launch_metadata_path` 保持在 `harness.*`。
### Phase 8:Browser Assist 底层合同注入
状态:进行中。
本阶段输出:
1. `browser_control` 先作为底层 Browser Assist 运行合同进入 `harness.browser_assist`,不新增独立 browser task 协议。
2. `@浏览器 / @Browser Agent / @Mini Tester` 仍保留原始用户消息,只把 `browser_requirement`、launch URL 与 contract snapshot 交给后端 runtime 决策。
3. `browser_control.bound_entries` 只登记 `@浏览器`、`@Browser Agent`、`@Mini Tester`、`@Web Scheduler`、`@Web Manage` 的 entry metadata;`@` 命令仍只是入口层,不能写 task / artifact / viewer 事实源。
4. 后端 `BrowserAssistRuntimeHint` 必须解析并保留 `browser_control` 合同快照,browser tool result metadata 需要携带同一组 contract 字段。
5. Evidence Pack 从 browser tool timeline metadata 导出 `browser_control` 的 `modalityRuntimeContracts`,使浏览器动作也进入 replay 可用的事实源。
6. Evidence Pack 的 `modalityRuntimeContracts.snapshotIndex.browserActionIndex` 必须汇总 action、session、URL、observation 与 screenshot,使 Browser Assist 不再只能靠 raw snapshots 人工查找。
7. Replay / grader 必须把 `browser_control` 识别成 Browser Assist 回归样本,检查 browser action trace / browserActionIndex、禁止 WebSearch 替代真实浏览器动作。
### Phase 9:PDF Extract 底层合同注入
状态:进行中。
本阶段输出:
1. `pdf_extract` 作为文档型底层运行合同进入 `harness.pdf_read_skill_launch.pdf_read_request`,`@读PDF` 仅保留为 entry source。
2. 前端 `@读PDF` 发送时保留原始消息,并注入 `modality_contract_key=pdf_extract`、`required_capabilities`、`routing_slot=base_model` 与 `runtime_contract` 快照。
3. 后端 `prepare_pdf_read_skill_launch_request_metadata` 必须补齐并保留同一组合同字段,避免旧客户端或缺省 metadata 退回普通聊天总结。
4. `Skill(pdf_read)` prompt 必须显式要求真实读取文件、保留合同字段,并禁止 `frontend_direct_pdf_parse` / `generic_chat_summary_only` 偏航。
5. Evidence Pack 必须能从 `Skill(pdf_read)` timeline metadata 导出 `pdf_extract` 的 `modalityRuntimeContracts`,使 PDF 读取不再只停留在入口 prompt 合同里。
6. Replay / grader 必须把 `pdf_extract` 识别成 PDF 读取回归样本,检查 `Skill(pdf_read)` / 文件读取 trace,并禁止 WebSearch、ToolSearch、Grep 或普通聊天替代真实读取。
7. 本阶段暂不把 PDF 单独建成 task artifact 协议;如果后续需要 PDF viewer / artifact UI,再从同一份 `pdf_extract` contract 继续下沉,不能新增平行事实源。
8. 完成底层闭环后,`@读PDF / @pdf / @read_pdf` 只登记为 `pdf_extract.bound_entries`,前端从 registry 读取 `entry_source`,不再把入口当作运行合同事实源。
### Phase 10:Voice Generation 服务型合同注入
状态:进行中。
本阶段输出:
1. `voice_generation` 作为音频型底层运行合同进入 `harness.service_scene_launch.service_scene_run`,`@配音` 仅保留为 entry source。
2. 前端 `@配音 / @voice / @dubbing` 发送时保留原始消息,并注入 `modality_contract_key=voice_generation`、`required_capabilities`、`routing_slot=voice_generation_model` 与 `runtime_contract` 快照。
3. 后端 `prepare_service_scene_launch_request_metadata` 对 `scene_key=voice_runtime` / `entry_source=at_voice_command` 的旧客户端 metadata 补齐同一组合同字段。
4. 服务型场景 prompt 必须显式说明当前底层合同,禁止退回 `legacy_tts_test_command` 或伪造“云端已提交”。
5. Evidence Pack 必须能从 voice runtime / service scene timeline args 或 metadata 导出 `voice_generation` 的 `modalityRuntimeContracts`,使配音合同进入 replay 可评分事实源。
6. Replay / grader 必须把 `voice_generation` 识别成本地 ServiceSkill/voice runtime 回归样本,检查 `service_scene_launch(scene_key=voice_runtime)` 或后续 audio_task/audio_output 证据,并禁止 `legacy_tts_test_command`、伪造云端提交、普通聊天文本或通用文件卡替代。
7. 本阶段新增最小 `audio_generate` task artifact 协议,把 `voice_generation` 写入标准 `audio_task/audio_output` 产物;聊天区提供最小音频任务卡与运行时文档 viewer,并允许执行器通过同一 task artifact 回写 `audio_output.completed`。当前 `lime-audio-worker` 已接入 OpenAI-compatible speech provider seam:有 current API Key Provider 凭证时写入 `.lime/runtime/audio/*.mp3` 并回写 `payload.audio_output.completed`;未配置 provider / model / client 时必须显式写回 `audio_provider_*` 错误,聊天任务卡与运行时文档 viewer 都从同一 task artifact 展示完成/失败事实,不回退 legacy TTS,也不伪造音频路径。
### Phase 11:Web Research 合同注入
状态:进行中。
本阶段输出:
1. `web_research` 作为混合型底层运行合同进入 `harness.research_skill_launch.research_request`、`harness.deep_search_skill_launch.deep_search_request`、`harness.site_search_skill_launch.site_search_request` 与 `harness.report_skill_launch.report_request`。
2. `@搜索 / @深搜 / @站点搜索 / @研报 / @竞品` 仅保留为 entry source;前端发送时注入 `modality_contract_key=web_research`、`required_capabilities`、`routing_slot=report_generation_model` 与 `runtime_contract` 快照。
3. 后端 `prepare_*_skill_launch_request_metadata` 需要为旧客户端或缺省 metadata 补齐同一组合同字段,避免回退成模型记忆回答、本地文件搜索或通用 WebSearch 旁路。
4. research / deep search prompt 必须显式要求首刀 `Skill(research)`,site search prompt 必须说明它是 `web_research` 合同下的站点搜索子入口且首刀 `Skill(site_search)`。
5. Evidence Pack 必须能从 `Skill(research)` / `Skill(site_search)` / `Skill(report_generate)` timeline args 或 metadata 导出 `web_research` 的 `modalityRuntimeContracts`,使联网研究进入 replay 可评分事实源。
6. Evidence Pack 的 `modalityRuntimeContracts.snapshotIndex` 必须提供可检索摘要:按 contract key、source、routing outcome、expected routing slot 与 tool trace 汇总,使联网研究不只能靠 raw snapshots 人工查找。
7. Replay / grader 必须把 `web_research` 识别成联网研究回归样本,检查 `Skill(research)` / `Skill(site_search)` / `Skill(report_generate)`、`search_query` / `lime_site_*` 工具时间线,并禁止模型记忆、本地文件搜索、ToolSearch 或通用 WebSearch 旁路替代真实研究。
8. 本阶段不新增独立 `report_generation` contract;`@研报 / @竞品` 先作为 `web_research` 下的报告型子入口,后续只有当 report artifact、执行器或 viewer 形成独立事实源时才拆子合同。
### Phase 12:Text Transform 合同注入
状态:进行中。
本阶段输出:
1. `text_transform` 作为文档型底层运行合同进入 `harness.summary_skill_launch.summary_request`、`harness.translation_skill_launch.translation_request` 与 `harness.analysis_skill_launch.analysis_request`。
2. `@读文件 / @总结 / @翻译 / @分析 / @发布合规 / @Logo拆解` 仅保留为 entry source;前端发送时注入 `modality_contract_key=text_transform`、`required_capabilities`、`routing_slot=base_model` 与 `runtime_contract` 快照。
3. 后端 `prepare_*_skill_launch_request_metadata` 需要为旧客户端或缺省 metadata 补齐同一组合同字段,避免回退成前端直出文本、ToolSearch、WebSearch 或普通聊天摘要。
4. summary / translation / analysis prompt 必须显式要求首刀 `Skill(summary)` / `Skill(translation)` / `Skill(analysis)`,显式文件路径场景可保留 `list_directory` / `read_file` 证据,但不能绕过底层合同。
5. Evidence Pack 必须能从 `Skill(summary)` / `Skill(translation)` / `Skill(analysis)` timeline args 或 metadata 导出 `text_transform` 的 `modalityRuntimeContracts`,使轻量文本/文档转换也进入 replay 可评分事实源。
6. Replay / grader 必须把 `text_transform` 识别成文本转换回归样本,检查文本转换 Skill trace 与必要文件读取证据,并禁止 `frontend_direct_text_transform`、ToolSearch、WebSearch 或普通聊天替代。
7. 本阶段不拆 `summary_generation`、`translation`、`analysis`、`publish_compliance` 或 `logo_decomposition` 平行 contract;只有当独立 artifact、执行器或 viewer 形成稳定事实源时才拆子合同。
### Phase 13:Audio Transcription 合同注入
状态:进行中。
本阶段输出:
1. `audio_transcription` 作为音频转写底层运行合同进入 `harness.transcription_skill_launch.transcription_task`,`@转写 / @transcribe / @Audio Extractor` 仅保留为 entry source。
2. 前端 `@转写` 发送时保留原始消息,并注入 `modality_contract_key=audio_transcription`、`required_capabilities`、`routing_slot=audio_transcription_model` 与 `runtime_contract` 快照。
3. 后端 `prepare_transcription_skill_launch_request_metadata` 需要为旧客户端或缺省 metadata 补齐同一组合同字段,避免回退成前端直连 ASR、普通文件读取或 generic file transcript。
4. `Skill(transcription_generate)` prompt 必须显式说明当前底层合同,继续压制 ToolSearch / WebSearch / Read / Glob / Grep 首刀偏航。
5. 标准 `transcription_generate` task file 必须写入同一 `audio_transcription` 合同与 `transcript.pending` 子产物,CLI / Agent tool 回退入口不能另写平行协议。
6. 媒体任务索引、Evidence Pack 与 Replay / grader 必须能定位 transcript 状态、来源、语言、输出格式与失败码,使 `audio_transcription` 不再只停留在入口 metadata。
7. 本阶段接入最小 `lime-transcription-worker` 与用户可见恢复层:OpenAI-compatible provider 成功时写入 `.lime/runtime/transcripts/*` 并回写 `transcript.completed`,provider/source/contract 失败时回写 `transcript.failed` 与明确错误码;聊天任务卡和 `.lime/runtime/transcription-generate/*.md` 运行时文档优先消费统一媒体任务索引的 `transcript_*` snapshot,并在完成态读取 `transcript_path` 文本内容供复制校对;当前 viewer 已能从 JSON / SRT / VTT transcript 中解析时间轴与说话人,生成可逐段编辑校对的段落表;保存校对稿时复用 ArtifactDocument 版本链,写入 `transcriptCorrection*` / `transcriptSegmentsCorrected` / `transcriptCorrectionDiffSummary` metadata,不改写原始 ASR 输出,并在 viewer 中展示“校对稿已保存”状态;更多 ASR adapter 与本地离线 ASR 后续继续消费同一 task artifact 与 `transcriptIndex`。
## 本轮最小闭环
第一轮已完成治理底座;当前纠偏后的最小闭环必须至少贯通一个真实运行时 vertical slice。
### 已完成治理底座
1. 新增 runtime fact map 文档。
2. 新增 contract schema 文档。
3. 新增首批底层 contract registry。
4. 新增 contract 校验脚本。
5. 接入 npm 脚本,作为后续 `verify:local` 集成前的显式检查入口。
### 当前实现闭环
1. 选择 `image_generation` 作为第一条 current vertical slice。
2. Rust 运行时在 `harness.image_skill_launch` 与内部 `image_task` 上注入 `modality_contract_key=image_generation`、`modality=image`、`required_capabilities`、`routing_slot` 与 `runtime_contract` 快照。
3. `lime_create_image_generation_task` 工具 schema、别名归一化与 request builder 接受并补齐 contract 字段。
4. `image_generate` task artifact payload 固化同一组 contract 字段,artifact 成为图片生成 contract 的 truth source。
5. DevBridge / browser mock 与前端 `CreateImageGenerationTaskArtifactRequest` 类型同步 contract 字段,避免浏览器链路变成假绿。
6. 图片工作台 viewer 从 task artifact / 工作台状态读取 `runtimeContract`,展示“按合同路由 / 合同阻止 / registry 能力缺口”。
7. `browser_control` 第九刀从 registry 解析 `browser_reasoning_model` 合同,并写入 `harness.browser_assist`,使 Browser Assist 首刀能看到 `modality_contract_key=browser_control`、`required_capabilities`、`routing_slot` 与 `runtime_contract` 快照。
8. `browser_control` 第十/十一刀在 Rust 侧解析同一份合同,并把 browser tool timeline metadata 纳入 evidence `modalityRuntimeContracts`,不再只停留在前端 request metadata。
9. `browser_control` 第十二刀把 evidence 快照继续写入 replay tags / expected checks / grader,要求回放继续走 Browser Assist、产生 browser action trace,并显式禁止 WebSearch 替代。
10. `pdf_extract` 第十三刀把 `@读PDF` 上层入口接到底层 `Skill(pdf_read)` 运行合同,前端和 Rust 侧都会保留同一份 contract snapshot。
11. `pdf_extract` 第十四刀把 `Skill(pdf_read)` timeline metadata 纳入 evidence `modalityRuntimeContracts`,并让 replay / grader 继续检查 PDF 读取 trace,形成入口 -> executor -> evidence -> replay 的闭环。
12. `pdf_extract` 第十五刀把 `@读PDF / @pdf / @read_pdf` 收为 registry entry binding,前端发送只消费 registry 派生的 `entry_source`,入口层不再拥有底层 contract 常量。
13. `voice_generation` 第十六刀把 `@配音` 服务型入口接到底层 `voice_runtime` 运行合同,前端与 Rust prompt 侧都会保留同一份 contract snapshot。
14. `voice_generation` 第二十二刀把 voice runtime / service scene timeline args 或 metadata 纳入 evidence `modalityRuntimeContracts`,并让 replay / grader 继续检查 `service_scene_launch(scene_key=voice_runtime)` 或后续 audio_task/audio_output 证据。
15. `web_research` 第十七刀把 `@搜索 / @深搜 / @站点搜索` 收为 registry entry binding,并把三条 research launch metadata 与 Rust prompt 收敛到同一份 `web_research` contract snapshot。
16. `web_research` 第十八刀把 `Skill(research)` / `Skill(site_search)` tool timeline args 或 metadata 纳入 evidence `modalityRuntimeContracts`,并让 replay / grader 继续检查联网研究 executor trace,形成入口 -> executor -> evidence -> replay 的闭环。
17. `web_research` 第十九刀把 evidence `modalityRuntimeContracts` 从 raw snapshots 扩展为 `snapshotIndex`,提供 contract/source/routing/tool trace 汇总,使联网研究合同进入可检索索引层。
18. `web_research` 第二十刀把 `@研报 / @竞品` 收为报告型子入口:入口仍走 `report_skill_launch -> Skill(report_generate)`,底层合同、evidence、replay 统一归入 `web_research`。
19. `text_transform` 第二十一刀把 `@读文件 / @总结 / @翻译 / @分析 / @发布合规 / @Logo拆解` 收为文本/文档转换子入口:入口仍分别走 `summary_skill_launch`、`translation_skill_launch`、`analysis_skill_launch`,底层合同、evidence、replay 统一归入 `text_transform`。
20. `voice_generation` 第二十三刀新增最小 `audio_generate` artifact protocol:`create_audio_generation_task_artifact` / `lime_create_audio_generation_task` 写入标准 `audio_task/audio_output` payload,前端 API、DevBridge mock、task index、evidence pack 与 replay grader 都消费同一份 `voice_generation` 合同快照。
21. `voice_generation` 第二十四刀把 `audio_generate` tool result 恢复为聊天区任务卡与 `.lime/runtime/audio-generate/*.md` 运行时文档;打开任务卡不再尝试展示隐藏 `.lime/tasks/audio_generate/*.json`,而是进入可读的音频任务 viewer,同时继续把 task JSON 作为 primary source。
22. `voice_generation` 第二十五刀新增最小 `audio_output` 完成态回写:`complete_audio_generation_task_artifact` 只更新标准 `audio_generate` task file,把 `audio_path / mime_type / duration_ms` 同步写入 payload、`payload.audio_output` 与 `record.result`,并通过同一媒体任务事件刷新前端,不新增云端执行协议。
23. `voice_generation` 第二十六刀把完成态回流接回聊天任务卡:`useWorkspaceAudioTaskPreviewRuntime` 监听同一媒体任务事件并轮询已追踪的 `audio_generate` task file,从 `payload.audio_output` / `record.result.audio_output` 恢复 `audioUrl / mimeType / durationMs`,让执行器回写后能直接在现有轻卡中出现可播放结果。
24. `voice_generation` 第二十七刀新增最小音频执行器骨架:`create_audio_generation_task_artifact` 创建标准 `audio_generate` 后会进入同一媒体任务执行链;在真实音频 provider 尚未接通时,worker 明确把 task 写成 `failed`、`last_error.code=audio_worker_unavailable`、`payload.audio_output.status=failed`,不伪造 `audio_path`,也不回流 `legacy_tts_test_command`。
25. `voice_generation` 第二十八刀把音频执行器从纯骨架推进到 OpenAI-compatible provider seam:worker 通过 current API Key Provider 解析 `provider_id/model/api_key/base_url`,调用 `/audio/speech` 获取真实音频 bytes,保存到 `.lime/runtime/audio/<task_id>.mp3`,并用同一 task artifact 回写 `payload.audio_output.completed`、`record.result.audio_output` 与 `worker_id=lime-audio-worker`;provider 未配置、resolver 缺失或非 OpenAI-compatible client 时分别写回 `audio_provider_unconfigured` / `audio_provider_resolver_unavailable` / `audio_provider_client_missing`,不回 legacy。
26. `voice_generation` 第二十九刀把 worker 失败态回流到聊天音频任务卡:`useWorkspaceAudioTaskPreviewRuntime` 从 `payload.audio_output` / `record.last_error` 解析 `audio_provider_*` 错误码、错误原因与 retryable,不保留失败 artifact 里不存在的 `audio_path`;`TaskMessagePreview` 显示 Provider 错误码和原因,让“未配置 provider / resolver 不可用 / adapter 缺失”成为用户可见事实,而不是泛化成普通失败。
27. `voice_generation` 第三十刀把完成/失败态同步到同一个运行时文档 viewer:音频任务轮询更新 `taskPreview` 的同时,会重建 `.lime/runtime/audio-generate/*.md` 对应的 ArtifactDocument metadata;打开任务卡时看到的是 `audio_output.completed` 的音频路径或 `audio_provider_*` 失败原因,而不是创建任务时的旧“等待执行器”文档。
28. `voice_generation` 第三十一刀把 `audio_output` 细节接入 evidence / replay:`audio_task.modality_runtime_contract` 快照现在携带 `audioOutput.status/audioPath/providerId/model/workerId/errorCode`,`snapshotIndex.audioOutputIndex` 可检索完成/失败态;replay tags、failure modes、expected 与 grader 会区分 `audio_output.completed` 和 Provider 失败,禁止把已完成音频降级成 Markdown 文本、普通聊天或通用文件卡。
29. `voice_generation` 第三十二刀把 `audio_output` 细节接入统一媒体任务索引:`list_media_task_artifacts` 的 `modality_runtime_contracts` 现在输出 `audio_output_count`、`audio_output_statuses`、`audio_output_error_codes` 与每个 snapshot 的 `audio_output_*` 字段;前端类型与浏览器 mock 同步,任务列表/恢复逻辑不必重新打开 task JSON 才能知道音频已完成、失败或缺少 Provider。
30. `voice_generation` 第三十三刀让聊天音频任务恢复优先消费统一媒体任务索引:`useWorkspaceAudioTaskPreviewRuntime` 会先读取 `list_media_task_artifacts(modalityContractKey=voice_generation, taskType=audio_generate)` 的 `audio_output_*` snapshot,把完成态音频路径或 Provider 失败直接同步到任务卡与同一条运行时文档;只有索引缺失该 task 或没有 `audio_output_status` 时才回退读取单个 task artifact。
31. `browser_control` 第三十八刀把同一份 `browserActionIndex` 接入最小 `browser_replay_viewer`:Harness evidence 面板的“打开复盘”会构造 `browser_assist` Artifact,并由 `BrowserAssistRenderer` 消费 `browserActionIndex` 展示 action/session/URL/observation/screenshot 与最近 `browser_session` / `browser_snapshot` 项;本刀不新增 browser task 协议,viewer 仍消费 Evidence Pack 的同一事实源。
32. `audio_transcription` 第三十九刀把 `@转写 / @transcribe / @Audio Extractor` 收为 registry entry binding:前端 `transcription_skill_launch.transcription_task` 与 Rust prompt prepare 都会保留 `audio_transcription` 合同快照,`transcript` artifact graph 也反向登记该 current contract;本刀只建立底层合同,不新增真实转写 worker、完整 transcript viewer 或 replay 索引。
33. `audio_transcription` 第四十刀把 `transcription_generate` task artifact 接到底层合同事实源:Agent tool 与 CLI 回退入口现在写入标准 `.lime/tasks/transcription_generate/*.json`,payload 固化 `audio_transcription` contract、`requested_target=transcript` 与 `transcript.pending`;`list_media_task_artifacts` 输出 transcript 状态/来源/语言/格式,Evidence Pack 输出 `snapshotIndex.transcriptIndex`,Replay / grader 能识别缺失 task trace、缺失 transcript index 与 `transcript.failed`。
34. `audio_transcription` 第四十一刀接入最小 `lime-transcription-worker`:Agent tool 创建 task 后会启动转写执行链,OpenAI-compatible provider 成功时把响应写入 `.lime/runtime/transcripts/<task>.json|txt|srt|vtt` 并回写 `transcript.completed` / `record.result.transcript`;缺少 AppHandle、provider/model、API Key、source 文件、非 OpenAI-compatible adapter 或 provider 请求失败时回写 `transcript.failed` 与 `transcription_*` 错误码,不回退 frontend ASR、普通文件读取或 generic_file transcript。
35. `audio_transcription` 第四十二刀把 transcript 恢复层接回聊天区:`lime_create_transcription_task` 工具结果现在生成 `.lime/runtime/transcription-generate/*.md` 运行时文档;`useWorkspaceTranscriptionTaskPreviewRuntime` 监听同一媒体任务事件并优先按 `audio_transcription + transcription_generate` 查询 `list_media_task_artifacts`,从 `transcript_status/path/source/language/output_format/error_code` snapshot 恢复任务卡和 viewer,索引缺失时才回退读取单个 task artifact。
36. `audio_transcription` 第四十三刀把 transcript 文本接入最小校对 viewer:完成态恢复时读取 `transcript_path` 指向的 `.lime/runtime/transcripts/*` 文本,并把内容嵌入 `.lime/runtime/transcription-generate/*.md` ArtifactDocument 的 `code_block`,打开任务卡即可复制和校对转写文本;失败态仍只展示 `transcription_*` 错误码,不伪造 transcript 内容。
37. `audio_transcription` 第四十四刀把 transcript viewer 从纯文本推进到时间轴/说话人段落层:恢复层会解析 `.json` verbose transcript、SRT 与 VTT 的 segment / speaker / start-end 时间码,写回 `taskPreview.transcriptSegments`、聊天轻卡时间轴预览和 `.lime/runtime/transcription-generate/*.md` 的“转写时间轴(可逐段编辑校对)”表格;仍不读取隐藏 task JSON 作为主路径,也不新增前端 ASR 或 `generic_file` 旁路。
38. `audio_transcription` 第四十五刀把 transcript viewer 从只读校对推进到可保存校对稿:`.lime/runtime/transcription-generate/*.md` ArtifactDocument 会标记 `transcriptCorrectionEnabled`、提示保存不改写原始 ASR 输出;用户编辑“转写文本”或“转写时间轴”并保存时,Workbench 复用同一 ArtifactDocument 版本链写入 `transcriptCorrectionStatus=saved`、`transcriptCorrectionEditedBlockId`、`transcriptCorrectionSourceTranscriptPath`、`transcriptCorrectionTextLength`、`transcriptCorrectionSegmentCount`、`transcriptCorrectionSpeakerCount` 与 `transcriptSegmentsCorrected`,不新增 Tauri command、frontend ASR 或并行 patch 协议。
39. `audio_transcription` 第四十六刀把已保存校对稿显性化:保存后会插入/更新 `transcript-correction-status` callout,显示原始 ASR 输出保持不可变、原文/校对稿文本长度差异、段落差异和说话人数差异;同时写入 `transcriptCorrectionDiffSummary`,让后续专用 transcript viewer、diff 面板或导出器不必重新扫描 block 才能识别校对变更。
40. `Phase 3 / Phase 5` 第四十七刀新增最小 `ModalityExecutionProfile` 与 `ExecutorAdapter` 事实源:`modalityExecutionProfiles.json` 覆盖 7 个 current contracts 与 7 个 executor adapters,`governance:modality-contracts` 会校验 profile 覆盖、adapter 绑定、支持位、产物、权限、LimeCore policy 与 failure mapping;本刀只建立 current 治理事实源,不新增命令、bridge、mock 或运行时执行分支。
41. `Phase 3 / Phase 5` 第四十八刀把 profile / adapter 事实源接入前端 runtime contract resolver:`resolveModalityRuntimeContractBinding()` 现在会从 `modalityExecutionProfiles.json` 解析 `execution_profile` 与 `executor_adapter` 快照,并随 `runtime_contract` 进入所有 current launch metadata;本刀仍不新增 Tauri command、bridge、mock 或 Rust executor 分支,为下一步 Rust runtime preflight / evidence 可视化提供统一输入。
42. `Phase 3 / Phase 5` 第四十九刀把 profile / adapter 快照接入 Evidence Pack:`runtime_evidence_pack_service` 会从 `runtime_contract.execution_profile.profile_key` 与 `runtime_contract.executor_adapter.adapter_key` 提取 `executionProfileKey` / `executorAdapterKey`,并写入 `modalityRuntimeContracts.snapshots[]`、`snapshotIndex.executionProfileKeys`、`snapshotIndex.executorAdapterKeys` 与 `toolTraceIndex.items[]`;本刀仍不改变真实 executor 行为,只让 evidence/replay 主链能看见 Phase 3/5 决策输入。
43. `Phase 3 / Phase 5` 第五十刀把 profile / adapter 快照接入统一媒体任务索引:Rust `runtime_contract` snapshot 现在写入 `execution_profile.profile_key` 与 `executor_adapter.adapter_key`,`list_media_task_artifacts` 的 `modality_runtime_contracts` 输出 `execution_profile_keys`、`executor_adapter_keys`,并在每条 task snapshot 上暴露 `execution_profile_key` / `executor_adapter_key`;前端类型和浏览器 fallback mock 同步,图片、配音、转写任务列表不必重新打开 task JSON 才能查询 profile / adapter。
44. `Phase 3 / Phase 5` 第五十一刀把 adapter registry 接入媒体 worker 执行前检查:图片、配音、转写 worker 在进入真实执行器前会校验同一份 `runtime_contract.execution_profile.profile_key`、`executor_adapter.adapter_key` 与 `executor_binding.executor_kind/binding_key`,不匹配时以 `*_execution_profile_*`、`*_executor_adapter_*`、`*_executor_binding_*` 阻断;`list_media_task_artifacts` 每条 snapshot 同步暴露 `executor_kind` / `executor_binding_key`,Evidence Pack 会把 runtime preflight 阻断识别为 `runtime_preflight` / `blocked`,避免 adapter 错配继续落成普通 provider 失败。
45. `Phase 6` 第五十二刀把最小 LimeCore policy refs/snapshot 种进同一底层主链:central Rust contract helper、前端 runtime contract resolver 与浏览器 fallback mock 都会写入 `limecore_policy_refs` 和 `limecore_policy_snapshot(status=refs_declared, decision=not_evaluated)`;Evidence Pack 新增 `snapshotIndex.limecorePolicyIndex`,统一媒体任务索引新增 `limecore_policy_refs`、`limecore_policy_snapshot_count/statuses` 与每条 snapshot 的 policy refs/status/decision。该刀只让后续 allow / ask / deny 有审计字段,不新增命令、不实现 LimeCore 云 run/poll,也不把上层 `@` 入口提前接成云执行协议。
46. `Phase 6` 第五十三刀把 policy snapshot 从纯 refs seed 推进到本地默认决策摘要:central Rust contract helper、前端 runtime contract resolver 与浏览器 fallback mock 现在写入 `status=local_defaults_evaluated`、`decision=allow`、`decision_source=local_default_policy`、`decision_scope=local_defaults_only`、`decision_reason=declared_policy_refs_with_no_local_deny_rule` 与 `unresolved_refs`;Evidence Pack 和统一媒体任务索引同步暴露 decision source / unresolved refs。该 `allow` 仅表示本地默认策略没有阻断 current 路由,不代表真实 LimeCore tenant policy、provider offer 或 gateway policy 已放行。
47. `Phase 6` 第五十四刀把 policy decision 摘要补成可审计输入清单:`limecore_policy_snapshot.policy_inputs[]` 为每个 policy ref 标记 `declared_only / modality_runtime_contract / limecore_pending`,`missing_inputs[]` 明确哪些控制面输入还未由真实 LimeCore 命中;Evidence Pack 的 `limecorePolicyIndex` 与统一媒体任务索引同步暴露 `missingInputs` / `limecore_policy_missing_inputs`。该刀仍不接云 run/poll,也不把本地默认 `allow` 伪装成 tenant/provider/gateway 放行。
48. `Phase 6` 第五十五刀把 policy gap 推进到 Harness evidence 可见面:`HarnessStatusPanel` 读取 `observabilitySummary.modalityRuntimeContracts.snapshotIndex.limecorePolicyIndex`,展示 `LimeCore 策略缺口` 卡片、控制面 refs、missing inputs、local default decision、profile / adapter 和 `declared_only / limecore_pending` 输入状态;该刀仍不新增命令、不接 LimeCore 云执行,也不把上层 `@` 入口当作策略事实源。
49. `Phase 6` 第五十六刀把 policy gap 推进到 replay/grader 回归面:`runtime_replay_case_service` 现在会从 `limecorePolicyIndex` 派生 `limecore-policy` / `limecore-policy-gap` / `limecore-local-default-policy` suite tags 与 `limecore_policy_missing_inputs` / `limecore_policy_local_defaults_only` failure modes,并把 policy refs、missing inputs、decision source 写入 expected / grader 检查;该刀只增强复盘验收,不新增命令、不接云 run/poll。
50. `Phase 6` 第五十七刀补真实 policy hit value 的空接线结构:`limecore_policy_snapshot`、Evidence Pack `limecorePolicyIndex`、统一媒体任务索引、前端 normalizer/types 与浏览器 fallback mock 都携带 `pending_hit_refs`、`policy_value_hits`、`policy_value_hit_count=0`;这为后续真实 `model_catalog / provider_offer / tenant_feature_flags / gateway_policy` 命中值提供稳定落点,但当前仍不写假值、不接 LimeCore 云调用。
51. `Phase 6` 第五十八刀补最小 policy hit resolver seam:central Rust contract helper 与前端 runtime contract resolver 现在能消费传入的 `policy_value_hits(status=resolved)`,自动派生 `evaluated_refs`、收缩 `missing_inputs / pending_hit_refs`,Evidence Pack、统一媒体任务索引与浏览器 fallback mock 也会在 snapshot 只携带命中值时按同一规则派生待命中 refs;本刀仍不接云 run/poll、不改本地默认 `decision=allow/local_defaults_only`,避免把“输入命中”伪装成真实策略放行。
52. `Phase 6` 第五十九刀接最小本地 `model_catalog` hit producer:图片任务执行前已有的 `model_registry` 能力评估现在会写入 `runtime_contract.limecore_policy_snapshot.policy_value_hits[]`,并把 `model_catalog` 从 `missing_inputs / pending_hit_refs` 移入 `evaluated_refs`;该刀仍不接 `provider_offer / tenant_feature_flags`,也不把目录命中升级成真实策略放行。
53. `Phase 6` 第六十刀接最小本地 `provider_offer` hit producer:图片任务进入真实执行器前会先用已解析的 `ImageGenerationRunnerConfig`、非空 API key 与 task payload 的 `provider_id/model` 写入 `policy_value_hits(status=resolved, value_source=local_provider_offer)`,并把 `provider_offer` 从 `missing_inputs / pending_hit_refs` 移入 `evaluated_refs`;该 hit 只保留 endpoint origin/path、adapter 与 credential 状态,不序列化 API key,也不把本地默认 `allow` 升级成真实策略放行。
54. `Phase 6` 第六十一刀接请求侧 `gateway_policy` hit producer:Browser Assist 与 Web Research 类 launch 会复用 `harness.oem_routing` / Rust `oem_policy` 已有事实源,把 tenant/provider/quota/can_invoke/fallback 输入写入 `policy_value_hits(status=resolved, value_source=request_oem_routing)`,并把 `gateway_policy` 从 `missing_inputs / pending_hit_refs` 移入 `evaluated_refs`;该 hit 不包含 token、不新增 Tauri command、不接 LimeCore 云 run/poll,也不把本地默认 `allow` 升级成真实网关策略放行。
55. `Phase 6` 第六十二刀接请求侧 `tenant_feature_flags` hit producer:Workspace send metadata 会把 OEM Cloud bootstrap snapshot 的 feature flags 以 `harness.tenant_feature_flags` 透传给 Rust runtime;central contract helper 会把该输入写入 `policy_value_hits(status=resolved, value_source=oem_cloud_bootstrap_features)`,并把 `tenant_feature_flags` 从 `missing_inputs / pending_hit_refs` 移入 `evaluated_refs`。该 hit 只记录 boolean feature flags 与 tenant id,不包含 session token,不新增 Tauri command,不接云 run/poll,也不把功能开关命中解释成真实策略放行。
56. `Phase 6` 第六十三刀补最小 allow / ask / deny evaluator seam:central Rust contract helper 与前端 runtime contract resolver 现在都会写入 `policy_evaluation`;当所有 refs 都有 `resolved` hit 且没有阻断信号时,snapshot 进入 `status=policy_inputs_evaluated`、`decision_source=policy_input_evaluator`、`decision_scope=resolved_policy_inputs`,并按 gateway can_invoke / offer_state / quota_low、tenant gatewayEnabled、model_catalog capability 与 provider credential state 推导 `allow / ask / deny`。该 evaluator 只消费已命中的 policy inputs,不接云 run/poll;仍有 missing inputs 时顶层 `decision` 保持本地默认解释。
57. `Phase 6` 第六十四刀把 policy decision explanation 接入 thread read:`AgentRuntimeThreadReadModel` 会扫描最近的 ToolCall metadata / FileArtifact content 中的 runtime contract,把 LimeCore policy snapshot 投影到 `runtime_summary.limecorePolicy`,包含顶层 decision、decision_source/scope/reason、refs、missing/pending refs、hit count 与 evaluator blocking/ask/pending refs。该刀只让现有 thread read 能解释最近一次 policy 决策,不新增 Tauri command、不接云 run/poll,也不把 thread read 变成云审计事实源。
58. `Phase 6` 第六十五刀把 policy evaluator explanation 接入统一媒体任务索引:`list_media_task_artifacts.modality_runtime_contracts` 现在汇总 evaluation status / decision / decision source 与 blocking / ask / pending refs,每条 snapshot 也输出对应字段;Rust index、前端类型、浏览器 fallback mock 与 `mediaTasks` 回归同步。该刀只让任务索引能展示 evaluator 解释,不新增 Tauri command、不接 LimeCore 云 run/poll,也不改变顶层 local default decision 语义。
59. `Phase 6` 第六十六刀把 policy evaluator explanation 接入配音/转写任务卡恢复层:新增共享 meta helper,`useWorkspaceAudioTaskPreviewRuntime` 与 `useWorkspaceTranscriptionTaskPreviewRuntime` 从统一媒体任务索引 snapshot 生成 `LimeCore 策略输入待命中 / 阻断 / 需确认` 轻卡标签,并补 audio input gap 与 transcription deny 回归。该刀只消费现有索引,不新增命令、不碰上层 `@`,也不把 pending input gap 解释成真实用户确认。
60. `Phase 6` 第六十七刀把 policy evaluator explanation 接入图片任务 viewer:`useWorkspaceImageTaskPreviewRuntime` 从图片 task artifact 的 `runtime_contract.limecore_policy_snapshot.policy_evaluation` 解析 snapshot / missing / pending / blocking / ask refs,`ImageTaskViewer` 复用共享 helper 展示 `LimeCore 策略输入待命中: N` 等轻量标签,并补图片恢复与 viewer 回归。该刀只消费已有 runtime contract,不新增命令、不接 LimeCore 云 run/poll,也不把图片上层 `@` 入口改成策略事实源。
61. `Phase 6` 第六十八刀把 policy evaluator explanation 接入图片消息轻卡:`ImageWorkbenchMessagePreview` 复用共享 helper,从 `preview.runtimeContract` 解析同一份 evaluator 摘要,并在聊天区图片预览顶部展示 `LimeCore 策略输入待命中 / 阻断 / 需确认` 胶囊标签;该刀只让图片消息卡消费已有 runtime contract,不新增命令、不接 LimeCore 云 run/poll,也不改图片消息的布局骨架。
62. `Phase 5/6` 第七十一刀把 profile / adapter 摘要接入 thread read:`AgentRuntimeThreadReadModel` 现在会扫描最近 ToolCall metadata / FileArtifact content 中的 runtime contract,并把 `contractKey`、`routingSlot`、`requiredCapabilities`、`profileKey`、`executorAdapterKey`、`executorKind`、`executorBindingKey` 写入 `runtime_summary.modalityRuntime`。该刀只投影已有底层合同,不新增命令、不接云 run/poll,也不把上层 `@` 命令当执行合同事实源。
63. `Phase 5/6` 第七十二刀把 profile / adapter / binding 摘要合入 `SessionExecutionRuntimeTaskProfile`:`build_runtime_task_profile()` 现在会从 request metadata 中已有的 `runtime_contract / modality_runtime_contract` 提取 `modalityContractKey`、`routingSlot`、`executionProfileKey`、`executorAdapterKey`、`executorKind`、`executorBindingKey`,并按 `modalityExecutionProfiles.json` 补齐 `permissionProfileKeys` 与 `userLockPolicy`;`lime_runtime.task_profile` 和 `task_profile_resolved` 事件会承载同一摘要,前端 API 类型与协议测试同步。该刀仍不新增 Tauri command、不接 LimeCore 云 run/poll、不把上层 `@` 命令当事实源;权限判定执行、用户显式模型锁定 enforcement 与真实 `gateway:*` adapter preflight 继续后置。
64. `Phase 5/6` 第七十三刀把 `routingSlot` 推进到最小模型能力 enforcement:`request_model_resolution` 现在会把 `routingSlot` 映射成 runtime model capability requirements,并用它过滤候选池、fallback 候选与多候选自动重选;`browser_reasoning_model` 会要求 reasoning,`image_generation_model` 会允许专用图片模型进入候选池,显式用户模型锁定仍 honored,但 `capability_gap` 会输出对应 `*_candidate_missing`。第 113 刀已把显式用户锁定导致的 capability gap 从“只解释”推进为 `user_locked_capability_gap` 执行前阻断;该刀本身不新增 Tauri command、不接 LimeCore 云 run/poll、不触碰上层 `@` 命令。
65. `Phase 5/6` 第七十四刀把 `permissionProfileKeys` 推进到最小 runtime permission summary:`request_model_resolution` 现在会生成 `SessionExecutionRuntimePermissionState`,随 `lime_runtime.permission_state` 写入 turn metadata;runtime view、thread read fallback summary、前端 execution runtime 类型与内部辅助任务 metadata 都能识别该摘要。`ask_user_question` 不视为风险权限,`blockingProfileKeys` 本刀保持为空,因此这一步只解释权限声明和需确认 profile,不执行真实授权或阻断。
66. `Phase 5/6` 第七十五刀把 `permission_state` 推到 thread read 结构化读取面:`AgentRuntimeThreadReadModel` 现在直接暴露 `permission_state`,前端 `AgentRuntimeThreadReadModel` 类型同步引用 `AsterSessionExecutionRuntimePermissionState`。这一步让上层读取完整 `requiredProfileKeys / askProfileKeys / blockingProfileKeys / notes`,不再只能从 `runtime_summary` 数字摘要或隐藏 turn metadata 推断;仍不新增事件、不阻断执行、不接真实权限授权。
暂不做:
1. 暂不把所有 `@`、按钮、Scene 批量登记到 `bound_entries`;已进入主链的 `image_generation`、`browser_control`、`pdf_extract`、`voice_generation`、`web_research`、`text_transform`、`audio_transcription` 继续按合同逐条收口,未进入 current 事实源的入口不批量扩张。
2. 暂不把 LimeCore 写成默认执行器;LimeCore 仍只作为 catalog / policy / offer / audit 控制面。
3. 暂不批量改造视频、PDF、搜索、研报等其他 viewer;已补到最小 viewer / index 的 browser 与 voice 继续沿同一事实源演进,未形成稳定 artifact graph 的能力先不另开 viewer 协议。
4. 暂不新增 PDF task artifact / reader viewer;PDF 本轮只收 executor trace、evidence 和 replay,避免在底层事实源未完全稳定前再开一套产物协议。
5. 暂不新增完整音频工作台,也暂不把 Gemini / Azure 专有语音 API、LimeCore 云 run/poll 或语音后处理工作流并入主链;voice 目前只提供最小 `audio_generate` 任务卡、运行时文档 viewer、OpenAI-compatible speech provider seam 与完成/失败态回流。未配置可用 provider 时仍必须显式失败,不伪造云端提交,也不把普通聊天文本或通用文件卡当作音频产物。
6. 暂不新增独立 `report_generation` 合同;`@研报 / @竞品` 继续走 `report_skill_launch -> Skill(report_generate)` 主链,但其底层能力归属先收敛到 `web_research`,避免把 report artifact 协议提前扩张成第二套事实源。
7. 暂不新增独立 `summary_generation`、`translation`、`analysis`、`publish_compliance` 或 `logo_decomposition` 合同;这组轻量文本/文档转换入口先统一收敛到 `text_transform`,避免把上层 `@` 命令提前扩张成平行底层事实源。
8. 暂不新增非 OpenAI-compatible ASR adapter 或本地离线 ASR 执行器;`audio_transcription` 当前交付标准 `transcription_generate` task writer、`lime-transcription-worker`、`transcript.completed/failed` 回写、统一媒体任务索引、聊天任务卡、可编辑校对运行时文档 viewer、JSON/SRT/VTT 时间轴与说话人段落展示、ArtifactDocument 版本化校对稿保存、校对稿状态/差异摘要、Evidence `transcriptIndex` 与 Replay 检查。
9. 暂不在本刀实现完整权限判定系统、同 turn 自动恢复、LimeCore 云端 allow / ask / deny evaluator、真实 `gateway:*` adapter preflight 或完整 GUI/evidence 可视化;当前已让图片、配音、转写媒体 worker 消费 Phase 3 / Phase 5 的 profile / adapter 事实源做最小执行前检查,并把 Browser Assist preflight、通用 Skill metadata/preflight、`lime_run_service_skill` voice compat guard、Phase 6 的 LimeCore policy refs/snapshot、model/offer/gateway/tenant hit producers、最小本地 policy input evaluator、thread read 摘要、`SessionExecutionRuntimeTaskProfile` profile/adapter/binding merge、`routingSlot` 模型能力 enforcement、`permissionProfileKeys` 最小 runtime permission summary、thread read 结构化 `permission_state`、统一媒体任务索引 explanation、配音/转写任务卡 meta、图片 viewer policy 标签与图片消息轻卡标签接进 current 主链。显式用户模型锁定已能输出 capability gap,且 `explicit_model_lock` gap 会以 `user_locked_capability_gap` 在模型执行前阻断;权限 profile 已能输出需确认摘要,Evidence / Replay 已把 `not_requested / requested` 未解决确认作为交付阻断事实;未 resolved 的 `requires_confirmation` 也已在 prelude 后、模型执行前阻断 turn;最小 `runtime_permission_confirmation:*` / `RequestUserInput` 权限确认、`agent_runtime_respond_action` 写回和下一轮 `resolved/denied` metadata merge 已接入;`runtime_user_lock_capability:*` 的本地最小确认式恢复也已接入,同 `turn_id` 恢复时可释放本轮显式 provider/model 偏好并重新走模型解析。后续继续把同一决策扩展到完整权限授权、用户锁定 gap 的完整 GUI 自动恢复、云端策略 evaluator、真实 Gateway adapter、更多任务卡与更多 GUI 可视化。
## 分类
### current
- `ModalityRuntimeContract`
- `runtime fact map`
- `domain artifact graph`
- `execution profile`
- `capability matrix`
- `evidence pack`
- `LimeCore catalog / policy` 作为云控制面
### compat
- 旧 `@` 命令 metadata,只能绑定到底层 contract
- 旧 `generic_file` 展示,只能作为兜底 artifact kind
- 旧本地 CLI adapter,只能作为 typed `local_cli` executor
- 旧 `lime_run_service_skill` 只能作为服务型场景 compat guard;`voice_generation` 必须携带并校验 `service_skill:voice_runtime` 合同,不允许恢复云 run/poll。
### deprecated
- 入口直接创建多模态 task
- viewer 自己猜 artifact 类型
- skill 内部自由选择 Bash / WebSearch / ToolSearch 作为首刀
### dead
- 把 LimeCore 当成所有多模态入口的默认执行器
- 用 `@` 命令名作为底层 contract 主键
- 用通用文件卡承载所有多模态结果
## 进度日志
- 2026-04-29:创建本计划;固定本轮从 Phase 0/1 开始,先做底层 fact map、contract schema 和轻量守卫,不改上层 `@` 命令。
- 2026-04-29:已新增 `runtime-fact-map.md`、`contract-schema.md`、`modalityRuntimeContracts.json` 与 `check-modality-runtime-contracts.mjs`;首批 5 个 current contract 为 `image_generation`、`browser_control`、`pdf_extract`、`voice_generation`、`web_research`,`bound_entries` 暂为空,等待 Phase 7 再接上层入口。
- 2026-04-29:已接入 `npm run governance:modality-contracts` 并通过;同时通过 `npm run harness:doc-freshness`、脚本语法检查和本轮触达 Markdown 链接 / code fence 检查。
- 2026-04-29:继续推进 Phase 2,新增 `capability-matrix.md` 与 `modalityCapabilityMatrix.json`;首批登记 13 个 capability 与 9 个 model role,并扩展 contract 守卫校验 `required_capabilities` / `routing_slot` 引用关系。
- 2026-04-29:已把 `npm run governance:modality-contracts` 接入 `npm run test:contracts`;`test:contracts`、`harness:doc-freshness`、脚本语法检查与本轮触达 Markdown 链接 / code fence 检查通过。
- 2026-04-29:开始真实运行时实现,完成 `image_generation` vertical slice 第一刀:`prepare_image_skill_launch_request_metadata` 注入底层 contract,`lime_create_image_generation_task` request builder / schema 补齐 contract 字段,`create_image_generation_task_artifact_inner` 将 `modality_contract_key`、`required_capabilities`、`routing_slot`、`runtime_contract` 写入 `image_generate` artifact payload,并补 Rust / TS 定向断言。
- 2026-04-29:验证通过 `npm run test:contracts`、`npm test -- src/components/agent/chat/workspace/useWorkspaceImageWorkbenchActionRuntime.test.tsx src/lib/tauri-mock/core.test.ts`、`npm run typecheck`,并通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml test_prepare_image_skill_launch_request_metadata_materializes_input_refs`;继续补跑图片 artifact / tool request Rust 定向测试时,当前工作区已有 `src-tauri/crates/agent/src/session_store.rs` 编译错误阻断,错误点不属于本轮 `image_generation` contract 改动。
- 2026-04-29:继续 `image_generation` vertical slice 第二刀:在图片任务 worker 进入 `lime_media_runtime::execute_image_generation_task_with_hook` 前读取 task artifact 的 contract / required capabilities / routing slot,并对明显文本模型候选(例如 `gpt-5.2`)输出 `image_generation_model_capability_gap`,阻止 contract 要求的图片生成任务误路由到普通文本模型。
- 2026-04-29:第二刀验证通过 `npm run test:contracts` 与 `npm run typecheck`;Rust 定向测试 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml validate_image_generation_task_execution_contract_should_reject_text_model_candidate` 被当前工作区已有 `src-tauri/src/commands/aster_agent_cmd/command_api/runtime_api.rs` 缺少 `AgentRuntimeSessionHistoryCursor` import 的编译错误阻断,未继续扩大范围修 unrelated 编译债。
- 2026-04-29:已用最小 import 修复 `runtime_api.rs` 的 `AgentRuntimeSessionHistoryCursor` 编译阻塞,并补通 `image_generation` preflight 的 Rust 定向验证:明显文本模型会被 `image_generation_model_capability_gap` 阻止,`gpt-image-1` 类图片模型候选可通过。
- 2026-04-29:继续 `image_generation` vertical slice 第三刀:`runtime_evidence_pack_service` 现在会从 `.lime/tasks/image_generate/*.json` 提取 `ModalityRuntimeContract` 与 routing 决策快照,写入 `runtime.json` / `artifacts.json` 的 `modalityRuntimeContracts`,并把 `modalityRuntimeContract` 纳入 `observabilitySummary.signalCoverage`;当 preflight 失败时,evidence 中可直接看到 `routingEvent=routing_not_possible`、`routingOutcome=blocked`、`failureCode=image_generation_model_capability_gap`。
- 2026-04-29:第三刀验证通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml should_export_modality_runtime_contract_snapshot_from_failed_image_task`、`validate_image_generation_task_execution_contract_should_accept_image_model_candidate`、`should_export_auxiliary_runtime_snapshots_from_image_task_artifact`、`npm run test:contracts`、`npm run typecheck` 与 `git diff --check`。
- 2026-04-29:继续 `image_generation` vertical slice 第四刀:`runtime_replay_case_service` 现在从 evidence `runtime.json` 读取 `modalityRuntimeContracts`,写入 replay `input.json` / `expected.json` / `grader.md` / `evidence-links.json`;同时把 `modality-runtime-contract`、`modality-image_generation`、`routing-not-possible` 纳入 suite tags,把 `modality_contract_routing_blocked` 与 `image_generation_model_capability_gap` 纳入 failure modes,并在 grader 中增加“多模态运行合同检查”,使模型误路由样本可被回归评分而不是只停留在 evidence 里。
- 2026-04-29:第四刀验证通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml should_carry_modality_runtime_contract_into_replay_case`、`should_export_runtime_replay_case_to_workspace`、`npm run test:contracts` 与 `git diff --check`。
- 2026-04-29:继续 `image_generation` vertical slice 第五刀:图片任务 worker 的模型能力 preflight 从纯字符串启发式推进到 `ModelRegistryState` / `EnhancedModelMetadata`,优先按 `task_families` 与 `output_modalities` 判断 `ImageGeneration` / `ImageEdit` / `Image` 输出能力;只有 registry 找不到模型时才回退到文本模型启发式,避免 `gpt-5.2` 这类普通文本模型误进图片执行器,同时允许 registry 明确声明的图片模型通过。
- 2026-04-29:第五刀进一步把 registry 判定结果持久化为 `image_generate` task payload 的 `model_capability_assessment`,并同步进入 evidence `modalityRuntimeContracts.snapshots[].modelCapabilityAssessment` 与 replay 的 `modalityContractChecks`,让后续回归能区分 `model_registry` 判定和 heuristic fallback,而不是只看到最终失败码。
- 2026-04-29:第五刀验证通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml assess_image_generation_model_capability_should_use_output_modality`、`assess_image_generation_model_capability_should_reject_registered_text_model`、`assess_image_generation_model_capability_should_match_provider_model_id`、`validate_image_generation_task_execution_contract_should_reject_registry_text_model`、`validate_image_generation_task_execution_contract_should_trust_registry_image_model`、`patch_image_task_model_capability_assessment_should_persist_registry_snapshot`、`should_export_modality_runtime_contract_snapshot_from_failed_image_task`、`should_carry_modality_runtime_contract_into_replay_case`、`npm run test:contracts` 与本轮触达文件 `git diff --check`。
- 2026-04-29:继续 `image_generation` vertical slice 第六刀:`list_media_task_artifacts` 现在可以按 `modalityContractKey` 与 `routingOutcome` 筛选图片任务,并在返回结果中附带 `modality_runtime_contracts` 索引摘要;索引包含 contract keys、routing outcome 计数、blocked 计数、registry assessment 计数与每个 task 的 routing snapshot,使 task index 成为多模态运行合同的可检索事实源,而不是只能靠单个 task file 或 evidence pack 反查。
- 2026-04-29:第六刀同步前端 `ListMediaTaskArtifactsRequest` / `ListMediaTaskArtifactsOutput` 类型与浏览器 fallback mock;图片任务恢复测试继续使用统一媒体任务接口,mock 也返回相同 `modality_runtime_contracts` 结构,避免浏览器链路假绿。
- 2026-04-29:第六刀验证通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml list_media_task_artifacts_inner_should_index_modality_contract_routing_blocks`、`media_task_artifact_controls_should_share_same_task_file_protocol`、`npm test -- src/lib/api/mediaTasks.test.ts src/lib/tauri-mock/core.test.ts src/components/agent/chat/workspace/useWorkspaceImageTaskPreviewRuntime.test.tsx`、`npm run typecheck`、`npm run test:contracts` 与本轮触达文件 `git diff --check`。
- 2026-04-29:继续 `image_generation` vertical slice 第七刀:前端图片任务恢复 runtime 现在会从 task artifact payload / `last_error` 解析 `runtimeContract` 快照,写入 `ImageWorkbenchTask` 与 `MessageImageWorkbenchPreview`;图片工作台 viewer 增加轻量胶囊标签,区分“运行合同 · 已按 image_generation 路由”“运行合同阻止 · image_generation_model_capability_gap”以及“模型能力来自 model_registry · 不支持图片生成”,让第六刀 task index 的路由事实进入用户可见 viewer。
- 2026-04-29:第七刀验证通过 `npm test -- src/components/agent/chat/components/ImageTaskViewer.test.tsx src/components/agent/chat/workspace/useWorkspaceImageTaskPreviewRuntime.test.tsx`、`npm run typecheck`、`npm run test:contracts`、`npm run verify:gui-smoke` 与本轮触达文件 `git diff --check`。
- 2026-04-29:继续 `image_generation` vertical slice 第八刀:`image_generation.bound_entries` 现在登记 `@配图 / @image / /image / @Vision 1`、`@海报 / @Flyer 3`、`@修图`、`@重绘`、`@分镜`、文稿正文配图和图片工作台续做入口;entry binding 只声明 `entry_source`、`launch_metadata_path` 和 input mapping,不拥有 task / artifact / viewer / evidence 事实源。
- 2026-04-29:第八刀新增 `src/lib/governance/modalityRuntimeContracts.ts`,前端 `imageSkillLaunch.ts` 改为从 registry 读取 `contractKey / modality / requiredCapabilities / routingSlot / runtimeContract`,避免上层入口继续硬编码底层 contract;守卫同步要求 entry binding 声明 `entry_source` 且 launch metadata 留在 `harness.*`。
- 2026-04-29:第八刀验证通过 `npm run governance:modality-contracts`、`npm test -- src/lib/governance/modalityRuntimeContracts.test.ts`、`npm test -- src/components/agent/chat/workspace/useWorkspaceImageWorkbenchActionRuntime.test.tsx src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx`、`npm run typecheck`、`npm run test:contracts`、`npm run verify:gui-smoke` 与本轮触达文件 `git diff --check`。
- 2026-04-29:继续第九刀 `browser_control`:泛化 `ModalityRuntimeContract` registry resolver,新增 Browser Assist launch metadata helper;`@浏览器 / @Browser Agent / @Mini Tester` 发送时保留原始消息并关闭 WebSearch,同时把 `browser_control` 合同快照注入 `harness.browser_assist`,`buildHarnessRequestMetadata` 负责保留这些底层合同字段并补齐 profile / backend / auto launch。
- 2026-04-29:第九刀验证通过 `npm test -- src/lib/governance/modalityRuntimeContracts.test.ts src/components/agent/chat/utils/harnessRequestMetadata.test.ts src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx`、`npm run governance:modality-contracts`、`npm run typecheck`、`npm run test:contracts`、`npm run verify:gui-smoke` 与本轮触达文件 whitespace / `git diff --check`。
- 2026-04-29:继续第十刀 `browser_control` 后端消费:新增 Rust 侧 `BROWSER_CONTROL_*` contract 常量与 `browser_control_runtime_contract()`,`BrowserAssistRuntimeHint` 现在会从 `harness.browser_assist` 解析 `modality_contract_key=browser_control`、`required_capabilities`、`routing_slot`、`runtime_contract` 与 `entry_source`;`LimeBrowserMcpTool` 成功或失败结果都会把这组字段写入 tool result metadata,让 Browser Assist 动作进入 runtime timeline 事实源。
- 2026-04-29:继续第十一刀 `browser_control` evidence:`runtime_evidence_pack_service` 现在会从 browser tool call metadata 导出 `modalityRuntimeContracts` snapshot,`source=browser_action_trace.modality_runtime_contract`、`routingEvent=browser_action_requested`、`routingOutcome=accepted`,使 Browser Assist 不再只能依赖图片 task artifact 的合同导出路径。
- 2026-04-29:第十/十一刀验证通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml test_extract_browser_assist_runtime_hint_from_harness_metadata`、`test_lime_browser_tool_attaches_modality_contract_metadata`、`should_export_browser_control_contract_snapshot_from_tool_metadata`、`npm run test:contracts`、`npm run verify:gui-smoke` 与本轮触达文件 `git diff --check`。
- 2026-04-29:继续第十二刀 `browser_control` replay/grader:`runtime_replay_case_service` 现在会把 `browser_control` 合同识别为 Browser Assist 回归样本,补 `browser-control`、`browser-assist`、`browser-action-trace` suite tags;`expected.json` / `grader.md` 会要求继续使用 `mcp__lime-browser__*`,禁止 WebSearch / 普通聊天替代真实浏览器动作,并检查 `browser_action_trace` / `browser_action_requested` 证据。
- 2026-04-29:第十二刀验证通过 `CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml should_carry_browser_control_contract_into_replay_grader_checks`、`should_carry_modality_runtime_contract_into_replay_case`、`npm run test:contracts`、`npm run verify:gui-smoke` 与本轮触达文件 `git diff --check`。
- 2026-04-29:继续第十三刀 `pdf_extract`:新增前端 registry resolver `resolvePdfExtractRuntimeContractBinding()`,`@读PDF` 发送时在 `harness.pdf_read_skill_launch.pdf_read_request` 注入 `pdf_extract` contract 快照;Rust 侧新增 `PDF_EXTRACT_*` 常量、`pdf_extract_runtime_contract()` 与 `insert_pdf_extract_contract_fields()`,`prepare_pdf_read_skill_launch_request_metadata` 会把缺省合同字段补齐到 launch 与 request,系统 prompt 显式要求 Skill(pdf_read) 保留 contract 字段、真实读取 PDF,并禁止 `frontend_direct_pdf_parse` / `generic_chat_summary_only`。
- 2026-04-29:第十三刀验证通过 `npm test -- src/lib/governance/modalityRuntimeContracts.test.ts src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx`、`npm run governance:modality-contracts`、`npm run typecheck`、`npm run test:contracts`、`npm run verify:gui-smoke`、`cargo fmt --manifest-path src-tauri/Cargo.toml --check`、`CARGO_TARGET_DIR=src-tauri/target/codex-modality-contract cargo test --manifest-path src-tauri/Cargo.toml pdf_read_skill_launch`、`cargo test --manifest-path src-tauri/Cargo.toml` 与本轮触达文件 `git diff --check`;`npm run verify:local` 初跑曾在全量 Rust 阶段暴露 PDF 断言误落到 resource_search 测试,已修复并用全量 `cargo test` 复验通过。
- 2026-04-29:继续第十四刀 `pdf_extract` evidence/replay:`runtime_evidence_pack_service` 现在会从 `Skill(pdf_read)` tool timeline metadata 或其 `args.pdf_read_request` 合同快照导出 `pdf_extract` 的 `modalityRuntimeContracts`,`source=pdf_read_skill_trace.modality_runtime_contract`、`routingEvent=executor_invoked`、`routingOutcome=accepted/failed`、`expectedRoutingSlot=base_model`。
- 2026-04-29:第十四刀同步 `runtime_replay_case_service`:replay tags 增加 `modality-pdf_extract`、`pdf-extract`、`pdf-read-skill`、`pdf-read-trace`,`expected.json` / `grader.md` 明确要求继续走 `Skill(pdf_read)` 或真实 `list_directory` / `read_file` 文件读取证据,并禁止 `frontend_direct_pdf_parse`、`generic_chat_summary_only`、ToolSearch、WebSearch 或 Grep 替代真实读 PDF。
- 2026-04-29:继续第十五刀 `pdf_extract` entry binding:`modalityRuntimeContracts.json` 现在登记 `at_pdf_read_command`,覆盖 `@读PDF / @pdf / @read_pdf`,并把 launch path 固定为 `harness.pdf_read_skill_launch.pdf_read_request`;`useWorkspaceSendActions` 从 `resolvePdfExtractRuntimeContractBinding().boundEntrySources` 读取 `entry_source`,只保留 `PDF_EXTRACT_DEFAULT_ENTRY_SOURCE` 作为 registry 缺省 fallback。
- 2026-04-29:继续第十六刀 `voice_generation`:新增前端 registry resolver `resolveVoiceGenerationRuntimeContractBinding()`,`voice_generation.bound_entries` 登记 `at_voice_command`;`@配音` 发送时在 `harness.service_scene_launch.service_scene_run` 注入 `voice_generation` contract 快照,后端 `prepare_service_scene_launch_request_metadata` 也会为 `voice_runtime` 旧 metadata 补齐合同字段,服务型场景 prompt 会输出底层合同、所需能力与 `runtime_contract(JSON)`,并禁止退回 `legacy_tts_test_command`。
- 2026-04-29:继续第十七刀 `web_research`:新增前端 registry resolver `resolveWebResearchRuntimeContractBinding()`,`web_research.bound_entries` 登记 `at_search_command`、`at_deep_search_command` 与 `at_site_search_command`;`@搜索 / @深搜 / @站点搜索` 发送时分别在 research / deep search / site search request 中注入 `web_research` contract 快照,Rust 侧 prepare 函数也会把缺省合同字段补齐到 launch 与 request,prompt 显式输出底层合同、所需能力与 `runtime_contract(JSON)`,并禁止退回模型记忆、本地文件搜索、通用 WebSearch 或 ToolSearch 偏航。
- 2026-04-30:继续第十八刀 `web_research` evidence/replay:`runtime_evidence_pack_service` 现在会从 `Skill(research)` / `Skill(site_search)` tool timeline 的 args 或 metadata 导出 `web_research` 的 `modalityRuntimeContracts`,`source=web_research_skill_trace.modality_runtime_contract`、`routingEvent=executor_invoked`、`routingOutcome=accepted/failed`、`expectedRoutingSlot=report_generation_model`;`runtime_replay_case_service` 同步增加 `web-research`、`research-skill`、`web-research-trace` tags,并在 expected / grader 中要求继续走 `Skill(research)` / `Skill(site_search)` 与真实 search*query / `lime_site*\*`证据,禁止`model_memory_only_answer`、`local_file_search_before_research_skill`、ToolSearch 或普通聊天替代。
- 2026-04-30:继续第十九刀 `web_research` 索引化:`runtime_evidence_pack_service` 的 `modalityRuntimeContracts` 现在输出 `snapshotIndex`,汇总 `contractKeys`、`sourceCounts`、`routingOutcomeCounts`、`expectedRoutingSlots` 与 `toolTraceIndex.items`;`web_research` replay input 会携带同一份索引,使回归样本能直接定位 `executorBindingKey=research`、`entrySource=at_search_command` 与 `web_research_skill_trace`,不再只能扫描 raw snapshots。
- 2026-04-30:继续第二十刀 `web_research` 报告型子入口:`web_research.bound_entries` 纳入 `at_report_command` 与 `at_competitor_command`,前端 `@研报 / @竞品` 发送会在 `harness.report_skill_launch.report_request` 注入同一份 `web_research` contract;Rust `report_skill_launch` prepare / prompt 也补齐合同字段,同时保留首刀 `Skill(report_generate)`,并把 evidence / replay 扩展到 `report_generate` trace,避免另开 `report_generation` 平行事实源。
- 2026-04-30:继续第二十一刀 `text_transform`:新增 `text_transform` contract 并登记 `at_file_read_command`、`at_summary_command`、`at_translation_command`、`at_analysis_command`、`at_publish_compliance_command`、`at_logo_decomposition_command`;前端 `@读文件 / @总结 / @翻译 / @分析 / @发布合规 / @Logo拆解`、Rust summary/translation/analysis prepare 与 prompt、evidence `text_transform_skill_trace`、replay tags / expected / grader 已统一收敛到同一份底层合同,入口层不再新增 summary / translation / analysis 平行事实源。
- 2026-04-30:继续第二十二刀 `voice_generation` evidence/replay:`runtime_evidence_pack_service` 现在会从 voice runtime / service scene timeline args 或 metadata 导出 `voice_generation` 的 `modalityRuntimeContracts`,`source=voice_generation_service_scene_trace.modality_runtime_contract`、`routingEvent=executor_invoked`、`routingOutcome=accepted/failed`、`expectedRoutingSlot=voice_generation_model`;`runtime_replay_case_service` 同步增加 `voice-generation`、`voice-runtime`、`voice-generation-trace` tags,并在 expected / grader 中要求继续走 `service_scene_launch(scene_key=voice_runtime)` / 本地 ServiceSkill runtime 或后续 audio_task/audio_output 证据,禁止 `legacy_tts_test_command`、伪造云端提交、普通聊天文本或通用文件卡替代。
- 2026-04-30:继续第二十三刀 `voice_generation` audio artifact protocol:新增 `audio_generate` task type、`create_audio_generation_task_artifact` Tauri 命令与 `lime_create_audio_generation_task` Agent tool,只写入标准 `audio_task/audio_output` 产物,不生成真实音频、不提交云端;前端 API、DevBridge mock、`list_media_task_artifacts` contract index、evidence `audio_task.modality_runtime_contract` 与 replay grader 已同步消费同一份 `voice_generation` 合同快照,下一刀再补 audio viewer / 真实 worker。
- 2026-04-30:继续第二十四刀 `voice_generation` audio preview/viewer:`lime_create_audio_generation_task` 工具结果现在会带回 `source_text / voice / audio_path / duration_ms` 等任务元数据,前端 `taskPreviewFromToolResult` 可生成 `audio_generate` 任务卡,并同步生成 `.lime/runtime/audio-generate/*.md` 运行时文档 viewer;聊天区任务卡采用 Lime 现有轻卡视觉,打开后进入可读文档而不是暴露隐藏 task JSON,真实音频 worker 与完整音频工作台继续后置。
- 2026-04-30:继续第二十五刀 `voice_generation` audio_output 回写闭环:新增 `complete_audio_generation_task_artifact` 命令与前端 API / DevBridge mock / governance catalog / mockPriority 同步,执行器可把标准 `audio_generate` task 从 pending 更新为 `succeeded`,并把完成态 `audio_output` 同时写入 payload 与 result;本刀仍不引入真实音频 worker、LimeCore 云执行 run/poll 或 legacy TTS。
- 2026-04-30:继续第二十六刀 `voice_generation` audio preview runtime:新增 `useWorkspaceAudioTaskPreviewRuntime`,聊天工作区会监听 `lime://creation_task_submitted` 并轮询仍在 running 或缺少音频 URL 的 `audio_generate` 任务,从标准 task artifact 恢复完成态 `audio_output` 到任务卡;这一步把第二十五刀写回口真正接回用户可见轻卡,但仍不新增完整音频工作台。
- 2026-04-30:继续第二十七刀 `voice_generation` audio worker skeleton:`create_audio_generation_task_artifact` 现在会在 emit 标准媒体任务事件后启动 `lime-audio-worker` 骨架;骨架先把任务推进到 `running`,随后在真实 provider 未接通时写回 `audio_worker_unavailable` 可重试失败与 `payload.audio_output.status=failed`,不生成假 `audio_path`、不调用 legacy TTS,也不新增 LimeCore 云 run/poll 协议。
- 2026-04-30:继续第二十八刀 `voice_generation` OpenAI-compatible audio provider seam:`lime-audio-worker` 不再停在纯骨架失败,而是从标准 `audio_generate` task payload / 全局 `media_defaults.voice` 解析 provider 与 model,通过 current API Key Provider 主链拿到 OpenAI-compatible `api_key/base_url`,调用 `/audio/speech` 返回真实音频 bytes 后保存到 `.lime/runtime/audio/<task_id>.mp3`,再回写 `payload.audio_output.completed` 与 `record.result.audio_output`;无 AppHandle、DbConnection、ApiKeyProviderService、provider/model/API Key 或非 OpenAI-compatible client 时写回明确 `audio_provider_*` 失败码,不回退 `legacy_tts_test_command`、`test_tts`、伪云端提交或通用文件卡。
- 2026-04-30:继续第二十九刀 `voice_generation` audio failure preview:聊天区 `useWorkspaceAudioTaskPreviewRuntime` 现在会从标准 `audio_generate` task artifact 的 `payload.audio_output` / `record.last_error` 读取 `audio_provider_*` 错误码、错误原因与 retryable,并在失败 artifact 没有 `audio_path` 时清空任务卡音频路径;`TaskMessagePreview` 同步展示 Provider 错误码和原因,让未配置 provider、resolver 不可用或 adapter 缺失直接回到用户可见任务卡,不再泛化成普通失败,也不伪造音频结果。
- 2026-04-30:继续第三十刀 `voice_generation` audio viewer sync:`useWorkspaceAudioTaskPreviewRuntime` 在从标准 `audio_generate` task artifact 恢复完成/失败态时,会同步重建同一条 `.lime/runtime/audio-generate/*.md` 运行时文档的 ArtifactDocument metadata;打开聊天任务卡时,viewer 会展示 `audio_output.completed` 的真实音频路径,或展示 `audio_provider_*` 错误码与原因,不再停留在创建任务时的“等待音频执行器”文档。
- 2026-04-30:继续第三十一刀 `voice_generation` audio evidence/replay details:`runtime_evidence_pack_service` 现在会从 `audio_generate` task artifact 导出 `audioOutput` 摘要,并在 `snapshotIndex.audioOutputIndex` 汇总 audio output 状态、Provider 错误码与 worker;`runtime_replay_case_service` 同步把 `audio-output-completed` / `audio-output-failed` 纳入 suite tags / failure modes,并在 expected / grader 中要求 replay 保留 `audio_output.completed` 与真实 `audio_path`,或显式处理 Provider 失败,不允许回退 legacy TTS、伪造音频路径、普通聊天文本或通用文件卡。
- 2026-04-30:继续第三十二刀 `voice_generation` audio task index details:`media_task_cmd` 的 `list_media_task_artifacts` 索引现在把 `audio_output` 完成/失败事实直接纳入 `modality_runtime_contracts`,包含按状态汇总、Provider 错误码汇总,以及每个 task snapshot 的 `audio_output_status/path/mime_type/duration_ms/error_code/retryable`;`src/lib/api/agentRuntime/types.ts` 与浏览器 fallback mock 同步更新,避免浏览器链路或前端恢复逻辑只能靠重新读取隐藏 task JSON 才能判断音频产物状态。
- 2026-04-30:继续第三十三刀 `voice_generation` audio index restore:聊天工作区的 `useWorkspaceAudioTaskPreviewRuntime` 现在会先按 `voice_generation + audio_generate` 查询统一媒体任务索引,从 snapshot 的 `audio_output_status/path/mime_type/duration_ms/error_code/retryable` 直接恢复任务卡和 `.lime/runtime/audio-generate/*.md` viewer;索引缺失或旧 task 没有 `audio_output_status` 时才保留原来的单 task artifact 读取兜底,避免主路径继续依赖隐藏 task JSON。
- 2026-04-30:继续第三十四刀 `Phase 4 artifact graph`:新增 `docs/roadmap/warp/artifact-graph.md` 与 `src/lib/governance/modalityArtifactGraph.json`,把 `image/audio/browser/pdf/report/presentation/webpage/generic_file` 的 artifact kind、truth source、viewer、evidence 与 task index 字段落成可检查事实源;`governance:modality-contracts` 现在会校验 contract 的 `artifact_kinds` 必须能在 artifact graph 中找到 truth source / viewer / evidence 交集,防止新增多模态能力继续退回未知 viewer 或通用文件卡旁路。
- 2026-04-30:继续第三十五刀 `browser_control entry binding`:`browser_control.bound_entries` 现在登记 `@浏览器 / @browser / @browse`、`@Browser Agent`、`@Mini Tester`、`@Web Scheduler`、`@Web Manage`,所有入口只声明 `harness.browser_assist` launch metadata、默认输入映射与可见性策略;前端 Browser Assist launch 会按触发词从 registry 派生 `entry_source`,不再把所有浏览器别名压成同一个入口来源,也不让入口直接写 task / artifact / viewer。
- 2026-04-30:继续第三十六刀 `browser_control browserActionIndex`:`runtime_evidence_pack_service` 现在会从 Browser Assist tool metadata 提取 `browserAction` 摘要,并在 `modalityRuntimeContracts.snapshotIndex.browserActionIndex` 汇总 actionCount、sessionCount、lastUrl、observationCount、screenshotCount、backend/status/action 计数与逐项 `browser_session` / `browser_snapshot` artifactKind;`runtime_replay_case_service` 同步把 `browser-action-index` 纳入 suite tags / expected / grader,要求 replay 继续导出可查询 browser session/snapshot 索引,而不是只靠 raw snapshots 或 WebSearch 替代。
- 2026-04-30:继续第三十七刀 `browser_control Harness visible index`:Evidence Pack 的 `observabilitySummary.modalityRuntimeContracts.snapshotIndex.browserActionIndex` 现在会携带裁剪后的 Browser Assist 摘要,前端 `AgentRuntimeEvidencePack` 类型与 normalizer 同步解析该索引;`HarnessStatusPanel` 在导出问题证据包后展示 Browser Assist 索引卡,直接暴露 action/session/URL/observation/screenshot 与最近 `browser_session` / `browser_snapshot` 项,证明浏览器复盘已从 runtime.json 人工查找前进到用户可见 evidence 面板。
- 2026-04-30:继续第三十八刀 `browser_control browser_replay_viewer`:`HarnessStatusPanel` 的 Browser Assist 索引卡新增“打开复盘”,以 evidence pack + `browserActionIndex` 构造临时 `browser_assist` Artifact;`BrowserAssistRenderer` 现在可从 artifact meta 或 JSON content 读取 `browserActionIndex`,渲染最小 `browser_replay_viewer`,展示动作统计、最近 URL、会话/target/entry、截图可用性与最近 `browser_session` / `browser_snapshot`。这一步把 evidence 可见摘要推进到 Lime 内部复盘面,但仍不新增 browser task 文件协议;完整交互回放、权限 profile 可视化与截图/DOM/network 深层展开继续后置。
- 2026-04-30:继续第三十九刀 `audio_transcription contract`:新增 current `audio_transcription` contract,绑定 `@转写 / @transcribe / @Audio Extractor` 到 `harness.transcription_skill_launch.transcription_task`;前端 `@转写` metadata 现在携带 `modality_contract_key=audio_transcription`、`required_capabilities=[text_generation,audio_transcription]`、`routing_slot=audio_transcription_model` 与 `runtime_contract`,Rust `transcription_skill_launch` 也会为旧 metadata 补齐同一合同并在 prompt 中禁止 `frontend_direct_asr`、`generic_file_transcript`、转写前 ToolSearch/WebSearch 偏航。`transcript` artifact graph 已反向登记该合同;真实转写 worker、transcript viewer 与 evidence/replay 索引继续后置。
- 2026-04-30:继续第四十刀 `audio_transcription transcript task/index/evidence/replay`:`lime_create_transcription_task`、`create_transcription_task_artifact_inner` 与 CLI `lime task create transcription` / `lime media transcription generate` 现在写入同一标准 `transcription_generate` task file,payload 固化 `audio_transcription` 合同、`requested_target=transcript` 与 `transcript.pending`;`list_media_task_artifacts` 增加 `transcript_count/statuses/error_codes` 与每个 task 的 `transcript_*` snapshot,Evidence Pack 增加 `snapshotIndex.transcriptIndex`,Replay / grader 增加 `audio-transcription`、`transcription-task-trace`、`transcript-index` 标签与缺失/失败阻断检查。真实 ASR worker、完成态 transcript 回写和专属 transcript viewer 仍作为下一刀。
- 2026-04-30:继续第四十一刀 `audio_transcription transcription worker`:`lime_create_transcription_task` 现在在创建标准 `transcription_generate` task 后启动 `lime-transcription-worker`;worker 校验 `audio_transcription` contract、读取本地 source file 或下载 `source_url`,通过 current API Key Provider 解析 OpenAI-compatible credential,调用 `/audio/transcriptions`,成功时写入 `.lime/runtime/transcripts/*` 并回写 `transcript.completed`、`transcript_path`、`provider_id/model` 与 `record.result.transcript`;失败时回写 `transcript.failed` 和 `transcription_provider_*` / `transcription_source_*` / contract 错误码,不回退 frontend ASR、普通文件读取或 generic_file transcript。最小 GUI 恢复层已在第四十二刀接回,文本校对已在第四十三刀接回,时间轴/说话人段落展示已在第四十四刀接回;编辑保存型 transcript viewer、更多 ASR adapter 与本地离线 ASR 继续后置。
- 2026-04-30:继续第四十二刀 `audio_transcription transcript visible restore`:聊天工作区新增 `useWorkspaceTranscriptionTaskPreviewRuntime`,会优先从 `list_media_task_artifacts(modalityContractKey=audio_transcription, taskType=transcription_generate)` 的 `transcript_*` snapshot 恢复完成/失败态,把 `transcript_path`、source、language、outputFormat、Provider 错误码同步到任务卡与 `.lime/runtime/transcription-generate/*.md` 运行时文档;只有索引缺失时才回退读取单个 task artifact,避免用户可见路径继续依赖隐藏 task JSON 或 generic_file transcript。
- 2026-04-30:继续第四十三刀 `audio_transcription transcript proofreading viewer`:`useWorkspaceTranscriptionTaskPreviewRuntime` 在完成态恢复时读取 `transcript_path` 对应文本,并把 `transcriptText` 写回任务预览和 `.lime/runtime/transcription-generate/*.md` 运行时文档;`buildTranscriptionTaskArtifactDocument` 现在输出“转写文本(可编辑校对)”代码块,让打开任务卡即可在 Lime 内部读取、复制与校对 transcript,而不是只看到路径或退回 generic_file。
- 2026-04-30:继续第四十四刀 `audio_transcription transcript timeline viewer`:新增 transcript segment 解析器,支持从 OpenAI verbose JSON、SRT 与 VTT 读取 `start/end/speaker/text`,并把结果回写到聊天任务卡时间轴预览与 `.lime/runtime/transcription-generate/*.md` ArtifactDocument 表格;本刀只消费同一 `transcription_generate` task file 与 `transcript_path` 输出,不新增协议、Tauri command、frontend ASR 或 generic_file 旁路。编辑保存型 transcript viewer、更多 ASR adapter 与本地离线 ASR 继续后置。
- 2026-04-30:继续第四十五刀 `audio_transcription transcript correction save`:复用现有 ArtifactDocument Workbench 编辑保存链路,让转写运行时文档的文本与时间轴从“可复制校对”推进到“可保存校对稿”;生成文档会标记 `transcriptCorrectionEnabled` / `transcriptCorrectionSource=artifact_document_version` 并提示保存不改写原始 ASR 输出,保存时补 `transcriptCorrectionStatus=saved`、编辑 block、原始 transcript 路径、文本长度、段落/说话人数与 `transcriptSegmentsCorrected`。本刀没有新增 Tauri command、Rust worker 分支、frontend ASR 或 generic_file patch 旁路。
- 2026-04-30:继续第四十六刀 `audio_transcription transcript correction status`:保存校对稿时会在同一 ArtifactDocument 中插入/更新 `transcript-correction-status` 成功提示块,明确“校对稿已保存为当前运行时文档的新版本;原始 ASR 输出文件保持不变”,并写入 `transcriptCorrectionDiffSummary`,汇总原文/校对稿文本长度、段落数量、变更段落和说话人数差异。该状态仍随同一 `.lime/runtime/transcription-generate/*.md` 运行时文档版本保存,不新增命令、worker 分支或并行 patch artifact。
- 2026-04-30:继续第四十七刀 `Phase 3 / Phase 5 execution profile registry`:新增 `docs/roadmap/warp/execution-profile.md` 与 `src/lib/governance/modalityExecutionProfiles.json`,把 7 个 current contracts 的 profile、artifact policy、LimeCore policy refs 与 executor adapters 落成机器事实源;`check-modality-runtime-contracts.mjs` 现在会读取 profile registry,校验每个 current contract 都被 profile 覆盖、每个 `executor_binding` 都有 adapter、adapter 支持位/产物/权限/failure mapping 与 contract 对齐。该刀不改 Tauri command、bridge、mock 或真实 executor 行为,只把 Phase 3/5 的主线底座从文档要求推进成可阻断错误配置的 current 守卫。
- 2026-04-30:继续第四十八刀 `Phase 3 / Phase 5 profile resolver`:新增 `src/lib/governance/modalityExecutionProfiles.ts` 与定向测试,`resolveModalityRuntimeContractBinding()` 会把 current contract 对应的 `execution_profile`、`executor_adapter`、`executionProfileKey`、`executorAdapterKey` 注入同一 runtime contract binding;所有现有上层入口继续只调用 runtime contract resolver,即可随 launch metadata 携带 profile / adapter 快照。本刀没有新增命令、bridge、mock、Rust executor 或 GUI surface,只把上一刀的机器事实源推进到前端主路径输入。
- 2026-04-30:继续第四十九刀 `Phase 3 / Phase 5 evidence snapshot`:`runtime_evidence_pack_service` 现在会从 runtime contract 中提取 `executionProfileKey` 与 `executorAdapterKey`,并写入 `modalityRuntimeContracts.snapshots[]`、`snapshotIndex.executionProfileKeys`、`snapshotIndex.executorAdapterKeys` 与 `toolTraceIndex.items[]`;图片 contract preflight 失败样本与 web_research Skill trace 样本都增加断言,证明 profile / adapter 已进入 evidence 主链,而不是只停留在前端 metadata 或治理 JSON。本刀未改命令、bridge、mock、GUI 或真实 executor 行为。
- 2026-04-30:继续第五十刀 `Phase 3 / Phase 5 task index snapshot`:Rust 多模态 `runtime_contract` snapshot 现在随 central contract helper 写入 `execution_profile.profile_key` 与 `executor_adapter.adapter_key`;`list_media_task_artifacts` 会把这些字段汇总到 `modality_runtime_contracts.execution_profile_keys`、`executor_adapter_keys`,并在每条 snapshot 输出 `execution_profile_key` / `executor_adapter_key`。前端 `MediaTaskModalityRuntimeContractIndex` 类型、浏览器 fallback mock、图片/配音/转写任务恢复测试同步更新,证明 profile / adapter 已进入统一媒体任务索引,而不是只停留在 evidence/replay。
- 2026-04-30:继续第五十一刀 `Phase 3 / Phase 5 media worker adapter preflight`:`validate_*_task_execution_contract` 现在会在图片、配音、转写 worker 进入真实执行器前校验 `execution_profile.profile_key`、`executor_adapter.adapter_key` 与 `executor_binding.executor_kind/binding_key`,错配时以 `runtime_preflight` 阶段阻断,而不是等 provider/worker 泛化失败;`list_media_task_artifacts` snapshot 同步暴露 `executor_kind` / `executor_binding_key`,浏览器 fallback mock 与前端类型跟进,Evidence Pack 也会把这类阻断标记为 `runtime_preflight` / `blocked`。
- 2026-05-01:继续第五十二刀 `Phase 6 policy snapshot seed`:central Rust contract helper、前端 runtime contract resolver 与浏览器 fallback mock 现在都会携带 `limecore_policy_refs` 与最小 `limecore_policy_snapshot(status=refs_declared, decision=not_evaluated)`;`list_media_task_artifacts` 汇总 policy refs/status/decision,Evidence Pack 增加 `snapshotIndex.limecorePolicyIndex`,前端 normalizer 与测试同步。该刀不新增命令、不实现真实 LimeCore 云执行,只把后续 allow / ask / deny 需要的审计字段接入 current 主链。
- 2026-05-01:继续第五十三刀 `Phase 6 local policy decision summary`:`limecore_policy_snapshot` 从 `not_evaluated` 推进到本地默认 `allow` 摘要,并写入 `decision_source=local_default_policy`、`decision_scope=local_defaults_only`、`decision_reason=declared_policy_refs_with_no_local_deny_rule` 与 `unresolved_refs`;统一媒体任务索引和 Evidence Pack 同步暴露这些解释字段。该刀仍不新增命令、不调用 LimeCore 云、不碰上层 `@` 入口;真实 tenant / provider / gateway policy 命中值继续后置。
- 2026-05-01:继续第五十四刀 `Phase 6 policy input gap summary`:`limecore_policy_snapshot` 新增 `policy_inputs[]` 与 `missing_inputs[]`,把每个 policy ref 标成 `declared_only / limecore_pending`;Evidence Pack `limecorePolicyIndex` 与统一媒体任务索引同步输出 missing inputs,让后续接入真实 LimeCore `model_catalog / provider_offer / tenant_feature_flags / gateway_policy` 命中值时有稳定 diff 面。该刀不新增命令、不触发云执行,也不修改上层 `@`。
- 2026-05-01:收口第五十四刀验证:定向前端/Rust 测试、`typecheck`、`governance:modality-contracts`、`test:contracts`、`harness:doc-freshness`、相关文件 `git diff --check` 与 `verify:local` 均已通过;GUI smoke 复用 headless Tauri 与 DevBridge,证明本轮底层审计字段接线没有破坏 workspace、browser runtime、site adapter 与 agent runtime tool surface 主路径。
- 2026-05-01:继续第五十五刀 `Phase 6 policy gap evidence visibility`:Harness evidence 面板新增 `LimeCore 策略缺口` 摘要卡,直接从 `limecorePolicyIndex` 展示 policy snapshot 数、refs、missing inputs、local default decision、profile / adapter、decision scope/reason 与 `declared_only / limecore_pending` 输入状态;该刀只消费现有 evidence 字段,不新增 Tauri command、不接 LimeCore 云 run/poll、不触碰上层 `@` 入口。
- 2026-05-01:继续第五十六刀 `Phase 6 policy gap replay grader`:Replay case 现在会把 `limecorePolicyIndex` 转成 suite tags、failure modes、success criteria、blocking checks 与多模态合同检查,要求回放继续保留 policy refs、missing inputs、`local_default_policy` / `local_defaults_only` 解释;该刀确保 policy gap 能被复盘验收,而不是只停留在 evidence UI。
- 2026-05-01:继续第五十七刀 `Phase 6 policy hit value wiring`:在不接云 run/poll 的前提下,为真实 LimeCore policy 命中值补空接线结构:`policy_value_hits[]` 保持空、`pending_hit_refs[]` 指向等待命中的 refs、`policy_value_hit_count=0`,并贯通 runtime contract、Evidence Pack、任务索引、前端类型/normalizer 与 mock;后续接真实 LimeCore 值时只需填充同一字段,不再改协议外形。
- 2026-05-01:继续第五十八刀 `Phase 6 policy hit resolver seam`:`policy_value_hits(status=resolved)` 现在会被 central runtime contract helper、前端 resolver、Evidence Pack、媒体任务索引与浏览器 mock 统一识别;命中的 ref 会进入 `evaluated_refs`,未命中的 ref 继续留在 `missing_inputs / pending_hit_refs`。该刀只建立“真实命中值写入与派生待命中 refs”的 seam,不接 LimeCore 云 run/poll,也不把本地默认 decision 升级成真实 allow / ask / deny。
- 2026-05-01:继续第五十九刀 `Phase 6 local model_catalog hit producer`:图片任务执行前复用已有 model registry assessment,把命中的模型目录事实写入 `policy_value_hits(status=resolved, value_source=local_model_catalog)`,同步更新 `runtime_contract` snapshot、当前 attempt input snapshot 与统一媒体任务索引;该刀只让 `model_catalog` 输入从 pending 变成 resolved,不接 provider offer / tenant flags,也不把本地默认 `allow` 解释成云策略放行。
- 2026-05-01:继续第六十刀 `Phase 6 local provider_offer hit producer`:图片任务进入真实执行器前复用已解析的本地 runner config/API key 与 task payload provider/model,把 `provider_offer` 写入 `policy_value_hits(status=resolved, value_source=local_provider_offer)`,同步收缩 `missing_inputs / pending_hit_refs`;snapshot 只记录 endpoint origin/path、adapter 与 credential 状态,不写 API key,不新增 Tauri command,也不接 LimeCore 云 run/poll。
- 2026-05-01:继续第六十一刀 `Phase 6 request gateway_policy hit producer`:Browser Assist 与 Web Research 类 launch 复用请求侧 `harness.oem_routing`,把 tenant/provider/quota/can_invoke/fallback 等真实路由输入写入对应 runtime contract 的 `policy_value_hits(status=resolved, value_source=request_oem_routing)`;命中后 `gateway_policy` 会进入 `evaluated_refs`,但 `decision` 仍保持 `local_default_policy / local_defaults_only`,不新增命令、不接云 run/poll,也不伪造 tenant feature flags。
- 2026-05-01:继续第六十二刀 `Phase 6 request tenant_feature_flags hit producer`:Workspace send metadata 从 OEM Cloud bootstrap snapshot 的 `features` 生成 `harness.tenant_feature_flags`,Rust runtime contract helper 在所有 request metadata runtime contract 中写入 `policy_value_hits(status=resolved, value_source=oem_cloud_bootstrap_features)`;命中后 `tenant_feature_flags` 会进入 `evaluated_refs`,但 `decision` 仍保持 `local_default_policy / local_defaults_only`,不新增命令、不接云 run/poll,也不把 feature flags 当作真实 allow / ask / deny evaluator。
- 2026-05-01:继续第六十三刀 `Phase 6 policy input evaluator seam`:central Rust contract helper 与前端 runtime contract resolver 新增 `policy_evaluation`,当所有 refs 都有 resolved hit 时用 `policy_input_evaluator` 给出 `allow / ask / deny` 顶层决策;gateway `can_invoke=false` / blocked、tenant `gatewayEnabled=false`、model catalog 不支持目标能力或 provider credential 非 configured 会产生 `deny`,quota low / subscribe required / logged out 会产生 `ask`。仍有 missing inputs 时只记录 `policy_evaluation.status=input_gap`,顶层仍保持 `local_default_policy / local_defaults_only`,不接云 run/poll、不新增命令。
- 2026-05-01:继续第六十四刀 `Phase 6 thread read policy explanation`:`AgentRuntimeThreadReadModel` 现在会从最新 tool metadata / file artifact 中的 runtime contract 提取 `limecore_policy_snapshot`,并写入 `runtime_summary.limecorePolicy`;上层读取 thread read 时可直接看到 contract key、snapshot status、顶层 decision/source/scope/reason、refs、missing/pending refs、hit count 与 evaluator blocking/ask/pending refs。本刀不新增命令、不接 LimeCore 云 run/poll,也不把 thread read 结果当云端 audit 事实源。
- 2026-05-01:继续第六十五刀 `Phase 6 media task policy evaluation index`:`list_media_task_artifacts` 的 `modality_runtime_contracts` 现在汇总 `policy_evaluation` status / decision / source 与 blocking / ask / pending refs,每条 snapshot 也输出同名 `limecore_policy_evaluation_*` 字段;前端类型、浏览器 fallback mock 与 mediaTasks 回归同步。该刀只把 evaluator explanation 推到任务索引,不新增命令、不接云 run/poll,也不把 input gap 的 evaluator `ask` 覆盖成顶层真实策略结论。
- 2026-05-01:继续第六十六刀 `Phase 6 task card policy evaluation meta`:配音与转写任务卡恢复层现在会消费统一媒体任务索引的 `limecore_policy_evaluation_*` snapshot,通过共享 helper 生成 `LimeCore 策略输入待命中: N`、`LimeCore 策略输入阻断: <ref>` 或 `LimeCore 策略输入需确认: <ref>` meta 标签;audio input gap 与 transcription deny 都有稳定回归。该刀只让现有任务卡展示 evaluator explanation,不新增 Tauri command、不接 LimeCore 云 run/poll,也不触碰上层 `@` 命令。
- 2026-05-01:继续第六十七刀 `Phase 6 image viewer policy evaluation meta`:图片任务恢复层现在会从 `runtime_contract.limecore_policy_snapshot.policy_evaluation` 解析 LimeCore evaluator 摘要,写入 `ImageRuntimeContractSnapshot` 并在图片 viewer 的运行合同标签旁展示 `LimeCore 策略输入待命中 / 阻断 / 需确认`;本刀只消费已有 task artifact runtime contract,不新增命令、不接 LimeCore 云 run/poll,也不修改图片上层入口。
- 2026-05-01:继续第六十八刀 `Phase 6 image message card policy evaluation meta`:图片消息轻卡现在从 `preview.runtimeContract` 读取同一 LimeCore evaluator 摘要,并在预览顶部 meta pills 展示 `LimeCore 策略输入待命中 / 阻断 / 需确认`;本刀只消费已有图片 runtime contract,不新增命令、不接 LimeCore 云 run/poll,也不改变轻卡打开 viewer 的导航能力。
- 2026-05-01:继续第六十九刀 `Phase 5 voice service compat guard`:旧 `lime_run_service_skill` 命中 `voice_generation / voice_runtime / at_voice_command / voice_generation_model` 时,会生成 `modality_runtime_contract` metadata 并校验 `contract_key=voice_generation`、`execution_profile=voice_generation_profile`、`executor_adapter=service_skill:voice_runtime`、`executor_binding=service_skill:voice_runtime`;错配返回 `runtime_preflight`,通过也只返回 compat guard,不执行云 run/poll。该刀把旧服务型工具收成 guard,不把它升级为 current executor,也不提前硬造 `gateway:*` adapter。
- 2026-05-02:继续第七十刀 `Phase 5/6 Rust preflight verification seam`:`WebSearchPreflightRequest` 现在从 `lime_agent` crate 正式导出,真实 WebSearch preflight 集成测试改为使用请求对象调用;automation runtime turn 与 DevBridge session 创建调用点同步补齐新增缺省参数,恢复 `cargo test -p lime <filter>` 对 Phase 5/6 Rust 集成测试编译面的覆盖。该刀不新增执行器、不改变 `@` 入口、不接 LimeCore 云 run/poll,只清掉阻挡后续 executor/policy preflight 定向验证的旧 API 漂移。
- 2026-05-02:继续第七十一刀 `Phase 5/6 thread read modality runtime summary`:`AgentRuntimeThreadReadModel` 现在会从最近 ToolCall metadata / FileArtifact content 的同一 `runtime_contract` 提取 profile / adapter / binding 摘要,并写入 `runtime_summary.modalityRuntime`;`limecorePolicy` 继续解释 policy decision input,`modalityRuntime` 解释 profile / adapter / executor binding。该刀不新增 Tauri command、不接 LimeCore 云 run/poll,也不把上层 `@` 命令升级成底层合同事实源。
- 2026-05-02:继续第七十二刀 `Phase 5/6 TaskProfile execution profile merge`:`SessionExecutionRuntimeTaskProfile` 新增 `modalityContractKey`、`routingSlot`、`executionProfileKey`、`executorAdapterKey`、`executorKind`、`executorBindingKey`、`permissionProfileKeys` 与 `userLockPolicy`;Rust request model resolution 会从 request metadata 已有 runtime contract 合并这些字段,并按 `modalityExecutionProfiles.json` 补齐 profile 权限与用户锁定策略,`task_profile_resolved` 前端协议测试也覆盖透传。该刀只推进 current runtime task profile 事实源,不新增命令、不接云 run/poll,也不让 `@` 命令成为底层执行合同事实源。
- 2026-05-02:继续第七十三刀 `Phase 5/6 routingSlot capability enforcement`:`request_model_resolution` 现在从 `SessionExecutionRuntimeTaskProfile.routingSlot` 派生 runtime model capability requirements,并把它用于候选模型计数、catalog fallback 与多候选自动重选;非显式用户锁定路径会优先选择满足 runtime slot 的模型,显式用户锁定路径继续 honored,但会通过 `capability_gap` 输出 `browser_reasoning_candidate_missing` / `image_generation_candidate_missing` 等 gap。该刀不新增 Tauri command、不接 LimeCore 云 run/poll、不把上层 `@` 命令当底层事实源;完整权限判定、用户确认/阻断执行与真实 `gateway:*` adapter preflight 继续后置。
- 2026-05-05:继续第七十四刀 `Phase 5/6 permissionProfileKeys runtime summary`:`request_model_resolution` 现在从 `SessionExecutionRuntimeTaskProfile.permissionProfileKeys` 生成 `SessionExecutionRuntimePermissionState`,随 `lime_runtime.permission_state` 写入 turn metadata;runtime view、thread read fallback summary、前端 execution runtime 类型和内部辅助任务 metadata 都同步识别这一摘要。`browser_control / web_search / media_upload / service_api_call / read_files / write_artifacts` 会进入 `askProfileKeys`,`ask_user_question` 只表示可补问用户,不视为风险权限;`blockingProfileKeys` 本刀保持为空,明确不做真实授权、不阻断执行、不新增命令、不接 LimeCore 云 run/poll、不把上层 `@` 命令当底层事实源。
- 2026-05-05:继续第七十五刀 `Phase 5/6 thread read permission state`:`AgentRuntimeThreadReadModel` 现在结构化暴露 `permission_state`,前端 thread read 类型同步引用同一 `AsterSessionExecutionRuntimePermissionState`;上层读取可以拿到完整 required/ask/blocking profile keys 与 notes,不需要只依赖 `runtime_summary.permissionAskCount`。该刀仍只增强读取面,不新增 Tauri command、不新增 runtime 事件、不接真实权限授权、不阻断执行。
- 2026-05-05:继续第七十六刀 `Phase 5/6 permission review runtime status`:`collect_runtime_request_resolution_side_events` 现在会把 `lime_runtime.permission_state(status=requires_confirmation)` 映射为现有 `runtime_status(phase=permission_review)` side event,metadata 携带 required/ask/blocking profile keys、decision source/scope 与 `declared_only=true`;`not_required` 不产生额外事件。该刀让权限声明态进入运行时事件流的可观察动作点,但仍不生成 `action_required`、不创建 pending approval、不阻断 turn、不新增 Tauri command、不接真实权限授权或 LimeCore 云 run/poll。
- 2026-05-05:继续第七十七刀 `Phase 5/6 frontend permission review event contract`:前端 `agentProtocol` 与聊天 runtime status 类型现在正式接受 `phase=permission_review`,并保留 permission status、required/ask/blocking profile keys、decision source/scope 与 `declared_only` metadata;协议解析测试覆盖该事件不会被 normalize 回普通 `routing`。该刀只修正第七十六刀事件的前端消费边界,不新增 UI surface、不新增 bridge/Tauri command、不接真实授权。
- 2026-05-05:继续第七十八刀 `Phase 5/6 permission evidence and replay facts`:Evidence Pack 的 `thread.runtimeFacts` 与 `observabilitySummary.runtimeFacts` 现在导出 `permissionState`,`signalCoverage` 增加 `permissionState` 覆盖项;Replay `runtimeFacts` 同步携带 `permissionState`,并在 `requires_confirmation` 或存在 `blockingProfileKeys` 时生成 blocking checks,要求回放不能把声明态权限误判为已获真实授权。该刀只增强审计/回放事实源,不创建 pending approval、不阻断执行、不新增命令或云策略。
- 2026-05-05:继续第七十九刀 `Phase 5/6 permission confirmation state seed`:`SessionExecutionRuntimePermissionState` 新增 `confirmationStatus / confirmationRequestId / confirmationSource`,当前声明态 `requires_confirmation` 明确写为 `confirmationStatus=not_requested`、`confirmationRequestId=null`、`confirmationSource=declared_profile_only`;内部辅助任务写 `not_required`。前端 execution runtime 类型同步该字段。该刀只建立真实授权接入前的单一状态槽,不复用 `ApprovalRequest` 伪造 pending request、不阻断执行、不新增命令。
- 2026-05-05:继续第八十刀 `Phase 5/6 live permission confirmation metadata`:`permission_review` runtime status metadata 现在同步携带 `confirmation_status / confirmation_request_id / confirmation_source`,前端 `agentProtocol` 会保留这些字段;`requires_confirmation` 的 live event 因此能明确表达“需要确认,但尚未发起真实审批请求”。该刀仍不创建 pending approval、不阻断执行、不新增命令。
- 2026-05-05:继续第八十一刀 `Phase 5/6 thread read pending approval projection`:`AgentRuntimeThreadReadModel.permission_state` 现在会从同一 thread read 中真实存在的 pending tool `ApprovalRequest` 派生 `confirmationStatus=requested`、`confirmationRequestId=<request_id>`、`confirmationSource=runtime_action_required`,`runtime_summary` 同步暴露 `permissionConfirmation*` 摘要。该刀只把已有真实 `action_required` 投影到权限确认状态槽,不创建审批、不改变 native approval 行为、不阻断执行、不新增命令。
- 2026-05-05:继续第八十二刀 `Phase 5/6 resolved approval projection`:thread read 现在会从已完成或失败的真实 tool `ApprovalRequest.response.confirmed` 派生 `confirmationStatus=resolved / denied`,并保留真实 request id 与 `runtime_action_required` 来源。该刀仍只做读取模型投影,不改变审批提交、权限 enforcement 或执行恢复语义。
- 2026-05-05:继续第八十三刀 `Phase 5/6 replay denied confirmation blocking`:Replay blocking checks 现在区分 permission confirmation 状态:`denied` 会生成明确阻断检查,`resolved` 不再被误报为“仍需确认”。该刀只修正 replay/grader 判定语义,不新增命令、不改审批执行、不接 GUI 或云策略。
- 2026-05-05:继续第八十四刀 `Phase 5/6 evidence permission confirmation coverage`:Evidence Pack 的 `signalCoverage.permissionState` 现在会区分确认结果:`denied` 标为 `blocked` 并写入真实 request id,`resolved` 保持 `exported` 并说明已通过。该刀让审计摘要与 Replay 阻断语义对齐,不新增命令、不改审批执行、不接 GUI 或云策略。
- 2026-05-05:继续第八十五刀 `Phase 5/6 evidence denied confirmation summary`:Evidence Pack 的 `knownGaps` 与 `summary.md` 现在会把 `confirmationStatus=denied` 暴露为人眼可见的阻断风险,摘要同时输出 `当前阻断信号`。该刀只增强导出解释,不改审批执行、不新增命令、不接 GUI 或云策略。
- 2026-05-05:继续第八十六刀 `Phase 5/6 handoff permission confirmation summary`:Handoff bundle 的 `plan.md / handoff.md / review-summary.md / progress.json` 与 Analysis handoff 的 `analysis-brief.md / analysis-context.json / copy_prompt` 现在同步读取 `thread_read.permission_state.confirmationStatus`,`denied` 会作为不能成功交付的阻断风险输出真实 request id/source,`resolved` 会明确解释真实权限确认已通过;该刀只统一交接/外部分析摘要语义,不改审批执行、不新增命令、不接 GUI 或云策略。
- 2026-05-05:继续第八十七刀 `Phase 5/6 review decision permission confirmation summary`:Review decision 模板现在从 Analysis handoff 的结构化 context 读取 `permissionConfirmationStatus / RequestId / Source / Summary`,并写入 `review-decision.md / review-decision.json` 的审核上下文、动态 checklist 与建议后续动作;`denied` 默认提示不能标记为 accepted,`resolved` 提醒不要误判为待处理阻塞。该刀只补人工审核记录的授权结论对齐,不改审批执行、不新增命令、不接 GUI 或云策略。
- 2026-05-05:继续第八十八刀 `Phase 5/6 review decision API permission confirmation contract`:`agent_runtime_export_review_decision_template` / `agent_runtime_save_review_decision` 的返回模型现在把 `permissionConfirmation*` 摘要提升到 API 顶层,前端 `AgentRuntimeReviewDecisionTemplate` 类型、normalizer、DevBridge mock 与 `src/lib/api/agent.test.ts` 已同步覆盖 camelCase / snake_case,GUI/API 读取 review decision 时不会再丢掉 denied/resolved 权限确认结论。该刀只固定读取契约,不改 UI 交互、不改审批执行、不新增命令、不接云策略。
- 2026-05-05:继续第八十九刀 `Phase 5/6 review decision GUI permission confirmation surface`:HarnessStatusPanel 的人工审核记录卡片现在直接展示 `permissionConfirmationStatus / Summary`,`denied` 会出现红色阻断提示,提醒当前 review decision 不能作为成功交付证据;对应组件回归固定了权限确认文案。该刀只把已有 API 字段接到 GUI 可见面,不改审批执行、不新增命令、不接云策略。
- 2026-05-05:继续第九十刀 `Phase 5/6 review decision dialog permission confirmation guardrail`:`RuntimeReviewDecisionDialog` 现在在填写人工审核结果前展示同一份 `permissionConfirmationStatus / Summary / request_id`,`denied` 使用阻断提示说明当前 review decision 不能作为成功交付证据;HarnessStatusPanel 回归覆盖弹窗里的权限确认文案。该刀只把已有 API 字段补到人工审核填写入口,不改审批执行、不新增命令、不接云策略。
- 2026-05-05:继续第九十一刀 `Phase 5/6 review decision denied acceptance guardrail`:`RuntimeReviewDecisionDialog` 在 `permissionConfirmationStatus=denied` 时会禁用“接受”选项,并在保存前阻止 `accepted` 结论写回;用户仍可保存拒绝、延后或需要更多证据的人工审核结果。HarnessStatusPanel 回归覆盖 denied 时不能保存 accepted、切换 rejected 后可正常保存。该刀只守住 review decision 写回语义,不改真实审批执行、不新增命令、不接云策略。
- 2026-05-05:继续第九十二刀 `Phase 5/6 review decision save API denied acceptance guardrail`:Rust `save_runtime_review_decision` 现在在 `permissionConfirmationStatus=denied` 且写入 `decisionStatus=accepted` 时直接返回错误,防止 API / SceneApp / 测试绕过 GUI 把被拒绝权限确认沉淀成成功交付结论;`rejected / deferred / needs_more_evidence` 仍可保存,Rust 回归覆盖 accepted 被拒与 rejected 可保存。该刀只守住 review decision 写回事实源,不改审批执行、不新增命令、不接云策略。
- 2026-05-05:继续第九十三刀 `Phase 5/6 review decision mock/API denied acceptance guardrail`:浏览器 `tauri-mock` 的 `agent_runtime_save_review_decision` 现在与 Rust save API 对齐,默认 denied 权限确认下保存 `accepted` 会抛错,保存 `rejected` 仍返回 denied 权限确认摘要;前端 API 回归确认后端 denied 错误会透传给调用方。该刀防止 DevBridge fallback / 浏览器测试链路假装 denied+accepted 成功,不改审批执行、不新增命令、不接云策略。
- 2026-05-05:继续第九十四刀 `Phase 7 entry binding inventory guardrail`:新增 `entry-binding-inventory.md` 并把 Phase 7 验收落进 `check-modality-runtime-contracts.mjs`,要求所有 current contract 至少有一个 entry binding、entry key 全局唯一、`entry_source` 只能引用同一 contract 下的入口、`launch_metadata_path` 只能停留在 `harness.*`,且 scene entry 只有声明 `client_scenes / scene_policy` 后才允许登记;`/scene-key` 明确保持 planned,等待 LimeCore Scene catalog 与 audit contract 接齐后再进入 current。该刀只收口入口绑定事实源,不新增 `@` 命令、不改执行器、不接云 run/poll。
- 2026-05-05:继续第九十五刀 `Phase 8 task index inventory guardrail`:新增 `task-index-inventory.md`,并把 task index 最低验收落进 `check-modality-runtime-contracts.mjs`:所有 current / partial artifact kind 必须声明 `task_id / contract_key / artifact_kind / status / created_at / updated_at`,且 `task_index_fields` 不允许重复。文档同时明确 `thread_id / turn_id / content_id / entry_key / model_id / executor_kind / cost_state / limit_state / limecore_policy_snapshot` 仍未统一稳定化,下一刀再从 `runtime_contract / entry_source / task_profile / policy snapshot` 回填查询维度。该刀只收口任务索引事实源,不新增执行器、不改 `@` 命令、不接云 run/poll。
- 2026-05-05:继续第九十六刀 `Phase 8 task index entry_key projection`:`list_media_task_artifacts.modality_runtime_contracts` 现在会把媒体 task payload 中的 `entry_key / entry_source` 归一投影为 snapshot `entry_key`,并汇总 `entry_keys`;`image_task / image_output / audio_task / audio_output / transcript` 的 artifact graph 索引声明与机器守卫同步要求 `entry_key`,前端类型、浏览器 fallback mock 与 mediaTasks / tauri-mock 回归已同步。该刀只稳定媒体任务查询维度,不新增执行器、不改上层 `@` 命令、不接 LimeCore 云 run/poll。
- 2026-05-05:继续第九十七刀 `Phase 8 task index executor policy dimensions`:`list_media_task_artifacts.modality_runtime_contracts` 现在汇总 `executor_kinds / executor_binding_keys`,媒体 artifact graph 与机器守卫同步要求 `entry_key / executor_kind / executor_binding_key / limecore_policy_snapshot_status`;前端类型、浏览器 fallback mock、mediaTasks 与 tauri-mock 回归已覆盖这些查询维度。该刀只把已有 runtime contract snapshot 投影为任务索引字段,不新增执行器、不改变上层 `@` 命令、不把本地默认 policy snapshot 伪造成 LimeCore 云策略结论。
- 2026-05-05:继续第九十八刀 `Phase 8 task index runtime identity anchors`:媒体任务创建请求、task payload、Rust `list_media_task_artifacts.modality_runtime_contracts`、前端类型与浏览器 fallback mock 现在同步承载 `thread_id / turn_id / content_id`,并汇总为 `thread_ids / turn_ids / content_ids`;媒体 artifact graph 与机器守卫同步要求这组运行身份锚点。该刀只把已有媒体任务身份字段接入查询索引,不新增命令、不改变上层 `@` 命令,也不把非媒体 Evidence / Replay artifact 伪装成已完成。
- 2026-05-05:继续第九十九刀 `Phase 8 task index modality skill model dimensions`:媒体任务索引现在从 task payload / runtime contract 投影 `modality / skill_id / model_id`,并汇总为 `modalities / skill_ids / model_ids`;`skill_id` 优先读取显式 payload 字段,缺省时只对 `skill / service_skill` executor 使用 `executor_binding_key` 作为当前查询锚点。媒体 artifact graph 与机器守卫同步要求这三个字段。该刀只稳定任务查询维度,不新增执行器、不改上层 `@` 命令,也不把非媒体 Evidence / Replay artifact 伪装成已完成。
- 2026-05-05:继续第一百刀 `Phase 8 task index cost limit summaries`:媒体任务索引现在从 task payload、`runtime_summary` 与 `task_profile` 中读取 `cost_state / limit_state` 摘要,并汇总 `cost_states / limit_states / estimated_cost_classes / limit_event_kinds / quota_low_count`;snapshot 同步输出 `cost_state / limit_state / estimated_cost_class / limit_event_kind / quota_low`。媒体 artifact graph 与机器守卫同步要求这组成本/限额查询字段;同轮补齐 `creation_tools` 构造媒体任务请求时的 `thread_id / turn_id` 透传,避免第九十八刀新增身份锚点后 Rust 字面量编译漂移。该刀只稳定已有摘要的任务索引口径,不新增 Tauri command、不接 LimeCore 云 run/poll、不把上层 `@` 命令变成底层事实源。
- 2026-05-05:继续第一百零一刀 `Phase 8 evidence taskIndex convergence`:Evidence Pack 的 `modalityRuntimeContracts.snapshotIndex` 新增 `taskIndex`,把 Browser / PDF / Web Research / Text Transform / Voice Service 等非媒体 runtime contract snapshot 的 `thread_id / turn_id / content_id / entry_key / modality / skill_id / model_id / executor_kind / executor_binding_key / cost_state / limit_state / estimated_cost_class / limit_event_kind / quota_low` 归一到与媒体任务索引一致的查询口径;前端 `AgentRuntimeEvidencePack` 类型与 normalizer 同步解析该索引。该刀只收敛 Evidence 审计事实源,不新增 Tauri command、不碰上层 `@` 命令、不接 LimeCore 云 run/poll;Replay / grader 消费同一 `taskIndex` 仍作为下一步收口。
- 2026-05-05:继续第一百零二刀 `Phase 8 replay taskIndex consumption`:Replay case 现在消费 Evidence `snapshotIndex.taskIndex`,在 `input.json.runtimeContext.runtimeFacts.modalityTaskIndex` 输出 identity / executor / cost-limit compact 摘要,suite tags 增加 `modality-task-index / modality-task-identity / modality-task-cost-limit`,`expected.json` 与 `grader.md` 会要求保留 thread/turn/content/entry、executor binding 与 cost/limit 摘要;缺少 `taskIndex` 的多模态合同会生成 blocking check。该刀只把第 101 刀的 Evidence 索引接入复盘验收,不新增命令、不碰上层 `@`、不接云 run/poll;任务中心 / 客服诊断消费非媒体索引仍作为下一步。
- 2026-05-05:继续第一百零三刀 `Phase 8 harness diagnostic taskIndex surface`:HarnessStatusPanel 的 Evidence Pack 区块新增“多模态任务索引”卡片,直接消费 `observability_summary.modality_runtime_contracts.snapshot_index.task_index`,展示 identity anchors、executor dimensions、cost/limit 统计与最近 `items[]`,并补组件回归覆盖 `thread/content/binding/limit` 可见。该刀只把既有 Evidence taskIndex 接入客服/开发诊断面板,不新增命令、不改 `@`、不接云 run/poll;任务中心过滤非媒体 artifact 仍作为下一步。
- 2026-05-05:继续第一百零四刀 `Phase 8 taskIndex query model`:新增 `src/lib/agentRuntime/modalityTaskIndexPresentation.ts`,把 Evidence `snapshotIndex.taskIndex` 统一转换为任务中心可复用的 facets、rows 与 exact filters,并让 HarnessStatusPanel 的“多模态任务索引”卡片复用同一查询模型而不是本地临时汇总。该刀只建立非媒体任务索引的前端查询消费层,不新增命令、不碰上层 `@`、不接 LimeCore 云 run/poll;完整任务中心列表 UI 接入仍作为 Phase 8 收尾项。
- 2026-05-05:继续第一百零五刀 `Phase 8 taskIndex task center filter surface`:HarnessStatusPanel 的“多模态任务索引”卡片新增“任务中心过滤列表”,直接消费 `modalityTaskIndexPresentation.rows`,支持按 entry、content、executor、cost、limit 过滤非媒体任务行,并展示过滤命中数与完整 artifact path。该刀把第 104 刀查询模型落成可见任务中心过滤 UI,不新增命令、不碰上层 `@`、不接 LimeCore 云 run/poll;后续若新增独立主任务中心入口,只能复用同一 rows。
- 2026-05-05:继续第一百零六刀 `Phase 8 taskIndex surface cleanup guard`:把第 105 刀内联在 HarnessStatusPanel 的 taskIndex 列表/过滤 UI 抽成 `HarnessTaskIndexSection`,HarnessStatusPanel 只保留挂载面;`check-modality-runtime-contracts.mjs` 新增 `task index presentation guard`,要求 `modalityTaskIndexPresentation` 继续导出 facets / rows / filters、section 必须消费这些 helper,且禁止 HarnessStatusPanel 重新内联 taskIndex 查询/list UI。该刀是治理减法和守卫,不新增命令、不碰上层 `@`、不接云 run/poll。
- 2026-05-05:继续第一百零七刀 `Phase 8 taskIndex section regression split`:新增 `HarnessTaskIndexSection.test.tsx`,独立覆盖 taskIndex 摘要、任务中心过滤列表、entry 过滤与清空过滤;`HarnessStatusPanel.test.tsx` 只保留 evidence pack 挂载与关键文案断言,不再承载 taskIndex 过滤交互细节。该刀继续收口测试边界,让 taskIndex UI 的回归跟随 current section,而不是依赖巨型面板测试;不新增命令、不碰上层 `@`、不接云 run/poll。
- 2026-05-05:继续第一百零八刀 `Phase 5/6 unresolved permission delivery block`:Evidence Pack 现在把 `requires_confirmation` 且 `confirmationStatus=not_requested / requested` 的权限状态标为 `signalCoverage.permissionState=blocked`,`knownGaps` 同步输出“尚未发起 ApprovalRequest”或“真实权限确认等待处理”的交付阻断说明;Replay 补回归固定 `not_requested` 会生成 blocking check,`resolved` 仍不阻断。该刀只推进审计/复盘/交付判定事实源,不伪造 `ApprovalRequest`、不改变 native approval 行为、不阻断 turn 执行、不新增命令、不接 LimeCore 云策略。
- 2026-05-05:继续第一百零九刀 `Phase 5/6 unresolved permission review decision guardrail`:`save_runtime_review_decision` 现在不只阻止 `denied + accepted`,也会在 `requires_confirmation` 且 `confirmationStatus=not_requested / requested / 未解决` 时拒绝保存 `accepted`;Review decision checklist / suggested actions 同步提示未解决权限确认不能作为成功交付证据,前端填写弹窗与浏览器 mock 也改为“权限确认未解决时不能保存接受”。该刀守住第 108 刀 Evidence / Replay 阻断语义的写回边界,不伪造 `ApprovalRequest`、不改变 native approval 行为、不阻断 turn 执行、不新增命令、不接 LimeCore 云策略。
- 2026-05-05:继续第一百一十刀 `Phase 5/6 unresolved permission handoff analysis risk`:Handoff bundle 与 Analysis handoff 现在把 `not_requested / requested / denied` 未解决权限确认统一视为交付阻断风险;`plan.md / handoff.md / review-summary.md`、analysis brief、analysis context 与 copy prompt 会显示“权限确认尚未解决 / 尚未发起真实审批请求 / 不能作为成功交付证据”,`resolved` 仍保持非阻断。该刀把第 108 刀 Evidence / Replay 阻断事实同步到交接与外部分析入口,不伪造 `ApprovalRequest`、不改变 native approval 行为、不阻断 turn 执行、不新增命令、不接 LimeCore 云策略。
- 2026-05-05:继续第一百一十一刀 `Phase 5/6 unresolved permission turn gating`:`runtime_turn` 现在会在 prelude 发出 `permission_review` 状态后、模型流真正开始前读取同一 `lime_runtime.permission_state`,当 `status=requires_confirmation` 且 `confirmationStatus` 不是 `resolved` 时把 turn 标为 failed 并发送错误事件;`permission_review` 文案同步说明未解决确认会阻断模型执行,`resolved` 才允许继续。该刀不伪造 `ApprovalRequest`、不新增 Tauri command、不接 LimeCore 云 run/poll,也不碰上层 `@` 命令;用户确认/恢复入口留给下一刀。
- 2026-05-05:继续第一百一十二刀 `Phase 5/6 permission confirmation request recovery`:runtime turn 在未 resolved 的 `requires_confirmation` 阻断前,会为 `confirmationStatus=not_requested` 且尚无 request id 的权限摘要写入真实 `runtime_permission_confirmation:<turn_id>` / `RequestUserInput(elicitation)` timeline item,并发送同源 `action_required`;响应复用既有 `agent_runtime_respond_action`,只对该前缀请求写回 completed response,不新增 Tauri command,也不把它伪装成工具 `ApprovalRequest`。下一轮恢复请求会从同一 session detail 读取最近权限确认 item,把 response 派生为 `confirmationStatus=resolved/denied`、真实 request id 与 `runtime_action_required` 来源,再交给同一 turn gating 判定;这完成的是本地最小确认恢复闭环,完整权限系统、同 turn 自动恢复、用户锁定 gap 确认式恢复、云端 policy evaluator 与真实 Gateway adapter 仍后置。
- 2026-05-05:继续第一百一十三刀 `Phase 5/6 user locked capability gap turn gating`:`request_model_resolution` 现在会给显式用户模型锁定导致的 runtime capability gap 标记 `capability_gap_source=explicit_model_lock`,并把 `limit_state.status` 收敛为 `user_locked_capability_gap`;`runtime_turn` 在 prelude 后、模型执行前读取同一 `lime_runtime.limit_state`,命中该状态时发出 routing runtime status、标记 turn failed 并发送错误事件,要求用户切换到满足 `routingSlot` 的模型或取消本轮显式锁定。该刀只把第 73 刀的 user lock gap 从解释推进为执行前阻断,不新增 Tauri command、不接 LimeCore 云 run/poll、不触碰上层 `@` 命令;确认式恢复与更完整 GUI 仍后置。
- 2026-05-06:继续第一百一十四刀 `Phase 5/6 user locked capability offline delivery block`:`user_locked_capability_gap` 现在不只 live turn 阻断,Evidence Pack `knownGaps`、Replay blocking checks、Handoff bundle、Analysis handoff 与 Review decision 也会同步把它视为不能成功交付的阻断事实;Review decision 读取 `limitStatus / capabilityGap / userLockedCapabilitySummary` 后会提示切换满足 `routingSlot` 的模型或取消显式锁定,并在保存 `accepted` 时由 Rust API 直接拒绝。该刀只把第 113 刀的执行前阻断补齐到离线审计 / 复盘 / 交接 / 外部分析 / 人工审核写回链,不新增命令、不接 LimeCore 云 run/poll、不碰上层 `@` 命令;用户锁定 gap 的确认式恢复与 GUI 自动恢复仍后置。
- 2026-05-06:继续第一百一十五刀 `Phase 5/6 user locked capability review API surface`:前端 `AgentRuntimeReviewDecisionTemplate` 与 normalizer 现在保留 `limitStatus / capabilityGap / userLockedCapabilitySummary`,DevBridge / browser mock 的 `agent_runtime_save_review_decision` 也会在 `limit_status=user_locked_capability_gap` 且保存 `accepted` 时抛出同类阻断错误;Harness 人工审核卡片和填写弹窗同步展示“模型锁定能力缺口”,并禁用 / 阻止“接受”结论。该刀把第 114 刀 Rust 离线阻断接到 API / Mock / GUI 读取与写回面,不新增命令、不接云 run/poll、不做独立任务中心或上层 `@` 入口。
- 2026-05-06:继续第一百一十六刀 `Phase 5/6 user locked capability confirmation recovery`:`runtime_turn` 现在会在 `user_locked_capability_gap` 执行前阻断时写入真实 `runtime_user_lock_capability:<turn_id>` / `RequestUserInput(elicitation)`,响应继续复用既有 `agent_runtime_respond_action` 写回 completed response,不新增 Tauri command、不伪造 `ApprovalRequest`;下一轮同 `turn_id` 恢复请求会读取 completed response,把用户确认投影为 `user_lock_capability_recovery(status=resolved/denied)`,其中 `resolved` 会释放本轮显式 `provider/model` 偏好并重新走 provider/model resolution,`denied` 保持阻断。该刀只完成本地最小确认式恢复,不接 LimeCore 云 run/poll、不扩上层 `@`,完整 GUI 自动重试与云端策略授权继续后置。
@@ -1,29 +0,0 @@
# OEM 云端商业闭环推进记录
## 主目标
把 Lime 客户端与 LimeCore 的云端购买链路收敛为单一 current 主链:套餐/充值下单 -> 真实支付 checkout -> 支付渠道 webhook -> 权益/积分/账本刷新 -> API Key 与网关使用。
## 当前阶段
- `current`:`useOemCloudAccess` 消费 `/client/cloud-activation`、订单、充值单、账本、支付配置与访问令牌,客户端不再本地兜底套餐、渠道或模型目录。
- `current`:支付完成后优先通过 LimeCore HTTPS `payments/{provider}/return` bridge 回到客户端,再 302 到 `lime://payment/return`,触发云端状态刷新和订单 watcher。
- `dead`:旧的客户端支付后台密钥配置入口已从设置页主链移除,管理型支付配置只保留在服务端管理面。
## 2026-04-28 进度
- 新增 `src/lib/oemCloudPaymentReturn.ts`,统一生成、解析、暂存和分发云端支付回跳事件。
- `useDeepLink` 识别 `lime://payment/return`,不走旧 `handle_deep_link` 命令,直接分发 current 云端商业刷新事件。
- `useOemCloudAccess` 在套餐购买和积分充值 checkout 中传入 `successUrl` / `cancelUrl`,支付页回跳后自动刷新云端权益、积分余额与账本,并重新接上订单 watcher。
- 增加回归覆盖 deep link 分发、checkout 回跳 URL、支付回跳后刷新商业状态。
## 2026-04-28 追加进度
- LimeCore 新增公开 HTTPS bridge:`/api/v1/public/tenants/{tenantId}/payments/{provider}/return`,支持 GET 与 form POST。
- bridge 只组装并 302 到 `lime://payment/return`,不在回跳页确认支付;真实权益发放仍只由 provider webhook 驱动。
- Lime 客户端 checkout 回跳 URL 改为 HTTPS bridge,保留客户端 deep link 消费和状态刷新逻辑。
- Creem checkout 已接入官方 `success_url` 参数,成功回跳进入 HTTPS bridge;取消回跳不伪造本地结果,继续依赖 watcher 与 webhook 后的服务端状态。
## 下一刀
继续用真实渠道沙箱串一次 EPay / Stripe / Creem / Waffo,确认各渠道回跳是否都能回到 `lime://payment/return`,同时验证 webhook finalize 后客户端账本、积分和权益刷新一致。
@@ -1,192 +0,0 @@
# 旧会话打开性能优化计划
## 目标
降低打开旧对话的首帧等待和后续 invoke 争抢,优先保证旧会话能快速显示目标会话与最近消息,再逐步补齐历史与执行轨迹。
## 当前进度
- `P0` 已完成:所有非显式全量的 `getSession` 调用补齐 `historyLimit: 40`,避免静默恢复和 missing-from-topics 校验拉取全量历史。
- `P1` 已完成:会话元数据 fallback 回填合并为一次 `updateSessionMetadata`;无本地快照切换时先进入目标会话 hydrating shell;侧边栏 focus refresh 降低为 idle 后台任务。
- `P2` 已推进:加载更多历史已从递增 tail window 改为分页加载;首屏返回最近 `40` 条并带 `history_cursor.oldest_message_id`,加载更早历史时优先用 `historyBeforeMessageId` cursor,每次请求 `50` 条,`historyOffset` 保留为兼容 fallback;历史会话的 MessageList 首帧先渲染文本,timeline 在 idle 后补齐;timeline turn 绑定减少中间数组分配;折叠的 `AgentThreadTimeline` 明细改为展开时再物化;旧历史中已完成的单步 timeline 明细默认只渲染摘要,展开时再物化;已排序的 turn/thread item 不再重复复制排序。
- `2026-04-29` 追加收口:过大的旧会话 transient / persisted snapshot 在恢复前直接丢弃;读取命中的会话快照不再同步刷新 `lastAccessedAt` 以避免点击时重写整张 snapshot map;历史消息与流式 text_delta 统一做 overlap 合并,修复累计快照式 delta 导致的重复吐字;完成态旧消息的执行过程与正文视觉分离,历史 timeline 默认折叠明细;浏览器 DevBridge 模式下跳过低优先级 metadata backfill,并且仅在活跃运行时订阅 team SSE,避免旧会话点击后抢占 bridge 连接。
- `2026-04-29` 追加 GUI 收口:复现旧会话顶部 `+` 新建后新 tab 仍显示旧消息的问题;`createFreshSession` 现在显式清空新建会话消息快照,同时 `ensureSession` 的首轮发送保留本地草稿,避免发送链路回归;旧会话 MessageList 仅在活跃发送/等待态底部锚定,已完成历史默认吸顶。
- `2026-04-29` 追加任务中心收口:复现旧会话中点击侧栏会话/顶部标签后被 `initialSessionId` 路由抢回的问题;任务中心本地切换现在在 `switchTopic` 前先标记 local override,失败时再回滚,避免异步切换期间显示旧 tab 或空态。
- `2026-04-29` 追加发送中吸顶收口:复现用户首条消息发送中仍贴近输入区、完成后才跳到上方的问题;MessageList 现在无论发送中、排队中还是完成态都保持 `justify-start`,避免完成前后布局跳动。
- `2026-04-29` 追加一级新建任务收口:任务中心内点击侧栏一级 `新建任务` 现在与对话架 `新建对话` 复用同一个可取消草稿事件;只有任务中心真实接管后才阻止路由,避免旧会话 A 新增草稿、切到旧会话 B 后再次点击侧栏新建时误跳回 `new-task-home`。
## 剩余事项
- Cursor 分页已完成:`agent_runtime_get_session` 继续作为 current 主链,新增 `historyBeforeMessageId` 请求字段与 `history_cursor` 响应字段;无 cursor 或缓存恢复缺少 cursor 时继续 fallback 到 `historyOffset`。
- 若用户仍反馈旧会话卡顿,再对 `AgentThreadTimeline` 内部渲染和 thread item 展开策略做专项测量。
- 若后续仍有 CPU / 内存尖峰,下一刀优先检查真实旧会话中是否存在超长 artifact / markdown 表格渲染,以及 `StreamingRenderer` 首屏 markdown 解析成本。
- `verify:local` 已完成通过;此前内嵌 smoke / 后续 smoke 重试中遇到的 DevBridge `fetch failed` 与临时 target 链接 `liblime_lib.dylib` 打开失败,已通过后续重跑消除,按历史环境/并发构建插曲保留记录。
## 验证记录
- `npm test -- src/components/agent/chat/utils/threadTimelineView.test.ts src/components/agent/chat/components/MessageList.test.tsx` 通过。
- `npm test -- src/components/agent/chat/hooks/agentRuntimeAdapter.test.ts src/components/agent/chat/hooks/agentSessionRefresh.test.ts src/components/agent/chat/hooks/useAsterAgentChat.test.tsx src/components/AppSidebar.test.tsx src/components/agent/chat/components/MessageList.test.tsx src/components/agent/chat/utils/threadTimelineView.test.ts` 通过。
- `npm run typecheck` 通过。
- `npm run lint` 通过。
- `cargo check --manifest-path src-tauri/Cargo.toml -p lime-services` 通过;期间修正 `api_key_provider_service.rs` 对运行时 Provider DTO 的显式导入,避免旧凭证池模型导出边界影响编译。
- `npm run verify:gui-smoke` 通过。
- `npm run generate:agent-runtime-clients` 通过,已更新 `agent_runtime_get_session` generated manifest。
- `npm test -- src/components/agent/chat/hooks/agentChatHistory.test.ts src/components/agent/chat/hooks/useAsterAgentChat.test.tsx` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml -p lime-core tail -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml -p lime-agent history_tail -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml -p lime-agent get_session_sync_with_history_limit_should_tail_persisted_history -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml normalize_runtime_session_history -- --nocapture` 通过。
- `npx eslint "src/lib/api/agentRuntime.ts" "src/lib/api/agentRuntime/*.ts" "src/components/agent/chat/hooks/agentChatHistory.ts" "src/components/agent/chat/hooks/useAgentSession.ts" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx" --max-warnings 0` 通过。
- `npm run test:contracts` 通过。
- `npm run generate:agent-runtime-clients` 通过,已同步 `historyBeforeMessageId` generated manifest。
- `npm test -- src/components/agent/chat/hooks/agentChatHistory.test.ts src/components/agent/chat/hooks/useAsterAgentChat.test.tsx` 通过(新增 cursor 起始索引与加载更早历史 cursor 请求断言)。
- `npm test -- src/lib/api/agent.test.ts src/components/agent/chat/hooks/agentRuntimeAdapter.test.ts` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml -p lime-core tail -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml -p lime-agent history_tail -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml -p lime-agent get_session_sync_with_history_limit_should_tail_persisted_history -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml normalize_runtime_session_history -- --nocapture` 通过。
- `cargo test --manifest-path src-tauri/Cargo.toml should_skip_runtime_queue_snapshots_for_cursor_history_page -- --nocapture` 通过;首次重跑曾撞到并发修改/编译中的 `runtime_evidence_pack_service.rs`,随后重跑通过。
- `npx eslint "src/lib/api/agentRuntime.ts" "src/lib/api/agentRuntime/*.ts" "src/components/agent/chat/hooks/useAgentSession.ts" "src/components/agent/chat/hooks/agentChatHistory.ts" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- `npm run test:contracts` 通过。
- `npm run verify:local` 前置检查、前端测试、Rust 全量测试均通过;最后内嵌 `verify:gui-smoke` 因 DevBridge `fetch failed` 中断。
- `npm run verify:gui-smoke` 后续重试在 headless Tauri 链接阶段失败,关键错误为临时 target 下 `liblime_lib.dylib` 打开失败;同命令此前已单独通过。
- `npm test -- src/components/agent/chat/components/AgentThreadTimeline.test.tsx src/components/agent/chat/components/MessageList.test.tsx` 通过。
- `npx eslint "src/components/agent/chat/components/AgentThreadTimeline.tsx" "src/components/agent/chat/components/AgentThreadTimeline.test.tsx" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" --max-warnings 0` 通过。
- `npm test -- src/components/agent/chat/utils/threadTimelineView.test.ts src/components/agent/chat/components/MessageList.test.tsx` 通过。
- `npx eslint "src/components/agent/chat/utils/threadTimelineView.ts" "src/components/agent/chat/utils/threadTimelineView.test.ts" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- `npm run verify:local` 通过;覆盖 `verify:app-version`、`lint`、`typecheck`、全量前端测试、`test:contracts`、全量 Rust 测试与内嵌 `verify:gui-smoke`。
- `npm test -- src/components/agent/chat/components/AgentThreadTimeline.test.tsx src/components/agent/chat/components/MessageList.test.tsx` 通过(新增旧历史单步 timeline 展开后再物化明细断言)。
- `npx eslint "src/components/agent/chat/components/AgentThreadTimeline.tsx" "src/components/agent/chat/components/AgentThreadTimeline.test.tsx" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- `npm run verify:gui-smoke` 通过。
- `npm test -- src/components/agent/chat/components/AgentThreadTimeline.test.tsx src/components/agent/chat/components/MessageList.test.tsx src/components/agent/chat/hooks/agentChatHistory.test.ts src/components/agent/chat/hooks/agentChatStorage.test.ts src/components/agent/chat/hooks/agentSessionScopedStorage.test.ts src/components/agent/chat/hooks/useAgentRuntimeSyncEffects.test.tsx src/components/agent/chat/hooks/useAsterAgentChat.test.tsx` 通过(7 files / 293 tests)。
- `npx eslint "src/components/agent/chat/hooks/useAgentSession.ts" "src/components/agent/chat/hooks/useAgentRuntimeSyncEffects.ts" "src/components/agent/chat/hooks/useAgentRuntimeSyncEffects.test.tsx" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx" "src/components/agent/chat/hooks/agentChatHistory.ts" "src/components/agent/chat/hooks/agentStreamRuntimeHandler.ts" "src/components/agent/chat/hooks/skillCommand.ts" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/AgentThreadTimeline.tsx" "src/components/agent/chat/hooks/agentChatStorage.ts" "src/components/agent/chat/hooks/agentSessionScopedStorage.ts" "src/components/agent/chat/hooks/agentChatHistory.test.ts" "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/components/AgentThreadTimeline.test.tsx" "src/components/agent/chat/hooks/agentChatStorage.test.ts" "src/components/agent/chat/hooks/agentSessionScopedStorage.test.ts" --max-warnings 0` 通过。
- `npm run bridge:health -- --timeout-ms 120000` 通过,DevBridge `/health` 约 `20ms` 就绪。
- Playwright E2E(`http://127.0.0.1:1420/`):连续切换 `E2E layout anchor -> 你好!👋 很高兴见到你! -> 你好!有什么我可以帮你的吗?😊 -> E2E layout anchor`,每次 `restoring=0`,耗时约 `109-425ms`,`body/html cursor=auto`,最终 `heap≈121MB`;`assistant-primary-timeline-shell` 高度 `94px`,`openDetails=0`,最终正文不在执行过程 shell 内,未检测到 assistant greeting 重复;等待 `9s` 后 `eventRequests=0`,控制台 `0 error / 1 warning`(仅浏览器模式 i18n 默认语言提示)。
- `npx vitest run "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx"` 通过(2 files / 230 tests),覆盖旧会话吸顶、新建任务清空旧消息与首轮发送草稿保留。
- `npx eslint "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/hooks/useAgentSession.ts" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx" --max-warnings 0` 通过。
- Playwright E2E(`http://127.0.0.1:1420/`):从新建空态点击侧边栏 `E2E layout anchor`,旧会话约 `237-331ms` 可见且 `message-list-column` 为 `justify-start`;再点击顶部 `+` 或侧边栏 `新建任务` 均回到 `青柠一下,灵感即来` 空态,旧会话文本不可见,`body cursor=auto`。本轮仅剩 DevBridge 偶发 `get_local_skills_for_app` / `project_memory_get` 1800ms timeout 控制台噪音,主交互未阻塞。
- Playwright E2E(`http://127.0.0.1:1420/`):从首页点击侧栏 `E2E layout anchor` 约 `419ms` 可见旧会话;从空态点击顶部旧会话标签约 `304ms` 可见旧会话;旧会话中点击顶部 `+` 约 `124ms` 回到新建空态;旧会话中点击侧栏 `新建任务` 约 `106ms` 回到新建空态;旧会话 `message-list-column` 保持 `justify-start`,`cursor=auto`,控制台 `0 error / 0 warning`。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(7 tests),覆盖任务中心切到非路由会话时不被 `initialSessionId` 抢回。
- `npx vitest run "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx"` 通过(3 files / 326 tests)。
- `npx eslint "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" "src/components/agent/chat/hooks/useAgentSession.ts" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- `npm run verify:gui-smoke` 通过。
- `git diff --check -- src/components/agent/chat/AgentChatWorkspace.tsx src/components/agent/chat/index.test.tsx src/components/agent/chat/components/MessageList.tsx src/components/agent/chat/components/MessageList.test.tsx src/components/agent/chat/hooks/useAgentSession.ts src/components/agent/chat/hooks/useAsterAgentChat.test.tsx docs/exec-plans/old-session-open-performance-plan.md` 通过。
- `npm run verify:local` 通过;覆盖 `verify:app-version`、`lint`、`typecheck`、全量前端测试、`test:contracts`、全量 Rust 测试与内嵌 `verify:gui-smoke`。
- Playwright E2E 复测(`http://127.0.0.1:1420/`):刷新后控制台 `0 error / 1 warning`(仅浏览器模式 i18n 默认语言提示);点击侧栏 `E2E layout anchor` 约 `133ms` 可见旧会话且 `message-list-column` 为 `justify-start`;随后点击侧栏 `新建任务` 约 `118ms` 回到 `青柠一下,灵感即来` 空态,旧消息不可见,`body/html cursor=auto`。
- `npx vitest run "src/components/agent/chat/components/MessageList.test.tsx" -t "短对话发送首帧|任务中心发送首帧|旧会话短消息"` 通过,覆盖发送中短消息与任务中心发送首帧均吸顶。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(7 tests),并将顶部加号测试改为等待按钮就绪后点击,降低慢机环境下的偶发失败。
- Playwright E2E 复测(`http://127.0.0.1:1420/`):发送 `吸顶测试,请只回复 OK。` 后立即采样、`250ms`、`1250ms` 均为 `message-list-column` `justify-start`,首条消息距滚动视口顶部约 `22px`,`scrollTop=0`,`body/html cursor=auto`,确认发送中首帧不再贴底;旧会话 `E2E layout anchor` 打开后 `message-list-column` 同样为 `justify-start`;侧栏 `新建任务` 约 `97ms` 回到 `青柠一下,灵感即来` 空态且旧消息不可见。刷新后控制台保持 `0 error / 1 warning`(仅浏览器模式 i18n 默认语言提示)。
- `npx vitest run "src/components/agent/chat/components/MessageList.test.tsx" "src/lib/api/memory.test.ts" -t "短对话发送首帧|任务中心发送首帧|旧会话短消息|project_memory_get|项目记忆|短时间重复|并发读取"` 通过(2 files / 6 tests)。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(7 tests)。
- `npx eslint "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" "src/lib/api/memory.ts" "src/lib/api/memory.test.ts" --max-warnings 0` 通过。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace / browser-runtime / site-adapters / service-skill-entry / runtime tool-surface page smoke 均通过。
- `2026-04-29` 追加浏览器式 Tab 收口:任务中心顶部 `+` 改为本地 `task-draft-*` 草稿 tab,点击阶段不再创建真实 session;`CustomEvent` 新建请求改为可取消事件,只有任务中心工作区实际接管后侧栏才停止路由跳转,避免侧栏 `新建任务` 在任务中心内误跳回 `new-task-home`。
- `npx eslint "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/taskCenterDraftTaskEvents.ts" "src/components/AppSidebar.tsx" "src/components/agent/chat/components/EmptyState.tsx" --max-warnings 0` 通过。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(8 tests),覆盖顶部加号和侧栏事件新建草稿 tab,不触发 `createFreshSession` / 路由跳出。
- `npx tsc --noEmit --pretty false` 通过;同时修复本轮 E2E 中暴露的 `EmptyState` 首页组件 HMR 崩溃,刷新后控制台恢复到 `0 error / 1 warning`(浏览器模式 i18n 默认语言提示)。
- Playwright E2E(`http://127.0.0.1:1420/`):从旧会话点击顶部 `+`,`task-center-tab-task-draft-*` 约 `77ms` active,`青柠一下,灵感即来` 约 `95ms` 可见;旧 `message-list-column` 不再渲染,旧消息不可见;invoke trace `10` 条,`createSessionCalls=0`,无 invoke error。
- 本轮 GUI smoke 未完成:`npm run bridge:health -- --timeout-ms 120000` 因当前工作区并行 Rust 改动导致 DevBridge 未就绪而等待,`tauri:dev:headless` 日志显示 `src-tauri/src/commands/aster_agent_cmd/service_skill_launch.rs` 存在非本轮编译错误(`ServiceSceneLaunchContext` 缺字段、mutable borrow 冲突),需先收口该 Rust 改动后再重跑 `npm run verify:gui-smoke`。
- `2026-04-29` 追加多旧会话切换复现:新增回归覆盖 `旧会话 A -> 顶部 + 新增草稿 -> 路由切到旧会话 B -> 再次顶部 +`。根因是路由 `initialSessionId` 切到真实旧会话时只替换真实 tab,没有清掉仍 active 的本地 draft id,导致旧会话 B tab 不是 active,后续新增状态被旧 draft 干扰;现在切到真实路由会话时仅取消 active draft,保留草稿 tab 列表。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "切到另一条旧会话后仍应继续新增本地草稿标签"` 通过。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(9 tests),覆盖顶部 +、侧栏事件、旧会话 A/B 切换后再次新增、路由抢回防护。
- `npx eslint "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/taskCenterDraftTaskEvents.ts" "src/components/AppSidebar.tsx" --max-warnings 0` 通过。
- `npx tsc --noEmit --pretty false` 通过。
- Playwright 真实旧会话 A/B 复测当前仍被 DevBridge 阻塞:页面控制台为 bridge 连接错误,`npm run bridge:health -- --timeout-ms 120000` 超时;阻塞仍来自非本轮 Rust 编译错误,需先恢复 `src-tauri/src/commands/aster_agent_cmd/service_skill_launch.rs` 后重测。
- `npx vitest run "src/components/AppSidebar.test.tsx" -t "任务中心内点击一级新建任务"` 通过,覆盖任务中心内一级主导航 `新建任务` 被草稿事件接管且不触发路由跳转。
- `npx vitest run "src/components/AppSidebar.test.tsx"` 通过(34 tests)。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(9 tests);首次与 AppSidebar 全量测试并行时出现一次顶部 `+` 慢机 transition 偶发断言,单独重跑与整组重跑均通过。
- `npx eslint "src/components/AppSidebar.tsx" "src/components/AppSidebar.test.tsx" --max-warnings 0` 通过。
- `npx tsc --noEmit --pretty false` 通过。
- `git diff --check -- src/components/AppSidebar.tsx src/components/AppSidebar.test.tsx src/components/agent/chat/AgentChatWorkspace.tsx src/components/agent/chat/index.test.tsx src/components/agent/chat/taskCenterDraftTaskEvents.ts` 通过。
- `npm run bridge:health -- --timeout-ms 120000` 重新通过;本轮先清理了当前仓库残留的并发 Tauri/Cargo dev 进程,随后 `DevBridge` 在 `http://127.0.0.1:3030/health` 返回 `status=ok`。
- Playwright E2E(`http://127.0.0.1:1420/`):刷新首页后执行 `旧会话 A(E2E layout anchor) -> 侧栏一级新建任务 -> 旧会话 B(Lime 助手) -> 侧栏一级新建任务`;打开 A 约 `669ms`,A 后新建草稿约 `63ms`,打开 B 约 `213ms`,B 后新建草稿约 `50ms`。两次新建均激活 `task-center-tab-task-draft-*`,不会出现 active `task-center-tab-new-task-home`;第二次后保留 `2` 个 draft tab,旧会话 tab inactive,首页空态可见,`body/html cursor=auto`。
- 同轮 Playwright invoke trace:总 `/invoke` `16` 次,`agent_runtime_create_session=0`;两次 `agent_runtime_get_session` 均携带 `historyLimit: 40`;侧栏列表仅一次 `agent_runtime_list_sessions`,`limit=37`;无 invoke error。控制台 `0 error / 2 warning`,warning 为浏览器模式事件流中止与 i18n 默认语言提示,均非主链阻塞。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、service-skill-entry、runtime tool-surface 与 runtime tool-surface page smoke 均通过。
- `npm run verify:local` 已执行到前端全量测试第 `26/43` 批后失败,失败集中在 `src/components/agent/chat/components/EmptyState.test.tsx` 的 `18` 条旧首页断言;实际渲染已切到新的 `HomeStartSurface` / `HomeSkillGallery` 首页文案与结构,属于本轮侧栏 tab 修复之外的首页改版测试漂移,需后续单独收口 EmptyState 测试或恢复对应首页能力。
- `2026-04-30` 追加修复“无法打开两个历史对话”:根因是外层 `AppSidebar` 点击历史会话只做路由跳转,任务中心没有本地打开意图;随后 `AgentChatWorkspace` 的 `initialSessionId` 同步 effect 将 open tab map 替换为单个目标会话。现在侧栏点击历史会话会先发送 `lime:task-center:open-task` 事件,任务中心先乐观 upsert 目标 tab 并记录本地路由追平意图;路由追平时仅激活/前置目标 tab,不再替换掉已有历史 tab。
- 同轮收口:任务中心内部 `handleOpenSidebarTaskTopic` / `handleResumeSidebarTask` / `handleResumeRecentSession` / fallback restore 默认走追加/激活语义,只有外部直达会话仍保留 replace 语义,避免旧缓存污染首开深链。
- `2026-04-30` 追加旧会话卡顿收口:MessageList 对恢复中或已分页旧会话使用更小的首屏批次(20 条)和 idle 小批量补齐;已完成的旧会话最后一个 turn 不再按 current turn 处理,避免历史 timeline 在首帧被当作活跃执行过程同步展开。
- `2026-04-30` 追加 invoke 降噪:`workspace_get` 增加 1s 短 TTL 与同 id in-flight 合并,避免旧会话切换期间工作区详情重复抢占 bridge;打开旧会话时自动回填的 `recent_preferences` / `recent_team_selection` 改为同 session 后台合并队列,12s 后 idle 再写,若期间已切走则直接丢弃,手动偏好修改仍保持即时低延迟同步。
- `2026-04-30` 追加旧会话预取:侧边栏会话按钮在 focus / pointer enter / pointer down 时发送 task-center prefetch 事件,当前 Agent 工作区复用 `runtime.getSession(historyLimit: 40)` 预取结果;若用户随后点击同一会话,`switchTopic` 直接等待同一个 in-flight 详情请求,不再重复打 `agent_runtime_get_session`,并把预取结果写入本地 tail snapshot。话题列表就绪后还会在测试环境外 idle 预取最近 3 条非当前会话,降低无本地 snapshot 时的首次点击等待。
- `2026-04-30` 追加路由恢复收口:从首页/侧栏通过 `initialSessionId` 进入旧会话时不再默认 `forceRefresh`,仅 `workspace_error` 会话继续强制刷新;命中 fresh tail snapshot 时先渲染缓存,远端详情刷新延后到 1.2s + idle,避免点击主链马上再打 `agent_runtime_get_session`。
- `npx vitest run "src/components/agent/chat/index.test.tsx" -t "任务中心初始会话标签"` 通过(11 tests),新增覆盖任务中心内部连续打开旧会话、外层侧栏事件 + 路由追平后保留两个历史 tab。
- `npx vitest run "src/components/AppSidebar.test.tsx"` 通过(35 tests),新增覆盖任务中心内点击已有会话会先通知本地标签栏,再继续导航。
- `npx eslint "src/components/AppSidebar.tsx" "src/components/AppSidebar.test.tsx" "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/index.test.tsx" "src/components/agent/chat/taskCenterDraftTaskEvents.ts" --max-warnings 0` 通过。
- `npx tsc --noEmit --pretty false` 通过。
- `npm run bridge:health -- --timeout-ms 120000` 通过,DevBridge `/health` 约 `22ms` 就绪。
- Playwright E2E(`http://127.0.0.1:1420/`):刷新后点击侧栏 `E2E layout anchor`,顶部仅显示并激活该旧会话;随后点击侧栏 `你好!👋 我是 Lime 助手,很高...`,顶部同时保留 `你好!👋 ...` 与 `E2E layout anchor` 两个历史 tab,目标会话 active,旧会话 inactive;页面停留 `http://127.0.0.1:1420/`,标题 `Lime`。
- 同轮 Playwright invoke trace:两次 `agent_runtime_get_session` 均携带 `historyLimit: 40`;侧栏 list 请求为 `agent_runtime_list_sessions` `limit=37/60`;全部 `/invoke` 为 `200 OK`,控制台 `0 error / 0 warning`。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、service-skill-entry、runtime tool-surface 与 runtime tool-surface page smoke 均通过。
- `npx vitest run "src/components/agent/chat/components/MessageList.test.tsx"` 通过(77 tests);新增覆盖已分页旧会话首帧只挂载更小尾部批次、已完成旧 turn 不再作为 active current turn 展开历史 timeline。
- `npx eslint "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" --max-warnings 0` 通过。
- `npx tsc --noEmit --pretty false` 通过。
- Playwright 性能复测(`http://127.0.0.1:1420/`):打开 `AI Trends Task`(188 总消息 / 最近 40)约 `567ms`,`longTasks=[]`,DOM 节点约 `637`;打开 `Slow typing E2E`(170 总消息 / 最近 40)约 `706ms`,`longTasks=[]`,DOM 节点约 `644`,控制台 `0 error / 0 warning`。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、service-skill-entry、runtime tool-surface 与 runtime tool-surface page smoke 均通过。
- `npm run verify:local` 本轮执行到 `lint` 阶段失败,失败点为非本轮文件 `src/components/agent/chat/home/HomeSceneSkillManagerDialog.tsx:388` 的 `react-hooks/exhaustive-deps` warning(`open` 为不必要依赖),本轮 touched 文件的定向 eslint 已通过。
- Playwright 预优化采样(`http://127.0.0.1:1420/`):打开 `Slow typing E2E` 约 `393ms` 可见,早期 invoke 为 `agent_runtime_update_session` ×2 + `agent_runtime_get_session` ×1,且两次 metadata 回填排在 `get_session` 前;本轮据此将自动 metadata 回填降为后台合并。
- `npx vitest run "src/lib/api/project.test.ts"` 通过(59 tests),覆盖 `workspace_get` 短缓存与并发去重。
- `npx vitest run "src/components/agent/chat/hooks/useSelectedTeamPreference.test.tsx"` 通过(13 tests),覆盖自动 fallback Team 回填走后台同步、手动切换仍即时回写。
- `npx eslint "src/lib/api/project.ts" "src/lib/api/project.test.ts" "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/hooks/useSelectedTeamPreference.ts" "src/components/agent/chat/hooks/useSelectedTeamPreference.test.tsx" --max-warnings 0` 通过。
- GUI smoke / Playwright 后置复测暂被本地并发 Cargo/Tauri 构建阻塞:`tauri:dev:headless` 首次暴露非本轮 `creation_tools.rs` 大型 `serde_json::json!` 递归限制,已在 `src-tauri/src/lib.rs` 补 `recursion_limit = "256"` 解除;随后仍有多个非本轮 `cargo test` / `cargo check` 并发进程占用 target/package lock,需环境空闲后重跑 `npm run bridge:health -- --timeout-ms 120000`、`npm run verify:gui-smoke` 与 Playwright 旧会话性能采样。
- `2026-04-30` Playwright 预取前采样(`http://127.0.0.1:1420/`):刷新后点击 `Slow typing E2E`,早期 invoke 仅 `workspace_get_default` / `workspace_list` / `agent_runtime_get_session(historyLimit:40)`,其中 `get_session` 约 `1261ms`;`localStorage/sessionStorage` 未命中 `aster_session_snapshots*`,说明当前慢点主要是无本地 tail snapshot 时必须等后端详情返回。
- `2026-04-30` Playwright 预取后采样(`http://127.0.0.1:1420/`):focus `AI Trends Task` 后预取 `agent_runtime_get_session(historyLimit:40)` 约 `116ms` 并写入 `aster_session_snapshots_*`;随后点击同会话,前 `500ms` 内不再出现 `agent_runtime_get_session`,仅剩 route/workspace 初始化类 `workspace_get/default/list`,消息组已可见,`body/html cursor=auto`。
- `npx vitest run "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx" "src/components/AppSidebar.test.tsx"` 通过(2 files / 191 tests),覆盖旧会话预取 in-flight 复用与侧边栏 focus 预取事件。
- `npx eslint "src/components/agent/chat/hooks/useAgentSession.ts" "src/components/agent/chat/hooks/useAsterAgentChat.ts" "src/components/agent/chat/hooks/useAsterAgentChat.test.tsx" "src/components/agent/chat/AgentChatWorkspace.tsx" "src/components/agent/chat/taskCenterDraftTaskEvents.ts" "src/components/AppSidebar.tsx" "src/components/AppSidebar.test.tsx" "src/components/app-sidebar/AppSidebarConversationShelf.tsx" --max-warnings 0` 通过。
- `npx tsc --noEmit --pretty false` 通过。
- `2026-04-30` 追加旧会话首帧渲染降载:历史恢复路径的助手正文新增 `MarkdownRenderer renderMode="light"`,旧会话首屏跳过高成本 `rehypeRaw` / `rehypeKatex` / Prism 代码高亮;实时流式与普通新消息继续走标准模式。已分页旧会话首屏批次从 `20` 缩小到 `10`,idle 补齐批次从 `12` 缩小到 `6`,且恢复路径 idle 延迟从 `120ms` 提高到 `600ms`,避免点击后立即连续吃主线程。
- `2026-04-30` 追加后端历史 payload 裁剪:`agent_thread_items` 的 tail / cursor 历史查询在 `file_artifact.content` 之外,继续裁剪超大的 `tool_call.output`、`command_execution.aggregated_output` 与 `web_search.output` 到约 `16KB` 并附截断提示;完整 `list_items_by_thread` 不裁剪,保留全量诊断能力。这针对本地 runtime.db 中数 MB 级 `social_generate_cover_image` tool output 导致旧会话恢复 CPU/内存尖峰的问题。
- Context7 / WebSearch 校准结论:React 官方文档说明 `useDeferredValue` / `startTransition` 只能降低优先级,不能让慢列表本身变快;TanStack Virtual 对动态高度长列表建议 `useVirtualizer + measureElement + overscan`;Tauri 文档建议大 payload 走 async command 与 Channel/chunk。当前先落地低风险的“减少首帧同步工作 + 裁剪历史 payload”,后续若 40 条历史仍卡,下一刀应评估 MessageList 动态高度虚拟化或 `agent_runtime_get_session` 分块返回。
- Playwright E2E(`http://127.0.0.1:1420/`):刷新后点击 `Slow typing E2E`,`agent_runtime_get_session(historyLimit:40)` 约 `322ms`,点击到首屏可见约 `605ms`,DOM 约 `491`,`body/html cursor=auto`,存在一次约 `113ms` long task;进一步缩小历史首屏批次后,等待页面稳定再切 `AI Trends Task -> Slow typing E2E`,可见旧会话 DOM 保持约 `480-626`,`cursor=auto`。当前 dev 环境 heap 读数约 `1.4GB`,更像 Vite/WebView 长会话累计值,不能单独当作本轮回归证据。
- `npm test -- "src/components/agent/chat/components/StreamingRenderer.test.tsx"` 通过(29 tests),覆盖 `markdownRenderMode="light"` 透传到 `MarkdownRenderer`。
- `npm test -- "src/components/agent/chat/components/MessageList.test.tsx"` 通过(82 tests),覆盖旧会话轻量 Markdown、长历史正文预览、历史 timeline 折叠与更小首屏批次。
- `npm run lint -- "src/components/agent/chat/components/MarkdownRenderer.tsx" "src/components/agent/chat/components/StreamingRenderer.tsx" "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/StreamingRenderer.test.tsx" "src/components/agent/chat/components/MessageList.test.tsx"` 通过。
- `cd src-tauri && CARGO_TARGET_DIR="/tmp/lime-core-test-target" cargo test -p lime-core item_tail_query_should` 通过(2 tests),覆盖历史 tail 查询裁剪大 file artifact 与大 tool output,完整查询不裁剪。
- `npm run test:contracts` 通过;本轮未新增命令,仅收窄 `agent_runtime_get_session` 历史窗口返回中的超大 inline payload。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、service-skill-entry、runtime tool-surface 与 runtime tool-surface page smoke 均通过。
- `npm run typecheck` 通过。
- `2026-04-30` 追加旧会话首帧二次降载:已分页/恢复旧会话中的长助手回复不再首帧挂载 `StreamingRenderer` / Markdown 解析,而是先展示纯文本预览,用户点击“展开完整内容”后才按轻量 Markdown 物化;超长历史回复的预览长度从约 `6000` 字收窄到约 `2000` 字,降低点击旧会话后的主线程解析与 DOM 压力。
- 同轮将历史 timeline 的 idle 物化从旧会话首帧后约 `80ms` 延后到约 `900ms`,避免消息刚可见后立刻构建完整 `buildMessageTurnTimeline` 抢占主线程;非旧会话仍保留原 `80ms` 行为。
- `npm test -- "src/components/agent/chat/components/MessageList.test.tsx"` 通过(83 tests),新增覆盖长助手回复纯文本预览与展开后再物化轻量 Markdown。
- Playwright E2E 复测(`http://127.0.0.1:1420/`):刷新后切 `AI Trends Task -> Slow typing E2E`,旧会话首屏约 `77ms` 可见,DOM 节点约 `403`,历史助手正文纯文本预览 `1` 个,历史 timeline 预览 `1` 个、真实 timeline 挂载 `0` 个,`longTasks=[]`,`body/html cursor=auto`,控制台 `0 error / 0 warning`。invoke trace 中两次 `agent_runtime_get_session` 均携带 `historyLimit: 40`,无 `/invoke` 错误。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、service-skill-entry、runtime tool-surface 与 runtime tool-surface page smoke 均通过。
- `2026-04-30` 追加旧会话后台渲染节流:恢复/已分页旧会话不再自动 idle 补齐当前窗口里更早的消息,首屏只保留尾部批次,用户点击“立即展开更早消息”时才继续物化,避免打开后数秒内后台持续挂载 Markdown / timeline 造成 CPU 与内存尖峰;普通非旧会话仍保留空闲自动补齐。
- 同轮进一步扩大历史 timeline 延后范围:即使旧会话只有少量消息,只要 execution/thread items 很多,也先显示正文,约 `900ms` 后再生成 timeline 摘要预览;这覆盖 `Slow typing E2E` 这类“消息少但工具轨迹多”的慢会话。
- `npm test -- "src/components/agent/chat/components/MessageList.test.tsx"` 通过(84 tests),新增覆盖旧会话不自动补齐历史窗口、少消息多执行过程延后构建 timeline。
- `npx eslint "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- 后置 `npm run verify:gui-smoke` 已尝试两次但未完成:当前本机已有非本轮 `cargo test --manifest-path src-tauri/Cargo.toml audio_generation_task` 与旧 `tauri:dev:headless` 长时间占用 Cargo artifact lock,DevBridge 无法就绪;为避免继续拉高 CPU,已停止本轮新拉起的 smoke 进程,未终止非本轮 cargo/tauri 进程。待该锁释放后需重跑 GUI smoke / Playwright。
- `2026-04-30` 追加首字前反馈优化:Playwright MCP 当前 transport closed,改用本地 Playwright + Chrome 复测;真实 DevBridge 恢复后发送 `首字延迟 E2E ...`,点击到用户消息可见约 `295ms`,新的 `assistant-first-token-placeholder` 同步约 `295ms` 可见。后端随后因本机缺少 `claude` 凭证在约 `5.3s` 返回失败,因此本轮无法测真实模型首 token,但已消除“首个模型事件前 assistant 气泡为空/像没响应”的 UI 体感慢点。
- 同轮代码收口:`MessageList` 在 assistant `isThinking` 且正文/parts/timeline 均为空但存在启动态 `runtimeStatus` 时,渲染轻量首字前占位;不把 runtimeStatus 塞回 `StreamingRenderer` 正文,也不渲染底部重复运行态,首个真实文本到来后自动切回正常流式渲染。
- `npm test -- "src/components/agent/chat/components/MessageList.test.tsx"` 通过(84 tests),新增覆盖启动态 runtimeStatus 只渲染轻量首字前占位、已有正文时不重复占位。
- `npx eslint "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- `git diff --check -- "src/components/agent/chat/components/MessageList.tsx" "src/components/agent/chat/components/MessageList.test.tsx"` 通过。
- `npm run verify:gui-smoke` 通过;复用现有 headless Tauri 与 DevBridge,workspace-ready、browser-runtime、site-adapters、service-skill-entry、runtime tool-surface 与 runtime tool-surface page smoke 均通过。
- `2026-04-30` 追加 DeepSeek 首字续测:Playwright MCP 仍为 `Transport closed`,继续用本地 Chrome + Playwright;模型选择入口需先展开“高级设置”,再点击 `button[role="combobox"]`,可切到 `DeepSeek / deepseek-v4-flash`。
- 同轮复现输出排版问题:DeepSeek 实际流里会返回 `thinking` 分片再返回最终 `text` 分片,例如 `llm_request.2.jsonl` 中先有 `thinking: "我们…好。"`,再有 `text: "好"`;旧前端在 thinking 关闭时仍渲染 thinking block,导致 UI 显示类似 `思考中…好。好`。
- 同轮代码收口:`agentStreamTurnEventBinding` 将本轮 `thinking` 偏好透传给 `agentStreamRuntimeHandler`;当 thinking 关闭时,`thinking_delta` 只激活流状态、不再写入 assistant `thinkingContent/contentParts`,后续 `text_delta` 会清理已有 thinking part,避免 DeepSeek 隐式 reasoning 混入最终正文。
- DeepSeek E2E 成功样本(`/tmp/lime-deepseek-first-token-e2e-retry.json`):`agent_runtime_create_session` 约 `73ms`,`agent_runtime_submit_turn` 约 `136ms`,首字前占位约 `117ms` 可见,最终 assistant 文本为 `好已完成·00:06`,不再出现 `思考中` / reasoning 重复吐字;过程中仍有一次非阻塞 `workspace_get` 5s timeout 噪音。
- DeepSeek E2E 失败样本(`/tmp/lime-deepseek-first-token-e2e-final.json`):同样选择 `DeepSeek / deepseek-v4-flash` 后,`agent_runtime_submit_turn` 偶发在 `60s` 前端超时并返回 DevBridge timeout,且本地 `aster/state/logs` 没有对应 LLM request 新日志,说明失败发生在提交链路/bridge 阻塞或运行时排队阶段,未进入 provider 首 token;这是后续首字慢优化的下一刀证据。
- `2026-05-02` 追加首页输入回车卡顿收口:`ensureSessionForCommandMetadata` 透传 `skipSessionRestore / skipSessionStartHooks`,首页首轮普通发送不再预热旧会话恢复,也跳过 submit 前队列恢复扫描;发送规划阶段新增 `workspaceSend.plan.ready` 性能点,能区分首页 shell、发送 plan、ensure session、submit invoke、首事件、首文本、首 paint。
- 同轮快速响应路由收口:`lime-hub/gpt-5.5`、`lime-hub/gpt-5.4` 与 DeepSeek 推理/Flash 模型的首轮轻量普通对话,若无显式 provider/model override、无工具/团队/上下文/能力路由,直接使用 `deepseek/deepseek-chat` 与短系统提示;Provider 列表未返回 DeepSeek 时也允许内置快速路由,当前已是 `deepseek-chat` 时只启用短提示词、不重复写 provider/model override。
- Playwright MCP E2E(`http://127.0.0.1:1420/`,输入 `只回答一个字:好`):修复前采样中首页到 `agentStream.request.start` 已约 `163ms`,但仍走 `provider=lime-hub / model=gpt-5.5`,`homeInputToFirstTextDeltaMs≈20515ms`;修复后两次复测均命中 `provider=deepseek / model=deepseek-chat`,样本一 `homeInputToPendingPreviewPaintMs=33ms`、`homeInputToStreamRequestStartMs=88ms`、`homeInputToFirstTextDeltaMs=1886ms`、`homeInputToFirstTextPaintMs=1914ms`,样本二 `23ms / 51ms / 1559ms / 1583ms`,`longTaskCount=0`,控制台 `0 error`。当前剩余首字耗时主要是 provider 首个文本 delta(约 `1.4-1.7s`),不是首页跳转或旧会话恢复阻塞。
- `npm test -- src/components/agent/chat/utils/fastResponseModel.test.ts src/lib/agentUiPerformanceMetrics.test.ts` 通过(16 tests);`npm test -- src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx -t "首页快路径首轮普通发送不应预热旧会话恢复|首轮轻量对话不等待 Provider 列表也应命中内置快速响应路由|首轮轻量对话有 DeepSeek 时应注入快速响应路由"` 通过(3 tests);`npm run verify:gui-smoke` 通过。
- `npm test -- "src/components/agent/chat/hooks/agentStreamRuntimeHandler.test.ts"` 通过(10 tests),新增覆盖 thinking 关闭时不渲染 reasoning_delta。
- `npm test -- "src/components/agent/chat/hooks/agentStreamTurnEventBinding.test.ts" "src/components/agent/chat/hooks/agentStreamSubmitExecution.test.ts"` 通过(7 tests)。
- `npx eslint "src/components/agent/chat/hooks/agentStreamRuntimeHandler.ts" "src/components/agent/chat/hooks/agentStreamTurnEventBinding.ts" "src/components/agent/chat/hooks/agentStreamSubmitExecution.ts" "src/components/agent/chat/hooks/agentStreamRuntimeHandler.test.ts" --max-warnings 0` 通过。
- `npm run typecheck` 通过。
- `npm run verify:gui-smoke` 通过;本轮临时 target 首次编译耗时较长,随后 DevBridge / workspace-ready / browser-runtime / site-adapters / service-skill-entry / runtime tool-surface page smoke 均通过。
- `2026-05-02` 追加首页首字体感指标拆分:在 `prepareAgentStreamSubmitDraft` 注入 assistant 草稿时记录 `agentStream.assistantDraft`,下一帧后记录 `agentStream.assistantDraftPaint`;性能汇总新增 `homeInputToAssistantDraftMs` 与 `homeInputToAssistantDraftPaintMs`,用于区分“页面已有首字前反馈”和“provider 真实首个 text_delta”。这避免继续把 DeepSeek 首包 `1.4-1.7s` 误判为首页跳转/主线程卡死。
- 同轮实现保持现有 UI 主链:不把“正在连接模型”等占位文案写进 assistant 正文,继续复用 `AssistantFirstTokenPlaceholder`;真实 `text_delta` 到来后仍由 `agentStreamRuntimeHandler` 用模型累积内容渲染,避免复发重复吐字 / 占位污染最终回复。
- 定向校验:`npm test -- "src/components/agent/chat/hooks/agentStreamSubmitDraft.test.ts" "src/lib/agentUiPerformanceMetrics.test.ts" "src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx" -t "assistant 草稿插入与绘制指标|首页输入提交到会话壳和发送派发|首轮轻量对话有 DeepSeek 时应注入快速响应路由"` 通过(3 tests);`npm run typecheck` 通过;`git diff --check -- "src/lib/agentUiPerformanceMetrics.ts" "src/lib/agentUiPerformanceMetrics.test.ts" "src/components/agent/chat/hooks/agentStreamSubmitDraft.ts" "src/components/agent/chat/hooks/agentStreamSubmitDraft.test.ts" "src/components/agent/chat/hooks/agentStreamUserInputSendPreparation.ts"` 通过。
- Playwright MCP 后置复测当前被本地 DevBridge 环境阻塞:`http://127.0.0.1:1420/` 可加载,但首页跳过 onboarding 后卡在“正在准备生成工作区”,控制台唯一 error 为 `http://127.0.0.1:3030/health net::ERR_CONNECTION_REFUSED`;`npm run tauri:dev:headless` 被已有 Vite `1420` 占用,`LIME_WEB_BRIDGE_REUSE_EXISTING_ONLY=1` 可复用前端但仍被现有多组 `cargo test/check/tauri dev` 锁竞争阻塞;改用隔离 `CARGO_TARGET_DIR="/tmp/lime-codex-e2e-target"` 后冷编译中途因 target 目录消失失败。待 `3030` 恢复后需补跑一次真实首页发送 E2E,重点采集 `homeInputToAssistantDraftPaintMs`、`homeInputToFirstRuntimeStatusMs`、`homeInputToFirstTextDeltaMs` 与 `longTaskCount`。
- `2026-05-05` 追加首轮纯聊天去 Browser Assist 负担:`withFastResponseMetadata` 写入快速响应路由时剥离默认 `harness.browser_assist / browserAssist`;首页首轮通用普通文本、无图片、无显式技能/浏览器需求、非 `/`/`@` 命令时跳过 `primeBrowserAssistBeforeSend`,避免发送前同步准备 Browser Assist 画布或把浏览器工具面带入普通快问快答。
- Playwright MCP E2E(`http://127.0.0.1:1420/`,输入 `只回答一个字:好`):`npm run bridge:health -- --timeout-ms 20000` 通过,DevBridge `/health` 约 `36ms`;真实发送链路中 `agent_runtime_create_session` 约 `49ms`,`agent_runtime_submit_turn` 约 `193ms`,`agent_runtime_get_session(historyLimit:40)` 约 `151ms`,最终正文为 `好`,`body/html cursor=auto`,DOM 约 `384`,heap used 约 `131MB`,发送期间仅 1 个约 `73ms` long task。`submit_turn.turn_config` 命中 `provider_preference=deepseek` / `model_preference=deepseek-chat` / `skip_pre_submit_resume=true`,`metadata.harness` 仅保留 `theme/preferences/session_mode/browser_user_step_required/fast_response_routing`,未携带 `browser_assist`。
- 同轮 E2E 观察到回复完成后还有一次辅助 `agent_generate_title`,耗时约 `2228ms`,随后 `agent_runtime_update_session(name)` 约 `2ms`;源码当前已通过 `AUTO_TITLE_DEFERRED_LOAD_MS=30000` + idle 调度延后自动标题生成,本轮采集器未记录每个 invoke 的绝对开始时间,因此暂不把它判定为首字慢根因。后续若继续出现“回答完仍卡 / 后续点击慢”,下一刀应补 invoke start-time 采集,并评估把标题生成下沉到更低优先级队列或延迟到用户空闲更久后执行。
- 定向校验:`npm test -- "src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx" -t "首轮轻量对话有 DeepSeek 时应注入快速响应路由|首页快路径首轮普通发送不应预热旧会话恢复|已携带 service_skill_launch metadata"` 通过(3 tests);`npx eslint "src/components/agent/chat/workspace/useWorkspaceSendActions.ts" "src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx" --max-warnings 0` 通过;`npm run typecheck` 通过;`git diff --check -- "src/components/agent/chat/workspace/useWorkspaceSendActions.ts" "src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx"` 通过。
- 后置 `npm run verify:gui-smoke` 已执行:workspace-ready、browser-runtime、site-adapters、agent-service-skill-entry、agent-runtime-tool-surface、agent-runtime-tool-surface-page 均通过;最后 `smoke:knowledge-gui` 因当前 worktree 里知识库命令 `knowledge_import_source` 未注册到 DevBridge 返回未知命令而失败。该失败属于现有知识库命令边界缺口,不是本轮首页首轮发送 / Browser Assist 降载回归;若要恢复全量 GUI smoke,需要单独按命令边界同步 `knowledge_import_source` 的前端调用、Rust dispatcher / handler、治理目录册与 mock。
@@ -1,242 +0,0 @@
# Provider 模型能力 taxonomy 进度
## 2026-04-22
### 目标
- 把本地模式、用户自管云端 Provider、OEM 云端目录的模型能力语义收成一套轻量 taxonomy
- 明确区分 `对话 / 视觉理解 / 图片生成 / 音频 / Embedding`,避免继续把 `llm / vlm / multimodal` 混成一类
- 支持 OpenAI relay / OEM 映射模型,例如 `gpt-images-2`
- 保持设置页与 Provider 目录页轻量,不走复杂“模型广场”路线
### 已完成
- Rust 模型注册表与请求解析链已补齐统一字段:
- `task_families`
- `input_modalities`
- `output_modalities`
- `runtime_features`
- `deployment_source`
- `management_plane`
- `canonical_model_id`
- `provider_model_id`
- `alias_source`
- 前端能力推断已统一到 `inferModelCapabilities.ts`:
- `gpt-images-2` 被识别为 `image_generation`
- `vision_understanding` 与 `image_generation` 明确分离
- 旧 `capabilities` 只作为 compat fallback
- Provider 模型目录 UI 已改成轻量能力筛选:
- 保留按能力筛选,不引入复杂模型广场
- 已区分 `本地 / 云端 / OEM 云端`
- 只有 `relay / oem` 别名才展示“实际映射”
- 图片生成选择链已切到统一 taxonomy:
- 图片 Provider 只会保留真正支持 `image_generation` 的模型
- 避免把视觉理解模型混进生图候选
- 媒体服务设置页已收口到统一“服务模型”骨架:
- 设置导航与首页快捷入口已从“媒体服务”统一改名为“服务模型”
- 服务模型页已对齐参考产品的总页结构,不再只保留图片 / 视频 / 语音三块
- 新增通用助理模型设置:
- 话题自动命名
- AI 图片话题命名
- 消息内容翻译
- 会话历史压缩
- 助理信息生成
- 输入自动补全
- 提示词重写
- 资源库提词重写
- 新增统一设置区块:
- 语音识别服务
- 自动结束语音识别
- OpenAI 语音合成模型
- OpenAI 语音识别模型
- AI 图片默认数量
- 图片 / 视频 / 语音统一为 `默认 Provider + 默认模型 + 自动回退`
- 图片默认数量已改为更接近参考页的 `滑杆 + 可直接输入数字` 简化交互
- 旧图片出图参数面与旧语音调参面已从 current 设置主路径删除
- 旧实验室 `VoiceSettings` 入口已清退,不再保留双轨 UI
- OEM 云端模型目录已收口到统一 schema:
- `oemCloudControlPlane.ts` 允许服务端直接下发 taxonomy 字段
- `oemCloudModelMetadata.ts` 负责把 OEM 模型目录归一为 metadata-like 结构
- OEM 设置页优先消费 taxonomy,`abilities` 只做兜底
- OEM fallback 能力别名已统一:
- `llm -> chat`
- `vlm / multimodal / omni -> vision_understanding`
- `image_generation / text_to_image -> image_generation`
- 已补回归并通过:
- OEM 模型协议解析
- OEM taxonomy helper
- Provider 模型目录别名展示
- OEM 设置页 taxonomy 优先级
- 图片生成筛选链
- 媒体服务设置页定向回归:
- `src/lib/imageGeneration.test.ts`
- `src/lib/serviceModels.test.ts`
- `src/lib/api/appConfig.test.ts`
- `src/components/settings-v2/agent/media-services/index.test.tsx`
- `src/components/settings-v2/agent/image-gen/index.test.tsx`
- `src/components/settings-v2/agent/video-gen/index.test.tsx`
- `src/components/settings-v2/agent/voice/index.test.tsx`
- `src/components/settings-v2/system/experimental/index.test.tsx`
- 服务模型总页新增断言覆盖:
- 输入自动补全开关持久化
- 自动结束语音识别持久化
- 资源库自定义提示词持久化
- 默认图片数量持久化
- Rust 配置 roundtrip:
- `cargo test -p lime-core test_workspace_preferences_supports_service_models_roundtrip`
- GUI 冒烟:
- `npm run verify:gui-smoke`
- `npm run governance:legacy-report`
- 已额外确认:
- `isImageProvider("lime-hub", "openai", ["gpt-images-2"]) === true`
- 当前未完成的环境校验:
- `npm run verify:local` 当前被仓库中其他改动阻塞:
- `src/lib/sceneapp/product.ts:2070`
- `src/lib/sceneapp/product.ts:2101`
- 错误为 `SceneAppScorecardViewModel.aggregate` 缺失
- 属于当前脏工作区里的 sceneapp 主线类型问题,不是本次服务模型页改动引入
### 当前边界
- `current`
- 本地 / 云端 / OEM 模型目录共享同一套轻量 taxonomy 语义
- OEM 云端目录支持直接下发 taxonomy,也支持旧 `abilities` 兼容
- UI 只做轻量筛选和标签展示,不做复杂模型广场
- 媒体服务设置只保留服务模型级默认项,不再继续扩展旧细项调参面
- `compat`
- OEM `abilities: string[]`
- OEM `upstreamMapping`
- 旧 `capabilities` 布尔字段
- `dead`
- `src/components/voice/VoiceSettings.tsx`
- 实验室里的旧语音输入设置面
- `not in scope`
- 把 OEM 云端目录完全同步进本地自定义模型持久化
- 引入独立模型 marketplace / 多维排序 / 高复杂过滤器
### 下一刀
1. 推动 OEM 服务端稳定下发 taxonomy 字段,逐步减少前端对 `abilities` 的兼容推断依赖。
2. 评估是否需要把 OEM 模型目录同步到 `useOemLimeHubProviderSync.ts` 的本地缓存面;若做,优先保持单一事实源,不新增第二套目录结构。
3. 若后续把 OEM 云端服务模型接入媒体服务设置,优先复用当前服务模型骨架,只扩展选项数据源与来源标签,不新增第二套设置页。
4. 若后续出现视频、文档理解、实时语音等新能力,再在现有 taxonomy 上小步扩展,不回退到“模糊模型类型”。
## 2026-04-23
### 继续收口
- 桌宠能力偏好页已统一回共享模型选择链:
- `MediaPreferenceSection -> SettingModelSelectorField -> ModelSelector`
- 不再继续保留旧 `providerLabel/providerValue/modelLabel/modelValue` 参数面
- 桌宠通用模型区块已显式走 `general` 主题筛选,避免把明显非对话模型混进 quick action 候选
- 桌宠通用 / TTS 区块都已按当前 consumer 能力做 Provider 过滤:
- 只展示当前桌宠 quick action / TTS 能真正消费的 Provider
- 继续兼容后续本地、自管云与 OEM taxonomy 扩展
- `@配音` 的定向回归已补:
- 显式 mock 全局 `media_defaults.voice`
- 断言 `service_scene_run` 中携带
- `preferred_provider_id`
- `preferred_model_id`
- `allow_fallback`
- 当 `preferred_provider_id + preferred_model_id` 同时存在时,当前发送链会把它们转成真实 `providerOverride / modelOverride`
- 当只声明 `preferred_provider_id`、未显式选模型时,不会误把当前会话模型硬塞给新 Provider
- 桌宠设置相关回归已补:
- `CompanionCapabilityPreferencesCard.test.tsx`
- `providers/index.test.tsx`
- 两处都断言当前页复用了统一 `ModelSelector` 组件,而不是另一套独立选择器
### 本轮校验事实
- 已通过:
- `npx vitest run "src/components/agent/chat/workspace/useWorkspaceSendActions.test.tsx"`
- 结果:`90` 个测试通过
- 已通过:
- `npx vitest run "src/components/settings-v2/agent/providers/CompanionCapabilityPreferencesCard.test.tsx" "src/components/settings-v2/agent/providers/index.test.tsx"`
- 结果:`15` 个测试通过
- 已通过:
- `cargo test -p lime-core test_workspace_preferences_supports_companion_defaults_roundtrip`
- `cargo test -p lime-core test_workspace_preferences_supports_service_models_roundtrip`
- 未完成:
- `cargo test -p lime ...` 在编译 `lime` 主 crate 链接阶段被环境阻塞
- 具体错误:`No space left on device`
- 当前磁盘余量:`1.4Gi`
### 2026-04-23 继续收口(第二刀)
- Rust `request_model_resolution` 已开始真正消费 `service_scene_launch` 里的首选服务偏好:
- 当前若回合 metadata 同时声明 `preferred_provider_id + preferred_model_id`
- 且前端本轮没有显式 `provider_preference / model_preference`
- 后端会直接基于该 scene launch 偏好解析 `provider_config`
- `service_scene_launch.allow_fallback` 已接入后端解析策略:
- `false`:首选 provider/model 不可解析时直接报错,不再静默回退会话默认
- `true`:首选 provider/model 失效时,允许继续回退到会话当前 provider/model
- 当前只声明 `preferred_provider_id`、未声明 `preferred_model_id` 时:
- 后端不会把它误当成完整首选模型偏好
- 仍沿用现有显式请求 / 会话恢复链,避免跨 Provider 误绑模型
- “服务模型”页已进一步简化:
- `输入自动补全助理` 当前已收口为只展示启停开关
- 不再暴露尚未接入 current 执行面的模型选择 UI
- 这样继续保持“能用的配置必须真生效;没接上的不假装可配”
### 2026-04-23 继续收口(第三刀)
- 桌宠设置页已删除未接入 current 执行面的 `桌宠语音播报` 偏好区块:
- 当前 Rust `companion_service` 只真实消费 `companion_defaults.general`
- `companion_defaults.tts` 之前只有设置页写入,没有 quick action / voice chat / 播报执行链 consumer
- 因此 current UI 只保留 `桌宠通用模型`,继续复用共享 `ModelSelector`
- 桌宠偏好保存逻辑同步收口:
- 保存 `桌宠通用模型` 时不再继续保留旧的 `companion_defaults.tts` 假设置
- 让桌宠页和实际执行面重新回到单一事实源
### 2026-04-23 继续收口(第四刀)
- `VoiceSettings` 已从“只配语音生成任务模型”补齐为当前真实语音主链设置页:
- 语音输入启停
- 主快捷键
- 翻译模式快捷键
- 麦克风设备
- 交互音效
- AI 润色开关
- 润色 / 翻译共用模型选择
- 默认润色指令
- 翻译模式指令
- 指令管理
- 配音 / TTS 任务默认模型
- 新语音设置页继续复用统一模型选择链:
- `SettingModelSelectorField -> ModelSelector`
- 不再继续使用旧 `PolishModelSelector` 作为另一套模型入口
- 新增语音设置都已接到 current consumer:
- `processor.polish_provider / polish_model` 已真实透传到本地网关请求头 `X-Provider-Id`
- `translate_instruction_id` 与指令删除后的回退逻辑已收口,避免保存失效指令引用
- `selected_device_id / sound_enabled / enabled / shortcut / translate_shortcut` 继续直连当前语音输入链
- dead `Config.voice` 已彻底删除:
- 前端 `appConfigTypes` 不再暴露旧 `voice` 面
- Rust `Config` 不再保留旧 `voice` 字段与导出
- 相关旧测试 mock 与假配置项已同步清理
### 本轮新增校验
- 已通过:
- `npx vitest run "src/components/settings-v2/agent/media-services/index.test.tsx"`
- 结果:`5` 个测试通过
- 已通过:
- `npx vitest run "src/components/settings-v2/agent/voice/index.test.tsx" "src/components/settings-v2/agent/media-services/index.test.tsx" "src/lib/api/appConfig.test.ts"`
- 结果:`18` 个测试通过
- 已通过:
- `cargo test --manifest-path "src-tauri/Cargo.toml" service_scene_model_preference`
- 结果:新增 `2` 个 Rust 定向测试通过
- 已通过:
- `cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-core test_voice_input_config_default`
- `cargo test --manifest-path "src-tauri/Cargo.toml" -p lime-services --lib`
- 结果:`lime-core` 定向测试通过,`lime-services` `174` 个测试通过
- 已通过:
- `npx vitest run "src/components/settings-v2/agent/providers/CompanionCapabilityPreferencesCard.test.tsx" "src/components/settings-v2/agent/providers/index.test.tsx"`
- 结果:`15` 个测试通过
- 当前环境事实:
- 磁盘余量已恢复到约 `76GiB`
- 默认 `target` 目录偶发被并行 `cargo run` 占锁;不是本轮代码错误
### 下一刀
1. 先清出足够磁盘空间,再补 `lime` 主 crate 的 Rust 定向测试,确认 `runtime_turn / service_scene_launch / prompt_context` 全链编译通过。
2. 若桌宠语音播报进入 current 执行链,优先复用这次已经统一好的 `companion_defaults.tts`,不要再新增第二套语音偏好入口。
-24
View File
@@ -1,24 +0,0 @@
# 技术债追踪
本文件记录需要持续、小额偿还的技术债,避免把问题堆到一次性大清理。
## 记录规则
1. 每条技术债都要写清具体代码面、影响和下一小步
2. 能回挂路线图主线的,优先回挂路线图;不能回挂的,登记到这里
3. 状态至少区分 `待处理`、`进行中`、`已完成`、`放弃`
## 条目
| ID | 日期 | 区域 | 差距 / 债务 | 影响 | 下一小步 | 关联文档 | 状态 |
| ------- | ---------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------ |
| CCD-001 | 2026-04-14 | Query Loop | Lime 已有 `submit_turn / runtime_turn / tool_runtime / evidence` 主链,但过去缺少单一 Query Loop 事实源;现已补齐 current 文档入口 | 已从“主链解释分散”降到“代码边界仍待继续收口”,后续推进顺序已稳定 | 继续沿 `docs/aiprompts/query-loop.md` 盘点代码侧重复边界,准备下一刀 helper 收口 | `docs/exec-plans/upstream-runtime-alignment-plan.md` | 已完成 |
| CCD-002 | 2026-04-14 | Remote Runtime | Telegram remote、OpenClaw、DevBridge、browser remote/debugging 等入口过去并存且语义混杂;现已补齐 `消息渠道 runtime + 浏览器连接器 / ChromeBridge` 的 current 主链,并明确 `DevBridge / OpenClaw` 为 compat、`telegram_remote_cmd` 为 deprecated | 已从“remote 入口平级并存”降到“只剩 compat / deprecated 退场减法”,后续 remote 主线解释已稳定 | 后续只允许 remote 新能力落到 `gateway_channel_*` 或 `browser connector / ChromeBridge` current ingress;`DevBridge / OpenClaw / telegram_remote_cmd` 默认只做减法和兼容维护 | `docs/exec-plans/upstream-runtime-alignment-plan.md` | 已完成 |
| CCD-003 | 2026-04-14 | Task / Agent | `subagent / automation / execution tracker / scheduler / heartbeat` 过去缺少统一 taxonomy;现已补齐 `agent turn / subagent turn / automation job` current 分型,并明确 `ExecutionTracker` 为摘要层、`SchedulerService` 为 compat 触发壳 | 已从“排期语言混乱”降到“只剩 compat / deprecated 退场减法”,后续主线解释已稳定 | 后续只允许新增长时执行需求落到 `agent turn / subagent turn / automation job` 三类之一;`SchedulerService` 与 `heartbeat_executions` 只继续做减法和退场 | `docs/exec-plans/upstream-runtime-alignment-plan.md` | 已完成 |
| CCD-004 | 2026-04-14 | Memory / Compaction | `memory_runtime_*`、`unified_memory_*`、`agent_runtime_compact_session` 与来源链 control plane 过去缺少统一主链叙事;现已补齐 current 文档入口,并明确 `project_memory_get` 为 compat 附属层、`memory_feedback_cmd` 为 deprecated | 已从“记忆、压缩、恢复分头演进”降到“后续只需守住 current 边界不回流” | 后续只允许新记忆 / 压缩需求落到 `docs/aiprompts/memory-compaction.md` 定义的 current 主链;发现回流时做定点治理 | `docs/exec-plans/upstream-runtime-alignment-plan.md` | 已完成 |
| CCD-005 | 2026-04-14 | State / History / Telemetry | reliability、harness、history、review、replay、evidence 过去分别成文且入口分散;现已补齐 `SessionDetail -> AgentRuntimeThreadReadModel -> RequestLog 关联键 -> export/history` 的 current 状态地图,并明确旧状态模型方案、reliability 计划与原始 request log 浏览面退回附属层 | 已从“状态模型叙事分散”降到“只剩 compat / deprecated 退场减法”,后续主线解释已稳定 | 后续只允许新状态 / 历史 / 遥测能力落到 `docs/aiprompts/state-history-telemetry.md` 定义的 current 主链;优先继续清 `reliability` 计划与 cleanup 旧语义 | `docs/exec-plans/upstream-runtime-alignment-plan.md` | 已完成 |
| CCD-006 | 2026-04-20 | Config / Host Surface | 参考运行时架构图复盘后确认:Lime 的 `ConfigTool` 宿主配置矩阵仍未补齐,但 `classifierPermissionsEnabled` 已接入 `current` 主链并与安全扫描 ML toggle 打通;进一步审计后又确认 `remoteControlAtStartup` 不能映射到现有 `get_auto_launch_status / set_auto_launch`,`taskCompleteNotifEnabled / inputNeededNotifEnabled / agentPushNotifEnabled` 也不能映射到更新提醒窗口或已下线的 `channels_cmd.rs` stub。`permissions.defaultMode` 这轮也已进一步收紧:prompt 层虽有 `PermissionMode::Plan / DontAsk` 文本能力,但 `plan` 仍只是显式 `EnterPlanMode / ExitPlanMode` 工具流,`dontAsk` 也还缺 provider flag 与 `PermissionInspector` 的全局 runtime 语义 | 若停在当前状态,`Config` 虽然不再把 classifier 开关卡死在 `unsupported`,但 remote-control / push-notification / permission-mode 仍无法通过 `current` 配置面真实控制;更糟的是,后续实现者容易把 OS auto-launch、更新提醒、plan prompt 文本或 stub notification CRUD 误判成可复用事实源,继续制造假对齐 | 已完成“真实宿主落点”与 `permissions.defaultMode` 执行语义审计;下一小步进一步收敛为二选一:要么新增真正的 remote-control / mobile push / dontAsk runtime 宿主面并把 `plan` 重构为可持久化权限模式,要么把这组差异明确沉淀为产品层不对齐;并继续明确 `channels_cmd.rs` 已非可复用宿主面,不再参与这组 setting 的事实源判断 | `docs/exec-plans/upstream-runtime-alignment-progress.md` | 进行中 |
| CCD-007 | 2026-04-20 | Team / Peer Messaging | `SendMessage / ListPeers` 已从“只支持 team 内 peers”收口到“team peers + synthetic `uds:<session-id>` 本机会话 peers”的 live-aware `current`:`ListPeers` 会优先返回同一 `working_dir` 下 active / queued 的本机顶层 session,不足时才回退最近 session;对照参考仓库 `peerAddress.ts` 与 `SendMessageTool` 后,本地 peer surface 也已重新收口为“仅通过 `send_to=uds:<session-id>` 暴露 peer address”,`SendMessage` 只把显式 `uds:<session-id>` 识别为 cross-session local peer 路由,不再把 bare local `session_id` 当成 peer address fallback。Lime 仍使用 synthetic local registry,而不是真实 UDS/socket peer transport;`bridge:` 也继续明确留在 gap | 当前 misleading surface 已清掉:Lime 不再是“识别 `uds:` 但永远失败”的半成品,也不再把 bare session-id 误扩成参考运行时不存在的 peer address。剩余差距已被显式压回 `session registry / local ingress / remote bridge ingress` 产品 gap,不再继续污染 current 边界 | 后续若产品明确需要真实 local ingress 或 remote peer messaging,应另开新专题,围绕显式 session registry / transport host 设计推进;在那之前,`CCD-007` 只维护当前 `uds:<session-id>` synthetic local peer current,不再扩展裸地址或假 bridge surface | `docs/exec-plans/upstream-runtime-alignment-progress.md` | 已完成 |
| CCD-008 | 2026-04-20 | Extension / Hook / Skill | `SkillTool`、project/plugin/session hooks、plugin skills/hooks/agents、plugin cache resolver 与 child worktree lifecycle 已陆续进入 `current` 主链;近几轮又继续补上了 `PermissionRequest.updatedPermissions` 的 session `setMode` 最小 current 子集、managed-session peer messaging、lead-side `shutdown_response` current subset、peer message display / cards / lifecycle-noise 收口,以及 lead-side current team context 下 `name + mode=plan` 命中 teammate plan lifecycle 的最小 honest reachability | 若停在当前状态,Lime 已不再是“plugin 只能带技能、不能真正带 runtime hooks”“压缩前完全绕过 hooks”“权限审批仍完全绕过 hooks”“turn 正常结束完全绕过 Stop hooks”“删除 session 完全绕过 SessionEnd hooks”“lead/team 已有 plan host 却仍被入口挡在外面”“peer 消息进了 runtime 却仍把 XML / lifecycle noise 原样暴露给主聊天”的假对齐;但扩展体系仍停留在“`SessionStart(clear)` 与 `SessionEnd.resume / clear / prompt_input_exit / logout / bypass_permissions_disabled` 仍无真实 current 宿主、`Stop` 仍缺 continuation gate、`PermissionRequest.updatedPermissions` 的 rules / directories / non-session destination 仍无 honest host、`isolation=remote` 与非 team child `plan` lifecycle 仍无 honest host、且 `hook.if / asyncRewake / 非 bash shell / allowedEnvVars` 仍显式未支持”的阶段 | 已完成 lead-side current team context 下 teammate `plan` reachability 收口;下一小步优先回到 `isolation=remote`,或只在未来真的补齐非 team child lifecycle / permission host 后再继续扩 `plan` 与 `updatedPermissions` 边界 | `docs/exec-plans/upstream-runtime-alignment-progress.md` | 进行中 |
| CCD-009 | 2026-04-20 | Remote Governance / Legacy Surface | 参考运行时对比后再次确认:`channels_cmd.rs` 这组 AI / 通知渠道 CRUD 基本仍是空壳,但仓库 current remote 事实源已经固定到 `gateway_channel_* + browser connector / ChromeBridge`;本轮已确认 `ImConfigPage` 走的是 `channelsRuntime.ts -> gateway_channel_*` current API,不依赖 `channels_cmd.rs` 旧 CRUD 命令,并已完成宿主面下线:移除 `runner.rs` 注册、移除 `commands/mod.rs` 模块声明、删除 `channels_cmd.rs` 文件本体,同时补上 `legacySurfaceCatalog` 的命令组与 Rust 文本守卫 | 若不下线,Rust 侧注册与 stub 文件会继续制造“仓库还有另一套可用渠道管理面”的误导,影响 remote 主链收口;现已通过删除与守卫把这类误导性 surface 从宿主编译图中清出 | 已完成删除后的最小验证:`legacySurfaceCatalog.test.ts`、`governance:legacy-report`、`test:contracts`、`cargo check --manifest-path src-tauri/Cargo.toml -p lime --lib` 均通过;下一步回到 `CCD-007`,继续判断 `SendMessage / ListPeers` 的 `uds:` / `bridge:` cross-session peer transport 是否应进入 `current` | `docs/aiprompts/remote-runtime.md` | 已完成 |
| CCD-010 | 2026-04-20 | Resources / Library | 资料库页过去同时存在“大 Hero + 页面级添加入口 + 侧栏新建入口 + 空态动作卡 + store selector / page 各写一套搜索排序分类规则”的旧心智;当前已把资料库 current 主链收窄为“项目切换 / 分类切换 / 搜索排序 / 浏览打开 / 重命名删除 / 图片视图上传插入”,并新增 `resourceQueries` 作为目录视图、分类视图与目录派生状态共用的查询事实源;同时已删掉 `selectors` 中间层,把内容区顶部收成“控制栏 + 当前范围状态条”一套壳,并把跨项目媒体提示并入当前范围状态条、移除内容列表头里的重复最近更新徽标;本轮继续沿主链做减法,把顶部剩余的文件夹 / 内容项 / 最近更新三张统计卡收薄为贴近项目切换的一条只读摘要,并把这组项目级派生进一步收回 `resourceQueries`,避免页面层再保留平行统计逻辑 | 若停在旧状态,后续实现者会继续在页面和 selector 两侧分别补过滤/分类规则,导致资料库行为漂移,也容易把已删除的页面级创建入口或重复状态壳重新接回 current 主链;顶部总览若继续保留独立统计卡,后续改动也容易在 Hero、状态条和分页脚三处重新长出不同口径的数量文案 | 下一小步继续判断这条项目只读摘要是否还应下沉到左侧“资料库切换”卡,彻底把项目级信息和范围级信息拆开;若新增范围派生文案或查询规则,只允许落到 `src/components/resources/services/resourceQueries.ts` 与当前范围状态条,不再把范围说明或结果统计回灌到 Hero | `src/components/resources/ResourcesPage.tsx` / `src/components/resources/services/resourceQueries.ts` | 进行中 |
@@ -1,205 +0,0 @@
# 参考运行时主链对齐计划
> 状态:进行中
> 更新时间:2026-04-14
> 对照基线:本地参考运行时源码镜像(2026-04-14 快照)
> 目标:把 Lime 与参考运行时的“运行时主链”对齐工作,收口成一份唯一的排期与差距事实源,避免路线图、治理减法和专题计划继续并行漂移。
## 1. 先给结论
Lime 当前不是“能力不够”,而是“主链不够单一”。
已确认的现状:
- `Tool Runtime / MCP / evidence / replay / review / subagent` 已具备较强底座
- `Query Loop`、`Task / Agent taxonomy`、`Remote runtime` 与 `Memory / Compaction` 已完成 current 主链收口
- 现有对齐文档散落在 `roadmap/`、`tech/`、`develop/`、`exec-plans/`,导致排期和治理容易串线
本计划固定一个判断:
**后续“对齐上游运行时”默认指向运行时主链对齐,而不是目录、UI 或命名的 1:1 复制。**
## 2. 对齐口径
### 2.1 对齐什么
默认对齐下面六条运行时主链:
1. `Query Loop`
2. `Tool Runtime`
3. `Memory / Compaction`
4. `Remote / SDK / Server Mode`
5. `Task / Agent / Coordinator`
6. `State / History / Telemetry`
### 2.2 不对齐什么
- 不以上游运行时的目录结构作为目标
- 不先追求 UI 行为逐像素一致
- 不为了对齐而回退 Lime 已有的 GUI、Artifact、Provider、Workspace 优势
- 不把已有专题路线图全部重写成新体系;已有文档保留为下游专项
## 2.3 当前进度
- `M0` 统一排期事实源:`done`
- `M1` Query Loop 收口:`done`
- `M2` Task / Agent taxonomy 收口:`done`
- `M3` Remote runtime 收口:`done`
- `M4` Memory / Compaction 收口:`done`
- `M5` State / History / Telemetry 收口:`done`
- 当前阶段:`六条运行时主链 current 入口已补齐,后续转入守 current 边界与 compat / deprecated 退场`
- 当前进度日志:`docs/exec-plans/upstream-runtime-alignment-progress.md`
## 3. 主链差距矩阵
| 主链 | 参考运行时基线模块 | Lime 当前事实源 | 当前判断 | 主要差距 | 下一刀 |
| --- | --- | --- | --- | --- | --- |
| `Query Loop` | `QueryEngine.ts`、`query.ts`、`query/deps.ts`、`services/tools/*`、`services/compact/*` | `src-tauri/src/commands/aster_agent_cmd/runtime_turn.rs`、`runtime_api.rs`、`tool_runtime.rs`、`docs/aiprompts/query-loop.md` | `aligned` | `Query Loop` current 事实源与主循环入口已收口;旧的 `alignment/state-model/conversation-efficiency` 专项文档都已退回 compat 历史档案,Artifact 文档族也已改回 current 入口引用;`TurnInputEnvelope -> SessionConfig` 的 turn context snapshot 分叉、`action_runtime` 辅助恢复链旁路,以及 `compact_session` 控制回合的最小上下文边界都已显式化;剩余散落在 `persona_cmd` / `theme_context_cmd` 的一次性临时会话配置已收口为专用 helper,零入口的旧 `AsterAgentWrapper::send_message` 已删除;Tauri 命令层原始 `agent.reply` / `stream_reply_with_policy` 扫描结果也已固定为 `action_runtime(current) + persona/theme_context(compat)` 三处,并补了源码扫描守卫 | 维持 aligned 主链;下一刀转向继续盘点 `src-tauri/src` 非命令层与 README/示例面是否还残留会误导实现者的原始执行旁路叙事 |
| `Tool Runtime` | `src/tools/*`、`services/tools/*`、`services/mcp/*`、`ToolSearchTool` | `src-tauri/src/agent_tools/catalog.rs`、`inventory.rs`、`src-tauri/src/commands/aster_agent_cmd/tool_runtime/*`、`docs/aiprompts/commands.md`、`docs/aiprompts/command-runtime.md`、`docs/prd/tools/architecture.md` | `aligned` | 能力基本齐,但仍需持续防止命名、inventory、mock、MCP 注入回退到第二事实源 | 维持 current 主链,后续只做减法和守卫,不新增并行入口 |
| `Memory / Compaction` | `services/compact/*`、`services/SessionMemory/*`、`memdir/*` | `docs/aiprompts/memory-compaction.md`、`src/lib/api/memoryRuntime.ts`、`src-tauri/src/commands/memory_management_cmd.rs`、`src-tauri/src/services/memory_source_resolver_service.rs`、`src-tauri/src/services/auto_memory_service.rs`、`src-tauri/src/commands/aster_agent_cmd/runtime_turn.rs`、`src-tauri/src/commands/aster_agent_cmd/command_api/runtime_api.rs`、`src/lib/api/unifiedMemory.ts`、`src-tauri/src/commands/unified_memory_cmd.rs` | `aligned` | `M4` 已建立统一主链:`来源链解析 -> 单回合 prefetch -> runtime_turn prompt augmentation -> session compaction -> working/durable memory 沉淀 -> GUI 稳定读模型`;`project_memory_get` 退回 compat 附属层,`memory_feedback_cmd` 退回 deprecated | 转入 `M5`,固定 `session / thread / turn / request / evidence / history` 的 current 状态地图 |
| `Remote / SDK / Server Mode` | `remote/*`、`server/*`、`entrypoints/sdk/*`、`cli/transports/*` | `docs/aiprompts/remote-runtime.md`、`src/lib/api/channelsRuntime.ts`、`src-tauri/src/commands/gateway_channel_cmd.rs`、`src/lib/webview-api.ts`、`src-tauri/src/commands/browser_connector_cmd.rs`、`src-tauri/src/commands/webview_cmd.rs`、`src-tauri/src/commands/browser_runtime_cmd.rs`、`src-tauri/src/dev_bridge/*`、`src-tauri/src/services/openclaw_service/*`、`src-tauri/src/commands/telegram_remote_cmd.rs` | `aligned` | `M3` 已建立统一 remote 主链:`消息渠道 runtime + 浏览器连接器 / ChromeBridge` 是 current ingress,`DevBridge / OpenClaw` 退回 compat,`telegram_remote_cmd` 退回 deprecated;后续残余减法转入 compat surface 继续处理 | 维持 aligned 主链,后续只做 compat / deprecated 退场减法 |
| `Task / Agent / Coordinator` | `tasks/*`、`tools/AgentTool`、`Task*Tool`、`EnterWorktreeTool`、`RemoteTriggerTool`、`coordinator/*` | `docs/aiprompts/task-agent-taxonomy.md`、`docs/aiprompts/query-loop.md`、`src-tauri/src/commands/aster_agent_cmd/subagent_runtime.rs`、`src-tauri/src/services/execution_tracker_service.rs`、`src-tauri/src/services/automation_service/*`、`src-tauri/src/app/scheduler_service.rs` | `aligned` | `M2` 已建立统一 taxonomy:`agent turn / subagent turn / automation job` 是 current 一等执行实体,`ExecutionTracker` 是统一执行摘要层,`SchedulerService` 退回 compat 触发壳;后续残余减法转入 execution tracker / scheduler 专项继续处理 | 维持 aligned 主链,后续只做定点减法与回归修复 |
| `State / History / Telemetry` | `state/*`、`bootstrap/*`、`assistant/sessionHistory.ts`、`cli/transports/ccrClient.ts`、`services/analytics/*` | `docs/aiprompts/state-history-telemetry.md`、`src-tauri/crates/agent/src/session_store.rs`、`src-tauri/src/commands/aster_agent_cmd/dto.rs`、`src-tauri/src/commands/aster_agent_cmd/command_api/runtime_api.rs`、`src-tauri/src/services/thread_reliability_projection_service.rs`、`src-tauri/src/services/runtime_handoff_artifact_service.rs`、`src-tauri/src/services/runtime_evidence_pack_service.rs`、`src-tauri/src/services/runtime_replay_case_service.rs`、`src-tauri/src/services/runtime_review_decision_service.rs`、`scripts/harness-eval-history-record.mjs` | `aligned` | `M5` 已建立统一状态地图,并继续完成 compat/deprecated 收口:`reliability / state-model / alignment / conversation-efficiency` 这些重型 compat 文档都已压成历史摘要档案,`telemetry_cmd.rs` 只再保留原始日志面定位 | 维持 current 主链;文档侧重型 compat 路线图已基本收完,下一刀优先回到 current 代码边界继续做减法 |
## 4. 现有文档归位
本计划是排期总入口,不替代现有专题方案。现有文档按下列方式归位:
| 文档群 | 归属主链 | 角色 |
| --- | --- | --- |
| `docs/roadmap/lime-aster-codex-alignment-roadmap.md` | `Query Loop`、`State / History / Telemetry` | Aster/Codex umbrella 历史专项档案,已吸收原状态模型与执行效率子专题,不再承担仓库级总排期或 current 实施入口职责 |
| `docs/roadmap/reliability/*` | `State / History / Telemetry` | Reliability control plane 专项 |
| `docs/tech/harness/*`、`docs/roadmap/harness-engine/*` | `State / History / Telemetry`、`Memory / Compaction` | Evidence / replay / review / observability / cleanup 专项 |
| `docs/develop/execution-tracker-technical-plan.md`、`docs/develop/execution-tracker-p1-p2-roadmap.md`、`docs/develop/scheduler-task-governance-p1.md` | `Task / Agent / Coordinator` | 长时执行与治理专项 |
| `docs/aiprompts/commands.md`、`docs/aiprompts/command-runtime.md`、`docs/prd/tools/*` | `Tool Runtime` | 当前工具面与协议事实源 |
如果后续新增专项文档,必须先标明它属于哪条主链;如果说不清主链归属,就不允许单独立项。
## 5. 排期纪律
从本计划起,后续排期默认遵守以下规则:
1. 同一时间只允许 `1` 条主链作为主任务。
2. 每轮最多再搭配 `1` 条服务主链的治理任务。
3. 所有任务必须声明:
- 所属主链
- 当前分类:`current / compat / deprecated / dead`
- 阶段出口
4. 治理减法如果不能直接缩短主链,就登记到 `docs/exec-plans/tech-debt-tracker.md`,不直接插队。
5. 专题路线图完成后,必须回写本计划的矩阵状态,避免“专题 done,但总盘子仍 unknown”。
## 6. 推荐推进顺序
### `M0` 统一排期事实源
- 建立本计划,固定六条主链
- 把现有对齐文档回挂到单一总入口
- 把当前未收口差距登记进技术债追踪
### `M1` Query Loop 收口
- 为 Lime 补一份单一 Query Loop 事实图
- 明确 `submit_turn -> turn runtime -> tool orchestration -> compaction -> evidence` 的 current 主链
- 退出条件:不再需要横跳多份文档才能解释 Lime 主循环
当前进度(2026-04-14):
- 已完成第一刀:`docs/aiprompts/query-loop.md` 已成为 Query Loop current 入口
- 已完成第二刀实现:`runtime_turn.rs` 已将 `run_start_metadata`、`runtime_status_session_config`、`build_session_config` 的重复拼装收口为稳定 helper,主循环可读性继续提升
- 已完成第三刀实现:单次流式执行成功后的 Artifact 自动落盘与记忆沉淀已收口为统一 helper,主成功分支与降级成功分支不再各自维护重复收尾逻辑
- 已完成第四刀实现:两处 `stream_reply_once` 的运行时事件记录闭包已收口为统一 helper,主分支与降级分支共享同一条事件记录路径
- 已完成第五刀实现:`RunFinishDecision` 组装与 terminal result 收尾已收口为统一 helper,主循环不再内联 success/error 两套终态处理
- 已完成第六刀实现:主执行分支与降级到 ReAct 的分支已共用“单次流式尝试” helper,`build_runtime_user_message(...)`、`build_session_config()`、`stream_reply_once(...)` 与成功收尾逻辑不再在主循环中重复展开
- 已完成第七刀实现:`CodeOrchestrated` 的扩展启用、失败降级、扩展清理已收口为统一策略 helper,`with_run_custom(...)` 内只保留一次 Query Loop 执行入口
- 已完成第八刀实现:runtime turn 初始化、status 投射与 service preload 事件已收口为统一前奏 helper,主循环的前置准备阶段已压成单一语义块
- 已完成第九刀实现:`run_start_metadata`、`timeline_recorder`、`runtime_status_session_config` 与流式 `session_config` 构建状态已收口为统一 execution context,主循环不再散落拼装这组前置状态
- 已完成第十刀实现:`run_observation`、`run_finish decision` 与 terminal finalize 链路已回收到 execution context,主循环不再手工拼接 tracked execution 与终态收尾
- 已完成第十一刀实现:`skill_tool_session_access` 与 `cancel_token` 已收口为统一 session scope,异常路径与正常路径共享同一套会话级清理边界
- 已完成第十二刀实现:`runtime_snapshot -> runtime_projection_snapshot -> turn_state -> turn_input_envelope` 已收口为统一 `build_runtime_turn_artifacts(...)` helper,主循环不再内联读取 snapshot、派生 thread/turn 和构建 turn 输入诊断
- 已完成第十三刀实现:`service_skill_preload -> prepared execution -> prelude/execute handoff` 已收口为 `prepare_runtime_turn_execution(...)` 与 `RuntimeTurnPreparedExecution`,submit 主路径已压成更明确的 `prepare -> execute` 语义块
- 已完成第十四刀实现:`provider_continuation -> workspace sandbox apply -> tracker/session scope bootstrap` 已收口为 `prepare_runtime_turn_submit_bootstrap(...)`,submit 主路径不再内联铺开这组前置副作用和运行期参数拼装
- 已完成第十五刀实现:`request.provider_config -> configure_provider/configure_provider_from_pool -> persist_session_provider_routing` 已收口为 `apply_runtime_turn_provider_config(...)`,submit 主路径不再内联铺开 provider apply 分支
- 已完成第十六刀实现:`resolved_prompt -> prompt augmentation -> requested/effective strategy persist` 已收口为 `prepare_runtime_turn_prompt_strategy(...)`,submit 主路径不再内联铺开 prompt/strategy 组装与持久化
- 已完成第十七刀实现:`tool surface metadata -> MCP warmup -> skill launch metadata normalize -> turn_input_builder seed` 已收口为 `prepare_runtime_turn_request(...)`,submit 主路径不再内联铺开 request prepare 分支
- 已完成第十八刀实现:`runtime_chat_mode -> web_search/request_tool_policy -> execution_profile` 已收口为 `prepare_runtime_turn_policy(...)`,submit 主路径不再内联铺开 policy resolve 逻辑
- 已完成第十九刀实现:`auto_continue -> workspace repair warning -> session_state_snapshot/working_dir update -> session_recent_runtime_context` 已收口为 `prepare_runtime_turn_session(...)`,submit 主路径的 session 级前置准备已压成单一 helper
- 已完成第二十刀实现:`session/policy/request/prompt_strategy -> provider apply/bootstrap` 已收口为 `prepare_runtime_turn_submit_preparation(...)`,submit 主路径已提升为单一 preflight 准备块
- 已完成第二十一刀实现:`sync_browser_assist_runtime_hint -> prepare_runtime_turn_execution -> agent guard -> emit_prelude_and_execute` 已收口为 `execute_runtime_turn_submit(...)`,submit 主路径当前已压成 `prepare -> scoped execute` 两段主骨架
- 已完成第二十二刀实现:`provider_config resolve -> harness metadata normalize -> workspace resolve/turn id/runtime_config` 已收口为 `prepare_runtime_turn_ingress_context(...)`,入口上下文边界不再散落在主路径中
- 已完成第二十三刀实现:`agent init/session_store check -> support tools register` 已收口为 `prepare_runtime_turn_entry(...)`,主路径的运行时入口准备已形成独立阶段
- 已完成第二十四刀实现:`model_skill_tool_access derive -> with_runtime_turn_session_scope -> execute_runtime_turn_submit` 已收口为 `execute_runtime_turn_with_session_scope(...)`,主路径不再拆包又重组 `submit_preparation`
- 已完成第二十五刀实现:`execute_aster_chat_request(...)` 已把 `entry -> ingress -> submit_preparation -> session_scope_execute` 提升为单一 `execute_runtime_turn_pipeline(...)` 调用,`M1` 外层 orchestration 收口完成
- 已补最小 Rust 定向校验:`env CARGO_TARGET_DIR="/Users/coso/Documents/dev/ai/aiclientproxy/lime/.codex-target-runtime-turn-2" cargo test --manifest-path "src-tauri/Cargo.toml" -p lime --lib commands::aster_agent_cmd::runtime_turn::tests:: -- --nocapture` 通过(`37 passed`,仅剩 `workspace_tools.rs` 既有 dead_code warning)
- 已补文档新鲜度校验:`npm run harness:doc-freshness` 通过(`clean`)
- 已补格式化校验:`cargo fmt --manifest-path "src-tauri/Cargo.toml" --all` 通过
- `M1` 退出判断:已满足“不再需要横跳多份文档才能解释 Lime 主循环”的出口条件,后续不再继续细切 `M1`
### `M2` Task / Agent taxonomy 收口
- 为 Lime 定义统一的 task / agent / coordinator 分型
- 将 `execution tracker / scheduler / subagent / automation` 收回同一语义层
- 退出条件:所有长时执行入口都能归到唯一 taxonomy
当前进度(2026-04-14):
- 已完成第一刀:`docs/aiprompts/task-agent-taxonomy.md` 已成为 Task / Agent / Coordinator current 入口
- 已完成第二刀:`ExecutionTracker / subagent runtime / automation service / scheduler trigger` 的 `current / compat / deprecated / dead` 已有明确归类
- 已完成第三刀:`docs/README.md`、`docs/aiprompts/README.md`、`docs/aiprompts/overview.md`、`AGENTS.md` 已同步回挂新入口,仓库导航不再继续把 scheduler/heartbeat 叙事误当 current 主线
- `M2` 退出判断:已满足“所有长时执行入口都能归到唯一 taxonomy”的出口条件,后续不再把 execution tracker、scheduler、subagent、automation 分散解释为多条平级主线
### `M3` Remote runtime 收口
- 盘点 remote/dev bridge/browser remote/IM remote 入口
- 声明唯一 current remote runtime
- 退出条件:remote 不再是多个并列产品旁路
当前进度(2026-04-14):
- 已完成第一刀:`docs/aiprompts/remote-runtime.md` 已成为 Remote runtime current 入口
- 已完成第二刀:`gateway_channel_* + browser connector / ChromeBridge` 已明确为 current remote ingress,`DevBridge / OpenClaw` 已归到 compat,`telegram_remote_cmd` 已归到 deprecated
- 已完成第三刀:`docs/README.md`、`docs/aiprompts/README.md`、`docs/aiprompts/overview.md`、`AGENTS.md` 已同步回挂新入口,仓库导航不再继续把 debug 桥或单通道 Telegram 入口误当 remote 主线
- `M3` 退出判断:已满足“remote 不再是多个并列产品旁路”的出口条件,后续只在 current ingress 上新增能力,其余 remote 面默认只做减法与兼容维护
### `M4` Memory / Compaction 收口
- 固定压缩边界、记忆预取、恢复和用户可见状态的主链
- 退出条件:`memory_runtime_*` 与 `compact_session` 不再被当成分散能力点看待
当前进度(2026-04-14):
- 已完成第一刀:`docs/aiprompts/memory-compaction.md` 已成为 Memory / Compaction current 入口
- 已完成第二刀:`来源链解析 -> 单回合 prefetch -> runtime_turn prompt augmentation -> session compaction -> working/durable memory 沉淀 -> GUI 稳定读模型` 已明确为 current 主链
- 已完成第三刀:`project_memory_get` 与角色/世界观/大纲资料链已明确退回 compat 附属层,`memory_feedback_cmd` 已退回 deprecated,`memory_search_cmd.rs.bak` 已标记为 dead
- 已完成第四刀:`docs/README.md`、`docs/aiprompts/README.md`、`docs/aiprompts/overview.md`、`AGENTS.md` 已同步回挂新入口,仓库导航不再继续把项目资料聚合或旧 feedback 侧链误当成记忆 / 压缩主线
- `M4` 退出判断:已满足“`memory_runtime_*` 与 `compact_session` 不再被当成分散能力点看待”的出口条件,后续只允许在 current 边界继续长记忆 / 压缩能力
### `M5` State / History / Telemetry 收口
- 把 reliability、harness、history、review、replay 的事实源统一成一张状态地图
- 退出条件:session / thread / turn / request / evidence / history 的读模型叙事收口
当前进度(2026-04-14):
- 已完成第一刀:`docs/aiprompts/state-history-telemetry.md` 已成为 State / History / Telemetry current 入口
- 已完成第二刀:`agent_sessions / agent_messages -> SessionDetail -> AgentRuntimeThreadReadModel -> RequestLog 关联键 -> handoff/evidence/replay/analysis/review -> history-record/trend/cleanup/dashboard -> HarnessStatusPanel / AgentThreadReliabilityPanel` 已明确为 current 主链
- 已完成第三刀:原 `state-model` 历史子专题、`docs/roadmap/reliability/*` 与 `telemetry_cmd.rs` 已明确退回 compat;cleanup 报表里残留的 `requestTelemetry:unlinked` 旧语义已明确为 deprecated
- 已完成第四刀:`docs/README.md`、`docs/aiprompts/README.md`、`docs/aiprompts/overview.md`、`AGENTS.md` 已同步回挂新入口,仓库导航不再继续把状态模型专题计划、reliability 计划或原始 request log 控制台误当成 current 主链
- 已完成第五刀:`docs/roadmap/reliability/README.md` 已补成 compat 目录入口;cleanup 核心脚本已把旧 `requestTelemetry:unlinked` 样本折叠为 `known_gap`,避免旧历史语义继续充当现役状态类别
- 已完成第六刀:`docs/roadmap/reliability/*.md` 全部补上 compat 提示,正文开头先回挂 `state-history-telemetry.md`;`telemetry_cmd.rs` 也已明确只暴露原始 `RequestLog` 与聚合统计,不再和 thread read / evidence 主链抢解释权
- 已完成第七刀:`docs/roadmap/reliability/*.md` 顶部重复的上位文档列表已压成统一 `README + current 主链 + PR 对应映射` 导航,专项正文不再继续堆叠第二套入口说明
- 已完成第八刀:整组 `docs/roadmap/reliability/*` 已进一步压缩为 compat 历史摘要档案,只保留落地结果、current 映射与延后增强项;重复的目标/问题/范围/实施清单正文已回退到仓库历史
- 已完成第九刀:原 `state-model` 历史摘要已完成压缩并最终并入 `docs/roadmap/lime-aster-codex-alignment-roadmap.md`,current 入口固定回到 `query-loop / state-history-telemetry / upstream-runtime-alignment-plan`
- 已完成第十刀:`docs/roadmap/lime-aster-codex-alignment-roadmap.md` 已固定为 compat umbrella 历史档案,只保留阶段映射、状态模型与执行效率的核心判断
- 已完成第十一刀:原 `conversation-execution-efficiency` 历史摘要已并入 `alignment-roadmap`,`docs/roadmap/artifacts/*` 对运行时边界的引用也已统一改回 `query-loop / task-agent-taxonomy / state-history-telemetry / upstream-runtime-alignment-plan`
- `M5` 退出判断:已满足“session / thread / turn / request / evidence / history 的读模型叙事收口”的出口条件,后续只允许在 current 边界上继续长能力
## 7. 当前默认判断
- `Tool Runtime` 继续视为 `current`
- `Query Loop`、`Task / Agent / Coordinator`、`Remote / SDK / Server Mode`、`Memory / Compaction`、`State / History / Telemetry` 视为 `aligned`
当前最值得继续推进的一刀固定为:
**文档侧重型 compat 路线图已基本压成历史摘要档案;当前已收口 `TurnInputEnvelope -> SessionConfig` 的 turn context snapshot 分叉、`action_runtime` 辅助恢复链的 turn context 旁路,并显式化 `compact_session` 控制回合的最小上下文边界;剩余散落在 `persona_cmd` / `theme_context_cmd` 的一次性临时会话配置也已收口为专用 helper,零入口旧发送壳已删除;Tauri 命令层原始执行面也已固定为 3 处并补了源码扫描守卫。下一刀转向继续盘点 `src-tauri/src` 非命令层与 README/示例面是否还残留会误导实现者的原始执行旁路叙事。**
这样做的原因是:六条运行时主链的 current 入口已经补齐;cleanup 的旧 `unlinked` 语义、telemetry 原始浏览面定位,以及 `reliability / state-model / alignment / conversation-efficiency` 这些重型 compat 文档都已经被收紧。继续留在文档治理上的边际收益已经明显下降,下一步更值得回到 current 代码边界继续做减法。
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
---
name: personal-ip-knowledge-builder
description: 将访谈稿、聊天记录、简历、公开内容、业务资料、案例和既有 DOCX/Markdown 文档,提炼成可被 AI 长期调用的个人 IP 知识库。适用于用户要求“生成个人知识库”“整理成个人 IP 成品知识库”“为创始人/专家/讲师/主播/顾问建立AI知识库”“把资料变成个人IP底层提示词/写作风格库/故事素材库/话术库”的场景。
license: Apache-2.0
compatibility:
agentKnowledge: ">=0.6.0"
metadata:
Lime_skill_bundle_version: "1.0.0"
Lime_knowledge_builder: "true"
Lime_knowledge_pack_type: "personal-profile"
Lime_knowledge_template: "personal-ip"
Lime_knowledge_family: "persona"
Lime_agent_knowledge_profile: "document-first"
Lime_agent_knowledge_runtime_mode: "persona"
---
# 个人 IP 知识库生成器
## 核心目标
把零散资料编译成一份结构化 Markdown 知识库,让后续 AI 能稳定调用这个人的事实、故事、观点、风格和边界,而不是每次临时总结。
默认输出中文;除非用户明确要求其他语言。
## 与 Agent Knowledge 的分工
本 Skill 只负责“怎么生产和维护知识”:
- 读取来源资料、模板、访谈问题和质量检查表。
- 生成或更新个人 IP 成品文档。
- 标记缺失事实、冲突事实和待用户确认的信息。
- 返回整理记录、质量诊断和 provenance 建议。
Agent Knowledge 负责“知识产物长什么样、如何安全进入上下文”:
- `KNOWLEDGE.md` 保存 pack metadata、`profile: document-first`、`runtime.mode: persona`。
- `documents/<pack-name>.md` 保存本 Skill 生成的主文档。
- `runs/compile-*.json` 记录本次 Builder Skill 输入、输出、版本和诊断。
- 运行时 Resolver 只消费 KnowledgePack,不在回答用户问题时执行本 Skill。
## 工作流
1. 先盘点输入资料:访谈、简历、聊天记录、文章、案例、产品服务、历史文案、DOCX/Markdown。
2. 如果输入是 DOCX,优先使用 `scripts/docx_to_markdown.py` 转成 Markdown 草稿。
3. 读取 `references/personal-ip-template.md`,按固定章节生成知识库。
4. 读取 `references/interview-questions.md`,识别缺失的高价值信息。
5. 缺少关键事实时,先问用户补齐;如果用户要求先生成,则用 `待补充` 标注,不要编造。
6. 提炼事实、故事、案例、方法论、价值观、表达风格、金句、可引用素材和禁忌边界。
7. 生成完整 Markdown,结尾必须包含“智能体应用指南”。
8. 用 `references/quality-checklist.md` 自检,必要时补一节“待补充信息清单”。
## Lime Runtime Binding 契约
当 Lime 通过 `knowledge_compile_pack` 调用本 Skill 时,输入输出必须保持下面的最小契约。
### 输入
```text
packName: <当前知识包名>
packType: personal-profile
profile: document-first
runtime.mode: persona
sources[]: sources/ 下的来源文件摘要和相对路径
metadata.primaryDocument: documents/<packName>.md
```
### 输出
```text
primaryDocument:
path: documents/<packName>.md
content: <按 references/personal-ip-template.md 生成的完整 Markdown>
status: draft | needs-review | ready | disputed
missingFacts[]: <待补充信息>
warnings[]: <质量或冲突提醒>
provenance:
kind: agent-skill
name: personal-ip-knowledge-builder
version: 1.0.0
```
固定规则:
1. 不输出独立于 KnowledgePack 的新目录结构;`documents/<packName>.md` 是主文档唯一写回目标。
2. 不直接改写 `KNOWLEDGE.md`;由 Lime 写入 `metadata.producedBy`、`runtime.mode` 和状态。
3. 不把模板复制进 Lime 代码;模板、访谈问题和质量检查表继续留在本 Skill 的 `references/`。
4. 不在运行时回答阶段执行;仅在用户导入、重新整理或维护 pack 时调用。
## 输出规则
- 不写成简历,也不写成宣传软文;要写成 AI 可调用的底层知识库。
- 区分事实、观点、推断和待补充信息。
- 保留真实语气,不要把人物包装成虚假的“成功学大师”。
- 尽量使用具体案例、数据、原话、场景和转折点。
- 每个章节都要有清晰标题,适合长期维护。
- 结尾必须包含:使用说明、AI 写作风格指南、核心价值观关键词、可引用故事素材、禁忌与边界。
## 推荐产物结构
在 Agent Knowledge v0.6.0 / Lime 中,优先写回:
```text
<pack-name>/
KNOWLEDGE.md # 由 Lime 维护 metadata
documents/<pack-name>.md # 本 Skill 生成的主文档
runs/compile-*.json # 由 Lime 记录本次整理 provenance
```
如果用户在普通对话中只要求一份独立文档,也可以输出单一 Markdown 作为临时交付;进入 Lime KnowledgePack 时必须回到上述 `document-first` 结构。
不再默认拆成:
```text
[person-id]-personal-ip/
knowledge.md
facts.md
voice.md
stories.md
boundaries.md
```
## 何时读取资源
- 需要章节骨架时,读取 `references/personal-ip-template.md`。
- 资料不足或要做访谈表时,读取 `references/interview-questions.md`。
- 输出前做质量检查时,读取 `references/quality-checklist.md`。
- 用户只要空白模板时,可复制 `assets/personal-ip-knowledge-skeleton.md`。
## DOCX 转 Markdown
如果需要先转换 DOCX:
```bash
python3 scripts/docx_to_markdown.py 输入.docx 输出.md
```
转换后再进行知识提炼。脚本只负责格式转换,不负责事实提炼。
@@ -0,0 +1,4 @@
interface:
display_name: "个人IP知识库生成器"
short_description: "把访谈、资料和案例提炼成可被AI长期调用的个人IP知识库。"
default_prompt: "请基于我提供的资料,生成一份结构化、可长期调用的个人IP知识库。"
@@ -0,0 +1,37 @@
# [姓名] 个人 IP 知识库
## 第一章 人物档案与基本信息
## 第二章 个人简介与核心定位
## 第三章 成长背景与关键经历
## 第四章 职业经历与业务发展
## 第五章 代表案例与实战成绩
## 第六章 核心方法论与行业洞察
## 第七章 价值观与人生哲学
## 第八章 性格特质与表达风格
## 第九章 金句语录与思想精华
## 第十章 核心能力与竞争优势
## 第十一章 未来愿景与发展规划
# 附录一 多场景介绍话术
# 附录二 智能体应用指南
## 一、知识库使用说明
## 二、AI 写作风格指南
## 三、核心价值观关键词
## 四、可引用的故事素材
## 五、禁忌与边界
@@ -0,0 +1,59 @@
# 个人 IP 知识库访谈问题
当资料不足时,优先问最能改变知识库质量的问题。不要一次问太多;每轮 5-8 个问题即可。
## 基础信息
1. 你希望别人怎么介绍你?
2. 你现在最核心的身份是什么?
3. 你服务什么人?帮他们解决什么问题?
4. 你最希望被记住的标签是什么?
5. 有没有必须避免的称呼或标签?
## 成长经历
1. 你来自哪里?成长环境对你有什么影响?
2. 你小时候或学生时代有什么明显特质?
3. 有没有一次经历改变了你的性格或人生选择?
4. 你经历过什么低谷?后来怎么走出来?
5. 哪个故事最能代表你这个人?
## 职业与业务
1. 你什么时候进入现在这个行业?
2. 第一份重要工作或项目是什么?
3. 你现在的产品 / 服务是什么?
4. 客户为什么选择你,而不是别人?
5. 你最拿手的能力是什么?
## 案例与数据
1. 你做过哪些代表项目?
2. 有没有具体结果或数据?
3. 哪个案例最能证明你的能力?
4. 这个案例中你做了哪些关键动作?
5. 哪些客户、数据或细节不能公开?
## 方法论与观点
1. 你对行业最大的判断是什么?
2. 你反对行业里哪些常见说法?
3. 你最常跟客户讲的一句话是什么?
4. 你做事最重要的方法论是什么?
5. 你认为成功最关键的因素是什么?
## 表达风格
1. 你说话更直接、温和、犀利、幽默还是陪伴型?
2. 你常用哪些词或句子?
3. 你不喜欢哪些表达?
4. 你希望 AI 写得像本人,还是更正式?
5. 有无过去写过的内容可作为风格样本?
## 禁忌边界
1. 哪些经历不希望被写?
2. 哪些成果不能夸大?
3. 哪些行业词不能乱用?
4. 哪些客户 / 数据不能公开?
5. 哪些表达会让你觉得“不像我”?
@@ -0,0 +1,148 @@
# 个人 IP 知识库标准模板
用于把某个人的访谈、经历、内容和业务资料整理成 AI 可长期调用的 Markdown 知识库。
## 标准章节
# [姓名] 个人 IP 知识库
## 第一章 人物档案与基本信息
### 1.1 基础档案
- 姓名 / 常用称呼
- 地域 / 当前城市
- 当前身份
- 主要行业
- 主要服务对象
- 当前业务 / 产品 / 服务
### 1.2 核心标签体系
用 5-10 个标签概括这个人,例如:
- 12年自媒体营销实战家
- 视频号增长顾问
- 大健康品牌操盘手
- 企业家私域转化教练
### 1.3 职业履历概览
用表格梳理阶段、时间、经历、代表成果。
## 第二章 个人简介与核心定位
### 2.1 个人简介
写成可用于官网、商务介绍、媒体资料的完整简介。
### 2.2 一句话定位
用一句话说明:这个人是谁,服务谁,解决什么问题,凭什么可信。
### 2.3 核心业务闭环
说明其业务如何从内容、流量、信任、转化到交付形成闭环。
## 第三章 成长背景与关键经历
### 3.1 家庭与成长环境
只写与价值观、性格、能力形成相关的内容。
### 3.2 求学与早期能力
提炼写作、表达、组织、销售、审美、技术等早期能力来源。
### 3.3 人生转折点
记录关键事件、低谷、高光、选择和它们带来的长期影响。
## 第四章 职业经历与业务发展
### 4.1 初入职场
说明第一阶段如何积累基本能力。
### 4.2 能力积累
提炼可迁移能力,而不是流水账。
### 4.3 创业 / 转型经历
说明为什么转型、怎么判断机会、踩过什么坑。
### 4.4 当前业务状态
说明当前主要服务、产品、渠道、客户和交付方式。
## 第五章 代表案例与实战成绩
### 5.1 代表项目
每个案例写清:背景、动作、结果、可复用经验。
### 5.2 关键数据
只写可验证或用户明确提供的数据;不确定则标注 `待补充`。
### 5.3 成功路径
提炼案例背后的方法论。
## 第六章 核心方法论与行业洞察
### 6.1 对行业的判断
写出这个人反复强调的行业认知。
### 6.2 对内容 / 产品 / 销售 / 交付的理解
根据资料选择相关维度,不要机械补齐。
### 6.3 与同行不同的观点
优先提炼有辨识度、有争议但有依据的观点。
## 第七章 价值观与人生哲学
- 工作观
- 金钱观
- 成长观
- 合作观
- 自由观
- 客户观
## 第八章 性格特质与表达风格
### 8.1 性格关键词
例如:直接、务实、犀利、温和、陪伴型、结果导向。
### 8.2 说话方式
说明句式、节奏、口头禅、常用比喻。
### 8.3 写作风格
说明适合的文体和不适合的文体。
## 第九章 金句语录与思想精华
按主题整理:事业、行业、客户、内容、人生、合作。
## 第十章 核心能力与竞争优势
- 专业能力
- 资源优势
- 经验壁垒
- 审美 / 表达 / 销售 / 组织能力
- 和同行相比的差异化
## 第十一章 未来愿景与发展规划
说明当前目标、未来方向、希望建立的影响力。
# 附录一 多场景介绍话术
至少包含:
- 30 秒电梯介绍
- 1 分钟正式介绍
- 社群介绍
- 商务场合介绍
- 短视频账号简介
- 直播间开场介绍
# 附录二 智能体应用指南
## 一、知识库使用说明
说明这份知识库适合被 AI 用在哪些任务中。
## 二、AI 写作风格指南
说明 AI 模拟此人写作时应遵守什么风格。
## 三、核心价值观关键词
用 `关键词 | 关键词 | 关键词` 格式输出。
## 四、可引用的故事素材
用列表整理可反复调用的故事。
## 五、禁忌与边界
说明不能写什么、不能夸大什么、哪些内容必须先确认。
@@ -0,0 +1,50 @@
# 个人 IP 知识库质量检查表
生成完成后逐项检查。
## 事实完整性
- 是否有清晰的人物基础档案?
- 是否有一句话定位?
- 是否区分了事实、观点、推断和待补充?
- 是否标注不确定信息为 `待补充`?
- 是否避免编造客户、数据、履历和成果?
## 结构完整性
- 是否包含人物经历、业务能力、代表案例、方法论、价值观、表达风格?
- 是否包含多场景介绍话术?
- 是否包含智能体应用指南?
- 是否包含禁忌与边界?
- 是否适合后续持续维护?
## 可调用性
- AI 是否能用它写文案、话术、介绍、方案和改稿?
- 是否有可引用故事素材?
- 是否有金句和常用表达?
- 是否说明了“不应该怎么写”?
- 是否能让不同模型保持同一大方向?
## 风格真实性
- 是否保留了本人真实语气?
- 是否避免过度神化和成功学包装?
- 是否使用了具体案例,而不是空泛形容?
- 是否符合这个人的行业、圈层和受众?
## 输出可交付性
- Markdown 标题层级是否清晰?
- 是否有目录感和章节感?
- 是否能直接写回 Agent Knowledge `documents/<pack-name>.md`?
- 是否适配 `profile: document-first` 与 `runtime.mode: persona`?
- 是否能让 Lime 在 `metadata.producedBy` 与 `runs/compile-*.json.builder_skill` 中记录本 Skill provenance?
- 如果资料不足,是否给出了“待补充信息清单”?
## Runtime 安全边界
- 是否明确把输出作为知识资料,而不是运行时指令?
- 是否避免要求 Resolver 在回答用户问题时执行本 Skill?
- 是否只引用来源材料中出现的事实,未确认信息都标记为 `待补充`?
- 是否把冲突事实、敏感信息和夸大风险写入 warnings 或待确认清单?
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""将 DOCX 的标题、正文、列表和表格转换为基础 Markdown。"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
from docx import Document
from docx.document import Document as DocumentObject
from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P
from docx.table import Table
from docx.text.paragraph import Paragraph
SEPARATOR_RE = re.compile(r"^[─━—\-_=]{6,}$")
def iter_block_items(parent):
if isinstance(parent, DocumentObject):
parent_elm = parent.element.body
parent_obj = parent
else:
parent_elm = parent._tc
parent_obj = parent
for child in parent_elm.iterchildren():
if isinstance(child, CT_P):
yield Paragraph(child, parent_obj)
elif isinstance(child, CT_Tbl):
yield Table(child, parent_obj)
def clean_text(text: str) -> str:
return re.sub(r"[ \t]+", " ", text.replace("\u00a0", " ")).strip()
def paragraph_to_markdown(paragraph: Paragraph) -> list[str]:
text = clean_text(paragraph.text)
if not text:
return []
style = paragraph.style.name if paragraph.style else ""
if style.startswith("Heading"):
match = re.search(r"(\d+)", style)
level = min(max(int(match.group(1)) if match else 1, 1), 6)
return [f"{'#' * level} {text}"]
if SEPARATOR_RE.match(text):
return ["---"]
if text.startswith(("• ", "· ", "● ", "○ ")):
return [f"- {text[2:].strip()}"]
return [text]
def table_to_markdown(table: Table) -> list[str]:
rows: list[list[str]] = []
for row in table.rows:
cells: list[str] = []
for cell in row.cells:
parts: list[str] = []
for block in iter_block_items(cell):
if isinstance(block, Paragraph):
line = clean_text(block.text)
if line:
parts.append(line)
elif isinstance(block, Table):
nested = table_to_markdown(block)
if nested:
parts.append("<br>".join(nested))
cells.append("<br>".join(parts).replace("|", r"\|"))
if rows and cells == rows[-1]:
continue
rows.append(cells)
if not rows:
return []
col_count = max(len(row) for row in rows)
rows = [row + [""] * (col_count - len(row)) for row in rows]
markdown = [
"| " + " | ".join(rows[0]) + " |",
"| " + " | ".join(["---"] * col_count) + " |",
]
for row in rows[1:]:
markdown.append("| " + " | ".join(row) + " |")
return markdown
def convert_docx(input_path: Path, output_path: Path) -> None:
doc = Document(str(input_path))
lines: list[str] = [
"<!--",
f"源文档:{input_path}",
"转换说明:由 DOCX 正文段落、标题和表格转换为 Markdown。",
"-->",
"",
]
for block in iter_block_items(doc):
block_lines = (
paragraph_to_markdown(block)
if isinstance(block, Paragraph)
else table_to_markdown(block)
)
if not block_lines:
continue
if lines and lines[-1] != "":
lines.append("")
lines.extend(block_lines)
lines.append("")
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
def main() -> None:
parser = argparse.ArgumentParser(description="将 DOCX 转换为基础 Markdown。")
parser.add_argument("input", type=Path, help="输入 DOCX 文件路径")
parser.add_argument("output", type=Path, help="输出 Markdown 文件路径")
args = parser.parse_args()
if not args.input.exists():
raise SystemExit(f"输入文件不存在:{args.input}")
if args.input.suffix.lower() != ".docx":
raise SystemExit("输入文件必须是 .docx")
convert_docx(args.input, args.output)
if __name__ == "__main__":
main()
+10 -10
View File
@@ -7,7 +7,7 @@
## 1. 为什么单独成文档
`/goal` 不应该放进 CreoAI 研究目录里当附属小节。
`/goal` 不应该放进 Skill Forge 研究目录里当附属小节。
原因是它研究的不是 Tool-Maker Agent,也不是电商运营自动化,而是另一个更小、更底层的 runtime pattern:
@@ -22,16 +22,16 @@ persistent thread goal
**一个 agent turn 结束后,系统如何知道还要不要继续推进同一个目标。**
这和 CreoAI 的三层架构有关,但不等同:
这和 Skill Forge 的三层架构有关,但不等同:
| 研究对象 | 主要回答什么 | Lime 对应层 |
| --- | --- | --- |
| CreoAI / Tool-Maker Agent | 能力如何被生成、验证、注册、长期运行 | Skill Forge / skills pipeline 上游 |
| Skill Forge / Tool-Maker Agent | 能力如何被生成、验证、注册、长期运行 | Skill Forge / skills pipeline 上游 |
| Codex `/goal` | 一个 thread goal 如何跨多轮 turn 自动续跑直到完成、暂停或耗尽预算 | Query Loop / automation job 上的目标推进控制环 |
固定结论:
**`/goal` 是独立研究对象;它可以作为 Lime Managed Objective 的参考,但不应被写成 CreoAI 或 Skill Forge 的子章节。**
**`/goal` 是独立研究对象;它可以作为 Lime Managed Objective 的参考,但不应被写成 Skill Forge 或 Skill Forge 的子章节。**
### 1.1 什么是 Thread Goal Loop
@@ -566,12 +566,12 @@ failed
3. Lime 有外部权限、浏览器登录态、workspace artifact。
4. Lime 有 evidence pack,可以支持更强审计。
## 10. 和 CreoAI 研究的关系
## 10. 和 Skill Forge 研究的关系
Codex `/goal` 可以补 CreoAI 三层架构里的第二层视角:
Codex `/goal` 可以补 Skill Forge 三层架构里的第二层视角:
```text
CreoAI 三层:
Skill Forge 三层:
Coding Agent / Agent Builder
-> Autonomous Execution / Runtime
-> Workspace / Agent App Surface
@@ -590,7 +590,7 @@ Autonomous Execution / Runtime 里的 persistent objective + continuation loop
因此两个研究目录应分工:
- `docs/research/creaoai/`:研究“能力如何被 agent 生成并长期运行”。
- `docs/research/skill-forge/`:研究“能力如何被 agent 生成并长期运行”。
- `docs/research/codex-goal/`:研究“目标如何跨 turn 被 runtime 持续推进”。
## 11. 研究结论
@@ -613,8 +613,8 @@ Autonomous Execution / Runtime 里的 persistent objective + continuation loop
3. `docs/roadmap/managed-objective/implementation-plan.md`
4. `docs/roadmap/managed-objective/diagrams.md`
它不直接塞进 `docs/roadmap/creaoai/`,除非只是说明两者关系。
它不直接塞进 `docs/roadmap/skill-forge/`,除非只是说明两者关系。
一句话:
**Codex `/goal` 研究应服务 Lime 的目标推进控制层;CreoAI 研究应服务 Lime 的能力生成与长期业务自动化闭环。两者有关,但必须分开建模。**
**Codex `/goal` 研究应服务 Lime 的目标推进控制层;Skill Forge 研究应服务 Lime 的能力生成与长期业务自动化闭环。两者有关,但必须分开建模。**
+3 -3
View File
@@ -311,7 +311,7 @@ pi-mono 的 extension 可以:
| `CustomEntry` | timeline / artifact / evidence event |
| `registerTool` | 只能在 verified registration 后映射到 Skill / ServiceSkill / tool_runtime |
| `registerCommand` | Lime 不需要复制 terminal command system |
| `registerProvider` | 与 CreoAI P1A 无关,不进入首期 |
| `registerProvider` | 与 Skill Forge P1A 无关,不进入首期 |
固定边界:
@@ -509,9 +509,9 @@ P1A 推荐先把 draft 作为 workspace-local 文件事实源:
2. 若 Lime 现有 artifact store 已有更合适路径,应优先复用现有封装。
3. 不要为了 P1A 新增全局数据库主事实源。
## 13. 和 CreoAI / Managed Objective 的关系
## 13. 和 Skill Forge / Managed Objective 的关系
pi-mono 参考的是 CreoAI 三层里的第一层:
pi-mono 参考的是 Skill Forge 三层里的第一层:
```text
Coding Agent / Agent Builder
@@ -1,19 +1,19 @@
# CREAO 研究总入口
# Skill Forge 研究总入口
> 状态:current research reference
> 更新时间:2026-05-06
> 目标:把 Founder Park 访谈中的 CREAO 案例拆成可持续对照的研究事实源,供 Lime 后续规划校准“Coding Agent 生成工具、Agent 可复用运行、组织 AI Native 反馈闭环”的产品范式。
> 目标:把 Founder Park 访谈中的 Skill Forge 案例拆成可持续对照的研究事实源,供 Lime 后续规划校准“Coding Agent 生成工具、Agent 可复用运行、组织 AI Native 反馈闭环”的产品范式。
## 1. 命名与来源边界
用户转述中出现过 `career AI`、`creaoio`、`creaoai` 等名称差异;本轮访谈明确指向 **CREAO**,官网为 `https://creao.ai/`。目录名继续保留 `creaoai`,文档内统一称为 **CREAO**。
用户要求后续文档不再暴露原外部产品名;本目录统一使用 **Skill Forge** 作为中性代称。目录名使用 `skill-forge`,不在本目录记录外部官网、融资、团队等尽调信息。
本目录只分析 Founder Park 访谈中体现的产品、组织与架构范式。
固定边界:
1. 本目录不把用户数、融资额、团队背景、上线时间、收入指标等访谈口径写成已核验事实。
2. 本目录不评估 CREAO 公司真实性、商业数据或投资信息。
2. 本目录不评估外部公司真实性、商业数据或投资信息。
3. 本目录只沉淀对 Lime 有用的产品、组织与工程启发。
一句话:
@@ -22,7 +22,7 @@
## 2. 目录定位
`docs/research/creaoai/` 只回答两类问题:
`docs/research/skill-forge/` 只回答两类问题:
1. 访谈里的三层架构、工具编码编排、Agent 产品模型和组织 harness 到底是什么。
2. Lime 应该学它的哪一层,不应该照搬哪一层。
@@ -31,9 +31,9 @@
固定分工:
1. `docs/research/creaoai/` 负责外部案例拆解和风险识别。
2. `docs/roadmap/creaoai/` 负责 Lime 自己的开发计划。
3. [../codex-goal/README.md](../codex-goal/README.md) 单独研究 Codex `/goal` 这类 persistent objective / continuation loop,不再塞进 CREAO 研究目录。
1. `docs/research/skill-forge/` 负责外部案例拆解和风险识别。
2. `docs/roadmap/skill-forge/` 负责 Lime 自己的开发计划。
3. [../codex-goal/README.md](../codex-goal/README.md) 单独研究 Codex `/goal` 这类 persistent objective / continuation loop,不再塞进 Skill Forge 研究目录。
4. 代码实现仍必须回到 Lime 现有 current 主链:`skills pipeline / Query Loop / tool_runtime / Workspace / evidence pack`。
## 3. 为什么单独建立这一层
@@ -76,7 +76,7 @@
- 自动执行必须受权限、dry-run、测试、证据、sandbox 和人工确认约束。
6. **组织 harness 是护城河之一**
- CREAO 强调 AI 扫描信号、提出需求、人类 planning、AI 实现、AB/log 反馈的闭环。
- Skill Forge 强调 AI 扫描信号、提出需求、人类 planning、AI 实现、AB/log 反馈的闭环。
7. **对 Lime 不应新增平行标准**
- 动态生成能力必须编译进 Lime 现有 Skill Bundle / ServiceSkill / Adapter Spec / tool_runtime 主链。
@@ -90,9 +90,9 @@
5. [lime-gap-analysis.md](./lime-gap-analysis.md)
6. [../pi-mono-coding-agent/README.md](../pi-mono-coding-agent/README.md)
7. [../codex-goal/README.md](../codex-goal/README.md)
8. [../../roadmap/creaoai/README.md](../../roadmap/creaoai/README.md)
9. [../../roadmap/creaoai/implementation-plan.md](../../roadmap/creaoai/implementation-plan.md)
10. [../../roadmap/creaoai/diagrams.md](../../roadmap/creaoai/diagrams.md)
8. [../../roadmap/skill-forge/README.md](../../roadmap/skill-forge/README.md)
9. [../../roadmap/skill-forge/implementation-plan.md](../../roadmap/skill-forge/implementation-plan.md)
10. [../../roadmap/skill-forge/diagrams.md](../../roadmap/skill-forge/diagrams.md)
## 6. 固定不照搬的东西
@@ -117,7 +117,7 @@ Lime 真正要学的是:
补充参考:
1. [../pi-mono-coding-agent/README.md](../pi-mono-coding-agent/README.md) 不是 CREAO 公司研究,而是本地开源 coding harness 对照。
1. [../pi-mono-coding-agent/README.md](../pi-mono-coding-agent/README.md) 不是 Skill Forge 公司研究,而是本地开源 coding harness 对照。
2. 它用于回答“Lime 缺的 Coding Agent 层工程上怎么切”。
3. 当前结论是:参考 pi-mono 的 `AgentSession` 分层、工具 allowlist、可插拔工具后端、事件与测试 harness;不复制它的终端产品、JSONL session 事实源或全仓库 shell/write 权限。
@@ -126,9 +126,9 @@ Lime 真正要学的是:
后续所有实现建议默认遵守以下顺序:
1. 先读本目录,确认外部范式到底启发了什么。
2. 再读 [../../roadmap/creaoai/README.md](../../roadmap/creaoai/README.md),确认 Lime 决定怎么做。
2. 再读 [../../roadmap/skill-forge/README.md](../../roadmap/skill-forge/README.md),确认 Lime 决定怎么做。
3. 涉及 skill / adapter / runtime binding 时,回看 [../../aiprompts/skill-standard.md](../../aiprompts/skill-standard.md) 与 [../../aiprompts/query-loop.md](../../aiprompts/query-loop.md)。
一句话:
**`research/creaoai` 负责防止误读外部案例,`roadmap/creaoai` 负责把启发收敛成 Lime current 主线;所有实现都必须回到 Skill / Query Loop / tool_runtime / Workspace / evidence,而不是新增 CREAO 仿制旁路。**
**`research/skill-forge` 负责防止误读外部案例,`roadmap/skill-forge` 负责把启发收敛成 Lime current 主线;所有实现都必须回到 Skill / Query Loop / tool_runtime / Workspace / evidence,而不是新增 Skill Forge 仿制旁路。**
@@ -0,0 +1,143 @@
# Skill Forge Agent 产品模型拆解
> 状态:current research reference
> 更新时间:2026-05-06
> 目标:把 Founder Park 访谈中 Agent / Skill / Memory / Widget / Schedule / Workspace 协作的产品模型拆清楚,避免把 Skill Forge 简化为 Skill Forge 或工具注册表。
## 1. 一句话结论
访谈里 Skill Forge 对 Agent 与 Skill 的区分很关键:
**Skill 更像 Agent 的 runbook;Agent 则是可复用工作单元,至少还包含 memory、widget、schedule、权限、运行历史和协作上下文。**
因此 Lime 的路线不能停在:
```text
verified skill -> 可调用工具
```
还要逐步走向:
```text
一次成功任务
-> 主动建议固化
-> Skill / runbook
-> Agent envelope:memory + schedule + widget + permission + evidence
-> 可 rerun / 可共享 / 可审计
```
## 2. 从任务到 Agent 的 aha moment
Skill Forge 访谈里真正的用户转化不是让用户先理解“什么是 Agent”,而是:
1. 用户先完成一个具体任务。
2. 系统发现任务结果稳定、方法可复用。
3. 系统主动建议“把这套方法转成以后可以复用的 Agent”。
4. 用户设置触发条件、输入、调度和权限。
5. 以后只看结果、处理阻塞、做决策。
固定判断:
**Proactive agentization 是产品层能力,不是 runtime 层能力。Runtime 负责能不能跑,产品面负责什么时候建议固化。**
## 3. Agent 与 Skill 的分层
| 层级 | 职责 | Lime 中的收敛方向 |
| --- | --- | --- |
| Skill / Runbook | 描述如何执行一类任务,包含步骤、脚本、示例、测试 | Agent Skill Bundle / Adapter Spec |
| Memory | 保存用户偏好、历史决策、方法论、上下文摘要 | memory compaction / state-history-telemetry |
| Widget | 展示输入、结果、状态、阻塞点、产物 | Workspace / Task Center / ServiceSkill 投影 |
| Schedule | 手动、定时、事件触发、rerun | automation job / Managed Objective |
| Permission | 控制外部读写、文件写入、发布、付款等 | tool_runtime / permission policy |
| Evidence | 证明生成、验证、注册、调用和结果 | artifact / timeline / evidence pack |
固定边界:
**Skill 不是完整 Agent;Agent 也不应成为绕过 Skill / tool_runtime / evidence 的新执行实体。**
## 4. 短高频重复任务优先
访谈里对“长时间任务”的判断有一个容易被误读的点:
1. 大多数商业化任务不是单个任务连续跑很久。
2. 更常见的是短暂、高频、重复、可拆分的知识工作。
3. 用户价值来自“每天 / 每周自动 rerun,并把人从重复整理中解放出来”。
因此本目录后续使用“长期运行”时,默认含义应是:
**可持久化、可调度、可恢复、可 rerun 的重复工作,而不是无限自主长跑。**
对 Lime 的含义:
1. P4 首期应优先支持 verified read-only skill 的手动运行、定时运行和失败阻塞。
2. 不应急着做无限链式自主任务。
3. completion audit 要证明每次 rerun 的产物和成功标准,而不是只证明模型“还在跑”。
## 5. Workspace 内共享,而不是公开 Marketplace 优先
访谈中 Skill Forge 区分了两种共享:
1. **公开 Marketplace / Skill Store**
- 用 agent 换取经济回报。
- 问题是每个 agent 高度依赖具体 workspace、context、sandbox 和账号环境。
2. **团队 Workspace 内共享**
- 同一团队在同一上下文、权限和基础设施中共享 agent、skill、context。
- 更贴近工作场景里的协作需求。
对 Lime 的固定判断:
**首期优先 workspace/team-scoped sharing,不优先公开交易市场。**
这意味着:
1. workspace-local skill 默认只在当前 workspace 可见。
2. team sharing 需要显式权限、来源、版本和 evidence。
3. Marketplace 只可作为后续启发入口,不应成为 P3/P4 主线。
## 6. 云端 sandbox 与本地 GUI 的张力
Skill Forge 访谈倾向云端,理由包括:
1. 每个请求独立 sandbox,环境隔离更稳定。
2. 平台修复一次基础设施问题,所有用户受益。
3. 人与 agent、agent 与 agent 的协作需要共享 context。
4. 云端可以更强控制数据库、文件系统、memory、connector 协议和性能。
Lime 是桌面 GUI 产品,不能直接照搬“完全云端”。应拆成两个问题:
1. **产品面**:Workspace、证据、任务、review 可以继续是 Lime 桌面 GUI 的强项。
2. **执行面**:高隔离、高复现、高风险外部操作,未来可接 remote runtime / sandbox profile。
固定边界:
**Lime 不因 Skill Forge 云端叙事放弃桌面主路径;但 P3E/P4 之后必须把 sandbox profile、session enable、tool_runtime 授权和 evidence 绑定起来。**
## 7. Memory 三层机制启发
访谈中的 memory 机制可抽象为三层:
1. **Thread 内压缩**
- context 接近上限时压缩,把应记住的内容写入文件系统或等价持久层。
2. **跨 Thread 长期记忆**
- 每次请求结束后判断是否有长期价值,抽取、去重并存储。
3. **新 Thread 相关记忆注入**
- 新任务开始时检索最相关的历史 memory,并注入上下文。
对 Lime 的含义:
**generated skill / agent envelope 不能只保存脚本;还应能引用产生它的方法论、用户偏好、历史修正和运行反馈。**
## 8. 对 Lime 的禁止项
1. 不把 `Skill` 直接宣称为完整 `Agent`。
2. 不为 Agent envelope 新增平行 runtime。
3. 不把 public marketplace 放到 workspace-local skill 之前。
4. 不把“长时运行”误写成无限自主执行。
5. 不让 Widget / Schedule / Memory 绕过 Workspace、automation job、Managed Objective 和 evidence。
## 9. 一句话结论
**Skill Forge 给 Lime 的产品启发是:把一次成功任务主动固化为可 rerun、可共享、可审计的 Agent;Skill Forge 只是这条链的上游,不是完整产品终点。**
@@ -1,8 +1,8 @@
# CREAO 三层架构拆解
# Skill Forge 三层架构拆解
> 状态:current research reference
> 更新时间:2026-05-06
> 目标:把 Founder Park 访谈中的 CREAO 三层架构拆成稳定系统层次,并补齐组织 harness、Agent 产品模型、sandbox、memory 与 outcome feedback 的横切面。
> 目标:把 Founder Park 访谈中的 Skill Forge 三层架构拆成稳定系统层次,并补齐组织 harness、Agent 产品模型、sandbox、memory 与 outcome feedback 的横切面。
## 1. 先给结论
@@ -102,7 +102,7 @@ persistent thread goal(同一会话线程上的持久目标状态)
-> budget / pause / resume / complete
```
它能解释“如何把一轮 agent turn 续成多轮目标推进”,但不能代表完整 CREAO 三层架构:
它能解释“如何把一轮 agent turn 续成多轮目标推进”,但不能代表完整 Skill Forge 三层架构:
1. 它不负责生成 Skill / Adapter / Contract / Test。
2. 它不负责 workspace-local skill catalog。
@@ -173,7 +173,7 @@ Skill / Runbook
## 6. 横切面一:组织开发 Harness
CREAO 访谈中,“Harness”不只指用户任务运行环境,也指公司自身的开发反馈系统。可以抽象为:
Skill Forge 访谈中,“Harness”不只指用户任务运行环境,也指公司自身的开发反馈系统。可以抽象为:
```text
行业动态 / GitHub / 竞品 / 用户日志 / 业务指标
@@ -194,7 +194,7 @@ CREAO 访谈中,“Harness”不只指用户任务运行环境,也指公司
## 7. 横切面二:Sandbox / Memory / Outcome Feedback
CREAO 访谈把稳定性放在模型智商之前,关键原因是普通商业化任务多为短暂、高频、重复的知识工作。
Skill Forge 访谈把稳定性放在模型智商之前,关键原因是普通商业化任务多为短暂、高频、重复的知识工作。
运行稳定性至少包括:
@@ -205,14 +205,14 @@ CREAO 访谈把稳定性放在模型智商之前,关键原因是普通商业
对 Lime 的边界:
1. 桌面 GUI 是 current 产品面,不因 CREAO 云端叙事被替换。
1. 桌面 GUI 是 current 产品面,不因 Skill Forge 云端叙事被替换。
2. 高隔离执行可逐步接 remote runtime / sandbox profile。
3. Memory 必须收敛到 Lime 的 compaction、state-history-telemetry 和 workspace context 主链。
4. Outcome telemetry 不应伪装成 evidence;两者相互引用但事实源不同。
## 8. 对 Lime 的映射
| CREAO 层级 / 横切面 | Lime 中应收敛到的主链 | 不应新增的旁路 |
| Skill Forge 层级 / 横切面 | Lime 中应收敛到的主链 | 不应新增的旁路 |
| --- | --- | --- |
| Coding Agent / Agent Builder | Skill Forge、Agent Skill Bundle、Adapter Spec、ServiceSkill 投影 | 平行 generated tool 类型 |
| Autonomous Execution | Query Loop、runtime_queue、tool_runtime、automation job、subagent | 独立 scheduler / workflow runtime |
@@ -222,7 +222,7 @@ CREAO 访谈把稳定性放在模型智商之前,关键原因是普通商业
一句话:
**Lime 不需要复制一个 CREAO,而是把这三层折回现有 skills pipeline 与 Harness Engine。**
**Lime 不需要复制一个 Skill Forge,而是把这三层折回现有 skills pipeline 与 Harness Engine。**
## 9. 关键风险
@@ -1,8 +1,8 @@
# CREAO 对照 Lime 的偏差分析
# Skill Forge 对照 Lime 的偏差分析
> 状态:current research reference
> 更新时间:2026-05-06
> 目标:判断 Lime 当前路线和 CREAO 访谈启发是否冲突,并明确后续应该补哪些产品、运行和组织闭环。
> 目标:判断 Lime 当前路线和 Skill Forge 访谈启发是否冲突,并明确后续应该补哪些产品、运行和组织闭环。
## 1. 总判断
@@ -10,7 +10,7 @@ Lime 当前方向不冲突。
更准确的判断是:
**Lime 已有底座,但 skills pipeline 还偏静态,Agent 产品面还偏薄;CREAO 启发的是把“能力生成、验证、注册、rerun、Agent 固化、反馈改进”补成闭环。**
**Lime 已有底座,但 skills pipeline 还偏静态,Agent 产品面还偏薄;Skill Forge 启发的是把“能力生成、验证、注册、rerun、Agent 固化、反馈改进”补成闭环。**
也就是说,问题不是 Lime 缺 tool,也不是缺 skill 标准,而是缺少:
@@ -25,7 +25,7 @@ Coding Agent 自动生成 capability
补充宗旨:
**CREAO 启发不等于无限放权。Lime 后续应坚持“权限永远显式受控,能力逐级开放”;限制的是未经验证、未经授权、不可审计的执行,不是限制 agent 的理解、设计和编码能力。**
**Skill Forge 启发不等于无限放权。Lime 后续应坚持“权限永远显式受控,能力逐级开放”;限制的是未经验证、未经授权、不可审计的执行,不是限制 agent 的理解、设计和编码能力。**
## 2. Lime 已经接近的部分
@@ -66,7 +66,7 @@ Lime 当前已经具备以下相关底座:
- 访谈中 Skill 只是 runbook;Lime 还需要把 verified skill 与 memory、widget、schedule、permission、evidence 组合成可 rerun Agent,而不是只展示“已注册技能”。
7. **Proactive agentization 不足**
- CREAO 的 aha moment 是成功任务后主动建议“继续这套方法 / 转成 Agent”。Lime 目前更偏手工注册和手工进入下一 gate,缺少从成功 turn 到 reusable agent 的产品转化面。
- Skill Forge 的 aha moment 是成功任务后主动建议“继续这套方法 / 转成 Agent”。Lime 目前更偏手工注册和手工进入下一 gate,缺少从成功 turn 到 reusable agent 的产品转化面。
8. **Sandbox / Memory / Outcome feedback 缺口**
- Lime 已有 evidence 与 runtime 主链,但还需要明确 sandbox profile、三层 memory 回流,以及 evidence 与 telemetry / experiment 的边界:evidence 证明做了什么,outcome feedback 证明有没有用。
@@ -118,24 +118,24 @@ Lime 当前已经具备以下相关底座:
1. `GeneratedTool` 作为与 Skill / ServiceSkill / Adapter 平级的长期主类型。
2. agent 生成代码后绕过 tool_runtime 直接执行。
3. 外部 API / CLI 原始 schema 直接成为 Lime 运行时协议。
4. 为“更像 CREAO”而复制电商垂类产品结构。
4. 为“更像 Skill Forge”而复制电商垂类产品结构。
5. 为自动化能力新增第二套 evidence pack。
6. 为了复刻 CREAO 而新增平行 AI PM、AB testing、memory 或 marketplace 系统。
6. 为了复刻 Skill Forge 而新增平行 AI PM、AB testing、memory 或 marketplace 系统。
7. 把公开 Marketplace 放在 workspace/team-scoped sharing 之前作为 P3/P4 主线。
## 5. 对 Lime 开发计划的直接要求
后续 `docs/roadmap/creaoai/` 必须做到:
后续 `docs/roadmap/skill-forge/` 必须做到:
1. 把 Skill Forge 写成 skills pipeline 的上游阶段,而不是新 runtime。
2. 把 generated capability 的结果固定为 Skill Bundle / Adapter Spec / ServiceSkill 投影。
3. 把 verification gate 写成注册前硬门槛。
4. 把 tool_runtime 与 evidence pack 写成唯一执行和事实源。
5. 把 workspace-local visibility 纳入首批产品验收。
6. 把 Codex `/goal` 参考单独留在 `docs/research/codex-goal/`;CREAO roadmap 只引用它来解释长期目标推进,不把它写成 Skill Forge 的一部分。
6. 把 Codex `/goal` 参考单独留在 `docs/research/codex-goal/`;Skill Forge roadmap 只引用它来解释长期目标推进,不把它写成 Skill Forge 的一部分。
7. 把 Agent envelope 写成 Skill Forge 之后的产品层:成功任务可建议固化为 Agent,但执行仍走 automation job / Managed Objective。
8. 把 sandbox profile、memory 回流、outcome telemetry 写为 P3E/P4 之后的扩展约束,不抢当前 `tool_runtime` 授权裁剪主线。
## 6. 一句话结论
**CREAO 启发不推翻 Lime 的 skills pipeline;它要求 Lime 把 skills pipeline 从“安装和调用技能”升级为“生成、编译、验证、注册、rerun,并把成功任务固化为可审计 Agent”。**
**Skill Forge 启发不推翻 Lime 的 skills pipeline;它要求 Lime 把 skills pipeline 从“安装和调用技能”升级为“生成、编译、验证、注册、rerun,并把成功任务固化为可审计 Agent”。**
@@ -0,0 +1,125 @@
# Skill Forge Pivot 与组织 Harness 拆解
> 状态:current research reference
> 更新时间:2026-05-06
> 目标:把 Founder Park 访谈里“产品 pivot 之前先组织 pivot”的部分拆成 Lime 可复用的组织与反馈闭环事实源,避免只把 Skill Forge 理解成 Tool-Maker Agent。
## 1. 一句话结论
Skill Forge 访谈里最值得补进 Lime 研究的,不只是 Coding Agent 会写工具,而是:
**当开发实现成本被 AI 压低后,公司的瓶颈从“工程排期”转移到“需求发现、方向判断、验证反馈和组织改造”。**
因此 Skill Forge 的 harness 至少有两层:
1. **产品运行 harness**:让 agent 在 sandbox、memory、schedule、permission、evidence 里稳定执行用户任务。
2. **组织开发 harness**:让 AI 扫描信号、提出需求、人类架构师判断、AI 实现、AB/log 反馈,再反哺下一轮改进。
现有 Lime 研究已经覆盖第一层的一部分;本文件补第二层。
## 2. 访谈口径中的 pivot 时间线
以下为访谈自述口径,未在本目录内做外部尽调核验:
1. **2025 年 1 月底**:公司成立,方向仍在探索。
2. **2025 年 6 月**:大部分团队到位,当时重点仍接近 Vibe Coding / 给人构建传统工具。
3. **2025 年 9 月**:第一版 Vibe Coding 平台上线。
4. **2026 年 1 月**:把产品方向转成 Super Agent。
5. **2026 年 2 月**:完成开发团队 AI-first 改造,一个月内重构新产品基础版本。
6. **2026 年 3 月 31 日**:Super Agent 版本正式上线。
固定判断:
**这不是“先有完美产品,再调整组织”;而是“组织生产方式先改造,产品形态才有足够速度 pivot”。**
## 3. Vibe Coding 失败信号
访谈中 Skill Forge 对上一版产品的反思可以抽象成四点:
1. **自己人用不起来**
- 如果团队自己搭出来的应用都难以长期使用,说明产品价值不在“更快造传统 App”。
2. **构建成本大于使用收益**
- 花在搭建、调优、修 bug 上的时间,超过了最终使用这个应用的时间。
3. **传统 UI 是给人工作的,不是给 AI 工作的**
- 如果未来执行者是 AI,AI 不需要传统 SaaS 的完整按钮、表单、页面和交互流。
4. **原型难以对抗专业 SaaS**
- 快速生成的 CRM / SaaS 原型很难比长期打磨的垂直 SaaS 更可靠。
对 Lime 的含义:
**Workspace UI 不应被设计成传统 app builder;它更应该是任务、阻塞、证据、产物、权限、rerun 和 agent 管理面。**
## 4. AI Native 开发闭环
访谈中的组织 harness 可以抽象为:
```text
外部信号 / 内部日志 / 用户反馈 / 竞品动态
-> AI 生成候选需求
-> 人类架构师做方向与主线判断
-> AI 拆任务、实现、测试、部署
-> AB testing / telemetry 验证效果
-> 日志和结果回流为下一轮 context
```
这个闭环和传统流程的差异:
1. **需求发现更自动**
- AI 可以持续看行业动态、GitHub、竞品、日志和用户行为,形成候选任务。
2. **人类判断更集中**
- 人不是逐个写实现,而是判断哪些任务符合产品主线、商业目标和风险边界。
3. **实现成本更低**
- 需求确认后,AI 快速实现并进入验证,而不是长期排期。
4. **上线不是终点**
- AB test、日志、使用指标和复盘结果继续回流到 harness。
## 5. 人类角色变化
访谈把人类角色分成两类:
1. **架构师 / planning owner**
- 不只是传统技术架构师,而是产品品味、商业判断、行业理解、技术能力的综合角色。
- 负责判断 AI 生成的大量需求中哪些值得做、是否偏离主线、风险是否可控。
2. **任务接收者 / reviewer / operator**
- 接收 AI 分配的 bug fix、UI 调整、验证、复盘等任务。
- 重点是审核、补上下文、处理阻塞,而不是从零执行所有工作。
对 Lime 的含义:
**Task Center 和 Workspace 不只展示“agent 在跑什么”,还要展示“人在哪些关键节点需要判断什么”。**
## 6. 对 Lime 的映射
| Skill Forge 组织 harness | Lime 应收敛到的主链 | 不应新增的旁路 |
| --- | --- | --- |
| AI 扫描候选需求 | roadmap / exec-plan / telemetry 摘要 / task intake | 独立 AI 产品经理数据库 |
| 人类 planning 判断 | Workspace review / Task Center / explicit approval | 模型直接决定 roadmap |
| AI 实现与修复 | Skill Forge / Query Loop / subagent / automation job | 绕过 runtime 的代码执行器 |
| AB / log 反馈 | telemetry / artifact / evidence / outcome summary | 第二套实验事实源 |
| 组织记忆回流 | repo docs / memory compaction / workspace context | 只存在聊天里的决策 |
固定事实源声明:
**Lime 只把组织 harness 的启发折回现有 roadmap、exec-plan、Workspace、telemetry、artifact 和 evidence 主链;不新增平行的 AI PM / AB / scheduler 系统。**
## 7. 研究边界
本文件不把以下访谈内容写成 Lime 的立即产品承诺:
1. 每天上线 5-8 个功能。
2. 95% 工作都交给 AI。
3. revenue run rate 或 ARPU 增长。
4. 融资金额、团队规模、市场扩散。
这些可以作为外部案例背景,但 Lime roadmap 只吸收可工程化的组织闭环。
## 8. 一句话结论
**Skill Forge 的非共识不是“AI 写代码更快”,而是“公司流程先围绕 AI 的能力重构,产品才能在每个变化节点快速转身”。**
@@ -1,4 +1,4 @@
# CREAO 的工具编码编排
# Skill Forge 的工具编码编排
> 状态:current research reference
> 更新时间:2026-05-06
@@ -83,7 +83,7 @@ Tool Maker Agent
## 3. 能力生成链路
CREAO 式工具编码编排可以抽象成下面这条链:
Skill Forge 式工具编码编排可以抽象成下面这条链:
```text
Capability Source
@@ -229,7 +229,7 @@ Tool-maker agent 解决的是:
## 6. 对 Lime skills pipeline 的启发
CREAO 的关键启发不是替代 skills pipeline,而是给它补上游:
Skill Forge 的关键启发不是替代 skills pipeline,而是给它补上游:
```text
用户目标
+714
View File
@@ -0,0 +1,714 @@
# Lime Agent Knowledge PRD v2 - 可视化设计文档
> 状态:current / 与 `prd-v2.md` 配套使用
> 更新时间:2026-05-08
> 关系:本文是 `prd-v2.md` 的可视化补充。所有图表、UI 原型、时序与流程严格对齐 Agent Knowledge v0.6.0 的 `document-first` / `runtime.mode` / `metadata.producedBy`,以及 v2 PRD 的 §2A persona/data 区分、§2B Skills-first、§5 Builder Skill 薄适配、§6 Builder Skill 清单、§7 Resolver、§9 命令面、§10.4 用户故事。
## 0. 文档目录
1. [§1 总体架构图](#1-总体架构图) — Skill Bundle / KnowledgePack / Runtime Binding 分层
2. [§2 核心流程图](#2-核心流程图) — Builder Skill 整理 / Resolver 决策 / 状态机
3. [§3 关键时序图](#3-关键时序图) — persona 整理、运行时调用、双产出
4. [§4 UI 原型](#4-ui-原型) — 5 个核心界面线框图
5. [§5 用户故事可视化](#5-用户故事可视化) — 5 个业务场景的端到端走查
## 1. 总体架构图
### 1.1 Skills-first 系统层次架构
```mermaid
flowchart TB
subgraph U[用户层]
U1[创作者 / 操盘手 / CEO]
end
subgraph E[入口层]
E1[输入框资料图标]
E2[场景命令<br/>/IP文案 /内容运营 /私域运营 /产品文案]
E3[Agent 输出沉淀]
E4[直接编辑 documents/.md]
end
subgraph P[产品投影层]
P1[资料管理页<br/>章节列表 + 状态]
P2[整理面板<br/>accept/edit/rewrite]
P3[沉淀面板<br/>新建 / 补充]
P4[高级视图<br/>Builder Skill provenance]
end
subgraph C[Skill 目录层]
C1[SkillCatalog.entries<br/>kind=skill - current]
C2[serviceSkillCatalog<br/>compat 产品投影]
C3[Builder Skill Bundle<br/>SKILL.md + references/ + scripts/]
end
subgraph S[服务层]
S1[knowledge_compile_pack<br/>薄适配: 选 Skill + 调 binding]
S2[Runtime Binding<br/>agent_turn / native_skill]
S3[knowledge_split_document<br/>自动切片]
S4[knowledge_resolve_context<br/>persona/data 分支]
end
subgraph D[KnowledgePack 数据层]
D1[KNOWLEDGE.md<br/>profile=document-first<br/>runtime.mode + producedBy]
D2[documents/<br/>成品文档 - v0.6 主事实源]
D3[sources/<br/>原始材料]
D4[compiled/splits/<br/>派生切片]
D5[runs/<br/>compile/context 审计记录]
end
subgraph R[Agent 运行时层]
R1[Fenced Wrapper<br/>mode=persona / data]
R2[agent_runtime_submit_turn]
R3[Model]
end
U1 --> E1
U1 --> E2
U1 --> E3
U1 --> E4
E1 --> P1
E3 --> P3
E4 --> D2
P1 --> P2
P2 --> S1
P3 --> S1
C1 --> S1
C2 -.compat 只委托.-> C1
C1 --> C3
S1 --> S2
S2 --> C3
C3 --> S1
D3 --> S1
D1 --> S1
S1 --> D2
S1 --> D5
D2 --> S3
S3 --> D4
E1 --> S4
E2 --> S4
D1 --> S4
D4 --> S4
S4 --> R1
R1 --> R2
R2 --> R3
R3 -.输出.-> U1
R3 -.可沉淀.-> E3
P4 --> D5
classDef user fill:#FEF3C7,stroke:#F59E0B,color:#78350F;
classDef entry fill:#EFF6FF,stroke:#3B82F6,color:#1E3A8A;
classDef product fill:#ECFDF5,stroke:#10B981,color:#064E3B;
classDef skill fill:#EDE9FE,stroke:#8B5CF6,color:#4C1D95;
classDef service fill:#FDF4FF,stroke:#A855F7,color:#581C87;
classDef data fill:#F8FAFC,stroke:#64748B,color:#0F172A;
classDef runtime fill:#FFF7ED,stroke:#F97316,color:#7C2D12;
class U1 user;
class E1,E2,E3,E4 entry;
class P1,P2,P3,P4 product;
class C1,C2,C3,S2 skill;
class S1,S3,S4 service;
class D1,D2,D3,D4,D5 data;
class R1,R2,R3 runtime;
```
**架构关键判断**:
1. `Builder Skill Bundle` 是生产工艺事实源,`KnowledgePack documents/` 是产物事实源。
2. `knowledge_compile_pack` 不拥有模板和章节生成逻辑,只做 Skill 选择、runtime binding 调用和文件写回。
3. `SkillCatalog.entries(kind=skill)` 是 current 目录投影;`serviceSkillCatalog` 只允许 compat 委托。
4. `documents/` 是 Agent Knowledge v0.6.0 `document-first` 数据层中心;`compiled/splits/`、`runs/` 都是派生 / 审计层。
5. 运行时层的 wrapper 只消费 `runtime.mode=persona|data`,决定 persona 还是 data 语义;它不执行 Builder Skill。
### 1.2 Skill Bundle 与 KnowledgePack 边界
```mermaid
flowchart LR
subgraph SB[Builder Skill Bundle - how]
SB1[SKILL.md<br/>工作流]
SB2[references/template.md<br/>章节骨架]
SB3[references/interview-questions.md<br/>缺口问题]
SB4[references/quality-checklist.md<br/>质量自检]
SB5[scripts/*.py<br/>格式转换]
SB6[agents/*.yaml<br/>模型适配]
end
subgraph KP[KnowledgePack - what]
KP1[KNOWLEDGE.md<br/>profile + runtime.mode<br/>primaryDocument + producedBy]
KP2[sources/<br/>原始资料]
KP3[documents/<doc>.md<br/>成品文档]
KP4[compiled/splits/<br/>派生切片]
KP5[runs/compile-*.json<br/>Skill provenance + 章节状态]
end
SB1 --> BIND[Runtime Binding]
SB2 --> BIND
SB3 --> BIND
SB4 --> BIND
SB5 --> BIND
SB6 --> BIND
KP1 --> BIND
KP2 --> BIND
BIND --> KP3
BIND --> KP5
KP3 --> SPLIT[knowledge_split_document]
SPLIT --> KP4
classDef skill fill:#EDE9FE,stroke:#8B5CF6,color:#4C1D95;
classDef pack fill:#ECFDF5,stroke:#10B981,color:#064E3B;
classDef service fill:#FDF4FF,stroke:#A855F7,color:#581C87;
class SB1,SB2,SB3,SB4,SB5,SB6 skill;
class KP1,KP2,KP3,KP4,KP5 pack;
class BIND,SPLIT service;
```
**边界规则**:
1. Skill 不复制进 pack;pack 只记录 `metadata.producedBy`、版本、digest 和 run 记录。
2. `references/` 是模板事实源;PRD 和 Lime 代码不再维护平行模板。
3. Skill 内部脚本只做转换或辅助处理,不直接写 pack;最终写入由 Lime 完成。
4. Knowledge runtime 消费 pack 时只读取 `KNOWLEDGE.md`、`documents/`、`compiled/` 和 `runs/context-*`;不得为了回答用户问题而执行 Builder Skill。
### 1.3 persona / data 双族架构
```mermaid
flowchart LR
subgraph PF[persona 族 pack]
PF1[personal-profile<br/>个人 IP]
PF2[brand-persona<br/>品牌人设]
PF_SKILL[persona Builder Skills<br/>personal-ip / brand-persona]
PF_CORE[核心人设章节<br/>voice / 性格 / 金句 / 禁忌 / 应用指南]
PF_FACT[事实章节<br/>履历 / 案例 / 数据]
end
subgraph DF[data 族 pack]
DF1[brand-product<br/>产品事实]
DF2[organization-knowhow<br/>组织 SOP]
DF3[growth-strategy<br/>增长策略]
DF4[content-operations<br/>内容运营]
DF5[private-domain-operations<br/>私域/社群运营]
DF6[live-commerce-operations<br/>直播运营]
DF7[campaign-operations<br/>活动运营]
DF_SKILL[data Builder Skills<br/>product / knowhow / growth / ops]
DF_FACT[事实与 playbook 章节<br/>参数 / 流程 / 节奏 / 指标 / 合规]
end
subgraph RT[运行时 Resolver]
RT_PERSONA[persona 分支<br/>核心人设无条件先注入]
RT_DATA[data 分支<br/>按任务命中章节]
RT_COMBINE[多 pack 协同<br/>1 persona + N data]
end
PF_SKILL --> PF_CORE
PF_SKILL --> PF_FACT
DF_SKILL --> DF_FACT
PF_CORE --> RT_PERSONA
PF_FACT --> RT_PERSONA
DF_FACT --> RT_DATA
RT_PERSONA --> RT_COMBINE
RT_DATA --> RT_COMBINE
RT_COMBINE --> WRAP[Fenced Wrapper<br/>顺序: persona 先, data 后]
WRAP --> MODEL[Model 输出]
classDef persona fill:#FEF3C7,stroke:#F59E0B,color:#78350F;
classDef data fill:#DBEAFE,stroke:#3B82F6,color:#1E3A8A;
classDef skill fill:#EDE9FE,stroke:#8B5CF6,color:#4C1D95;
classDef runtime fill:#FFF7ED,stroke:#F97316,color:#7C2D12;
class PF1,PF2,PF_CORE,PF_FACT persona;
class DF1,DF2,DF3,DF4,DF5,DF6,DF7,DF_FACT data;
class PF_SKILL,DF_SKILL skill;
class RT_PERSONA,RT_DATA,RT_COMBINE,WRAP,MODEL runtime;
```
**协同判断**:
1. persona pack 提供"怎么说";data pack 提供"说什么"。
2. 同一品牌可同时存在 brand-persona + brand-product + content-operations 等多个独立 pack。
3. 运营类知识库仍属于 data family,提供节奏、SOP、指标和复盘,不新增第三族。
4. wrapper 顺序固定:persona 先建立表达语境,data 再加载具体事实和运营 playbook。
5. 最多 1 个 persona + N 个 data;两个 persona 同时启用会让模型扮演冲突。
## 2. 核心流程图
### 2.1 Builder Skill 整理流程(对应 v2 §5)
```mermaid
flowchart TD
Start([用户导入访谈稿/资料]) --> Import[knowledge_import_source]
Import --> PackMeta[读取 KNOWLEDGE.md<br/>profile / runtime.mode / type / producedBy / limeTemplate]
PackMeta --> SkillPick{能否找到<br/>Builder Skill?}
SkillPick -->|显式选择或 producedBy 默认建议| SkillRef[skillBundleRef]
SkillPick -->|SkillCatalog 命中| SkillRef
SkillPick -->|serviceSkillCatalog compat| SkillRef
SkillPick -->|找不到| Block[阻断: 选择或安装 Skill]
SkillRef --> Binding[Runtime Binding]
Binding --> SkillFlow[执行 Builder Skill 工作流<br/>SKILL.md + references/ + scripts/]
SkillFlow --> Coverage{章节覆盖判定<br/>由 Skill 返回}
Coverage -->|full / partial| DocOut[primaryDocument]
Coverage -->|missing| Missing[文档中显式待补充]
Coverage -->|conflict| Conflict[冲突摘要 + disputed]
DocOut --> Output[KnowledgeBuilderSkillOutput]
Missing --> Output
Conflict --> Output
Output --> WriteMeta[写入 metadata.producedBy]
Output --> WriteRun[写入 runs/compile-*.json<br/>builder_skill + chapters[]]
WriteMeta --> WriteDoc
WriteRun --> WriteDoc[写入 documents/<doc>.md]
WriteDoc --> Split[knowledge_split_document]
Split --> ReviewPanel[资料管理页章节列表]
ReviewPanel --> UserAction{用户操作}
UserAction -->|accept| ReadyCheck
UserAction -->|edit| EditChapter[编辑 documents 或章节]
UserAction -->|rewrite| Binding
EditChapter --> Split
ReadyCheck{所有章节<br/>accepted/missing?}
ReadyCheck -->|是| StatusReady([pack: ready])
ReadyCheck -->|否| StatusReview([pack: needs-review])
Block --> StatusReview
classDef start fill:#FEF3C7,stroke:#F59E0B,color:#78350F;
classDef skill fill:#EDE9FE,stroke:#8B5CF6,color:#4C1D95;
classDef data fill:#F8FAFC,stroke:#64748B,color:#0F172A;
classDef status fill:#ECFDF5,stroke:#10B981,color:#064E3B;
classDef warn fill:#FEE2E2,stroke:#EF4444,color:#7F1D1D;
class Start,Import start;
class SkillRef,Binding,SkillFlow skill;
class PackMeta,DocOut,Output,WriteDoc,WriteRun,Split,ReviewPanel,EditChapter data;
class StatusReady status;
class Missing,Conflict,Block,StatusReview warn;
```
**关键控制点**:
1. 先选 Builder Skill,再整理;没有 Skill 不允许 LLM 直生成。
2. 章节级生成、回退、质量检查属于 Skill 工作流;Lime 只校验输出契约。
3. `runs/compile-*.json` 必须记录 Skill provenance,方便后续复现和审计。
4. `documents/<doc>.md` 写入后统一由 `knowledge_split_document` 派生切片。
### 2.2 Resolver 决策流程(对应 v2 §7.2)
```mermaid
flowchart TD
Req([用户请求 + 当前 pack]) --> PackType{pack family?}
PackType -->|persona| PersonaBranch[persona 分支]
PackType -->|data| DataBranch[data 分支]
PersonaBranch --> CoreInject[核心人设章节无条件注入<br/>voice + 性格 + 金句 + 禁忌 + 应用指南]
CoreInject --> CheckBudget1{persona token<br/>≤ personaCoreBudget 3500?}
CheckBudget1 -->|超| BlockPersona[阻断<br/>提示用户精简文档]
CheckBudget1 -->|未超| AddFactPersona[按任务追加事实章节<br/>履历/案例]
DataBranch --> EstimateTokens[主文档 token 估算]
EstimateTokens --> SizeCheck{size?}
SizeCheck -->|≤ smallBudget| Full[完整注入主文档]
SizeCheck -->|≤ midBudget| BriefAndPick[brief + 应用指南 + 任务相关章节]
SizeCheck -->|≤ hardCap| StrictPick[brief + 1-3 最相关章节]
SizeCheck -->|> hardCap| BlockData[阻断<br/>要求拆分文档]
AddFactPersona --> WrapPersona[persona wrapper<br/>mode=persona]
Full --> WrapData[data wrapper<br/>mode=data]
BriefAndPick --> WrapData
StrictPick --> WrapData
WrapPersona --> MultiPack{是否启用了<br/>多个 pack?}
WrapData --> MultiPack
MultiPack -->|是| OrderCheck[wrapper 顺序<br/>persona 先 / data 后]
MultiPack -->|否| Single[单 pack 注入]
OrderCheck --> WriteRun[runs/context-*.json]
Single --> WriteRun
WriteRun --> Inject[注入 Agent Runtime]
Inject --> Model([Model 输出])
BlockPersona --> UserNotify[提示用户操作]
BlockData --> UserNotify
classDef start fill:#FEF3C7,stroke:#F59E0B,color:#78350F;
classDef persona fill:#FEF3C7,stroke:#F59E0B,color:#78350F;
classDef data fill:#DBEAFE,stroke:#3B82F6,color:#1E3A8A;
classDef wrap fill:#FFF7ED,stroke:#F97316,color:#7C2D12;
classDef warn fill:#FEE2E2,stroke:#EF4444,color:#7F1D1D;
class Req,Model start;
class PersonaBranch,CoreInject,AddFactPersona,WrapPersona persona;
class DataBranch,EstimateTokens,Full,BriefAndPick,StrictPick,WrapData data;
class MultiPack,OrderCheck,Single,WriteRun,Inject wrap;
class BlockPersona,BlockData,UserNotify warn;
```
**Resolver 关键判断**:
1. `family` 决定走 persona 或 data 分支,不混用。
2. persona 分支不做"按 token 大小四档决策",因为核心人设章节必须无条件注入。
3. data 分支才走 v2 §7.2 的 full / brief+sections / strict-pick / block 四档。
4. 多 pack 协同时 wrapper 顺序固定:persona wrapper 永远在 data wrapper 之前。
### 2.3 状态流转流程(对应 v2 §12)
```mermaid
stateDiagram-v2
[*] --> draft: 创建 pack
draft --> needs_review: Builder Skill 返回章节状态
needs_review --> ready: 全部章节 accepted/missing
needs_review --> disputed: 有 conflict 章节
ready --> stale: N 天未更新 + sources 新增
ready --> disputed: 检测到冲突
stale --> needs_review: 重新整理
disputed --> needs_review: 解决冲突
ready --> archived: 用户归档
stale --> archived: 用户归档
needs_review --> archived: 用户归档
archived --> [*]: 删除
note right of draft
不进入运行时
end note
note right of needs_review
不进入运行时
UI 显示徽章
end note
note right of ready
可进入运行时
默认候选
end note
note right of stale
使用时提示过期
end note
note right of disputed
默认阻断
需用户确认
end note
```
**状态判定规则**:
1. 触发器是 Skill 返回的**章节级**判定。
2. 全部章节 accepted 或显式 missing → 整 pack ready。
3. 任意章节 needs-review → 整 pack needs-review。
4. coverage: conflict → 整 pack disputed。
5. UI 不暴露章节状态机给普通用户;只在高级模式显示。
## 3. 关键时序图
### 3.1 P1:个人 IP Builder Skill 整理时序
```mermaid
sequenceDiagram
autonumber
actor User as 用户
participant UI as 资料管理页
participant API as knowledge_compile_pack
participant Catalog as SkillCatalog
participant Binding as Runtime Binding
participant Skill as personal-ip-knowledge-builder
participant Pack as KnowledgePack
participant Split as knowledge_split_document
User->>UI: 导入访谈稿 / 点击整理
UI->>API: packPath + sources + profile=document-first + runtime.mode=persona + limeTemplate=personal-ip
API->>Catalog: 查找 personal-ip-knowledge-builder
Catalog-->>API: skillBundleRef + digest + resources
API->>Binding: 输入 KnowledgeBuilderSkillInput
Binding->>Skill: 加载 SKILL.md / references / scripts
Skill-->>Binding: KnowledgeBuilderSkillOutput
Binding-->>API: primaryDocument + chapters[] + issues
API->>Pack: 写 KNOWLEDGE.md metadata.producedBy
API->>Pack: 写 documents/<doc>.md
API->>Pack: 写 runs/compile-*.json.builder_skill
API->>Split: 对 primaryDocument 切片
Split->>Pack: 写 compiled/splits + index.json
API-->>UI: pack status + chapters[]
UI-->>User: 显示完整文档 / 章节审阅 / Skill provenance
```
### 3.2 运行时调用时序:persona + data 协同
```mermaid
sequenceDiagram
autonumber
actor User as 用户
participant Input as 输入框 / 场景命令
participant Resolver as knowledge_resolve_context
participant Pack as KnowledgePack Index
participant Runtime as agent_runtime_submit_turn
participant Model as Model
User->>Input: /IP文案 以谢晶口吻介绍金花黑茶
Input->>Resolver: request + active packs
Resolver->>Pack: 读取 profile / runtime.mode / status / primaryDocument
Resolver->>Pack: 读取 persona compiled/index.json
Resolver->>Pack: 读取 data compiled/index.json
Resolver->>Resolver: persona 核心章节先选
Resolver->>Resolver: data 事实章节按任务命中
Resolver->>Runtime: fenced persona wrapper + data wrapper
Runtime->>Model: 提交带上下文的 turn
Model-->>Runtime: 输出文案
Runtime-->>Input: 返回结果 + context run id
Input-->>User: 展示输出,可沉淀为 sources
```
### 3.3 Agent 输出沉淀为资料时序
```mermaid
sequenceDiagram
autonumber
actor User as 用户
participant Chat as Agent 输出
participant Panel as 沉淀面板
participant Pack as KnowledgePack
participant API as knowledge_compile_pack
participant Skill as Builder Skill
Chat-->>User: 生成一段可复用内容
User->>Panel: 点击"沉淀到资料"
Panel->>Pack: 写入 sources/agent-output-*.md
Panel->>User: 选择补充已有 pack 或新建 pack
User->>Panel: 选择 personal-profile pack
Panel->>API: 重新整理 pack
API->>Skill: 调用同一 Builder Skill,传 acceptedChapters
Skill-->>API: 返回增量后的 primaryDocument + chapters[]
API->>Pack: 更新 metadata.producedBy / documents/ 与 runs/
Panel-->>User: 显示新增资料影响的章节
```
## 4. UI 原型
### 4.1 创建资料:用户看到模板,系统绑定 Skill
```text
┌──────────────────────────────────────────────────────────────┐
│ 新建项目资料 │
├──────────────────────────────────────────────────────────────┤
│ 资料类型 │
│ [个人 IP] [品牌人设] [品牌产品] [组织 Know-how] [运营类] │
│ 标准形态:Agent Knowledge v0.6 document-first / persona │
│ │
│ 上传资料 │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 拖入访谈稿、DOCX、聊天记录、公开资料 │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ 高级信息 │
│ 使用 Builder Skill: personal-ip-knowledge-builder v1.0.0 │
│ 来源: SkillCatalog / seeded │
│ 写入: metadata.producedBy + runs/compile.builder_skill │
│ │
│ [取消] [开始整理] │
└──────────────────────────────────────────────────────────────┘
```
说明:普通用户看到"资料类型";高级信息才暴露 Builder Skill,不把工程概念推给普通用户。
### 4.2 整理进度:显示 Skill 阶段而不是 Lime 自建步骤
```text
┌──────────────────────────────────────────────────────────────┐
│ 正在整理:谢晶个人 IP │
├──────────────────────────────────────────────────────────────┤
│ Builder Skill │
│ personal-ip-knowledge-builder │
│ │
│ 进度 │
│ ✓ 读取 sources/访谈稿_20260201.docx │
│ ✓ 使用 scripts/docx_to_markdown.py 转换为 Markdown │
│ ✓ 按 references/personal-ip-template.md 生成章节 │
│ ! 金句语录来源不足,建议补充访谈 │
│ ✓ references/quality-checklist.md 自检完成 │
│ │
│ 预计产物 │
│ documents/谢晶_个人IP知识库v1.0.md │
└──────────────────────────────────────────────────────────────┘
```
### 4.3 资料详情:成品文档优先
```text
┌──────────────────────────────────────────────────────────────┐
│ 谢晶 个人 IP 知识库 ready │
├──────────────────────────────────────────────────────────────┤
│ 主文档 │
│ documents/谢晶_个人IP知识库v1.0.md [打开完整文档] [导出] │
│ │
│ 最近整理 │
│ producedBy: personal-ip-knowledge-builder v1.0.0 │
│ Run: compile-20260207T103000Z.json │
│ profile: document-first runtime.mode: persona │
│ │
│ 章节审阅 │
│ [✓] 人物档案与基本信息 full │
│ [✓] 个人简介与核心定位 full │
│ [!] 金句语录与思想精华 partial │
│ [-] 未来愿景与发展规划 missing │
│ │
│ [重新整理] [补充资料] [高级:查看 Skill provenance] │
└──────────────────────────────────────────────────────────────┘
```
### 4.4 输入框资料选择:persona / data 可见
```text
┌──────────────────────────────────────────────────────────────┐
│ 选择本轮资料 │
├──────────────────────────────────────────────────────────────┤
│ Persona │
│ (●) 谢晶个人 IP ready personal-profile │
│ ( ) 品牌官方口吻 draft brand-persona │
│ │
│ Data │
│ [✓] 金花黑茶产品事实 ready brand-product │
│ [✓] 本月内容运营日历 ready content-ops │
│ [ ] 私域社群转化 SOP stale private-domain │
│ [ ] 客服 SOP stale organization │
│ │
│ 规则:最多 1 个 persona + N 个 data │
│ [取消] [确认启用] │
└──────────────────────────────────────────────────────────────┘
```
### 4.5 运行时证据:context run 可追踪
```text
┌──────────────────────────────────────────────────────────────┐
│ 本轮使用资料 │
├──────────────────────────────────────────────────────────────┤
│ Wrapper 顺序 │
│ 1. 谢晶个人 IP mode=persona selected=5 sections │
│ 2. 金花黑茶产品事实 mode=data selected=3 sections │
│ 规则:persona/data 均为受保护数据,不执行 Builder Skill │
│ │
│ Context Run │
│ runs/context-20260207T104500Z.json │
│ │
│ 选中的切片 │
│ compiled/splits/谢晶/.../金句语录.md │
│ compiled/splits/金花黑茶/.../合规边界.md │
└──────────────────────────────────────────────────────────────┘
```
## 5. 用户故事可视化
### 5.1 MCN 机构:统一博主人设,团队协作不漂移
```mermaid
journey
title MCN 内容总监用个人 IP Builder Skill 统一博主人设
section 建库
导入博主访谈和历史爆款: 4: 内容总监
选择个人 IP 模板: 5: 内容总监
Lime 绑定 personal-ip-knowledge-builder: 5: Lime
section 审阅
打开完整 Markdown 文档: 5: 内容总监
接受核心人设章节: 4: 内容总监
标记缺失案例为待补充: 4: 内容总监
section 使用
团队用 /IP文案 写短视频脚本: 5: 编导
Resolver 注入 persona 核心章节: 5: Lime
输出风格稳定: 5: 博主
```
验收重点:同一个 persona pack 让不同编导、不同模型输出都不漂。
### 5.2 品牌操盘手:创始人 IP + 产品事实双包协同
```mermaid
flowchart LR
A[创始人访谈] --> B[personal-ip-knowledge-builder]
B --> C[创始人 persona pack]
D[产品资料 / FAQ / 合规] --> E[brand-product-knowledge-builder]
E --> F[产品 data pack]
C --> G[Resolver<br/>persona first]
F --> G
G --> H[以创始人口吻介绍产品]
H --> I[有风格 + 有准确事实 + 不越过合规]
```
验收重点:persona 只决定表达方式,产品事实和合规边界来自 data pack。
### 5.3 企业服务商:客户 SOP 知识库,新人快速上手
```mermaid
flowchart TD
A[客户交付文档 / 会议纪要 / FAQ] --> B[organization-knowhow-knowledge-builder]
B --> C[organization-knowhow pack]
C --> D[新员工提问]
D --> E[Resolver 选 SOP / 失败案例 / FAQ]
E --> F[Agent 输出下一步操作]
F --> G[新人按 SOP 执行]
```
验收重点:输出必须是可执行步骤,不只是概念说明;不可回答边界必须能被单独注入。
### 5.4 创业公司 CEO:增长策略从资料沉淀到行动计划
```mermaid
flowchart TD
A[商业计划 / 指标 / 渠道复盘] --> B[growth-strategy-knowledge-builder]
B --> C[growth-strategy pack]
C --> D[/增长策略 生成 30/60/90 天计划]
D --> E[Resolver 选择指标 / 渠道 / 假设章节]
E --> F[Agent 输出带指标的行动计划]
F --> G[执行结果再沉淀为 sources]
G --> B
```
验收重点:增长策略必须能闭环到下一轮 sources,而不是一次性报告。
### 5.5 运营负责人:内容、私域、直播和活动 playbook
```mermaid
flowchart TD
A[内容日历 / 选题库 / 爆款复盘] --> B[content-operations-knowledge-builder]
C[用户分层 / 社群 SOP / 转化话术] --> D[private-domain-operations-knowledge-builder]
E[货盘 / 直播脚本 / 场控节奏] --> F[live-commerce-operations-knowledge-builder]
G[活动目标 / 渠道 / 素材 / 预算] --> H[campaign-operations-knowledge-builder]
B --> I[content-operations pack]
D --> J[private-domain-operations pack]
F --> K[live-commerce-operations pack]
H --> L[campaign-operations pack]
I --> M[Resolver data wrappers]
J --> M
K --> M
L --> M
M --> N[运营执行输出<br/>内容日历 / 触达节奏 / 直播脚本 / 活动清单]
N --> O[执行结果沉淀回 sources]
O --> B
O --> D
O --> F
O --> H
```
验收重点:运营类 pack 必须输出可执行动作、负责人、节奏、指标和复盘口径;不能只生成“建议多做内容和私域”这种泛泛建议。
## 6. 与 PRD 的一致性检查
| PRD 章节 | 本文图表 | 一致性要求 |
| --- | --- | --- |
| §11 Agent Knowledge v0.6.0 | §1.1 / §1.2 / §3.1 / §4.3 | pack 必须显式使用 `profile=document-first`、`runtime.mode`、`metadata.producedBy` |
| §2B Skills-first | §1.1 / §1.2 / §2.1 | Builder Skill 是工艺事实源,Lime 不自建整理引擎 |
| §5 整理契约 | §2.1 / §3.1 | `knowledge_compile_pack` 只做 Skill 选择、binding、写回 |
| §6 Skill 清单 | §1.3 / §4.1 / §5.5 | UI 模板来自 SkillCatalog 投影,不来自 Lime 内置模板目录;运营类也是 data family |
| §7 Resolver | §2.2 / §3.2 / §4.4 | persona / data 分支和 wrapper 顺序一致 |
| §9 命令面 | §4.5 | context run 与 command / scene 入口可追踪 |
| §10.4 用户故事 | §5.1-§5.5 | 每个业务故事都能回到一个 Builder Skill 或 pack 协同 |
File diff suppressed because it is too large Load Diff
+136 -26
View File
@@ -1,8 +1,9 @@
# Lime Agent Knowledge PRD
> 状态:current PRD / productized knowledge module architecture
> 更新时间:2026-05-05
> 状态:v1 / legacy current reference;v2 current 事实源见 `docs/roadmap/knowledge/prd-v2.md`
> 更新时间:2026-05-08
> 目标:把团队的 Agent Knowledge 标准接入 Lime,并让普通用户通过现有 Agent 输入框资料图标、File Manager 和首页引导完成项目资料的生成、沉淀、确认与使用闭环。
> v2 覆盖范围:目录结构、架构、时序、命令边界、Builder Skill、document-first profile、persona/data runtime mode 与运营类知识库路线,以 `docs/roadmap/knowledge/prd-v2.md` 和 `docs/roadmap/knowledge/prd-v2-diagrams.md` 为准。
## 1. 背景与结论
@@ -15,7 +16,7 @@ Lime 现有 `docs/knowledge` 已经沉淀了 Markdown-first 项目知识库、
但这些方案仍有几个问题:
1. 目录结构仍偏 Lime 自定义,例如 `knowledge.md`、`pack.json`、`source/`。
2. 知识运行时安全契约不够强,容易把知识正文里的指令误当成用户或系统指令。
2. 旧方案缺少 Agent Knowledge v0.5.0 runtime contract,容易把知识正文里的指令误当成用户或系统指令。
3. `KnowledgePack`、`Skill`、`Memory`、`Inspiration` 的边界虽有共识,但还没有进入路线图主事实源。
4. 旧文档更像方案探索,不应继续作为产品实现的唯一锚点。
@@ -31,6 +32,13 @@ Lime 现有 `docs/knowledge` 已经沉淀了 Markdown-first 项目知识库、
4. Agent 生成结果必须能继续沉淀为项目资料,形成生成到使用的闭环。
5. 普通用户界面不展示 packName、metadata、compiled、token、runtime fence、本机完整路径等开发者细节。
标准基线(v1 历史基线;v2 已升级到 Agent Knowledge v0.6.0):
1. v1 对齐 Agent Knowledge `v0.5.0`;v2 对齐 `v0.6.0`,使用 `profile: document-first`、`runtime.mode` 和 `metadata.producedBy`。
2. `KNOWLEDGE.md` 仍是唯一必需入口;必需 frontmatter 是 `name`、`description`、`type`、`status`。
3. Agent Knowledge runtime 只加载受保护事实上下文;Agent Skills runtime 加载流程说明。二者可以共享发现、激活、预算和信任机制,但激活语义不同。
4. v1 的 `knowledge_resolve_context` 按 v0.5.0 runtime standard 输出 bounded fenced context;v2 继续沿用 `context-resolution.schema.json`,但 selection 必须额外消费 `profile` 与 `runtime.mode`。
固定边界:
```text
@@ -290,7 +298,9 @@ Lime 的知识包目录采用 Agent Knowledge 标准:
indexes/
runs/
compile-20260501T103000Z.json
context-20260501T104500Z.json
schemas/
evals/
assets/
```
@@ -302,6 +312,7 @@ Lime 的知识包目录采用 Agent Knowledge 标准:
4. `compiled/` 是运行时派生视图,可以重建,不能成为独立事实源。
5. `indexes/` 只用于找候选,必须可从 `sources/`、`wiki/`、`compiled/` 重建。
6. `runs/` 记录导入、编译、lint、评审、查询过程证据。
7. `runs/context-*.json` 记录 runtime context resolution 诊断,结构对齐 Agent Knowledge `context-resolution.schema.json`。
### 7.2 `KNOWLEDGE.md` frontmatter
@@ -309,7 +320,7 @@ Lime 的知识包目录采用 Agent Knowledge 标准:
---
name: founder-personal-ip
description: 创始人个人 IP 的事实、故事、表达风格、场景话术和禁忌边界。
type: personal-ip
type: personal-profile
status: ready
version: 1.0.0
language: zh-CN
@@ -320,9 +331,21 @@ maintainers:
- content-team
metadata:
limeWorkspaceId: example-workspace
limeTemplate: personal-ip
---
```
类型映射规则:
| Lime 用户模板 | Agent Knowledge `type` | Lime metadata |
| --- | --- | --- |
| 个人 IP | `personal-profile` | `metadata.limeTemplate: personal-ip` |
| 品牌产品 | `brand-product` | `metadata.limeTemplate: brand-product` |
| 组织 Know-how | `organization-knowhow` | `metadata.limeTemplate: organization-knowhow` |
| 增长策略 | `growth-strategy`(v0.6 标准;v1 曾使用 `custom:lime-growth-strategy`) | `metadata.limeTemplate: growth-strategy` |
规则:UI 可以继续使用“个人 IP / 增长策略”等产品语言;标准文件里的 `type` 必须使用 Agent Knowledge 标准类型或 `custom:<namespace>`。
状态枚举沿用 Agent Knowledge:
```text
@@ -347,7 +370,13 @@ none | recommended | required
interface KnowledgePack {
name: string;
description: string;
type: "personal-ip" | "brand-product" | "organization-knowhow" | "growth-strategy" | string;
type:
| "personal-profile"
| "brand-product"
| "organization-knowhow"
| "domain-reference"
| "research-wiki"
| `custom:${string}`;
status: "draft" | "ready" | "needs-review" | "stale" | "disputed" | "archived";
version?: string;
language?: string;
@@ -357,6 +386,10 @@ interface KnowledgePack {
rootPath: string;
defaultForWorkspace: boolean;
updatedAt: string;
metadata?: {
limeWorkspaceId?: string;
limeTemplate?: "personal-ip" | "brand-product" | "organization-knowhow" | "growth-strategy" | string;
};
}
interface KnowledgeSource {
@@ -379,17 +412,37 @@ interface KnowledgeCompiledView {
generatedAt: string;
}
interface KnowledgeContextResolution {
packName: string;
status: KnowledgePack["status"];
grounding: KnowledgePack["grounding"];
selectedViews: KnowledgeCompiledView[];
warnings: string[];
tokenEstimate: number;
interface KnowledgeContextResolutionRun {
runId: string;
query: string;
status: "passed" | "needs-review" | "stale" | "disputed" | "failed";
resolver?: {
tool: "lime-knowledge-resolver" | string;
version?: string;
strategy: "compiled-first" | string;
};
activatedPacks: Array<{
name: string;
activation: "explicit" | "implicit" | "resolver-driven";
status?: KnowledgePack["status"];
trust?: KnowledgePack["trust"];
grounding?: KnowledgePack["grounding"];
selectedFiles: string[];
sourceAnchors?: string[];
warnings?: Array<{
severity: "info" | "warning" | "error";
path?: string;
message: string;
}>;
}>;
missing?: string[];
tokenEstimate?: number;
fencedContext: string;
}
```
持久化到 `runs/context-*.json` 时使用 Agent Knowledge schema 的 snake_case 字段;TypeScript DTO 可以使用 camelCase,但必须在 API 边界显式转换。
### 7.4 模块边界
后端知识域必须独立于 Tauri 壳:
@@ -424,6 +477,7 @@ src/components/agent/chat/workspace/knowledge/ # Workspace 与现有 Agent 发
4. File Manager、首页、输入框资料图标、`@` 兼容入口和消息工具栏只发起资料动作,不承载知识领域逻辑。
5. 项目资料使用必须回到现有 Agent,不新增独立 Agent。
6. 知识 UI 不挂到 `src/components/memory`,避免把 Knowledge 和 Memory 重新混成一层。
7. `knowledge_resolve_context` 是唯一 runtime context resolver 边界;任何页面、Skill、Memory 或 Inspiration 路径都不得自行拼装知识 prompt。
## 8. 总体架构
@@ -489,6 +543,7 @@ flowchart TB
4. `Resolver` 是运行时唯一知识上下文组装边界。
5. 模型永远只接收 fenced knowledge context,不直接服从知识正文里的指令。
6. Memory 和 Inspiration 只能补充偏好与样例,不抢资料事实源。
7. Resolver 可以复用 Skills runtime 的 metadata-first discovery、activation、context budget 和 trust check 机制,但不能复用 Skill activation 语义;Knowledge activation 只提供数据上下文。
## 9. 分层边界
@@ -646,6 +701,15 @@ flowchart TD
## 12. 运行时契约
Lime runtime 的 v1 基线对齐 Agent Knowledge v0.5.0;v2 继续保留下列安全流程,但标准字段以 `prd-v2.md` 的 v0.6.0 `profile` / `runtime.mode` / `metadata.producedBy` 为准:
1. 通过 `KNOWLEDGE.md` 发现知识包。
2. 先读取 catalog metadata。
3. 只激活相关知识包。
4. 选择最小可用上下文。
5. 把选中内容包裹为数据。
6. 需要审计时记录 `runs/context-*.json`。
知识包进入模型前必须由 Resolver 包裹:
```text
@@ -658,6 +722,15 @@ flowchart TD
</knowledge_pack>
```
wrapper 必须保留:
1. 知识包名称。
2. 状态。
3. 信任级别。
4. grounding 策略。
5. 选中路径。
6. 告警。
推荐 prompt 组装顺序:
```text
@@ -679,6 +752,15 @@ system / developer 约束
5. 是否在知识缺失时标注 `待补充` 或 `待确认`。
6. 是否按用户当前请求和 Scene Skill 输出结构完成任务。
runtime 安全要求:
1. 发现或激活阶段不得执行知识包内脚本。
2. `indexes/` 不得作为事实权威。
3. `stale`、`disputed` 或 `needs-review` 不能静默当作 `ready` 使用。
4. 低信任知识包不能无诊断遮蔽高信任知识包。
5. `compiled/` 或 `wiki/` 足够时,不加载原始 `sources/` 进入模型上下文。
6. `runs/context-*.json` 必须对齐 `context-resolution.schema.json`,用于 eval、调试和审计;普通用户 UI 不展示完整 JSON。
## 13. 成本与降级策略
```mermaid
@@ -752,6 +834,7 @@ knowledge_list_packs
knowledge_get_pack
knowledge_set_default_pack
knowledge_resolve_context
knowledge_validate_context_run
```
职责:
@@ -764,6 +847,7 @@ knowledge_resolve_context
| `knowledge_get_pack` | 读取单个知识包详情、状态、风险和运行时视图。 |
| `knowledge_set_default_pack` | 设置 workspace 默认知识包。 |
| `knowledge_resolve_context` | 按任务、状态、信任、预算解析 fenced context。 |
| `knowledge_validate_context_run` | 校验 `runs/context-*.json` 是否符合 Agent Knowledge context-resolution schema;用于开发、CI 和诊断面板,不进入普通用户主路径。 |
命令实现时必须同步:
@@ -777,6 +861,14 @@ knowledge_resolve_context
**后续知识包能力只允许向 `KnowledgePack + knowledge_* + Knowledge Context Resolver` 收敛;`project_memory_get` 只保留项目资料附属层职责,不能继续定义知识包主链。**
命令契约补充:
1. `knowledge_list_packs` 只返回 catalog metadata,不返回完整 `KNOWLEDGE.md` 正文。
2. `knowledge_get_pack` 可以读取 guide、状态、风险和维护视图,但默认不返回 raw `sources/` 全文。
3. `knowledge_resolve_context` 返回 fenced context、warnings、selectedFiles、sourceAnchors、missing、tokenEstimate,并可选择写入 `runs/context-*.json`。
4. `knowledge_validate_context_run` 只校验诊断记录,不参与模型上下文组装。
5. 所有 context resolution 记录对外用 Agent Knowledge schema 字段:`run_id`、`activated_packs`、`selected_files`、`source_anchors`、`token_estimate`。
## 16. 与现有 Lime 主链关系
### 16.1 与 Memory
@@ -826,7 +918,14 @@ Builder Skill 不应包含:
2. 敏感业务资料。
3. 需要来源、状态、评审生命周期治理的具体知识资产。
`docs/knowledge/skills/personal-ip-knowledge-builder/` 当前作为原型参考保留;正式产品化 Skill 以 `src-tauri/resources/default-skills/knowledge_builder/SKILL.md` 为 current 入口,并随 Lime 默认 Skills 安装到本地技能目录。
`docs/knowledge/skills/personal-ip-knowledge-builder/` 在 v2 中升级为 P1 current Builder Skill 事实源;`src-tauri/resources/default-skills/knowledge_builder/SKILL.md` 仅保留为 v1 历史实现 / compat 候选,不再承接新的章节模板或运营类扩展。
runtime 关系:
1. Skills runtime 注入 `SKILL.md` 作为流程说明。
2. Agent Knowledge runtime 注入 fenced factual context。
3. 同一轮可以同时选中 Skill 与 KnowledgePack,但二者必须保留不同信任契约。
4. Builder Skill 可以调用 `knowledge_import_source`、`knowledge_compile_pack`、`knowledge_validate_context_run` 等维护命令;最终回答仍通过 `knowledge_resolve_context` 读取已维护知识工件。
## 17. Current / Compat / Deprecated / Dead
@@ -842,9 +941,9 @@ Builder Skill 不应包含:
6. Agent Knowledge 标准目录结构与 `KnowledgePack`。
7. `KNOWLEDGE.md` 与 `sources/ -> wiki/ -> compiled/ -> runs/`。
8. `Knowledge Context Resolver` 与 fenced knowledge context。
9. `knowledge_*` 最小命令面。
10. `knowledge_builder` 内置 Builder Skill。
11. `docs/roadmap/knowledge/prd.md` 与 `docs/exec-plans/agent-knowledge-implementation-plan.md`。
9. `knowledge_*` 最小命令面,包括 `knowledge_resolve_context` 和 `knowledge_validate_context_run`。
10. `docs/knowledge/skills/personal-ip-knowledge-builder/`(v2 P1 current Builder Skill)。
11. `docs/roadmap/knowledge/prd-v2.md`、`docs/roadmap/knowledge/prd-v2-diagrams.md` 与 `docs/exec-plans/agent-knowledge-implementation-plan.md`。
### 17.2 `compat`
@@ -854,14 +953,14 @@ Builder Skill 不应包含:
2. `docs/knowledge/markdown-first-knowledge-pack-plan.md`。
3. `docs/knowledge/lime-project-knowledge-base-solution.md`。
4. `docs/knowledge/agent-skills-and-knowledge-pack-boundary.md`。
5. `docs/knowledge/skills/personal-ip-knowledge-builder/`。
6. `docs/knowledge/个人IP知识库样例.md`。
7. `project_memory_get` 项目资料附属层。
5. `docs/knowledge/个人IP知识库样例.md`。
6. `project_memory_get` 项目资料附属层。
7. `src-tauri/resources/default-skills/knowledge_builder/SKILL.md`(v1 内置整理器,v2 迁移为 compat / deprecated 候选)。
退出条件:
1. 新知识包实现落地后,旧文档只保留为 research / archive,README 中指向本 PRD。
2. Builder Skill 原型迁入正式 `knowledge_builder` 后,旧路径不再作为运行时 skill source。
2. `personal-ip-knowledge-builder` 接入 seeded `SkillCatalog.entries(kind=skill)` 后,`knowledge_builder` 不再作为运行时 skill source。
3. 项目资料附属层与知识包主链在 UI 和命令上完全分离。
### 17.3 `deprecated`
@@ -880,7 +979,7 @@ Builder Skill 不应包含:
## 18. 分阶段路线
### Phase 1:Markdown-first 项目资料闭环
### Phase 1:Markdown-first 项目资料闭环与 v0.5 runtime 基线
交付:
@@ -892,6 +991,8 @@ Builder Skill 不应包含:
6. 支持现有 Agent 输入框显式选择和启用项目资料。
7. Runtime 通过 `knowledge_resolve_context` 注入 fenced context。
8. File Manager、输入框资料图标、`@资料` 兼容入口、首页引导和 Agent 输出沉淀纳入同一资料模块路径。
9. `knowledge_resolve_context` 输出可选 `runs/context-*.json`,并能用 `knowledge_validate_context_run` 校验。
10. Lime 用户模板与 Agent Knowledge 标准类型分离,`metadata.limeTemplate` 保留产品模板来源。
验收场景:
@@ -901,6 +1002,8 @@ Builder Skill 不应包含:
4. 用户把满意的 Agent 输出继续沉淀为新资料或补充到当前资料。
5. 未确认草稿不默认用于生成。
6. 知识正文里的“忽略系统规则”等文本不会改变模型规则。
7. runtime 诊断记录符合 Agent Knowledge `context-resolution.schema.json`。
8. `knowledge_validate_context_run` 只校验诊断记录,不进入普通用户主路径。
### Phase 2:资料来源扩展与质量检查
@@ -968,6 +1071,7 @@ Builder Skill 不应包含:
4. 知识上下文必须 fenced。
5. `project_memory_get` 不参与知识包主链。
6. 命令、Bridge、治理目录册、mock 同步。
7. `runs/context-*.json` 对齐 `context-resolution.schema.json`,至少覆盖 `run_id`、`query`、`status`、`activated_packs`、`selected_files` 和 `token_estimate`。
### 19.3 安全验收
@@ -984,6 +1088,7 @@ Builder Skill 不应包含:
```bash
test -f docs/roadmap/knowledge/prd.md
rg -n "File Manager|@资料|沉淀为项目资料|knowledge_import_source|knowledge_resolve_context|KnowledgePack" docs/roadmap/knowledge/prd.md
rg -n "v0.5.0|context-resolution|knowledge_validate_context_run|Agent Knowledge runtime" docs/roadmap/knowledge/prd.md
```
实现阶段:
@@ -1003,9 +1108,13 @@ npm run verify:gui-smoke
Agent Knowledge 标准来源:
1. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/specification.md`
2. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/what-is-agent-knowledge.md`
3. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/agent-knowledge-vs-skills.md`
1. Agent Knowledge `v0.5.0` release:`https://github.com/limecloud/agentknowledge/releases/tag/v0.5.0`
2. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/specification.md`
3. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/client-implementation/runtime-standard.md`
4. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/client-implementation/runtime-context-resolver.md`
5. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/agent-knowledge-vs-skills.md`
6. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/public/schemas/context-resolution.schema.json`
7. `/Users/coso/Documents/dev/ai/limecloud/agentknowledge/docs/zh/versions/v0.5.0/overview.md`
Lime 旧方案参考:
@@ -1018,6 +1127,7 @@ Lime 旧方案参考:
迁移原则:
1. 旧文档里的产品洞察、Markdown-first 策略和 Builder Skill 模板可复用,但正式 Builder 事实源是 `knowledge_builder`。
1. 旧文档里的产品洞察、Markdown-first 策略可复用;v2 正式 Builder 事实源是 Agent Skills 包(首个为 `personal-ip-knowledge-builder`),不是内置 `knowledge_builder` 整理器。
2. 旧文档里的自定义目录结构迁移为 Agent Knowledge 标准目录。
3. 旧文档不再作为新实现的事实源;新实现以本 PRD 和 Agent Knowledge 标准为准。
3. 旧文档不再作为新实现的事实源;新实现以 `prd-v2.md` 和 Agent Knowledge v0.6.0 标准为准。
4. 如 Agent Knowledge 后续发布新版本,先更新 `prd-v2.md` 的标准基线和 runtime/schema 差异,再进入代码实现。
+4 -4
View File
@@ -11,7 +11,7 @@
- [../../aiprompts/task-agent-taxonomy.md](../../aiprompts/task-agent-taxonomy.md)
- [../../aiprompts/state-history-telemetry.md](../../aiprompts/state-history-telemetry.md)
- [../../aiprompts/harness-engine-governance.md](../../aiprompts/harness-engine-governance.md)
- [../creaoai/README.md](../creaoai/README.md)
- [../skill-forge/README.md](../skill-forge/README.md)
配套文档:
@@ -36,7 +36,7 @@
这些能力分别已有自己的 current 主链。`Managed Objective` 只能消费它们,不能替代它们。
这也是为什么实现 CreoAI 方向时,不能只实现 Managed Objective:Coding Agent / Skill Forge 仍然必须作为上游能力生成层单独落地,详见 [../creaoai/coding-agent-layer.md](../creaoai/coding-agent-layer.md)。
这也是为什么实现 Skill Forge 方向时,不能只实现 Managed Objective:Coding Agent / Skill Forge 仍然必须作为上游能力生成层单独落地,详见 [../skill-forge/coding-agent-layer.md](../skill-forge/coding-agent-layer.md)。
## 2. 先给结论
@@ -147,9 +147,9 @@ persistent thread goal(同一会话线程上的持久目标状态)
因此本路线图只借鉴 runtime pattern,不复制 command surface。
## 6. 与 CreoAI / Skill Forge 的关系
## 6. 与 Skill Forge / Skill Forge 的关系
CreoAI 路线图关注:
Skill Forge 路线图关注:
```text
能力生成 -> Skill / Adapter 编译 -> verification gate -> 注册 -> 执行
+1 -1
View File
@@ -190,7 +190,7 @@ flowchart TD
2. 模型总结只解释 evidence,不替代 evidence。
3. audit result 是 objective state 的输入,不是 evidence pack 的替代品。
## 7. 与 CreoAI / Skill Forge 的关系图
## 7. 与 Skill Forge / Skill Forge 的关系图
```mermaid
flowchart TB
@@ -45,7 +45,7 @@
完成标准:
1. 文档能解释 `/goal` 与 Managed Objective 的区别。
2. 文档能解释 Managed Objective 与 CreoAI / Skill Forge 的区别。
2. 文档能解释 Managed Objective 与 Skill Forge / Skill Forge 的区别。
3. 文档明确 `goal_runtime / objective_evidence / objective_scheduler` 这些方向是 dead。
## 3. P1:Objective state scaffold
@@ -1,20 +1,26 @@
# Lime CREAO 对照开发路线图
# Lime Skill Forge 对照开发路线图
> 状态:P0-P4 最小闭环完成;P4 Managed execution / Agent envelope 已通过完成审计
> 更新时间:2026-05-06
> 目标:把 CREAO 访谈里的 “Coding Agent 编码 CLI / API / tools、成功任务固化为 Agent、组织 AI Native 反馈闭环” 收敛成 Lime 可执行路线图,补强 skills pipeline 的生成、验证、注册、rerun 和 evidence 闭环。
> 状态:P0-P10 第六刀完成;Read-Only HTTP API policy 已有 runtime evidence pack 端到端审计回归,并通过 GUI smoke
> 更新时间:2026-05-07
> 目标:把 Skill Forge 访谈里的 “Coding Agent 编码 CLI / API / tools、成功任务固化为 Agent、组织 AI Native 反馈闭环” 收敛成 Lime 可执行路线图,补强 skills pipeline 的生成、验证、注册、rerun 和 evidence 闭环。
配套研究:
- [../../research/creaoai/README.md](../../research/creaoai/README.md)
- [../../research/creaoai/pivot-and-org-harness.md](../../research/creaoai/pivot-and-org-harness.md)
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../research/creaoai/architecture-breakdown.md](../../research/creaoai/architecture-breakdown.md)
- [../../research/creaoai/tool-coding-orchestration.md](../../research/creaoai/tool-coding-orchestration.md)
- [../../research/creaoai/lime-gap-analysis.md](../../research/creaoai/lime-gap-analysis.md)
- [../../research/skill-forge/README.md](../../research/skill-forge/README.md)
- [../../research/skill-forge/pivot-and-org-harness.md](../../research/skill-forge/pivot-and-org-harness.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
- [../../research/skill-forge/architecture-breakdown.md](../../research/skill-forge/architecture-breakdown.md)
- [../../research/skill-forge/tool-coding-orchestration.md](../../research/skill-forge/tool-coding-orchestration.md)
- [../../research/skill-forge/lime-gap-analysis.md](../../research/skill-forge/lime-gap-analysis.md)
- [../../research/pi-mono-coding-agent/README.md](../../research/pi-mono-coding-agent/README.md)
- [../../research/codex-goal/README.md](../../research/codex-goal/README.md)
- [../../exec-plans/creaoai-completion-audit.md](../../exec-plans/creaoai-completion-audit.md)
- [../../exec-plans/skill-forge-completion-audit.md](../../exec-plans/skill-forge-completion-audit.md)
- [../../exec-plans/skill-forge-prompt-to-artifact-p5-plan.md](../../exec-plans/skill-forge-prompt-to-artifact-p5-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p6-audit.md](../../exec-plans/skill-forge-readonly-http-api-p6-audit.md)
- [../../exec-plans/skill-forge-readonly-http-api-p7-plan.md](../../exec-plans/skill-forge-readonly-http-api-p7-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p8-plan.md](../../exec-plans/skill-forge-readonly-http-api-p8-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p9-plan.md](../../exec-plans/skill-forge-readonly-http-api-p9-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p10-plan.md](../../exec-plans/skill-forge-readonly-http-api-p10-plan.md)
配套图纸:
@@ -30,7 +36,7 @@
## 0. 当前落地状态
截至 2026-05-06,CREAO 路线已经完成 **P0-P4 最小闭环**,并通过 [P0-P4 completion audit](../../exec-plans/creaoai-completion-audit.md) 收口:
截至 2026-05-06,Skill Forge 路线已经完成 **P0-P4 最小闭环**,并通过 [P0-P4 completion audit](../../exec-plans/skill-forge-completion-audit.md) 收口:
1. `Capability Draft` 已支持 create / list / get / verify / register 命令链。
2. verification gate 通过后,draft 才能进入 `verified_pending_registration`。
@@ -63,10 +69,16 @@
29. Agent card / sharing 已采用派生形态:`workspace-local/<skill-directory>` 由已注册 Skill、Managed Job 和 completion audit 派生;共享范围先限制在当前 workspace / team,不进入 public Marketplace,也不新增 Agent card 存储表。
30. Workspace/team sharing discovery 边界已显式展示:同 workspace 成员通过 registered skill discovery 发现 `.agents/skills/<skill-directory>`,复用同一 Managed Job / evidence 事实源,不新增分享命令或 Marketplace。
31. Completion audit 已完成 P0-P4 要求映射;Agent envelope gate 已收紧为只有 `completionAuditSummary.decision=completed` 且 automation owner / Workspace Skill ToolCall / artifact-or-timeline 三项 evidence 齐全时才进入 `evidence_ready`,单独 `evidencePackId` 不再绕过 audit。
32. 主线下一刀已切到 [P5 prompt-to-artifact E2E 样例](../../exec-plans/skill-forge-prompt-to-artifact-p5-plan.md):用“只读 CLI / 公开 API 每日报告”验证从 prompt、draft、verification、registration、first artifact、completion audit 到 Agent envelope 建议的完整产品证据链。
33. P5 已完成后,主线切到 [P6 只读 HTTP API adapter](../../exec-plans/skill-forge-readonly-http-api-p6-plan.md),并已通过 [P6 completion audit](../../exec-plans/skill-forge-readonly-http-api-p6-audit.md):verification gate 已要求网络只读权限声明、fixture input、`tests/` fixture、expected output、fixture dry-run 入口、dry-run expected-output binding、dry-run execute、structured evidence、dry-run offline 与 no credentials,并通过 Rust 定向测试、capability_draft 全套和 DevBridge smoke 证明正向 / 负向 draft 行为;`readonly_http_fixture_dry_run_execute` 会输出 script / expected output / duration / hash / stdout preview evidence,能力草案面板会消费本次 verification report 展示这些审计证据;只读 HTTP/API authoring 模板已抽到 `scripts/lib/readonly-http-api-draft-template.mjs`,默认生成 P6 所需 fixture / expected output / dry-run 文件;浏览器 mock verification 已对齐同一组 gate 与 evidence;仍不发真实 HTTP 请求,不新增 runtime。
34. P7 [只读 HTTP API 执行授权](../../exec-plans/skill-forge-readonly-http-api-p7-plan.md) 第一刀到第四刀已落地:在不打开真实联网 runtime 的前提下,verification gate 额外要求 `policy/readonly-http-session.json` 或等价 policy 声明 session-required、read-only GET、evidence-audited、受控 `credential_reference` 与 `execution_preflight` 边界;缺少该 policy、凭证引用策略或执行前检查计划的 API 草案不能进入 pending registration;正向 `readonly_http_execution_preflight` 会输出 approval request evidence,包含 preflight mode、endpoint source、GET、credential reference 与 evidence schema,并可被 API normalizer 和能力草案面板展示;authoring 模板、DevBridge smoke、浏览器 mock、API / UI 回归已对齐该 gate。
35. P8 [注册 provenance 延续](../../exec-plans/skill-forge-readonly-http-api-p8-plan.md) 第三刀已落地:`capability_draft_register` 会校验最新 passed verification report 与 manifest provenance 一致,并把 `readonly_http_execution_preflight` evidence 写入 registration summary;registered discovery、浏览器 mock、API normalizer 与 Workspace registered skills 面板会投影 / 展示该 preflight provenance,并持久化 `approvalRequests[]` pending artifact,包含 `approvalId`、`sourceCheckId`、`Endpoint=runtime_input`、`method=GET`、`credentialReferenceId=readonly_api_session`、`evidenceSchema`、`policyPath` 与 `createdAt`;本阶段仍不发真实 HTTP 请求、不保存 token、不新增 connector store、runtime 或 scheduler。
36. P9 [授权 artifact 消费门禁](../../exec-plans/skill-forge-readonly-http-api-p9-plan.md) 第十一刀已落地:每条 pending `approvalRequests[]` 已携带 `consumptionGate`、`credentialResolver`、`consumptionInputSchema`、`sessionInputIntake` 与 `sessionInputSubmissionContract`,并通过 `capability_draft_submit_approval_session_inputs` 校验当前 session 的授权确认、runtime endpoint、凭证引用确认和 evidence 捕获确认;校验通过会生成 `controlledGetPreflight` 与 `dryPreflightPlan`,只保留 `requestUrlHash=sha256(endpoint)` 与 planned evidence keys;`capability_draft_execute_controlled_get` 只在同一份 session 输入合同通过后执行一次性 GET,并返回 `responseStatus`、`responseSha256`、`executedAt`、截断 response preview 与 evidence;正向和 `request_failed` 结果会额外落盘 `.lime/capability-drafts/controlled-get-evidence/<artifact>.json`,只保存 hash、status、response metadata 与 evidence metadata,不保存 endpoint 明文、token 明文或 response preview 正文;`agent_runtime_export_evidence_pack` 现在会按当前 session 读取这些非敏感 artifact,并在 `runtime.json` / `artifacts.json` / `summary.md` 中输出 `capabilityDraftControlledGetEvidence` 可消费摘要;该摘要不复制 endpoint、token 或 response preview,也不注入 runtime、不进入 scheduler/default tool surface;校验失败返回 `blocked` 且不落 artifact;前端 API 网关、Rust command registration、DevBridge dispatcher、治理目录册、mock priority 与浏览器 mock 已同步,UI 仍不显示输入框、token 输入、调度或默认运行入口。
37. P10 [completion audit 消费](../../exec-plans/skill-forge-readonly-http-api-p10-plan.md) 第六刀已落地:`completionAuditSummary` 现在会消费当前 session 的 `capabilityDraftControlledGetEvidence` 摘要,输出受控 GET artifact / executed / scanned / skipped unsafe 计数、status 分布、`controlledGetEvidenceRequired` 与 `requiredEvidence.controlledGetEvidence`;前端 normalizer 与 Agent envelope presentation 已同步这些字段,Workspace 固化入口会在 evidence label 中展示 `受控 GET <executed>/<artifact> executed`,且未 completed 的 audit 会保持“等待 Completion Audit”而不是退回“还没有成功运行证据”;当 Workspace registered skill 带 `readonly_http_execution_preflight` verification gate / approval request 时,Managed Job 草案会在 `managed_objective.completion_evidence_policy` 声明必须具备 executed 受控 GET evidence,completion audit 因此会在缺该证据时输出 `missing_controlled_get_evidence` 并保持 `verifying`;前端 Agent envelope `evidence_ready` gate 也会防御性检查 `controlledGetEvidenceRequired`,即使收到异常 `decision=completed` 但缺 `requiredEvidence.controlledGetEvidence`,也不会启用固化入口;runtime evidence pack 已补端到端回归,证明同一个 owner metadata 在缺受控 GET evidence 时 `verifying`,补齐当前 session executed artifact 后才 `completed`,且不会复制 endpoint、token 或 response preview;GUI smoke 已通过,覆盖 workspace ready、browser runtime、site adapters、agent service skill entry、runtime tool surface、knowledge GUI 与 design canvas 主路径。普通非 HTTP 目标不受影响。该信号只作为 completion audit / Agent envelope 可见输入,不能单独把审计或固化入口推到 `completed / enabled`。
## 1. 先给结论
Lime 不应该另做一个 CREAO 式平行工具生成系统。
Lime 不应该另做一个 Skill Forge 式平行工具生成系统。
Lime 应该做的是:
@@ -78,7 +90,7 @@ Lime 应该做的是:
## 2. 权限宗旨
CREAO 路线的核心不是“无限放权”,而是:
Skill Forge 路线的核心不是“无限放权”,而是:
**权限永远显式受控,能力逐级开放;限制的是未经验证、未经授权、不可审计的执行,不是限制 agent 的理解、设计和编码能力。**
@@ -155,7 +167,7 @@ Level 6: policy-approved scheduled external write
### 5.1 Coding Agent / Agent Builder
`Coding Agent` 是 CREAO 启发里最核心的一层,负责把用户讲清楚的业务目标变成可验证的能力草案。详细设计见 [./coding-agent-layer.md](./coding-agent-layer.md)。
`Coding Agent` 是 Skill Forge 启发里最核心的一层,负责把用户讲清楚的业务目标变成可验证的能力草案。详细设计见 [./coding-agent-layer.md](./coding-agent-layer.md)。
本层可以参考 [../../research/pi-mono-coding-agent/README.md](../../research/pi-mono-coding-agent/README.md) 中对 `pi-mono` 的调研,但只参考 coding harness 的工程切面:会话分层、工具 allowlist、可插拔工具后端、事件生命周期和 deterministic test harness。Lime 不引入 pi-style 终端产品、JSONL session 事实源或全仓库 shell/write 权限。
@@ -261,8 +273,8 @@ Level 6: policy-approved scheduled external write
交付:
1. `docs/research/creaoai/` 研究拆解。
2. `docs/roadmap/creaoai/` 开发计划。
1. `docs/research/skill-forge/` 研究拆解。
2. `docs/roadmap/skill-forge/` 开发计划。
3. 明确 `Skill Forge` 不新增 runtime。
4. 明确 generated capability 必须进入 Skill / Adapter 标准。
@@ -389,7 +401,7 @@ AI 图层化设计不是 Skill Forge 的子阶段。
## 9. 组织 Harness 与反馈闭环
CREAO 访谈中的组织 harness 不直接进入 P3E 主线,但会影响后续 roadmap 和 Workspace 设计。
Skill Forge 访谈中的组织 harness 不直接进入 P3E 主线,但会影响后续 roadmap 和 Workspace 设计。
固定收敛方式:
@@ -1,8 +1,8 @@
# CREAO / Coding Agent 方案架构 Review
# Skill Forge / Coding Agent 方案架构 Review
> 状态:review gate
> 更新时间:2026-05-06
> 目标:在进入实现前,重新检查 CREAO 启发下的 Lime 方案是否缺层、缺闭环,或误把 Skill / 目标续跑当成完整 Agent 系统。
> 目标:在进入实现前,重新检查 Skill Forge 启发下的 Lime 方案是否缺层、缺闭环,或误把 Skill / 目标续跑当成完整 Agent 系统。
依赖文档:
@@ -11,9 +11,9 @@
- [./implementation-plan.md](./implementation-plan.md)
- [./diagrams.md](./diagrams.md)
- [../managed-objective/README.md](../managed-objective/README.md)
- [../../research/creaoai/pivot-and-org-harness.md](../../research/creaoai/pivot-and-org-harness.md)
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../research/creaoai/architecture-breakdown.md](../../research/creaoai/architecture-breakdown.md)
- [../../research/skill-forge/pivot-and-org-harness.md](../../research/skill-forge/pivot-and-org-harness.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
- [../../research/skill-forge/architecture-breakdown.md](../../research/skill-forge/architecture-breakdown.md)
- [../../research/pi-mono-coding-agent/README.md](../../research/pi-mono-coding-agent/README.md)
## 1. Review 结论
@@ -24,7 +24,7 @@
**必须先实现 Coding Agent / Skill Forge 的能力生成闭环,再实现 tool_runtime 授权调用与 Managed Objective 的可调度 rerun 闭环;Agent envelope 只能作为 Workspace 产品面叠在 verified skill 之后。**
如果反过来先做 Managed Objective,会得到一个目标续跑器;它能让已有工具多跑几轮,但不能复现 CREAO 案例里最关键的能力:
如果反过来先做 Managed Objective,会得到一个目标续跑器;它能让已有工具多跑几轮,但不能复现 Skill Forge 案例里最关键的能力:
```text
AI 根据用户目标现场写 adapter / wrapper / script
@@ -100,7 +100,7 @@ Level 6: policy-approved scheduled external write
来源:
- [../../research/creaoai/architecture-breakdown.md](../../research/creaoai/architecture-breakdown.md)
- [../../research/skill-forge/architecture-breakdown.md](../../research/skill-forge/architecture-breakdown.md)
已明确:
@@ -116,7 +116,7 @@ Coding Agent / Agent Builder
来源:
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
- [./README.md](./README.md)
已明确:
@@ -130,7 +130,7 @@ Coding Agent / Agent Builder
来源:
- [../../research/creaoai/pivot-and-org-harness.md](../../research/creaoai/pivot-and-org-harness.md)
- [../../research/skill-forge/pivot-and-org-harness.md](../../research/skill-forge/pivot-and-org-harness.md)
已明确:
@@ -226,7 +226,7 @@ verified draft
现在 evidence pack 主要面向 runtime execution。
但 CREAO 这条链还需要证明:
但 Skill Forge 这条链还需要证明:
1. Coding Agent 为什么生成这些文件。
2. 它读取了哪些 source refs。
@@ -305,7 +305,7 @@ P3D 之前解决的是 draft、verification、registration、readiness 和 Query
### 4.7 Outcome telemetry 还缺与 evidence 的分界
CREAO 访谈强调 AB testing 和日志反馈,但 Lime 不能把它们塞进 evidence pack 里。
Skill Forge 访谈强调 AB testing 和日志反馈,但 Lime 不能把它们塞进 evidence pack 里。
固定边界:
@@ -353,7 +353,7 @@ Lime 当前是桌面 GUI 产品,需要明确三种情况:
### 4.10 多 Skill workflow / DAG 还不能现在做
CREAO 电商案例是多能力链:监控、找货、生图、视频、文案、定价、上架。
Skill Forge 电商案例是多能力链:监控、找货、生图、视频、文案、定价、上架。
但 Lime 首期如果直接做 DAG,会把范围炸开。
@@ -2,22 +2,22 @@
> 状态:proposal
> 更新时间:2026-05-06
> 目标:把 CREAO 启发中最关键的 “Coding Agent 现场写代码、调 CLI / API、生成 adapter 和测试” 单独定义清楚,避免路线图退化成只有 Managed Objective 的目标续跑器。
> 目标:把 Skill Forge 启发中最关键的 “Coding Agent 现场写代码、调 CLI / API、生成 adapter 和测试” 单独定义清楚,避免路线图退化成只有 Managed Objective 的目标续跑器。
依赖文档:
- [./README.md](./README.md)
- [./implementation-plan.md](./implementation-plan.md)
- [./diagrams.md](./diagrams.md)
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../research/creaoai/architecture-breakdown.md](../../research/creaoai/architecture-breakdown.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
- [../../research/skill-forge/architecture-breakdown.md](../../research/skill-forge/architecture-breakdown.md)
- [../../research/pi-mono-coding-agent/README.md](../../research/pi-mono-coding-agent/README.md)
- [../../aiprompts/skill-standard.md](../../aiprompts/skill-standard.md)
- [../../aiprompts/query-loop.md](../../aiprompts/query-loop.md)
## 1. 为什么必须单独成层
你指出的问题是对的:如果只有 `Managed Objective`,Lime 得到的是一个“目标续跑控制层”;但 CREAO 案例最关键的不是续跑本身,而是:
你指出的问题是对的:如果只有 `Managed Objective`,Lime 得到的是一个“目标续跑控制层”;但 Skill Forge 案例最关键的不是续跑本身,而是:
```text
Coding Agent 根据业务目标
@@ -302,7 +302,7 @@ Coding Agent / Skill Forge
## 9. 首期实现切片建议
如果现在要开始实现 CREAO 方向,第一刀不应该是自动续跑,而应该是:
如果现在要开始实现 Skill Forge 方向,第一刀不应该是自动续跑,而应该是:
**P1A:Coding Agent 生成 workspace-local skill draft 的最小闭环。**
@@ -1,4 +1,4 @@
# CREAO 启发下的 Lime 架构图与流程图
# Skill Forge 启发下的 Lime 架构图与流程图
> 状态:proposal
> 更新时间:2026-05-06
@@ -1,7 +1,7 @@
# CREAO 启发下的 Lime 实施计划
# Skill Forge 启发下的 Lime 实施计划
> 状态:P0-P4 最小闭环完成;P4 Managed execution / Agent envelope 已通过完成审计
> 更新时间:2026-05-06
> 状态:P0-P10 第六刀完成;Read-Only HTTP API policy 已有 runtime evidence pack 端到端审计回归,并通过 GUI smoke
> 更新时间:2026-05-07
> 目标:把 Skill Forge / workspace-local generated skill / Agent envelope 的落地拆成可执行阶段,确保实现不偏离 Lime current 主链。
依赖文档:
@@ -12,13 +12,19 @@
- [./diagrams.md](./diagrams.md)
- [./prototype.md](./prototype.md)
- [../managed-objective/README.md](../managed-objective/README.md)
- [../../research/creaoai/pivot-and-org-harness.md](../../research/creaoai/pivot-and-org-harness.md)
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../exec-plans/creaoai-completion-audit.md](../../exec-plans/creaoai-completion-audit.md)
- [../../research/skill-forge/pivot-and-org-harness.md](../../research/skill-forge/pivot-and-org-harness.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
- [../../exec-plans/skill-forge-completion-audit.md](../../exec-plans/skill-forge-completion-audit.md)
- [../../exec-plans/skill-forge-prompt-to-artifact-p5-plan.md](../../exec-plans/skill-forge-prompt-to-artifact-p5-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p6-audit.md](../../exec-plans/skill-forge-readonly-http-api-p6-audit.md)
- [../../exec-plans/skill-forge-readonly-http-api-p7-plan.md](../../exec-plans/skill-forge-readonly-http-api-p7-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p8-plan.md](../../exec-plans/skill-forge-readonly-http-api-p8-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p9-plan.md](../../exec-plans/skill-forge-readonly-http-api-p9-plan.md)
- [../../exec-plans/skill-forge-readonly-http-api-p10-plan.md](../../exec-plans/skill-forge-readonly-http-api-p10-plan.md)
## 0. 当前实现进度
截至 2026-05-06,本计划已经完成 **P0-P4 最小闭环**,并通过 [P0-P4 completion audit](../../exec-plans/creaoai-completion-audit.md) 收口:
截至 2026-05-06,本计划已经完成 **P0-P4 最小闭环**,并通过 [P0-P4 completion audit](../../exec-plans/skill-forge-completion-audit.md) 收口:
1. P1A / P2 的最小文件事实源、静态 verification gate 和状态机已经落地。
2. P3A 已新增 `capability_draft_register`:只允许 `verified_pending_registration`,注册前复核 manifest 文件完整性与 Agent Skills 标准。
@@ -51,6 +57,12 @@
29. Agent card 与 sharing 已收敛为派生展示:Agent card id 使用 `workspace-local/<skill-directory>`,事实源来自 registered skill、Managed Job 和 completion audit;sharing 先限定 workspace / team 范围,不做 public Marketplace。
30. Workspace/team sharing discovery 已明确:团队成员通过同一 workspace root 的 registered skill discovery 发现 `.agents/skills/<skill-directory>`,并复用同一 Managed Job / evidence 事实源。
31. Completion audit 已逐项映射 P0-P4 要求到代码、测试、命令验证与文档证据;Agent envelope gate 已收紧,单独 `evidencePackId` 不再进入 `evidence_ready`,必须由 completed completion audit 和三项 evidence 共同打开固化入口。
32. P5 已开主线计划:用真实 prompt-to-artifact 样例验证“只读 CLI 每日报告”从 draft 到 Agent envelope 建议的产品闭环;P5 只做 demo / smoke / evidence,不新增 runtime、scheduler 或 Marketplace。
33. P6 第一刀已落地并通过 [P6 completion audit](../../exec-plans/skill-forge-readonly-http-api-p6-audit.md):只读 HTTP API 草案必须显式声明网络只读权限,并携带 fixture input、`tests/` fixture、expected output 与 fixture dry-run 入口,且 dry-run 入口必须绑定 expected output、保持离线,并在 verification 中执行出与 expected output 一致的 JSON;`readonly_http_fixture_dry_run_execute` 正向 check 会携带 script / expected output / duration / hash / stdout preview evidence,能力草案面板会消费本次 verification report 展示这些审计证据;只读 HTTP/API authoring 模板已抽到 `scripts/lib/readonly-http-api-draft-template.mjs`,默认生成 P6 所需 fixture / expected output / dry-run 文件并保留负向 gate 样例;浏览器 mock verification 已对齐同一组 gate 与 evidence,避免无后端 GUI 预览假通过;生成文件不得包含凭证字段;GET/read-only API 可通过 verification,未声明权限、缺 fixture 输入、缺 fixture、缺 expected output、缺 dry-run 入口、dry-run 未绑定 expected output、dry-run actual/expected 不一致、dry-run 入口真实联网、含凭证字段或外部写操作仍失败;Rust capability_draft 全套与 DevBridge smoke 已覆盖正向 / 负向 draft,不发真实 HTTP 请求、不注册、不进入 runtime。
34. P7 第一刀到第四刀已落地:只读 HTTP API 草案在 P6 gate 之外,还必须提供 session authorization policy、受控 `credential_reference` 与 `execution_preflight`,声明未来真实只读 API 执行需要用户 session 授权、只允许 GET、凭证只能从用户 session 配置解析且不进入 generated files,并能在真实请求前生成 approval request 与 evidence schema;正向 preflight check 会把 `preflightMode`、`endpointSource`、`method=GET`、`credentialReferenceId`、`evidenceSchema` 和 `policyPath` 写入 verification evidence,API normalizer 与能力草案面板可直接消费展示;本阶段仍不发真实 HTTP 请求、不保存 token、不新增 connector store 或 runtime。
35. P8 第三刀已落地:注册阶段会读取最新 passed verification report,校验其 report id 与 manifest `lastVerification` 一致,并把 `readonly_http_execution_preflight` evidence 写入 registration summary;registered discovery、浏览器 mock、API normalizer 与 Workspace registered skills 面板会投影 / 展示该 provenance,并持久化 pending `Session approval request artifact`,只记录未来真实 API 执行前需要请求授权的 endpoint 来源、GET 方法、凭证引用、证据 schema、policy 路径和 artifact id;本阶段仍只证明“注册包可追溯授权门禁并可审计授权请求”,不代表真实 HTTP 已可运行。
36. P9 第十一刀已落地:pending `Session approval request artifact` 现在不仅携带 `consumptionGate`、`credentialResolver`、`consumptionInputSchema`、`sessionInputIntake` 与 `sessionInputSubmissionContract`,还通过 `capability_draft_submit_approval_session_inputs` 校验当前 session 的授权确认、runtime endpoint、凭证引用确认和 evidence 捕获确认;校验通过返回 `validated_pending_runtime_gate`,并附带 `controlledGetPreflight.status=ready_for_controlled_get_preflight` 与 `dryPreflightPlan.status=planned_without_execution`;dry plan 只保留 `requestUrlHash=sha256(endpoint)`、planned evidence keys、policy path 和 credential reference;`capability_draft_execute_controlled_get` 只在同一份 session 输入合同通过后执行一次性 GET,并返回 `responseStatus`、`responseSha256`、`executedAt`、截断 response preview 与 evidence;正向和 `request_failed` 结果会额外写入 `.lime/capability-drafts/controlled-get-evidence/<artifact>.json`,只保存 hash、status、response metadata 与 evidence metadata,不保存 endpoint 明文、token 明文或 response preview 正文;`agent_runtime_export_evidence_pack` 现在会按当前 session 读取这些非敏感 artifact,并在 `runtime.json` / `artifacts.json` / `summary.md` 中输出 `capabilityDraftControlledGetEvidence` 可消费摘要;该摘要不复制 endpoint、token 或 response preview,也不注入 runtime、不进入 scheduler/default tool surface;校验失败返回 `blocked` 且不落 artifact。前端 API 网关、Rust command registration、DevBridge dispatcher、治理目录册、mock priority 与浏览器 mock 已同步,UI 仍不显示输入框、token 输入、调度或默认运行入口。
37. P10 第六刀已落地:`completionAuditSummary` 现在消费当前 session 的受控 GET evidence 摘要,输出 artifact / executed / scanned / skipped unsafe 计数、status 分布、`controlledGetEvidenceRequired` 与 `requiredEvidence.controlledGetEvidence`;前端 normalizer 与 Agent envelope presentation 已同步这些字段,Workspace 固化入口会展示受控 GET external evidence 计数,并在 audit 未 completed 时继续显示“等待 Completion Audit”;当 registered skill 带 `readonly_http_execution_preflight` verification gate / approval request 时,Managed Job 草案会声明 `completion_evidence_policy.controlled_get_evidence_required=true`,completion audit 会在缺 executed 受控 GET evidence 时保持 `verifying` 并输出 `missing_controlled_get_evidence`;前端 Agent envelope `evidence_ready` gate 也会防御性要求 `controlledGetEvidenceRequired -> requiredEvidence.controlledGetEvidence`,避免异常 summary 直接打开固化入口;runtime evidence pack 已补端到端回归,证明同一 Read-Only HTTP API policy 在缺受控 GET evidence 时 `verifying`,补齐当前 session executed artifact 后才 `completed`,并继续验证 endpoint / token / response preview 不进入 evidence pack;GUI smoke 已通过,覆盖 workspace ready、browser runtime、site adapters、agent service skill entry、runtime tool surface、knowledge GUI 与 design canvas 主路径;普通非 HTTP 目标仍按原三项 evidence 判定。该信号只让 completion audit / Agent envelope 可见外部只读 evidence,不能单独 completed 或启用固化入口,仍不注入 Query Loop、runtime、scheduler 或 default tool surface。
## 1. 实施总原则
@@ -100,8 +112,8 @@ Level 6: policy-approved scheduled external write
任务:
1. 新增 `docs/research/creaoai/` 研究拆解。
2. 新增 `docs/roadmap/creaoai/` 路线图、实施计划和图纸。
1. 新增 `docs/research/skill-forge/` 研究拆解。
2. 新增 `docs/roadmap/skill-forge/` 路线图、实施计划和图纸。
3. 在文档中固定:`Skill Forge` 是生成阶段,不是 runtime。
4. 在文档中固定:`Generated Capability Draft` 不是长期主类型。
5. 在文档中固定:`Agent envelope` 是 Workspace 产品组合面,不是执行实体。
@@ -142,7 +154,7 @@ Level 6: policy-approved scheduled external write
### 3.0 为什么 P1 必须先做 Coding Agent
CREAO 启发的核心不是已有工具多跑几轮,而是 Coding Agent 能把 CLI / API / docs / website 编译为可复用能力。
Skill Forge 启发的核心不是已有工具多跑几轮,而是 Coding Agent 能把 CLI / API / docs / website 编译为可复用能力。
因此 P1 的最小实现对象应是:
@@ -360,8 +372,8 @@ P1 不做完整独立 Coding Agent。首期只做受控的 `Capability Authoring
- [../../research/codex-goal/README.md](../../research/codex-goal/README.md)
- [../managed-objective/README.md](../managed-objective/README.md)
- [../../research/creaoai/pivot-and-org-harness.md](../../research/creaoai/pivot-and-org-harness.md)
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../research/skill-forge/pivot-and-org-harness.md](../../research/skill-forge/pivot-and-org-harness.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
- [./coding-agent-layer.md](./coding-agent-layer.md)
- [./architecture-review.md](./architecture-review.md)
@@ -1,4 +1,4 @@
# CREAO 启发下的 Skill Forge / Agent Envelope 产品原型图
# Skill Forge 启发下的 Skill Forge / Agent Envelope 产品原型图
> 状态:proposal
> 更新时间:2026-05-06
@@ -10,7 +10,7 @@
- [./implementation-plan.md](./implementation-plan.md)
- [./diagrams.md](./diagrams.md)
- [../managed-objective/prototype.md](../managed-objective/prototype.md)
- [../../research/creaoai/agent-product-model.md](../../research/creaoai/agent-product-model.md)
- [../../research/skill-forge/agent-product-model.md](../../research/skill-forge/agent-product-model.md)
## 1. 原型原则
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "lime",
"version": "1.29.0",
"version": "1.30.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lime",
"version": "1.29.0",
"version": "1.30.0",
"dependencies": {
"@babel/standalone": "^7.29.0",
"@fabianlars/tauri-plugin-oauth": "^2",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "lime",
"private": true,
"version": "1.29.0",
"version": "1.30.0",
"type": "module",
"engines": {
"node": ">=22.0.0"
+1 -1
View File
@@ -112,7 +112,7 @@ npm run build:release -- \
```bash
npm run build:release -- \
--target-triple "aarch64-apple-darwin" \
--version "1.29.0" \
--version "1.30.0" \
--out-dir "./dist"
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@limecloud/lime-cli",
"version": "1.29.0",
"version": "1.30.0",
"description": "Lime 官方任务 CLI",
"bin": {
"lime": "scripts/run.js"
+74 -331
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env node
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { chromium } from "playwright";
const DEFAULTS = {
appUrl: "http://127.0.0.1:1420/",
@@ -10,18 +14,9 @@ const DEFAULTS = {
intervalMs: 1_000,
};
const INVOKE_TIMEOUT_CEILING_MS = 180_000;
const INVOKE_RETRY_COUNT = 10;
const INVOKE_RETRY_DELAY_MS = 1_000;
const BROWSER_ACTION_RETRY_COUNT = 6;
const BROWSER_ACTION_RETRY_DELAY_MS = 1_000;
const BROWSER_SESSION_RECOVERY_LIMIT = 2;
const POST_HEALTH_SETTLE_MS = 1_500;
const POST_LAUNCH_SETTLE_MS = 1_500;
const DEFAULT_ACTION_TIMEOUT_MS = 45_000;
const ONBOARDING_VERSION = "1.1.0";
const PROMPT_TEXT = "请回复一句:smoke harness";
const SMOKE_PROFILE_KEY = "smoke-agent-runtime-tool-surface-page";
const WORKSPACE_HARNESS_DEBUG_OVERRIDE_KEY =
"lime:debug:workspace-harness-enabled:v1";
const RUNTIME_TOOL_AVAILABILITY_OVERRIDE = {
@@ -142,80 +137,6 @@ function logStage(label) {
console.log(`[smoke:agent-runtime-tool-surface-page] stage=${label}`);
}
function deepClone(value) {
return JSON.parse(JSON.stringify(value));
}
function isTransientInvokeError(error) {
return (
error?.name === "TimeoutError" ||
(error instanceof TypeError && error.message === "fetch failed")
);
}
async function invoke(options, cmd, args) {
const invokeTimeoutMs = Math.min(options.timeoutMs, INVOKE_TIMEOUT_CEILING_MS);
const requestInit = {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ cmd, args }),
signal: AbortSignal.timeout(invokeTimeoutMs),
};
for (let attempt = 1; attempt <= INVOKE_RETRY_COUNT; attempt += 1) {
try {
const response = await fetch(options.invokeUrl, requestInit);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const payload = await response.json();
if (payload?.error) {
throw new Error(String(payload.error));
}
return payload?.result;
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
if (!isTransientInvokeError(error) || attempt >= INVOKE_RETRY_COUNT) {
if (error?.name === "TimeoutError") {
throw new Error(
`[smoke:agent-runtime-tool-surface-page] ${cmd} 超时,${invokeTimeoutMs}ms 内未收到 DevBridge 响应`,
);
}
throw new Error(
`[smoke:agent-runtime-tool-surface-page] ${cmd} 请求失败: ${detail}`,
);
}
console.warn(
`[smoke:agent-runtime-tool-surface-page] ${cmd} 第 ${attempt} 次请求失败,${INVOKE_RETRY_DELAY_MS}ms 后重试: ${detail}`,
);
await sleep(INVOKE_RETRY_DELAY_MS);
}
}
throw new Error(
`[smoke:agent-runtime-tool-surface-page] ${cmd} 请求失败: unknown error`,
);
}
async function closeSmokeProfileSession(options, profileKey, label) {
try {
await invoke(options, "close_chrome_profile_session", {
profile_key: profileKey,
});
} catch (error) {
console.warn(
`[smoke:agent-runtime-tool-surface-page] ${label}: ${
error instanceof Error ? error.message : String(error)
}`,
);
}
}
async function waitForHealth(options) {
const startedAt = Date.now();
let lastError = null;
@@ -537,136 +458,6 @@ function buildRuntimeSummaryCheckScript() {
})()`;
}
function extractJavascriptValue(actionResult) {
return (
actionResult?.data?.result ??
actionResult?.data?.value ??
actionResult?.data?.result?.result ??
actionResult?.data?.result?.value ??
actionResult?.data?.result ??
null
);
}
function isRetryableBrowserActionFailure(detail) {
return (
typeof detail === "string" &&
(detail.includes("CDP 调试端口不可用") ||
detail.includes("没有可用的 Chrome 会话") ||
detail.includes("未找到 profile_key="))
);
}
async function launchSmokeBrowserSession(options, profileKey) {
const launchResponse = await invoke(options, "launch_browser_session", {
request: {
profile_key: profileKey,
url: options.appUrl,
headless: true,
open_window: false,
// 真实 Lime 页面在 cdp_direct + frames/both 下会持续产出 frame 流,
// 这里会把后续 Runtime.evaluate 挤到超时;页面 smoke 只需要事件流即可。
stream_mode: "events",
},
});
const sessionId = launchResponse?.session?.session_id ?? null;
assert(
typeof sessionId === "string" && sessionId.trim(),
"launch_browser_session 未返回 session.session_id",
);
await sleep(POST_LAUNCH_SETTLE_MS);
return sessionId;
}
async function runBrowserAction(
options,
profileKey,
action,
args = {},
label = action,
recovery,
) {
for (let attempt = 1; attempt <= BROWSER_ACTION_RETRY_COUNT; attempt += 1) {
const result = await invoke(options, "browser_execute_action", {
request: {
profile_key: profileKey,
backend: "cdp_direct",
action,
args,
timeout_ms: DEFAULT_ACTION_TIMEOUT_MS,
},
});
if (result?.success === true) {
return result;
}
const detail = String(result?.error || JSON.stringify(result ?? null));
if (
isRetryableBrowserActionFailure(detail) &&
attempt < BROWSER_ACTION_RETRY_COUNT
) {
if (recovery && recovery.count < BROWSER_SESSION_RECOVERY_LIMIT) {
recovery.count += 1;
console.warn(
`[smoke:agent-runtime-tool-surface-page] browser_execute_action(${label}) 丢失托管 Chrome 会话,尝试第 ${recovery.count} 次重启: ${detail}`,
);
recovery.sessionId = await launchSmokeBrowserSession(
options,
profileKey,
);
continue;
}
console.warn(
`[smoke:agent-runtime-tool-surface-page] browser_execute_action(${label}) 第 ${attempt} 次失败,${BROWSER_ACTION_RETRY_DELAY_MS}ms 后重试: ${detail}`,
);
await sleep(BROWSER_ACTION_RETRY_DELAY_MS);
continue;
}
throw new Error(
`[smoke:agent-runtime-tool-surface-page] browser_execute_action(${label}) 失败: ${detail}`,
);
}
throw new Error(
`[smoke:agent-runtime-tool-surface-page] browser_execute_action(${label}) 失败: unknown error`,
);
}
async function runJavascript(
options,
profileKey,
expression,
label = "javascript",
recovery,
) {
const result = await runBrowserAction(
options,
profileKey,
"javascript",
{
expression,
return_by_value: true,
},
`javascript:${label}`,
recovery,
);
return extractJavascriptValue(result);
}
async function readPageMarkdown(options, profileKey, recovery) {
const result = await runBrowserAction(
options,
profileKey,
"read_page",
{},
"read_page",
recovery,
);
return String(result?.data?.markdown || "");
}
async function waitForCheck(options, label, check) {
const startedAt = Date.now();
let lastValue = null;
@@ -686,6 +477,46 @@ async function waitForCheck(options, label, check) {
);
}
async function launchPlaywrightContext(userDataDir) {
const launchOptions = {
headless: true,
viewport: { width: 1440, height: 960 },
};
try {
return await chromium.launchPersistentContext(userDataDir, {
...launchOptions,
channel: "chrome",
});
} catch (chromeError) {
console.warn(
`[smoke:agent-runtime-tool-surface-page] Chrome channel 启动失败,尝试 Playwright 自带 Chromium: ${
chromeError instanceof Error ? chromeError.message : String(chromeError)
}`,
);
return chromium.launchPersistentContext(userDataDir, launchOptions);
}
}
async function evaluateScript(page, expression) {
return page.evaluate(expression);
}
async function readPageText(page) {
return page.evaluate(() => {
const text = document.body?.innerText || "";
const fieldText = Array.from(document.querySelectorAll("textarea, input"))
.map((element) =>
element instanceof HTMLTextAreaElement ||
element instanceof HTMLInputElement
? element.value
: "",
)
.join("\n");
return `${text}\n${fieldText}`;
});
}
async function main() {
if (typeof fetch !== "function") {
throw new Error("当前 Node 运行时不支持 fetch,请使用 Node 18+");
@@ -695,34 +526,22 @@ async function main() {
logStage("wait-health");
await waitForHealth(options);
await sleep(POST_HEALTH_SETTLE_MS);
const profileKey = SMOKE_PROFILE_KEY;
const browserRecovery = {
count: 0,
sessionId: null,
};
const userDataDir = fs.mkdtempSync(
path.join(os.tmpdir(), `lime-runtime-tool-surface-page-${process.pid}-`),
);
let context = null;
try {
logStage("cleanup-old-profile");
await closeSmokeProfileSession(
options,
profileKey,
"预清理旧 smoke profile 失败",
);
logStage("launch-browser-session");
browserRecovery.sessionId = await launchSmokeBrowserSession(
options,
profileKey,
);
logStage("launch-playwright-page");
context = await launchPlaywrightContext(userDataDir);
const page = context.pages()[0] ?? (await context.newPage());
await page.goto(options.appUrl, { waitUntil: "domcontentloaded" });
logStage("wait-page-storage-ready");
await waitForCheck(options, "Lime 首页 origin 可访问", async () => {
const value = await runJavascript(
options,
profileKey,
const value = await evaluateScript(
page,
buildPageStorageReadyScript(options.appUrl),
"wait-page-storage-ready",
browserRecovery,
);
return {
ok: value?.ok === true,
@@ -731,31 +550,15 @@ async function main() {
});
logStage("bootstrap-harness-storage");
await runJavascript(
options,
profileKey,
buildHarnessBootstrapScript(),
"bootstrap-harness-storage",
browserRecovery,
);
await evaluateScript(page, buildHarnessBootstrapScript());
logStage("refresh-page");
await runBrowserAction(
options,
profileKey,
"refresh_page",
{},
"refresh_page",
browserRecovery,
);
await page.reload({ waitUntil: "domcontentloaded" });
logStage("wait-empty-state");
await waitForCheck(options, "首页空态加载", async () => {
const text = await runJavascript(
options,
profileKey,
const text = await evaluateScript(
page,
'document.body ? document.body.innerText : ""',
"wait-empty-state-text",
browserRecovery,
);
return {
ok:
@@ -768,13 +571,7 @@ async function main() {
logStage("fill-prompt");
await waitForCheck(options, "首页输入框出现", async () => {
const value = await runJavascript(
options,
profileKey,
buildComposerReadyScript(),
"wait-composer-ready",
browserRecovery,
);
const value = await evaluateScript(page, buildComposerReadyScript());
return {
ok: value?.ok === true,
value,
@@ -785,12 +582,9 @@ async function main() {
options,
"首页输入框可写入",
async () => {
const value = await runJavascript(
options,
profileKey,
const value = await evaluateScript(
page,
buildFillPromptScript(PROMPT_TEXT),
"fill-prompt",
browserRecovery,
);
return {
ok: value?.ok === true,
@@ -805,13 +599,7 @@ async function main() {
logStage("wait-send-ready");
const sendReady = await waitForCheck(options, "发送按钮可用", async () => {
const value = await runJavascript(
options,
profileKey,
buildSendReadyScript(),
"wait-send-ready",
browserRecovery,
);
const value = await evaluateScript(page, buildSendReadyScript());
return {
ok: value?.ok === true,
value,
@@ -823,13 +611,7 @@ async function main() {
);
logStage("click-send");
const submitted = await runJavascript(
options,
profileKey,
buildClickSendScript(),
"click-send",
browserRecovery,
);
const submitted = await evaluateScript(page, buildClickSendScript());
assert(
submitted?.ok === true,
`提交输入失败: ${JSON.stringify(submitted ?? null)}`,
@@ -837,12 +619,9 @@ async function main() {
logStage("wait-harness-button");
await waitForCheck(options, "Harness 按钮出现", async () => {
const value = await runJavascript(
options,
profileKey,
const value = await evaluateScript(
page,
buildWorkbenchButtonCheckScript(),
"wait-harness-button",
browserRecovery,
);
return {
ok: value?.hasButton === true,
@@ -851,13 +630,7 @@ async function main() {
});
logStage("open-harness");
const openWorkbench = await runJavascript(
options,
profileKey,
buildOpenWorkbenchScript(),
"open-harness",
browserRecovery,
);
const openWorkbench = await evaluateScript(page, buildOpenWorkbenchScript());
assert(
openWorkbench?.ok === true,
`打开 Harness 失败: ${JSON.stringify(openWorkbench ?? null)}`,
@@ -868,12 +641,9 @@ async function main() {
options,
"Runtime 能力摘要出现",
async () => {
const value = await runJavascript(
options,
profileKey,
const value = await evaluateScript(
page,
buildRuntimeSummaryCheckScript(),
"check-runtime-summary",
browserRecovery,
);
const hasAllRequired = REQUIRED_RUNTIME_SUMMARY_FLAGS.every(
(key) => value?.[key] === true,
@@ -887,49 +657,22 @@ async function main() {
},
);
logStage("read-page-markdown");
const pageMarkdown = await readPageMarkdown(
options,
profileKey,
browserRecovery,
);
logStage("read-page-text");
const pageText = await readPageText(page);
for (const warning of FORBIDDEN_PAGE_WARNINGS) {
assert(
!pageMarkdown.includes(warning),
!pageText.includes(warning),
`真实页面仍出现不应存在的页级告警: ${warning}`,
);
}
console.log(
`[smoke:agent-runtime-tool-surface-page] 通过 session=${browserRecovery.sessionId} profile=${profileKey}`,
);
console.log("[smoke:agent-runtime-tool-surface-page] 通过");
console.log(
`[smoke:agent-runtime-tool-surface-page] summary=${JSON.stringify(summaryFlags)}`,
);
} finally {
if (browserRecovery.sessionId) {
logStage("close-cdp-session");
try {
await invoke(options, "close_cdp_session", {
request: {
session_id: browserRecovery.sessionId,
},
});
} catch (error) {
console.warn(
`[smoke:agent-runtime-tool-surface-page] 清理浏览器会话失败: ${
error instanceof Error ? error.message : String(error)
}`,
);
}
}
logStage("close-profile-session");
await closeSmokeProfileSession(
options,
profileKey,
"关闭 smoke profile 失败",
);
await context?.close().catch(() => undefined);
fs.rmSync(userDataDir, { recursive: true, force: true });
}
}
+13
View File
@@ -19,11 +19,18 @@ const tauriHeadlessConfigPath = path.join(
"tauri.conf.headless.json",
);
const packageJsonPath = path.join(repoRoot, "package.json");
const cliNpmPackageJsonPath = path.join(
repoRoot,
"packages",
"lime-cli-npm",
"package.json",
);
const cargo = readCargoVersions(cargoTomlPath);
const tauriConfig = readJson(tauriConfigPath);
const tauriHeadlessConfig = readJson(tauriHeadlessConfigPath);
const packageJson = readJson(packageJsonPath);
const cliNpmPackageJson = readJson(cliNpmPackageJsonPath);
const sourceVersion = cargo.workspaceVersion;
const issues = [];
@@ -44,6 +51,12 @@ if ((packageJson.version ?? null) !== sourceVersion) {
);
}
if ((cliNpmPackageJson.version ?? null) !== sourceVersion) {
issues.push(
`packages/lime-cli-npm/package.json version (${cliNpmPackageJson.version ?? "missing"}) 与 workspace.version (${sourceVersion ?? "missing"}) 不一致`,
);
}
if ((tauriConfig.version ?? null) !== sourceVersion) {
issues.push(
`src-tauri/tauri.conf.json version (${tauriConfig.version ?? "missing"}) 与 workspace.version (${sourceVersion ?? "missing"}) 不一致`,
+374
View File
@@ -4,6 +4,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import zlib from "node:zlib";
import { chromium } from "playwright";
const DEFAULTS = {
@@ -12,10 +13,138 @@ const DEFAULTS = {
invokeUrl: "http://127.0.0.1:3030/invoke",
timeoutMs: 180_000,
intervalMs: 1_000,
analyzer: "default",
projectRoundtrip: true,
};
const ACTION_TIMEOUT_MS = 45_000;
const POST_HEALTH_SETTLE_MS = 1_000;
const WORKER_REFINED_TEXT = "WORKER REFINED TEXT";
const WORKER_OCR_TEXT = "WORKER OCR TEXT";
const WORKER_REFINED_SUBJECT_META = "subject / 置信度 93%";
const WORKER_DEFAULT_SUBJECT_META = "subject / 置信度 94%";
const WORKER_MATTING_SUBJECT_META = "subject / 置信度 94%";
const WORKER_OCR_PRIORITY_TEXT =
"OCR priority: Smoke failing OCR provider -> Smoke empty OCR provider -> Smoke OCR priority browser Worker provider";
const WORKER_CLEAN_PLATE_SOURCE =
"背景修补来源:Simple browser clean plate provider / simple_neighbor_inpaint_v1";
const WORKER_MODEL_SLOT_TEXT = "WORKER OCR TEXT";
const WORKER_MODEL_SLOT_SUBJECT_META = "subject / 置信度 94%";
const WORKER_MODEL_SLOT_CLEAN_PLATE_SOURCE =
"背景修补来源:Simple browser clean plate provider / simple_neighbor_inpaint_v1";
const WORKER_MODEL_SLOT_JSON_EXECUTOR_FIXTURE =
"Analyzer model slots provider JSON executor fixture";
const WORKER_MODEL_SLOT_NATIVE_OCR_JSON_EXECUTOR_FIXTURE =
"Analyzer model slots native OCR JSON executor fixture";
const WORKER_MODEL_SLOT_NATIVE_OCR_PRIORITY =
"OCR priority: Tauri native OCR -> Worker OCR provider via model slot JSON executor";
const WORKER_MODEL_SLOT_EXPORT_RELATIVE_PATH =
".lime/layered-designs/design-canvas-smoke.layered-design";
const ANALYZER_MODES = new Set([
"default",
"worker",
"worker-refined",
"worker-matting",
"worker-ocr",
"worker-ocr-priority",
"worker-clean-plate",
"worker-model-slots",
"worker-model-slots-native-ocr",
"native",
]);
const ANALYZER_BADGE_TEXT = {
default: "默认 analyzer",
native: "Native analyzer 已启用",
worker: "Worker analyzer 已启用",
"worker-refined": "Worker refined analyzer 已启用",
"worker-matting": "Worker subject matting analyzer 已启用",
"worker-ocr": "Worker OCR analyzer 已启用",
"worker-ocr-priority": "Worker OCR priority analyzer 已启用",
"worker-clean-plate": "Worker clean plate analyzer 已启用",
"worker-model-slots": "Worker model slots analyzer 已启用",
"worker-model-slots-native-ocr":
"Worker model slots native OCR analyzer 已启用",
};
const ANALYZER_RESULT_TEXT = {
default: "Worker local heuristic analyzer",
native: "Tauri native heuristic analyzer",
worker: "Worker local heuristic analyzer",
"worker-refined": "Worker local heuristic analyzer",
"worker-matting": "Worker local heuristic analyzer",
"worker-ocr": "Worker local heuristic analyzer",
"worker-ocr-priority": "Worker local heuristic analyzer",
"worker-clean-plate": "Worker local heuristic analyzer",
"worker-model-slots": "Worker local heuristic analyzer",
"worker-model-slots-native-ocr": "Worker local heuristic analyzer",
};
const ANALYZER_EXTRA_CHECK = {
default: {
subjectCandidateName: /[☑☐]\s*主体候选/,
subjectMeta: WORKER_DEFAULT_SUBJECT_META,
cleanPlateSource: WORKER_CLEAN_PLATE_SOURCE,
},
worker: {
subjectCandidateName: /[☑☐]\s*主体候选/,
subjectMeta: WORKER_DEFAULT_SUBJECT_META,
cleanPlateSource: WORKER_CLEAN_PLATE_SOURCE,
},
"worker-refined": {
subjectCandidateName: /[☑☐]\s*主体候选/,
subjectMeta: WORKER_REFINED_SUBJECT_META,
textCandidateName: /[☑☐]\s*标题文字候选/,
text: WORKER_REFINED_TEXT,
},
"worker-matting": {
subjectCandidateName: /[☑☐]\s*主体候选/,
subjectMeta: WORKER_MATTING_SUBJECT_META,
},
"worker-ocr": {
textCandidateName: /[☑☐]\s*标题文字候选/,
text: WORKER_OCR_TEXT,
},
"worker-ocr-priority": {
priorityText: WORKER_OCR_PRIORITY_TEXT,
textCandidateName: /[☑☐]\s*标题文字候选/,
text: WORKER_OCR_TEXT,
},
"worker-clean-plate": {
cleanPlateSource: WORKER_CLEAN_PLATE_SOURCE,
},
"worker-model-slots": {
fixtureText: WORKER_MODEL_SLOT_JSON_EXECUTOR_FIXTURE,
subjectCandidateName: /[☑☐]\s*主体候选/,
subjectMeta: WORKER_MODEL_SLOT_SUBJECT_META,
textCandidateName: /[☑☐]\s*标题文字候选/,
text: WORKER_MODEL_SLOT_TEXT,
cleanPlateSource: WORKER_MODEL_SLOT_CLEAN_PLATE_SOURCE,
capabilityText: "3 项 / 均生产可用",
modelSlotExecutionTexts: [
"模型执行",
"主体抠图:smoke-subject-matting-slot-v1 / attempt 1/1 / succeeded",
"背景修补:smoke-clean-plate-slot-v1 / attempt 1/1 / succeeded",
"OCR TextLayer:smoke-ocr-slot-v1 / attempt 1/1 / succeeded",
],
},
"worker-model-slots-native-ocr": {
fixtureText: WORKER_MODEL_SLOT_NATIVE_OCR_JSON_EXECUTOR_FIXTURE,
priorityText: WORKER_MODEL_SLOT_NATIVE_OCR_PRIORITY,
subjectCandidateName: /[☑☐]\s*主体候选/,
subjectMeta: WORKER_MODEL_SLOT_SUBJECT_META,
textCandidateName: /[☑☐]\s*标题文字候选/,
text: WORKER_MODEL_SLOT_TEXT,
cleanPlateSource: WORKER_MODEL_SLOT_CLEAN_PLATE_SOURCE,
capabilityText: "3 项 / 均生产可用",
modelSlotExecutionTexts: [
"模型执行",
"主体抠图:smoke-subject-matting-slot-v1 / attempt 1/1 / succeeded",
"背景修补:smoke-clean-plate-slot-v1 / attempt 1/1 / succeeded",
"OCR TextLayer:smoke-ocr-slot-v1 / attempt 1/1 / succeeded",
],
},
};
function printHelp() {
console.log(`
@@ -34,6 +163,9 @@ Lime Design Canvas Smoke
--invoke-url <url> DevBridge invoke 地址,默认 http://127.0.0.1:3030/invoke
--timeout-ms <ms> 总超时,默认 180000
--interval-ms <ms> 轮询间隔,默认 1000
--analyzer <mode> analyzer 注入模式:default / worker / worker-refined / worker-matting / worker-ocr / worker-ocr-priority / worker-clean-plate / worker-model-slots / worker-model-slots-native-ocr / native,默认 default(产品默认 worker-first)
--project-roundtrip 上传拆层前验证 prompt seed 工程保存与重新打开(默认开启)
--skip-project-roundtrip 跳过工程保存/重新打开,仅用于定位非持久化链路问题
-h, --help 显示帮助
`);
}
@@ -74,10 +206,26 @@ function parseArgs(argv) {
continue;
}
if (arg === "--analyzer" && argv[index + 1]) {
options.analyzer = String(argv[index + 1]).trim();
index += 1;
continue;
}
if (arg === "--help" || arg === "-h") {
printHelp();
process.exit(0);
}
if (arg === "--project-roundtrip") {
options.projectRoundtrip = true;
continue;
}
if (arg === "--skip-project-roundtrip") {
options.projectRoundtrip = false;
continue;
}
}
if (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 30_000) {
@@ -89,6 +237,11 @@ function parseArgs(argv) {
if (!options.appUrl || !options.healthUrl || !options.invokeUrl) {
throw new Error("--app-url、--health-url、--invoke-url 均不能为空");
}
if (!ANALYZER_MODES.has(options.analyzer)) {
throw new Error(
"--analyzer 必须是 worker、worker-refined、worker-matting、worker-ocr、worker-ocr-priority、worker-clean-plate、worker-model-slots、worker-model-slots-native-ocr、native 或 default",
);
}
return options;
}
@@ -103,6 +256,68 @@ function assert(condition, message) {
}
}
function crc32(buffer) {
let crc = 0xffffffff;
for (const byte of buffer) {
crc ^= byte;
for (let bit = 0; bit < 8; bit += 1) {
crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);
}
}
return (crc ^ 0xffffffff) >>> 0;
}
function pngChunk(type, data) {
const typeBuffer = Buffer.from(type, "ascii");
const length = Buffer.alloc(4);
length.writeUInt32BE(data.length, 0);
const checksum = Buffer.alloc(4);
checksum.writeUInt32BE(crc32(Buffer.concat([typeBuffer, data])), 0);
return Buffer.concat([length, typeBuffer, data, checksum]);
}
function createSmokeFlatImagePngBuffer(width = 360, height = 560) {
const header = Buffer.alloc(13);
header.writeUInt32BE(width, 0);
header.writeUInt32BE(height, 4);
header[8] = 8; // bit depth
header[9] = 6; // RGBA
const rowSize = width * 4 + 1;
const raw = Buffer.alloc(rowSize * height);
for (let y = 0; y < height; y += 1) {
const rowOffset = y * rowSize;
raw[rowOffset] = 0;
for (let x = 0; x < width; x += 1) {
const offset = rowOffset + 1 + x * 4;
const inHeadline = y > height * 0.06 && y < height * 0.2;
const inSubject =
x > width * 0.18 &&
x < width * 0.82 &&
y > height * 0.24 &&
y < height * 0.82;
const inAccent = x > width * 0.68 && y > height * 0.7;
raw[offset] = inSubject ? 38 : inHeadline ? 18 : inAccent ? 245 : 238;
raw[offset + 1] = inSubject ? 86 : inHeadline ? 24 : inAccent ? 163 : 242;
raw[offset + 2] = inSubject ? 142 : inHeadline ? 42 : inAccent ? 72 : 234;
raw[offset + 3] = 255;
}
}
return Buffer.concat([
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
pngChunk("IHDR", header),
pngChunk("IDAT", zlib.deflateSync(raw)),
pngChunk("IEND", Buffer.alloc(0)),
]);
}
function logStage(label) {
console.log(`[smoke:design-canvas] stage=${label}`);
}
@@ -200,10 +415,54 @@ async function resolveDefaultWorkspace(options) {
};
}
async function assertWorkerModelSlotsManifest(page, workspace) {
assert(workspace.rootPath, "worker-model-slots manifest 断言缺少 workspace rootPath");
const output = await page.evaluate(
async ({ projectRootPath, exportDirectoryRelativePath }) => {
const { readLayeredDesignProjectExport } = await import(
"/src/lib/api/layeredDesignProject.ts"
);
return readLayeredDesignProjectExport({
projectRootPath,
exportDirectoryRelativePath,
});
},
{
projectRootPath: workspace.rootPath,
exportDirectoryRelativePath: WORKER_MODEL_SLOT_EXPORT_RELATIVE_PATH,
},
);
const manifestJson = pickStringField(output, "manifestJson", "manifest_json");
assert(manifestJson, "worker-model-slots 工程导出缺少 manifestJson");
const manifest = JSON.parse(manifestJson);
const slots = Array.isArray(manifest?.analyzerModelSlots)
? manifest.analyzerModelSlots
: [];
const slotIds = slots.map((slot) => String(slot?.config?.id ?? ""));
assert(
slotIds.includes("smoke-subject-matting-slot") &&
slotIds.includes("smoke-clean-plate-slot") &&
slotIds.includes("smoke-ocr-slot"),
`worker-model-slots manifest 缺少 analyzerModelSlots: ${JSON.stringify(
slotIds,
)}`,
);
assert(
slots.every((slot) => slot?.readiness?.valid === true),
`worker-model-slots manifest readiness 未全部通过: ${JSON.stringify(
slots.map((slot) => slot?.readiness),
)}`,
);
}
function buildSmokeUrl(options, workspace) {
const url = new URL("/design-canvas-smoke", options.appUrl);
url.searchParams.set("projectRootPath", workspace.rootPath);
url.searchParams.set("projectId", workspace.projectId);
url.searchParams.set("analyzer", options.analyzer);
return url.toString();
}
@@ -277,6 +536,11 @@ async function runPageFlow(options, smokeUrl) {
await waitForText(page, "smoke 标题", "canvas:design 专属 GUI Smoke");
await waitForText(page, "artifact 类型", "canvas:design");
await waitForText(page, "事实源标记", "LayeredDesignDocument");
await waitForText(
page,
"analyzer 标记",
ANALYZER_BADGE_TEXT[options.analyzer],
);
await waitForText(page, "画布标题", "Smoke 图层设计海报");
await waitForText(page, "图层栏", "图层");
await waitForText(page, "属性栏", "属性");
@@ -301,6 +565,116 @@ async function runPageFlow(options, smokeUrl) {
timeout: ACTION_TIMEOUT_MS,
});
if (options.projectRoundtrip) {
logStage("project-roundtrip-save-open");
await page.getByRole("button", { name: "导出设计工程", exact: true }).click({
timeout: ACTION_TIMEOUT_MS,
});
await waitForText(page, "工程目录保存结果", "已保存图层设计工程");
await waitForText(page, "工程目录保存路径", "design-canvas-smoke.layered-design");
if (
options.analyzer === "worker-model-slots" ||
options.analyzer === "worker-model-slots-native-ocr"
) {
const smokeUrlObject = new URL(smokeUrl);
await assertWorkerModelSlotsManifest(page, {
rootPath: smokeUrlObject.searchParams.get("projectRootPath") ?? "",
});
}
await page.getByRole("button", { name: "打开最近工程", exact: true }).click({
timeout: ACTION_TIMEOUT_MS,
});
await waitForText(page, "工程目录恢复结果", "已打开图层设计工程");
await waitForText(page, "恢复后画布标题", "Smoke 图层设计海报");
await waitForText(page, "恢复后图层栏", "主标题");
}
logStage("upload-flat-image-extraction");
await page
.locator('[data-testid="design-canvas-flat-image-input"]')
.setInputFiles({
name: "smoke-flat-image.png",
mimeType: "image/png",
buffer: createSmokeFlatImagePngBuffer(),
});
await waitForText(page, "上传扁平图结果", "已载入扁平图 draft");
const expectedAnalyzerResult = ANALYZER_RESULT_TEXT[options.analyzer];
if (expectedAnalyzerResult) {
await waitForText(
page,
"analyzer 执行结果",
expectedAnalyzerResult,
);
}
const extraCheck = ANALYZER_EXTRA_CHECK[options.analyzer];
if (extraCheck) {
if (extraCheck.fixtureText) {
await waitForText(
page,
"Worker model slots JSON executor fixture",
extraCheck.fixtureText,
);
}
if (extraCheck.priorityText) {
await waitForText(
page,
"Worker OCR priority provider 标记",
extraCheck.priorityText,
);
}
if (extraCheck.subjectCandidateName && extraCheck.subjectMeta) {
await page
.getByRole("button", { name: extraCheck.subjectCandidateName })
.click({ timeout: ACTION_TIMEOUT_MS });
await waitForText(
page,
"Worker subject matting 输出",
extraCheck.subjectMeta,
);
}
if (extraCheck.textCandidateName && extraCheck.text) {
await page
.getByRole("button", { name: extraCheck.textCandidateName })
.click({ timeout: ACTION_TIMEOUT_MS });
await waitForText(
page,
"Worker refined TextLayer 输出",
extraCheck.text,
);
}
if (extraCheck.cleanPlateSource) {
await waitForText(
page,
"Worker clean plate provider 来源",
extraCheck.cleanPlateSource,
);
}
if (extraCheck.capabilityText) {
await waitForText(
page,
"Worker model slots capability 矩阵",
extraCheck.capabilityText,
);
}
if (extraCheck.modelSlotExecutionTexts) {
for (const text of extraCheck.modelSlotExecutionTexts) {
await waitForText(page, "Worker model slots 执行证据", text);
}
}
}
await waitForText(page, "拆层确认面板", "拆层确认");
await waitForText(page, "进入编辑入口", "进入图层编辑");
await waitForText(page, "候选图层", "候选图层");
await page.getByRole("button", { name: "进入图层编辑", exact: true }).click({
timeout: ACTION_TIMEOUT_MS,
});
await page
.getByRole("button", { name: "进入图层编辑", exact: true })
.waitFor({ state: "hidden", timeout: ACTION_TIMEOUT_MS });
await waitForText(page, "确认后属性面板", "位置与尺寸");
if (consoleErrors.length > 0) {
throw new Error(
`[smoke:design-canvas] 页面存在 ${consoleErrors.length} 条 console error: ${JSON.stringify(
+10 -3
View File
@@ -350,6 +350,13 @@ async function clickScopedButton(page, { scope, text, ariaLabel, index = 0 }) {
}
}
async function openKnowledgePageFromMainNav(page) {
await clickScopedButton(page, {
scope: '[data-testid="app-sidebar-main-nav"]',
ariaLabel: "项目资料",
});
}
async function waitForKnowledgePack(options, label, matcher) {
const startedAt = Date.now();
let lastError = null;
@@ -557,7 +564,7 @@ async function runPlaywrightGuiFlow(options) {
await clickPageControl(page, { ariaLabel: "关闭文件管理器" });
logStage("open-knowledge-page");
await clickPageControl(page, { ariaLabel: "项目资料" });
await openKnowledgePageFromMainNav(page);
logStage("wait-knowledge-overview");
await waitForPageText(
@@ -606,7 +613,7 @@ async function runPlaywrightGuiFlow(options) {
}
logStage("return-knowledge-before-agent-result");
await clickPageControl(page, { ariaLabel: "项目资料" });
await openKnowledgePageFromMainNav(page);
logStage("prepare-agent-result");
await seedAgentResultForKnowledgeCapture(page, options);
@@ -636,7 +643,7 @@ async function runPlaywrightGuiFlow(options) {
);
logStage("return-knowledge-page");
await clickPageControl(page, { ariaLabel: "项目资料" });
await openKnowledgePageFromMainNav(page);
logStage("wait-captured-agent-result");
await waitForPageText(
+31 -20
View File
@@ -1,7 +1,8 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import process from "node:process";
import { execFile } from "node:child_process";
import { afterEach, describe, expect, it } from "vitest";
const repoRoot = process.cwd();
@@ -29,8 +30,10 @@ function writeText(filePath: string, value: string) {
fs.writeFileSync(filePath, value, "utf8");
}
function sleepMs(durationMs: number) {
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, durationMs);
function delayMs(durationMs: number) {
return new Promise<void>((resolve) => {
setTimeout(resolve, durationMs);
});
}
function createHarnessManifest(caseDir: string) {
@@ -137,16 +140,24 @@ function createReplayFixture(tempRoot: string) {
}
function runNodeScript(scriptRelativePath: string, args: string[]) {
const output = execFileSync(
process.execPath,
[path.join(repoRoot, scriptRelativePath), ...args],
{
cwd: repoRoot,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
},
);
return JSON.parse(output);
return new Promise<any>((resolve, reject) => {
execFile(
process.execPath,
[path.join(repoRoot, scriptRelativePath), ...args],
{
cwd: repoRoot,
encoding: "utf8",
},
(error, stdout) => {
if (error) {
reject(error);
return;
}
resolve(JSON.parse(stdout));
},
);
});
}
afterEach(() => {
@@ -159,7 +170,7 @@ afterEach(() => {
});
describe("Harness eval history window", () => {
it("history-record 应记录并裁剪历史窗口,trend 应复用该目录", () => {
it("history-record 应记录并裁剪历史窗口,trend 应复用该目录", async () => {
const tempRoot = createTempRoot();
const caseDir = createReplayFixture(tempRoot);
const manifestPath = path.join(tempRoot, "manifest.json");
@@ -167,7 +178,7 @@ describe("Harness eval history window", () => {
writeJson(manifestPath, createHarnessManifest(caseDir));
runNodeScript("scripts/harness-eval-history-record.mjs", [
await runNodeScript("scripts/harness-eval-history-record.mjs", [
"--format",
"json",
"--manifest",
@@ -177,8 +188,8 @@ describe("Harness eval history window", () => {
"--retain",
"2",
]);
sleepMs(10);
runNodeScript("scripts/harness-eval-history-record.mjs", [
await delayMs(10);
await runNodeScript("scripts/harness-eval-history-record.mjs", [
"--format",
"json",
"--manifest",
@@ -188,8 +199,8 @@ describe("Harness eval history window", () => {
"--retain",
"2",
]);
sleepMs(10);
runNodeScript("scripts/harness-eval-history-record.mjs", [
await delayMs(10);
await runNodeScript("scripts/harness-eval-history-record.mjs", [
"--format",
"json",
"--manifest",
@@ -206,7 +217,7 @@ describe("Harness eval history window", () => {
.sort();
expect(historyFiles).toHaveLength(2);
const report = runNodeScript("scripts/harness-eval-trend-report.mjs", [
const report = await runNodeScript("scripts/harness-eval-trend-report.mjs", [
"--format",
"json",
"--manifest",
@@ -0,0 +1,239 @@
export function buildReadonlyHttpApiGeneratedFiles({
includeFixture = true,
includeExpectedOutput = true,
includeFixtureInput = true,
includeSessionAuthorization = true,
includeCredentialReference = true,
includeExecutionPreflight = true,
includeDryRunEntry = true,
includeDryRunExpectedOutputBinding = true,
includeDryRunMismatch = false,
includeNetworkedDryRun = false,
includeCredentialHeader = false,
} = {}) {
const files = [
{
relativePath: "SKILL.md",
content: [
"---",
"name: 只读 HTTP API 每日报告",
"description: 把公开只读 HTTP API 响应整理成 Markdown 趋势摘要。",
"---",
"",
"# 只读 HTTP API 每日报告",
"",
"## 何时使用",
"当用户需要读取公开只读 API 并生成趋势报告时使用。",
"",
"## 输入",
"- endpoint: 只读 API 地址。",
"- fixture_path: P6 dry-run 必须提供的本地 fixture 路径。",
"",
"## 执行步骤",
"1. 仅允许 GET / fixture dry-run。",
"2. 不发送 POST / PUT / PATCH / DELETE。",
"3. 基于响应或 fixture 生成 Markdown 趋势摘要。",
"",
"## 输出",
"- markdown_report: Markdown 趋势摘要。",
"",
"## 权限边界",
"只读 HTTP API 访问;不保存 token,不发布,不删除,不写外部系统。",
].join("\n"),
},
{
relativePath: "contract/input.schema.json",
content: JSON.stringify(
{
type: "object",
required: ["endpoint"],
properties: {
endpoint: { type: "string", format: "uri" },
...(includeFixtureInput ? { fixture_path: { type: "string" } } : {}),
},
additionalProperties: false,
},
null,
2,
),
},
{
relativePath: "contract/output.schema.json",
content: JSON.stringify(
{
type: "object",
required: ["markdown_report"],
properties: {
markdown_report: { type: "string" },
evidence_notes: { type: "array", items: { type: "string" } },
},
additionalProperties: false,
},
null,
2,
),
},
{
relativePath: "examples/input.sample.json",
content: JSON.stringify(
includeFixtureInput
? {
endpoint: "https://api.example.test/metrics",
fixture_path: "tests/fixture.json",
}
: {
endpoint: "https://api.example.test/metrics",
},
null,
2,
),
},
{
relativePath: "scripts/README.md",
content: [
"# 只读 HTTP API wrapper",
"",
"P6 第一刀不发真实 HTTP 请求;只验证 GET / read-only API draft 的权限 gate。",
"接入真实网络前必须继续使用 fixture dry-run、用户配置、session 授权和 evidence 记录。",
].join("\n"),
},
];
if (includeFixture) {
files.splice(4, 0, {
relativePath: "tests/fixture.json",
content: JSON.stringify(
{
metrics: [
{ label: "workflow", value: 42 },
{ label: "approval", value: 18 },
{ label: "audit", value: 12 },
],
},
null,
2,
),
});
}
if (includeExpectedOutput) {
files.splice(includeFixture ? 5 : 4, 0, {
relativePath: "tests/expected-output.json",
content: JSON.stringify(
{
markdown_report: "# 趋势摘要\n\n- workflow: 42\n- approval: 18\n- audit: 12",
evidence_notes: ["fixture dry-run only"],
},
null,
2,
),
});
}
if (includeSessionAuthorization) {
const insertIndex = files.findIndex((file) => file.relativePath === "scripts/README.md");
files.splice(insertIndex === -1 ? files.length : insertIndex, 0, {
relativePath: "policy/readonly-http-session.json",
content: JSON.stringify(
{
mode: "session_required",
access: "read-only",
allowed_methods: ["GET"],
credential_policy: "no_generated_credentials",
credential_source: "user_session_config",
...(includeCredentialReference
? {
credential_reference: {
scope: "session",
source: "user_session_config",
required: false,
reference_id: "readonly_api_session",
},
}
: {}),
...(includeExecutionPreflight
? {
execution_preflight: {
mode: "approval_request",
endpoint_source: "runtime_input",
method: "GET",
...(includeCredentialReference
? { credential_reference_id: "readonly_api_session" }
: {}),
evidence_schema: [
"request_url_hash",
"request_method",
"response_status",
"response_sha256",
"executed_at",
],
},
}
: {}),
evidence: [
"request_url_hash",
"response_status",
"response_sha256",
"fixture_fallback",
],
},
null,
2,
),
});
}
if (includeDryRunEntry) {
files.push({
relativePath: "scripts/dry-run.mjs",
content: includeNetworkedDryRun
? 'await fetch("https://api.example.test/metrics", { method: "GET" });'
: [
"import fs from 'node:fs';",
"const input = JSON.parse(fs.readFileSync('examples/input.sample.json', 'utf8'));",
"const fixture = JSON.parse(fs.readFileSync(input.fixture_path, 'utf8'));",
...(includeDryRunExpectedOutputBinding
? ["const expected = JSON.parse(fs.readFileSync('tests/expected-output.json', 'utf8'));"]
: []),
"const metrics = Array.isArray(fixture.metrics) ? fixture.metrics : [];",
"const markdown = ['# 趋势摘要', '', ...metrics.map((item) => `- ${item.label}: ${item.value}`)].join('\\n');",
includeDryRunMismatch
? "const actual = { markdown_report: '# 趋势摘要\\n\\n- mismatch: 0', evidence_notes: ['fixture dry-run only'] };"
: "const actual = { markdown_report: markdown, evidence_notes: ['fixture dry-run only'] };",
...(includeDryRunExpectedOutputBinding
? [
"if (actual.markdown_report !== expected.markdown_report) throw new Error('dry-run output mismatch');",
]
: []),
"console.log(JSON.stringify(actual));",
].join("\n"),
});
}
if (includeCredentialHeader) {
files.push({
relativePath: "scripts/client.ts",
content:
'await fetch(endpoint, { method: "GET", headers: { Authorization: `Bearer ${token}` } });',
});
}
return files;
}
export function buildReadonlyHttpApiCreateRequest(
workspaceRoot,
permissionSummary,
options = {},
) {
return {
workspaceRoot,
name: "只读 HTTP API 每日报告",
description: "把公开只读 HTTP API 响应整理成 Markdown 趋势摘要。",
userGoal: "每天 9 点读取公开只读 API 或 fixture,生成 Markdown 趋势摘要。",
sourceKind: "api",
sourceRefs: ["docs/exec-plans/skill-forge-readonly-http-api-p7-plan.md"],
permissionSummary,
generatedFiles: buildReadonlyHttpApiGeneratedFiles(options),
};
}
@@ -0,0 +1,159 @@
import { describe, expect, it } from "vitest";
import {
buildReadonlyHttpApiCreateRequest,
buildReadonlyHttpApiGeneratedFiles,
} from "./readonly-http-api-draft-template.mjs";
function contentByPath(files: Array<{ relativePath: string; content: string }>) {
return new Map(files.map((file) => [file.relativePath, file.content]));
}
describe("readonly-http-api-draft-template", () => {
it("默认生成 P6 verification 可接受的只读 HTTP/API 草案文件", () => {
const files = buildReadonlyHttpApiGeneratedFiles();
const contents = contentByPath(files);
expect([...contents.keys()]).toEqual([
"SKILL.md",
"contract/input.schema.json",
"contract/output.schema.json",
"examples/input.sample.json",
"tests/fixture.json",
"tests/expected-output.json",
"policy/readonly-http-session.json",
"scripts/README.md",
"scripts/dry-run.mjs",
]);
const inputSchema = JSON.parse(
contents.get("contract/input.schema.json") || "{}",
);
const sampleInput = JSON.parse(
contents.get("examples/input.sample.json") || "{}",
);
expect(inputSchema.properties.fixture_path.type).toBe("string");
expect(sampleInput.fixture_path).toBe("tests/fixture.json");
expect(contents.get("tests/expected-output.json")).toContain(
"fixture dry-run only",
);
const sessionPolicy = JSON.parse(
contents.get("policy/readonly-http-session.json") || "{}",
);
expect(sessionPolicy).toMatchObject({
mode: "session_required",
access: "read-only",
allowed_methods: ["GET"],
credential_policy: "no_generated_credentials",
credential_reference: {
scope: "session",
source: "user_session_config",
required: false,
reference_id: "readonly_api_session",
},
execution_preflight: {
mode: "approval_request",
endpoint_source: "runtime_input",
method: "GET",
credential_reference_id: "readonly_api_session",
},
});
expect(sessionPolicy.execution_preflight.evidence_schema).toContain(
"response_sha256",
);
expect(sessionPolicy.evidence).toContain("response_sha256");
expect(contents.get("scripts/dry-run.mjs")).toContain(
"tests/expected-output.json",
);
expect(contents.get("scripts/dry-run.mjs")).toContain("console.log");
expect(contents.get("scripts/dry-run.mjs")).not.toContain("fetch(");
expect(contents.get("scripts/dry-run.mjs")).not.toContain("https://");
expect(contents.get("SKILL.md")).toContain("不保存 token");
});
it("生成 capability_draft_create 请求时固定 api source 与只读权限输入", () => {
const request = buildReadonlyHttpApiCreateRequest("/tmp/work", [
"Level 0 只读发现",
"允许只读 HTTP API GET 请求,不做外部写操作",
]);
expect(request).toMatchObject({
workspaceRoot: "/tmp/work",
sourceKind: "api",
sourceRefs: ["docs/exec-plans/skill-forge-readonly-http-api-p7-plan.md"],
permissionSummary: [
"Level 0 只读发现",
"允许只读 HTTP API GET 请求,不做外部写操作",
],
});
expect(request.generatedFiles.map((file) => file.relativePath)).toContain(
"scripts/dry-run.mjs",
);
});
it("负向样例开关能稳定构造各个 P6 gate 的失败草案", () => {
const missingFixtureInput = contentByPath(
buildReadonlyHttpApiGeneratedFiles({ includeFixtureInput: false }),
);
expect(
missingFixtureInput.get("contract/input.schema.json"),
).not.toContain("fixture_path");
expect(missingFixtureInput.get("examples/input.sample.json")).not.toContain(
"fixture_path",
);
expect(
buildReadonlyHttpApiGeneratedFiles({ includeExpectedOutput: false }).some(
(file) => file.relativePath === "tests/expected-output.json",
),
).toBe(false);
expect(
buildReadonlyHttpApiGeneratedFiles({ includeDryRunEntry: false }).some(
(file) => file.relativePath === "scripts/dry-run.mjs",
),
).toBe(false);
expect(
buildReadonlyHttpApiGeneratedFiles({
includeSessionAuthorization: false,
}).some((file) => file.relativePath === "policy/readonly-http-session.json"),
).toBe(false);
expect(
contentByPath(
buildReadonlyHttpApiGeneratedFiles({
includeCredentialReference: false,
}),
)
.get("policy/readonly-http-session.json")
?.includes("credential_reference"),
).toBe(false);
expect(
contentByPath(
buildReadonlyHttpApiGeneratedFiles({
includeExecutionPreflight: false,
}),
)
.get("policy/readonly-http-session.json")
?.includes("execution_preflight"),
).toBe(false);
expect(
contentByPath(
buildReadonlyHttpApiGeneratedFiles({
includeDryRunExpectedOutputBinding: false,
}),
).get("scripts/dry-run.mjs"),
).not.toContain("tests/expected-output.json");
expect(
contentByPath(
buildReadonlyHttpApiGeneratedFiles({ includeNetworkedDryRun: true }),
).get("scripts/dry-run.mjs"),
).toContain("fetch(");
expect(
contentByPath(
buildReadonlyHttpApiGeneratedFiles({ includeDryRunMismatch: true }),
).get("scripts/dry-run.mjs"),
).toContain("mismatch: 0");
expect(
contentByPath(
buildReadonlyHttpApiGeneratedFiles({ includeCredentialHeader: true }),
).get("scripts/client.ts"),
).toContain("Authorization");
});
});
+420
View File
@@ -0,0 +1,420 @@
#!/usr/bin/env node
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import process from "node:process";
const DEFAULTS = {
healthUrl: "http://127.0.0.1:3030/health",
invokeUrl: "http://127.0.0.1:3030/invoke",
timeoutMs: 120_000,
intervalMs: 1_000,
workspaceRoot: "",
cleanup: false,
json: false,
};
function printHelp() {
console.log(`
Prompt-to-Artifact Smoke
用途:
用临时 workspace 走通 read-only Capability Draft -> verification -> registration -> discovery -> binding readiness。
该 smoke 只验证 P5 第一刀的 prompt-to-artifact 前半链,不执行外部 CLI、不联网、不创建长期自动化。
用法:
node scripts/prompt-to-artifact-smoke.mjs [选项]
选项:
--health-url <url> DevBridge 健康检查地址,默认 http://127.0.0.1:3030/health
--invoke-url <url> DevBridge invoke 地址,默认 http://127.0.0.1:3030/invoke
--timeout-ms <ms> 等待 DevBridge 超时,默认 120000
--interval-ms <ms> 轮询间隔,默认 1000
--workspace-root <dir> 指定 smoke workspace;默认创建临时目录
--cleanup 成功或失败后删除本脚本创建的临时 workspace
--json 只输出 JSON summary
-h, --help 显示帮助
示例:
node scripts/prompt-to-artifact-smoke.mjs --timeout-ms 120000
`);
}
function parseArgs(argv) {
const options = { ...DEFAULTS };
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--health-url" && argv[index + 1]) {
options.healthUrl = String(argv[++index]).trim();
continue;
}
if (arg === "--invoke-url" && argv[index + 1]) {
options.invokeUrl = String(argv[++index]).trim();
continue;
}
if (arg === "--timeout-ms" && argv[index + 1]) {
options.timeoutMs = Number(argv[++index]);
continue;
}
if (arg === "--interval-ms" && argv[index + 1]) {
options.intervalMs = Number(argv[++index]);
continue;
}
if (arg === "--workspace-root" && argv[index + 1]) {
options.workspaceRoot = path.resolve(String(argv[++index]).trim());
continue;
}
if (arg === "--cleanup") {
options.cleanup = true;
continue;
}
if (arg === "--json") {
options.json = true;
continue;
}
if (arg === "--help" || arg === "-h") {
printHelp();
process.exit(0);
}
throw new Error(`未知参数: ${arg}`);
}
if (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 1_000) {
throw new Error("--timeout-ms 必须是 >= 1000 的数字");
}
if (!Number.isFinite(options.intervalMs) || options.intervalMs < 100) {
throw new Error("--interval-ms 必须是 >= 100 的数字");
}
return options;
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
function pickString(target, ...keys) {
for (const key of keys) {
const value = target?.[key];
if (typeof value === "string" && value.trim()) {
return value.trim();
}
}
return "";
}
function pickArray(target, ...keys) {
for (const key of keys) {
const value = target?.[key];
if (Array.isArray(value)) {
return value;
}
}
return [];
}
async function checkHealth(url) {
const response = await fetch(url, { method: "GET" });
const text = await response.text();
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return text ? JSON.parse(text) : null;
}
async function waitForHealth(options) {
const startedAt = Date.now();
let lastError = null;
while (Date.now() - startedAt < options.timeoutMs) {
try {
const payload = await checkHealth(options.healthUrl);
if (!options.json) {
console.log(`[prompt-to-artifact:p5-smoke] DevBridge 已就绪 (${Date.now() - startedAt}ms)`);
}
return payload;
} catch (error) {
lastError = error;
await sleep(options.intervalMs);
}
}
throw new Error(
`DevBridge 未就绪,请先启动 npm run tauri:dev:headless。最后错误: ${
lastError instanceof Error ? lastError.message : String(lastError)
}`,
);
}
async function invoke(invokeUrl, cmd, args = {}) {
const response = await fetch(invokeUrl, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ cmd, args }),
});
if (!response.ok) {
throw new Error(`${cmd} HTTP ${response.status}: ${response.statusText}`);
}
const payload = await response.json();
if (payload?.error) {
throw new Error(`${cmd} failed: ${payload.error}`);
}
return payload?.result;
}
function buildGeneratedFiles() {
return [
{
relativePath: "SKILL.md",
content: [
"---",
"name: 只读 CLI 每日报告",
"description: 把只读 CLI 或 fixture 输出整理成 Markdown 趋势摘要。",
"---",
"",
"# 只读 CLI 每日报告",
"",
"## 何时使用",
"当用户需要把本地只读 CLI 输出、日志片段或 fixture 数据整理为每日趋势报告时使用。",
"",
"## 输入",
"- topic: 报告主题。",
"- fixture_path: 可选,只读 fixture 路径。",
"",
"## 执行步骤",
"1. 读取用户提供的只读 CLI 输出或 fixture 内容。",
"2. 提炼趋势、异常、代表样例和下一步建议。",
"3. 生成 Markdown 报告。",
"",
"## 输出",
"- markdown_report: Markdown 趋势摘要。",
"",
"## 权限边界",
"只读读取用户提供内容;不安装依赖,不联网,不发布,不删除文件。",
].join("\n"),
},
{
relativePath: "contract/input.schema.json",
content: JSON.stringify(
{
type: "object",
required: ["topic"],
properties: {
topic: { type: "string" },
fixture_path: { type: "string" },
},
additionalProperties: false,
},
null,
2,
),
},
{
relativePath: "contract/output.schema.json",
content: JSON.stringify(
{
type: "object",
required: ["markdown_report"],
properties: {
markdown_report: { type: "string" },
evidence_notes: { type: "array", items: { type: "string" } },
},
additionalProperties: false,
},
null,
2,
),
},
{
relativePath: "examples/input.sample.json",
content: JSON.stringify(
{
topic: "AI Agent adoption",
fixture_path: "tests/fixture.json",
},
null,
2,
),
},
{
relativePath: "tests/fixture.json",
content: JSON.stringify(
{
rows: [
{ label: "Agent workflow", mentions: 42 },
{ label: "Human approval", mentions: 18 },
{ label: "Evidence audit", mentions: 12 },
],
},
null,
2,
),
},
{
relativePath: "scripts/README.md",
content: [
"# Wrapper 说明",
"",
"P5 第一刀不执行真实 CLI。后续接入真实只读 CLI 时,必须继续保持只读参数、fixture dry-run 和人工确认边界。",
].join("\n"),
},
];
}
async function prepareWorkspace(options) {
if (options.workspaceRoot) {
await fs.mkdir(options.workspaceRoot, { recursive: true });
return { workspaceRoot: options.workspaceRoot, createdTemp: false };
}
const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "lime-prompt-artifact-p5-"));
return { workspaceRoot, createdTemp: true };
}
async function runSmoke(options) {
await waitForHealth(options);
const workspace = await prepareWorkspace(options);
const startedAt = new Date().toISOString();
try {
const createRequest = {
workspaceRoot: workspace.workspaceRoot,
name: "只读 CLI 每日报告",
description: "把只读 CLI 或 fixture 输出整理成 Markdown 趋势摘要。",
userGoal:
"每天 9 点读取只读 CLI 或 fixture 输出,生成 Markdown 趋势摘要,失败时提示我检查配置。",
sourceKind: "cli",
sourceRefs: ["docs/exec-plans/skill-forge-prompt-to-artifact-p5-plan.md"],
permissionSummary: ["Level 0 只读发现", "Level 1 draft-scoped write"],
generatedFiles: buildGeneratedFiles(),
};
const draft = await invoke(options.invokeUrl, "capability_draft_create", {
request: createRequest,
});
const draftId = pickString(draft, "draftId", "draft_id");
assert(draftId, "capability_draft_create 未返回 draftId");
const verification = await invoke(options.invokeUrl, "capability_draft_verify", {
request: { workspaceRoot: workspace.workspaceRoot, draftId },
});
const verificationStatus = pickString(
verification?.draft,
"verificationStatus",
"verification_status",
);
assert(
verificationStatus === "verified_pending_registration",
`verification 未进入 pending registration: ${verificationStatus}`,
);
const failedChecks = pickArray(verification?.report, "checks").filter(
(check) => pickString(check, "status") === "failed",
);
assert(failedChecks.length === 0, `verification 存在失败项: ${JSON.stringify(failedChecks)}`);
const registration = await invoke(options.invokeUrl, "capability_draft_register", {
request: { workspaceRoot: workspace.workspaceRoot, draftId },
});
const registeredSkillDirectory = pickString(
registration?.registration,
"registeredSkillDirectory",
"registered_skill_directory",
);
assert(registeredSkillDirectory, "registration 未返回 registeredSkillDirectory");
const registeredSkills = await invoke(
options.invokeUrl,
"capability_draft_list_registered_skills",
{ request: { workspaceRoot: workspace.workspaceRoot } },
);
assert(Array.isArray(registeredSkills), "registered skills 返回不是数组");
const registeredSkill = registeredSkills.find(
(skill) =>
pickString(skill, "registeredSkillDirectory", "registered_skill_directory") ===
registeredSkillDirectory || pickString(skill, "name") === "只读 CLI 每日报告",
);
assert(registeredSkill, "registered discovery 未找到刚注册的 skill");
assert(
registeredSkill.launchEnabled === false || registeredSkill.launch_enabled === false,
"registered discovery 不应默认 launchEnabled=true",
);
const bindingSnapshot = await invoke(
options.invokeUrl,
"agent_runtime_list_workspace_skill_bindings",
{
request: {
workspaceRoot: workspace.workspaceRoot,
caller: "assistant",
workbench: true,
},
},
);
const bindings = pickArray(bindingSnapshot, "bindings");
const binding = bindings.find(
(item) => pickString(item, "registered_skill_directory", "registeredSkillDirectory") === registeredSkillDirectory,
);
assert(binding, "runtime binding readiness 未找到刚注册的 skill");
assert(
pickString(binding, "binding_status", "bindingStatus") === "ready_for_manual_enable",
`binding 未 ready_for_manual_enable: ${pickString(binding, "binding_status", "bindingStatus")}`,
);
assert(binding.launch_enabled === false || binding.launchEnabled === false, "binding 不应默认 launch enabled");
assert(binding.tool_runtime_visible === false || binding.toolRuntimeVisible === false, "binding 不应默认 tool runtime visible");
const summary = {
status: "passed",
startedAt,
finishedAt: new Date().toISOString(),
workspaceRoot: workspace.workspaceRoot,
draftId,
verificationStatus,
verificationReportId: pickString(
verification?.report,
"reportId",
"report_id",
),
registeredSkillDirectory,
registeredSkillName: pickString(registeredSkill, "name"),
bindingStatus: pickString(binding, "binding_status", "bindingStatus"),
nextGate: pickString(binding, "next_gate", "nextGate"),
cleanup: options.cleanup && workspace.createdTemp,
};
if (!options.json) {
console.log("[prompt-to-artifact:p5-smoke] 通过");
console.log(JSON.stringify(summary, null, 2));
} else {
console.log(JSON.stringify(summary));
}
return { summary, workspace };
} catch (error) {
error.workspaceRoot = workspace.workspaceRoot;
throw error;
} finally {
if (options.cleanup && workspace.createdTemp) {
await fs.rm(workspace.workspaceRoot, { recursive: true, force: true });
}
}
}
async function main() {
if (typeof fetch !== "function") {
throw new Error("当前 Node 运行时不支持 fetch,请使用 Node 18+");
}
const options = parseArgs(process.argv.slice(2));
await runSmoke(options);
}
main().catch((error) => {
const detail = error instanceof Error ? error.message : String(error);
console.error(`[prompt-to-artifact:p5-smoke] 失败: ${detail}`);
if (error?.workspaceRoot) {
console.error(`[prompt-to-artifact:p5-smoke] workspaceRoot: ${error.workspaceRoot}`);
}
process.exit(1);
});
+1
View File
@@ -63,6 +63,7 @@ const HARNESS_CLEANUP_CONTRACT_FILES = new Set([
const INTEGRITY_FILES = new Set([
"package.json",
"packages/lime-cli-npm/package.json",
"src-tauri/Cargo.toml",
"src-tauri/tauri.conf.json",
"src-tauri/tauri.conf.headless.json",
+616
View File
@@ -0,0 +1,616 @@
#!/usr/bin/env node
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { buildReadonlyHttpApiCreateRequest } from "./lib/readonly-http-api-draft-template.mjs";
const DEFAULTS = {
healthUrl: "http://127.0.0.1:3030/health",
invokeUrl: "http://127.0.0.1:3030/invoke",
timeoutMs: 120_000,
intervalMs: 1_000,
workspaceRoot: "",
cleanup: false,
json: false,
};
function printHelp() {
console.log(`
Read-Only HTTP API Capability Draft Smoke
用途:
用临时 workspace 验证 P6 第一刀:只读 HTTP API draft 必须声明网络只读权限。
该 smoke 只走 capability_draft_create -> capability_draft_verify,不发真实 HTTP 请求、不注册、不进入 runtime。
用法:
node scripts/readonly-http-api-smoke.mjs [选项]
选项:
--health-url <url> DevBridge 健康检查地址,默认 http://127.0.0.1:3030/health
--invoke-url <url> DevBridge invoke 地址,默认 http://127.0.0.1:3030/invoke
--timeout-ms <ms> 等待 DevBridge 超时,默认 120000
--interval-ms <ms> 轮询间隔,默认 1000
--workspace-root <dir> 指定 smoke workspace;默认创建临时目录
--cleanup 成功或失败后删除本脚本创建的临时 workspace
--json 只输出 JSON summary
-h, --help 显示帮助
`);
}
function parseArgs(argv) {
const options = { ...DEFAULTS };
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--health-url" && argv[index + 1]) {
options.healthUrl = String(argv[++index]).trim();
continue;
}
if (arg === "--invoke-url" && argv[index + 1]) {
options.invokeUrl = String(argv[++index]).trim();
continue;
}
if (arg === "--timeout-ms" && argv[index + 1]) {
options.timeoutMs = Number(argv[++index]);
continue;
}
if (arg === "--interval-ms" && argv[index + 1]) {
options.intervalMs = Number(argv[++index]);
continue;
}
if (arg === "--workspace-root" && argv[index + 1]) {
options.workspaceRoot = path.resolve(String(argv[++index]).trim());
continue;
}
if (arg === "--cleanup") {
options.cleanup = true;
continue;
}
if (arg === "--json") {
options.json = true;
continue;
}
if (arg === "--help" || arg === "-h") {
printHelp();
process.exit(0);
}
throw new Error(`未知参数: ${arg}`);
}
if (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 1_000) {
throw new Error("--timeout-ms 必须是 >= 1000 的数字");
}
if (!Number.isFinite(options.intervalMs) || options.intervalMs < 100) {
throw new Error("--interval-ms 必须是 >= 100 的数字");
}
return options;
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
function pickString(target, ...keys) {
for (const key of keys) {
const value = target?.[key];
if (typeof value === "string" && value.trim()) {
return value.trim();
}
}
return "";
}
function pickArray(target, ...keys) {
for (const key of keys) {
const value = target?.[key];
if (Array.isArray(value)) {
return value;
}
}
return [];
}
function collectEvidenceKeys(check) {
return pickArray(check, "evidence")
.map((item) => pickString(item, "key"))
.filter(Boolean);
}
async function checkHealth(url) {
const response = await fetch(url, { method: "GET" });
const text = await response.text();
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return text ? JSON.parse(text) : null;
}
async function waitForHealth(options) {
const startedAt = Date.now();
let lastError = null;
while (Date.now() - startedAt < options.timeoutMs) {
try {
const payload = await checkHealth(options.healthUrl);
if (!options.json) {
console.log(`[readonly-http-api:p6-smoke] DevBridge 已就绪 (${Date.now() - startedAt}ms)`);
}
return payload;
} catch (error) {
lastError = error;
await sleep(options.intervalMs);
}
}
throw new Error(
`DevBridge 未就绪,请先启动 npm run tauri:dev:headless。最后错误: ${
lastError instanceof Error ? lastError.message : String(lastError)
}`,
);
}
async function invoke(invokeUrl, cmd, args = {}) {
const response = await fetch(invokeUrl, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ cmd, args }),
});
if (!response.ok) {
throw new Error(`${cmd} HTTP ${response.status}: ${response.statusText}`);
}
const payload = await response.json();
if (payload?.error) {
throw new Error(`${cmd} failed: ${payload.error}`);
}
return payload?.result;
}
function buildCreateRequest(workspaceRoot, permissionSummary, options = {}) {
return buildReadonlyHttpApiCreateRequest(
workspaceRoot,
permissionSummary,
options,
);
}
async function prepareWorkspace(options) {
if (options.workspaceRoot) {
await fs.mkdir(options.workspaceRoot, { recursive: true });
return { workspaceRoot: options.workspaceRoot, createdTemp: false };
}
const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "lime-readonly-http-api-p6-"));
return { workspaceRoot, createdTemp: true };
}
async function createAndVerify(options, workspaceRoot, permissionSummary, draftOptions = {}) {
const draft = await invoke(options.invokeUrl, "capability_draft_create", {
request: buildCreateRequest(workspaceRoot, permissionSummary, draftOptions),
});
const draftId = pickString(draft, "draftId", "draft_id");
assert(draftId, "capability_draft_create 未返回 draftId");
const verification = await invoke(options.invokeUrl, "capability_draft_verify", {
request: { workspaceRoot, draftId },
});
const verificationStatus = pickString(
verification?.draft,
"verificationStatus",
"verification_status",
);
const checks = pickArray(verification?.report, "checks");
const failedChecks = checks.filter((check) => pickString(check, "status") === "failed");
return { draftId, verificationStatus, checks, failedChecks };
}
async function runSmoke(options) {
await waitForHealth(options);
const workspace = await prepareWorkspace(options);
const startedAt = new Date().toISOString();
try {
const positive = await createAndVerify(options, workspace.workspaceRoot, [
"Level 0 只读发现",
"允许只读 HTTP API GET 请求,不做外部写操作",
]);
assert(
positive.verificationStatus === "verified_pending_registration",
`正向 draft 未进入 pending registration: ${positive.verificationStatus}`,
);
assert(
positive.failedChecks.length === 0,
`正向 draft 存在失败项: ${JSON.stringify(positive.failedChecks)}`,
);
const positiveDryRunExecutionCheck = positive.checks.find(
(check) => pickString(check, "id") === "readonly_http_fixture_dry_run_execute",
);
assert(
positiveDryRunExecutionCheck,
"正向 draft 未返回 readonly_http_fixture_dry_run_execute check",
);
const positiveDryRunExecuteEvidenceKeys = collectEvidenceKeys(positiveDryRunExecutionCheck);
for (const key of [
"scriptPath",
"expectedOutputPath",
"durationMs",
"actualSha256",
"expectedSha256",
"stdoutPreview",
]) {
assert(
positiveDryRunExecuteEvidenceKeys.includes(key),
`正向 dry-run execute 缺少 evidence key: ${key}`,
);
}
const positivePreflightCheck = positive.checks.find(
(check) => pickString(check, "id") === "readonly_http_execution_preflight",
);
assert(
positivePreflightCheck,
"正向 draft 未返回 readonly_http_execution_preflight check",
);
const positivePreflightEvidenceKeys = collectEvidenceKeys(positivePreflightCheck);
for (const key of [
"preflightMode",
"endpointSource",
"method",
"credentialReferenceId",
"evidenceSchema",
]) {
assert(
positivePreflightEvidenceKeys.includes(key),
`正向 execution preflight 缺少 evidence key: ${key}`,
);
}
const negative = await createAndVerify(options, workspace.workspaceRoot, ["Level 0 只读发现"]);
assert(
negative.verificationStatus === "verification_failed",
`负向 draft 未失败: ${negative.verificationStatus}`,
);
const riskCheck = negative.failedChecks.find(
(check) => pickString(check, "id") === "static_risk_scan",
);
assert(riskCheck, "负向 draft 未命中 static_risk_scan");
assert(
String(riskCheck.message ?? "").includes("网络只读权限"),
`负向 draft 风险信息不包含网络只读权限: ${String(riskCheck.message ?? "")}`,
);
const missingFixture = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeFixture: false },
);
assert(
missingFixture.verificationStatus === "verification_failed",
`缺 fixture draft 未失败: ${missingFixture.verificationStatus}`,
);
const fixtureCheck = missingFixture.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_fixture",
);
assert(fixtureCheck, "缺 fixture draft 未命中 readonly_http_fixture");
assert(
String(fixtureCheck.message ?? "").includes("fixture"),
`缺 fixture draft 风险信息不包含 fixture: ${String(fixtureCheck.message ?? "")}`,
);
const missingExpectedOutput = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeExpectedOutput: false },
);
assert(
missingExpectedOutput.verificationStatus === "verification_failed",
`缺 expected output draft 未失败: ${missingExpectedOutput.verificationStatus}`,
);
const expectedOutputCheck = missingExpectedOutput.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_expected_output",
);
assert(expectedOutputCheck, "缺 expected output draft 未命中 readonly_http_expected_output");
assert(
String(expectedOutputCheck.message ?? "").includes("expected output"),
`缺 expected output draft 风险信息不包含 expected output: ${String(
expectedOutputCheck.message ?? "",
)}`,
);
const missingFixtureInput = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeFixtureInput: false },
);
assert(
missingFixtureInput.verificationStatus === "verification_failed",
`缺 fixture input draft 未失败: ${missingFixtureInput.verificationStatus}`,
);
const fixtureInputCheck = missingFixtureInput.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_fixture_input",
);
assert(fixtureInputCheck, "缺 fixture input draft 未命中 readonly_http_fixture_input");
assert(
String(fixtureInputCheck.message ?? "").includes("fixture"),
`缺 fixture input draft 风险信息不包含 fixture: ${String(
fixtureInputCheck.message ?? "",
)}`,
);
const missingDryRunEntry = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeDryRunEntry: false },
);
assert(
missingDryRunEntry.verificationStatus === "verification_failed",
`缺 fixture dry-run 入口 draft 未失败: ${missingDryRunEntry.verificationStatus}`,
);
const dryRunCheck = missingDryRunEntry.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_fixture_dry_run",
);
assert(dryRunCheck, "缺 fixture dry-run 入口 draft 未命中 readonly_http_fixture_dry_run");
assert(
String(dryRunCheck.message ?? "").includes("dry-run"),
`缺 fixture dry-run 入口 draft 风险信息不包含 dry-run: ${String(
dryRunCheck.message ?? "",
)}`,
);
const missingDryRunExpectedOutputBinding = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeDryRunExpectedOutputBinding: false },
);
assert(
missingDryRunExpectedOutputBinding.verificationStatus === "verification_failed",
`dry-run 未绑定 expected output draft 未失败: ${missingDryRunExpectedOutputBinding.verificationStatus}`,
);
const dryRunExpectedOutputBindingCheck = missingDryRunExpectedOutputBinding.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_fixture_dry_run_expected_output",
);
assert(
dryRunExpectedOutputBindingCheck,
"dry-run 未绑定 expected output draft 未命中 readonly_http_fixture_dry_run_expected_output",
);
assert(
String(dryRunExpectedOutputBindingCheck.message ?? "").includes("expected output"),
`dry-run 未绑定 expected output draft 风险信息不包含 expected output: ${String(
dryRunExpectedOutputBindingCheck.message ?? "",
)}`,
);
const mismatchedDryRun = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeDryRunMismatch: true },
);
assert(
mismatchedDryRun.verificationStatus === "verification_failed",
`dry-run actual/expected 不一致 draft 未失败: ${mismatchedDryRun.verificationStatus}`,
);
const dryRunExecutionCheck = mismatchedDryRun.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_fixture_dry_run_execute",
);
assert(
dryRunExecutionCheck,
"dry-run actual/expected 不一致 draft 未命中 readonly_http_fixture_dry_run_execute",
);
assert(
String(dryRunExecutionCheck.message ?? "").includes("dry-run"),
`dry-run actual/expected 不一致 draft 风险信息不包含 dry-run: ${String(
dryRunExecutionCheck.message ?? "",
)}`,
);
const networkedDryRun = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeNetworkedDryRun: true },
);
assert(
networkedDryRun.verificationStatus === "verification_failed",
`真实联网 dry-run draft 未失败: ${networkedDryRun.verificationStatus}`,
);
const dryRunOfflineCheck = networkedDryRun.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_fixture_dry_run_offline",
);
assert(dryRunOfflineCheck, "真实联网 dry-run draft 未命中 readonly_http_fixture_dry_run_offline");
assert(
String(dryRunOfflineCheck.message ?? "").includes("真实联网"),
`真实联网 dry-run draft 风险信息不包含真实联网: ${String(
dryRunOfflineCheck.message ?? "",
)}`,
);
const credentialDraft = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeCredentialHeader: true },
);
assert(
credentialDraft.verificationStatus === "verification_failed",
`凭证字段 draft 未失败: ${credentialDraft.verificationStatus}`,
);
const credentialCheck = credentialDraft.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_no_credentials",
);
assert(credentialCheck, "凭证字段 draft 未命中 readonly_http_no_credentials");
assert(
String(credentialCheck.message ?? "").includes("凭证"),
`凭证字段 draft 风险信息不包含凭证: ${String(credentialCheck.message ?? "")}`,
);
const missingSessionAuthorization = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeSessionAuthorization: false },
);
assert(
missingSessionAuthorization.verificationStatus === "verification_failed",
`缺 session authorization policy draft 未失败: ${missingSessionAuthorization.verificationStatus}`,
);
const sessionAuthorizationCheck = missingSessionAuthorization.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_session_authorization",
);
assert(
sessionAuthorizationCheck,
"缺 session authorization policy draft 未命中 readonly_http_session_authorization",
);
assert(
String(sessionAuthorizationCheck.message ?? "").includes("authorization") ||
String(sessionAuthorizationCheck.message ?? "").includes("授权"),
`缺 session authorization policy draft 风险信息不包含授权: ${String(
sessionAuthorizationCheck.message ?? "",
)}`,
);
const missingCredentialReference = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeCredentialReference: false },
);
assert(
missingCredentialReference.verificationStatus === "verification_failed",
`缺 credential_reference draft 未失败: ${missingCredentialReference.verificationStatus}`,
);
const credentialReferenceCheck = missingCredentialReference.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_credential_reference",
);
assert(
credentialReferenceCheck,
"缺 credential_reference draft 未命中 readonly_http_credential_reference",
);
assert(
String(credentialReferenceCheck.message ?? "").includes("credential_reference") ||
String(credentialReferenceCheck.message ?? "").includes("凭证引用"),
`缺 credential_reference draft 风险信息不包含凭证引用: ${String(
credentialReferenceCheck.message ?? "",
)}`,
);
const missingExecutionPreflight = await createAndVerify(
options,
workspace.workspaceRoot,
["Level 0 只读发现", "允许只读 HTTP API GET 请求,不做外部写操作"],
{ includeExecutionPreflight: false },
);
assert(
missingExecutionPreflight.verificationStatus === "verification_failed",
`缺 execution_preflight draft 未失败: ${missingExecutionPreflight.verificationStatus}`,
);
const executionPreflightCheck = missingExecutionPreflight.failedChecks.find(
(check) => pickString(check, "id") === "readonly_http_execution_preflight",
);
assert(
executionPreflightCheck,
"缺 execution_preflight draft 未命中 readonly_http_execution_preflight",
);
assert(
String(executionPreflightCheck.message ?? "").includes("execution_preflight") ||
String(executionPreflightCheck.message ?? "").includes("preflight"),
`缺 execution_preflight draft 风险信息不包含 preflight: ${String(
executionPreflightCheck.message ?? "",
)}`,
);
const summary = {
status: "passed",
startedAt,
finishedAt: new Date().toISOString(),
workspaceRoot: workspace.workspaceRoot,
positiveDraftId: positive.draftId,
positiveVerificationStatus: positive.verificationStatus,
positiveDryRunExecuteEvidenceKeys,
positivePreflightEvidenceKeys,
negativeDraftId: negative.draftId,
negativeVerificationStatus: negative.verificationStatus,
negativeFailedCheck: pickString(riskCheck, "id"),
missingFixtureDraftId: missingFixture.draftId,
missingFixtureVerificationStatus: missingFixture.verificationStatus,
missingFixtureFailedCheck: pickString(fixtureCheck, "id"),
missingExpectedOutputDraftId: missingExpectedOutput.draftId,
missingExpectedOutputVerificationStatus: missingExpectedOutput.verificationStatus,
missingExpectedOutputFailedCheck: pickString(expectedOutputCheck, "id"),
missingFixtureInputDraftId: missingFixtureInput.draftId,
missingFixtureInputVerificationStatus: missingFixtureInput.verificationStatus,
missingFixtureInputFailedCheck: pickString(fixtureInputCheck, "id"),
missingDryRunEntryDraftId: missingDryRunEntry.draftId,
missingDryRunEntryVerificationStatus: missingDryRunEntry.verificationStatus,
missingDryRunEntryFailedCheck: pickString(dryRunCheck, "id"),
missingDryRunExpectedOutputBindingDraftId: missingDryRunExpectedOutputBinding.draftId,
missingDryRunExpectedOutputBindingVerificationStatus:
missingDryRunExpectedOutputBinding.verificationStatus,
missingDryRunExpectedOutputBindingFailedCheck: pickString(
dryRunExpectedOutputBindingCheck,
"id",
),
mismatchedDryRunDraftId: mismatchedDryRun.draftId,
mismatchedDryRunVerificationStatus: mismatchedDryRun.verificationStatus,
mismatchedDryRunFailedCheck: pickString(dryRunExecutionCheck, "id"),
networkedDryRunDraftId: networkedDryRun.draftId,
networkedDryRunVerificationStatus: networkedDryRun.verificationStatus,
networkedDryRunFailedCheck: pickString(dryRunOfflineCheck, "id"),
credentialDraftId: credentialDraft.draftId,
credentialVerificationStatus: credentialDraft.verificationStatus,
credentialFailedCheck: pickString(credentialCheck, "id"),
missingSessionAuthorizationDraftId: missingSessionAuthorization.draftId,
missingSessionAuthorizationVerificationStatus:
missingSessionAuthorization.verificationStatus,
missingSessionAuthorizationFailedCheck: pickString(sessionAuthorizationCheck, "id"),
missingCredentialReferenceDraftId: missingCredentialReference.draftId,
missingCredentialReferenceVerificationStatus:
missingCredentialReference.verificationStatus,
missingCredentialReferenceFailedCheck: pickString(credentialReferenceCheck, "id"),
missingExecutionPreflightDraftId: missingExecutionPreflight.draftId,
missingExecutionPreflightVerificationStatus:
missingExecutionPreflight.verificationStatus,
missingExecutionPreflightFailedCheck: pickString(executionPreflightCheck, "id"),
cleanup: options.cleanup && workspace.createdTemp,
};
if (options.cleanup && workspace.createdTemp) {
await fs.rm(workspace.workspaceRoot, { recursive: true, force: true });
}
return summary;
} catch (error) {
error.workspaceRoot = workspace.workspaceRoot;
if (options.cleanup && workspace.createdTemp) {
await fs.rm(workspace.workspaceRoot, { recursive: true, force: true });
}
throw error;
}
}
async function main() {
const options = parseArgs(process.argv.slice(2));
try {
const summary = await runSmoke(options);
if (options.json) {
console.log(JSON.stringify(summary));
} else {
console.log("[readonly-http-api:p6-smoke] 通过");
console.log(JSON.stringify(summary, null, 2));
}
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
console.error(`[readonly-http-api:p6-smoke] 失败: ${detail}`);
if (error?.workspaceRoot) {
console.error(`[readonly-http-api:p6-smoke] workspaceRoot: ${error.workspaceRoot}`);
}
process.exit(1);
}
}
await main();
+22 -1
View File
@@ -19,6 +19,7 @@ const batchSize =
: 16;
const serialTestFiles = new Set([
"scripts/lib/harness-eval-history-record.test.ts",
"scripts/lib/harness-eval-history-window.test.ts",
"src/components/agent/chat/index.test.tsx",
"src/components/agent/chat/hooks/useAsterAgentChat.test.tsx",
"src/components/workspace/WorkbenchPage.test.tsx",
@@ -28,6 +29,21 @@ const serialTestFiles = new Set([
"src/components/agent/chat/components/TeamWorkspaceBoard.test.tsx",
"src/components/settings-v2/system/automation/index.test.tsx",
]);
const ignoredTestPathSegments = [
"/node_modules/",
"/tmp/lime-pnpm-frozen-node_modules/",
];
function normalizeTestPath(file) {
return path.resolve(file).replaceAll("\\", "/");
}
function shouldIgnoreCollectedTestFile(file) {
const normalized = normalizeTestPath(file);
return ignoredTestPathSegments.some((segment) =>
normalized.includes(segment),
);
}
function runVitest(args, label) {
if (label) {
@@ -83,7 +99,12 @@ function collectTestFiles() {
const parsed = JSON.parse(result.stdout || "[]");
return parsed
.map((entry) => (typeof entry === "string" ? entry : entry?.file))
.filter((entry) => typeof entry === "string" && entry.length > 0);
.filter(
(entry) =>
typeof entry === "string" &&
entry.length > 0 &&
!shouldIgnoreCollectedTestFile(entry),
);
}
function chunkFiles(files, size) {
+19 -18
View File
@@ -5065,7 +5065,7 @@ dependencies = [
[[package]]
name = "lime"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"anyhow",
"arboard",
@@ -5092,6 +5092,7 @@ dependencies = [
"glob",
"hex",
"if-addrs",
"image",
"indexmap 2.13.0",
"jsonwebtoken",
"lime-agent",
@@ -5171,7 +5172,7 @@ dependencies = [
[[package]]
name = "lime-agent"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"anyhow",
"aster-core",
@@ -5200,7 +5201,7 @@ dependencies = [
[[package]]
name = "lime-browser-runtime"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"chrono",
"futures",
@@ -5217,7 +5218,7 @@ dependencies = [
[[package]]
name = "lime-cli"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"clap",
"lime-core",
@@ -5229,7 +5230,7 @@ dependencies = [
[[package]]
name = "lime-config"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"async-trait",
"lime-core",
@@ -5245,7 +5246,7 @@ dependencies = [
[[package]]
name = "lime-core"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"aster-models",
"async-trait",
@@ -5298,7 +5299,7 @@ dependencies = [
[[package]]
name = "lime-gateway"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"aes",
"axum 0.7.9",
@@ -5328,7 +5329,7 @@ dependencies = [
[[package]]
name = "lime-infra"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"chrono",
"dashmap 5.5.3",
@@ -5348,7 +5349,7 @@ dependencies = [
[[package]]
name = "lime-knowledge"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"chrono",
"hex",
@@ -5361,7 +5362,7 @@ dependencies = [
[[package]]
name = "lime-mcp"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"async-trait",
"dirs 5.0.1",
@@ -5377,7 +5378,7 @@ dependencies = [
[[package]]
name = "lime-media-runtime"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"axum 0.7.9",
"base64 0.22.1",
@@ -5410,7 +5411,7 @@ dependencies = [
[[package]]
name = "lime-processor"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"async-trait",
"lime-core",
@@ -5429,7 +5430,7 @@ dependencies = [
[[package]]
name = "lime-providers"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"anyhow",
"async-stream",
@@ -5484,7 +5485,7 @@ dependencies = [
[[package]]
name = "lime-server"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"aster-core",
"async-stream",
@@ -5528,7 +5529,7 @@ dependencies = [
[[package]]
name = "lime-server-utils"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"axum 0.7.9",
"futures",
@@ -5543,7 +5544,7 @@ dependencies = [
[[package]]
name = "lime-services"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"anyhow",
"aster-core",
@@ -5588,7 +5589,7 @@ dependencies = [
[[package]]
name = "lime-skills"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"async-trait",
"dirs 5.0.1",
@@ -5606,7 +5607,7 @@ dependencies = [
[[package]]
name = "lime-websocket"
version = "1.29.0"
version = "1.30.0"
dependencies = [
"axum 0.7.9",
"chrono",
+3 -2
View File
@@ -9,7 +9,7 @@ exclude = [
resolver = "2"
[workspace.package]
version = "1.29.0"
version = "1.30.0"
edition = "2021"
authors = ["coso"]
repository = "https://github.com/aiclientproxy/lime"
@@ -198,7 +198,7 @@ version = "2.4"
[package]
name = "lime"
version = "1.29.0"
version = "1.30.0"
description = "AI API Proxy Desktop App"
authors = ["you"]
edition = "2021"
@@ -307,6 +307,7 @@ notify.workspace = true
parking_lot.workspace = true
tiktoken-rs.workspace = true
base64.workspace = true
image = { version = "0.25.9", default-features = false, features = ["png"] }
bytes.workspace = true
rand.workspace = true
sha2.workspace = true
+15 -18
View File
@@ -1537,9 +1537,13 @@ pub async fn get_runtime_session_detail_with_history_page(
&& before_message_id.is_none()
&& should_load_runtime_overlay(&detail, history_limit);
let session_started_at = Instant::now();
let session = if load_runtime_overlay {
match read_session(session_id, false, "读取运行态 session 失败").await {
let overlay_started_at = Instant::now();
let (session, runtime_snapshot) = if load_runtime_overlay {
let (session_result, snapshot_result) = tokio::join!(
read_session(session_id, false, "读取运行态 session 失败"),
load_aster_runtime_snapshot(session_id),
);
let session = match session_result {
Ok(session) => Some(session),
Err(error) => {
tracing::warn!(
@@ -1549,15 +1553,8 @@ pub async fn get_runtime_session_detail_with_history_page(
);
None
}
}
} else {
None
};
let session_ms = session_started_at.elapsed().as_millis();
let runtime_snapshot_started_at = Instant::now();
let runtime_snapshot = if load_runtime_overlay {
match load_aster_runtime_snapshot(session_id).await {
};
let snapshot = match snapshot_result {
Ok(snapshot) => Some(snapshot),
Err(error) => {
tracing::warn!(
@@ -1567,11 +1564,12 @@ pub async fn get_runtime_session_detail_with_history_page(
);
None
}
}
};
(session, snapshot)
} else {
None
(None, None)
};
let runtime_snapshot_ms = runtime_snapshot_started_at.elapsed().as_millis();
let overlay_ms = overlay_started_at.elapsed().as_millis();
let usage_fallback_started_at = Instant::now();
if let Some(session) = session.as_ref() {
@@ -1672,12 +1670,11 @@ pub async fn get_runtime_session_detail_with_history_page(
let total_ms = started_at.elapsed().as_millis();
tracing::info!(
"[SessionStore] get_runtime_session_detail 完成: session_id={}, total_ms={}, detail_ms={}, read_runtime_session_ms={}, runtime_snapshot_ms={}, usage_fallback_ms={}, execution_runtime_ms={}, apply_snapshot_ms={}, child_subagents_ms={}, parent_context_ms={}, history_limit={:?}, history_offset={}, before_message_id={:?}, runtime_overlay_loaded={}, subagent_runtime_context_loaded={}, messages_count={}, turns_count={}, items_count={}, child_subagents_count={}, has_parent_context={}",
"[SessionStore] get_runtime_session_detail 完成: session_id={}, total_ms={}, detail_ms={}, overlay_ms={}, usage_fallback_ms={}, execution_runtime_ms={}, apply_snapshot_ms={}, child_subagents_ms={}, parent_context_ms={}, history_limit={:?}, history_offset={}, before_message_id={:?}, runtime_overlay_loaded={}, subagent_runtime_context_loaded={}, messages_count={}, turns_count={}, items_count={}, child_subagents_count={}, has_parent_context={}",
session_id,
total_ms,
detail_ms,
session_ms,
runtime_snapshot_ms,
overlay_ms,
usage_fallback_ms,
execution_runtime_ms,
apply_snapshot_ms,
@@ -306,6 +306,45 @@ impl AgentRunDao {
iter.collect()
}
pub fn list_active_runs_by_session(
conn: &Connection,
session_id: &str,
limit: usize,
) -> Result<Vec<AgentRun>, rusqlite::Error> {
let mut stmt = conn.prepare(
"SELECT id, source, source_ref, session_id, status, started_at, finished_at, duration_ms,
error_code, error_message, metadata, created_at, updated_at
FROM agent_runs
WHERE session_id = ?1
AND status IN ('queued', 'running')
ORDER BY started_at DESC
LIMIT ?2",
)?;
let iter = stmt.query_map(params![session_id, limit as i64], |row| {
let status_raw: String = row.get(4)?;
let status =
AgentRunStatus::try_from(status_raw.as_str()).unwrap_or(AgentRunStatus::Error);
Ok(AgentRun {
id: row.get(0)?,
source: row.get(1)?,
source_ref: row.get(2)?,
session_id: row.get(3)?,
status,
started_at: row.get(5)?,
finished_at: row.get(6)?,
duration_ms: row.get(7)?,
error_code: row.get(8)?,
error_message: row.get(9)?,
metadata: row.get(10)?,
created_at: row.get(11)?,
updated_at: row.get(12)?,
})
})?;
iter.collect()
}
pub fn list_terminal_runs_by_session(
conn: &Connection,
session_id: &str,
@@ -508,6 +547,45 @@ mod tests {
assert_eq!(second_page[0].id, "run-success");
}
#[test]
fn list_active_runs_by_session_should_filter_active_status() {
let conn = setup_conn();
let mut run_queued = sample_run("run-queued", AgentRunStatus::Queued);
run_queued.session_id = Some("session-a".to_string());
run_queued.started_at = "2026-03-06T10:00:00Z".to_string();
run_queued.created_at = run_queued.started_at.clone();
run_queued.updated_at = run_queued.started_at.clone();
AgentRunDao::create_run(&conn, &run_queued).expect("写入 run-queued 失败");
let mut run_running = sample_run("run-running", AgentRunStatus::Running);
run_running.session_id = Some("session-a".to_string());
run_running.started_at = "2026-03-06T11:00:00Z".to_string();
run_running.created_at = run_running.started_at.clone();
run_running.updated_at = run_running.started_at.clone();
AgentRunDao::create_run(&conn, &run_running).expect("写入 run-running 失败");
let mut run_success = sample_run("run-success", AgentRunStatus::Success);
run_success.session_id = Some("session-a".to_string());
run_success.started_at = "2026-03-06T12:00:00Z".to_string();
run_success.created_at = run_success.started_at.clone();
run_success.updated_at = run_success.started_at.clone();
AgentRunDao::create_run(&conn, &run_success).expect("写入 run-success 失败");
let mut run_other_session = sample_run("run-other", AgentRunStatus::Running);
run_other_session.session_id = Some("session-b".to_string());
run_other_session.started_at = "2026-03-06T13:00:00Z".to_string();
run_other_session.created_at = run_other_session.started_at.clone();
run_other_session.updated_at = run_other_session.started_at.clone();
AgentRunDao::create_run(&conn, &run_other_session).expect("写入 run-other 失败");
let active = AgentRunDao::list_active_runs_by_session(&conn, "session-a", 10)
.expect("查询活跃 run 失败");
assert_eq!(active.len(), 2);
assert_eq!(active[0].id, "run-running");
assert_eq!(active[1].id, "run-queued");
}
#[test]
fn refresh_running_run_should_update_session_and_metadata() {
let conn = setup_conn();
File diff suppressed because it is too large Load Diff
+5
View File
@@ -1133,6 +1133,8 @@ pub fn run() {
commands::capability_draft_cmd::capability_draft_verify,
commands::capability_draft_cmd::capability_draft_register,
commands::capability_draft_cmd::capability_draft_list_registered_skills,
commands::capability_draft_cmd::capability_draft_submit_approval_session_inputs,
commands::capability_draft_cmd::capability_draft_execute_controlled_get,
// Skill Execution commands
commands::skill_exec_cmd::execute_skill,
commands::skill_exec_cmd::list_executable_skills,
@@ -1160,6 +1162,7 @@ pub fn run() {
commands::knowledge_cmd::knowledge_set_default_pack,
commands::knowledge_cmd::knowledge_update_pack_status,
commands::knowledge_cmd::knowledge_resolve_context,
commands::knowledge_cmd::knowledge_validate_context_run,
// Ecommerce Review Reply commands
commands::ecommerce_review_reply_cmd::execute_ecommerce_review_reply,
commands::browser_runtime_cmd::open_browser_runtime_debugger_window,
@@ -1479,7 +1482,9 @@ pub fn run() {
commands::document_import_cmd::import_document,
commands::document_import_cmd::import_document_to_session,
commands::document_import_cmd::save_exported_document,
commands::layered_design_cmd::analyze_layered_design_flat_image,
commands::layered_design_cmd::read_layered_design_project_export,
commands::layered_design_cmd::recognize_layered_design_text,
commands::layered_design_cmd::save_layered_design_project_export,
// Workspace commands
commands::workspace_cmd::workspace_create,
@@ -142,20 +142,20 @@ fn build_user_lock_capability_response(
})
}
fn complete_runtime_permission_confirmation_request(
fn complete_runtime_request_user_input_item(
app: &AppHandle,
event_name: Option<&str>,
db: &DbConnection,
request: &AgentRuntimeRespondActionRequest,
request_id: &str,
response: serde_json::Value,
request_kind_label: &str,
validate_request_id: impl FnOnce(&str) -> Result<(), String>,
) -> Result<(), String> {
let mut item = {
let conn = lime_core::database::lock_db(db)?;
lime_core::database::dao::agent_timeline::AgentTimelineDao::get_item(
&conn,
&request.request_id,
)
.map_err(|error| format!("读取权限确认请求失败: {error}"))?
.ok_or_else(|| format!("权限确认请求不存在: {}", request.request_id))?
lime_core::database::dao::agent_timeline::AgentTimelineDao::get_item(&conn, request_id)
.map_err(|error| format!("读取{request_kind_label}失败: {error}"))?
.ok_or_else(|| format!("{request_kind_label}不存在: {request_id}"))?
};
let lime_core::database::dao::agent_timeline::AgentThreadItemPayload::RequestUserInput {
@@ -166,11 +166,12 @@ fn complete_runtime_permission_confirmation_request(
..
} = item.payload
else {
return Err("权限确认请求不是 RequestUserInput,拒绝写回".to_string());
return Err(format!(
"{request_kind_label}不是 RequestUserInput,拒绝写回"
));
};
if !is_runtime_permission_confirmation_request_id(&request_id) {
return Err("请求 ID 不是运行时权限确认请求,拒绝写回".to_string());
}
validate_request_id(&request_id)?;
let now = chrono::Utc::now().to_rfc3339();
item.status = lime_core::database::dao::agent_timeline::AgentThreadItemStatus::Completed;
@@ -182,13 +183,13 @@ fn complete_runtime_permission_confirmation_request(
action_type,
prompt,
questions,
response: Some(build_permission_confirmation_response(request)),
response: Some(response),
};
{
let conn = lime_core::database::lock_db(db)?;
lime_core::database::dao::agent_timeline::AgentTimelineDao::upsert_item(&conn, &item)
.map_err(|error| format!("写回权限确认请求失败: {error}"))?;
.map_err(|error| format!("写回{request_kind_label}失败: {error}"))?;
}
if let Some(event_name) = event_name.filter(|value| !value.trim().is_empty()) {
@@ -197,7 +198,8 @@ fn complete_runtime_permission_confirmation_request(
&RuntimeAgentEvent::ItemCompleted { item: item.clone() },
) {
tracing::warn!(
"[AsterAgent] 发送权限确认完成事件失败: event_name={}, error={}",
"[AsterAgent] 发送{}完成事件失败: event_name={}, error={}",
request_kind_label,
event_name,
error
);
@@ -208,70 +210,74 @@ fn complete_runtime_permission_confirmation_request(
Ok(())
}
fn complete_runtime_permission_confirmation_request(
app: &AppHandle,
event_name: Option<&str>,
db: &DbConnection,
request: &AgentRuntimeRespondActionRequest,
) -> Result<(), String> {
complete_runtime_request_user_input_item(
app,
event_name,
db,
&request.request_id,
build_permission_confirmation_response(request),
"权限确认请求",
|request_id| {
if is_runtime_permission_confirmation_request_id(request_id) {
Ok(())
} else {
Err("请求 ID 不是运行时权限确认请求,拒绝写回".to_string())
}
},
)
}
fn complete_runtime_user_lock_capability_request(
app: &AppHandle,
event_name: Option<&str>,
db: &DbConnection,
request: &AgentRuntimeRespondActionRequest,
) -> Result<(), String> {
let mut item = {
let conn = lime_core::database::lock_db(db)?;
lime_core::database::dao::agent_timeline::AgentTimelineDao::get_item(
&conn,
&request.request_id,
)
.map_err(|error| format!("读取模型锁定能力确认请求失败: {error}"))?
.ok_or_else(|| format!("模型锁定能力确认请求不存在: {}", request.request_id))?
};
complete_runtime_request_user_input_item(
app,
event_name,
db,
&request.request_id,
build_user_lock_capability_response(request),
"模型锁定能力确认请求",
|request_id| {
if is_runtime_user_lock_capability_request_id(request_id) {
Ok(())
} else {
Err("请求 ID 不是运行时模型锁定能力确认请求,拒绝写回".to_string())
}
},
)
}
let lime_core::database::dao::agent_timeline::AgentThreadItemPayload::RequestUserInput {
request_id,
action_type,
prompt,
questions,
..
} = item.payload
else {
return Err("模型锁定能力确认请求不是 RequestUserInput,拒绝写回".to_string());
};
if !is_runtime_user_lock_capability_request_id(&request_id) {
return Err("请求 ID 不是运行时模型锁定能力确认请求,拒绝写回".to_string());
}
let now = chrono::Utc::now().to_rfc3339();
item.status = lime_core::database::dao::agent_timeline::AgentThreadItemStatus::Completed;
item.completed_at = Some(now.clone());
item.updated_at = now;
item.payload =
lime_core::database::dao::agent_timeline::AgentThreadItemPayload::RequestUserInput {
request_id,
action_type,
prompt,
questions,
response: Some(build_user_lock_capability_response(request)),
};
{
let conn = lime_core::database::lock_db(db)?;
lime_core::database::dao::agent_timeline::AgentTimelineDao::upsert_item(&conn, &item)
.map_err(|error| format!("写回模型锁定能力确认请求失败: {error}"))?;
}
if let Some(event_name) = event_name.filter(|value| !value.trim().is_empty()) {
if let Err(error) = app.emit(
event_name,
&RuntimeAgentEvent::ItemCompleted { item: item.clone() },
) {
tracing::warn!(
"[AsterAgent] 发送模型锁定能力确认完成事件失败: event_name={}, error={}",
event_name,
error
);
}
emit_action_resume_runtime_status(app, event_name);
}
Ok(())
fn complete_runtime_ask_or_elicitation_request(
app: &AppHandle,
event_name: Option<&str>,
db: &DbConnection,
request: &AgentRuntimeRespondActionRequest,
submitted_user_data: serde_json::Value,
) -> Result<(), String> {
complete_runtime_request_user_input_item(
app,
event_name,
db,
&request.request_id,
serde_json::json!({
"confirmed": request.confirmed,
"response": request.response,
"userData": submitted_user_data,
"metadata": request.metadata,
"source": "runtime_request_user_input",
}),
"补充信息请求",
|_| Ok(()),
)
}
async fn load_runtime_workspace_settings_or_default(
@@ -428,6 +434,7 @@ pub async fn agent_runtime_respond_action(
let user_data = build_runtime_action_user_data(&request);
let action_scope = build_runtime_action_scope(&request);
let resume_event_name = normalize_optional_text(request.event_name.clone());
let submitted_user_data = user_data.clone();
submit_runtime_elicitation_response_internal(
state.inner(),
db.inner(),
@@ -439,12 +446,15 @@ pub async fn agent_runtime_respond_action(
action_scope,
},
)
.await
.map(|_| {
if let Some(event_name) = resume_event_name.as_deref() {
emit_action_resume_runtime_status(&app, event_name);
}
})
.await?;
complete_runtime_ask_or_elicitation_request(
&app,
resume_event_name.as_deref(),
db.inner(),
&request,
submitted_user_data,
)
}
}
}
@@ -347,9 +347,16 @@ pub async fn agent_runtime_get_session(
let dto_started_at = Instant::now();
let loaded_messages_count = detail.messages.len();
let messages_count =
lime_agent::count_session_messages_sync(runtime.db(), &session_id)
.unwrap_or(loaded_messages_count);
let is_first_history_page = normalized_history_offset == 0
&& normalized_history_before_message_id.is_none();
let messages_count = if is_first_history_page {
Some(
lime_agent::count_session_messages_sync(runtime.db(), &session_id)
.unwrap_or(loaded_messages_count),
)
} else {
None
};
let mut response = AgentRuntimeSessionDetail::from_session_detail_with_thread_read(
detail,
queued_turns,
@@ -295,7 +295,8 @@ pub struct AgentRuntimeSessionDetail {
pub created_at: i64,
pub updated_at: i64,
pub thread_id: String,
pub messages_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub messages_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub history_limit: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -1334,7 +1335,7 @@ impl AgentRuntimeSessionDetail {
created_at: detail.created_at,
updated_at: detail.updated_at,
thread_id: detail.thread_id,
messages_count: detail.messages.len(),
messages_count: None,
history_limit: None,
history_offset: None,
history_cursor: None,
@@ -3018,6 +3018,16 @@ fn extract_metadata_usize(
.and_then(|value| usize::try_from(value).ok())
}
fn extract_metadata_bool(
object: &serde_json::Map<String, serde_json::Value>,
keys: &[&str],
) -> bool {
keys.iter()
.filter_map(|key| object.get(*key))
.find_map(serde_json::Value::as_bool)
.unwrap_or(false)
}
fn resolve_requested_knowledge_context(
request_metadata: Option<&serde_json::Value>,
workspace_root: &str,
@@ -3052,6 +3062,10 @@ fn resolve_requested_knowledge_context(
name,
task,
max_chars: extract_metadata_usize(knowledge_pack, &["max_chars", "maxChars"]),
activation: extract_metadata_string(knowledge_pack, &["activation"])
.or_else(|| Some("explicit".to_string())),
write_run: extract_metadata_bool(knowledge_pack, &["write_run", "writeRun"]),
run_reason: extract_metadata_string(knowledge_pack, &["run_reason", "runReason"]),
})
.map(Some)
}
@@ -3084,7 +3098,15 @@ fn merge_system_prompt_with_knowledge_context(
if resolution.warnings.is_empty() {
"状态提示:无。".to_string()
} else {
format!("状态提示:{}。", resolution.warnings.join(";"))
format!(
"状态提示:{}。",
resolution
.warnings
.iter()
.map(|warning| warning.message.as_str())
.collect::<Vec<_>>()
.join(";")
)
},
resolution.fenced_context
);
@@ -4532,7 +4554,7 @@ fn format_permission_turn_gating_error(
let ask_profile_keys = runtime_permission_ask_profile_label(permission_state);
format!(
"运行时权限声明需要真实确认,当前 turn 已在模型执行前阻断:confirmationStatus={confirmation_status},askProfileKeys={ask_profile_keys}。本阻断不创建 ApprovalRequest;请先接入真实权限确认或移除对应执行需求。"
"运行时权限声明需要真实确认,当前 turn 已在模型执行前等待用户确认:confirmationStatus={confirmation_status},askProfileKeys={ask_profile_keys}。已创建真实权限确认请求;请确认后重试或恢复本轮执行。"
)
}
@@ -9256,7 +9278,7 @@ mod tests {
let error = format_permission_turn_gating_error(&permission_state);
assert!(error.contains("confirmationStatus=not_requested"));
assert!(error.contains("askProfileKeys=read_files"));
assert!(error.contains("不创建 ApprovalRequest"));
assert!(error.contains("已创建真实权限确认请求"));
}
#[test]
+23 -5
View File
@@ -3,11 +3,15 @@
//! 业务规则集中在 `capability_draft_service`,这里不注册、不执行未验证草案。
use crate::services::capability_draft_service::{
create_capability_draft, get_capability_draft, list_capability_drafts,
list_workspace_registered_skills, register_capability_draft, verify_capability_draft,
CapabilityDraftRecord, CreateCapabilityDraftRequest, GetCapabilityDraftRequest,
ListCapabilityDraftsRequest, ListWorkspaceRegisteredSkillsRequest,
RegisterCapabilityDraftRequest, RegisterCapabilityDraftResult, VerifyCapabilityDraftRequest,
create_capability_draft, execute_capability_draft_controlled_get, get_capability_draft,
list_capability_drafts, list_workspace_registered_skills, register_capability_draft,
submit_capability_draft_approval_session_inputs, verify_capability_draft,
CapabilityDraftRecord, CreateCapabilityDraftRequest,
ExecuteCapabilityDraftControlledGetRequest, ExecuteCapabilityDraftControlledGetResult,
GetCapabilityDraftRequest, ListCapabilityDraftsRequest, ListWorkspaceRegisteredSkillsRequest,
RegisterCapabilityDraftRequest, RegisterCapabilityDraftResult,
SubmitCapabilityDraftApprovalSessionInputsRequest,
SubmitCapabilityDraftApprovalSessionInputsResult, VerifyCapabilityDraftRequest,
VerifyCapabilityDraftResult, WorkspaceRegisteredSkillRecord,
};
@@ -52,3 +56,17 @@ pub fn capability_draft_list_registered_skills(
) -> Result<Vec<WorkspaceRegisteredSkillRecord>, String> {
list_workspace_registered_skills(request)
}
#[tauri::command]
pub fn capability_draft_submit_approval_session_inputs(
request: SubmitCapabilityDraftApprovalSessionInputsRequest,
) -> Result<SubmitCapabilityDraftApprovalSessionInputsResult, String> {
submit_capability_draft_approval_session_inputs(request)
}
#[tauri::command]
pub async fn capability_draft_execute_controlled_get(
request: ExecuteCapabilityDraftControlledGetRequest,
) -> Result<ExecuteCapabilityDraftControlledGetResult, String> {
execute_capability_draft_controlled_get(request).await
}
+58 -118
View File
@@ -187,14 +187,14 @@ fn infer_gate_key_from_stage(stage: &str) -> Option<String> {
}
}
fn derive_run_title(run: &AgentRun) -> String {
let parsed_metadata = run
.metadata
.as_ref()
.and_then(|raw| serde_json::from_str::<Value>(raw).ok());
fn parse_run_metadata(run: &AgentRun) -> Option<Value> {
run.metadata
.as_deref()
.and_then(|raw| serde_json::from_str(raw).ok())
}
let metadata_title = parsed_metadata
.as_ref()
fn derive_run_title(run: &AgentRun, metadata: Option<&Value>) -> String {
let metadata_title = metadata
.and_then(|value| {
metadata_string(
value,
@@ -206,8 +206,7 @@ fn derive_run_title(run: &AgentRun) -> String {
return title;
}
let skill_title = parsed_metadata
.as_ref()
let skill_title = metadata
.and_then(|value| value.get("skill_name"))
.and_then(Value::as_str)
.map(str::trim)
@@ -217,8 +216,7 @@ fn derive_run_title(run: &AgentRun) -> String {
return title;
}
let task_title = parsed_metadata
.as_ref()
let task_title = metadata
.and_then(|value| value.get("task_name"))
.and_then(Value::as_str)
.map(str::trim)
@@ -245,14 +243,8 @@ fn derive_run_title(run: &AgentRun) -> String {
}
}
fn derive_run_gate_key(run: &AgentRun, title: &str) -> String {
let parsed_metadata = run
.metadata
.as_ref()
.and_then(|raw| serde_json::from_str::<Value>(raw).ok());
if let Some(value) = parsed_metadata
.as_ref()
fn derive_run_gate_key(run: &AgentRun, metadata: Option<&Value>, title: &str) -> String {
if let Some(value) = metadata
.and_then(|value| value.get("gate_key"))
.and_then(Value::as_str)
.and_then(normalize_gate_key)
@@ -260,18 +252,14 @@ fn derive_run_gate_key(run: &AgentRun, title: &str) -> String {
return value;
}
if let Some(value) = parsed_metadata
.as_ref()
if let Some(value) = metadata
.and_then(|value| metadata_string(value, &["stage"]))
.and_then(infer_gate_key_from_stage)
{
return value;
}
let metadata_probe = parsed_metadata
.as_ref()
.map(|value| value.to_string())
.unwrap_or_default();
let metadata_probe = metadata.map(|value| value.to_string()).unwrap_or_default();
let source_ref_probe = run.source_ref.clone().unwrap_or_default();
let probe = format!(
"{} {} {} {}",
@@ -289,14 +277,8 @@ fn derive_current_gate_key(queue_items: &[GeneralWorkbenchRunTodoItem]) -> Strin
.unwrap_or_else(|| "idle".to_string())
}
fn derive_run_execution_id(run: &AgentRun) -> Option<String> {
let parsed_metadata = run
.metadata
.as_ref()
.and_then(|raw| serde_json::from_str::<Value>(raw).ok());
parsed_metadata
.as_ref()
fn derive_run_execution_id(metadata: Option<&Value>) -> Option<String> {
metadata
.and_then(|value| {
metadata_string(
value,
@@ -311,26 +293,22 @@ fn derive_run_execution_id(run: &AgentRun) -> Option<String> {
.map(str::to_string)
}
fn derive_run_artifact_paths(run: &AgentRun) -> Vec<String> {
let parsed_metadata = run
.metadata
.as_ref()
.and_then(|raw| serde_json::from_str::<Value>(raw).ok());
parsed_metadata
.as_ref()
fn derive_run_artifact_paths(metadata: Option<&Value>) -> Vec<String> {
metadata
.map(extract_artifact_protocol_paths_from_value)
.unwrap_or_default()
}
fn build_terminal_item(run: &AgentRun) -> GeneralWorkbenchRunTerminalItem {
let title = derive_run_title(run);
let gate_key = derive_run_gate_key(run, title.as_str());
let metadata = parse_run_metadata(run);
let metadata_ref = metadata.as_ref();
let title = derive_run_title(run, metadata_ref);
let gate_key = derive_run_gate_key(run, metadata_ref, title.as_str());
GeneralWorkbenchRunTerminalItem {
run_id: run.id.clone(),
execution_id: derive_run_execution_id(run),
execution_id: derive_run_execution_id(metadata_ref),
session_id: run.session_id.clone(),
artifact_paths: derive_run_artifact_paths(run),
artifact_paths: derive_run_artifact_paths(metadata_ref),
title,
gate_key,
status: run.status.clone(),
@@ -341,25 +319,6 @@ fn build_terminal_item(run: &AgentRun) -> GeneralWorkbenchRunTerminalItem {
}
}
fn derive_recent_terminal_items(
runs: &[AgentRun],
limit: usize,
) -> Vec<GeneralWorkbenchRunTerminalItem> {
runs.iter()
.filter(|run| {
matches!(
run.status,
AgentRunStatus::Success
| AgentRunStatus::Error
| AgentRunStatus::Canceled
| AgentRunStatus::Timeout
)
})
.take(limit)
.map(build_terminal_item)
.collect()
}
#[tauri::command]
pub async fn execution_run_get_general_workbench_state(
db: State<'_, DbConnection>,
@@ -373,26 +332,28 @@ pub async fn execution_run_get_general_workbench_state(
let safe_limit = limit.unwrap_or(3).clamp(1, 10);
let tracker = ExecutionTracker::new(db.inner().clone());
let mut runs = tracker.list_runs_by_session(trimmed_session_id, safe_limit * 5)?;
let mut active_runs =
tracker.list_active_runs_by_session(trimmed_session_id, safe_limit * 2)?;
let now = Utc::now();
let stale_run_ids = collect_stale_run_ids(runs.as_slice(), now);
let stale_run_ids = collect_stale_run_ids(active_runs.as_slice(), now);
if !stale_run_ids.is_empty() {
mark_stale_runs_as_timeout(db.inner(), stale_run_ids.as_slice(), &now.to_rfc3339())?;
runs = tracker.list_runs_by_session(trimmed_session_id, safe_limit * 5)?;
active_runs = tracker.list_active_runs_by_session(trimmed_session_id, safe_limit * 2)?;
}
let queue_items: Vec<GeneralWorkbenchRunTodoItem> = runs
let queue_items: Vec<GeneralWorkbenchRunTodoItem> = active_runs
.iter()
.filter(|run| matches!(run.status, AgentRunStatus::Running | AgentRunStatus::Queued))
.take(safe_limit)
.map(|run| {
let title = derive_run_title(run);
let gate_key = derive_run_gate_key(run, title.as_str());
let metadata = parse_run_metadata(run);
let metadata_ref = metadata.as_ref();
let title = derive_run_title(run, metadata_ref);
let gate_key = derive_run_gate_key(run, metadata_ref, title.as_str());
GeneralWorkbenchRunTodoItem {
run_id: run.id.clone(),
execution_id: derive_run_execution_id(run),
execution_id: derive_run_execution_id(metadata_ref),
session_id: run.session_id.clone(),
artifact_paths: derive_run_artifact_paths(run),
artifact_paths: derive_run_artifact_paths(metadata_ref),
title,
gate_key,
status: run.status.clone(),
@@ -410,7 +371,9 @@ pub async fn execution_run_get_general_workbench_state(
};
let current_gate_key = derive_current_gate_key(queue_items.as_slice());
let recent_terminals = derive_recent_terminal_items(runs.as_slice(), safe_limit);
let terminal_runs = tracker.list_terminal_runs_by_session(trimmed_session_id, safe_limit, 0)?;
let recent_terminals: Vec<GeneralWorkbenchRunTerminalItem> =
terminal_runs.iter().map(build_terminal_item).collect();
let latest_terminal = recent_terminals.first().cloned();
Ok(GeneralWorkbenchRunState {
@@ -440,8 +403,8 @@ pub async fn execution_run_list_general_workbench_history(
let tracker = ExecutionTracker::new(db.inner().clone());
let now = Utc::now();
let runs_for_timeout = tracker.list_runs_by_session(trimmed_session_id, safe_limit * 5)?;
let stale_run_ids = collect_stale_run_ids(runs_for_timeout.as_slice(), now);
let active_runs = tracker.list_active_runs_by_session(trimmed_session_id, safe_limit)?;
let stale_run_ids = collect_stale_run_ids(active_runs.as_slice(), now);
if !stale_run_ids.is_empty() {
mark_stale_runs_as_timeout(db.inner(), stale_run_ids.as_slice(), &now.to_rfc3339())?;
}
@@ -493,14 +456,16 @@ mod tests {
let run = sample_run_with_metadata(Some(serde_json::json!({
"gate_key": "publish_confirm"
})));
let gate_key = derive_run_gate_key(&run, "任意标题");
let metadata = parse_run_metadata(&run);
let gate_key = derive_run_gate_key(&run, metadata.as_ref(), "任意标题");
assert_eq!(gate_key, "publish_confirm");
}
#[test]
fn derive_run_gate_key_should_fallback_to_probe_inference() {
let run = sample_run_with_metadata(None);
let gate_key = derive_run_gate_key(&run, "执行选题调研");
let metadata = parse_run_metadata(&run);
let gate_key = derive_run_gate_key(&run, metadata.as_ref(), "执行选题调研");
assert_eq!(gate_key, "topic_select");
}
@@ -580,8 +545,9 @@ mod tests {
"execution_id": "exec-12345",
"version_id": "version-legacy",
})));
let metadata = parse_run_metadata(&run);
assert_eq!(
derive_run_execution_id(&run),
derive_run_execution_id(metadata.as_ref()),
Some("exec-12345".to_string())
);
}
@@ -595,9 +561,10 @@ mod tests {
"content-posts/demo.cover.json"
],
})));
let metadata = parse_run_metadata(&run);
assert_eq!(
derive_run_artifact_paths(&run),
derive_run_artifact_paths(metadata.as_ref()),
vec![
"content-posts/demo.md".to_string(),
"content-posts/demo.cover.json".to_string(),
@@ -611,8 +578,12 @@ mod tests {
"harness_theme": "general",
"stage": "publish_prep"
})));
let metadata = parse_run_metadata(&run);
assert_eq!(derive_run_gate_key(&run, "社媒发布包"), "publish_confirm");
assert_eq!(
derive_run_gate_key(&run, metadata.as_ref(), "社媒发布包"),
"publish_confirm"
);
}
#[test]
@@ -621,8 +592,9 @@ mod tests {
"harness_theme": "general",
"version_label": "社媒初稿"
})));
let metadata = parse_run_metadata(&run);
assert_eq!(derive_run_title(&run), "社媒初稿");
assert_eq!(derive_run_title(&run, metadata.as_ref()), "社媒初稿");
}
#[test]
@@ -630,9 +602,10 @@ mod tests {
let run = sample_run_with_metadata(Some(serde_json::json!({
"source_file_name": "content-posts/demo.md"
})));
let metadata = parse_run_metadata(&run);
assert_eq!(
derive_run_artifact_paths(&run),
derive_run_artifact_paths(metadata.as_ref()),
vec!["content-posts/demo.md".to_string()]
);
}
@@ -647,9 +620,10 @@ mod tests {
"absolute_path": " /tmp\\content-posts\\final.md "
}
})));
let metadata = parse_run_metadata(&run);
assert_eq!(
derive_run_artifact_paths(&run),
derive_run_artifact_paths(metadata.as_ref()),
vec![
"content-posts/nested.md".to_string(),
"/tmp/content-posts/final.md".to_string(),
@@ -657,40 +631,6 @@ mod tests {
);
}
#[test]
fn derive_recent_terminal_items_should_keep_multiple_terminal_runs() {
let mut latest_error_run = sample_run_with_metadata(Some(serde_json::json!({
"run_title": "最新失败运行"
})));
latest_error_run.id = "run-error".to_string();
latest_error_run.status = AgentRunStatus::Error;
latest_error_run.started_at = "2026-03-06T05:00:00Z".to_string();
latest_error_run.finished_at = Some("2026-03-06T05:02:00Z".to_string());
let mut running_run = sample_run_with_metadata(Some(serde_json::json!({
"run_title": "运行中"
})));
running_run.id = "run-running".to_string();
running_run.status = AgentRunStatus::Running;
running_run.started_at = "2026-03-06T04:30:00Z".to_string();
running_run.finished_at = None;
let mut previous_success_run = sample_run_with_metadata(Some(serde_json::json!({
"run_title": "上一轮成功运行"
})));
previous_success_run.id = "run-success".to_string();
previous_success_run.status = AgentRunStatus::Success;
previous_success_run.started_at = "2026-03-06T04:00:00Z".to_string();
previous_success_run.finished_at = Some("2026-03-06T04:08:00Z".to_string());
let recent_terminals =
derive_recent_terminal_items(&[latest_error_run, running_run, previous_success_run], 3);
assert_eq!(recent_terminals.len(), 2);
assert_eq!(recent_terminals[0].run_id, "run-error");
assert_eq!(recent_terminals[1].run_id, "run-success");
}
#[test]
fn collect_stale_run_ids_should_only_pick_expired_non_terminal_runs() {
let now = Utc::now();
+14 -5
View File
@@ -6,12 +6,13 @@
use lime_knowledge::{
compile_knowledge_pack, get_knowledge_pack, import_knowledge_source, list_knowledge_packs,
resolve_knowledge_context, set_default_knowledge_pack, update_knowledge_pack_status,
KnowledgeCompilePackRequest, KnowledgeCompilePackResponse, KnowledgeContextResolution,
KnowledgeGetPackRequest, KnowledgeImportSourceRequest, KnowledgeImportSourceResponse,
KnowledgeListPacksRequest, KnowledgeListPacksResponse, KnowledgePackDetail,
KnowledgeResolveContextRequest, KnowledgeSetDefaultPackRequest,
validate_knowledge_context_run, KnowledgeCompilePackRequest, KnowledgeCompilePackResponse,
KnowledgeContextResolution, KnowledgeGetPackRequest, KnowledgeImportSourceRequest,
KnowledgeImportSourceResponse, KnowledgeListPacksRequest, KnowledgeListPacksResponse,
KnowledgePackDetail, KnowledgeResolveContextRequest, KnowledgeSetDefaultPackRequest,
KnowledgeSetDefaultPackResponse, KnowledgeUpdatePackStatusRequest,
KnowledgeUpdatePackStatusResponse,
KnowledgeUpdatePackStatusResponse, KnowledgeValidateContextRunRequest,
KnowledgeValidateContextRunResponse,
};
/// 导入知识包来源资料
@@ -69,3 +70,11 @@ pub async fn knowledge_resolve_context(
) -> Result<KnowledgeContextResolution, String> {
resolve_knowledge_context(request)
}
/// 校验 context-resolution run record
#[tauri::command]
pub async fn knowledge_validate_context_run(
request: KnowledgeValidateContextRunRequest,
) -> Result<KnowledgeValidateContextRunResponse, String> {
validate_knowledge_context_run(request)
}
+879 -2
View File
@@ -1,12 +1,15 @@
//! 图层化设计工程导出命令。
//! 图层化设计工程导出与扁平图分析命令。
//!
//! 该命令只负责把 `LayeredDesignDocument` 的导出投影写入项目目录,
//! 该模块只负责把 `LayeredDesignDocument` 的导出投影写入项目目录,
//! 或把扁平图分析结果投影回 `LayeredDesignDocument.extraction`,
//! 不定义新的设计事实源。
use base64::{engine::general_purpose::STANDARD, Engine as _};
use image::{DynamicImage, ImageFormat, Rgba, RgbaImage};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::io::Cursor;
use std::path::{Component, Path, PathBuf};
use std::time::Duration;
use url::Url;
@@ -15,6 +18,7 @@ const LAYERED_DESIGN_EXPORT_ROOT: &str = ".lime/layered-designs";
const MAX_LAYERED_DESIGN_EXPORT_FILES: usize = 512;
const MAX_REMOTE_LAYERED_DESIGN_ASSET_BYTES: usize = 20 * 1024 * 1024;
const REMOTE_LAYERED_DESIGN_ASSET_TIMEOUT_SECS: u64 = 20;
const MAX_LAYERED_DESIGN_OCR_IMAGE_BYTES: usize = 20 * 1024 * 1024;
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -45,6 +49,36 @@ pub struct ReadLayeredDesignProjectExportRequest {
pub export_directory_relative_path: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecognizeLayeredDesignTextRequest {
pub image_src: String,
pub width: u32,
pub height: u32,
#[serde(default)]
pub candidate_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnalyzeLayeredDesignFlatImageRequest {
pub image: AnalyzeLayeredDesignFlatImageInput,
#[serde(default)]
pub created_at: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnalyzeLayeredDesignFlatImageInput {
pub src: String,
pub width: u32,
pub height: u32,
#[serde(default)]
pub mime_type: Option<String>,
#[serde(default)]
pub has_alpha: Option<bool>,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SaveLayeredDesignProjectExportOutput {
@@ -75,6 +109,122 @@ pub struct ReadLayeredDesignProjectExportOutput {
pub updated_at_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignTextBoundingBox {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignRecognizedTextBlock {
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub bounding_box: Option<LayeredDesignTextBoundingBox>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RecognizeLayeredDesignTextOutput {
pub supported: bool,
pub engine: String,
pub blocks: Vec<LayeredDesignRecognizedTextBlock>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AnalyzeLayeredDesignFlatImageOutput {
pub supported: bool,
pub engine: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<LayeredDesignStructuredAnalyzerResult>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignStructuredAnalyzerResult {
pub analyzer: LayeredDesignStructuredAnalyzerInfo,
pub generated_at: String,
pub candidates: Vec<LayeredDesignStructuredCandidate>,
pub clean_plate: LayeredDesignStructuredCleanPlate,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignStructuredAnalyzerInfo {
pub kind: String,
pub label: String,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignStructuredCandidate {
pub id: String,
#[serde(rename = "type")]
pub candidate_type: String,
pub role: String,
pub name: String,
pub confidence: f32,
pub rect: LayeredDesignTextBoundingBox,
pub z_index: i32,
pub image: LayeredDesignStructuredAsset,
#[serde(skip_serializing_if = "Option::is_none")]
pub mask: Option<LayeredDesignStructuredAsset>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignStructuredAsset {
pub id: String,
pub kind: String,
pub src: String,
pub width: u32,
pub height: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_alpha: Option<bool>,
pub created_at: String,
pub params: Value,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LayeredDesignStructuredCleanPlate {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub asset: Option<LayeredDesignStructuredAsset>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone)]
struct NativeAnalyzerCandidateSpec {
id: &'static str,
name: &'static str,
role: &'static str,
kind: &'static str,
rect: NativeAnalyzerRect,
confidence: f32,
z_index: i32,
}
#[derive(Debug, Clone, Copy)]
struct NativeAnalyzerRect {
x: u32,
y: u32,
width: u32,
height: u32,
}
#[derive(Debug, Clone)]
struct PreparedExportFile {
relative_path: PathBuf,
@@ -214,6 +364,270 @@ fn require_absolute_project_root(project_root_path: &str) -> Result<PathBuf, Str
Ok(project_root)
}
fn decode_layered_design_ocr_image_data_url(image_src: &str) -> Result<Option<Vec<u8>>, String> {
let Some(rest) = image_src.trim().strip_prefix("data:") else {
return Ok(None);
};
let Some((header, encoded)) = rest.split_once(',') else {
return Err("OCR 图片 data URL 格式无效".to_string());
};
let mut header_parts = header.split(';');
let mime_type = header_parts.next().unwrap_or_default();
let is_base64 = header_parts.any(|part| part.eq_ignore_ascii_case("base64"));
if !mime_type.to_ascii_lowercase().starts_with("image/") {
return Ok(None);
}
if !is_base64 {
return Err("OCR 图片 data URL 必须使用 base64 编码".to_string());
}
if encoded.len() > MAX_LAYERED_DESIGN_OCR_IMAGE_BYTES * 2 {
return Err("OCR 图片 data URL 超过大小上限".to_string());
}
let bytes = STANDARD
.decode(encoded.trim())
.map_err(|error| format!("OCR 图片 base64 无效: {error}"))?;
if bytes.len() > MAX_LAYERED_DESIGN_OCR_IMAGE_BYTES {
return Err("OCR 图片超过大小上限".to_string());
}
Ok(Some(bytes))
}
fn decode_layered_design_analyzer_png_data_url(image_src: &str) -> Result<Option<Vec<u8>>, String> {
let Some(rest) = image_src.trim().strip_prefix("data:") else {
return Ok(None);
};
let Some((header, encoded)) = rest.split_once(',') else {
return Err("Analyzer 图片 data URL 格式无效".to_string());
};
let mut header_parts = header.split(';');
let mime_type = header_parts.next().unwrap_or_default().to_ascii_lowercase();
let is_base64 = header_parts.any(|part| part.eq_ignore_ascii_case("base64"));
if mime_type != "image/png" {
return Ok(None);
}
if !is_base64 {
return Err("Analyzer 图片 data URL 必须使用 base64 编码".to_string());
}
if encoded.len() > MAX_LAYERED_DESIGN_OCR_IMAGE_BYTES * 2 {
return Err("Analyzer 图片 data URL 超过大小上限".to_string());
}
let bytes = STANDARD
.decode(encoded.trim())
.map_err(|error| format!("Analyzer 图片 base64 无效: {error}"))?;
if bytes.len() > MAX_LAYERED_DESIGN_OCR_IMAGE_BYTES {
return Err("Analyzer 图片超过大小上限".to_string());
}
Ok(Some(bytes))
}
fn native_analyzer_unsupported_output(
message: impl Into<String>,
) -> AnalyzeLayeredDesignFlatImageOutput {
AnalyzeLayeredDesignFlatImageOutput {
supported: false,
engine: "native_heuristic_analyzer".to_string(),
result: None,
message: Some(message.into()),
}
}
fn clamp_u32(value: u32, min: u32, max: u32) -> u32 {
value.max(min).min(max)
}
fn native_analyzer_crop_rect(
image_width: u32,
image_height: u32,
left_ratio: f32,
top_ratio: f32,
width_ratio: f32,
height_ratio: f32,
) -> NativeAnalyzerRect {
let width = clamp_u32(
(image_width as f32 * width_ratio).round() as u32,
72_u32.min(image_width),
image_width,
);
let height = clamp_u32(
(image_height as f32 * height_ratio).round() as u32,
72_u32.min(image_height),
image_height,
);
let x = clamp_u32(
(image_width as f32 * left_ratio).round() as u32,
0,
image_width.saturating_sub(width),
);
let y = clamp_u32(
(image_height as f32 * top_ratio).round() as u32,
0,
image_height.saturating_sub(height),
);
NativeAnalyzerRect {
x,
y,
width,
height,
}
}
fn build_native_analyzer_candidate_specs(
image_width: u32,
image_height: u32,
) -> Vec<NativeAnalyzerCandidateSpec> {
vec![
NativeAnalyzerCandidateSpec {
id: "subject",
name: "主体候选",
role: "subject",
kind: "subject",
rect: native_analyzer_crop_rect(image_width, image_height, 0.16, 0.16, 0.68, 0.7),
confidence: 0.74,
z_index: 20,
},
NativeAnalyzerCandidateSpec {
id: "headline",
name: "标题文字候选",
role: "text",
kind: "text_raster",
rect: native_analyzer_crop_rect(image_width, image_height, 0.12, 0.06, 0.76, 0.18),
confidence: 0.62,
z_index: 40,
},
NativeAnalyzerCandidateSpec {
id: "logo",
name: "Logo 候选",
role: "logo",
kind: "logo",
rect: native_analyzer_crop_rect(image_width, image_height, 0.06, 0.06, 0.28, 0.16),
confidence: 0.48,
z_index: 48,
},
NativeAnalyzerCandidateSpec {
id: "fragment",
name: "边角碎片",
role: "background_fragment",
kind: "effect",
rect: native_analyzer_crop_rect(image_width, image_height, 0.72, 0.72, 0.22, 0.22),
confidence: 0.22,
z_index: 56,
},
]
}
fn is_inside_native_analyzer_ellipse(x: u32, y: u32, width: u32, height: u32) -> bool {
let center_x = width as f32 / 2.0;
let center_y = height as f32 / 2.0;
let radius_x = (width as f32 * 0.42).max(1.0);
let radius_y = (height as f32 * 0.46).max(1.0);
let dx = (x as f32 + 0.5 - center_x) / radius_x;
let dy = (y as f32 + 0.5 - center_y) / radius_y;
dx * dx + dy * dy <= 1.0
}
fn encode_rgba_png_data_url(image: &RgbaImage) -> Result<String, String> {
let mut output = Vec::new();
let dynamic = DynamicImage::ImageRgba8(image.clone());
dynamic
.write_to(&mut Cursor::new(&mut output), ImageFormat::Png)
.map_err(|error| format!("编码 analyzer PNG 失败: {error}"))?;
Ok(format!("data:image/png;base64,{}", STANDARD.encode(output)))
}
fn crop_native_analyzer_image(source: &RgbaImage, rect: NativeAnalyzerRect) -> RgbaImage {
image::imageops::crop_imm(source, rect.x, rect.y, rect.width, rect.height).to_image()
}
fn create_native_analyzer_mask(rect: NativeAnalyzerRect) -> RgbaImage {
let mut mask = RgbaImage::from_pixel(rect.width, rect.height, Rgba([0, 0, 0, 255]));
for y in 0..rect.height {
for x in 0..rect.width {
if is_inside_native_analyzer_ellipse(x, y, rect.width, rect.height) {
mask.put_pixel(x, y, Rgba([255, 255, 255, 255]));
}
}
}
mask
}
fn apply_native_analyzer_mask(crop: &RgbaImage, rect: NativeAnalyzerRect) -> RgbaImage {
let mut output = crop.clone();
for y in 0..rect.height {
for x in 0..rect.width {
if !is_inside_native_analyzer_ellipse(x, y, rect.width, rect.height) {
let pixel = output.get_pixel_mut(x, y);
pixel.0[3] = 0;
}
}
}
output
}
fn average_native_analyzer_corner_color(source: &RgbaImage) -> Rgba<u8> {
let width = source.width().saturating_sub(1);
let height = source.height().saturating_sub(1);
let samples = [
source.get_pixel(0, 0),
source.get_pixel(width, 0),
source.get_pixel(0, height),
source.get_pixel(width, height),
];
let channel = |index: usize| -> u8 {
(samples
.iter()
.map(|pixel| u32::from(pixel.0[index]))
.sum::<u32>()
/ samples.len() as u32)
.min(255) as u8
};
Rgba([channel(0), channel(1), channel(2), 250])
}
fn create_native_analyzer_clean_plate(source: &RgbaImage, rect: NativeAnalyzerRect) -> RgbaImage {
let mut output = source.clone();
let fill = average_native_analyzer_corner_color(source);
for y in 0..rect.height {
for x in 0..rect.width {
if is_inside_native_analyzer_ellipse(x, y, rect.width, rect.height) {
let target_x = rect.x + x;
let target_y = rect.y + y;
if target_x < output.width() && target_y < output.height() {
output.put_pixel(target_x, target_y, fill);
}
}
}
}
output
}
fn native_analyzer_rect_value(rect: NativeAnalyzerRect) -> Value {
serde_json::json!({
"x": rect.x,
"y": rect.y,
"width": rect.width,
"height": rect.height,
})
}
fn unsupported_layered_design_text_ocr_output(
message: impl Into<String>,
) -> RecognizeLayeredDesignTextOutput {
RecognizeLayeredDesignTextOutput {
supported: false,
engine: "native_ocr_unavailable".to_string(),
blocks: Vec::new(),
message: Some(message.into()),
}
}
fn ensure_layered_design_export_relative_dir(relative_path: PathBuf) -> Result<PathBuf, String> {
let export_root = Path::new(LAYERED_DESIGN_EXPORT_ROOT);
if relative_path == export_root || relative_path.starts_with(export_root) {
@@ -839,6 +1253,346 @@ pub(crate) fn read_layered_design_project_export_inner(
})
}
pub(crate) fn recognize_layered_design_text_inner(
request: RecognizeLayeredDesignTextRequest,
) -> Result<RecognizeLayeredDesignTextOutput, String> {
if request.width == 0 || request.height == 0 {
return Err("OCR 图片宽高必须大于 0".to_string());
}
let _candidate_id = request.candidate_id.as_deref();
let Some(bytes) = decode_layered_design_ocr_image_data_url(&request.image_src)? else {
return Ok(unsupported_layered_design_text_ocr_output(
"当前 native OCR 只接收 data:image/*;base64 图片来源",
));
};
recognize_layered_design_text_from_image_bytes(&bytes, request.width, request.height)
}
#[cfg(target_os = "macos")]
#[link(name = "Vision", kind = "framework")]
unsafe extern "C" {}
#[cfg(target_os = "macos")]
#[allow(deprecated)]
fn recognize_layered_design_text_from_image_bytes(
bytes: &[u8],
width: u32,
height: u32,
) -> Result<RecognizeLayeredDesignTextOutput, String> {
use cocoa::base::{id, nil, BOOL, YES};
use cocoa::foundation::{NSAutoreleasePool, NSRect, NSUInteger};
use objc::runtime::Class;
use objc::{class, msg_send, sel, sel_impl};
use std::ffi::{c_void, CStr};
use std::os::raw::c_char;
unsafe fn nsstring_to_string(value: id) -> Option<String> {
if value == nil {
return None;
}
let c_string: *const c_char = msg_send![value, UTF8String];
if c_string.is_null() {
return None;
}
Some(CStr::from_ptr(c_string).to_string_lossy().into_owned())
}
unsafe fn nserror_to_string(error: id) -> Option<String> {
if error == nil {
return None;
}
let description: id = msg_send![error, localizedDescription];
nsstring_to_string(description)
}
fn convert_vision_bounding_box(
bounding_box: NSRect,
image_width: u32,
image_height: u32,
) -> LayeredDesignTextBoundingBox {
let width = f64::from(image_width);
let height = f64::from(image_height);
let box_width = (bounding_box.size.width as f64 * width).max(1.0);
let box_height = (bounding_box.size.height as f64 * height).max(1.0);
let x = (bounding_box.origin.x as f64 * width).clamp(0.0, width);
let y = ((1.0 - bounding_box.origin.y as f64 - bounding_box.size.height as f64) * height)
.clamp(0.0, height);
LayeredDesignTextBoundingBox {
x,
y,
width: box_width.min((width - x).max(1.0)),
height: box_height.min((height - y).max(1.0)),
}
}
unsafe {
let pool = NSAutoreleasePool::new(nil);
let data: id = msg_send![
class!(NSData),
dataWithBytes: bytes.as_ptr() as *const c_void
length: bytes.len() as NSUInteger
];
if data == nil {
pool.drain();
return Err("创建 OCR 图片数据失败".to_string());
}
let Some(request_class) = Class::get("VNRecognizeTextRequest") else {
pool.drain();
return Ok(unsupported_layered_design_text_ocr_output(
"当前 macOS 版本不支持 Vision OCR",
));
};
let Some(handler_class) = Class::get("VNImageRequestHandler") else {
pool.drain();
return Ok(unsupported_layered_design_text_ocr_output(
"当前 macOS 版本不支持 Vision OCR 图片处理器",
));
};
let request: id = msg_send![request_class, alloc];
let request: id = msg_send![request, init];
if request == nil {
pool.drain();
return Err("创建 Vision OCR 请求失败".to_string());
}
let accurate_level = 1usize;
let _: () = msg_send![request, setRecognitionLevel: accurate_level];
let _: () = msg_send![request, setUsesLanguageCorrection: YES];
let options: id = msg_send![class!(NSDictionary), dictionary];
let handler: id = msg_send![handler_class, alloc];
let handler: id = msg_send![handler, initWithData: data options: options];
if handler == nil {
pool.drain();
return Err("创建 Vision OCR 图片处理器失败".to_string());
}
let requests: id = msg_send![class!(NSArray), arrayWithObject: request];
let mut error: id = nil;
let ok: BOOL = msg_send![handler, performRequests: requests error: &mut error];
if ok != YES {
let message =
nserror_to_string(error).unwrap_or_else(|| "Vision OCR 执行失败".to_string());
pool.drain();
return Err(message);
}
let observations: id = msg_send![request, results];
let count: NSUInteger = if observations == nil {
0
} else {
msg_send![observations, count]
};
let mut blocks = Vec::new();
for index in 0..count {
let observation: id = msg_send![observations, objectAtIndex: index];
if observation == nil {
continue;
}
let candidates: id = msg_send![observation, topCandidates: 1usize];
if candidates == nil {
continue;
}
let text_candidate: id = msg_send![candidates, firstObject];
if text_candidate == nil {
continue;
}
let text_value: id = msg_send![text_candidate, string];
let Some(text) = nsstring_to_string(text_value).map(|value| value.trim().to_string())
else {
continue;
};
if text.is_empty() {
continue;
}
let bounding_box: NSRect = msg_send![observation, boundingBox];
let confidence: f32 = msg_send![text_candidate, confidence];
blocks.push(LayeredDesignRecognizedTextBlock {
text,
bounding_box: Some(convert_vision_bounding_box(bounding_box, width, height)),
confidence: confidence.is_finite().then_some(confidence),
});
}
pool.drain();
Ok(RecognizeLayeredDesignTextOutput {
supported: true,
engine: "macos_vision".to_string(),
blocks,
message: None,
})
}
}
#[cfg(not(target_os = "macos"))]
fn recognize_layered_design_text_from_image_bytes(
_bytes: &[u8],
_width: u32,
_height: u32,
) -> Result<RecognizeLayeredDesignTextOutput, String> {
Ok(unsupported_layered_design_text_ocr_output(
"当前平台尚未接入 native OCR 引擎",
))
}
pub(crate) fn analyze_layered_design_flat_image_inner(
request: AnalyzeLayeredDesignFlatImageRequest,
) -> Result<AnalyzeLayeredDesignFlatImageOutput, String> {
if request.image.width == 0 || request.image.height == 0 {
return Err("Analyzer 图片宽高必须大于 0".to_string());
}
if request
.image
.mime_type
.as_deref()
.map(|mime| !mime.eq_ignore_ascii_case("image/png"))
.unwrap_or(false)
{
return Ok(native_analyzer_unsupported_output(
"当前 native analyzer 首刀只支持 image/png",
));
}
let Some(bytes) = decode_layered_design_analyzer_png_data_url(&request.image.src)? else {
return Ok(native_analyzer_unsupported_output(
"当前 native analyzer 只接收 data:image/png;base64 图片来源",
));
};
let source = image::load_from_memory_with_format(&bytes, ImageFormat::Png)
.map_err(|error| format!("解析 analyzer PNG 失败: {error}"))?
.to_rgba8();
let image_width = source.width().max(1);
let image_height = source.height().max(1);
let generated_at = request
.created_at
.filter(|value| !value.trim().is_empty())
.unwrap_or_else(|| chrono::Utc::now().to_rfc3339());
let specs = build_native_analyzer_candidate_specs(image_width, image_height);
let subject_id = specs
.iter()
.find(|spec| spec.role == "subject")
.map(|spec| spec.id);
let mut subject_rect = None;
let mut candidates = Vec::new();
for spec in specs {
let is_subject = Some(spec.id) == subject_id;
if is_subject {
subject_rect = Some(spec.rect);
}
let crop = crop_native_analyzer_image(&source, spec.rect);
let image_rgba = if is_subject {
apply_native_analyzer_mask(&crop, spec.rect)
} else {
crop
};
let image_src = encode_rgba_png_data_url(&image_rgba)?;
let mask = if is_subject {
let mask = create_native_analyzer_mask(spec.rect);
Some(LayeredDesignStructuredAsset {
id: format!("{}-mask", spec.id),
kind: "mask".to_string(),
src: encode_rgba_png_data_url(&mask)?,
width: spec.rect.width,
height: spec.rect.height,
has_alpha: Some(false),
created_at: generated_at.clone(),
params: serde_json::json!({
"seed": "native_heuristic_subject_mask",
}),
})
} else {
None
};
candidates.push(LayeredDesignStructuredCandidate {
id: format!("{}-candidate", spec.id),
candidate_type: "image".to_string(),
role: spec.role.to_string(),
name: spec.name.to_string(),
confidence: spec.confidence,
rect: LayeredDesignTextBoundingBox {
x: f64::from(spec.rect.x),
y: f64::from(spec.rect.y),
width: f64::from(spec.rect.width),
height: f64::from(spec.rect.height),
},
z_index: spec.z_index,
image: LayeredDesignStructuredAsset {
id: format!("{}-asset", spec.id),
kind: spec.kind.to_string(),
src: image_src,
width: spec.rect.width,
height: spec.rect.height,
has_alpha: Some(is_subject || request.image.has_alpha.unwrap_or(false)),
created_at: generated_at.clone(),
params: serde_json::json!({
"seed": if is_subject {
"native_heuristic_subject_masked"
} else {
"native_heuristic_crop"
},
"inputMimeType": request.image.mime_type.as_deref().unwrap_or("image/png"),
"outputMimeType": "image/png",
"sourceRect": native_analyzer_rect_value(spec.rect),
}),
},
mask,
});
}
let clean_plate = if let Some(rect) = subject_rect {
let clean_plate = create_native_analyzer_clean_plate(&source, rect);
LayeredDesignStructuredCleanPlate {
status: None,
asset: Some(LayeredDesignStructuredAsset {
id: "native-heuristic-clean-plate-asset".to_string(),
kind: "clean_plate".to_string(),
src: encode_rgba_png_data_url(&clean_plate)?,
width: image_width,
height: image_height,
has_alpha: Some(false),
created_at: generated_at.clone(),
params: serde_json::json!({
"seed": "native_heuristic_clean_plate",
}),
}),
message: Some(
"当前 clean plate 来自 Tauri native heuristic 近似修补;进入编辑后仍需人工核对主体移动边缘。"
.to_string(),
),
}
} else {
LayeredDesignStructuredCleanPlate {
status: Some("not_requested".to_string()),
asset: None,
message: Some("Tauri native heuristic 未找到主体候选。".to_string()),
}
};
Ok(AnalyzeLayeredDesignFlatImageOutput {
supported: true,
engine: "native_heuristic_analyzer".to_string(),
result: Some(LayeredDesignStructuredAnalyzerResult {
analyzer: LayeredDesignStructuredAnalyzerInfo {
kind: "local_heuristic".to_string(),
label: "Tauri native heuristic analyzer".to_string(),
},
generated_at,
candidates,
clean_plate,
}),
message: None,
})
}
#[tauri::command]
pub async fn save_layered_design_project_export(
request: SaveLayeredDesignProjectExportRequest,
@@ -853,6 +1607,20 @@ pub fn read_layered_design_project_export(
read_layered_design_project_export_inner(request)
}
#[tauri::command]
pub fn recognize_layered_design_text(
request: RecognizeLayeredDesignTextRequest,
) -> Result<RecognizeLayeredDesignTextOutput, String> {
recognize_layered_design_text_inner(request)
}
#[tauri::command]
pub fn analyze_layered_design_flat_image(
request: AnalyzeLayeredDesignFlatImageRequest,
) -> Result<AnalyzeLayeredDesignFlatImageOutput, String> {
analyze_layered_design_flat_image_inner(request)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -880,6 +1648,115 @@ mod tests {
}
}
fn test_png_data_url(width: u32, height: u32) -> String {
let mut image = RgbaImage::new(width, height);
for y in 0..height {
for x in 0..width {
image.put_pixel(
x,
y,
Rgba([((x * 23) % 255) as u8, ((y * 29) % 255) as u8, 180, 255]),
);
}
}
encode_rgba_png_data_url(&image).expect("test PNG should encode")
}
#[test]
fn recognize_layered_design_text_should_skip_unsupported_source() {
let output = recognize_layered_design_text_inner(RecognizeLayeredDesignTextRequest {
image_src: "https://example.com/headline.png".to_string(),
width: 320,
height: 120,
candidate_id: Some("headline-candidate".to_string()),
})
.expect("recognize text");
assert!(!output.supported);
assert_eq!(output.engine, "native_ocr_unavailable");
assert!(output.blocks.is_empty());
assert!(output
.message
.as_deref()
.unwrap_or_default()
.contains("data:image"));
}
#[test]
fn recognize_layered_design_text_should_reject_empty_dimensions() {
let error = recognize_layered_design_text_inner(RecognizeLayeredDesignTextRequest {
image_src: "data:image/png;base64,ZmFrZQ==".to_string(),
width: 0,
height: 120,
candidate_id: None,
})
.expect_err("empty dimensions should fail");
assert!(error.contains("宽高"));
}
#[test]
fn analyze_layered_design_flat_image_should_return_structured_native_result() {
let output =
analyze_layered_design_flat_image_inner(AnalyzeLayeredDesignFlatImageRequest {
image: AnalyzeLayeredDesignFlatImageInput {
src: test_png_data_url(64, 96),
width: 64,
height: 96,
mime_type: Some("image/png".to_string()),
has_alpha: Some(false),
},
created_at: Some("2026-05-07T00:00:00.000Z".to_string()),
})
.expect("analyze flat image");
assert!(output.supported);
assert_eq!(output.engine, "native_heuristic_analyzer");
let result = output.result.expect("structured result");
assert_eq!(result.analyzer.label, "Tauri native heuristic analyzer");
assert_eq!(result.generated_at, "2026-05-07T00:00:00.000Z");
assert_eq!(result.candidates.len(), 4);
let subject = result
.candidates
.iter()
.find(|candidate| candidate.id == "subject-candidate")
.expect("subject candidate");
assert_eq!(subject.role, "subject");
assert!(subject.image.src.starts_with("data:image/png;base64,"));
assert!(subject.mask.is_some());
assert!(result
.clean_plate
.asset
.as_ref()
.expect("clean plate")
.src
.starts_with("data:image/png;base64,"));
}
#[test]
fn analyze_layered_design_flat_image_should_skip_unsupported_source() {
let output =
analyze_layered_design_flat_image_inner(AnalyzeLayeredDesignFlatImageRequest {
image: AnalyzeLayeredDesignFlatImageInput {
src: "https://example.com/flat.png".to_string(),
width: 64,
height: 96,
mime_type: Some("image/png".to_string()),
has_alpha: None,
},
created_at: None,
})
.expect("unsupported source should not fail command");
assert!(!output.supported);
assert!(output.result.is_none());
assert!(output
.message
.as_deref()
.unwrap_or_default()
.contains("data:image/png"));
}
fn minimal_request(project_root_path: String) -> SaveLayeredDesignProjectExportRequest {
SaveLayeredDesignProjectExportRequest {
project_root_path,
+22 -1
View File
@@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::env;
use std::fs;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::{command, AppHandle, Emitter, Runtime};
@@ -481,7 +482,11 @@ async fn fetch_configured_voice_model_catalog(
return Ok(None);
};
let client = reqwest::Client::builder()
let mut client_builder = reqwest::Client::builder();
if is_loopback_voice_model_catalog_url(&catalog_url) {
client_builder = client_builder.no_proxy();
}
let client = client_builder
.build()
.map_err(|error| format!("创建 HTTP 客户端失败: {error}"))?;
let payload = client
@@ -512,6 +517,22 @@ fn parse_limecore_voice_model_catalog(
map_limecore_voice_model_catalog(response)
}
fn is_loopback_voice_model_catalog_url(catalog_url: &str) -> bool {
let Ok(url) = reqwest::Url::parse(catalog_url) else {
return false;
};
let Some(host) = url.host_str() else {
return false;
};
host.eq_ignore_ascii_case("localhost")
|| host
.parse::<IpAddr>()
.map(|addr| addr.is_loopback())
.unwrap_or(false)
}
fn map_limecore_voice_model_catalog(
response: LimecoreVoiceModelCatalogResponse,
) -> Result<Vec<VoiceModelCatalogEntry>, String> {
+1 -1
View File
@@ -190,7 +190,7 @@ pub async fn handle_command(
return Ok(result);
}
if let Some(result) = capability_drafts::try_handle(cmd, args.as_ref())? {
if let Some(result) = capability_drafts::try_handle(cmd, args.as_ref()).await? {
return Ok(result);
}
@@ -1,16 +1,18 @@
use super::{args_or_default, parse_nested_arg};
use crate::commands::capability_draft_cmd::{
capability_draft_create, capability_draft_get, capability_draft_list,
capability_draft_list_registered_skills, capability_draft_register, capability_draft_verify,
capability_draft_create, capability_draft_execute_controlled_get, capability_draft_get,
capability_draft_list, capability_draft_list_registered_skills, capability_draft_register,
capability_draft_submit_approval_session_inputs, capability_draft_verify,
};
use crate::services::capability_draft_service::{
CreateCapabilityDraftRequest, GetCapabilityDraftRequest, ListCapabilityDraftsRequest,
ListWorkspaceRegisteredSkillsRequest, RegisterCapabilityDraftRequest,
CreateCapabilityDraftRequest, ExecuteCapabilityDraftControlledGetRequest,
GetCapabilityDraftRequest, ListCapabilityDraftsRequest, ListWorkspaceRegisteredSkillsRequest,
RegisterCapabilityDraftRequest, SubmitCapabilityDraftApprovalSessionInputsRequest,
VerifyCapabilityDraftRequest,
};
use serde_json::Value as JsonValue;
pub(super) fn try_handle(
pub(super) async fn try_handle(
cmd: &str,
args: Option<&JsonValue>,
) -> Result<Option<JsonValue>, Box<dyn std::error::Error>> {
@@ -63,6 +65,25 @@ pub(super) fn try_handle(
.map_err(|error| format!("读取 Workspace 已注册能力失败: {error}"))?,
)?
}
"capability_draft_submit_approval_session_inputs" => {
let args = args_or_default(args);
let request: SubmitCapabilityDraftApprovalSessionInputsRequest =
parse_nested_arg(&args, "request")?;
serde_json::to_value(
capability_draft_submit_approval_session_inputs(request)
.map_err(|error| format!("校验 approval session 输入失败: {error}"))?,
)?
}
"capability_draft_execute_controlled_get" => {
let args = args_or_default(args);
let request: ExecuteCapabilityDraftControlledGetRequest =
parse_nested_arg(&args, "request")?;
serde_json::to_value(
capability_draft_execute_controlled_get(request)
.await
.map_err(|error| format!("执行受控 GET 失败: {error}"))?,
)?
}
_ => return Ok(None),
};
@@ -89,6 +89,26 @@ pub(super) async fn try_handle(
.map_err(to_dyn_error)?,
)?
}
"recognize_layered_design_text" => {
let args = args_or_default(args);
let request: crate::commands::layered_design_cmd::RecognizeLayeredDesignTextRequest =
parse_nested_arg(&args, "request")?;
serde_json::to_value(
crate::commands::layered_design_cmd::recognize_layered_design_text_inner(request)
.map_err(to_dyn_error)?,
)?
}
"analyze_layered_design_flat_image" => {
let args = args_or_default(args);
let request: crate::commands::layered_design_cmd::AnalyzeLayeredDesignFlatImageRequest =
parse_nested_arg(&args, "request")?;
serde_json::to_value(
crate::commands::layered_design_cmd::analyze_layered_design_flat_image_inner(
request,
)
.map_err(to_dyn_error)?,
)?
}
_ => return Ok(None),
};
@@ -44,6 +44,11 @@ pub(super) async fn try_handle(
parse_nested_arg(&args, "request")?;
serde_json::to_value(lime_knowledge::resolve_knowledge_context(request)?)?
}
"knowledge_validate_context_run" => {
let request: lime_knowledge::KnowledgeValidateContextRunRequest =
parse_nested_arg(&args, "request")?;
serde_json::to_value(lime_knowledge::validate_knowledge_context_run(request)?)?
}
_ => return Ok(None),
};
File diff suppressed because it is too large Load Diff
@@ -241,6 +241,16 @@ impl ExecutionTracker {
.map_err(|e| format!("查询会话执行记录失败: {e}"))
}
pub fn list_active_runs_by_session(
&self,
session_id: &str,
limit: usize,
) -> Result<Vec<AgentRun>, String> {
let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?;
AgentRunDao::list_active_runs_by_session(&conn, session_id, limit)
.map_err(|e| format!("查询会话活跃执行记录失败: {e}"))
}
pub fn list_terminal_runs_by_session(
&self,
session_id: &str,

Some files were not shown because too many files have changed in this diff Show More