补充Run历史聊天入口

新增聊天侧runs命令基于已加载状态生成Run读取草稿

补充主窗口Run历史入口和帮助命令测试

同步AI游戏创作App技术方案和共享决策记录
This commit is contained in:
AIGameCreator App
2026-07-03 04:27:46 +08:00
parent f8af8ca507
commit 4a6a9d4a2f
4 changed files with 103 additions and 1 deletions
+52
View File
@@ -1854,6 +1854,7 @@ const chatCommandHelp = [
'/assets:列出本地项目资产',
'/artifacts:列出常用生成产物读取命令',
'/run-artifacts:列出最近 Run 产物读取命令',
'/runs:列出已加载 Run 历史读取命令',
'/logs:列出常用日志读取命令',
'/asset-register 路径 [kind] [mediaType]:登记项目内已有资产',
'/read 路径:读取本地项目内文本文件',
@@ -3625,6 +3626,29 @@ function summarizeRunArtifactReadDrafts(trace: GameCreationAgentRunTrace | null)
.join('\n')}`;
}
function summarizeAgentRunHistoryReadDrafts(
trace: GameCreationAgentRunTrace | null,
history: AgentRunHistoryItem[],
) {
const lines: string[] = [];
if (trace) {
lines.push(
`- 当前指针 ${trace.runId} · ${formatAgentRunStatus(trace)}:/trace`,
);
}
history.slice(0, 5).forEach((item) => {
lines.push(
`- ${item.trace.runId} · ${formatAgentRunStatus(item.trace)} · ${item.path} · ${item.size}B:/read ${item.path}`,
);
});
if (history.length > 5) {
lines.push(`- 还有 ${history.length - 5} 个已加载历史 run`);
}
return lines.length > 0
? `Run 历史读取命令:\n${lines.join('\n')}`
: 'Run 历史读取命令:暂无已加载 run';
}
function localAgentConversationReceipt(agent: AgentStatusCard) {
return `已记录给 ${agent.title}。下一次生成会把这条对话作为该 agent 的上下文读取。`;
}
@@ -5692,6 +5716,34 @@ export function App() {
return;
}
if (prompt === '/runs') {
if (!requireChatProjectForUserAction()) {
return;
}
const firstHistory = agentRunHistory[0] ?? null;
setMessages((current) => [
...current,
{
role: 'assistant',
text: summarizeAgentRunHistoryReadDrafts(
agentRunTrace,
agentRunHistory,
),
draftCommand: firstHistory
? `/read ${firstHistory.path}`
: agentRunTrace
? '/trace'
: undefined,
draftCommandLabel: firstHistory
? '读取首个历史 Run'
: agentRunTrace
? '查看最近 Run'
: undefined,
},
]);
return;
}
if (prompt === '/logs') {
if (!requireChatProjectForUserAction()) {
return;
@@ -2168,6 +2168,22 @@ describe('AI 游戏创作 App 界面边界', () => {
),
).toHaveLength(runLocalCountBeforeRisks);
const fileReadCountBeforeRuns = invoke.mock.calls.filter(
([command]) => command === 'read_local_project_file',
).length;
submitChat('/runs');
expect(await screen.findByText(/Run 历史读取命令:/)).not.toBeNull();
expect(
screen.getByText(/当前指针 run-main-shortcut-trace .*:\/trace/),
).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '查看最近 Run' }));
expect(screen.getByLabelText('创作想法')).toHaveProperty('value', '/trace');
expect(
invoke.mock.calls.filter(
([command]) => command === 'read_local_project_file',
),
).toHaveLength(fileReadCountBeforeRuns);
const runLocalCountBeforeNext = invoke.mock.calls.filter(
([command]) => command === 'run_limited_local_command',
).length;
@@ -3499,8 +3515,36 @@ describe('AI 游戏创作 App 界面边界', () => {
projectPath: '/tmp/authorized-game',
});
});
await waitFor(() => {
expect(invoke).toHaveBeenCalledWith('read_local_project_file', {
projectPath: '/tmp/authorized-game',
relativePath: '.agent/runs/run-hidden-from-chat.json',
commandId: 'agent.trace_read',
});
});
expect(screen.queryByLabelText('最近 run')).toBeNull();
expect(screen.queryByText(/run-hidden-from-chat/)).toBeNull();
const runReadCountBeforeRunsCommand = invoke.mock.calls.filter(
([command]) => command === 'read_local_project_file',
).length;
submitChat('/runs');
expect(await screen.findByText(/Run 历史读取命令:/)).not.toBeNull();
expect(
screen.getByText(
/run-hidden-from-chat .* \.agent\/runs\/run-hidden-from-chat\.json .*:\/read \.agent\/runs\/run-hidden-from-chat\.json/,
),
).not.toBeNull();
fireEvent.click(screen.getByRole('button', { name: '读取首个历史 Run' }));
expect(screen.getByLabelText('创作想法')).toHaveProperty(
'value',
'/read .agent/runs/run-hidden-from-chat.json',
);
expect(
invoke.mock.calls.filter(
([command]) => command === 'read_local_project_file',
),
).toHaveLength(runReadCountBeforeRunsCommand);
});
it('confirms before opening a run history trace when policy requires it', async () => {
@@ -7759,6 +7803,9 @@ describe('AI 游戏创作 App 界面边界', () => {
expect(
screen.getByText(/\/run-artifacts:列出最近 Run 产物读取命令/),
).not.toBeNull();
expect(
screen.getByText(/\/runs:列出已加载 Run 历史读取命令/),
).not.toBeNull();
expect(
screen.getByText(/\/logs:列出常用日志读取命令/),
).not.toBeNull();
@@ -3858,6 +3858,7 @@
- 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 调整:普通用户通过聊天输入 `/runs` 查看已加载 Run 历史读取命令,只基于主窗口当前已加载的 latest trace 和最多 100 个历史 run 中已经载入的批次生成 `/trace` 或 `/read .agent/runs/...` 草稿;该入口不得额外触发 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