接入正式项目总控对话入口

登录后项目开发页接入 project-supervisor active Session 与 same-run steer
为全新项目自动创建 Supervisor Session 并隔离 legacy 项目对话写入
展示总控运行状态、确认问答与专业 Agent 只读协作状态
补充桌面窄屏样式、界面回归测试和 V1.28 文档
This commit is contained in:
AIGameCreator App
2026-07-16 16:29:45 +08:00
parent 84328de5ad
commit 13629c60cb
6 changed files with 1057 additions and 371 deletions
File diff suppressed because it is too large Load Diff
+167
View File
@@ -885,6 +885,14 @@ textarea {
font-size: 13px;
}
.launcher-project-runtime-summary {
display: block;
margin-top: 5px;
color: #687386;
font-size: 11px;
overflow-wrap: anywhere;
}
.launcher-page-actions {
display: flex;
flex-wrap: wrap;
@@ -1838,6 +1846,138 @@ textarea {
font-size: 12px;
}
.project-supervisor-surface {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(220px, 280px);
gap: 18px;
min-width: 0;
min-height: 560px;
}
.project-supervisor-conversation {
display: grid;
grid-template-rows: minmax(340px, 1fr) auto auto auto;
align-content: stretch;
gap: 10px;
min-width: 0;
}
.project-supervisor-message-list {
min-height: 340px;
max-height: calc(100vh - 330px);
border-radius: 8px;
background: #fff;
}
.project-supervisor-surface .agent-runtime-status {
margin: 0;
}
.project-supervisor-surface .agent-runtime-status header,
.project-supervisor-collaboration header {
align-items: center;
}
.project-supervisor-composer {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: end;
gap: 10px;
}
.project-supervisor-composer textarea {
min-width: 0;
min-height: 72px;
max-height: 180px;
padding: 10px 12px;
resize: vertical;
border: 1px solid #cfd7e6;
border-radius: 8px;
background: #fff;
color: #111827;
font: inherit;
line-height: 1.5;
}
.project-supervisor-composer button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-width: 84px;
height: 40px;
padding: 0 16px;
border: 1px solid #1f6feb;
border-radius: 8px;
background: #1f6feb;
color: #fff;
}
.project-supervisor-composer button:disabled {
border-color: #cfd6e1;
background: #e8ecf2;
color: #8b95a5;
}
.project-supervisor-workspace-status {
color: #7b8492;
font-size: 11px;
overflow-wrap: anywhere;
}
.project-supervisor-collaboration {
min-width: 0;
padding-left: 18px;
border-left: 1px solid #e5e7eb;
}
.project-supervisor-collaboration header {
display: flex;
justify-content: space-between;
gap: 10px;
margin-bottom: 8px;
}
.project-supervisor-collaboration h2,
.project-supervisor-collaboration p {
margin: 0;
}
.project-supervisor-collaboration h2 {
color: #111827;
font-size: 14px;
}
.project-supervisor-collaboration header small,
.project-supervisor-collaboration p {
color: #8b8b8b;
font-size: 11px;
}
.project-supervisor-collaboration > div {
display: grid;
}
.project-supervisor-collaboration article {
display: grid;
gap: 5px;
min-width: 0;
padding: 10px 0;
border-top: 1px solid #eef0f3;
}
.project-supervisor-collaboration article strong {
color: #111827;
font-size: 12px;
}
.project-supervisor-collaboration article span,
.project-supervisor-collaboration article small {
color: #647084;
font-size: 11px;
overflow-wrap: anywhere;
}
.launcher-card-collage {
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -2084,6 +2224,33 @@ textarea {
.launcher-development-assets div {
grid-template-columns: 1fr;
}
.project-supervisor-surface {
grid-template-columns: 1fr;
min-height: auto;
}
.project-supervisor-conversation {
grid-template-rows: minmax(320px, auto) auto auto auto;
}
.project-supervisor-message-list {
max-height: 52vh;
}
.project-supervisor-composer {
grid-template-columns: 1fr;
}
.project-supervisor-composer button {
width: 100%;
}
.project-supervisor-collaboration {
padding: 14px 0 0;
border-top: 1px solid #e5e7eb;
border-left: 0;
}
}
.launcher-dialog-backdrop {
@@ -49,14 +49,18 @@ function renderAppAt(path: string) {
function renderLauncherAt(
path: string,
initialView: 'home' | 'agent-chat' = 'home',
strictMode = false,
) {
window.history.pushState({}, '', path);
const launcher = React.createElement(WorkspaceLauncher, {
currentUser: testAuthUser,
initialView,
onLogout: vi.fn(),
});
render(
React.createElement(WorkspaceLauncher, {
currentUser: testAuthUser,
initialView,
onLogout: vi.fn(),
}),
strictMode
? React.createElement(React.StrictMode, null, launcher)
: launcher,
);
}
@@ -193,6 +197,7 @@ function agentRuntimeUserInputRequest({
function createProjectSupervisorRuntimeHarness({
projectPath = '/tmp/authorized-game',
sessionId = 'supervisor-session-active',
initialSessionExists = true,
projectMessages = [],
supervisorMessages = [],
initialRuntime,
@@ -201,6 +206,7 @@ function createProjectSupervisorRuntimeHarness({
}: {
projectPath?: string;
sessionId?: string;
initialSessionExists?: boolean;
projectMessages?: Array<Record<string, unknown>>;
supervisorMessages?: Array<Record<string, unknown>>;
initialRuntime?: Record<string, unknown>;
@@ -213,6 +219,7 @@ function createProjectSupervisorRuntimeHarness({
);
let messageSequence = 0;
let steerSequence = 0;
let sessionExists = initialSessionExists;
let currentProjectMessages = [...projectMessages];
let currentSupervisorMessages = [...supervisorMessages];
const runtimeState = (
@@ -317,6 +324,27 @@ function createProjectSupervisorRuntimeHarness({
return [runtimeResult()];
}
if (command === 'list_game_creator_agent_sessions') {
return {
path: `${projectPath}/.agent/conversations/agents/project-supervisor/sessions.json`,
agentId: 'project-supervisor',
activeSessionId: sessionExists ? sessionId : null,
sessions: sessionExists
? [
{
sessionId,
title: '项目总控',
createdAt: 1000,
updatedAt: 2000,
archivedAt: null,
messageCount: currentSupervisorMessages.length,
legacy: false,
},
]
: [],
};
}
if (command === 'create_game_creator_agent_session') {
sessionExists = true;
return {
path: `${projectPath}/.agent/conversations/agents/project-supervisor/sessions.json`,
agentId: 'project-supervisor',
@@ -324,7 +352,7 @@ function createProjectSupervisorRuntimeHarness({
sessions: [
{
sessionId,
title: '项目总控',
title: String(args?.title ?? ''),
createdAt: 1000,
updatedAt: 2000,
archivedAt: null,
@@ -1404,7 +1432,7 @@ describe('AI 游戏创作 App 界面边界', () => {
fireEvent.click(screen.getByRole('button', { name: '打开' }));
await waitFor(() => {
expect(screen.getByLabelText('项目开发画布')).not.toBeNull();
expect(screen.getByLabelText('项目总控对话')).not.toBeNull();
});
expect(screen.getByText('authorized-game')).not.toBeNull();
expect(screen.getByText('/tmp/authorized-game')).not.toBeNull();
@@ -5742,7 +5770,7 @@ describe('AI 游戏创作 App 界面边界', () => {
});
});
it('refreshes recent project status before entering the project placeholder', async () => {
it('refreshes recent project status before entering the Supervisor surface', async () => {
let inspectCount = 0;
const invoke = vi.fn(
async (command: string, args?: Record<string, unknown>) => {
@@ -5772,10 +5800,9 @@ describe('AI 游戏创作 App 界面边界', () => {
expect(await screen.findByText('authorized-game')).not.toBeNull();
fireEvent.click(screen.getByText('authorized-game'));
expect(await screen.findByLabelText('项目开发画布')).not.toBeNull();
expect(await screen.findByLabelText('项目总控对话')).not.toBeNull();
expect(screen.getByText('authorized-game-fresh')).not.toBeNull();
expect(screen.getByText('passed')).not.toBeNull();
expect(screen.getByText('evaluator-passed')).not.toBeNull();
expect(screen.getByText('run: passed · evaluator-passed')).not.toBeNull();
expect(invoke).not.toHaveBeenCalledWith(
'open_game_creator_workspace_window',
expect.anything(),
@@ -5898,11 +5925,15 @@ describe('AI 游戏创作 App 界面边界', () => {
expect(screen.getByText('已取消')).not.toBeNull();
});
it('creates a project from the home agent input without starting generation', async () => {
it('creates a project from home and sends the first requirement once to the active Project Supervisor Session', async () => {
const manifest = createGameCreationAppManifest(
'local-project-draft',
'home-created-game',
);
const supervisorHarness = createProjectSupervisorRuntimeHarness({
projectPath: '/tmp/home-created-game',
initialSessionExists: false,
});
const invoke = vi.fn(
async (command: string, args?: Record<string, unknown>) => {
if (command === 'pick_local_project_directory') {
@@ -5926,23 +5957,14 @@ describe('AI 游戏创作 App 界面边界', () => {
manifestPath: '/tmp/home-created-game/.agent/manifest.json',
};
}
if (command === 'chat_with_game_creator_role_agent') {
return {
replyText: mockRoleAgentReply(),
};
}
if (command === 'append_local_conversation_message') {
return {
path: '.agent/conversations/project.jsonl',
agentId: null,
messages: [],
};
}
throw new Error(`unexpected invoke ${command}`);
return supervisorHarness.invoke(command, args);
},
);
window.__TAURI__ = { core: { invoke } };
renderLauncherAt('/?launcher');
window.__TAURI__ = {
core: { invoke },
event: { listen: supervisorHarness.listen },
};
renderLauncherAt('/?launcher', 'home', true);
fireEvent.click(screen.getByRole('button', { name: '做素材' }));
fireEvent.change(screen.getByLabelText('创作想法'), {
@@ -5967,11 +5989,12 @@ describe('AI 游戏创作 App 界面边界', () => {
fireEvent.click(screen.getByRole('button', { name: '开启创作' }));
expect(await screen.findByLabelText('项目开发画布')).not.toBeNull();
expect(await screen.findByLabelText('项目总控对话')).not.toBeNull();
expect(screen.getByText('home-created-game')).not.toBeNull();
expect(screen.getByText('/tmp/home-created-game')).not.toBeNull();
expect(screen.getByText('做素材')).not.toBeNull();
expect(screen.getByText('做一张像素风厨师角色图')).not.toBeNull();
expect(
await screen.findByText(/做一张像素风厨师角色图/),
).not.toBeNull();
expect(screen.getByText('assets/uploads/reference.png')).not.toBeNull();
expect(invoke).toHaveBeenCalledWith('init_local_game_project', {
projectPath: '/tmp/home-created-game',
@@ -5984,38 +6007,46 @@ describe('AI 游戏创作 App 界面边界', () => {
mediaType: 'image/png',
bytes: fileBytes,
});
const conversationCalls = invoke.mock.calls.filter(
([command]) => command === 'append_local_conversation_message',
expect(invoke).toHaveBeenCalledWith('list_game_creator_agent_sessions', {
projectPath: '/tmp/home-created-game',
agentId: 'project-supervisor',
});
expect(invoke).toHaveBeenCalledWith('create_game_creator_agent_session', {
projectPath: '/tmp/home-created-game',
agentId: 'project-supervisor',
title: '项目总控',
});
const startCalls = invoke.mock.calls.filter(
([command]) => command === 'start_game_creator_agent_runtime_task',
);
expect(conversationCalls).toHaveLength(2);
expect(conversationCalls[0]?.[1]).toMatchObject({
expect(startCalls).toHaveLength(1);
expect(startCalls[0]?.[1]).toMatchObject({
projectPath: '/tmp/home-created-game',
agentId: null,
message: {
role: 'user',
agentId: null,
content: expect.stringContaining('初始意图:art / 做素材'),
},
agentId: 'project-supervisor',
sessionId: supervisorHarness.sessionId,
task: expect.stringContaining('初始意图:art / 做素材'),
runId: expect.stringMatching(/^project-supervisor-task-/),
});
expect(conversationCalls[0]?.[1]).toMatchObject({
message: {
content: expect.stringContaining('做一张像素风厨师角色图'),
},
expect(startCalls[0]?.[1]).toMatchObject({
task: expect.stringContaining('做一张像素风厨师角色图'),
});
expect(conversationCalls[0]?.[1]).toMatchObject({
message: {
content: expect.stringContaining('附件:reference.png'),
},
expect(startCalls[0]?.[1]).toMatchObject({
task: expect.stringContaining('附件:reference.png'),
});
expect(conversationCalls[1]?.[1]).toMatchObject({
projectPath: '/tmp/home-created-game',
agentId: null,
message: {
role: 'assistant',
agentId: null,
content: expect.stringContaining('assets/uploads/reference.png'),
},
await waitFor(() => {
expect(
invoke.mock.calls.filter(
([command]) => command === 'resume_game_creator_agent_runtime_tasks',
),
).toHaveLength(1);
});
expect(
invoke.mock.calls.filter(
([command, args]) =>
command === 'append_local_conversation_message' &&
(args as Record<string, unknown>)?.agentId === null,
),
).toHaveLength(0);
expect(invoke).not.toHaveBeenCalledWith(
'generate_local_game_draft',
expect.anything(),
@@ -6034,6 +6065,209 @@ describe('AI 游戏创作 App 界面边界', () => {
);
});
it('opens an existing project into the active Supervisor Session, restores history, then starts and steers the same run', async () => {
const projectPath = '/tmp/launcher-supervisor-game';
const manifest = createGameCreationAppManifest(
'local-project-draft',
'launcher-supervisor-game',
);
const supervisorHarness = createProjectSupervisorRuntimeHarness({
projectPath,
supervisorMessages: [
{
schemaVersion: 'game-creator-conversation.v1',
role: 'assistant',
content: '已恢复的项目总控历史',
agentId: 'project-supervisor',
messageId: 'launcher-supervisor-history',
updatedAt: 2000,
},
],
});
const invoke = vi.fn(
async (command: string, args?: Record<string, unknown>) => {
if (command === 'inspect_local_project_directory') {
return {
projectPath,
exists: true,
isDirectory: true,
isGameCreatorProject: true,
projectName: 'launcher-supervisor-game',
recentRunStatus: null,
recentRunStopReason: null,
};
}
if (command === 'get_local_game_manifest') {
return manifest;
}
return supervisorHarness.invoke(command, args);
},
);
window.__TAURI__ = {
core: { invoke },
event: { listen: supervisorHarness.listen },
};
renderLauncherProjectsAt('/?launcher');
fireEvent.change(screen.getByLabelText('项目目录'), {
target: { value: projectPath },
});
fireEvent.click(screen.getByRole('button', { name: '打开' }));
const supervisorSurface = await screen.findByLabelText('项目总控对话');
expect(
await within(supervisorSurface).findByText('已恢复的项目总控历史'),
).not.toBeNull();
expect(invoke).toHaveBeenCalledWith('list_game_creator_agent_sessions', {
projectPath,
agentId: 'project-supervisor',
});
expect(invoke).toHaveBeenCalledWith('read_local_conversation', {
projectPath,
agentId: 'project-supervisor',
sessionId: supervisorHarness.sessionId,
});
expect(screen.getByLabelText('专业 Agent 协作状态')).not.toBeNull();
expect(screen.queryByLabelText('选择 Agent')).toBeNull();
expect(screen.queryByRole('dialog', { name: 'Agent 对话' })).toBeNull();
expect(supervisorHarness.listen).not.toHaveBeenCalledWith(
'game-creator-agent-progress',
expect.any(Function),
);
const policyReadCountBeforeChat = invoke.mock.calls.filter(
([command]) => command === 'read_project_permission_policy',
).length;
fireEvent.change(screen.getByLabelText('项目需求'), {
target: { value: '先完成正式客户端玩法拆解' },
});
fireEvent.click(within(supervisorSurface).getByRole('button', { name: '发送' }));
await waitFor(() => {
expect(invoke).toHaveBeenCalledWith(
'start_game_creator_agent_runtime_task',
{
projectPath,
agentId: 'project-supervisor',
sessionId: supervisorHarness.sessionId,
task: '先完成正式客户端玩法拆解',
runId: expect.stringMatching(/^project-supervisor-task-/),
},
);
});
const startCall = invoke.mock.calls.find(
([command]) => command === 'start_game_creator_agent_runtime_task',
);
const runId = String(startCall?.[1]?.runId ?? '');
await waitFor(() => {
expect(
(within(supervisorSurface).getByRole('button', {
name: '发送',
}) as HTMLButtonElement).disabled,
).toBe(false);
});
fireEvent.change(screen.getByLabelText('项目需求'), {
target: { value: '补充:优先复用现有素材' },
});
fireEvent.click(within(supervisorSurface).getByRole('button', { name: '发送' }));
await waitFor(() => {
expect(invoke).toHaveBeenCalledWith(
'steer_game_creator_agent_runtime_task',
{
projectPath,
agentId: 'project-supervisor',
sessionId: supervisorHarness.sessionId,
runId,
steerId: expect.stringMatching(/^project-supervisor-steer-/),
instruction: '补充:优先复用现有素材',
},
);
});
expect(
invoke.mock.calls.filter(
([command]) => command === 'start_game_creator_agent_runtime_task',
),
).toHaveLength(1);
expect(
invoke.mock.calls.filter(
([command]) => command === 'read_project_permission_policy',
),
).toHaveLength(policyReadCountBeforeChat);
});
it('does not render a ready Supervisor stream twice when its assistant is already in restored history', async () => {
const projectPath = '/tmp/launcher-supervisor-finalizing';
const reply = '已经落盘的唯一项目总控回复';
const manifest = createGameCreationAppManifest(
'local-project-draft',
'launcher-supervisor-finalizing',
);
const runId = 'supervisor-finalizing-run';
const supervisorHarness = createProjectSupervisorRuntimeHarness({
projectPath,
supervisorMessages: [
{
schemaVersion: 'game-creator-conversation.v1',
role: 'assistant',
content: reply,
agentId: 'project-supervisor',
messageId: 'launcher-supervisor-final-assistant',
updatedAt: 7000,
},
],
initialRuntime: {
runId,
status: 'running',
phase: 'finalizing',
loopIteration: 1,
appliedSteerCursor: 0,
updatedAt: 7000,
},
initialResponseStream: projectSupervisorResponseStream({
runId,
sequence: 8,
accumulatedText: reply,
status: 'ready',
}),
});
const invoke = vi.fn(
async (command: string, args?: Record<string, unknown>) => {
if (command === 'inspect_local_project_directory') {
return {
projectPath,
exists: true,
isDirectory: true,
isGameCreatorProject: true,
projectName: 'launcher-supervisor-finalizing',
recentRunStatus: null,
recentRunStopReason: null,
};
}
if (command === 'get_local_game_manifest') {
return manifest;
}
return supervisorHarness.invoke(command, args);
},
);
window.__TAURI__ = {
core: { invoke },
event: { listen: supervisorHarness.listen },
};
renderLauncherProjectsAt('/?launcher');
fireEvent.change(screen.getByLabelText('项目目录'), {
target: { value: projectPath },
});
fireEvent.click(screen.getByRole('button', { name: '打开' }));
const supervisorSurface = await screen.findByLabelText('项目总控对话');
expect(await within(supervisorSurface).findByText(reply)).not.toBeNull();
expect(within(supervisorSurface).getAllByText(reply)).toHaveLength(1);
expect(
within(supervisorSurface).queryByLabelText('项目总控 Agent 实时回复'),
).toBeNull();
});
it('rejects launcher project paths with control characters before Tauri calls', () => {
const invoke = vi.fn(async () => undefined);
window.__TAURI__ = { core: { invoke } };
@@ -6731,10 +6965,9 @@ describe('AI 游戏创作 App 界面边界', () => {
fireEvent.click(screen.getByText('厨房突围').closest('button')!);
await waitFor(() => {
expect(screen.getByLabelText('项目开发画布')).not.toBeNull();
expect(screen.getByLabelText('项目总控对话')).not.toBeNull();
});
expect(screen.getByText('done')).not.toBeNull();
expect(screen.getByText('preview-running')).not.toBeNull();
expect(screen.getByText('run: done · preview-running')).not.toBeNull();
expect(invoke).not.toHaveBeenCalledWith(
'open_game_creator_workspace_window',
expect.anything(),
@@ -7058,7 +7291,7 @@ describe('AI 游戏创作 App 界面边界', () => {
projectId: 'local-project-draft',
name: 'non-empty-game',
});
expect(screen.getByLabelText('项目开发画布')).not.toBeNull();
expect(screen.getByLabelText('项目总控对话')).not.toBeNull();
});
expect(invoke).not.toHaveBeenCalledWith(
'open_game_creator_workspace_window',
@@ -23260,13 +23493,6 @@ describe('AI 游戏创作 App 界面边界', () => {
expect(
await screen.findByText('已设置本地项目:/tmp/authorized-game'),
).not.toBeNull();
await waitFor(() => {
expect(
invoke.mock.calls.filter(
([command]) => command === 'read_project_permission_policy',
).length,
).toBeGreaterThanOrEqual(2);
});
invoke.mockClear();
submitChat('/policy-confirm file.write');
@@ -23329,13 +23555,6 @@ describe('AI 游戏创作 App 界面边界', () => {
expect(
await screen.findByText('已设置本地项目:/tmp/authorized-game'),
).not.toBeNull();
await waitFor(() => {
expect(
invoke.mock.calls.filter(
([command]) => command === 'read_project_permission_policy',
).length,
).toBeGreaterThanOrEqual(2);
});
invoke.mockClear();
submitChat('/policy-deny not.a.command');
@@ -20,13 +20,15 @@
- 背景:V1.16 已建立 Supervisor 的 durable static delivery/claim 和同一父 run 唯一回复,但旧 `agent.delegate` 只描述目标与任务,Runtime 只能确认子任务终态,不能持久证明预期产物、验证证据或返工关系;实施计划中也仍有普通用户进入单 Agent 对话的旧表述。
- 决策:`project-supervisor` 固定为正式用户唯一默认对话与最终回复 Agent。专业 Agent 和 isolated child 只向父 run 提交内部回执、摘要与证据;开发窗口单 Agent 直调和 `agc:swarm` 调试不获得正式用户回复所有权。
- 正式 GUI:登录后的单窗口客户端从首页创建项目或从项目组打开已有项目后,项目开发页只挂载 Supervisor 用户面。首条需求直接投递 active `project-supervisor` Session;全新项目还没有该 Session 时先通过既有 Session 命令创建并设为 active;已有非终态 run 的后续输入使用 same-run steer。普通用户只看到 Supervisor 对话、紧凑 Runtime 状态、确认/Needs input 和专业 Agent 协作只读状态;专业 Agent picker、Session 管理、完整计划和工具台继续只属于开发入口。
- 对话与配置边界:legacy `.agent/conversations/project.jsonl` 只作有界兼容读取,正式 Runtime user/流式草稿/final assistant 不再由 React 双写到 legacy 项目对话,规范消息只归属 Supervisor Session。正式项目页缺少 LLM/AppData 配置时只显示 Runtime 错误,由单窗口壳全局“配置”入口处理,不自动弹开发配置框。
- 合同:新 native `agent.delegate` 的 strict schema 固定携带 `agentId / task / acceptanceCriteria / expectedArtifacts / repairOfDelegationId / runId`,六个字段均必填,后两者可为 `null``acceptanceCriteria` 为 1-8 项;`expectedArtifacts` 为 0-16 个精确项目内非私有相对文件,不接受 glob。旧持久 action 缺字段按空合同恢复,不迁移已有 pending/delivery/claim sidecar。
- 交付与门禁:durable delivery、ready receipt 和 claim 快照原样保存合同及 `structuredResult`;结构化结果包含 `contractStatus=evidence-ready|needs-repair`、artifact path/SHA-256、`missingExpectedArtifacts``verificationRequired``verifiedRevision`、安全 `evidence/error`。Runtime 只在 child completed、预期产物齐全、必要 verification passed 时判 evidence-ready;语义是否满足仍由 Supervisor 按 acceptance criteria、摘要和证据裁决。
- 返工:Supervisor 只有在同一父 run 已认领原 delivery 后,才能为 needs-repair 或语义未通过发出 `repairOfDelegationId=<原 delegationId>` 的新委派。repair 必须完整继承原合同并交回原专业 Agent,深度固定为 1,同一原 delivery 同时最多一个非 suppressed repair;相同 durable action 重放幂等复用,不同重复或并发竞争拒绝。`suppressed` repair 不算完成,同一 action 可在无终态字段时原地恢复;若该 action 已持久失败,新 action 只可在所有既有 repair 均 suppressed 时重做基础设施投递。repair 继续在原父 Session/run 收束,不产生第二条用户回复。
- Prompt 与完成:专业 Agent task prompt 必须携带完整合同并明确只交内部回执;Supervisor prompt 明确不得把 evidence-ready 自动当作语义通过,也不得忽略 needs-repair。无法自行裁决的问题统一通过既有 `user.input_request` 汇总询问用户。所有必要 delivery/claim/repair、结构化计划、verification、确认、用户输入及其它既有 blocker 清零后,才允许原 Supervisor finalization 写唯一 assistant。
- 影响范围:AI 游戏创作 Agent Runtime 的 native tool schema、静态委派 delivery/claim/receipt、恢复与 finalization、Supervisor/专业 Agent prompt、正式用户对话入口、确定性测试和真实 Provider E2E;编码级细节以 Runtime V1.28 章节为准。
- 验证方式:确定性回归覆盖 strict schema、旧 action 空合同恢复且 sidecar 不迁移、合同跨 Runner 重启、artifact/verification 客观门禁、语义验收边界、单层唯一 repair、并发幂等和 final barrier。真实 `gpt-5.5` swarm 必须证明同一 Supervisor run 下两个专业 Agent 真并行、一份弱交付恰好触发一次 repair、唯一 Supervisor assistant、重复 action/receipt/message 为 0、敏感信息泄漏为 0。
- 当前状态:Runtime 当前实现切片及其定向本地回归已完成。`project_supervisor_` 30/30 与 native 合同 parser→executor→delivery 1/1 PASS,覆盖真实 SHA/verification、旧 sidecar 字节不迁移、双线程 repair 唯一活跃投递、suppressed repair 阻断与同 action/新 action 基础设施恢复、父 lane 忙时损坏证据持续重试 reconciliation、规范字段和别名空合同拒绝及唯一 Supervisor assistant/completed;这不代表完整 V1.28 验收清单已通过。正式用户 GUI 路由尚未接入,Runner 强杀恢复仍未验收;修正隔离测试驱动后,真实 `openai_chat / gpt-5.5` 在 Supervisor 首轮 planning 即发生 transport failure 且未创建 delivery,因此 V1.28 整体仍为 NOT PASS。
- 当前状态:Runtime 当前实现切片及其定向本地回归已完成。`project_supervisor_` 30/30 与 native 合同 parser→executor→delivery 1/1 PASS,覆盖真实 SHA/verification、旧 sidecar 字节不迁移、双线程 repair 唯一活跃投递、suppressed repair 阻断与同 action/新 action 基础设施恢复、父 lane 忙时损坏证据持续重试 reconciliation、规范字段和别名空合同拒绝及唯一 Supervisor assistant/completed正式 GUI 接入后的 `appSurface.test.ts` 当前为 279/279 PASS。这不代表完整 V1.28 验收清单已通过Runner 强杀恢复仍未验收;修正隔离测试驱动后,真实 `openai_chat / gpt-5.5` 在 Supervisor 首轮 planning 即发生 transport failure 且未创建 delivery,因此“双专业 Agent 真并行、一次弱交付恰好一次 repair、Runner 强杀恢复、唯一最终回复”的真实门禁仍未通过,V1.28 整体保持 NOT PASS。
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md``docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
## 2026-07-16 AI 游戏创作 Agent Runtime 只并行持久只读批次
@@ -1021,7 +1021,14 @@ V1.27 在 V1.26 一次最多三个原生 action 的基础上,让同一 Agent
V1.28 收紧 V1.16 的静态专业 Agent 协作协议:`project-supervisor` 是正式用户唯一默认对话 Agent,也是唯一可以向正式用户提交最终回复的 Agent;静态专业 Agent 与 isolated child 只向父 run 交付内部回执、摘要和证据。开发窗口仍可直调单个专业 Agent,`agc:swarm` 仍可显式指定其它父 Agent 做调试,但这些入口不构成正式用户对话或第二条用户回复。若本节与 V1.16 或实施计划中的旧表述冲突,以本节为准。
**状态:合同委派、结构化回执、单层 repair 和 Supervisor finalization 门禁已进入 Runtime当前实现切片的定向本地回归已通过,但正式用户 GUI 路由尚未接入,Runner 强杀恢复和真实 `gpt-5.5` 双 Agent swarm 尚未完成验收,因此 V1.28 整体仍为 NOT PASS。**
**状态:合同委派、结构化回执、单层 repair 和 Supervisor finalization 门禁已进入 Runtime正式用户 GUI 已把项目开发页的普通主聊天接入 `project-supervisor` active Session,当前实现切片与前端定向回归已通过。但 Runner 强杀恢复和真实 `gpt-5.5` 双 Agent swarm 加恰好一次 repair 尚未完成验收,因此 V1.28 整体仍为 NOT PASS。**
### 正式 GUI 接入边界
- 登录后的单窗口客户端在首页创建项目或从项目组打开已有项目后,项目开发页只挂载 Supervisor 用户面。首页首条需求直接启动 active `project-supervisor` Session;全新项目尚无该 Session 时先通过既有 Session 命令创建并设为 active,再投递首条需求。已有项目先恢复该 Session、对话和 Runtime,匹配 Session 已存在非终态 run 时,后续普通输入固定走 same-run steer,不切换父 Session/run。
- 正式页只展示 Supervisor 对话、紧凑 Runtime 状态、确认/Needs input 和专业 Agent 协作只读状态。专业 Agent picker、单 Agent 对话、Session 管理、完整计划和工具台继续只属于 `?agent-chat` 开发入口,不进入普通用户项目开发页。
- `.agent/conversations/project.jsonl` 仅作为 legacy 项目历史兼容读取并与 Supervisor Session 历史有界合并。正式 Runtime 的 user 消息和最终 assistant 只写入 Supervisor Session,流式草稿只作可丢失展示缓存;三者都不再由 React 写回 legacy 项目对话,避免同一轮双写。
- 正式页遇到 LLM/AppData 配置缺失时只展示 Runtime 错误,由单窗口壳的全局“配置”入口处理;Supervisor-only 项目页不自动弹出开发配置框。
### Native `agent.delegate` 合同
@@ -1086,7 +1093,7 @@ repair 深度固定为 `1`;同一原 delivery 同时最多存在一个非 `sup
真实 `gpt-5.5` swarm 验收必须在无固定工具顺序和修复配方的任务中,由同一 `project-supervisor` 父 run 并行委派两个专业 Agent;其中一份弱交付必须形成可解释的 `needs-repair` 或被 Supervisor 判为语义不满足,并恰好触发一次 repair。验收期间强杀并重启 Runner,证明合同、delivery/claim/repair 身份和原父 Session/run 不丢失;最终正式用户 conversation 只有一条由 `project-supervisor` 写入的 assistant。全量 task/event/action/delivery/claim/receipt/conversation/Provider lifecycle 交叉检查必须得到重复 action、receipt、message 均为 `0`,且凭据、私有正文、绝对路径、诱饵和 Provider payload 泄漏均为 `0`
2026-07-16 本地确定性回归已完成当前实现切片:`project_supervisor_` 30/30 PASS,另有 native `agent.delegate` 合同从 function call parser 到执行器和 durable delivery 的贯通测试 1/1 PASS。覆盖真实 artifact SHA-256、缺失产物与 verification gate、旧 Ready sidecar 字节不迁移、同原 delivery 双线程 repair 竞争只产生一个活跃 delivery/task/audit、错误目标与 repair-of-repair 拒绝、`suppressed` repair 持续阻断且同 action/新 action 基础设施恢复、父 lane 忙时损坏 verification 持续重试并在释放后进入 reconciliation、规范字段和别名的显式空合同执行器拒绝,以及 repair 前零 assistant、repair 后唯一 Supervisor assistant/completed。Runner 重启扫描会再次发布终态 child 并重新挂接 reconciliation 重试,但本轮尚未完成强杀验收。
2026-07-16 本地确定性回归已完成当前实现切片:`project_supervisor_` 30/30 PASS,另有 native `agent.delegate` 合同从 function call parser 到执行器和 durable delivery 的贯通测试 1/1 PASS。覆盖真实 artifact SHA-256、缺失产物与 verification gate、旧 Ready sidecar 字节不迁移、同原 delivery 双线程 repair 竞争只产生一个活跃 delivery/task/audit、错误目标与 repair-of-repair 拒绝、`suppressed` repair 持续阻断且同 action/新 action 基础设施恢复、父 lane 忙时损坏 verification 持续重试并在释放后进入 reconciliation、规范字段和别名的显式空合同执行器拒绝,以及 repair 前零 assistant、repair 后唯一 Supervisor assistant/completed。正式 GUI 接入后的 `appSurface.test.ts` 当前为 279/279 PASS,覆盖首页首条需求只投递 active Supervisor Session、已有项目恢复、非终态 run same-run steer、专业 Agent 只读状态、开发入口隔离、legacy 项目对话零新增双写和唯一终态 assistant。Runner 重启扫描会再次发布终态 child 并重新挂接 reconciliation 重试,但本轮尚未完成强杀验收。
同日真实 Provider 验收未通过。第一次隔离运行的测试驱动误把多行任务拆成多个 steer,不能作为并行结论;修正为单条输入并移除逐个确认后,正式 `openai_chat / gpt-5.5` 在 Supervisor 首轮 planning 即返回 transport failure,尚未创建 delivery。此前那次较长运行也以同类 transport failure 终止,且只形成一个专业委派。因此当前没有“双专业 Agent 真并行、一次弱交付恰好一次 repair、Runner 强杀恢复、唯一最终回复”的真实证据,不得记录 V1.28 PASS。
@@ -6,7 +6,7 @@
## 技术选择
- 桌面壳:新建 `apps/ai-game-creator-shell`,与现有 `apps/desktop-shell` 分离,避免把游戏创作本地能力塞进主站宿主壳;启动时先检查平台登录态,未登录只展示登录页,登录后进入单窗口客户端首页;正式用户窗口常驻左侧栏和顶部栏,并在首页、项目组、指南 / 反馈和项目开发占位之间切换。发布配置只登记 `client` 用户窗口;debug 构建可由 Tauri setup 额外打开 `developer` 开发窗口,当前开发窗口路由到 `index.html?agent-chat` 承载单 Agent 对话,后续再扩展任务、文件、预览和日志面板。
- 桌面壳:新建 `apps/ai-game-creator-shell`,与现有 `apps/desktop-shell` 分离,避免把游戏创作本地能力塞进主站宿主壳;启动时先检查平台登录态,未登录只展示登录页,登录后进入单窗口客户端首页;正式用户窗口常驻左侧栏和顶部栏,并在首页、项目组、指南 / 反馈和项目开发之间切换。发布配置只登记 `client` 用户窗口;debug 构建可由 Tauri setup 额外打开 `developer` 开发窗口,当前开发窗口路由到 `index.html?agent-chat` 承载单 Agent 对话,后续再扩展任务、文件、预览和日志面板。
- 平台后端:继续使用 `server-rs + Axum + SpacetimeDB`;本地开发启动独立客户端时,`agc` / Tauri dev 会先启动或复用配套 SpacetimeDB 与 `api-server`,再启动固定端口 Vite,并通过 `/api` 代理访问实际后端端口。
- 本地能力:使用 Tauri Rust command;正式用户 App 只启动 `127.0.0.1` 本地 HTTP preview 并交给外部浏览器,不在正式用户窗口内承载游戏预览画面。
- Agent Runtime:扩展 `server-rs/crates/platform-agent`,不引入 LangChain、AutoGen、Microsoft Agent Framework 或 OpenAI Agents SDK sidecar 作为核心。
@@ -153,7 +153,7 @@ Agent Runtime 负责:
- 2026-07-15 V1.17 公共审计收紧:`thinking_summary` event 只保存固定摘要、正文 SHA-256 与字符数,legacy `plan` event 只保存步骤数;结构化计划审计只保存 explanation 的哈希与字符数,以及 step 标题哈希、状态和数量。模型 thinking、legacy plan 标题、repair 错误和调用体只允许出现在对应私有 Runtime 上下文或有界 repair 请求中,不得复制到公共 event、task 或 Agent DB 正文字段。
- 2026-07-10 补充:Agent Runtime state / result 新增 `taskQueue`,从 `.agent/runtime/tasks/<agentId>.jsonl` 中每个 `runId` 的最新记录汇总 `total / pending / running / completed / failed / latestRunId`;开发窗口 Runtime 面板、主窗口 Agent 状态列表、`agent.run_status` observation 和下一轮 planning prompt 都读取该摘要,用于判断同一 Agent 是否仍有排队任务。该字段是运行观测摘要,不新增调度器、SQLite 或独立 worker。
- 2026-07-10 补充:Runtime 新增 `agent.schedule_ready` 调度入口。开发构建可在权限确认后扫描 manifest ready task,把依赖已完成且仍为 `pending` 的任务标成 `running`,并按 taskId 投递到对应 Agent 的既有后台队列;source 固定为 `agent-ready-task-scheduler`,审计记录写 `agent.runtime.ready_task.scheduled`。该入口只把 manifest ready task 接入现有 per-agent 队列、锁、JSONL、LLM loop、工具策略和事件流,不新增独立 worker,也不会在默认确认策略下静默启动。
- 2026-07-10 补充,2026-07-16 由 V1.28 收口目标:主窗口 Agent 状态栏的“调度 Ready”只在开发模式显示。点击后复用项目策略确认弹窗,确认通过才调用 `schedule_game_creator_agent_ready_tasks`,并把返回的 Runtime 合并回 Agent 状态卡;正式用户窗口目标态只展示 `project-supervisor` 主聊天和专业 Agent 协作状态,不提供专业 Agent / child 的单 Agent 对话入口,也不直接暴露 ready-task 调度按钮。当前 `agc:chat` 已使用 Supervisor,正式用户 GUI 路由仍待接入
- 2026-07-10 补充,2026-07-16 由 V1.28 收口:主窗口 Agent 状态栏的“调度 Ready”只在开发模式显示。点击后复用项目策略确认弹窗,确认通过才调用 `schedule_game_creator_agent_ready_tasks`,并把返回的 Runtime 合并回 Agent 状态卡;正式用户项目开发页已只展示 `project-supervisor` 主聊天和专业 Agent 协作只读状态,不提供专业 Agent / child 的单 Agent 对话入口,也不直接暴露 ready-task 调度按钮。专业 Agent picker、完整 Runtime 计划和工具台继续只属于开发入口
- 2026-07-10 补充:`agent.schedule_ready` 同时接入后台 Agent loop 白名单工具。Agent 可在 `task.update``task.create` 后自行请求调度新 ready task;执行仍走 `agent.schedule_ready` 项目权限策略,确认策略命中时当前 run 停在 `waiting-for-confirmation`,不会继续消费队列或启动下游 Agent。确认通过后复用同一 scheduler,把 ready task 投递到对应 Agent 的既有后台队列。
- 2026-07-10 补充:后台任务工具箱已加入 `project.checkpoint`。Agent 可在写入文件、更新任务或批量修改前自行创建本地 checkpointRuntime 复用 `project.checkpoint` 权限策略和项目写锁,observation 只返回 checkpoint id、文件数和总字节数,不返回本机绝对路径。策略要求确认或拒绝时不会创建 checkpoint。
- 2026-07-10 补充:后台任务工具箱已加入 `project.restore`。Agent 可在 diff 或自检发现本轮修改走偏后请求恢复到指定 checkpointRuntime 复用 `project.restore` 权限策略和项目写锁,observation 只返回 checkpoint id、恢复文件数和删除文件数,不返回本机绝对路径。默认确认策略下不会静默回滚用户项目。
@@ -168,14 +168,14 @@ Agent Runtime 负责:
- 2026-07-10 补充:后台任务工具箱已加入 `agent.delegate`。Agent 可在 loop 中把明确任务投递到另一个 Agent 的独立后台队列,复用目标 Agent 原有锁和 pending drain 语义;同一目标 Agent 串行,不同目标 Agent 可并行。该工具受 `agent.delegate` 策略保护,策略要求确认或拒绝时不会写目标对话、不会启动目标后台任务,也不会写 `agent.runtime.agent.delegate` 审计记录。
- 2026-07-10 补充:`agent.delegate` 已形成可恢复的父子任务闭环。`delegationId` 由 durable pending action 的 `actionId` 派生,子任务记录会保存 `parentAgentId / parentRunId / delegationId`,终态记录额外保存经过统一凭据清洗和安全截断的 `terminalDetail`;同一委派的提交和回执分别受 delegation 级 OS 文件锁保护,同一目标 Agent 的 runId 分配与 pending 追加还受任务账本 OS 锁保护。子任务进入 `completed / failed / cancelled / budget-exhausted` 任一终态时,Runtime 按 `delegationId` 幂等生成且至多生成一次 `agent.delegate.result` 回执,失败、排队或活跃取消、预算耗尽都必须回传,不能只覆盖成功。回执会向父 Agent 既有队列追加固定 runId、`source=agent-delegate-receipt` 的续跑任务,把完整的已清洗 `terminalDetail` 交回父 run,不再只保留 80 字符 UI 摘要;回执 prompt 明确禁止重复同一委派,排队期间不提前写入父会话,真正开始执行时才幂等落盘,用户消息或回执消息落盘失败时不会进入 LLM。回执任务保留父 run 关联,并在真正开始或恢复前再次检查父 run 状态,关联缺失或父 run 不存在时失败关闭;该续跑仍受父 Agent 原有 FIFO、per-Agent OS 锁、权限确认、取消、恢复和 `needs-reconciliation` 屏障约束,不直接重入父 run、不插队、不新增独立 worker;父 run 已取消或普通失败时只保留 suppressed receipt 审计,不自动复活,父 Session 归档与切换会被未结束委派阻止,极端归档竞态下回执回落到父 Agent 当前可写 Session。恢复先恢复 pending action / reconciliation 屏障,再扫描“子任务终态已落盘但回执未提交”的窗口并补齐缺失回执;`needs-reconciliation` 本身不回执,只有人工核对后最终取消才回传 `cancelled`
- 历史记录(已由 V1.1 独立 Runner 替代):Runtime 最初通过 `resume_game_creator_agent_runtime_tasks` 把本地 JSONL 队列重接到当前 App 进程。当前恢复入口仍保留权限、任务顺序和 `agent.runtime.background_task.recovered` 审计语义,但实际由独立 Runner 接管原 run / session;已发出的上游 LLM 请求仍不能从网络中间点续传。
- 2026-07-10 补充,2026-07-16 由 V1.28 澄清目标:后台 planning 与预算内 final reply 使用专用最小上下文,只预置 Agent 身份、sessionId、runId、执行模式和工具策略;Agent 私有记忆、项目记忆、黑板、对话、资产、项目索引与文件正文只能经对应工具通过权限 gate 后作为 observation 进入下一轮。只有开发窗口的专业 Agent 前台直调可使用对应角色上下文;正式用户前台接入后统一进入 `project-supervisor`。长黑板、记忆和对话按尾部截断,确保最新结论与最新定向消息优先保留。
- 2026-07-10 补充,2026-07-16 由 V1.28 澄清目标:同一 Agent 的开发前台直调、流式调试和后台任务统一使用 `.agent/runtime/locks/<agentId>.lock` OS 文件锁。开发前台不再在整个 LLM 请求期间占用项目级写锁;同 Agent 后台任务在开发前台运行时只入队,前台成功或失败后把当前 Agent 锁直接移交给 drain,不重新抢锁,也不允许 drain 启动异常把已经完成的调试结果改判为失败。正式用户 GUI 接入后不通过该入口直聊专业 Agent;不同 Agent 继续并行,真实项目写工具只在副作用执行期间短暂申请项目写锁。
- 2026-07-10 补充,2026-07-16 由 V1.28 澄清:后台 planning 与预算内 final reply 使用专用最小上下文,只预置 Agent 身份、sessionId、runId、执行模式和工具策略;Agent 私有记忆、项目记忆、黑板、对话、资产、项目索引与文件正文只能经对应工具通过权限 gate 后作为 observation 进入下一轮。只有开发窗口的专业 Agent 前台直调可使用对应角色上下文;正式用户前台现已统一进入 `project-supervisor`。长黑板、记忆和对话按尾部截断,确保最新结论与最新定向消息优先保留。
- 2026-07-10 补充,2026-07-16 由 V1.28 澄清:同一 Agent 的开发前台直调、流式调试和后台任务统一使用 `.agent/runtime/locks/<agentId>.lock` OS 文件锁。开发前台不再在整个 LLM 请求期间占用项目级写锁;同 Agent 后台任务在开发前台运行时只入队,前台成功或失败后把当前 Agent 锁直接移交给 drain,不重新抢锁,也不允许 drain 启动异常把已经完成的调试结果改判为失败。正式用户 GUI 不通过该入口直聊专业 Agent;不同 Agent 继续并行,真实项目写工具只在副作用执行期间短暂申请项目写锁。
- 2026-07-10 补充:默认 `agent.resume=confirm` 时,客户端自动恢复命令只做 auto gate 并返回待确认错误;主工作区和独立开发 Agent 聊天窗口在首次读取项目 Runtime 时都必须显示 `agent.resume` 确认条,确认对象绑定发起时的项目路径,切换项目会取消旧确认,异步返回后也不得把旧项目 Runtime 合并到新项目 UI。开发者确认后调用独立 `confirm_resume_game_creator_agent_runtime_tasks`,该命令仍执行 deny-only 权限检查后才接回 durable queue。临时调用失败不锁死项目路径,允许后续刷新重试;明确 deny 或取消都不恢复任务。
- 2026-07-10 补充,2026-07-12 更新,2026-07-15 增加 V1.17 完成门禁并由 V1.21 澄清:后台 Agent 返回空 `actions` 后,只有不存在 `project.verify` 等既有 blocker,且当前结构化计划的全部必要步骤均为 `completed`,才视为 loop 已收束。工具 action 序号和成功 observation 不会自动推进结构化计划;未完成时 Runtime 返回 `runtime.plan_update` blocker,在同一 run 要求 Agent 按真实进度更新。每 6 轮只做进度 checkpoint 与停滞检测;有新的独立 observation 时继续同一 run,最近 6 轮没有独立进展或相邻 checkpoint 重复时终态才写为 `status=failed / phase=budget-exhausted`error 使用 `loop-budget-exhausted` 机器可读前缀,不再调用 final reply 后写 completed 审计。上下文摘要只由 token 阈值或显式 `/compact` 触发。解析阶段保留过滤后的 action 总数,每轮超过 3 个 action 时写入 `runtime.tool_budget` observation 并只执行前三个,要求下一轮重新排序。Runtime 默认 `allowedTools` 直接由实际可执行工具白名单派生,避免 UI 观测与执行边界漂移。
- 2026-07-15 V1.18 补充:开发单 Agent 对话框使用 `执行 / 聊天 / 目标` 三段模式,Goal 创建/编辑在独立弹层完成,并可查看状态、revision、完成标准以及暂停/恢复/清理;正式用户窗口不展示 Goal 管理控件。Provider 中断边界先持久化可恢复的当前 v5 contextRunner 重启先收束 Goal control`paused` 在 finalization/pending action 前直接保持休眠。resume 只从 `paused` 续接,先删除同一 run 旧 cancel tombstonefinalization v3 在 assistant 后先投影 Runtime completed,再写 Goal completed 并补 Goal 终态投影。
- 任务图能力:每轮 Orchestrator agenda、ready / active task 选择、Evaluator 结构化返工路由、返工轮 carry-over。
- 记忆能力:短期记忆 `memory/session.md`、长期记忆 `memory/project.md`、项目级黑板 `memory/blackboard.md` 和角色私有记忆 `memory/agents/<group>/<role>.md`;黑板用于共享重要跨 agent 记忆,角色私有记忆只给对应角色 brief 读取和追加。最近 project / agent conversation 会作为短期 prompt 上下文读取,不替代正式 memory 文件。
- 对话能力目标:结构化对话记录统一落在 `.agent/conversations/` 的 append-only JSONL;正式用户 GUI 接入后只写 `project-supervisor` 的 active Agent Session`.agent/conversations/project.jsonl` 仅保留为 legacy 只读背景。当前 `agc:chat` 已按该路由落盘;开发窗口直调单个专业 Agent 时只写对应 `.agent/conversations/agents/<agentId>/` Session;专业 Agent 与 child 的终态只形成内部回执/证据,不写正式用户 assistant,也不把原始对话混进项目黑板或角色私有记忆。
- 对话能力:结构化对话记录统一落在 `.agent/conversations/` 的 append-only JSONL;正式用户 GUI 只写 `project-supervisor` 的 active Agent Session`.agent/conversations/project.jsonl` 仅保留为 legacy 只读背景。`agc:chat` 与正式项目开发页均已按该路由落盘;开发窗口直调单个专业 Agent 时只写对应 `.agent/conversations/agents/<agentId>/` Session;专业 Agent 与 child 的终态只形成内部回执/证据,不写正式用户 assistant,也不把原始对话混进项目黑板或角色私有记忆。
- 本地能力:生成代码和资产落盘、本地 manifest、受限运行命令、本地 HTTP 预览。
- 文件能力:`file.list/read/write/delete` 只允许访问项目目录内的相对路径,拒绝绝对路径、`..`、反斜杠和符号链接。
- 产物治理能力:写入前生成本地 checkpoint,写入后记录相对路径 diff,用户确认后可 restore 到最近 checkpointrestore 会回滚到 checkpoint 状态,包括删除 checkpoint 后新增的受跟踪文件。
@@ -317,12 +317,12 @@ game-project/
- 本地 HTTP 预览能启动,并在外部浏览器展示可玩原型。
- 美术/音乐资产能从画板链路回流到本地项目。
- 短期记忆、长期记忆、项目黑板和角色私有记忆按授权本地项目路径读写;普通用户仍只通过聊天命令访问短期 / 长期 / 黑板记忆,角色私有记忆只在单 agent 对话和生成 loop 中按目标 agent 读取。
- 结构化对话记录按授权本地项目路径追加 JSONL;普通聊天、`/history`、工作区历史和agent 对话读取 `.agent/conversations/`,最近 project / agent 对话可进入生成 prompt 上下文,但 v1 不提供 fork、archive 或云端同步。
- 结构化对话记录按授权本地项目路径追加 JSONL;正式聊天读取 Supervisor active Session 与只读 legacy 项目历史,开发Agent 对话读取对应 Agent Session。开发入口已支持本地 Session 新建、切换、归档和分叉,但不提供云端同步。
- Agent 状态列表从 `.agent/manifest.json` 的任务 / 角色清单、`.agent/run.latest.json` / `.agent/runs/<runId>.json` 的 step、taskGraph、passPlans、lifecycleStatus,以及 `read_game_creator_agent_runtimes` 批量读取的 `.agent/runtime/agents/<taskId>.json` 和最近任务派生;v1 不新增独立状态数据库,也不承诺完整后台 runner。
- App 启动先检查平台登录态;登录后进入同一个客户端首页,不再有面向用户的启动器 / 主窗口切换概念。首页按 `做游戏` / `做素材` / `做方案` 保存 `game` / `art` / `doc` 初始意图,发送时弹出原生目录选择,目标目录存在且非空时必须二次确认;确认后调用 `init_local_game_project` 初始化本地项目、`upload_local_asset` 导入附件`append_local_conversation_message` 记录首条需求和接收回执,再写入最近项目并切到项目开发占位页。本流程不调用 `generate_local_game_draft``generate_platform_art_asset` 或 LLM 聊天
- debug 构建启动后在用户 `client` 窗口之外额外打开 `developer` 窗口;该窗口用于开发者单独选择 Agent、切换时自动读取该 Agent 历史,并把用户消息和真实 Agent 回复持久化到 `.agent/conversations/agents/<agentId>.jsonl`普通用户窗口不得出现 `Agent 聊天` 导航入口。
- 首页最近项目只展示最近 3 个有效项目;项目组页在同一窗口管理最近项目、打开项目、新建项目和显示目录。打开项目只读取已初始化项目并切到项目开发占位页,不打开第二窗口;新建项目仍沿用非空目录确认,不自动重建无效历史路径。
- 项目开发占位页保留左侧栏和顶部栏,展示项目名、路径、创建模式、首条需求、附件导入结果、最近 run 状态和后续“项目开发画布”占位;本轮不落地真正画板 + Agent 双栏
- App 启动先检查平台登录态;登录后进入同一个客户端首页,不再有面向用户的启动器 / 主窗口切换概念。首页按 `做游戏` / `做素材` / `做方案` 保存 `game` / `art` / `doc` 初始意图,发送时弹出原生目录选择,目标目录存在且非空时必须二次确认;确认后调用 `init_local_game_project` 初始化本地项目、`upload_local_asset` 导入附件,再把首条需求直接投递给 active `project-supervisor` Session 的后台 Runtime写入最近项目并切到项目开发页。本流程不调用 `generate_local_game_draft``generate_platform_art_asset`、一次性 `chat_with_game_creator_agent` 或 legacy 项目对话 append
- debug 构建启动后在用户 `client` 窗口之外额外打开 `developer` 窗口;该窗口用于开发者单独选择 Agent、管理对应 active/archived Session 并读取历史,用户消息和真实 Agent 回复持久化到 `.agent/conversations/agents/<agentId>/` 下的规范 Session。普通用户窗口不得出现 `Agent 聊天` 导航、picker 或工具台入口。
- 首页最近项目只展示最近 3 个有效项目;项目组页在同一窗口管理最近项目、打开项目、新建项目和显示目录。打开项目只读取已初始化项目并切到项目开发页,不打开第二窗口;新建项目仍沿用非空目录确认,不自动重建无效历史路径。
- 项目开发页保留左侧栏和顶部栏,顶部展示项目名、路径和最近 run 状态;中间只挂载 active `project-supervisor` Session 的正式对话面、Runtime 状态、确认/Needs input 和专业 Agent 协作只读状态,底部保留附件导入结果。真正的项目开发画布仍未落地;专业 Agent picker、完整计划和工具台继续留在开发入口
## v1 验收证据矩阵
@@ -395,7 +395,7 @@ game-project/
- `/publish` 聊天入口由 `appSurface.test.ts` 主窗口 smoke 覆盖:只基于当前已加载 manifest、最近 run trace、预览状态、资产来源和最近命令生成发布准备清单,提供 `/run``/trace``/agent-resume ``/export` 草稿,不触发 Tauri 读写、预览启动、文件读取或新增普通用户面板。
- 主窗口策略快捷入口只填入 `/policy-confirm project.index``/policy-confirm asset.register``/policy-confirm memory.write``/policy-confirm preview.start``/policy-confirm preview.open``/policy-confirm preview.stop``/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;用于证明正式发布只登记一个用户窗口,登录后同一 WebView 渲染首页、项目组和项目开发占位Tauri 不自动额外打开 `developer` 窗口,开发面板只在 Vite dev 的 `?dev` / `#dev` 分支渲染,旧窗口切换 command 仅保留兼容、不进入用户主流程。
- `npm run check:native-shells`:覆盖 AI 游戏创作壳的 release/dev 边界、正式用户 App 不嵌入游戏预览 iframe、用户侧预览命令交给外部浏览器和 Tauri release `--no-bundle` 构建 smoke;用于证明正式发布只登记一个用户窗口,登录后同一 WebView 渲染首页、项目组和项目开发Tauri 不自动额外打开 `developer` 窗口,开发面板只在 Vite dev 的 `?dev` / `#dev` 分支渲染,旧窗口切换 command 仅保留兼容、不进入用户主流程。
- `npm run check:encoding``git diff --check`:覆盖中文文档、中文命令文案和补丁空白;用于避免乱码、尾随空白和无关格式漂移。
- `npm run ai-game-creator-shell:llm-status`:只检查 LLM 客户端配置是否就绪,不请求上游、不显示 API Key;用于本机联调前确认配置。发布版启动时会在 Tauri 应用配置目录生成默认 `game-creator.config.json`,仓库内 `apps/ai-game-creator-shell/game-creator.config.json` 只作为默认模板。
- `npm run ai-game-creator-shell:agent-run -- --no-wait /绝对项目路径 "游戏创作需求"`:使用真实 OpenAI-compatible 配置跑一次本地生成、落盘、自检和预览;用于人工验收真实 provider 路径。真实 provider 配置放在 Tauri 应用配置目录的 `game-creator.config.json` 中,至少设置 `llm.apiKey`,需要覆盖默认服务时设置 `llm.baseUrl``llm.model`;默认 API kind 为 `openai_responses`,旧 Chat Completions 兼容网关设置 `llm.apiKind``openai_chat`Anthropic Messages 网关设置 `llm.apiKind``anthropic`URL 会在 base URL 后拼 `/v1/messages`,例如 Minimax Anthropic base URL 可配置为 `https://api.minimaxi.com/anthropic`;真实网关长请求若在非流式响应前被 60 秒空闲连接切断,联调时设置 `llm.stream``true`
@@ -434,7 +434,7 @@ game-project/
- 主窗口的 agent 状态列表以 manifest 角色任务为底表,再合并最近 run trace 中 `taskGraph.tasks` 的任务状态、同 taskId / group / role 的最新 step 状态、输入输出路径、错误摘要、lifecycleStatus 和 `activeTaskIds` / `carriedTaskIds` / `readyTaskIds` 编排标记;如果 trace 缺失或过期,只展示 manifest 的静态任务状态和“暂无最近运行证据”。
- 共享契约和 `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 配置;主聊天 Agent、Planner、Orchestrator、Generator、Evaluator 和 16 个角色 agent 都可在 `agentLlm` 中单独覆盖 API Key、base URL、模型、API 类型和流式请求,空项继承全局 LLM 配置;主聊天 Agent 使用 `agentLlm.chat`默认生成链路仍只让 Planner / Generator 调 LLM,配置了 `agentLlm.<taskId>` 的角色 agent 会改用自己的 provider 生成 brief,未配置的角色 agent 继续使用本地 brief;生成游戏、主聊天或平台美术时如果返回 LLM / editorApi 缺配置错误,主窗口自动打开同一个运行时配置弹窗;API Key 输入框使用密码字段并关闭自动填充,数值项在 UI 层夹住下限,Rust 写配置时也拒绝过低超时,保存时只写运行时配置文件,不写仓库模板、本地项目、trace 或 manifest。
- 主窗口“配置”面板和聊天 `/config` 命令读写 Tauri 应用配置目录中的 `game-creator.config.json`,覆盖 LLM API Key、base URL、模型、API 类型、流式请求、超时、重试和画板 External API 配置;主聊天 Agent、Planner、Orchestrator、Generator、Evaluator 和 16 个角色 agent 都可在 `agentLlm` 中单独覆盖 API Key、base URL、模型、API 类型和流式请求,空项继承全局 LLM 配置;正式主聊天优先使用 `agentLlm.project-supervisor`,旧 `agentLlm.chat` 只作兼容回退。默认生成链路仍只让 Planner / Generator 调 LLM,配置了 `agentLlm.<taskId>` 的角色 agent 会改用自己的 provider 生成 brief,未配置的角色 agent 继续使用本地 brief。旧生成链路或平台美术返回 LLM / editorApi 缺配置错误时可沿用运行时配置弹窗;正式 `project-supervisor` 项目页只展示错误,由单窗口壳的全局“配置”入口处理,不自动弹出开发配置框。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-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 loopPlanner 写 `.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 执行层按 dependencyWaves 调度,wave 之间串行、同一 wave 内多个角色 Agent 以 async task 并行运行;每个角色 brief 必须读取自己的私有记忆 `memory/agents/<group>/<role>.md`、本 Agent 对话和项目黑板 `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` 静态自检和预览试玩。
@@ -462,7 +462,7 @@ game-project/
- `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/``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 的手动预览启动不阻断。
- 普通聊天文本进入 `project-supervisor` 的 active Session;匹配 Session 已有非终态 run 时转为 same-run steer,不切换父 Session/run。它读取当前 Supervisor Session、legacy 项目对话、短期 / 长期记忆、项目黑板、本地资产和有界仓库上下文,可以正常澄清、使用 Runtime 白名单工具、按项目权限请求确认,并把专业工作委派给现有 Agent;同一父 run 同时活跃的静态专业委派不超过 3 个。静态 delivery/claim 与 isolated all-join 都必须由同一父 run 收齐、认领并完成 observation 持久化后才允许形成唯一最终回复;专业 Agent 的内部对话、工具计划和动态 child 不直接展示给普通用户。显式 `/generate <创作想法>``/draft <创作想法>` 继续保留为确定性旧生成 loop 的待确认快捷命令;它不替代 Supervisor 的自主 Runtime。用户确认旧生成命令后,正式用户聊天仍展示 Planner、Orchestrator、6 组角色 brief、Generator、Evaluator、ArtifactWriter 和 `game.static_smoke` 的真实进度,并按原契约写入本地产物与记忆。
- 当前单窗口项目开发页的普通聊天文本进入 `project-supervisor` 的 active Session;匹配 Session 已有非终态 run 时转为 same-run steer,不切换父 Session/run。它读取当前 Supervisor Session、只读兼容的 legacy 项目对话、短期 / 长期记忆、项目黑板、本地资产和有界仓库上下文,可以正常澄清、使用 Runtime 白名单工具、按项目权限请求确认,并把专业工作委派给现有 Agent;同一父 run 同时活跃的静态专业委派不超过 3 个。新的 user 消息和最终 assistant 只由 Runtime 写入 Supervisor Session,流式草稿只作可丢失展示缓存;React 不再把同一轮写回 legacy 项目对话。静态 delivery/claim 与 isolated all-join 都必须由同一父 run 收齐、认领并完成 observation 持久化后才允许形成唯一最终回复;专业 Agent 的内部对话、工具计划和动态 child 不直接展示给普通用户。显式 `/generate <创作想法>``/draft <创作想法>` 继续保留为确定性旧生成 loop 的待确认快捷命令;它不替代 Supervisor 的自主 Runtime。用户确认旧生成命令后,正式用户聊天仍展示 Planner、Orchestrator、6 组角色 brief、Generator、Evaluator、ArtifactWriter 和 `game.static_smoke` 的真实进度,并按原契约写入本地产物与记忆。
- `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` 不覆盖记忆文件,而是继续追加短期对话记录、长期创作目标记录、项目黑板摘要和角色私有摘要,保留用户迭代历史。
@@ -524,13 +524,13 @@ game-project/
- 聊天输入 `/passes` 只使用主窗口当前已加载的最近 run trace,从 `artifacts` 中筛选 `.agent/passes/` 相对路径,在聊天里列出轮次产物读取命令并提供首个 `/read` 草稿;该命令不调用 Tauri 读写、不读取文件、不新增普通用户轮次面板,真正读取仍由用户发送 `/read` 并走 `file.read` 权限流。
- 聊天输入 `/internals` 只列出项目内部真相源读取命令,覆盖 `.agent/manifest.json``.agent/run.latest.json``.agent/spec.md``.agent/findings.md``.agent/policy.json``.agent/project.index.json``.agent/agent.db``.agent/conversations/project.jsonl`;该命令不调用 Tauri 读写、不读取文件、不新增普通用户内部文件面板,真正读取仍由用户发送 `/read` 并走 `file.read` 权限流。
- 聊天输入 `/publish` 只使用主窗口当前已加载的 manifest、最近 run trace、预览状态、资产来源和最近命令摘要,在聊天里生成发布准备清单,列出原型通过状态、预览、任务、资产、音频、包装说明和试玩包导出状态,并提供 `/run``/trace``/agent-resume ``/export` 草稿;该命令不调用 Tauri 读写、不启动或打开预览、不读取文件、不新增普通用户面板,真正导出仍由用户发送 `/export` 并走确认流。
- 窗口可从 agent 状态列表进入单个 agent 对话;该入口只加载目标 agent 的 conversation JSONL,发送消息后追加到同一 agent conversation,不开启平行任务图、不 fork run,也不归档历史会话
- v1 普通用户登录后直接进入单窗口客户端首页;同一窗口中切换首页、项目组、指南 / 反馈和项目开发占位。项目组页管理最近项目、打开项目、新建项目和显示目录;打开项目只切换到项目开发占位,不调用 `open_game_creator_workspace_window` 打开第二窗口。旧 Tauri 窗口 command 只保留兼容,不进入用户主流程。
- 开发窗口可从 Agent 状态列表进入单个专业 Agent 对话并管理其 Session;正式用户项目开发页只读展示专业 Agent 协作状态,不提供单 Agent 对话入口、Session 控件或工具台
- v1 普通用户登录后直接进入单窗口客户端首页;同一窗口中切换首页、项目组、指南 / 反馈和项目开发。项目组页管理最近项目、打开项目、新建项目和显示目录;打开项目只切换到项目开发,不调用 `open_game_creator_workspace_window` 打开第二窗口。旧 Tauri 窗口 command 只保留兼容,不进入用户主流程。
- 主窗口可通过系统文件管理器显示当前项目目录,也可在聊天输入 `/open-project` 走同一只读打开动作;该操作只打开本地目录,不初始化项目、不写项目文件、不切换工作区。主窗口头部显示最近 `.agent/run.latest.json` 的 run 状态摘要和当前项目预览状态,并通过“刷新状态”重新读取同一 trace,不新增状态数据库。
- 首页、项目组页和项目开发占位共用同一个运行时配置弹窗,读写 Tauri 应用配置目录中的 `game-creator.config.json`;API Key 仍不进入本地项目、trace、manifest 或聊天记录。
- 首页、项目组页和项目开发页共用单窗口壳的全局运行时配置弹窗,读写 Tauri 应用配置目录中的 `game-creator.config.json`正式 Supervisor 项目页缺配置时只显示错误,不自动打开该弹窗。API Key 仍不进入本地项目、trace、manifest 或聊天记录。
- 首页发送和项目组新建都通过 Tauri 原生目录选择器选择项目路径;用户取消目录选择时不覆盖已有输入或草稿。
- 最近项目列表只来自本机 WebView storage,可移除单项或清空;这些操作不触碰项目目录,也不触发窗口切换。项目组页会只读检查最近项目路径,已初始化项目优先显示 `.agent/manifest.json` 里的项目名并保留路径副信息;如果 `.agent/run.latest.json` 可读,则显示最近 run 的 status / stopReason;缺失、非目录、未初始化或检查失败的条目禁用打开,用户可移除后重新选择。刷新最近项目只重新执行这组只读检查,不改项目目录或最近项目列表;刷新未完成时按钮显示“刷新中”并禁用,最近项目条目显示“检查中”且临时禁用打开 / 显示操作,避免重复触发并让用户知道检查仍在进行;“显示”只通过系统文件管理器打开已确认存在的本地目录,未初始化但存在的目录也可显示,缺失、非目录或检查失败时禁用,不初始化项目、不写最近项目。
- 项目组页“打开”只进入已初始化且 `.agent/manifest.json` 可读的 AI 游戏项目;路径不存在、不是文件夹或只是普通文件夹时留在当前页提示,不自动创建目录。“新建项目”和聊天 `project.create` 新建入口会先检查目标目录,目标目录已经存在且非空时必须弹出确认提醒;用户继续后才调用 `init_local_game_project`,默认项目名取目标文件夹名,再写入最近项目并切到项目开发占位;用户取消或初始化失败时不切换视图、也不写入最近项目。
- 项目组页“打开”只进入已初始化且 `.agent/manifest.json` 可读的 AI 游戏项目;路径不存在、不是文件夹或只是普通文件夹时留在当前页提示,不自动创建目录。“新建项目”和聊天 `project.create` 新建入口会先检查目标目录,目标目录已经存在且非空时必须弹出确认提醒;用户继续后才调用 `init_local_game_project`,默认项目名取目标文件夹名,再写入最近项目并切到项目开发;用户取消或初始化失败时不切换视图、也不写入最近项目。
- 聊天输入 `/commands` 会只读列出 Tauri runtime 暴露的受限命令白名单,读取失败或非 Tauri 环境下回退到共享契约默认列表;该命令不执行白名单命令,也不要求先初始化项目。
- 聊天输入 `/smoke` 会生成待确认的 `command.run_limited` 内置命令,当前只映射到白名单 `game.static_smoke`,不开放任意命令解析。
- 聊天输入 `/run` 会生成待确认的 `game.run_local` 内置命令,确认后复用白名单 `game.static_smoke` 运行当前 `game/index.html`,通过后启动只读本地 HTTP 预览并交给外部浏览器;该命令不开放任意 shell。
@@ -550,9 +550,9 @@ game-project/
- 开发模式可执行 `canvas.project_open`,打开本机 Genarrative 编辑器里的指定画板项目。
- 开发模式可执行 `canvas.project_sync`,同步平台画板项目中的已有资源到本地项目资产目录;该命令要求 External API Key 具备 `editor:project``editor:asset` 相关 scope。
- 开发模式可执行 `canvas.asset_import`,将项目内已有文件按画板来源导入 manifest;也可执行 `canvas.export_import`,把现有画板素材导出 ZIP 回流为本地项目资产。`game.generate_draft` 在配置 `editorApi.apiKey` 时会自动复用 External Editor API 的图片生成接口生成首版美术素材,不新增平行资产模型。
- 普通模式目标态渲染 `project-supervisor` 主聊天、上传入口和专业 Agent 协作状态列表,不提供专业 Agent / child 单 Agent 对话;当前主窗口尚未完成这条路由接入。Tauri 主窗口不承载工具台布局,也不承载游戏预览画面。
- 普通模式已在登录后的单窗口项目开发页渲染 `project-supervisor` 主聊天和专业 Agent 协作只读状态,不提供专业 Agent / child 单 Agent 对话;首页首条需求直接投递 active Supervisor Session,全新项目没有该 Session 时先通过既有 Session 命令创建并设为 active,已有项目恢复该 Session 与历史后继续交互。Tauri 主窗口不承载开发 Agent picker、工具台布局或游戏预览画面。
- 聊天生成草案后会尝试启动只读 `127.0.0.1:<port>` 静态 HTTP server,调用系统外部浏览器打开预览,并把预览地址回到聊天消息;外部浏览器打开失败时保留本地 URL 供用户手动复制。
- 开发模式仅在 Vite dev 环境响应 `?dev``#dev`Tauri dev 可额外打开 `developer` 窗口显示专业组、本地项目、预览 iframe 和内置命令日志;正式构建忽略 dev 参数,release 配置只登记一个用户窗口,登录后同窗口进入首页并在项目组 / 项目开发占位之间切换。
- 开发模式仅在 Vite dev 环境响应 `?dev``#dev`Tauri dev 可额外打开 `developer` 窗口显示专业组、本地项目、预览 iframe 和内置命令日志;正式构建忽略 dev 参数,release 配置只登记一个用户窗口,登录后同窗口进入首页并在项目组 / 项目开发之间切换。
- `check:native-shells` 会运行 `ai-game-creator-shell:check``ai-game-creator-shell:build -- --no-bundle`,并静态检查 release 只登记 `client / index.html` 这一个用户窗口、开发窗口只在 debug 下打开,开发面板必须挂在 `devMode` 分支内,正式用户 App 不能嵌入游戏预览 iframerelease CSP 也不能允许 `frame-src http://127.0.0.1:*`,用户侧预览命令必须调用 `open_local_game_preview` 交给系统外部浏览器,用户主流程不得调用旧工作区窗口切换 command。
- 共享契约提供 `GAME_CREATION_AGENT_CAPABILITIES` 和内置命令权限枚举;开发模式会展示能力列表。
- 共享契约提供 manifest task schema 和 ready-task 选择器,用于记录任务拆分、专业组、角色模板、依赖、产物、验收条件和当前可执行任务。
@@ -583,5 +583,5 @@ game-project/
- V1.28 的新 native `agent.delegate` 必须同时携带 `agentId / task / acceptanceCriteria / expectedArtifacts / repairOfDelegationId / runId`,其中 `acceptanceCriteria` 为 1-8 项,`expectedArtifacts` 为 0-16 个精确项目内非私有相对文件,两个引用字段可为 `null`。旧持久 action 缺字段只按空合同恢复,已有 pending/delivery/claim sidecar 不迁移。durable delivery 与 claim receipt 原样保存合同和 `structuredResult`,后者包含 `evidence-ready | needs-repair`、artifact path/SHA-256、缺失产物、验证要求与 revision、安全 evidence/error。
- Runtime 只按 child completed、预期产物齐全和必要 verification passed 判定 `evidence-ready`Supervisor 仍须按 `acceptanceCriteria` 结合摘要与证据做语义验收,不能把 evidence-ready 自动视为通过,也不能忽略 needs-repair。专业 Agent prompt 带完整合同;无法自行裁决的问题由 Supervisor 汇总后通过 `user.input_request` 向用户提问。
- Supervisor 认领原 delivery 后可为 needs-repair 或语义未通过创建一个 `repairOfDelegationId=<原 delegationId>` 的新委派。repair 必须完整继承原合同并交回原专业 Agent,只能留在同一父 run、深度为 1、同一原 delivery 同时最多一个非 suppressed 投递;相同重放幂等复用,不同重复/并发请求拒绝。`suppressed` repair 继续阻断,同一 action 可原地恢复;该 action 已持久失败时,新 action 只可在全部既有 repair 均 suppressed 时重做基础设施投递。所有必要 delivery/claim/repair、结构化计划、verification、确认、用户输入和其它既有 blocker 清零后,原 Supervisor run 才能写唯一用户回复。
- V1.28 Runtime 当前实现切片及其定向本地回归已完成:`project_supervisor_` 30/30、native 合同 parser→executor→delivery 1/1 PASS,覆盖合同证据、旧 sidecar 不迁移、双线程 repair 唯一活跃投递、suppressed repair 阻断与同 action/新 action 基础设施恢复、父 lane 忙时证据损坏持续重试 reconciliation、规范字段和别名空合同拒绝及唯一 Supervisor assistant/completed;这不代表完整 V1.28 验收清单已通过。正式用户 GUI 路由尚未接入,Runner 强杀恢复仍未验收;真实 `gpt-5.5` 隔离复测在修正测试驱动后于 Supervisor 首轮 planning 发生 transport failure,未创建 delivery。两个专业 Agent 真并行、一份弱交付恰好一次 repair、零重复与零泄漏的真实门禁仍未通过,V1.28 整体保持 NOT PASS。
- V1.28 Runtime 当前实现切片及其定向本地回归已完成:`project_supervisor_` 30/30、native 合同 parser→executor→delivery 1/1 PASS,覆盖合同证据、旧 sidecar 不迁移、双线程 repair 唯一活跃投递、suppressed repair 阻断与同 action/新 action 基础设施恢复、父 lane 忙时证据损坏持续重试 reconciliation、规范字段和别名空合同拒绝及唯一 Supervisor assistant/completed正式 GUI 接入后的 `appSurface.test.ts` 当前为 279/279 PASS,覆盖 active Supervisor Session、已有项目恢复、same-run steer、专业 Agent 只读状态、开发入口隔离和 legacy 零新增双写。这不代表完整 V1.28 验收清单已通过Runner 强杀恢复仍未验收;真实 `gpt-5.5` 隔离复测在修正测试驱动后于 Supervisor 首轮 planning 发生 transport failure,未创建 delivery。两个专业 Agent 真并行、一份弱交付恰好一次 repair、零重复与零泄漏的真实门禁仍未通过,V1.28 整体保持 NOT PASS。
- 开发模式可通过本地项目文件面板执行 `file.list/read/write/delete`,普通用户界面不暴露文件面板。