落地游戏创作本地工程护栏
新增项目 checkpoint、diff、restore、本地索引、写锁和项目级权限策略。 接入聊天命令和共享命令契约,普通用户可确认恢复与策略写入。 补充上下文敏感信息过滤,避免密钥和本机配置进入 LLM prompt。 更新 AI 游戏创作 App 文档、共享记忆和相关测试。
This commit is contained in:
@@ -499,6 +499,7 @@ try {
|
||||
step.agent === 'Planner' &&
|
||||
step.inputPaths?.includes('memory/session.md') &&
|
||||
step.inputPaths?.includes('memory/project.md') &&
|
||||
step.inputPaths?.includes('memory/blackboard.md') &&
|
||||
step.inputPaths?.includes('.agent/manifest.json'),
|
||||
),
|
||||
'trace missing planner memory or manifest inputs',
|
||||
@@ -508,6 +509,8 @@ try {
|
||||
(step) =>
|
||||
step.agent === '策划组 / Director' &&
|
||||
step.pass === 1 &&
|
||||
step.inputPaths?.includes('memory/blackboard.md') &&
|
||||
step.inputPaths?.includes('memory/agents/design/director.md') &&
|
||||
step.inputPaths?.includes('.agent/manifest.json') &&
|
||||
step.inputPaths?.includes('.agent/passes/pass-1/agenda.md'),
|
||||
),
|
||||
@@ -518,6 +521,7 @@ try {
|
||||
(step) =>
|
||||
step.agent === 'Generator' &&
|
||||
step.pass === 2 &&
|
||||
step.inputPaths?.includes('memory/blackboard.md') &&
|
||||
step.inputPaths?.includes('.agent/manifest.json') &&
|
||||
step.inputPaths?.includes('.agent/passes/pass-2/agenda.md') &&
|
||||
step.inputPaths?.includes('.agent/passes/pass-2/task-graph.json'),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,11 @@
|
||||
/** @vitest-environment jsdom */
|
||||
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
|
||||
import {
|
||||
act,
|
||||
cleanup,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
} from '@testing-library/react';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import React from 'react';
|
||||
@@ -196,8 +202,19 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
expect(
|
||||
screen.getByText(/\/audit:审计当前项目的 Agent 能力证据/),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/\/agent-status:查看最近 run 生命周期/)).not.toBeNull();
|
||||
expect(screen.getByText(/\/agent-kill:标记最近 run 为 killed/)).not.toBeNull();
|
||||
expect(screen.getByText(/\/checkpoint:保存本地项目快照/)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/\/restore checkpoint-id:恢复 checkpoint/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/\/policy-deny 命令:拒绝项目内某个内置命令/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/\/agent-status:查看最近 run 生命周期/),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/\/agent-kill:标记最近 run 为 killed/),
|
||||
).not.toBeNull();
|
||||
expect(screen.queryByLabelText('开发环境')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -422,7 +439,11 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
pass: 2,
|
||||
mode: 'repair',
|
||||
summary: '第 2 轮返工',
|
||||
activeTaskIds: ['code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director'],
|
||||
dependencyWaves: [
|
||||
['code-prototype'],
|
||||
@@ -433,7 +454,11 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
repairRoutes: [
|
||||
{
|
||||
issue: '缺少输入监听',
|
||||
taskIds: ['code-prototype', 'quality-review', 'preview-readiness'],
|
||||
taskIds: [
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
reason: 'code-runtime',
|
||||
},
|
||||
],
|
||||
@@ -466,6 +491,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
{ path: 'game/index.html', kind: 'file', size: 1024 },
|
||||
{ path: 'memory/session.md', kind: 'file', size: 64 },
|
||||
{ path: 'memory/project.md', kind: 'file', size: 64 },
|
||||
{ path: 'memory/blackboard.md', kind: 'file', size: 64 },
|
||||
{
|
||||
path: 'memory/agents/design/director.md',
|
||||
kind: 'file',
|
||||
size: 64,
|
||||
},
|
||||
{ path: '.agent/logs/command.log', kind: 'file', size: 64 },
|
||||
],
|
||||
};
|
||||
@@ -511,7 +542,7 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
screen.getByText(/Planner\/Orchestrator\/Generator\/Evaluator:通过/),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/返工路由\/Carry-over:通过/)).not.toBeNull();
|
||||
expect(screen.getByText(/短期\/长期记忆:通过/)).not.toBeNull();
|
||||
expect(screen.getByText(/记忆:通过/)).not.toBeNull();
|
||||
expect(screen.getByText(/本地产物:通过/)).not.toBeNull();
|
||||
expect(screen.getByText(/本地 HTTP 预览:通过/)).not.toBeNull();
|
||||
expect(screen.getByText(/画板回流:通过/)).not.toBeNull();
|
||||
@@ -569,7 +600,9 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
if (command === 'list_local_project_files') {
|
||||
return {
|
||||
projectPath: String(args?.projectPath ?? ''),
|
||||
files: [{ path: '.agent/logs/command.log', kind: 'file', size: 64 }],
|
||||
files: [
|
||||
{ path: '.agent/logs/command.log', kind: 'file', size: 64 },
|
||||
],
|
||||
};
|
||||
}
|
||||
if (command === 'read_local_project_file') {
|
||||
@@ -788,7 +821,11 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
taskGraph: {
|
||||
goal: '做一个三轮仍失败的厨房弹幕游戏',
|
||||
readyTaskIds: [],
|
||||
activeTaskIds: ['code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director'],
|
||||
repairFocus: ['缺少输入监听'],
|
||||
repairRoutes: [
|
||||
@@ -805,7 +842,11 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
pass: 3,
|
||||
mode: 'repair',
|
||||
summary: '第 3 轮返工仍未通过',
|
||||
activeTaskIds: ['code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director'],
|
||||
dependencyWaves: [
|
||||
['code-prototype'],
|
||||
@@ -816,7 +857,11 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
repairRoutes: [
|
||||
{
|
||||
issue: '缺少输入监听',
|
||||
taskIds: ['code-prototype', 'quality-review', 'preview-readiness'],
|
||||
taskIds: [
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
reason: 'code-runtime',
|
||||
},
|
||||
],
|
||||
@@ -941,6 +986,62 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('restores checkpoints through the authorized local project path', async () => {
|
||||
const manifest = createGameCreationAppManifest(
|
||||
'local-project-draft',
|
||||
'未命名游戏原型',
|
||||
);
|
||||
const invoke = vi.fn(
|
||||
async (command: string, args?: Record<string, unknown>) => {
|
||||
if (command === 'append_local_permission_log') {
|
||||
return {};
|
||||
}
|
||||
if (command === 'init_local_game_project') {
|
||||
const projectPath = String(args?.projectPath ?? '');
|
||||
return {
|
||||
projectPath,
|
||||
manifestPath: `${projectPath}/.agent/manifest.json`,
|
||||
manifest,
|
||||
};
|
||||
}
|
||||
if (command === 'restore_local_project_checkpoint') {
|
||||
return {
|
||||
checkpointId: String(args?.checkpointId ?? ''),
|
||||
restoredCount: 3,
|
||||
};
|
||||
}
|
||||
if (command === 'get_local_game_manifest') {
|
||||
return manifest;
|
||||
}
|
||||
throw new Error(`unexpected invoke ${command}`);
|
||||
},
|
||||
);
|
||||
window.__TAURI__ = { core: { invoke } };
|
||||
renderAppAt('/');
|
||||
|
||||
submitChat('/project /tmp/authorized-game');
|
||||
fireEvent.click(screen.getByRole('button', { name: '确认' }));
|
||||
expect(
|
||||
await screen.findByText('已设置本地项目:/tmp/authorized-game'),
|
||||
).not.toBeNull();
|
||||
|
||||
submitChat('/restore checkpoint-1');
|
||||
fireEvent.click(screen.getByRole('button', { name: '确认' }));
|
||||
|
||||
expect(
|
||||
await screen.findByText(/已恢复 3 个文件:checkpoint-1/),
|
||||
).not.toBeNull();
|
||||
expect(invoke).toHaveBeenCalledWith('restore_local_project_checkpoint', {
|
||||
projectPath: '/tmp/authorized-game',
|
||||
checkpointId: 'checkpoint-1',
|
||||
});
|
||||
expect(invoke).toHaveBeenCalledWith('append_local_permission_log', {
|
||||
projectPath: '/tmp/authorized-game',
|
||||
event: 'permission.confirm',
|
||||
commandId: 'project.restore',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not stop a preview before a local project is initialized', () => {
|
||||
const invoke = vi.fn();
|
||||
window.__TAURI__ = { core: { invoke } };
|
||||
@@ -1273,7 +1374,9 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '确认' }));
|
||||
|
||||
expect(
|
||||
await screen.findByText(/运行通过,预览已启动:http:\/\/127\.0\.0\.1:3210\//),
|
||||
await screen.findByText(
|
||||
/运行通过,预览已启动:http:\/\/127\.0\.0\.1:3210\//,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/已交给外部浏览器打开。/)).not.toBeNull();
|
||||
expect(invoke).toHaveBeenCalledWith('run_limited_local_command', {
|
||||
@@ -1570,10 +1673,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
};
|
||||
},
|
||||
);
|
||||
const listen = vi.fn(async (_event: string, handler: typeof progressHandler) => {
|
||||
progressHandler = handler;
|
||||
return vi.fn();
|
||||
});
|
||||
const listen = vi.fn(
|
||||
async (_event: string, handler: typeof progressHandler) => {
|
||||
progressHandler = handler;
|
||||
return vi.fn();
|
||||
},
|
||||
);
|
||||
window.__TAURI__ = { core: { invoke }, event: { listen } };
|
||||
renderAppAt('/');
|
||||
|
||||
@@ -1748,13 +1853,23 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
taskGraph: {
|
||||
goal: '做一个反弹弹幕厨房游戏',
|
||||
readyTaskIds: ['preview-playtest'],
|
||||
activeTaskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director', 'design-foundation'],
|
||||
repairFocus: ['缺少输入监听'],
|
||||
repairRoutes: [
|
||||
{
|
||||
issue: '缺少输入监听',
|
||||
taskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
taskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
reason: 'code-runtime',
|
||||
},
|
||||
],
|
||||
@@ -1765,7 +1880,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
pass: 2,
|
||||
mode: 'repair',
|
||||
summary: '第 2 轮按 Evaluator 反馈返工',
|
||||
activeTaskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director', 'design-foundation'],
|
||||
dependencyWaves: [
|
||||
['code-director'],
|
||||
@@ -1777,7 +1897,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
repairRoutes: [
|
||||
{
|
||||
issue: '缺少输入监听',
|
||||
taskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
taskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
reason: 'code-runtime',
|
||||
},
|
||||
],
|
||||
@@ -1870,10 +1995,14 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
submitChat('做一个反弹弹幕厨房游戏');
|
||||
fireEvent.click(screen.getByRole('button', { name: '确认' }));
|
||||
|
||||
expect(screen.getByText(/开始调用 LLM:Planner 正在整理规格。/)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/开始调用 LLM:Planner 正在整理规格。/),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/Generator 生成代码和资产清单/)).not.toBeNull();
|
||||
expect(
|
||||
await screen.findByText(/已保存并启动本地预览:http:\/\/127\.0\.0\.1:3210\//),
|
||||
await screen.findByText(
|
||||
/已保存并启动本地预览:http:\/\/127\.0\.0\.1:3210\//,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/已交给外部浏览器打开。/)).not.toBeNull();
|
||||
expect(screen.getByText(/Run:run-chat-generate/)).not.toBeNull();
|
||||
@@ -1882,8 +2011,16 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/工具调用:36\/128/)).not.toBeNull();
|
||||
expect(screen.getByText(/LLM 对话:/)).not.toBeNull();
|
||||
expect(screen.getByText(/Planner #0 · completed · planning · llm\.chat\.planner/)).not.toBeNull();
|
||||
expect(screen.getByText(/Generator #2 · completed · generate · llm\.chat\.generator/)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Planner #0 · completed · planning · llm\.chat\.planner/,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Generator #2 · completed · generate · llm\.chat\.generator/,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/active 任务:程序组 \/ Director 拆解程序实现\(code-director\), 程序组 \/ Code 生成可运行原型\(code-prototype\), 程序组 \/ Review 执行质量评审\(quality-review\), 程序组 \/ Preview 执行静态自检\(preview-readiness\)/,
|
||||
@@ -1899,9 +2036,13 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/编排轮次:/)).not.toBeNull();
|
||||
expect(screen.getByText(/产物快照:/)).not.toBeNull();
|
||||
expect(screen.getByText(/- game\/index\.html · fnv1a64:game/)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/- game\/index\.html · fnv1a64:game/),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/最近步骤:/)).not.toBeNull();
|
||||
expect(screen.getByText(/Evaluator #2 · passed · evaluation/)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/Evaluator #2 · passed · evaluation/),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/完整 trace:\/trace/)).not.toBeNull();
|
||||
expect(screen.queryByLabelText('开发环境')).toBeNull();
|
||||
expect(screen.queryByText('编排 Trace')).toBeNull();
|
||||
@@ -2163,13 +2304,23 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
taskGraph: {
|
||||
goal: '做一个反弹弹幕厨房游戏',
|
||||
readyTaskIds: ['code-director'],
|
||||
activeTaskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director'],
|
||||
repairFocus: ['缺少输入监听'],
|
||||
repairRoutes: [
|
||||
{
|
||||
issue: '缺少输入监听',
|
||||
taskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
taskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
reason: 'code-repair',
|
||||
},
|
||||
],
|
||||
@@ -2180,7 +2331,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
pass: 2,
|
||||
mode: 'repair',
|
||||
summary: '第 2 轮按 Evaluator 反馈返工',
|
||||
activeTaskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
activeTaskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
carriedTaskIds: ['design-director'],
|
||||
dependencyWaves: [
|
||||
['code-director'],
|
||||
@@ -2192,7 +2348,12 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
repairRoutes: [
|
||||
{
|
||||
issue: '缺少输入监听',
|
||||
taskIds: ['code-director', 'code-prototype', 'quality-review', 'preview-readiness'],
|
||||
taskIds: [
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
],
|
||||
reason: 'code-repair',
|
||||
},
|
||||
],
|
||||
@@ -2337,7 +2498,8 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
...resultByAction,
|
||||
activityPath: '/tmp/authorized-game/.agent/activity.jsonl',
|
||||
outputPath: '/tmp/authorized-game/.agent/output.jsonl',
|
||||
contextBundlePath: '/tmp/authorized-game/.agent/context.bundle.json',
|
||||
contextBundlePath:
|
||||
'/tmp/authorized-game/.agent/context.bundle.json',
|
||||
};
|
||||
}
|
||||
if (command === 'read_local_project_file') {
|
||||
@@ -2361,7 +2523,9 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
|
||||
submitChat('/agent-status');
|
||||
expect(
|
||||
await screen.findByText(/run run-control-chat 当前状态:pending \/ pending/),
|
||||
await screen.findByText(
|
||||
/run run-control-chat 当前状态:pending \/ pending/,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(invoke).toHaveBeenCalledWith('control_agent_run', {
|
||||
projectPath: '/tmp/authorized-game',
|
||||
|
||||
@@ -112,10 +112,37 @@ describe('AI 游戏创作聊天记忆命令', () => {
|
||||
expect(needsInitializedChatProject('canvas.export_import')).toBe(true);
|
||||
expect(needsInitializedChatProject('memory.write')).toBe(true);
|
||||
expect(needsInitializedChatProject('memory.delete')).toBe(true);
|
||||
expect(needsInitializedChatProject('project.checkpoint')).toBe(true);
|
||||
expect(needsInitializedChatProject('project.restore')).toBe(true);
|
||||
expect(needsInitializedChatProject('project.policy_write')).toBe(true);
|
||||
expect(needsInitializedChatProject('project.create')).toBe(false);
|
||||
expect(needsInitializedChatProject('canvas.project_open')).toBe(false);
|
||||
});
|
||||
|
||||
it('describes project safety commands before confirmation', () => {
|
||||
expect(
|
||||
pendingCommandDetail({ id: 'project.checkpoint' }, '/tmp/game'),
|
||||
).toBe('保存 /tmp/game 当前项目快照');
|
||||
expect(
|
||||
pendingCommandDetail(
|
||||
{ id: 'project.restore', checkpointId: 'checkpoint-1' },
|
||||
'/tmp/game',
|
||||
),
|
||||
).toBe('从 checkpoint-1 恢复已跟踪项目文件');
|
||||
expect(
|
||||
pendingCommandDetail(
|
||||
{
|
||||
id: 'project.policy_write',
|
||||
policy: {
|
||||
deniedCommands: ['file.write'],
|
||||
confirmCommands: [],
|
||||
},
|
||||
},
|
||||
'/tmp/game',
|
||||
),
|
||||
).toBe('写入 /tmp/game/.agent/policy.json');
|
||||
});
|
||||
|
||||
it('describes agent run lifecycle controls before confirmation', () => {
|
||||
expect(pendingCommandDetail({ id: 'agent.kill' }, '/tmp/game')).toBe(
|
||||
'标记 /tmp/game/.agent/run.latest.json 为 killed,并写入 activity/output',
|
||||
|
||||
@@ -3850,6 +3850,8 @@
|
||||
- 2026-06-30 调整:`game.generate_draft` 在 `editorApi.apiKey` 已配置且美术组缺少 `canvas` 来源图片资产时,直接调用 External Editor API 的 `/api/external/v1/editor/images/generations` 生成首版美术素材,再通过 `/api/external/v1/assets/read-url` 换签下载到本地 `assets/canvas-generated/`,登记为 `canvas` 来源资产并追加 `canvas.asset_generate` 本地索引记录。API Key 不写入 manifest、agent.db、trace 或日志;生成失败不阻断本地原型生成,会在 trace 中记录失败并保留同步建议。
|
||||
- 2026-06-25 调整,2026-06-30 更新:美术组 `Asset` 和音乐组 `SFX` 角色在 loop 中读取 `.agent/manifest.json`;当本地项目还没有对应类型的 `canvas` 来源资产时,角色 step 会追加 `agent.tool.suggest.canvas.project_sync` toolCall:美术组需要 `image/*` 或 `application/vnd.genarrative.image-sequence`,音乐组需要 `audio/*`。美术组在未配置 `editorApi.apiKey` 或平台生图失败时仍只给出同步建议;音乐组不调用图片生成接口,只建议同步已有音频资源。
|
||||
- 2026-06-24 调整:同一本地项目多次 `game.generate_draft` 必须追加 `memory/session.md` 与 `memory/project.md`,不得覆盖历史对话和创作目标记录。
|
||||
- 2026-07-01 调整:AI 游戏创作 App 在 `memory/session.md` 与 `memory/project.md` 之外新增项目级黑板 `memory/blackboard.md`,只记录重要跨 agent 决策、依赖和风险摘要;每个角色 agent 拥有私有记忆 `memory/agents/<group>/<role>.md`。角色 brief 必须读取自己的私有记忆和项目黑板;`game.generate_draft` 通过 Evaluator 与 `game.static_smoke` 后,追加项目黑板摘要和各角色成功产出摘要,不得覆盖既有记忆。失败 run 仍只保留 trace 和 pass 快照,不写最终记忆摘要。
|
||||
- 2026-07-01 调整:AI 游戏创作 App 借鉴 Godcoder 的本地工程护栏,但只收敛到五项本地机制:`ArtifactWriter` 写入前 checkpoint、写入后 diff、用户确认 restore;进入 LLM 前过滤密钥和本机配置痕迹;`.agent/agent.db` 继续作为轻量 JSONL 项目索引,`/index` 额外刷新 `.agent/project.index.json`;同一项目写入通过 `.agent/project.lock` 串行化;`.agent/policy.json` 记录项目级命令拒绝 / 确认策略。v1 不引入通用 IDE 插件、云工作区、SQLite 或任意 shell 代理。
|
||||
- 2026-06-24 调整:普通用户通过聊天输入 `/help` 发现可用内置命令;命令发现必须留在聊天消息里,不得因此暴露开发面板。
|
||||
- 2026-06-24 调整:聊天区待确认命令的日志语义必须区分 `permission.pending`、`permission.confirm` 和 `permission.cancel`;待确认卡片必须展示本地写入目标路径,避免用户在不知道落盘位置时确认。
|
||||
- 2026-06-24 调整:普通用户通过聊天输入 `/status` 读取 `.agent/manifest.json` 的项目状态摘要,只在聊天消息里展示项目目录、任务状态、资产数量、预览状态和最近命令;不得为了状态查看暴露任务、文件或日志面板。
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
- Agent Runtime:扩展 `server-rs/crates/platform-agent`,不引入 LangChain、AutoGen、Microsoft Agent Framework 或 OpenAI Agents SDK sidecar 作为核心。
|
||||
- 设计参考:借鉴 OpenAI Agents SDK 的 Agent、Tools、Handoffs、Guardrails、Tracing 抽象,但运行时由 Genarrative 自己掌控。
|
||||
- Run 控制参考:借鉴 Harbour 的控制平面思想,只吸收 `run lifecycle`、activity/output stream、context bundle、kill/retry/resume 等本地运行治理能力;不引入 Harbour 的多租户后台、调度 UI、通用 shell workflow 或远程 runner 作为 v1 依赖。
|
||||
- 本地工程参考:借鉴 Godcoder 的本地产物 checkpoint / diff / restore、上下文安全过滤、轻量项目索引、项目级写锁和项目级权限策略;不引入通用 IDE 插件、云工作区或任意代码代理。
|
||||
|
||||
## Runtime 边界
|
||||
|
||||
@@ -20,7 +21,7 @@ Agent Runtime 负责:
|
||||
- 总任务拆分和任务图状态流转。
|
||||
- 6 个专业组调度:策划组、美术组、程序组、数值组、音乐组、运营组。
|
||||
- 工具注册、权限 gate、工具调用预算和执行日志。
|
||||
- 短期记忆、长期记忆、本地 manifest 和产物索引。
|
||||
- 短期记忆、长期记忆、项目黑板、角色私有记忆、本地 manifest 和产物索引。
|
||||
- 本地项目文件、运行命令、HTTP 预览和验收自检。
|
||||
|
||||
专业组不是 6 个单 Agent。每组内部按任务动态使用 Director、Gameplay、Code、Asset、Preview、Playtest、Polish、Publish 等角色模板。
|
||||
@@ -31,9 +32,11 @@ Agent Runtime 负责:
|
||||
- 命令能力:内置命令调用、权限 gate、执行日志;v1 只允许白名单受限命令,不执行任意 shell。
|
||||
- 编排能力:任务拆分、任务图依赖、专业组调度、多智能体协作。
|
||||
- 任务图能力:每轮 Orchestrator agenda、ready / active task 选择、Evaluator 结构化返工路由、返工轮 carry-over。
|
||||
- 记忆能力:短期记忆 `memory/session.md`、长期记忆 `memory/project.md`,支持读取、保存和删除。
|
||||
- 记忆能力:短期记忆 `memory/session.md`、长期记忆 `memory/project.md`、项目级黑板 `memory/blackboard.md` 和角色私有记忆 `memory/agents/<group>/<role>.md`;黑板用于共享重要跨 agent 记忆,角色私有记忆只给对应角色 brief 读取和追加。
|
||||
- 本地能力:生成代码和资产落盘、本地 manifest、受限运行命令、本地 HTTP 预览。
|
||||
- 文件能力:`file.list/read/write/delete` 只允许访问项目目录内的相对路径,拒绝绝对路径、`..`、反斜杠和符号链接。
|
||||
- 产物治理能力:写入前生成本地 checkpoint,写入后记录相对路径 diff,用户确认后可 restore 到最近 checkpoint。
|
||||
- 安全能力:上下文进入 LLM 前先做密钥模式和本机配置痕迹过滤;项目写入走项目级写锁和项目级权限策略。
|
||||
- 创作能力:策划、美术、程序、数值、音乐、运营 6 组专业 agent,组内按任务使用 Director、Gameplay、Code、Asset、Preview、Playtest、Polish、Publish 等角色模板。
|
||||
|
||||
## 本地项目结构
|
||||
@@ -43,14 +46,24 @@ game-project/
|
||||
game/
|
||||
assets/
|
||||
memory/
|
||||
session.md
|
||||
project.md
|
||||
blackboard.md
|
||||
agents/
|
||||
<group>/
|
||||
<role>.md
|
||||
exports/
|
||||
.agent/
|
||||
agent.db
|
||||
manifest.json
|
||||
policy.json
|
||||
project.lock
|
||||
project.index.json
|
||||
run.latest.json
|
||||
activity.jsonl
|
||||
output.jsonl
|
||||
context.bundle.json
|
||||
checkpoints/
|
||||
runs/
|
||||
logs/
|
||||
```
|
||||
@@ -86,7 +99,7 @@ game-project/
|
||||
- 生成代码和资产进入用户本地项目目录。
|
||||
- 本地 HTTP 预览能启动,并在外部浏览器展示可玩原型。
|
||||
- 美术/音乐资产能从画板链路回流到本地项目。
|
||||
- 长期记忆可查看、可编辑、可删除。
|
||||
- 短期记忆、长期记忆、项目黑板和角色私有记忆按授权本地项目路径读写;普通用户仍只通过聊天命令访问短期 / 长期记忆。
|
||||
|
||||
## v1 验收证据矩阵
|
||||
|
||||
@@ -99,7 +112,7 @@ game-project/
|
||||
## 当前最小落地
|
||||
|
||||
- `apps/ai-game-creator-shell` 是独立 Tauri App,不复用 `apps/desktop-shell`。
|
||||
- 本地项目初始化会创建 `game/`、`assets/`、`memory/`、`exports/`、`.agent/logs/`,写入 `.agent/manifest.json`,生成 append-only JSONL 本地产物索引 `.agent/agent.db`,并生成默认 `game/index.html`。
|
||||
- 本地项目初始化会创建 `game/`、`assets/`、`memory/`、`memory/agents/`、`exports/`、`.agent/logs/`,写入 `.agent/manifest.json`,生成 append-only JSONL 本地项目索引 `.agent/agent.db`,并生成默认 `game/index.html`。
|
||||
- 普通用户可在聊天框输入 `/project /绝对路径` 生成待确认的 `project.create` 命令,用于授权并初始化本地项目目录;相对路径不会生成待确认命令;开发窗口仍可直接编辑项目路径。
|
||||
- 普通用户侧的生成、上传、运行、自检、预览状态 / 启动 / 打开 / 停止、记忆写入和画板资产导入都必须先完成 `/project` 初始化;未初始化时只提示设置本地项目,不落到默认临时目录。
|
||||
- 终端可用 `npm run ai-game-creator-shell:llm-status` 检查 LLM 客户端配置是否就绪;桌面 App 主窗口“配置”面板可读写 Tauri 应用配置目录中的 `game-creator.config.json`,`/llm-status` / 生成入口读取同一份配置,CLI 开发入口无 AppHandle 时才回退读取仓库旁边的配置模板和 gitignored 本机覆盖文件;不请求上游、不显示 API Key,缺配置时以非零状态退出或在聊天里提示未就绪。
|
||||
@@ -113,21 +126,24 @@ game-project/
|
||||
- 主窗口“配置”面板读写 Tauri 应用配置目录中的 `game-creator.config.json`,覆盖 LLM API Key、base URL、模型、API 类型、流式请求、超时、重试和画板 External API 配置;保存时只写运行时配置文件,不写仓库模板、本地项目、trace 或 manifest。
|
||||
- 聊天输入 `/llm-status` 会触发只读 `llm.config_check`,确认 LLM base_url、model 和 API Key 是否已从客户端配置读取;状态消息不会显示或保存 API Key。
|
||||
- `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 写入 `.agent/passes/pass-N/groups/<group>/*.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` 静态自检和预览试玩。
|
||||
- `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/<group>/<role>.md` 和项目黑板 `memory/blackboard.md`,写入 `.agent/passes/pass-N/groups/<group>/*.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` 失败,不写最终游戏产物。
|
||||
- loop 每次运行会写 `.agent/run.latest.json` 和 `.agent/runs/<runId>.json`,记录 `Planner` / `Orchestrator` agenda / 16 个组内角色 brief 或 carry-over / 6 个 `GroupCoordinator` 汇总 / `Generator` / 6 个专业组交接 / `Evaluator` 质量评审 / `ArtifactWriter` / `Playtest` step、每步 `toolCalls`、输入文件、输出文件、状态、轮次、maxPasses、toolCallCount、maxToolCalls、stopReason、nextStep 和错误摘要;Planner、角色 agent 和 Generator 的 `inputPaths` 必须包含对应记忆文件、`.agent/manifest.json` 和 agenda 等上下文来源;每个 step 必须带 phase、taskId、group 和 role,`.agent/run.latest.json.taskGraph` 必须记录 goal、readyTaskIds、activeTaskIds、carriedTaskIds、repairFocus、repairRoutes 和当前任务状态;`.agent/run.latest.json.passPlans` 必须按轮记录 mode、summary、activeTaskIds、carriedTaskIds、dependencyWaves、repairFocus 和 repairRoutes,作为 `/trace` 与开发窗口判断编排 loop 是否真实发生的直接证据;`run.latest.json` 是当前指针,`.agent/runs/` 保留历史 run trace;开发窗口读取 latest 展示编排过程,并复用 `file.list/read` 列出和载入历史 run,普通用户窗口不展示。
|
||||
- loop 每次运行会写 `.agent/run.latest.json` 和 `.agent/runs/<runId>.json`,记录 `Planner` / `Orchestrator` agenda / 16 个组内角色 brief 或 carry-over / 6 个 `GroupCoordinator` 汇总 / `Generator` / 6 个专业组交接 / `Evaluator` 质量评审 / `ArtifactWriter` / `Playtest` step、每步 `toolCalls`、输入文件、输出文件、状态、轮次、maxPasses、toolCallCount、maxToolCalls、stopReason、nextStep 和错误摘要;Planner、角色 agent 和 Generator 的 `inputPaths` 必须包含对应记忆文件、`.agent/manifest.json` 和 agenda 等上下文来源,其中角色 agent 必须包含自己的 `memory/agents/<group>/<role>.md` 和 `memory/blackboard.md`;每个 step 必须带 phase、taskId、group 和 role,`.agent/run.latest.json.taskGraph` 必须记录 goal、readyTaskIds、activeTaskIds、carriedTaskIds、repairFocus、repairRoutes 和当前任务状态;`.agent/run.latest.json.passPlans` 必须按轮记录 mode、summary、activeTaskIds、carriedTaskIds、dependencyWaves、repairFocus 和 repairRoutes,作为 `/trace` 与开发窗口判断编排 loop 是否真实发生的直接证据;`run.latest.json` 是当前指针,`.agent/runs/` 保留历史 run trace;开发窗口读取 latest 展示编排过程,并复用 `file.list/read` 列出和载入历史 run,普通用户窗口不展示。
|
||||
- `.agent/run.latest.json` 的 `artifacts` 使用结构化记录,包含相对路径、字节数和 `fnv1a64:` checksum;除最终本地产物外,也会收集 `.agent/passes/pass-N/` 快照,便于确认返工前后的产物差异。
|
||||
- 通过 Evaluator 和 `game.static_smoke` 后,Agent loop 会把本次 runId、状态、轮次、下一步、active / carry-over 任务和最终本地产物摘要追加到 `memory/session.md` 与 `memory/project.md`;下一次 Planner、组内角色和 Generator 会通过记忆输入自然读取上一轮稳定原型状态,而不只依赖开发窗口 trace。
|
||||
- `.agent/agent.db` 当前作为最小本地索引文件使用 JSONL:初始化写入 `project.init`,每次 `game.generate_draft` 追加目标、标题和本地产物路径,上传 / 登记 / 画板导入资产时追加 `asset.register` 或 `asset.update`;v1 不引入 SQLite 依赖。
|
||||
- 通过 Evaluator 和 `game.static_smoke` 后,Agent loop 会把本次 runId、状态、轮次、下一步、active / carry-over 任务和最终本地产物摘要追加到 `memory/session.md` 与 `memory/project.md`,把重要跨 agent 决策 / 依赖 / 风险摘要追加到 `memory/blackboard.md`,并把各角色本轮成功产出的角色摘要追加到 `memory/agents/<group>/<role>.md`;下一次 Planner、组内角色和 Generator 会通过记忆输入自然读取上一轮稳定原型状态,而不只依赖开发窗口 trace。
|
||||
- `.agent/agent.db` 当前作为最小本地项目索引文件使用 JSONL:初始化写入 `project.init`,每次 `game.generate_draft` 追加目标、标题、本地产物路径、checkpoint 和 diff 摘要,上传 / 登记 / 画板导入资产时追加 `asset.register` 或 `asset.update`;v1 不引入 SQLite 依赖。
|
||||
- `game.generate_draft`、资产导入、记忆写入、预览状态写入、checkpoint / restore 和 policy 写入会先按 `.agent/policy.json` 判断本次命令是否被项目策略拒绝,再拿项目级 `.agent/project.lock` 串行化;锁只保护同一本地项目,v1 不做后台锁管理。
|
||||
- `ArtifactWriter` 写入最终产物前把当前项目文件保存到 `.agent/checkpoints/<checkpointId>/`,写入后把新增、修改、删除计数记录到 `.agent/agent.db`;聊天命令 `/checkpoint`、`/diff checkpoint-id` 和 `/restore checkpoint-id` 允许用户手动保存、对比和确认恢复 checkpoint。
|
||||
- Planner、组内角色和 Generator 读取上下文前会先做安全过滤:拒绝 `.env*`、`game-creator.config*`、Authorization / Cookie / API Key / Token / Bearer 等密钥样式内容,并清理 `sk-*` / `tnr_sk_*` token;被过滤内容不进入 LLM prompt。
|
||||
- `.agent/run.latest.json` 的 schema 固定为共享契约 `GAME_CREATION_AGENT_RUN_SCHEMA_VERSION = game-creator-agent-run.v1`;TS 与 Rust 都从共享契约读取 run trace 类型,避免开发窗口和 Tauri 写入结构漂移。
|
||||
- `.agent/run.latest.json` 增加可选 `lifecycleStatus`,把一次生成 run 映射到本地最小生命周期:`scheduled / running / waiting / pending / done / failed / killed`。聊天命令 `/agent-status` 读取最近 run,`/agent-kill` 标记为 `killed`,`/agent-retry` 与 `/agent-resume [说明]` 标记为 `pending`,并写入 `.agent/activity.jsonl`、`.agent/output.jsonl` 和 `.agent/context.bundle.json`。v1 只做本地状态控制,不承诺真正中断已在上游执行中的 LLM 请求;后续引入独立 runner 后再把 `pending` 接入 claim。
|
||||
- `game.generate_draft` 写入最终产物后会复用白名单受限命令 `game.static_smoke` 做一次生成后自检,至少检查 `game/index.html` 包含 canvas、canvas 渲染上下文、绘制调用、主循环、非空输入监听、明确目标、失败或胜利状态和重开路径,且不使用远程资源、`eval`、`new Function`、`localStorage`、`fetch`、`WebSocket` 或 `ServiceWorker`,也不得包含固定星核传送门模板词、纯按钮计分模板或 `TODO` / `待实现` / `这里省略` 等未完成实现;画板资源占位引用允许出现在 asset id 或说明中,并把该工具调用写入 `.agent/run.latest.json` 与 `.agent/logs/command.log`;自检失败则本次命令失败,不继续启动预览。
|
||||
- `ArtifactWriter` step 使用 `file.write.local_artifacts` 工具调用记录最终写入的 `memory/`、`game/`、`assets/`、`exports/` 和 `.agent/manifest.json` 路径;写入完成后 `nextStep` 指向 `game.static_smoke`。
|
||||
- `ArtifactWriter` step 使用 `file.write.local_artifacts` 工具调用记录最终写入的 `memory/`、`memory/agents/`、`game/`、`assets/`、`exports/` 和 `.agent/manifest.json` 路径;写入完成后 `nextStep` 指向 `game.static_smoke`。
|
||||
- `preview.start` / `preview.stop` 会追加 `.agent/logs/preview.log`,并在 `.agent/run.latest.json` 已存在时追加 `Preview` step 和 `preview.*` toolCall,记录本地 HTTP 预览 URL 与停止事件;单全局本地预览被新项目替换时,会 best-effort 把旧项目 manifest、preview log 和 trace 记录为 stopped,避免旧项目残留 running;本地 HTTP server 的 `/` 映射到 `game/index.html`,只允许读取 canonical 后仍位于项目真实 `game/` 或真实 `assets/` 下的文件,拒绝 `memory/`、`.agent/`、`exports/`、`..`、一级 `game` / `assets` 符号链接目录和内部符号链接越界,并为常见图片、音频、视频和 Web 资源返回对应 MIME;静态 `HEAD` 返回真实 `Content-Length` 但不返回 body,确保浏览器和媒体资源探测可用;上传和画板回流资产可被生成游戏引用但不会暴露记忆或 trace;没有 run trace 的手动预览启动不阻断。
|
||||
- 聊天输入会生成待确认的 `game.generate_draft` 内置命令;用户确认后,正式用户聊天会实时展示 Planner LLM、Orchestrator、6 组角色 brief、Generator LLM、Evaluator 质量评审、ArtifactWriter 和 `game.static_smoke` 的进度,再把 LLM 返回的结构化草案写入短期记忆 `memory/session.md`、长期记忆 `memory/project.md`、设计草案 `game/game_design.md`、数值配置 `game/balance.json`、美术清单 `assets/manifest.art.json`、音乐音效清单 `assets/manifest.audio.json`、发布包装草案 `exports/README.md` 和可运行 `game/index.html`。生成完成后,普通聊天消息会自动展示最近一次 Agent loop 的 Run、LLM 对话、轮次、工具调用、active / carry-over 任务、返工焦点、编排轮次、最近步骤、画板同步建议命令和本地产物快照;完整证据仍由 `/trace` 读取同一份 `.agent/run.latest.json`。
|
||||
- 聊天输入会生成待确认的 `game.generate_draft` 内置命令;用户确认后,正式用户聊天会实时展示 Planner LLM、Orchestrator、6 组角色 brief、Generator LLM、Evaluator 质量评审、ArtifactWriter 和 `game.static_smoke` 的进度,再把 LLM 返回的结构化草案写入短期记忆 `memory/session.md`、长期记忆 `memory/project.md`、项目黑板 `memory/blackboard.md`、角色私有记忆 `memory/agents/<group>/<role>.md`、设计草案 `game/game_design.md`、数值配置 `game/balance.json`、美术清单 `assets/manifest.art.json`、音乐音效清单 `assets/manifest.audio.json`、发布包装草案 `exports/README.md` 和可运行 `game/index.html`。生成完成后,普通聊天消息会自动展示最近一次 Agent loop 的 Run、LLM 对话、轮次、工具调用、active / carry-over 任务、返工焦点、编排轮次、最近步骤、画板同步建议命令和本地产物快照;完整证据仍由 `/trace` 读取同一份 `.agent/run.latest.json`。
|
||||
- `game.generate_draft` 的 `game/index.html` 必须是可试玩原型,至少包含输入、主循环、目标、失败或胜利状态和重开路径;不能只输出按钮计分或纯展示页。
|
||||
- `game.generate_draft` 会校验 LLM 输出:`balance`、美术清单和音乐清单必须是 JSON object,`gameHtml` 必须是自包含 HTML、包含 `canvas` 与 `requestAnimationFrame`,不得加载远程脚本或资源,不得使用 `eval` / `new Function` / `localStorage` / `fetch` / `WebSocket` / `ServiceWorker`,不得把包含 `<` / `>` 的用户输入原样写入 HTML。
|
||||
- 同一项目内多次 `game.generate_draft` 不覆盖记忆文件,而是继续追加短期对话记录和长期创作目标记录,保留用户迭代历史。
|
||||
- 同一项目内多次 `game.generate_draft` 不覆盖记忆文件,而是继续追加短期对话记录、长期创作目标记录、项目黑板摘要和角色私有摘要,保留用户迭代历史。
|
||||
- `game.generate_draft` 会把本轮协作写入 `.agent/logs/agent.log` 和 manifest `commandRuns`,并将已完成的组内角色任务标记为完成,`preview-readiness` 在生成后等待本地 `game.static_smoke`,预览试玩任务等待用户确认。
|
||||
- `game.generate_draft` 会把组内角色 brief、专业组汇总 brief、专业组 / 角色 / 产物交接摘要写入 `.agent/passes/pass-N/groups/`、`game/game_design.md`、`exports/README.md` 和 `.agent/logs/agent.log`,用于追踪 6 组 agent 的最小协作结果。
|
||||
- 文件选择会生成待确认的 `asset.upload` 内置命令;用户确认后才把文件写入 `assets/uploads/` 并登记到 manifest。
|
||||
|
||||
@@ -255,6 +255,7 @@ describe('AI 游戏创作 App 共享契约', () => {
|
||||
'audio-asset-plan',
|
||||
'code-director',
|
||||
'code-prototype',
|
||||
'quality-review',
|
||||
'preview-readiness',
|
||||
'preview-playtest',
|
||||
'publish-strategy',
|
||||
|
||||
@@ -16,6 +16,12 @@ export const GAME_CREATION_APP_COMMANDS = [
|
||||
{ id: 'help.show', permission: 'auto' },
|
||||
{ id: 'project.create', permission: 'confirm' },
|
||||
{ id: 'project.status', permission: 'auto' },
|
||||
{ id: 'project.index', permission: 'auto' },
|
||||
{ id: 'project.checkpoint', permission: 'confirm' },
|
||||
{ id: 'project.diff', permission: 'auto' },
|
||||
{ id: 'project.restore', permission: 'confirm' },
|
||||
{ id: 'project.policy_read', permission: 'auto' },
|
||||
{ id: 'project.policy_write', permission: 'confirm' },
|
||||
{ id: 'task.list', permission: 'auto' },
|
||||
{ id: 'agent.trace_read', permission: 'auto' },
|
||||
{ id: 'agent.run_status', permission: 'auto' },
|
||||
@@ -101,6 +107,8 @@ export const GAME_CREATION_AGENT_CAPABILITIES = [
|
||||
{ id: 'short-term-memory', area: 'agent-runtime', title: '短期记忆' },
|
||||
{ id: 'long-term-memory', area: 'agent-runtime', title: '长期记忆' },
|
||||
{ id: 'local-artifacts', area: 'local-runtime', title: '本地产物保存' },
|
||||
{ id: 'project-checkpoints', area: 'local-runtime', title: '项目快照与恢复' },
|
||||
{ id: 'project-index', area: 'local-runtime', title: '本地项目索引' },
|
||||
{ id: 'local-preview', area: 'local-runtime', title: '本地 HTTP 预览' },
|
||||
{
|
||||
id: 'canvas-project-sync',
|
||||
@@ -109,6 +117,7 @@ export const GAME_CREATION_AGENT_CAPABILITIES = [
|
||||
},
|
||||
{ id: 'developer-window', area: 'dev-runtime', title: '开发窗口' },
|
||||
{ id: 'guardrails', area: 'dev-runtime', title: '权限 Gate' },
|
||||
{ id: 'project-policy', area: 'dev-runtime', title: '项目级权限策略' },
|
||||
{ id: 'trace-log', area: 'dev-runtime', title: '执行日志' },
|
||||
] as const satisfies readonly GameCreationAgentCapabilityDescriptor[];
|
||||
|
||||
@@ -271,7 +280,9 @@ export const GAME_CREATION_APP_SEED_TASKS = [
|
||||
status: 'pending',
|
||||
dependencies: ['code-prototype'],
|
||||
artifacts: ['.agent/findings.md', '.agent/run.latest.json'],
|
||||
acceptanceCriteria: ['玩法、资产、数值、程序和发布包装通过跨专业组质量评审'],
|
||||
acceptanceCriteria: [
|
||||
'玩法、资产、数值、程序和发布包装通过跨专业组质量评审',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'preview-readiness',
|
||||
|
||||
@@ -21,10 +21,16 @@ pub struct GameCreationAppCommandDescriptor {
|
||||
pub permission: GameCreationAppPermission,
|
||||
}
|
||||
|
||||
pub const GAME_CREATION_APP_COMMANDS: [GameCreationAppCommandDescriptor; 33] = [
|
||||
pub const GAME_CREATION_APP_COMMANDS: [GameCreationAppCommandDescriptor; 39] = [
|
||||
command("help.show", GameCreationAppPermission::Auto),
|
||||
command("project.create", GameCreationAppPermission::Confirm),
|
||||
command("project.status", GameCreationAppPermission::Auto),
|
||||
command("project.index", GameCreationAppPermission::Auto),
|
||||
command("project.checkpoint", GameCreationAppPermission::Confirm),
|
||||
command("project.diff", GameCreationAppPermission::Auto),
|
||||
command("project.restore", GameCreationAppPermission::Confirm),
|
||||
command("project.policy_read", GameCreationAppPermission::Auto),
|
||||
command("project.policy_write", GameCreationAppPermission::Confirm),
|
||||
command("task.list", GameCreationAppPermission::Auto),
|
||||
command("agent.trace_read", GameCreationAppPermission::Auto),
|
||||
command("agent.run_status", GameCreationAppPermission::Auto),
|
||||
@@ -72,7 +78,7 @@ pub struct GameCreationAgentCapabilityDescriptor {
|
||||
pub title: &'static str,
|
||||
}
|
||||
|
||||
pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescriptor; 23] = [
|
||||
pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescriptor; 26] = [
|
||||
capability("chat", "user", "聊天入口"),
|
||||
capability("file-upload", "user", "上传文件"),
|
||||
capability("built-in-commands", "agent-runtime", "内置命令调用"),
|
||||
@@ -103,10 +109,13 @@ pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescript
|
||||
capability("short-term-memory", "agent-runtime", "短期记忆"),
|
||||
capability("long-term-memory", "agent-runtime", "长期记忆"),
|
||||
capability("local-artifacts", "local-runtime", "本地产物保存"),
|
||||
capability("project-checkpoints", "local-runtime", "项目快照与恢复"),
|
||||
capability("project-index", "local-runtime", "本地项目索引"),
|
||||
capability("local-preview", "local-runtime", "本地 HTTP 预览"),
|
||||
capability("canvas-project-sync", "local-runtime", "画板项目资源同步"),
|
||||
capability("developer-window", "dev-runtime", "开发窗口"),
|
||||
capability("guardrails", "dev-runtime", "权限 Gate"),
|
||||
capability("project-policy", "dev-runtime", "项目级权限策略"),
|
||||
capability("trace-log", "dev-runtime", "执行日志"),
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user