diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index f74a45ada..5f424f3a1 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -1864,6 +1864,7 @@ const chatCommandHelp = [ '/project /绝对路径:设置本地项目目录', '/config:打开运行时配置', '/llm-status:检查 LLM 配置', + '/llm-routes:查看 Agent LLM 路由清单', '/capabilities:查看 Agent 能力清单', '/audit:审计当前项目的 Agent 能力证据', '/status:查看项目状态', @@ -6283,6 +6284,7 @@ function summarizeNextProjectActions( addSuggestion('查看数值与难度口径', '/balance'); addSuggestion('查看当前阻塞项', '/blockers'); addSuggestion('查看试玩就绪度', '/ready'); + addSuggestion('查看 Agent LLM 路由', '/llm-routes'); addSuggestion('查看任务依赖链', '/deps'); addSuggestion('准备下一轮改版说明', '/revise'); addSuggestion('查看隐私与导出边界', '/privacy'); @@ -7788,6 +7790,71 @@ function formatLlmAgentStatusLine(agent: GameCreatorAgentLlmConfigStatus) { return parts.join(','); } +function formatLlmRouteEndpoint( + status: Pick< + GameCreatorLlmConfigStatus, + 'baseUrl' | 'model' | 'apiKind' | 'stream' | 'apiKeyPresent' + >, +) { + return `${status.model ?? '未命名模型'} @ ${ + status.baseUrl ?? '未设置 base_url' + },${status.apiKind},流式 ${ + status.stream ? '开启' : '关闭' + },API Key ${status.apiKeyPresent ? '已读取' : '未读取'}`; +} + +function isSameResolvedLlmRouteAsGlobal( + globalStatus: GameCreatorLlmConfigStatus, + agentStatus: GameCreatorAgentLlmConfigStatus, +) { + return ( + agentStatus.baseUrl === globalStatus.baseUrl && + agentStatus.model === globalStatus.model && + agentStatus.apiKind === globalStatus.apiKind && + agentStatus.stream === globalStatus.stream + ); +} + +function summarizeAgentLlmRoutes(status: GameCreatorLlmConfigStatus) { + const agents = status.agents ?? []; + const readyCount = agents.filter((agent) => agent.configured).length; + const gapAgents = agents.filter((agent) => !agent.configured); + const separateRouteAgents = agents.filter( + (agent) => !isSameResolvedLlmRouteAsGlobal(status, agent), + ); + const routeLines = + agents.length > 0 + ? agents.map((agent) => { + const routeMode = isSameResolvedLlmRouteAsGlobal(status, agent) + ? '解析后与全局一致' + : '单独路由'; + const parts = [ + `- ${agent.label}:${agent.configured ? '已配置' : '未就绪'}`, + routeMode, + formatLlmRouteEndpoint(agent), + ]; + if (!agent.configured && agent.error) { + parts.push(`错误:${agent.error}`); + } + return parts.join(' · '); + }) + : ['- 暂无 Agent 路由']; + const draftCommand = gapAgents.length > 0 ? '/config' : '/llm-status'; + + return { + text: [ + 'Agent LLM 路由:', + `- 默认路由:${formatLlmRouteEndpoint(status)}`, + `- Agent:${readyCount}/${agents.length} 就绪 · ${separateRouteAgents.length} 个单独路由 · ${gapAgents.length} 个缺口`, + `- 路由清单:\n${routeLines.join('\n')}`, + '- 边界:只读取运行时配置解析结果;不请求上游;不显示 API Key;不写项目', + `- 建议:${draftCommand}`, + ].join('\n'), + draftCommand, + draftCommandLabel: gapAgents.length > 0 ? '打开配置' : '查看状态', + }; +} + function llmStatusForAgentCard( status: GameCreatorLlmConfigStatus | null, agent: AgentStatusCard, @@ -11038,6 +11105,11 @@ export function App() { return; } + if (prompt === '/llm-routes') { + void executeLlmRouteSummary(); + return; + } + if (prompt === '/open-project' || prompt === '/show-project') { if (!requireChatProjectForUserAction()) { return; @@ -11857,6 +11929,45 @@ export function App() { } } + async function executeLlmRouteSummary() { + const invoke = resolveTauriInvoke(); + if (!invoke) { + setLlmConfigStatus(null); + setMessages((current) => [ + ...current, + { role: 'assistant', text: '需要在 Tauri App 内运行。' }, + ]); + return; + } + + try { + const status = await invoke( + 'check_game_creator_llm_config', + ); + setLlmConfigStatus(status); + setCommandLog((current) => [...current, 'llm.route_summary']); + const summary = summarizeAgentLlmRoutes(status); + setMessages((current) => [ + ...current, + { + role: 'assistant', + text: summary.text, + draftCommand: summary.draftCommand, + draftCommandLabel: summary.draftCommandLabel, + }, + ]); + } catch (error) { + setLlmConfigStatus(null); + setMessages((current) => [ + ...current, + { + role: 'assistant', + text: error instanceof Error ? error.message : String(error), + }, + ]); + } + } + async function executeAgentCapabilitiesChat() { const invoke = resolveTauriInvoke(); let capabilities: readonly GameCreationAgentCapabilityDescriptor[] = diff --git a/apps/ai-game-creator-shell/tests/appSurface.test.ts b/apps/ai-game-creator-shell/tests/appSurface.test.ts index f797c11b6..19d572df8 100644 --- a/apps/ai-game-creator-shell/tests/appSurface.test.ts +++ b/apps/ai-game-creator-shell/tests/appSurface.test.ts @@ -4996,6 +4996,7 @@ describe('AI 游戏创作 App 界面边界', () => { expect(screen.getByText(/查看下一轮小步清单:\/todo/)).not.toBeNull(); expect(screen.getByText(/查看当前阻塞项:\/blockers/)).not.toBeNull(); expect(screen.getByText(/查看试玩就绪度:\/ready/)).not.toBeNull(); + expect(screen.getByText(/查看 Agent LLM 路由:\/llm-routes/)).not.toBeNull(); expect(screen.getByText(/查看任务依赖链:\/deps/)).not.toBeNull(); expect(screen.getByText(/准备下一轮改版说明:\/revise/)).not.toBeNull(); expect(screen.getByText(/查看隐私与导出边界:\/privacy/)).not.toBeNull(); @@ -12785,6 +12786,9 @@ describe('AI 游戏创作 App 界面边界', () => { expect( screen.getByText(/\/audit:审计当前项目的 Agent 能力证据/), ).not.toBeNull(); + expect( + screen.getByText(/\/llm-routes:查看 Agent LLM 路由清单/), + ).not.toBeNull(); expect(screen.getByText(/\/checkpoint:保存本地项目快照/)).not.toBeNull(); expect(screen.getByText(/\/export:导出本地试玩包/)).not.toBeNull(); expect(screen.getByText(/\/exports:列出本地试玩包/)).not.toBeNull(); @@ -17837,6 +17841,90 @@ describe('AI 游戏创作 App 界面边界', () => { expect(invoke).toHaveBeenCalledWith('check_game_creator_llm_config'); }); + it('summarizes Agent LLM routes from chat without leaking API key values', async () => { + const invoke = vi.fn(async (command: string) => { + if (command === 'check_game_creator_llm_config') { + return { + configured: true, + apiKeyPresent: true, + baseUrl: 'https://llm.example.test/v1', + model: 'gpt-main', + apiKind: 'openai_responses', + stream: false, + error: null, + agents: [ + { + agentId: 'planner', + label: 'Planner', + configured: true, + apiKeyPresent: true, + baseUrl: 'https://llm.example.test/v1', + model: 'gpt-main', + apiKind: 'openai_responses', + stream: false, + error: null, + }, + { + agentId: 'generator', + label: 'Generator', + configured: true, + apiKeyPresent: true, + baseUrl: 'https://generator.example.test/v1', + model: 'generator-model', + apiKind: 'openai_chat', + stream: true, + error: null, + }, + { + agentId: 'audio-sfx', + label: '音效规划', + configured: false, + apiKeyPresent: false, + baseUrl: 'https://llm.example.test/v1', + model: 'gpt-main', + apiKind: 'openai_responses', + stream: false, + error: + 'LLM 未配置:请在 agentLlm.audio-sfx.apiKey 中设置 API Key', + }, + ], + }; + } + throw new Error(`unexpected invoke ${command}`); + }); + window.__TAURI__ = { core: { invoke } }; + renderAppAt('/'); + + submitChat('/llm-routes'); + + expect(await screen.findByText(/Agent LLM 路由:/)).not.toBeNull(); + const chatText = screen.getByLabelText('聊天').textContent ?? ''; + expect(chatText).toContain( + '默认路由:gpt-main @ https://llm.example.test/v1,openai_responses,流式 关闭,API Key 已读取', + ); + expect(chatText).toContain('Agent:2/3 就绪 · 1 个单独路由 · 1 个缺口'); + expect(chatText).toContain( + 'Planner:已配置 · 解析后与全局一致 · gpt-main @ https://llm.example.test/v1,openai_responses,流式 关闭,API Key 已读取', + ); + expect(chatText).toContain( + 'Generator:已配置 · 单独路由 · generator-model @ https://generator.example.test/v1,openai_chat,流式 开启,API Key 已读取', + ); + expect(chatText).toContain( + '音效规划:未就绪 · 解析后与全局一致 · gpt-main @ https://llm.example.test/v1,openai_responses,流式 关闭,API Key 未读取 · 错误:LLM 未配置:请在 agentLlm.audio-sfx.apiKey 中设置 API Key', + ); + expect(chatText).toContain( + '边界:只读取运行时配置解析结果;不请求上游;不显示 API Key;不写项目', + ); + expect(screen.getByRole('button', { name: '打开配置' })).not.toBeNull(); + expect(screen.queryByText(/sk-test-secret/)).toBeNull(); + expect(screen.queryByText(/generator-secret/)).toBeNull(); + expect(invoke).toHaveBeenCalledWith('check_game_creator_llm_config'); + expect(invoke).not.toHaveBeenCalledWith( + 'generate_local_game_draft', + expect.anything(), + ); + }); + it('checks LLM config from the main window shortcut without leaking the API key value', async () => { const invoke = vi.fn(async (command: string) => { if (command === 'check_game_creator_llm_config') { diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 09dd27d9e..fb7273e16 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -40,7 +40,7 @@ ## 2026-06-30 AI 游戏创作 App 使用客户端配置文件 - 背景:`apps/ai-game-creator-shell` 是客户端 App,不应通过 `.env` 或进程环境变量承载 LLM / 画板同步配置;旧口径会让本地 secrets、CLI wrapper 和桌面 App 启动逻辑混在一起。 -- 决策:仓库内 `apps/ai-game-creator-shell/game-creator.config.json` 只作为默认模板;发布 App 启动时在 Tauri 应用配置目录写入默认 `game-creator.config.json`,真实密钥和本机覆盖项都保存在该运行时配置文件中。主窗口提供“配置”面板读写该运行时 JSON;开发 CLI 无 AppHandle 时才回退读取仓库旁边的模板和 gitignored 本机覆盖文件。`llm.apiKey/baseUrl/model/apiKind/stream/requestTimeoutMs/maxRetries/retryBackoffMs` 驱动全局 LLM 路径,`agentLlm.` 可为 Planner、Generator 和角色 agent 单独覆盖 API Key、base URL、模型、API 类型和流式请求,空项继承全局配置;`editorApi.baseUrl/apiKey` 驱动画板项目同步;`/llm-status` 只展示全局和各 agent resolved 后的 baseUrl、model、apiKind、stream 和 API Key 是否存在,不显示密钥。生成游戏或平台美术遇到 LLM / editorApi 缺配置错误时,主窗口自动打开运行时配置弹窗,但错误消息仍只显示缺失项,不回显密钥值。 +- 决策:仓库内 `apps/ai-game-creator-shell/game-creator.config.json` 只作为默认模板;发布 App 启动时在 Tauri 应用配置目录写入默认 `game-creator.config.json`,真实密钥和本机覆盖项都保存在该运行时配置文件中。主窗口提供“配置”面板读写该运行时 JSON;开发 CLI 无 AppHandle 时才回退读取仓库旁边的模板和 gitignored 本机覆盖文件。`llm.apiKey/baseUrl/model/apiKind/stream/requestTimeoutMs/maxRetries/retryBackoffMs` 驱动全局 LLM 路径,`agentLlm.` 可为 Planner、Generator 和角色 agent 单独覆盖 API Key、base URL、模型、API 类型和流式请求,空项继承全局配置;`editorApi.baseUrl/apiKey` 驱动画板项目同步;`/llm-status` 只展示全局和各 agent resolved 后的 baseUrl、model、apiKind、stream 和 API Key 是否存在,不显示密钥;`/llm-routes` 复用同一只读检查结果,按 agent 展示 resolved provider 路由、单独路由数量和缺口数量,不请求上游、不显示密钥、不写项目。生成游戏或平台美术遇到 LLM / editorApi 缺配置错误时,主窗口自动打开运行时配置弹窗,但错误消息仍只显示缺失项,不回显密钥值。 - 影响范围:AI 游戏创作 App 的 Tauri Rust 配置加载、主窗口配置面板、CLI wrapper、agent-run smoke、`check-config` 门禁、`.gitignore` 和实施计划文档。 - 验证方式:运行 `npm run ai-game-creator-shell:typecheck`、`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 1e7281146..93f9018d6 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -101,6 +101,7 @@ game-project/ - 用户能创建本地 Web 游戏项目。 - 用户侧看到当前工作区项目名 / 路径、最近 run、预览状态、任务完成数 / ready 数、资产数量 / 来源分布、最近命令摘要、聊天框、上传入口、Agent 状态列表和单 Agent 对话;聊天输入 `/brief` 可在普通聊天消息里生成当前项目简报并提供 `/next` 草稿,不新增普通用户面板;聊天输入 `/risks` 可在普通聊天消息里查看当前项目风险并提供首个风险处理草稿,不新增普通用户面板;聊天输入 `/review` 可在普通聊天消息里查看 Evaluator 评审、返工焦点和返工路线并提供读取评审记录或继续修复草稿,不新增普通用户面板;聊天输入 `/context` 可在普通聊天消息里查看生成上下文来源并提供读取首个上下文或查看黑板草稿,不新增上下文面板;聊天输入 `/timeline` 可在普通聊天消息里查看项目活动时间线并提供最近日志、trace 或历史草稿,不新增时间线面板;聊天输入 `/playtest` 可在普通聊天消息里查看最近 run 是否可试玩、当前预览状态和 Playtest 任务状态并提供启动或打开预览草稿,不新增普通用户试玩面板;聊天输入 `/handoff` 可在普通聊天消息里生成当前项目交接摘要并提供 `/next` 草稿,不新增普通用户面板;聊天输入 `/runs` 可在普通聊天消息里列出已加载 Run 历史读取命令,不新增普通用户面板;聊天输入 `/run-files` 可在普通聊天消息里列出 Agent 运行辅助文件读取命令,不新增普通用户面板;聊天输入 `/internals` 可在普通聊天消息里列出项目内部真相源读取命令,不新增内部文件面板;聊天输入 `/art` 可基于当前 manifest 盘点美术素材并提供生成或读取草稿,不新增美术面板;聊天输入 `/audio` 可基于当前 manifest 盘点音频素材并提供登记或读取草稿,不新增音频生成系统;聊天输入 `/publish` 可基于当前 manifest 和最近 run 生成发布准备清单并提供运行、trace、继续或导出草稿,不新增发布面板;聊天输入 `/listing` 可基于当前 manifest 和最近 run 汇总作品页标题、卖点、标签口径、封面素材和发布说明状态并提供读取发布说明或发布准备草稿,不新增作品页面板;聊天输入 `/cover` 可基于当前 manifest 和最近 run 准备封面与缩略图检查并提供运行、美术或作品页草稿,不新增封面面板;聊天输入 `/screenshots` 可基于当前 manifest 和最近 run 准备宣传截图清单并提供运行或作品页草稿,不新增截图面板;聊天输入 `/trailer` 可基于当前 manifest 和最近 run 准备试玩短视频脚本并提供运行或交付草稿,不新增录屏面板;聊天输入 `/faq` 可基于当前 manifest 和最近 run 准备试玩常见问答并提供运行或交付草稿,不新增 FAQ 面板;聊天输入 `/post` 可基于当前 manifest 和最近 run 准备社区发布文案并提供运行或上架草稿,不新增社区发布面板;聊天输入 `/store` 可基于当前 manifest 和最近 run 准备上架资料清单并提供运行或作品页草稿,不新增上架面板;聊天输入 `/media-kit` 可基于当前 manifest 和最近 run 准备媒体资料包清单并提供运行、截图或发布说明草稿,不新增媒体包面板;聊天输入 `/release-notes` 可基于当前 manifest 和最近 run 准备试玩更新说明并提供运行、资料包或发布说明草稿,不新增更新说明面板;聊天输入 `/known-issues` 可基于当前 manifest 和最近 run 准备已知问题清单并提供运行、评审或交付草稿,不新增已知问题面板;聊天输入 `/export` 确认后把当前可试玩原型导出为本地试玩 ZIP,聊天输入 `/exports` 可只读列出已导出的本地试玩包;Agent 状态列表和单 Agent 对话在 `/llm-status` 后显示该 agent 当前 LLM provider / 模型 / 流式 / API Key 读取状态,但不显示密钥本体;最近项目资产入口显示本地路径、kind、mediaType 和来源类型,并可一键填入 `/read` 草稿,开发环境通过独立窗口查看任务拆分、专业组细节、产物、文件面板、嵌入预览以及 `.agent/logs/command.log` / `preview.log` / `agent.log`。 +- 聊天输入 `/llm-routes` 会触发同一只读 LLM 配置检查,在普通聊天消息里列出全局 LLM 和每个 agent resolved 后的 provider 路由、模型、API 类型、流式开关、API Key 是否已读取、单独路由数量和缺口数量;该命令不请求上游、不显示 API Key 本体、不写本地项目、不新增普通用户配置面板,缺口处理只提供 `/config` 草稿。 - 聊天输入 `/goal` 可在普通聊天消息里查看创作目标来源,不读取 spec、上下文或 trace 文件,也不新增普通用户目标面板。 - 聊天输入 `/spec` 可在普通聊天消息里查看创作规格包,不读取规格文件、不启动预览、不写项目,也不新增普通用户规格面板。 - 聊天输入 `/mvp` 可在普通聊天消息里查看本轮最小可玩范围、当前状态、试玩包状态和暂不做事项,不读取文件、不启动预览、不导出试玩包,也不新增普通用户 MVP 面板。 @@ -241,7 +242,7 @@ game-project/ - 共享契约和 `platform-agent` 会按任务依赖与 `completed` 状态计算当前可执行任务,作为 v1 的最小编排选择器;每轮 `Orchestrator` 的 activeTaskIds、carriedTaskIds、repairRoutes 和 dependencyWaves 由 `platform-agent` 纯编排内核产出,`apps/ai-game-creator-shell` 只负责写入 `.agent/passes/pass-N/` 和执行本地工具;`Evaluator` 会在 `.agent/findings.md` 写出 `## Repair Routes` JSON,下一轮编排优先采用该结构化 taskIds,解析不到时才退回关键词路由;返工路由会按任务图自动扩展下游影响任务,例如美术资产变化会继续触发程序预览和运营包装重算。 - `game.generate_draft` 使用 OpenAI-compatible LLM 配置生成结构化 JSON 草案,发布 App 的配置项来自 Tauri 应用配置目录中的 `game-creator.config.json`:`llm.apiKey`、`llm.baseUrl`、`llm.model`、`llm.apiKind`、`llm.stream`、`llm.requestTimeoutMs`、`llm.maxRetries`、`llm.retryBackoffMs`;默认 API kind 为 `openai_responses`,可设 `llm.apiKind=openai_chat` 切回旧 Chat Completions 兼容网关,或 `llm.apiKind=anthropic` 走 Anthropic Messages;`llm.stream=true` 时 Planner 和 Generator 使用流式请求;缺少配置或模型返回非法 JSON 时直接失败,不静默回退固定模板。 - 主窗口“配置”面板和聊天 `/config` 命令读写 Tauri 应用配置目录中的 `game-creator.config.json`,覆盖 LLM API Key、base URL、模型、API 类型、流式请求、超时、重试和画板 External API 配置;Planner、Orchestrator、Generator、Evaluator 和 16 个角色 agent 都可在 `agentLlm` 中单独覆盖 API Key、base URL、模型、API 类型和流式请求,空项继承全局 LLM 配置;默认生成链路仍只让 Planner / Generator 调 LLM,配置了 `agentLlm.` 的角色 agent 会改用自己的 provider 生成 brief,未配置的角色 agent 继续使用本地 brief;生成游戏或平台美术时如果返回 LLM / editorApi 缺配置错误,主窗口自动打开同一个运行时配置弹窗;API Key 输入框使用密码字段并关闭自动填充,数值项在 UI 层夹住下限,Rust 写配置时也拒绝过低超时,保存时只写运行时配置文件,不写仓库模板、本地项目、trace 或 manifest。 -- 聊天输入 `/llm-status` 会触发只读 `llm.config_check`,确认全局 LLM 以及各 agent resolved 后的 base_url、model、API 类型和 API Key 是否已从客户端配置读取;状态消息不会显示或保存 API Key;当前生成链路只要求 Planner / Generator 就绪。 +- 聊天输入 `/llm-status` 会触发只读 `llm.config_check`,确认全局 LLM 以及各 agent resolved 后的 base_url、model、API 类型和 API Key 是否已从客户端配置读取;状态消息不会显示或保存 API Key;当前生成链路只要求 Planner / Generator 就绪。`/llm-routes` 复用同一检查结果,但输出按 agent 展开的路由清单和缺口摘要,用于确认哪些 agent 解析后走全局路由、哪些 agent 走单独 provider。 - `game.generate_draft` 的 LLM JSON 必须包含 `handoffs` 数组,覆盖 `design`、`balance`、`art`、`audio`、`code`、`publishing` 6 个专业组;每组必须给出 role、summary、outputs 和 next,缺组或交接内容不完整会判定为模型输出无效并进入返工。 - `game.generate_draft` 的真实生成路径使用最小 Planner / Orchestrator / 组内角色 agent / Generator / Evaluator loop:Planner 写 `.agent/spec.md`;每轮 Orchestrator 先写 `.agent/passes/pass-N/agenda.md` 和 `.agent/passes/pass-N/task-graph.json`,首轮全量调度 16 个角色任务,返工轮按 `.agent/findings.md` 生成结构化 `repairRoutes`,重跑命中问题的角色任务及其下游依赖任务,其余角色 brief 从上一轮 carry-over;`task-graph.json` 记录 activeTaskIds、carriedTaskIds、repairFocus、repairRoutes 和按依赖排序的 dependencyWaves;每个角色 brief 必须读取自己的私有记忆 `memory/agents//.md` 和项目黑板 `memory/blackboard.md`,写入 `.agent/passes/pass-N/groups//*.md`,再汇总为 `.agent/passes/pass-N/groups/*.md`;Generator 必须读取用户需求、记忆、`.agent/spec.md`、本轮 `agenda.md`、`task-graph.json`、`.agent/findings.md` 和 6 组汇总 brief 后返回结构化 JSON;每轮会把 Generator 草案拆成 6 组交接快照,写入 `.agent/passes/pass-N/`;Evaluator 做质量评审并写 `.agent/findings.md`,通过后才进入 `game.static_smoke` 静态自检和预览试玩。 - loop 最多执行 3 轮;Evaluator 发现 HTML 非自包含、缺少 `canvas`、缺少 `requestAnimationFrame`、缺少输入监听或用户输入未转义时,把问题写入 `.agent/findings.md` 并让下一轮 Generator 修复。3 轮仍失败则 `game.generate_draft` 失败,不写最终游戏产物。 @@ -307,7 +308,7 @@ game-project/ - 聊天输入 `/todo` 只使用主窗口当前已加载的 manifest 和最近 run trace,在聊天里列出下一轮优先小步,优先失败、active、carry 和 ready 任务,没有 trace 时回退当前 ready 任务,并提供 `/tasks`、`/review` 或 `/next` 草稿;该命令不调用 Tauri 读写、不读取任务文件、不启动 run、不修改项目、不新增普通用户小步面板,真正查看任务或继续修复仍由用户发送对应草稿并走原权限流。 - 聊天输入 `/handoff` 只使用主窗口当前已加载的 manifest、最近 run trace、Agent 状态和已载入历史 run 批次,在聊天里生成项目交接摘要,并提供 `/next` 作为后续草稿;该命令不调用 Tauri 读写、不读取文件、不启动或打开预览、不新增普通用户面板,用户必须再发送草稿并按原命令权限流继续。 - 聊天输入 `/runs` 只使用主窗口当前已加载的 latest trace 和已载入历史 run 批次,在聊天里列出 `/trace` 和 `/read .agent/runs/...` 读取草稿;该命令不调用 Tauri 读写、不滚动加载更多历史、不启动或打开预览、不新增普通用户面板,用户必须再发送草稿并按原命令权限流继续。 -- 聊天输入 `/next` 只使用主窗口当前已加载的 manifest、最近 run trace 和最近命令摘要,在聊天里给出下一步建议,列出 `/goal`、`/spec`、`/mvp`、`/pitch`、`/demo`、`/rules`、`/tutorial`、`/mobile`、`/compatibility`、`/accessibility`、`/localization`、`/performance`、`/polish`、`/blockers`、`/ready`、`/deps`、`/revise`、`/privacy`、`/audience`、`/invite`、`/bug-report`、`/survey`、`/cover`、`/screenshots`、`/trailer`、`/faq`、`/post`、`/store`、`/media-kit`、`/release-notes`、`/known-issues`、`/tasks`、`/criteria`、`/groups`、`/balance`、`/budget`、`/qa`、`/changes`、`/todo`、`/trace`、`/review`、`/context`、`/timeline`、`/playtest`、`/test-plan`、`/feedback`、`/retention`、`/share`、`/listing`、`/run`、`/export`、`/exports`、`/open-preview`、`/assets`、`/credits`、`/art`、`/audio`、`/publish`、`/artifacts`、`/run-artifacts`、`/passes`、`/run-files`、`/internals`、`/logs`、`/agent-resume ` 等安全命令草稿方向,并提供一个首选草稿;该命令不调用 Tauri 读写、不启动或打开预览、不读取文件,用户必须再发送草稿并按原命令权限流继续。 +- 聊天输入 `/next` 只使用主窗口当前已加载的 manifest、最近 run trace 和最近命令摘要,在聊天里给出下一步建议,列出 `/goal`、`/spec`、`/mvp`、`/pitch`、`/demo`、`/rules`、`/tutorial`、`/mobile`、`/compatibility`、`/accessibility`、`/localization`、`/performance`、`/polish`、`/blockers`、`/ready`、`/llm-routes`、`/deps`、`/revise`、`/privacy`、`/audience`、`/invite`、`/bug-report`、`/survey`、`/cover`、`/screenshots`、`/trailer`、`/faq`、`/post`、`/store`、`/media-kit`、`/release-notes`、`/known-issues`、`/tasks`、`/criteria`、`/groups`、`/balance`、`/budget`、`/qa`、`/changes`、`/todo`、`/trace`、`/review`、`/context`、`/timeline`、`/playtest`、`/test-plan`、`/feedback`、`/retention`、`/share`、`/listing`、`/run`、`/export`、`/exports`、`/open-preview`、`/assets`、`/credits`、`/art`、`/audio`、`/publish`、`/artifacts`、`/run-artifacts`、`/passes`、`/run-files`、`/internals`、`/logs`、`/agent-resume ` 等安全命令草稿方向,并提供一个首选草稿;该命令不调用 Tauri 读写、不启动或打开预览、不读取文件,用户必须再发送草稿并按原命令权限流继续。 - 聊天输入 `/review` 只使用主窗口当前已加载的最近 run trace,在聊天里列出 Evaluator 通过 / 需返工状态、返工焦点、返工路线、下一步和最近评审步骤;该命令不调用 Tauri 读写、不读取 `.agent/findings.md`、不新增普通用户评审面板,真正读取评审记录仍由用户发送 `/read .agent/findings.md` 并走 `file.read` 权限流,需要返工时只填入 `/agent-resume ` 草稿。 - 聊天输入 `/context` 只使用主窗口当前已加载的 manifest 和最近 run trace,在聊天里列出项目对话、短期记忆、长期记忆、项目黑板、Agent 对话、Agent 私有记忆、manifest、最近 trace 和最近 LLM 输入路径;该命令不调用 Tauri 读写、不读取上下文文件、不新增普通用户上下文面板,真正查看上下文仍由用户发送 `/read` 或 `/memory blackboard` 并走原权限流。 - 聊天输入 `/timeline` 只使用主窗口当前已加载的 manifest.commandRuns 和最近 run trace,在聊天里列出最近命令、日志读取草稿、最近 Run 状态和最近 Agent 步骤;该命令不调用 Tauri 读写、不读取日志或 trace 文件、不新增普通用户时间线面板,真正查看日志、trace 或历史仍由用户发送对应草稿并走原权限流。