补充项目风险命令入口

新增聊天侧risks命令基于当前状态生成风险摘要

补充主窗口风险入口和帮助命令测试

同步AI游戏创作App技术方案和共享决策记录
This commit is contained in:
AIGameCreator App
2026-07-03 04:19:38 +08:00
parent d046866c07
commit f8af8ca507
4 changed files with 162 additions and 1 deletions
+123
View File
@@ -1826,6 +1826,7 @@ const chatCommandHelp = [
'/audit:审计当前项目的 Agent 能力证据',
'/status:查看项目状态',
'/brief:生成当前项目简报',
'/risks:查看当前项目风险',
'/next:查看下一步建议',
'/open-project:在系统文件管理器中显示项目目录',
'/switch-project:回到项目启动器切换工作区',
@@ -2020,6 +2021,111 @@ function summarizeProjectBrief(
}`;
}
function summarizeProjectRisks(
nextManifest: GameCreationAppManifest,
trace: GameCreationAgentRunTrace | null,
) {
const risks: Array<{ text: string; command?: string }> = [];
const addRisk = (text: string, command?: string) => {
if (command && risks.some((risk) => risk.command === command)) {
return;
}
risks.push({ text, command });
};
const tasks = taskRowsFromManifest(nextManifest);
const failedTasks = tasks.filter((task) => task.status === 'failed');
const readyTasks = selectGameCreationAppReadyTasks({ tasks });
const commandRuns = nextManifest.commandRuns ?? [];
const latestCommandRun = commandRuns[commandRuns.length - 1];
const preview = nextManifest.preview;
const tracePassed =
trace?.status === 'passed' ||
trace?.status === 'artifacts-written' ||
trace?.stopReason === 'evaluator-passed';
const hasCanvasImageAsset = nextManifest.assets.some(
(asset) =>
asset.source.kind === 'canvas' &&
(asset.mediaType.startsWith('image/') ||
asset.mediaType === 'application/vnd.genarrative.image-sequence'),
);
if (!trace) {
addRisk(
'暂无最近 Agent run,当前项目还缺少生成闭环证据。',
'/next',
);
} else if (
trace.lifecycleStatus === 'killed' ||
trace.status === 'failed' ||
trace.stopReason === 'max-passes-exhausted'
) {
addRisk(
`最近 run 未完成:${trace.status} / ${trace.stopReason}`,
'/agent-resume ',
);
} else if (!tracePassed) {
addRisk(
`最近 run 尚未通过 Evaluator${trace.status} / ${trace.stopReason}`,
'/trace',
);
}
if (failedTasks.length > 0) {
addRisk(
`${failedTasks.length} 个任务处于失败状态。`,
'/tasks',
);
}
if (latestCommandRun?.status === 'failed') {
addRisk(
`最近命令 ${latestCommandRun.commandId} 失败。`,
'/logs',
);
}
if (tracePassed && !(preview?.status === 'running' && preview.url)) {
addRisk(
'最近 run 已通过,但当前本地预览未运行。',
'/run',
);
}
if (readyTasks.length > 0) {
addRisk(
`还有 ${readyTasks.length} 个 ready 任务等待处理。`,
'/tasks',
);
}
if (nextManifest.assets.length === 0) {
addRisk(
'暂无本地资产,首版原型可能缺少可复用素材。',
'/asset-register assets/hero.png image image/png',
);
} else if (!hasCanvasImageAsset) {
addRisk(
'暂无画板来源图片资产,美术组可能只能先使用占位素材。',
'/sync-canvas-project ',
);
}
const firstAction = risks.find((risk) => risk.command);
return {
text:
risks.length > 0
? `项目风险:\n${risks
.map(
(risk) => `- ${risk.text}${risk.command ? ` 建议:${risk.command}` : ''}`,
)
.join('\n')}`
: '项目风险:\n- 暂未发现需要立即处理的风险。',
draftCommand: firstAction?.command,
draftCommandLabel: firstAction ? '处理首个风险' : undefined,
};
}
function summarizeNextProjectActions(
nextManifest: GameCreationAppManifest,
trace: GameCreationAgentRunTrace | null,
@@ -5352,6 +5458,23 @@ export function App() {
return;
}
if (prompt === '/risks') {
if (!requireChatProjectForUserAction()) {
return;
}
const summary = summarizeProjectRisks(manifest, agentRunTrace);
setMessages((current) => [
...current,
{
role: 'assistant',
text: summary.text,
draftCommand: summary.draftCommand,
draftCommandLabel: summary.draftCommandLabel,
},
]);
return;
}
if (prompt === '/next') {
if (!requireChatProjectForUserAction()) {
return;
@@ -2134,6 +2134,40 @@ describe('AI 游戏创作 App 界面边界', () => {
),
).toHaveLength(manifestReadCountBeforeBrief);
const manifestReadCountBeforeRisks = invoke.mock.calls.filter(
([command]) => command === 'get_local_game_manifest',
).length;
const fileReadCountBeforeRisks = invoke.mock.calls.filter(
([command]) => command === 'read_local_project_file',
).length;
const runLocalCountBeforeRisks = invoke.mock.calls.filter(
([command]) => command === 'run_limited_local_command',
).length;
submitChat('/risks');
expect(await screen.findByText(/项目风险:/)).not.toBeNull();
expect(
screen.getByText(/最近 run 已通过,但当前本地预览未运行。 建议:\/run/),
).not.toBeNull();
expect(screen.getByText(/还有 1 个 ready 任务等待处理。 建议:\/tasks/))
.not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '处理首个风险' }));
expect(screen.getByLabelText('创作想法')).toHaveProperty('value', '/run');
expect(
invoke.mock.calls.filter(
([command]) => command === 'get_local_game_manifest',
),
).toHaveLength(manifestReadCountBeforeRisks);
expect(
invoke.mock.calls.filter(
([command]) => command === 'read_local_project_file',
),
).toHaveLength(fileReadCountBeforeRisks);
expect(
invoke.mock.calls.filter(
([command]) => command === 'run_limited_local_command',
),
).toHaveLength(runLocalCountBeforeRisks);
const runLocalCountBeforeNext = invoke.mock.calls.filter(
([command]) => command === 'run_limited_local_command',
).length;
@@ -7712,6 +7746,7 @@ describe('AI 游戏创作 App 界面边界', () => {
screen.getByText(/\/switch-project:回到项目启动器切换工作区/),
).not.toBeNull();
expect(screen.getByText(/\/brief:生成当前项目简报/)).not.toBeNull();
expect(screen.getByText(/\/risks:查看当前项目风险/)).not.toBeNull();
expect(screen.getByText(/\/next:查看下一步建议/)).not.toBeNull();
expect(
screen.getByText(
@@ -3857,6 +3857,7 @@
- 2026-07-03 调整:主窗口 Agent 状态栏新增“继续说明”,只把 `/agent-resume ` 填入聊天输入框,让用户补充说明后再走原确认流;策略快捷入口新增 project.index、asset.register、memory.write、preview.open、preview.stop、conversation.read 和 conversation.write 确认草稿,同样只填输入框,不直接写 `.agent/policy.json`
- 2026-07-03 调整:主窗口 header 常驻项目摘要只从当前已加载的 manifest / trace 派生任务完成数、ready 数、资产来源分布和最近命令结果;未选择工作区时不显示,不为了摘要额外触发 Tauri 读取或写入,也不把任务、文件、run history 或预览开发面板搬进普通用户窗口。
- 2026-07-03 调整:普通用户通过聊天输入 `/brief` 触发项目简报入口,只基于主窗口当前已加载的 manifest、最近 run trace、预览状态、资产数量和最近命令生成聊天内简报,并提供 `/next` 作为后续草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览,也不得新增普通用户面板。
- 2026-07-03 调整:普通用户通过聊天输入 `/risks` 查看当前项目风险,只基于主窗口当前已加载的 manifest、最近 run trace、预览状态、任务状态、资产来源和最近命令派生风险摘要,并提供首个风险处理草稿;该入口不得触发 Tauri 读写、不得读取文件、不得启动或打开预览,也不得新增普通用户面板。
- 2026-07-03 调整:`/llm-status` 读取到的 agent 级 LLM 配置状态可回填到主窗口 Agent 状态列表、聊天侧 `/agents` 汇总和单 Agent 对话头部,显示 provider 类型、模型、流式开关和 API Key 是否已读取;密钥本体仍不能进入聊天、状态列表、manifest、trace 或本地项目文件。
- 2026-07-03 调整:开发窗口日志面板提供 `.agent/logs/command.log``.agent/logs/preview.log``.agent/logs/agent.log` 的只读查看入口,复用 `file.read` 授权策略;普通用户聊天输入 `/logs` 只列出这三个日志文件对应的 `/read ...` 草稿 / 命令并提供首个草稿,不直接读取日志,不新增普通用户日志面板,实际读取仍走聊天侧 `file.read`
- 2026-07-03 调整:单 Agent 对话面板允许用户把当前输入手动追加到该 agent 的 `memory/agents/<group>/<role>.md` 私有记忆;写入复用 `memory.write` 项目策略、项目锁和 Tauri 本地目录能力,不把普通对话流水自动混入私有记忆;聊天侧 `/agent-conversations``/agent-memories` 只列出同一批 Agent 对话与私有记忆读取命令并提供首个 `/read` 草稿,不直接读取文件。
File diff suppressed because one or more lines are too long