diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index f771466d9..81fd57140 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -3636,6 +3636,8 @@ export function App() { const [commandLog, setCommandLog] = useState([ `${GAME_CREATION_APP_COMMANDS.length} 个命令已登记权限。`, ]); + const [projectLogStatus, setProjectLogStatus] = useState('未读取'); + const [projectLogContent, setProjectLogContent] = useState(''); const [conversationWriteVersion, setConversationWriteVersion] = useState(0); const savedConversationCountRef = useRef(0); const savedConversationProjectPathRef = useRef(null); @@ -7934,6 +7936,53 @@ export function App() { } } + async function handleProjectLogRead( + relativePath: string, + skipPolicyConfirm = false, + ) { + const invoke = resolveTauriInvoke(); + if (!invoke) { + setProjectLogStatus('需要在 Tauri App 内运行'); + return; + } + const nextProjectPath = resolveChatProjectPath(localProject); + if (!nextProjectPath) { + setProjectLogStatus('请先初始化本地项目'); + return; + } + setProjectLogStatus(`正在读取 ${relativePath}`); + try { + if ( + !skipPolicyConfirm && + (await queueProjectPolicyConfirmationIfNeeded( + invoke, + 'file.read', + nextProjectPath, + `读取 ${nextProjectPath}/${relativePath}`, + '准备读取项目日志。', + () => void handleProjectLogRead(relativePath, true), + )) + ) { + setProjectLogStatus('等待确认'); + return; + } + const result = await invoke( + 'read_local_project_file', + { + projectPath: nextProjectPath, + relativePath, + commandId: 'file.read', + }, + ); + setProjectLogContent(result.content); + setProjectLogStatus(`已读取:${result.path}`); + setCommandLog((current) => [...current, `file.read ${result.path}`]); + } catch (error) { + setProjectLogContent(''); + setProjectLogStatus(error instanceof Error ? error.message : String(error)); + } + } + async function executeLimitedCommandList() { const invoke = resolveTauriInvoke(); let commands: GameCreationAppLimitedRunCommandDescriptor[] = [ @@ -10825,10 +10874,36 @@ export function App() { aria-label="日志" >
-

内置命令

- +

日志

+
+ + + + +
{limitedLocalCommands.map((command) => ( @@ -10842,6 +10917,10 @@ export function App() { ))}

{limitedCommandStatus}

+

{projectLogStatus}

+ {projectLogContent ? ( +
{projectLogContent}
+ ) : null} {commandLog.map((entry, index) => (

{entry}

))} diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 2974d0bd3..bb022485d 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -1019,6 +1019,16 @@ h2 { background: #1f6feb; } +.project-log-content { + max-height: 180px; + margin: 0 0 10px; + padding: 10px; + overflow: auto; + border: 1px solid #dde3ee; + background: #f8fafd; + white-space: pre-wrap; +} + .preview-frame { display: grid; width: 100%; diff --git a/apps/ai-game-creator-shell/tests/appSurface.test.ts b/apps/ai-game-creator-shell/tests/appSurface.test.ts index cc7fc94dd..6b5610f62 100644 --- a/apps/ai-game-creator-shell/tests/appSurface.test.ts +++ b/apps/ai-game-creator-shell/tests/appSurface.test.ts @@ -11540,7 +11540,7 @@ describe('AI 游戏创作 App 界面边界', () => { 'local-project-draft', '未命名游戏原型', ); - const invoke = vi.fn(async (command: string) => { + const invoke = vi.fn(async (command: string, args?: Record) => { if (command === 'is_local_project_directory_non_empty') { return false; } @@ -11562,6 +11562,29 @@ describe('AI 游戏创作 App 界面边界', () => { }; } if (command === 'read_local_project_file') { + if (args?.relativePath === '.agent/logs/command.log') { + return { + path: '.agent/logs/command.log', + absolutePath: + '/tmp/authorized-game/.agent/logs/command.log', + content: 'permission.pending preview.start\npermission.confirm preview.start\n', + }; + } + if (args?.relativePath === '.agent/logs/preview.log') { + return { + path: '.agent/logs/preview.log', + absolutePath: + '/tmp/authorized-game/.agent/logs/preview.log', + content: 'preview.start http://127.0.0.1:3210/\n', + }; + } + if (args?.relativePath === '.agent/logs/agent.log') { + return { + path: '.agent/logs/agent.log', + absolutePath: '/tmp/authorized-game/.agent/logs/agent.log', + content: 'Planner 正在整理规格\nGenerator 已写入草案\n', + }; + } throw new Error( '读取文件元数据失败:/tmp/authorized-game/.agent/run.latest.json: No such file or directory (os error 2)', ); @@ -11610,6 +11633,31 @@ describe('AI 游戏创作 App 界面边界', () => { await screen.findByText('已打开:/tmp/authorized-game'), ).not.toBeNull(); + fireEvent.click(logPanel.getByRole('button', { name: '命令日志' })); + expect( + await screen.findByText('已读取:.agent/logs/command.log'), + ).not.toBeNull(); + expect(screen.getByText(/permission\.confirm preview\.start/)).not.toBeNull(); + expect(invoke).toHaveBeenCalledWith('read_local_project_file', { + projectPath: '/tmp/authorized-game', + relativePath: '.agent/logs/command.log', + commandId: 'file.read', + }); + + fireEvent.click(logPanel.getByRole('button', { name: '预览日志' })); + expect( + await screen.findByText('已读取:.agent/logs/preview.log'), + ).not.toBeNull(); + expect( + screen.getByText(/preview\.start http:\/\/127\.0\.0\.1:3210\//), + ).not.toBeNull(); + + fireEvent.click(logPanel.getByRole('button', { name: 'Agent日志' })); + expect( + await screen.findByText('已读取:.agent/logs/agent.log'), + ).not.toBeNull(); + expect(screen.getByText(/Generator 已写入草案/)).not.toBeNull(); + fireEvent.click(logPanel.getByRole('button', { name: '自定义自检' })); expect( screen.getByText('运行 自定义自检 于 /tmp/authorized-game'), diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 380543769..23b1ad5dc 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -3857,6 +3857,7 @@ - 2026-07-03 调整:主窗口 Agent 状态栏新增“继续说明”,只把 `/agent-resume ` 填入聊天输入框,让用户补充说明后再走原确认流;策略快捷入口新增 conversation.read / conversation.write 确认草稿,同样只填输入框,不直接写 `.agent/policy.json`。 - 2026-07-03 调整:主窗口 header 常驻项目摘要只从当前已加载的 manifest / trace 派生任务完成数、ready 数、资产来源分布和最近命令结果;未选择工作区时不显示,不为了摘要额外触发 Tauri 读取或写入,也不把任务、文件、run history 或预览开发面板搬进普通用户窗口。 - 2026-07-03 调整:`/llm-status` 读取到的 agent 级 LLM 配置状态可回填到主窗口 Agent 状态列表和单 Agent 对话头部,显示 provider 类型、模型、流式开关和 API Key 是否已读取;密钥本体仍不能进入聊天、状态列表、manifest、trace 或本地项目文件。 +- 2026-07-03 调整:开发窗口日志面板提供 `.agent/logs/command.log`、`.agent/logs/preview.log` 和 `.agent/logs/agent.log` 的只读查看入口,复用 `file.read` 授权策略;普通用户窗口仍只通过聊天 `/read` 和摘要消息查看需要的日志,不新增日志面板。 - 2026-06-25 调整:本地 HTTP 预览静态 `HEAD` 必须返回与 `GET` 相同的真实 `Content-Length`,但不返回 body;浏览器、图片、音频和视频探测不能拿到 `Content-Length: 0` 的假响应。 - 2026-06-25 调整:普通用户通过聊天输入 `/run` 触发待确认 `game.run_local`,确认后只能复用白名单 `game.static_smoke` 自检当前 `game/index.html`,通过后启动 `127.0.0.1` 本地 HTTP 预览。独立执行 `game.static_smoke` 时如果已有 `.agent/run.latest.json`,必须追加 `Playtest / game.static_smoke` trace step,避免“运行了代码但编排 trace 不可见”。 - 2026-06-25 调整:普通用户通过聊天输入 `/trace` 触发只读 `agent.trace_read`,读取 `.agent/run.latest.json` 并在聊天里摘要 loop 轮次、stopReason、nextStep、active / carry-over 任务、repairRoutes、agent 建议命令和最近 step。trace 面板仍只在开发窗口展示,普通用户窗口不新增面板。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 07558eb90..130e4c0c1 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -100,7 +100,7 @@ game-project/ ## v1 验收 - 用户能创建本地 Web 游戏项目。 -- 用户侧看到当前工作区项目名 / 路径、最近 run、预览状态、任务完成数 / ready 数、资产数量 / 来源分布、最近命令摘要、聊天框、上传入口、Agent 状态列表和单 Agent 对话;Agent 状态列表和单 Agent 对话在 `/llm-status` 后显示该 agent 当前 LLM provider / 模型 / 流式 / API Key 读取状态,但不显示密钥本体;最近项目资产入口显示本地路径、kind、mediaType 和来源类型,并可一键填入 `/read` 草稿,开发环境通过独立窗口查看任务拆分、专业组细节、产物、文件面板、嵌入预览和错误日志。 +- 用户侧看到当前工作区项目名 / 路径、最近 run、预览状态、任务完成数 / ready 数、资产数量 / 来源分布、最近命令摘要、聊天框、上传入口、Agent 状态列表和单 Agent 对话;Agent 状态列表和单 Agent 对话在 `/llm-status` 后显示该 agent 当前 LLM provider / 模型 / 流式 / API Key 读取状态,但不显示密钥本体;最近项目资产入口显示本地路径、kind、mediaType 和来源类型,并可一键填入 `/read` 草稿,开发环境通过独立窗口查看任务拆分、专业组细节、产物、文件面板、嵌入预览以及 `.agent/logs/command.log` / `preview.log` / `agent.log`。 - 生成代码和资产进入用户本地项目目录。 - 本地 HTTP 预览能启动,并在外部浏览器展示可玩原型。 - 美术/音乐资产能从画板链路回流到本地项目。 @@ -111,7 +111,7 @@ game-project/ ## v1 验收证据矩阵 -- `npm run ai-game-creator-shell:check`:覆盖壳 typecheck、聊天命令单测、用户 / 开发窗口 UI 边界 smoke、主窗口命令按钮复用 `/help` 命令列表、主窗口项目摘要从 manifest / trace 派生任务完成数、ready 数、资产来源分布和最近命令且未选项目时不显示、灵感草稿只填充输入框不提交、能力按钮复用 `/capabilities`、LLM状态按钮复用 `/llm-status` 且结果回填 Agent 状态列表和单 Agent 对话的 provider / 模型 / 流式 / API Key 读取状态、项目状态按钮复用 `/status`、权限按钮复用 `/policy` 且策略草稿按钮只填入 `/policy-confirm preview.start` / `/policy-confirm agent.run_status`、审计按钮复用 `/audit`、资产按钮复用 `/assets` 且资产结果可一键复用 `/read`、任务按钮复用 `/tasks`、Trace 按钮复用 `/trace`、文件按钮复用 `/files` 且文件结果可一键复用 `/read`、索引按钮复用 `/index`、记忆 / 短期记忆 / 黑板按钮复用 `/memory long|short|blackboard`、快照按钮复用 `/checkpoint`、快照列表按钮复用 `/checkpoints` 且 checkpoint 结果可一键复用 `/diff` / `/restore`、历史按钮复用 `/history`、受限命令白名单按钮复用 `/commands` 且无需项目初始化、静态自检快捷按钮复用 `/smoke`、预览状态快捷按钮复用 `/preview-status`、主窗口运行时配置面板读写 Tauri 配置目录中的 `game-creator.config.json`、支持全局与每个 agent 单独选择 LLM Provider 且不把 API Key 写入聊天、聊天侧 `/capabilities` 展示标准 Agent 能力清单且不打开开发面板、聊天侧 `/audit` 从 manifest / 本地文件 / `.agent/run.latest.json` 分别汇总用户面、6 组任务配置、6 组协作证据、任务编排、loop、记忆、本地产物、HTTP 预览、画板回流和权限日志证据且不打开开发面板;未生成 `.agent/run.latest.json` 前,`/audit` 只能标记任务配置通过,不能把 6 组协作证据误判为通过;trace 已存在但状态为 `failed`、`needs-revision`、`running`、`max-passes-exhausted` 或缺少 `Evaluator passed` 步骤时,`/audit` 不能把 loop 误判为通过。聊天侧 `/llm-status` 只显示 base_url / model / API Key 已读取状态且不泄露密钥本体、聊天侧长期记忆查看 / 追加 / 覆盖 / 删除的授权本地项目路径、上传资产写入后的 manifest 刷新和 `/assets` 聊天可见性、`/smoke` 聊天侧确认后只通过授权本地项目路径执行白名单 `game.static_smoke`、`/run` 聊天侧确认后通过授权本地项目路径执行 `game.static_smoke`、启动 `127.0.0.1` 本地预览并交给外部浏览器、`/preview` 聊天侧确认后通过授权本地项目路径启动 `127.0.0.1` 本地预览并交给外部浏览器、`/status` 聊天侧项目 / 任务 / 资产 / 预览 / 最近命令汇总、`/files` 聊天侧本地项目文件列表、`/read` 聊天侧文件读取的授权本地项目路径、`/tasks` 聊天侧任务拆分与下一步专业组展示的授权本地项目路径、聊天确认生成后实时展示 Planner / Orchestrator / 角色 brief / Generator / Evaluator / 写盘 / 自检进度,并自动读取 `.agent/run.latest.json` 在普通聊天消息里展示 Run、LLM 对话、loop 轮次、工具调用、active / carry-over 任务、返工焦点、编排轮次、最近步骤、画板同步建议命令和本地产物快照、`/trace` 聊天侧读取 `.agent/run.latest.json` 并展示 loop 轮次 / active 任务 / 返工路线 / dependency waves 的授权本地项目路径、`platform-agent` 编排测试、共享契约测试、Tauri 本地能力测试和无密钥本地 provider 端到端 smoke;用于证明独立 App、真实 LLM-compatible loop、本地落盘、自检和 HTTP 预览闭环,并覆盖 loop 跑满 3 轮失败时不会写入最终游戏产物。 +- `npm run ai-game-creator-shell:check`:覆盖壳 typecheck、聊天命令单测、用户 / 开发窗口 UI 边界 smoke、主窗口命令按钮复用 `/help` 命令列表、主窗口项目摘要从 manifest / trace 派生任务完成数、ready 数、资产来源分布和最近命令且未选项目时不显示、灵感草稿只填充输入框不提交、能力按钮复用 `/capabilities`、LLM状态按钮复用 `/llm-status` 且结果回填 Agent 状态列表和单 Agent 对话的 provider / 模型 / 流式 / API Key 读取状态、开发日志面板只读读取 `.agent/logs/command.log` / `preview.log` / `agent.log`、项目状态按钮复用 `/status`、权限按钮复用 `/policy` 且策略草稿按钮只填入 `/policy-confirm preview.start` / `/policy-confirm agent.run_status`、审计按钮复用 `/audit`、资产按钮复用 `/assets` 且资产结果可一键复用 `/read`、任务按钮复用 `/tasks`、Trace 按钮复用 `/trace`、文件按钮复用 `/files` 且文件结果可一键复用 `/read`、索引按钮复用 `/index`、记忆 / 短期记忆 / 黑板按钮复用 `/memory long|short|blackboard`、快照按钮复用 `/checkpoint`、快照列表按钮复用 `/checkpoints` 且 checkpoint 结果可一键复用 `/diff` / `/restore`、历史按钮复用 `/history`、受限命令白名单按钮复用 `/commands` 且无需项目初始化、静态自检快捷按钮复用 `/smoke`、预览状态快捷按钮复用 `/preview-status`、主窗口运行时配置面板读写 Tauri 配置目录中的 `game-creator.config.json`、支持全局与每个 agent 单独选择 LLM Provider 且不把 API Key 写入聊天、聊天侧 `/capabilities` 展示标准 Agent 能力清单且不打开开发面板、聊天侧 `/audit` 从 manifest / 本地文件 / `.agent/run.latest.json` 分别汇总用户面、6 组任务配置、6 组协作证据、任务编排、loop、记忆、本地产物、HTTP 预览、画板回流和权限日志证据且不打开开发面板;未生成 `.agent/run.latest.json` 前,`/audit` 只能标记任务配置通过,不能把 6 组协作证据误判为通过;trace 已存在但状态为 `failed`、`needs-revision`、`running`、`max-passes-exhausted` 或缺少 `Evaluator passed` 步骤时,`/audit` 不能把 loop 误判为通过。聊天侧 `/llm-status` 只显示 base_url / model / API Key 已读取状态且不泄露密钥本体、聊天侧长期记忆查看 / 追加 / 覆盖 / 删除的授权本地项目路径、上传资产写入后的 manifest 刷新和 `/assets` 聊天可见性、`/smoke` 聊天侧确认后只通过授权本地项目路径执行白名单 `game.static_smoke`、`/run` 聊天侧确认后通过授权本地项目路径执行 `game.static_smoke`、启动 `127.0.0.1` 本地预览并交给外部浏览器、`/preview` 聊天侧确认后通过授权本地项目路径启动 `127.0.0.1` 本地预览并交给外部浏览器、`/status` 聊天侧项目 / 任务 / 资产 / 预览 / 最近命令汇总、`/files` 聊天侧本地项目文件列表、`/read` 聊天侧文件读取的授权本地项目路径、`/tasks` 聊天侧任务拆分与下一步专业组展示的授权本地项目路径、聊天确认生成后实时展示 Planner / Orchestrator / 角色 brief / Generator / Evaluator / 写盘 / 自检进度,并自动读取 `.agent/run.latest.json` 在普通聊天消息里展示 Run、LLM 对话、loop 轮次、工具调用、active / carry-over 任务、返工焦点、编排轮次、最近步骤、画板同步建议命令和本地产物快照、`/trace` 聊天侧读取 `.agent/run.latest.json` 并展示 loop 轮次 / active 任务 / 返工路线 / dependency waves 的授权本地项目路径、`platform-agent` 编排测试、共享契约测试、Tauri 本地能力测试和无密钥本地 provider 端到端 smoke;用于证明独立 App、真实 LLM-compatible loop、本地落盘、自检和 HTTP 预览闭环,并覆盖 loop 跑满 3 轮失败时不会写入最终游戏产物。 - 主窗口策略快捷入口只填入 `/policy-confirm preview.start`、`/policy-confirm agent.run_status`、`/policy-confirm conversation.read` 或 `/policy-confirm conversation.write` 草稿;Agent 状态栏的“继续说明”只填入 `/agent-resume ` 草稿,不直接触发 run 生命周期写入。 - 主窗口最近 checkpoint 列表展示 checkpoint id、文件数、大小和创建时间,同时提供直接对比、填入 `/diff`、确认回滚和填入 `/restore`;回滚继续走 `project.restore` 确认卡,不直接写项目文件。 - `npm run check:native-shells`:覆盖 AI 游戏创作壳的 release/dev 窗口边界、正式用户 App 不嵌入游戏预览 iframe、用户侧预览命令交给外部浏览器和 Tauri release `--no-bundle` 构建 smoke;用于证明正式发布只登记 `launcher` 启动器窗口,选择工作区后才关闭启动器并打开 `main` 主窗口,开发面板只在 debug/dev 路径打开,独立壳能完成 release 编译。