Codex/fix pr188 191 ci (#194)
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/194 Co-authored-by: kdletters <kdletters@qq.com> Co-committed-by: kdletters <kdletters@qq.com>
This commit was merged in pull request #194.
This commit is contained in:
@@ -133,7 +133,7 @@ node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 ap
|
||||
- [ ] `npm run dev` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
|
||||
- [ ] BgFilter worker 在 api-server 前 ready,父子共享实际 base URL / Token,Rust watch 只触发一次组合重启。
|
||||
- [ ] `npm run dev:web` 在主站端口不可用时能切换到可用端口。
|
||||
- [ ] `npm run agc` / `npm run agc:game-chat` 在 Linux 使用用户段 `start + 5`,Tauri、Vite、marker 和预检使用同一最终端口。
|
||||
- [ ] `npm run agc` 在 Linux 使用用户段 `start + 5`,Tauri、Vite、marker 和预检使用同一最终端口。
|
||||
- [ ] 文档同步更新 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
- [ ] 长期踩坑同步更新 `docs/project-memory/shared-memory/pitfalls.md`。
|
||||
- [ ] 修改中文文件后运行 `npm run check:encoding`。
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/start-tauri-dev.mjs",
|
||||
"game-chat": "node scripts/start-tauri-dev.mjs --game-chat",
|
||||
"dev-server": "node scripts/start-dev-server.mjs",
|
||||
"dev-stack": "node scripts/start-dev-stack.mjs",
|
||||
"build": "npm --prefix ../.. exec tauri -- build",
|
||||
"build:game-chat-release": "npm --prefix ../.. exec tauri -- build --config src-tauri/tauri.game-chat-release.conf.json --bundles nsis --features game-chat-release",
|
||||
"llm-status": "node scripts/run-cli-with-config.mjs --llm-status",
|
||||
"agent-task": "node scripts/run-cli-with-config.mjs --agent-task",
|
||||
"chat": "node scripts/run-cli-with-config.mjs --swarm-chat",
|
||||
@@ -26,7 +24,6 @@
|
||||
"agent-runtime:mixed-swarm-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-static-isolated-autonomous-chat",
|
||||
"agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-autonomous-chat",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-autonomous-playable-lane-defense",
|
||||
"agent-runtime:supervisor-game-chat-single-main-playable-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-game-chat-single-main-playable",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-e2e": "node scripts/agent-runtime-deterministic-playable-e2e.mjs",
|
||||
"agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-self-test": "node scripts/agent-runtime-deterministic-playable-e2e.mjs --self-test",
|
||||
"agent-runtime:supervisor-swarm-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-transient-retry",
|
||||
|
||||
@@ -71,10 +71,7 @@ import { isProjectSkillSuite } from '../suites/project-skill.mjs';
|
||||
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
|
||||
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
|
||||
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
|
||||
import {
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite,
|
||||
isSupervisorGameChatSingleMainPlayableSuite,
|
||||
} from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import {
|
||||
isSupervisorSwarmAutonomousChatSuite,
|
||||
isSupervisorSwarmCollaborationPolicyMixedRecoverySuite,
|
||||
@@ -835,14 +832,7 @@ export async function prepareIsolatedSuiteAppData({
|
||||
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
|
||||
const isolatedConfig = await loadConfig(appDataDir);
|
||||
const expectedBinding = state.config.providerBinding;
|
||||
const agentIds = isSupervisorGameChatSingleMainPlayableSuite()
|
||||
? [
|
||||
projectSupervisorAgentId,
|
||||
mainAgentId,
|
||||
'art-director',
|
||||
'art-asset-plan',
|
||||
]
|
||||
: [projectSupervisorAgentId];
|
||||
const agentIds = [projectSupervisorAgentId];
|
||||
const bindings = agentIds.map((agentId) => {
|
||||
const effective = effectiveAgentLlmConfig(isolatedConfig.config, agentId);
|
||||
assert(
|
||||
@@ -860,9 +850,6 @@ export async function prepareIsolatedSuiteAppData({
|
||||
});
|
||||
const effectiveBinding = {
|
||||
...bindings[0],
|
||||
...(isSupervisorGameChatSingleMainPlayableSuite()
|
||||
? { providerAgentMode: isolatedConfig.config.agentMode?.trim() ?? null }
|
||||
: {}),
|
||||
boundAgentIds: [...agentIds].sort(),
|
||||
};
|
||||
assert(
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
scopedAgentsSuite,
|
||||
steerRunnerKillSuite,
|
||||
supervisorAutonomousPlayableLaneDefenseSuite,
|
||||
supervisorGameChatSingleMainPlayableSuite,
|
||||
supervisorSwarmAutonomousChatSuite,
|
||||
supervisorSwarmCollaborationPolicyMixedRecoverySuite,
|
||||
supervisorSwarmFinalReplyTransientRetrySuite,
|
||||
@@ -71,7 +70,6 @@ export function parseArguments(args) {
|
||||
suite === supervisorSwarmToolPlanHandoffRunnerKillSuite ||
|
||||
suite === supervisorSwarmAutonomousChatSuite ||
|
||||
suite === supervisorAutonomousPlayableLaneDefenseSuite ||
|
||||
suite === supervisorGameChatSingleMainPlayableSuite ||
|
||||
suite === supervisorSwarmStaticIsolatedAutonomousChatSuite ||
|
||||
suite === supervisorSwarmCollaborationPolicyMixedRecoverySuite ||
|
||||
suite === steerRunnerKillSuite ||
|
||||
|
||||
@@ -28,10 +28,7 @@ import {
|
||||
isGoalRuntimeSuite,
|
||||
} from '../suites/goal.mjs';
|
||||
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
|
||||
import {
|
||||
isSupervisorAutonomousPlayableLaneDefenseSuite,
|
||||
isSupervisorGameChatSingleMainPlayableSuite,
|
||||
} from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
|
||||
import {
|
||||
isSupervisorSwarmSuite,
|
||||
supervisorSwarmVerificationFixtureSource,
|
||||
@@ -46,33 +43,20 @@ import { isIsolatedRunnerSuite } from './reporting.mjs';
|
||||
export function requiredAgentIdsForSuite() {
|
||||
return isUserInputRuntimeSuite()
|
||||
? [projectSupervisorAgentId]
|
||||
: isSupervisorGameChatSingleMainPlayableSuite()
|
||||
? [
|
||||
projectSupervisorAgentId,
|
||||
mainAgentId,
|
||||
'art-director',
|
||||
'art-asset-plan',
|
||||
]
|
||||
: isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
? [projectSupervisorAgentId]
|
||||
: isSupervisorSwarmSuite()
|
||||
? [
|
||||
projectSupervisorAgentId,
|
||||
supervisorSwarmDesignAgentId,
|
||||
supervisorSwarmQualityAgentId,
|
||||
]
|
||||
: isIsolatedRunnerSuite()
|
||||
? [mainAgentId]
|
||||
: [mainAgentId, 'quality-review'];
|
||||
: isSupervisorAutonomousPlayableLaneDefenseSuite()
|
||||
? [projectSupervisorAgentId]
|
||||
: isSupervisorSwarmSuite()
|
||||
? [
|
||||
projectSupervisorAgentId,
|
||||
supervisorSwarmDesignAgentId,
|
||||
supervisorSwarmQualityAgentId,
|
||||
]
|
||||
: isIsolatedRunnerSuite()
|
||||
? [mainAgentId]
|
||||
: [mainAgentId, 'quality-review'];
|
||||
}
|
||||
|
||||
export function expectedProviderBindingForSuite(config) {
|
||||
if (
|
||||
isSupervisorGameChatSingleMainPlayableSuite() &&
|
||||
config.agentMode !== 'provider'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const agentIds = requiredAgentIdsForSuite();
|
||||
const effectiveConfigs = agentIds.map((agentId) =>
|
||||
effectiveAgentLlmConfig(config, agentId),
|
||||
@@ -110,9 +94,6 @@ export function expectedProviderBindingForSuite(config) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...(isSupervisorGameChatSingleMainPlayableSuite()
|
||||
? { providerAgentMode: 'provider' }
|
||||
: {}),
|
||||
providerModel: expectedIdentity[0],
|
||||
providerApiKind: expectedIdentity[1],
|
||||
providerReasoningEffort: expectedIdentity[2],
|
||||
@@ -123,7 +104,7 @@ export function expectedProviderBindingForSuite(config) {
|
||||
|
||||
export async function checkPrerequisites(config) {
|
||||
const requiredAgents = requiredAgentIdsForSuite();
|
||||
let llmConfigured = requiredAgents.every((agentId) => {
|
||||
const llmConfigured = requiredAgents.every((agentId) => {
|
||||
const effective = effectiveAgentLlmConfig(config, agentId);
|
||||
return ['apiKey', 'baseUrl', 'model'].every(
|
||||
(key) =>
|
||||
@@ -131,9 +112,6 @@ export async function checkPrerequisites(config) {
|
||||
);
|
||||
});
|
||||
const providerBinding = expectedProviderBindingForSuite(config);
|
||||
if (isSupervisorGameChatSingleMainPlayableSuite()) {
|
||||
llmConfigured = llmConfigured && providerBinding !== null;
|
||||
}
|
||||
const editorApiConfigured = ['apiKey', 'baseUrl'].every(
|
||||
(key) =>
|
||||
typeof config.editorApi?.[key] === 'string' &&
|
||||
|
||||
@@ -227,9 +227,6 @@ export const supervisorSwarmAutonomousChatSuite =
|
||||
export const supervisorAutonomousPlayableLaneDefenseSuite =
|
||||
'supervisor-autonomous-playable-lane-defense';
|
||||
|
||||
export const supervisorGameChatSingleMainPlayableSuite =
|
||||
'supervisor-game-chat-single-main-playable';
|
||||
|
||||
export const supervisorSwarmStaticIsolatedAutonomousChatSuite =
|
||||
'supervisor-swarm-static-isolated-autonomous-chat';
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+10
-538
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const repoRoot = resolve(appRoot, '../..');
|
||||
const npmCli =
|
||||
process.env.npm_execpath ??
|
||||
resolve(dirname(process.execPath), 'node_modules/npm/bin/npm-cli.js');
|
||||
|
||||
function run(args, extraEnv = {}) {
|
||||
const result = spawnSync(process.execPath, [npmCli, ...args], {
|
||||
cwd: appRoot,
|
||||
env: { ...process.env, ...extraEnv },
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
run(['--prefix', repoRoot, 'run', 'ai-game-creator-shell:typecheck']);
|
||||
run(
|
||||
[
|
||||
'--prefix',
|
||||
repoRoot,
|
||||
'exec',
|
||||
'vite',
|
||||
'--',
|
||||
'build',
|
||||
'--config',
|
||||
'vite.config.ts',
|
||||
],
|
||||
{ VITE_AGC_GAME_CHAT_ONLY: 'true' },
|
||||
);
|
||||
@@ -35,12 +35,6 @@ const windowsTauriConfig = JSON.parse(
|
||||
'utf8',
|
||||
),
|
||||
);
|
||||
const gameChatReleaseTauriConfig = JSON.parse(
|
||||
fs.readFileSync(
|
||||
new URL('../src-tauri/tauri.game-chat-release.conf.json', import.meta.url),
|
||||
'utf8',
|
||||
),
|
||||
);
|
||||
const cargoManifestSource = fs.readFileSync(
|
||||
new URL('../src-tauri/Cargo.toml', import.meta.url),
|
||||
'utf8',
|
||||
@@ -90,22 +84,10 @@ const appEntrypointSource = fs.readFileSync(
|
||||
new URL('../src/main.tsx', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const appModuleSource = fs.readFileSync(
|
||||
new URL('../src/App.tsx', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const gameChatReleaseBuildSource = fs.readFileSync(
|
||||
new URL('../scripts/build-game-chat-release.mjs', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const tauriHandlerSource = fs.readFileSync(
|
||||
new URL('../src-tauri/src/main.rs', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const tauriWindowSource = fs.readFileSync(
|
||||
new URL('../src-tauri/src/windows.rs', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const tauriRustSource = readSourceTree(
|
||||
new URL('../src-tauri/src/', import.meta.url),
|
||||
'.rs',
|
||||
@@ -130,6 +112,7 @@ const allowedUncalledTauriCommands = [
|
||||
'create_ui_design_resource',
|
||||
'open_game_creator_launcher_window',
|
||||
'open_game_creator_workspace_window',
|
||||
'stop_local_game_preview_if_matches',
|
||||
];
|
||||
const sourceExtensions = new Set([
|
||||
'.json',
|
||||
@@ -1197,29 +1180,6 @@ if (
|
||||
);
|
||||
}
|
||||
|
||||
const gameChatReleaseAppIndex = appModuleSource.indexOf(
|
||||
'export function GameChatReleaseApp(',
|
||||
);
|
||||
const gameChatReleaseBranchIndex = appEntrypointSource.indexOf(
|
||||
'{gameChatReleaseMode ? (',
|
||||
);
|
||||
const authenticatedClientIndex = appEntrypointSource.indexOf(
|
||||
'<AuthenticatedClient>',
|
||||
gameChatReleaseBranchIndex,
|
||||
);
|
||||
if (
|
||||
gameChatReleaseAppIndex === -1 ||
|
||||
gameChatReleaseBranchIndex === -1 ||
|
||||
!appEntrypointSource
|
||||
.slice(gameChatReleaseBranchIndex, authenticatedClientIndex)
|
||||
.includes('gameChatApp') ||
|
||||
authenticatedClientIndex < gameChatReleaseBranchIndex
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator game-chat release must render the local chat App before the platform authentication boundary',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
packageConfig.scripts?.['agent-run'] !==
|
||||
'node scripts/run-cli-with-config.mjs --agent-run'
|
||||
@@ -1566,103 +1526,6 @@ if (packageConfig.scripts?.dev !== 'node scripts/start-tauri-dev.mjs') {
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
packageConfig.scripts?.['game-chat'] !==
|
||||
'node scripts/start-tauri-dev.mjs --game-chat'
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator shell game-chat must run through the managed Tauri dev launcher',
|
||||
);
|
||||
}
|
||||
|
||||
const gameChatInitialUrlApply =
|
||||
'apply_game_chat_initial_window_url(tauri_context.config_mut(), options)';
|
||||
const gameChatInitialUrlApplyIndexes = Array.from(
|
||||
tauriHandlerSource.matchAll(
|
||||
/apply_game_chat_initial_window_url\(tauri_context\.config_mut\(\), options\)/gu,
|
||||
),
|
||||
(match) => match.index,
|
||||
);
|
||||
const tauriContextIndex = tauriHandlerSource.indexOf(
|
||||
'let mut tauri_context = tauri::generate_context!()',
|
||||
);
|
||||
const tauriBuilderIndex = tauriHandlerSource.indexOf(
|
||||
'tauri::Builder::default()',
|
||||
);
|
||||
if (
|
||||
gameChatInitialUrlApplyIndexes.length !== 1 ||
|
||||
tauriContextIndex === -1 ||
|
||||
tauriBuilderIndex === -1 ||
|
||||
gameChatInitialUrlApplyIndexes[0] < tauriContextIndex ||
|
||||
gameChatInitialUrlApplyIndexes[0] > tauriBuilderIndex
|
||||
) {
|
||||
throw new Error(
|
||||
`AI game creator game-chat URL must be applied exactly once between Context creation and Tauri Builder creation: ${gameChatInitialUrlApply}`,
|
||||
);
|
||||
}
|
||||
|
||||
const tauriSetupStartIndex = tauriHandlerSource.indexOf('.setup(move |app| {');
|
||||
const tauriSetupEndIndex = tauriHandlerSource.indexOf(
|
||||
'.invoke_handler(',
|
||||
tauriSetupStartIndex,
|
||||
);
|
||||
if (tauriSetupStartIndex === -1 || tauriSetupEndIndex === -1) {
|
||||
throw new Error('AI game creator Tauri setup block is missing');
|
||||
}
|
||||
const tauriSetupSource = tauriHandlerSource.slice(
|
||||
tauriSetupStartIndex,
|
||||
tauriSetupEndIndex,
|
||||
);
|
||||
for (const forbiddenSetupSnippet of [
|
||||
'game_chat_launch.as_ref()',
|
||||
'navigate_client_to_game_chat',
|
||||
'.navigate(',
|
||||
]) {
|
||||
if (tauriSetupSource.includes(forbiddenSetupSnippet)) {
|
||||
throw new Error(
|
||||
`AI game creator setup must not perform game-chat runtime navigation: ${forbiddenSetupSnippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const forbiddenSnippet of [
|
||||
'navigate_client_to_game_chat',
|
||||
'client.url()',
|
||||
'client.navigate(',
|
||||
]) {
|
||||
if (
|
||||
`${tauriHandlerSource}\n${tauriWindowSource}`.includes(forbiddenSnippet)
|
||||
) {
|
||||
throw new Error(
|
||||
`AI game creator game-chat startup must not navigate an initialized client WebView: ${forbiddenSnippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
/get_webview_window\s*\(\s*['"]client['"]\s*\)/u.test(
|
||||
`${tauriHandlerSource}\n${tauriWindowSource}`,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator game-chat startup must not look up the runtime client WebView',
|
||||
);
|
||||
}
|
||||
|
||||
for (const requiredSnippet of [
|
||||
'fn apply_game_chat_initial_window_url(',
|
||||
'.find(|window| window.label == "client")',
|
||||
'client.url = game_chat_window_url(',
|
||||
]) {
|
||||
if (
|
||||
!`${tauriHandlerSource}\n${tauriWindowSource}`.includes(requiredSnippet)
|
||||
) {
|
||||
throw new Error(
|
||||
`AI game creator game-chat initial WindowConfig guardrail drifted: ${requiredSnippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!tauriHandlerSource.includes('.build(tauri_context)') ||
|
||||
!tauriHandlerSource.includes('handle_game_creator_gui_run_event(&event)')
|
||||
@@ -1680,80 +1543,12 @@ if (
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!appEntrypointSource.includes(
|
||||
"import.meta.env.VITE_AGC_GAME_CHAT_ONLY === 'true'",
|
||||
) ||
|
||||
!appEntrypointSource.includes(
|
||||
"import.meta.env.DEV && initialSearchParams.has('game-chat')",
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator game-chat release must be compile-time fixed while preserving the dev query entry',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
packageConfig.scripts?.['build:game-chat-release'] !==
|
||||
'npm --prefix ../.. exec tauri -- build --config src-tauri/tauri.game-chat-release.conf.json --bundles nsis --features game-chat-release' ||
|
||||
rootPackageConfig.scripts?.['agc:build:game-chat-release'] !==
|
||||
'npm --prefix apps/ai-game-creator-shell run build:game-chat-release --'
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator game-chat release build commands must stay wired through the dedicated Tauri config',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
gameChatReleaseTauriConfig.productName !== 'Genarrative Game Chat' ||
|
||||
gameChatReleaseTauriConfig.version !== '0.1.1' ||
|
||||
gameChatReleaseTauriConfig.identifier === tauriConfig.identifier ||
|
||||
gameChatReleaseTauriConfig.build?.beforeBuildCommand !==
|
||||
'node scripts/build-game-chat-release.mjs' ||
|
||||
!gameChatReleaseTauriConfig.bundle?.targets?.includes('nsis')
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator game-chat release must keep version 0.1.1, its independent identity, frontend build, and NSIS target',
|
||||
);
|
||||
}
|
||||
|
||||
const gameChatReleaseWindows = gameChatReleaseTauriConfig.app?.windows ?? [];
|
||||
const gameChatReleaseClientWindow = gameChatReleaseWindows[0];
|
||||
if (
|
||||
gameChatReleaseWindows.length !== 1 ||
|
||||
gameChatReleaseClientWindow?.label !== 'client' ||
|
||||
gameChatReleaseClientWindow?.url !== 'index.html' ||
|
||||
gameChatReleaseClientWindow?.width !== 1280 ||
|
||||
gameChatReleaseClientWindow?.height !== 800 ||
|
||||
gameChatReleaseClientWindow?.minWidth !== 1280 ||
|
||||
gameChatReleaseClientWindow?.minHeight !== 720
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator game-chat client window must default to 1280x800 and stay at least 1280x720',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
tauriConfig.version !== '0.1.6' ||
|
||||
packageConfig.version !== '0.1.6' ||
|
||||
cargoPackageVersion !== '0.1.6'
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator standard release must remain version 0.1.6 while game-chat uses its dedicated version',
|
||||
);
|
||||
}
|
||||
|
||||
for (const requiredSnippet of [
|
||||
"'ai-game-creator-shell:typecheck'",
|
||||
"VITE_AGC_GAME_CHAT_ONLY: 'true'",
|
||||
"'--config'",
|
||||
"'vite.config.ts'",
|
||||
]) {
|
||||
if (!gameChatReleaseBuildSource.includes(requiredSnippet)) {
|
||||
throw new Error(
|
||||
`AI game creator game-chat release build guardrail drifted: ${requiredSnippet}`,
|
||||
);
|
||||
}
|
||||
throw new Error('AI game creator standard release must remain version 0.1.6');
|
||||
}
|
||||
|
||||
const devServerSource = fs.readFileSync(
|
||||
|
||||
@@ -18,29 +18,9 @@ const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
||||
const repoRoot = resolve(appRoot, '../..');
|
||||
const tauriCliPath = resolve(repoRoot, 'node_modules/@tauri-apps/cli/tauri.js');
|
||||
|
||||
function parseLauncherArguments(argv) {
|
||||
const args = [...argv];
|
||||
const gameChat = args[0] === '--game-chat';
|
||||
if (gameChat) {
|
||||
args.shift();
|
||||
}
|
||||
return { gameChat, args };
|
||||
}
|
||||
|
||||
function buildTauriArguments(argv, devUrl = readAgcDevEndpoint().url) {
|
||||
const { gameChat, args } = parseLauncherArguments(argv);
|
||||
const args = [...argv];
|
||||
const configOverride = JSON.stringify({ build: { devUrl } });
|
||||
if (gameChat) {
|
||||
return [
|
||||
'dev',
|
||||
'--config',
|
||||
configOverride,
|
||||
'--',
|
||||
'--',
|
||||
'--game-chat',
|
||||
...args,
|
||||
];
|
||||
}
|
||||
const separatorIndex = args.indexOf('--');
|
||||
if (separatorIndex < 0) {
|
||||
return ['dev', ...args, '--config', configOverride];
|
||||
@@ -144,7 +124,6 @@ function isDirectModuleExecution() {
|
||||
export {
|
||||
buildTauriArguments,
|
||||
isDirectModuleExecution,
|
||||
parseLauncherArguments,
|
||||
runTauriDev,
|
||||
spawnTauriCli,
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ publish = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
game-chat-release = []
|
||||
|
||||
[build-dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
@@ -75,25 +75,10 @@ struct VisualContractVariants {
|
||||
editor_unavailable: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum RoleOverlayRootSourceKind {
|
||||
SupervisorGameChat,
|
||||
}
|
||||
|
||||
impl RoleOverlayRootSourceKind {
|
||||
fn rust_variant(self) -> &'static str {
|
||||
match self {
|
||||
Self::SupervisorGameChat => "SupervisorGameChat",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct RoleOverlay {
|
||||
agent_id: String,
|
||||
root_source_kind: Option<RoleOverlayRootSourceKind>,
|
||||
sections: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -339,17 +324,10 @@ pub fn compile_manifest(manifest_path: &Path) -> Result<CompiledPromptBundle, St
|
||||
overlay.agent_id
|
||||
));
|
||||
}
|
||||
let overlay_key = (overlay.agent_id.clone(), overlay.root_source_kind);
|
||||
let overlay_key = overlay.agent_id.clone();
|
||||
if !overlays.insert(overlay_key) {
|
||||
return Err(format!("role overlay 重复:{}", overlay.agent_id));
|
||||
}
|
||||
if manifest.role_overlays.iter().any(|other| {
|
||||
other.agent_id == overlay.agent_id
|
||||
&& other.root_source_kind != overlay.root_source_kind
|
||||
&& (other.root_source_kind.is_none() || overlay.root_source_kind.is_none())
|
||||
}) {
|
||||
return Err(format!("role overlay selector 重叠:{}", overlay.agent_id));
|
||||
}
|
||||
validate_section_sequence(
|
||||
&format!("role overlay {}", overlay.agent_id),
|
||||
&overlay.sections,
|
||||
@@ -967,21 +945,11 @@ fn render_rust(manifest: &PromptBundleManifest, sections: &BTreeMap<String, Stri
|
||||
"pub(crate) const RUNTIME_PROMPT_VISUAL_NO_EDITOR_SECTION: &str = {};\n",
|
||||
rust_literal(&manifest.variants.visual_contract.editor_unavailable)
|
||||
));
|
||||
output.push_str(
|
||||
"#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub(crate) enum RuntimePromptRootSourceKind {\n SupervisorGameChat,\n}\n\n",
|
||||
);
|
||||
output.push_str(
|
||||
"pub(crate) const RUNTIME_PROMPT_ROLE_OVERLAYS: &[(&str, Option<RuntimePromptRootSourceKind>, &[&str])] = &[\n",
|
||||
);
|
||||
output.push_str("pub(crate) const RUNTIME_PROMPT_ROLE_OVERLAYS: &[(&str, &[&str])] = &[\n");
|
||||
for overlay in &manifest.role_overlays {
|
||||
let root_source_kind = overlay
|
||||
.root_source_kind
|
||||
.map(|kind| format!("Some(RuntimePromptRootSourceKind::{})", kind.rust_variant()))
|
||||
.unwrap_or_else(|| "None".to_string());
|
||||
output.push_str(&format!(
|
||||
" ({}, {}, &{}),\n",
|
||||
" ({}, &{}),\n",
|
||||
rust_literal(&overlay.agent_id),
|
||||
root_source_kind,
|
||||
render_string_slice(&overlay.sections)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
"isolatedAgentContract": "isolated-agent-contract.md",
|
||||
"platformDefault": "platform/default.md",
|
||||
"platformLinux": "platform/linux.md",
|
||||
"codePrototypeGameChat": "roles/code-prototype-game-chat.md",
|
||||
"projectSupervisorGameChatRouting": "supervisor/game-chat-routing.md",
|
||||
"providerIsolatedToolContract": "provider/isolated-tool-contract.md",
|
||||
"providerAutonomousRunProfile": "provider/autonomous-run-profile.md",
|
||||
"providerAutonomousSupervisorManifest": "provider/autonomous-supervisor-manifest.md",
|
||||
@@ -73,20 +71,6 @@
|
||||
}
|
||||
},
|
||||
"roleOverlays": [
|
||||
{
|
||||
"agentId": "project-supervisor",
|
||||
"rootSourceKind": "supervisorGameChat",
|
||||
"sections": [
|
||||
"projectSupervisorGameChatRouting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agentId": "code-prototype",
|
||||
"rootSourceKind": "supervisorGameChat",
|
||||
"sections": [
|
||||
"codePrototypeGameChat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agentId": "project-planning",
|
||||
"sections": [
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
本片段只适用于普通 GUI / CLI 的完整 `autonomous-game-build` manifest DAG;不得用于持久 source 为 `project-supervisor-game-chat` 的单主 route,后者不得用此修复建立固定首批委派。普通 DAG 的本次修复原生工具目录只保留 agent.delegate。必须在同一响应一次性建立完整首批合同,且只允许以下三个非 repair 委派,各出现一次:design-director 与 code-director 的 task 或 acceptanceCriteria 必须显式声明只读且不得修改项目,expectedArtifacts 必须为 [];art-director 必须是非只读规范图生成任务,expectedArtifacts 必须包含 assets/art-spec.png。三者都必须提供非空 task、1-8 条 acceptanceCriteria,并设置 repairOfDelegationId=null、runId=null。不得委派 code-prototype、quality-review、design-foundation、art-asset-plan 或其它底层 Agent,不得调用 agent.spawn_isolated,不得更新计划、读取、搜索、查询状态、修改项目或返回最终回复。不要解释,不要 markdown,不要代码围栏。
|
||||
本片段适用于 GUI / CLI 的完整 `autonomous-game-build` manifest DAG。普通 DAG 的本次修复原生工具目录只保留 agent.delegate。必须在同一响应一次性建立完整首批合同,且只允许以下三个非 repair 委派,各出现一次:design-director 与 code-director 的 task 或 acceptanceCriteria 必须显式声明只读且不得修改项目,expectedArtifacts 必须为 [];art-director 必须是非只读规范图生成任务,expectedArtifacts 必须包含 assets/art-spec.png。三者都必须提供非空 task、1-8 条 acceptanceCriteria,并设置 repairOfDelegationId=null、runId=null。不得委派 code-prototype、quality-review、design-foundation、art-asset-plan 或其它底层 Agent,不得调用 agent.spawn_isolated,不得更新计划、读取、搜索、查询状态、修改项目或返回最终回复。不要解释,不要 markdown,不要代码围栏。
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
本片段只适用于普通 GUI / CLI 的完整 `autonomous-game-build` manifest DAG,不适用于持久 source 为 `project-supervisor-game-chat` 的单主 route。任何根路径都必须先由 Supervisor 用 `agent.goal_contract` 冻结其对当前用户最终意图、约束、开放问题和动态验收图的理解;每个 required 节点的 requiredEvidence 必须逐项写成 `tool:<Runtime 工具名>`,由对应工具在当前 revision 的真实成功回执证明,不能写自然语言证据描述或拿无关成功动作替代。固定 manifest 和 game-chat route 只提供执行上下文,不能替代这项语义决定。普通 DAG 中,正式 manifest 任务图是 Goal Contract 之后的唯一首轮专业执行链:不得在 manifest 之前另行创建 code-prototype、quality-review、art-director、design-foundation 或 art-asset-plan 的首批 agent.delegate;这些角色会由 Runtime 按 manifest 依赖顺序调度。没有待认领的显式返工合同时也不得额外委派。game-chat 则由其专用路由提示决定:Supervisor 持久化意图后只启动 code-prototype,只有该主 Agent 的 asset.list 审计证实真实缺口时才可委派受限美术 child。请直接推进/观察适用于当前 root source 的任务路径;Runtime 会在你尝试收束时调度 ready task,并在任务图或动态验收图完成前阻止最终交付。
|
||||
本片段适用于 GUI / CLI 的完整 `autonomous-game-build` manifest DAG。任何根路径都必须先由 Supervisor 用 `agent.goal_contract` 冻结其对当前用户最终意图、约束、开放问题和动态验收图的理解;每个 required 节点的 requiredEvidence 必须逐项写成 `tool:<Runtime 工具名>`,由对应工具在当前 revision 的真实成功回执证明,不能写自然语言证据描述或拿无关成功动作替代。固定 manifest 只提供执行上下文,不能替代这项语义决定。正式 manifest 任务图是 Goal Contract 之后的唯一首轮专业执行链:不得在 manifest 之前另行创建 code-prototype、quality-review、art-director、design-foundation 或 art-asset-plan 的首批 agent.delegate;这些角色会由 Runtime 按 manifest 依赖顺序调度。没有待认领的显式返工合同时也不得额外委派。请直接推进/观察适用于当前 root source 的任务路径;Runtime 会在你尝试收束时调度 ready task,并在任务图或动态验收图完成前阻止最终交付。
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
game-chat 使用单主素材审计快车道。你是唯一的 `code-prototype` 主 Agent;必须先保护既有游戏语义和已完成产物。若本轮尚未取得成功的 `asset.list` observation,第一步必须调用 `asset.list` 核对当前正式资产、Canvas 登记和可复用状态;不得仅凭用户措辞、关键词或旧摘要判断已有素材是否可用。审计后必须用 `agent.route_manifest` 持久化审计结论:完整覆盖使用 `use-existing-art, missingAssetSlots=[]`,真实缺口使用 `generate-missing-art` 且只列出实际缺失槽位;不得把 Supervisor 的用户 intent 改写成重生成路线。审计证明核心规范图、透明 spritesheet、切片清单或四类切片存在真实缺口时,才可一次委派对应的 `art-director` 或 `art-asset-plan` 补齐;任务必须精确说明缺失项和验收产物,child 只能写 `assets/**`,不得修改 `game/**`、删除/补丁程序文件或替你接入游戏。一次最多保留一个活跃美术委派,收到并认领其回执后继续同一 Run;不得在美术已齐或仅凭“UI 没有用素材”等描述时生成、委派或扣费。Supervisor 持久化的整体重做意图也只是上下文,不能改变这条审计门或授权整套美术强制重生成。
|
||||
|
||||
在成功审计并取得可用素材、或已认领必要美术回执后,才读取游戏入口:如果 observation 尚未包含 game/index.html 的当前正文与摘要,本响应下一步必须调用 file.read(path=game/index.html),不得猜测入口仍是初始化占位。只有文件缺失或正文与初始化页面一致、且当前 run 尚未写入项目时,才允许用一次 file.write 生成完整、自包含、可运行的 game/index.html;检测到非占位入口、已有可玩实现、当前 revision 已验证,或当前 run 已发生修改时,禁止整文件 file.write,必须保留原玩法,优先执行静态检查/试玩,确需修复时只能依据已读取的精确原文做最小 file.patch。不得把“继续”、continue 或其它纯续跑词当作游戏主题;缺少可继承的具体目标时必须失败关闭,不得另造新玩法。
|
||||
|
||||
HTML 必须满足固定试玩合同,包含真实 Canvas 游戏循环、键盘与触控输入、开始、主要操作、重开、胜负状态和移动端布局。assets/art-spec.png 只能作为视觉规范与派生参考,不得在运行时加载、铺作背景或裁切实体。必须等待已登记且透明有效的 assets/art-spritesheet.png 与 assets/art-spritesheet-slices/manifest.json,从切片清单按 player、blocks-and-targets、obstacles-and-scene、feedback-effects 四种 usage 加载四个不同的独立透明素材,并在活动 Canvas 中通过 drawImage 绘制对应核心玩家、方块/目标、障碍/场景和反馈;不得猜测整张图集是等分网格,不得把整张图集作为 img、CSS background 或完整 drawImage 展示,也不得以纯代码几何替代核心实体。图集、四类切片或其可见使用任一缺失时不得交付。完成最后一处项目写入后,由你在同一 Run 执行 `game.static_smoke`,再执行 desktop 与 mobile `preview.validate`;不得把接入、检查或试玩交回固定验证节点。首次实现或最小修复后不要继续扩写功能。
|
||||
@@ -1,5 +0,0 @@
|
||||
game-chat 的固定关键词和资产探测只作为 `advisoryOnly=true` 的补充上下文,不能直接选择、重置或跳过任务节点。当前根 Run 尚无 Goal Contract 时,你必须先自行理解用户真正要做的事,并把本轮唯一动作设为 `agent.goal_contract`:完整区分最终 outcome、不可协商约束、偏好、禁止假设、开放问题和本次任务动态生成的 acceptance nodes;不得照抄固定信号、玩法模板或素材类型代替理解。每个 required 节点使用稳定 criterionId;requiredEvidence 必须逐项写成 `tool:<Runtime 工具名>`,声明真正能够证明该标准的工具回执,不能写自然语言证据描述,也不能用无关成功动作自证;只有用户目标确实不要求的标准才可标为 optional。
|
||||
|
||||
Goal Contract 冻结后,当前根 Run 尚无工作流决策时,才把本轮唯一动作设为 `agent.route_manifest`。`strategy=audit-existing-first, missingAssetSlots=[]` 是防止未经审计生成或重做素材的执行安全上下文;`intentSummary` 必须忠实概括已冻结 Goal Contract,不得照抄固定信号代替理解。这个动作只记录执行路由,不代表生成许可,也不能把整体重做解释成整套美术的强制重生成。不得根据关键词自行声称已有资产完整,也不得在 Goal Contract 和结构化路由前委派或调度美术 Agent。
|
||||
|
||||
这一步只持久化用户意图和单主路径,不审计资产、不代替 `code-prototype` 判断缺口,也不得创建 `design-director`、`code-director`、`art-director`、`art-asset-plan`、试玩或其它固定首波节点。持久路由后 Runtime 只启动同一根 Run 的 `code-prototype`。它先以 `asset.list` 取得权威资产、Canvas 登记和可复用状态;只有该审计证明 `art-spec` 或核心 spritesheet 确实缺失,才可由该主 Agent 向对应美术角色发起一次受限的 durable 委派。美术 child 仅可写 `assets/**`,回执由同一 `code-prototype` 认领后恢复其原 Run 接入、静态检查和桌面/移动试玩。完整覆盖时不得生成、委派或扣费;整体重做意图同样必须经过这次审计,不能绕过资产复用或授权整套美术重生成。Runtime 负责校验根/父子身份、路径、Canvas 登记、合同指纹、缺口一致性和写入范围,但不替你解释用户意图或生成美术。
|
||||
@@ -273,19 +273,13 @@ fn game_creator_codex_app_server_error_detail_indicates_auth_failure(
|
||||
fn game_creator_codex_app_server_failed_turn_error(
|
||||
turn: &serde_json::Value,
|
||||
) -> platform_llm::LlmError {
|
||||
let Some(error) = turn
|
||||
.get("error")
|
||||
.filter(|error| !error.is_null())
|
||||
else {
|
||||
let Some(error) = turn.get("error").filter(|error| !error.is_null()) else {
|
||||
return game_creator_codex_app_server_error_kind("other");
|
||||
};
|
||||
if game_creator_codex_app_server_error_detail_indicates_auth_failure(error) {
|
||||
return game_creator_codex_app_server_error_kind("unauthorized");
|
||||
}
|
||||
let Some(info) = error
|
||||
.get("codexErrorInfo")
|
||||
.filter(|info| !info.is_null())
|
||||
else {
|
||||
let Some(info) = error.get("codexErrorInfo").filter(|info| !info.is_null()) else {
|
||||
return game_creator_codex_app_server_error_kind("other");
|
||||
};
|
||||
if let Some(kind) = info.as_str() {
|
||||
|
||||
@@ -2051,7 +2051,7 @@ fn direct_taonier_strict_art_package_is_valid(root: &Path) -> bool {
|
||||
if direct_registered_taonier_slice_paths(root) != expected_slice_paths {
|
||||
return false;
|
||||
}
|
||||
let expected_art_manifest = game_chat_fast_path_art_manifest_content();
|
||||
let expected_art_manifest = art_manifest_content();
|
||||
if std::fs::read(root.join("assets/manifest.art.json"))
|
||||
.ok()
|
||||
.as_deref()
|
||||
@@ -2105,7 +2105,7 @@ fn direct_registered_taonier_slice_paths(root: &Path) -> Vec<String> {
|
||||
let Some(source_resource_id) = spritesheet.source.resource_id.as_deref() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Ok(validated_slices) = game_chat_fast_path_validated_art_slices(root) else {
|
||||
let Ok(validated_slices) = validated_art_slices(root) else {
|
||||
return Vec::new();
|
||||
};
|
||||
if validated_slices.len() != 4 {
|
||||
@@ -5455,7 +5455,7 @@ mod tests {
|
||||
register_direct_taonier_art_slice_entries_fixture(root.path());
|
||||
std::fs::write(
|
||||
root.path().join("assets/manifest.art.json"),
|
||||
game_chat_fast_path_art_manifest_content(),
|
||||
art_manifest_content(),
|
||||
)
|
||||
.expect("old art manifest");
|
||||
let old_spritesheet = std::fs::read(root.path().join(DIRECT_CODEX_SPRITESHEET_ASSET_PATH))
|
||||
@@ -6672,7 +6672,7 @@ mod tests {
|
||||
register_direct_taonier_art_slice_entries_fixture(root);
|
||||
std::fs::write(
|
||||
root.join("assets/manifest.art.json"),
|
||||
game_chat_fast_path_art_manifest_content(),
|
||||
art_manifest_content(),
|
||||
)
|
||||
.expect("art manifest");
|
||||
}
|
||||
@@ -6857,7 +6857,7 @@ mod tests {
|
||||
.expect("replacement receipt");
|
||||
std::fs::write(
|
||||
root.join("assets/manifest.art.json"),
|
||||
game_chat_fast_path_art_manifest_content(),
|
||||
art_manifest_content(),
|
||||
)
|
||||
.expect("replacement art manifest");
|
||||
mutate_manifest_at(root, |manifest| {
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) use canvas_generation::{
|
||||
ExternalGenerationInitialResponse,
|
||||
};
|
||||
pub(in crate::agent) use canvas_generation::{
|
||||
commit_prepared_platform_art_asset_at, commit_prepared_platform_art_asset_strict_slices_at,
|
||||
commit_prepared_platform_art_asset_at,
|
||||
generate_platform_art_asset_with_retained_runtime_options_at,
|
||||
generate_platform_art_asset_with_runtime_options_at,
|
||||
platform_art_generation_error_result_unknown, register_existing_platform_art_slices_at,
|
||||
|
||||
@@ -6234,7 +6234,7 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
) -> Result<(), String> {
|
||||
if slices.len() != 4 {
|
||||
return Err(format!(
|
||||
"game-chat 图集必须恰好包含 4 个独立切片,实际为 {} 个",
|
||||
"strict spritesheet 图集必须恰好包含 4 个独立切片,实际为 {} 个",
|
||||
slices.len()
|
||||
));
|
||||
}
|
||||
@@ -6242,41 +6242,45 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
"game-chat 图集必须包含稳定的 Canvas resourceId,已在本地落盘前拒绝提交".to_string()
|
||||
"strict spritesheet 图集必须包含稳定的 Canvas resourceId,已在本地落盘前拒绝提交"
|
||||
.to_string()
|
||||
})?;
|
||||
let task_id = task_id
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
"game-chat 图集必须包含稳定的 External Editor taskId,已拒绝提交".to_string()
|
||||
"strict spritesheet 图集必须包含稳定的 External Editor taskId,已拒绝提交".to_string()
|
||||
})?;
|
||||
let asset_object_id = asset_object_id
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
"game-chat 图集必须包含稳定的 Canvas assetObjectId,已拒绝提交".to_string()
|
||||
"strict spritesheet 图集必须包含稳定的 Canvas assetObjectId,已拒绝提交".to_string()
|
||||
})?;
|
||||
if canvas_project_id.map(str::trim) != Some(canvas_context.project_id.as_str()) {
|
||||
return Err("game-chat 图集响应不属于当前请求的 Canvas projectId,已拒绝提交".to_string());
|
||||
return Err(
|
||||
"strict spritesheet 图集响应不属于当前请求的 Canvas projectId,已拒绝提交".to_string(),
|
||||
);
|
||||
}
|
||||
if generation_route != "/api/external/v1/editor/icon-spritesheets/generations"
|
||||
|| generation_kind != "icon-spritesheet"
|
||||
{
|
||||
return Err("game-chat 图集生成 route/kind 与严格图集合同不一致".to_string());
|
||||
return Err("strict spritesheet 图集生成 route/kind 与严格图集合同不一致".to_string());
|
||||
}
|
||||
if spritesheet_slice_layout.map(str::trim) != Some("grid-2x2") {
|
||||
return Err(
|
||||
"game-chat 图集必须由 External Editor 以 grid-2x2 固定切片合同生成".to_string(),
|
||||
"strict spritesheet 图集必须由 External Editor 以 grid-2x2 固定切片合同生成"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
if reference_resource_ids.len() != 1
|
||||
|| reference_resource_ids[0].trim().is_empty()
|
||||
|| reference_resource_ids[0].trim() == resource_id
|
||||
{
|
||||
return Err("game-chat 图集必须绑定唯一且独立的 art-spec resourceId".to_string());
|
||||
return Err("strict spritesheet 图集必须绑定唯一且独立的 art-spec resourceId".to_string());
|
||||
}
|
||||
if !has_transparent_pixels || !has_visible_pixels {
|
||||
return Err("game-chat 图集必须同时包含真实透明像素和非透明可见内容".to_string());
|
||||
return Err("strict spritesheet 图集必须同时包含真实透明像素和非透明可见内容".to_string());
|
||||
}
|
||||
let mut resource_ids = std::collections::HashSet::with_capacity(slices.len());
|
||||
let mut asset_object_ids = std::collections::HashSet::with_capacity(slices.len());
|
||||
@@ -6284,13 +6288,13 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
for (index, slice) in slices.iter().enumerate() {
|
||||
if !slice.has_transparent_pixels {
|
||||
return Err(format!(
|
||||
"game-chat 图集第 {} 个切片没有任何透明像素,不是可独立贴图",
|
||||
"strict spritesheet 图集第 {} 个切片没有任何透明像素,不是可独立贴图",
|
||||
index + 1
|
||||
));
|
||||
}
|
||||
if !slice.has_visible_pixels {
|
||||
return Err(format!(
|
||||
"game-chat 图集第 {} 个切片全透明且没有可见内容",
|
||||
"strict spritesheet 图集第 {} 个切片全透明且没有可见内容",
|
||||
index + 1
|
||||
));
|
||||
}
|
||||
@@ -6301,15 +6305,17 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"game-chat 图集第 {} 个切片缺少稳定 Canvas resourceId",
|
||||
"strict spritesheet 图集第 {} 个切片缺少稳定 Canvas resourceId",
|
||||
index + 1
|
||||
)
|
||||
})?;
|
||||
if !resource_ids.insert(slice_resource_id) {
|
||||
return Err("game-chat 图集切片的稳定 Canvas resourceId 重复".to_string());
|
||||
return Err("strict spritesheet 图集切片的稳定 Canvas resourceId 重复".to_string());
|
||||
}
|
||||
if slice_resource_id == resource_id {
|
||||
return Err("game-chat 图集切片 resourceId 不能复用整图 resourceId".to_string());
|
||||
return Err(
|
||||
"strict spritesheet 图集切片 resourceId 不能复用整图 resourceId".to_string(),
|
||||
);
|
||||
}
|
||||
let slice_asset_object_id = slice
|
||||
.asset_object_id
|
||||
@@ -6318,51 +6324,54 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"game-chat 图集第 {} 个切片缺少稳定 Canvas assetObjectId",
|
||||
"strict spritesheet 图集第 {} 个切片缺少稳定 Canvas assetObjectId",
|
||||
index + 1
|
||||
)
|
||||
})?;
|
||||
if !asset_object_ids.insert(slice_asset_object_id) {
|
||||
return Err("game-chat 图集切片的稳定 Canvas assetObjectId 重复".to_string());
|
||||
return Err("strict spritesheet 图集切片的稳定 Canvas assetObjectId 重复".to_string());
|
||||
}
|
||||
if slice_asset_object_id == asset_object_id {
|
||||
return Err("game-chat 图集切片 assetObjectId 不能复用整图 assetObjectId".to_string());
|
||||
return Err(
|
||||
"strict spritesheet 图集切片 assetObjectId 不能复用整图 assetObjectId".to_string(),
|
||||
);
|
||||
}
|
||||
if slice.canvas_project_id.as_deref().map(str::trim)
|
||||
!= Some(canvas_context.project_id.as_str())
|
||||
{
|
||||
return Err(format!(
|
||||
"game-chat 图集第 {} 个切片不属于当前请求的 Canvas projectId",
|
||||
"strict spritesheet 图集第 {} 个切片不属于当前请求的 Canvas projectId",
|
||||
index + 1
|
||||
));
|
||||
}
|
||||
if slice.task_id.as_deref().map(str::trim) != Some(task_id) {
|
||||
return Err(format!(
|
||||
"game-chat 图集第 {} 个切片未绑定当前 External Editor taskId",
|
||||
"strict spritesheet 图集第 {} 个切片未绑定当前 External Editor taskId",
|
||||
index + 1
|
||||
));
|
||||
}
|
||||
if slice.source_resource_id.as_deref().map(str::trim) != Some(resource_id) {
|
||||
return Err(format!(
|
||||
"game-chat 图集第 {} 个切片缺少与整图一致的 sourceResourceId",
|
||||
"strict spritesheet 图集第 {} 个切片缺少与整图一致的 sourceResourceId",
|
||||
index + 1
|
||||
));
|
||||
}
|
||||
let validated = validate_platform_art_png_bytes_with_limits(
|
||||
&slice.download.bytes,
|
||||
&format!("game-chat 图集第 {} 个切片", index + 1),
|
||||
&format!("strict spritesheet 图集第 {} 个切片", index + 1),
|
||||
)?;
|
||||
if validated.content_sha256 != slice.content_sha256
|
||||
|| validated.pixel_sha256 != slice.pixel_sha256
|
||||
{
|
||||
return Err(format!(
|
||||
"game-chat 图集第 {} 个切片内容摘要与待写入字节不一致",
|
||||
"strict spritesheet 图集第 {} 个切片内容摘要与待写入字节不一致",
|
||||
index + 1
|
||||
));
|
||||
}
|
||||
if !pixel_sha256s.insert(slice.pixel_sha256.as_str()) {
|
||||
return Err(
|
||||
"game-chat 图集切片规范像素摘要重复,无法证明四类素材视觉上相互独立".to_string(),
|
||||
"strict spritesheet 图集切片规范像素摘要重复,无法证明四类素材视觉上相互独立"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6370,7 +6379,7 @@ fn validate_strict_platform_art_spritesheet_contract(
|
||||
}
|
||||
|
||||
fn strict_game_art_manifest_bytes() -> Vec<u8> {
|
||||
game_chat_fast_path_art_manifest_content().into_bytes()
|
||||
art_manifest_content().into_bytes()
|
||||
}
|
||||
|
||||
fn strict_game_art_contract_receipt_bytes(
|
||||
@@ -6733,7 +6742,7 @@ pub(in crate::agent) fn register_existing_platform_art_slices_at(
|
||||
}
|
||||
let receipt: serde_json::Value = serde_json::from_slice(&receipt_bytes)
|
||||
.map_err(|error| format!("解析旧项目平台图集私有回执失败:{error}"))?;
|
||||
let validated_slices = game_chat_fast_path_validated_art_slices(root)?;
|
||||
let validated_slices = validated_art_slices(root)?;
|
||||
let slices = receipt
|
||||
.get("slices")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
@@ -11337,7 +11346,7 @@ mod canvas_generation_tests {
|
||||
&replacement_options(),
|
||||
|_| Ok(()),
|
||||
)
|
||||
.expect_err("strict game-chat commit must require exactly four slices");
|
||||
.expect_err("strict spritesheet commit must require exactly four slices");
|
||||
|
||||
assert!(error.contains("恰好包含 4 个独立切片"));
|
||||
assert_eq!(fs::read(path).expect("read preserved sheet"), b"old-image");
|
||||
@@ -11365,7 +11374,7 @@ mod canvas_generation_tests {
|
||||
commit_prepared_platform_art_asset_strict_slices_at(root, prepared, &options, |_| {
|
||||
Ok(())
|
||||
})
|
||||
.expect_err("strict game-chat commit must require a stable resource id");
|
||||
.expect_err("strict spritesheet commit must require a stable resource id");
|
||||
|
||||
assert!(error.contains("resourceId"));
|
||||
assert!(!sheet_path.exists());
|
||||
@@ -11391,7 +11400,7 @@ mod canvas_generation_tests {
|
||||
commit_prepared_platform_art_asset_strict_slices_at(root, prepared, &options, |_| {
|
||||
Ok(())
|
||||
})
|
||||
.expect_err("strict game-chat commit must require a stable main assetObjectId");
|
||||
.expect_err("strict spritesheet commit must require a stable main assetObjectId");
|
||||
|
||||
assert!(error.contains("assetObjectId"));
|
||||
assert!(!sheet_path.exists());
|
||||
@@ -11417,7 +11426,7 @@ mod canvas_generation_tests {
|
||||
commit_prepared_platform_art_asset_strict_slices_at(root, prepared, &options, |_| {
|
||||
Ok(())
|
||||
})
|
||||
.expect_err("strict game-chat commit must require every slice assetObjectId");
|
||||
.expect_err("strict spritesheet commit must require every slice assetObjectId");
|
||||
|
||||
assert!(error.contains("assetObjectId"));
|
||||
assert!(!sheet_path.exists());
|
||||
|
||||
@@ -575,7 +575,7 @@ fn game_creator_project_planning_tool_plan_system_prompt() -> String {
|
||||
/// art-director/design-foundation/art-asset-plan/code-prototype 的视觉产物
|
||||
/// 合同)和 `supervisorIntro`(要求「自行查看静态角色目录,选择最匹配的不同
|
||||
/// 专业 Agent」)——两段都在暗示存在可并行委派的专业组,而 plan 根 run 没有
|
||||
/// 这个能力。除 plan 根以外的一切 source(gui/cli/game-chat/未知)都必须逐字
|
||||
/// 这个能力。除 plan 根以外的一切 source(gui/cli/未知)都必须逐字
|
||||
/// 保留既有合成结果,不能被这里的分支误伤。
|
||||
fn game_creator_project_supervisor_tool_plan_prompt(
|
||||
prompt: &str,
|
||||
@@ -681,22 +681,12 @@ fn runtime_prompt_platform_section_id(linux: bool) -> &'static str {
|
||||
|
||||
pub(crate) fn game_creator_agent_runtime_role_overlay_prompt(
|
||||
agent_id: &str,
|
||||
root_source: Option<&str>,
|
||||
_root_source: Option<&str>,
|
||||
) -> String {
|
||||
let root_source_kind = match root_source.map(str::trim) {
|
||||
Some(AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE) => {
|
||||
Some(RuntimePromptRootSourceKind::SupervisorGameChat)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let sections = RUNTIME_PROMPT_ROLE_OVERLAYS
|
||||
.iter()
|
||||
.filter(|(overlay_agent_id, overlay_root_source_kind, _)| {
|
||||
*overlay_agent_id == agent_id
|
||||
&& overlay_root_source_kind
|
||||
.is_none_or(|required| Some(required) == root_source_kind)
|
||||
})
|
||||
.flat_map(|(_, _, sections)| sections.iter().copied())
|
||||
.filter(|(overlay_agent_id, _)| *overlay_agent_id == agent_id)
|
||||
.flat_map(|(_, sections)| sections.iter().copied())
|
||||
.map(required_runtime_prompt_section)
|
||||
.collect::<Vec<_>>();
|
||||
render_runtime_prompt_sections(§ions)
|
||||
@@ -789,8 +779,6 @@ mod tests {
|
||||
"isolatedAgentContract",
|
||||
"platformDefault",
|
||||
"platformLinux",
|
||||
"codePrototypeGameChat",
|
||||
"projectSupervisorGameChatRouting",
|
||||
"providerIsolatedToolContract",
|
||||
"providerAutonomousRunProfile",
|
||||
"providerAutonomousSupervisorManifest",
|
||||
@@ -814,34 +802,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_prompt_game_chat_routing_overlays_keep_supervisor_authoritative() {
|
||||
let supervisor = game_creator_agent_runtime_role_overlay_prompt(
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
Some(AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE),
|
||||
);
|
||||
assert!(supervisor.contains("固定关键词和资产探测只作为"));
|
||||
assert!(supervisor.contains("必须先自行理解用户真正要做的事"));
|
||||
assert!(supervisor.contains("intentSummary"));
|
||||
assert!(supervisor.contains("agent.route_manifest"));
|
||||
assert!(!supervisor.contains("第一步必须调用 `asset.list`"));
|
||||
|
||||
let code_prototype = game_creator_agent_runtime_role_overlay_prompt(
|
||||
"code-prototype",
|
||||
Some(AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE),
|
||||
);
|
||||
assert!(code_prototype.contains("第一步必须调用 `asset.list`"));
|
||||
assert!(code_prototype.contains("不得仅凭用户措辞"));
|
||||
assert!(code_prototype.contains("child 只能写 `assets/**`"));
|
||||
|
||||
assert!(game_creator_agent_runtime_role_overlay_prompt(
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
None,
|
||||
)
|
||||
.is_empty());
|
||||
assert!(game_creator_agent_runtime_role_overlay_prompt("code-prototype", None).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_prompt_provider_graph_fragments_come_from_the_manifest() {
|
||||
for (section_id, expected) in [
|
||||
@@ -855,7 +815,7 @@ mod tests {
|
||||
),
|
||||
(
|
||||
RUNTIME_PROMPT_PROVIDER_AUTONOMOUS_SUPERVISOR_MANIFEST_SECTION,
|
||||
"固定 manifest 和 game-chat route 只提供执行上下文",
|
||||
"固定 manifest 只提供执行上下文",
|
||||
),
|
||||
(
|
||||
RUNTIME_PROMPT_PROVIDER_INITIAL_COLLABORATION_REPAIR_SECTION,
|
||||
@@ -927,7 +887,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Supervisor 的核心行为准则是「用验收标准和预期产物把边界清晰的任务委派
|
||||
/// 出去」(`identity-contract.md`),这在自主构建链路上正确,搬到策划链路
|
||||
/// 上却恰好碾过决策卡协议:把边界定清楚等于把用户没说的都替他决定掉。
|
||||
@@ -1695,7 +1654,7 @@ mod tests {
|
||||
),
|
||||
(
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE,
|
||||
AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE,
|
||||
),
|
||||
(
|
||||
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
|
||||
@@ -1729,7 +1688,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// gui/cli/game-chat 等非 plan source 的 Supervisor system prompt 必须与
|
||||
/// gui/cli 等非 plan source 的 Supervisor system prompt 必须与
|
||||
/// M1A-4 之前逐字相同:直接用既有 section 常量手工拼出改动前的合成结果,
|
||||
/// 逐字比对,防止上面的 plan 根分支误伤这条现役路径。
|
||||
#[test]
|
||||
@@ -1752,7 +1711,6 @@ mod tests {
|
||||
for source in [
|
||||
AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE,
|
||||
AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE,
|
||||
AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE,
|
||||
"",
|
||||
"project-supervisor-plan-forged",
|
||||
] {
|
||||
|
||||
@@ -1137,7 +1137,6 @@ pub(in crate::agent) fn agent_runtime_public_action_input_summary(
|
||||
| "agent.message"
|
||||
| "agent.delegate"
|
||||
| "agent.schedule_ready"
|
||||
| "agent.route_manifest"
|
||||
| "agent.action_history"
|
||||
| "agent.run_status"
|
||||
| GAME_CREATOR_MCP_CALL_TOOL
|
||||
@@ -1633,21 +1632,6 @@ pub(crate) fn agent_runtime_tool_action_input_summary(
|
||||
.and_then(|value| value.as_u64())
|
||||
.unwrap_or(1)
|
||||
),
|
||||
"agent.route_manifest" => format!(
|
||||
"strategy={} · missingAssetSlots={}",
|
||||
text(&["strategy"]),
|
||||
input
|
||||
.get("missingAssetSlots")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
.map(|slots| {
|
||||
slots
|
||||
.iter()
|
||||
.filter_map(serde_json::Value::as_str)
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
})
|
||||
.unwrap_or_default()
|
||||
),
|
||||
"agent.action_history" => format!(
|
||||
"runId={} · actionId={} · tool={} · status={} · limit={}",
|
||||
text(&["runId", "run_id"]),
|
||||
|
||||
@@ -89,15 +89,6 @@ pub(crate) async fn execute_game_creator_agent_runtime_tool_action_with_pending_
|
||||
{
|
||||
return blocker;
|
||||
}
|
||||
if let Some(blocker) = game_chat_delegated_art_agent_input_mutation_block(
|
||||
root,
|
||||
agent_id,
|
||||
run_id,
|
||||
tool,
|
||||
&action.input,
|
||||
) {
|
||||
return blocker;
|
||||
}
|
||||
if let Some(blocker) =
|
||||
supervisor_orchestrator_mcp_mutation_block_at(root, agent_id, run_id, action).await
|
||||
{
|
||||
@@ -476,9 +467,6 @@ pub(crate) async fn execute_game_creator_agent_runtime_tool_action_with_pending_
|
||||
"agent.schedule_ready" => {
|
||||
observe_agent_runtime_schedule_ready_tasks(root, agent_id, run_id, &action.input)
|
||||
}
|
||||
"agent.route_manifest" => {
|
||||
observe_agent_runtime_route_manifest(root, agent_id, run_id, &action.input)
|
||||
}
|
||||
"agent.action_history" => observe_agent_runtime_project_snapshot_with_lock(
|
||||
root,
|
||||
agent_id,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user