Merge remote-tracking branch 'origin/master' into codex/docs-current-master
Project CI / Repository checks (push) Successful in 12m13s
Project CI / Frontend tests (push) Successful in 13m20s
Project CI / Backend tests (push) Successful in 23m7s
Project CI / Native shell tests (push) Failing after 31m49s

This commit is contained in:
2026-08-26 10:12:00 +08:00
110 changed files with 4694 additions and 10547 deletions
@@ -16,7 +16,6 @@
"retryBackoffMs": 500
},
"agentLlm": {},
"mcpServers": {},
"planning": {
"capabilityEnabled": true
}
@@ -73,8 +73,7 @@ export function isCatalogBoundToolPlanFunctionName(name, protocol) {
if (protocol !== 'native_runtime_tools') return false;
return (
['update_agent_plan', 'respond_to_user'].includes(name) ||
name.startsWith('runtime_tool_') ||
name.startsWith('mcp_tool_')
name.startsWith('runtime_tool_')
);
}
@@ -65,14 +65,6 @@ import {
isGoalRuntimeSuite,
runGoalRuntimeE2e,
} from './suites/goal.mjs';
import {
collectPartialMcpEvidence,
emptyMcpEvidence,
isMcpRuntimeSuite,
mcpPrivateValues,
runMcpRuntimeE2e,
stopMcpHttpFixture,
} from './suites/mcp.mjs';
import {
collectPartialParallelReadEvidence,
emptyParallelReadEvidence,
@@ -183,7 +175,6 @@ if (selfTestRequested) {
if (isContextCompactionSuite()) {
state.evidence = emptyContextCompactionEvidence();
}
if (isMcpRuntimeSuite()) state.evidence = emptyMcpEvidence();
if (isUserInputRuntimeSuite()) state.evidence = emptyUserInputEvidence();
if (isScopedAgentsSuite()) state.evidence = emptyScopedAgentsEvidence();
if (isProjectSkillSuite()) state.evidence = emptyProjectSkillEvidence();
@@ -201,7 +192,6 @@ if (selfTestRequested) {
if (
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -242,8 +232,6 @@ if (selfTestRequested) {
await runWebSearchE2e();
} else if (isContextCompactionSuite()) {
await runContextCompactionE2e();
} else if (isMcpRuntimeSuite()) {
await runMcpRuntimeE2e();
} else if (isUserInputRuntimeSuite()) {
await runUserInputRuntimeE2e();
} else if (isScopedAgentsSuite()) {
@@ -343,15 +331,6 @@ if (selfTestRequested) {
recordError('interactive-cli-cleanup-failed', error);
}
}
if (isMcpRuntimeSuite() && state.mcp.httpFixture) {
try {
await stopMcpHttpFixture();
state.evidence.httpFixtureStopped = true;
} catch (error) {
state.status = 'FAIL';
recordError('mcp-http-fixture-cleanup-failed', error);
}
}
if (
isSupervisorAutonomousPlayableLaneDefenseSuite() &&
state.isolatedRunner.appDataDir
@@ -521,28 +500,6 @@ if (selfTestRequested) {
state.status = 'FAIL';
recordError('web-search-formal-config-cli-call-detected');
}
} else if (isMcpRuntimeSuite()) {
state.evidence.mcpRunnerStopped = state.isolatedRunner.stopped;
state.evidence.mcpAppDataCleanupPerformed =
state.isolatedRunner.cleanupPerformed;
state.evidence.mcpRunnerKillMethod = killMethod;
state.evidence.mcpRunnerPidfdClaimCount =
state.isolatedRunner.pidfdClaimCount;
state.evidence.mcpRunnerPidfdSignalCount =
state.isolatedRunner.pidfdSignalCount;
state.evidence.formalConfigCliCallCount =
state.isolatedRunner.sourceConfigCliCallCount;
state.evidence.sourceRunnerEndpointUnchanged =
state.isolatedRunner.sourceRunnerEndpointUnchanged;
state.evidence.sourceConfigReplicaCount =
state.isolatedRunner.configLinks.length;
state.evidence.sourceConfigReplicasVerified =
state.isolatedRunner.sourceConfigLinksVerified;
state.evidence.isolatedAppDataUsed = true;
if (state.isolatedRunner.sourceConfigCliCallCount > 0) {
state.status = 'FAIL';
recordError('mcp-formal-config-cli-call-detected');
}
} else if (isUserInputRuntimeSuite()) {
state.evidence.userInputRunnerStopped = state.isolatedRunner.stopped;
state.evidence.userInputAppDataCleanupPerformed =
@@ -879,16 +836,6 @@ if (selfTestRequested) {
recordError('context-compaction-partial-evidence-read-failed', error);
}
}
if (isMcpRuntimeSuite() && state.projectRoot && state.status !== 'PASS') {
try {
state.evidence = {
...state.evidence,
...(await collectPartialMcpEvidence()),
};
} catch (error) {
recordError('mcp-partial-evidence-read-failed', error);
}
}
if (
isUserInputRuntimeSuite() &&
state.projectRoot &&
@@ -1115,19 +1062,6 @@ if (selfTestRequested) {
report = JSON.stringify(summary, null, 2);
}
}
if (isMcpRuntimeSuite()) {
state.mcp.reportLeakCount = countExactSecrets(
Buffer.from(report),
mcpPrivateValues(),
);
state.evidence.mcpReportLeakCount = state.mcp.reportLeakCount;
if (state.mcp.reportLeakCount > 0) {
state.status = 'FAIL';
recordError('mcp-private-context-report-leak-detected');
summary = buildSummary();
report = JSON.stringify(summary, null, 2);
}
}
if (isUserInputRuntimeSuite()) {
state.userInput.reportLeakCount = countExactSecrets(
Buffer.from(report),
@@ -1230,7 +1164,6 @@ if (selfTestRequested) {
if (
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -1278,9 +1211,6 @@ if (selfTestRequested) {
const remainingWebSearchReportLeakCount = isWebSearchSuite()
? countExactSecrets(Buffer.from(report), webSearchPrivateLeakValues())
: 0;
const remainingMcpReportLeakCount = isMcpRuntimeSuite()
? countExactSecrets(Buffer.from(report), mcpPrivateValues())
: 0;
const remainingUserInputReportLeakCount = isUserInputRuntimeSuite()
? countExactSecrets(
Buffer.from(report),
@@ -1316,7 +1246,6 @@ if (selfTestRequested) {
const remainingFormalConfigPathReportLeakCount =
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -1330,7 +1259,6 @@ if (selfTestRequested) {
remainingProjectPathReportLeakCount > 0 ||
remainingResponseStreamReportLeakCount > 0 ||
remainingWebSearchReportLeakCount > 0 ||
remainingMcpReportLeakCount > 0 ||
remainingUserInputReportLeakCount > 0 ||
remainingScopedAgentsReportLeakCount > 0 ||
remainingProjectSkillReportLeakCount > 0 ||
@@ -1345,23 +1273,21 @@ if (selfTestRequested) {
? 'disposable-project-path-report-redaction-required'
: remainingResponseStreamReportLeakCount > 0
? 'response-stream-report-redaction-required'
: remainingMcpReportLeakCount > 0
? 'mcp-report-redaction-required'
: remainingUserInputReportLeakCount > 0
? 'user-input-report-redaction-required'
: remainingScopedAgentsReportLeakCount > 0
? 'scoped-agents-report-redaction-required'
: remainingProjectSkillReportLeakCount > 0
? 'project-skill-report-redaction-required'
: remainingParallelReadReportLeakCount > 0
? 'parallel-read-report-redaction-required'
: remainingSupervisorAutonomousPlayableReportLeakCount > 0
? 'supervisor-autonomous-playable-report-redaction-required'
: remainingSupervisorSwarmReportLeakCount > 0
? 'supervisor-swarm-report-redaction-required'
: remainingFormalConfigPathReportLeakCount > 0
? 'formal-config-path-report-redaction-required'
: 'web-search-report-redaction-required',
: remainingUserInputReportLeakCount > 0
? 'user-input-report-redaction-required'
: remainingScopedAgentsReportLeakCount > 0
? 'scoped-agents-report-redaction-required'
: remainingProjectSkillReportLeakCount > 0
? 'project-skill-report-redaction-required'
: remainingParallelReadReportLeakCount > 0
? 'parallel-read-report-redaction-required'
: remainingSupervisorAutonomousPlayableReportLeakCount > 0
? 'supervisor-autonomous-playable-report-redaction-required'
: remainingSupervisorSwarmReportLeakCount > 0
? 'supervisor-swarm-report-redaction-required'
: remainingFormalConfigPathReportLeakCount > 0
? 'formal-config-path-report-redaction-required'
: 'web-search-report-redaction-required',
);
const safeSummary = {
status: state.status,
@@ -1376,7 +1302,6 @@ if (selfTestRequested) {
projectPathReportLeakCount: remainingProjectPathReportLeakCount,
responseStreamReportLeakCount: remainingResponseStreamReportLeakCount,
webSearchReportLeakCount: remainingWebSearchReportLeakCount,
mcpReportLeakCount: remainingMcpReportLeakCount,
userInputReportLeakCount: remainingUserInputReportLeakCount,
scopedAgentsReportLeakCount: remainingScopedAgentsReportLeakCount,
projectSkillReportLeakCount: remainingProjectSkillReportLeakCount,
@@ -24,8 +24,6 @@ import {
linuxPidfdHelperSource,
localConfigFileName,
mainAgentId,
mcpAppDataSentinelFileName,
mcpAppDataSentinelSchema,
parallelReadAppDataSentinelFileName,
parallelReadAppDataSentinelSchema,
projectSkillAppDataSentinelFileName,
@@ -65,7 +63,6 @@ import {
} from '../runtime-state.mjs';
import { isContextCompactionSuite } from '../suites/context-compaction.mjs';
import { isGoalRuntimeSuite } from '../suites/goal.mjs';
import { isMcpRuntimeSuite } from '../suites/mcp.mjs';
import { isParallelReadSuite } from '../suites/parallel-read.mjs';
import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
@@ -96,7 +93,6 @@ import {
loadConfig,
mergeConfigPatch,
safeEffectiveAgentLlmPolicy,
sameEffectiveAgentLlm,
sameEffectiveAgentLlmWithoutStream,
} from './config.mjs';
import { collectApiKeys, isPathInside, readJson } from './io.mjs';
@@ -171,14 +167,6 @@ export function isolatedSuiteAppDataProfile() {
codePrefix: 'context-compaction-appdata',
};
}
if (isMcpRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-mcp-',
sentinelName: mcpAppDataSentinelFileName,
sentinelSchema: mcpAppDataSentinelSchema,
codePrefix: 'mcp-appdata',
};
}
if (isUserInputRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-user-input-',
@@ -513,7 +501,6 @@ export async function verifySourceAppDataDirectoryUntouched() {
export async function prepareIsolatedSuiteAppData({
streamAgentId = null,
webSearchAgentId = null,
mcpConfigFactory = null,
configOverlay = null,
} = {}) {
assert(
@@ -521,9 +508,8 @@ export async function prepareIsolatedSuiteAppData({
'isolated-appdata-used-outside-isolated-suite',
);
assert(
[streamAgentId, webSearchAgentId, mcpConfigFactory, configOverlay].filter(
Boolean,
).length <= 1,
[streamAgentId, webSearchAgentId, configOverlay].filter(Boolean).length <=
1,
'isolated-appdata-multiple-overlays-forbidden',
);
if (configOverlay) {
@@ -569,21 +555,6 @@ export async function prepareIsolatedSuiteAppData({
);
startSourceAppDataDirectoryGuard(sourceConfigDir, profile);
}
const mcpOverlay = mcpConfigFactory
? await mcpConfigFactory(appDataDir)
: null;
if (mcpOverlay) {
assert(
isPlainObject(mcpOverlay) &&
isPlainObject(mcpOverlay.mcpServers) &&
Object.keys(mcpOverlay.mcpServers).length > 0 &&
Array.isArray(mcpOverlay.secrets) &&
mcpOverlay.secrets.every(isNonEmptyString),
'mcp-config-overlay-invalid',
);
for (const secret of mcpOverlay.secrets) suiteSecrets.add(secret);
}
const sourceConfigs = [];
for (const name of [configFileName, localConfigFileName]) {
const sourcePath = path.join(sourceConfigDir, name);
@@ -635,21 +606,15 @@ export async function prepareIsolatedSuiteAppData({
for (const source of sourceConfigs) {
mergeConfigPatch(mergedSourceConfig, source.config);
}
const overlayAgentId =
streamAgentId ?? webSearchAgentId ?? (mcpOverlay ? mainAgentId : null);
const overlayAgentId = streamAgentId ?? webSearchAgentId;
const sourceEffective = overlayAgentId
? effectiveAgentLlmConfig(mergedSourceConfig, overlayAgentId)
: null;
let activeConfigSource = null;
if (
overlayAgentId &&
(mcpOverlay || webSearchAgentId || sourceEffective.stream !== true)
) {
const sameEffective = mcpOverlay
? sameEffectiveAgentLlm
: webSearchAgentId
? sameEffectiveAgentLlmWithoutWebSearch
: sameEffectiveAgentLlmWithoutStream;
if (overlayAgentId && (webSearchAgentId || sourceEffective.stream !== true)) {
const sameEffective = webSearchAgentId
? sameEffectiveAgentLlmWithoutWebSearch
: sameEffectiveAgentLlmWithoutStream;
activeConfigSource =
sourceConfigs.find(
(source) =>
@@ -667,11 +632,9 @@ export async function prepareIsolatedSuiteAppData({
);
assert(
Boolean(activeConfigSource),
mcpOverlay
? 'mcp-source-config-cannot-accept-mcp-only-overlay'
: webSearchAgentId
? 'web-search-source-config-cannot-accept-search-only-overlay'
: 'response-stream-source-config-cannot-accept-stream-only-overlay',
webSearchAgentId
? 'web-search-source-config-cannot-accept-search-only-overlay'
: 'response-stream-source-config-cannot-accept-stream-only-overlay',
);
}
@@ -686,7 +649,6 @@ export async function prepareIsolatedSuiteAppData({
const linkedPath = path.join(appDataDir, linkedName);
const storageMode =
isWebSearchSuite() ||
isMcpRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
@@ -779,38 +741,24 @@ export async function prepareIsolatedSuiteAppData({
if (activeConfigSource) {
const overrideKey = webSearchAgentId ? 'webSearchEnabled' : 'stream';
const overlay = mcpOverlay
? { mcpServers: mcpOverlay.mcpServers }
: { agentLlm: { [overlayAgentId]: { [overrideKey]: true } } };
if (mcpOverlay) {
assert(
JSON.stringify(Object.keys(overlay)) ===
JSON.stringify(['mcpServers']) &&
Object.keys(overlay.mcpServers).length === 2,
'mcp-overlay-shape-invalid',
);
} else {
assert(
collectApiKeys(overlay).length === 0 &&
JSON.stringify(Object.keys(overlay)) ===
JSON.stringify(['agentLlm']) &&
JSON.stringify(Object.keys(overlay.agentLlm)) ===
JSON.stringify([overlayAgentId]) &&
JSON.stringify(Object.keys(overlay.agentLlm[overlayAgentId])) ===
JSON.stringify([overrideKey]),
webSearchAgentId
? 'web-search-overlay-shape-invalid'
: 'response-stream-overlay-shape-invalid',
);
}
const overlay = { agentLlm: { [overlayAgentId]: { [overrideKey]: true } } };
assert(
collectApiKeys(overlay).length === 0 &&
JSON.stringify(Object.keys(overlay)) === JSON.stringify(['agentLlm']) &&
JSON.stringify(Object.keys(overlay.agentLlm)) ===
JSON.stringify([overlayAgentId]) &&
JSON.stringify(Object.keys(overlay.agentLlm[overlayAgentId])) ===
JSON.stringify([overrideKey]),
webSearchAgentId
? 'web-search-overlay-shape-invalid'
: 'response-stream-overlay-shape-invalid',
);
await fs.writeFile(
path.join(appDataDir, localConfigFileName),
`${JSON.stringify(overlay)}\n`,
{ flag: 'wx', mode: 0o600 },
);
if (mcpOverlay) {
state.isolatedRunner.mcpOverrideCreated = true;
} else if (webSearchAgentId) {
if (webSearchAgentId) {
state.isolatedRunner.webSearchOverrideCreated = true;
} else {
state.isolatedRunner.streamOverrideCreated = true;
@@ -907,22 +855,6 @@ export async function prepareIsolatedSuiteAppData({
);
state.webSearch.effectiveEnabled = true;
}
if (mcpOverlay) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
mainAgentId,
);
assert(
Object.keys(isolatedConfig.config.mcpServers ?? {}).length === 2 &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'mcp-effective-runtime-config-invalid',
);
}
if (isUserInputRuntimeSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
@@ -6,7 +6,6 @@ import {
contextCompactionSuite,
goalRuntimeSuite,
localConfigFileName,
mcpRuntimeSuite,
parallelReadSuite,
processSessionSuites,
projectSkillSuite,
@@ -59,7 +58,6 @@ export function parseArguments(args) {
suite === responseStreamSuite ||
suite === webSearchSuite ||
suite === contextCompactionSuite ||
suite === mcpRuntimeSuite ||
suite === userInputRuntimeSuite ||
suite === scopedAgentsSuite ||
suite === projectSkillSuite ||
@@ -15,7 +15,6 @@ import {
} from '../runtime-state.mjs';
import { isContextCompactionSuite } from '../suites/context-compaction.mjs';
import { isGoalRuntimeSuite } from '../suites/goal.mjs';
import { isMcpRuntimeSuite } from '../suites/mcp.mjs';
import { isParallelReadSuite } from '../suites/parallel-read.mjs';
import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
@@ -111,7 +110,6 @@ export function buildSummary() {
projectPathReportLeakCount: state.projectPathReportLeakCount,
...(isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -410,7 +408,6 @@ export function isIsolatedRunnerSuite() {
isResponseStreamSuite() ||
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -43,12 +43,6 @@ export const contextCompactionAppDataSentinelFileName =
export const contextCompactionAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-context-compaction-appdata.v1';
export const mcpAppDataSentinelFileName =
'.agent-runtime-real-e2e-mcp-appdata.json';
export const mcpAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-mcp-appdata.v1';
export const userInputAppDataSentinelFileName =
'.agent-runtime-real-e2e-user-input-appdata.json';
@@ -185,8 +179,6 @@ export const webSearchSuite = 'web-search';
export const contextCompactionSuite = 'context-compaction';
export const mcpRuntimeSuite = 'mcp-runtime';
export const userInputRuntimeSuite = 'user-input-runtime';
export const scopedAgentsSuite = 'scoped-agents';
@@ -289,25 +281,6 @@ export const isolatedAgentJoinDeliverySchemaVersion =
export const isolatedAgentJoinClaimSchemaVersion =
'game-creator-isolated-agent-join-claim.v1';
export const mcpFixtureScript = path.join(
appRoot,
'src-tauri/test-fixtures/mcp-server.mjs',
);
export const mcpStdioQuery = `MCP_STDIO_QUERY_${randomUUID().replaceAll('-', '')}`;
export const mcpHttpQuery = `MCP_HTTP_QUERY_${randomUUID().replaceAll('-', '')}`;
export const mcpMutationValue = `MCP_MUTATION_${randomUUID().replaceAll('-', '')}`;
export const mcpKillMutationValue = `MCP_KILL_MUTATION_${randomUUID().replaceAll('-', '')}`;
export const mcpBearerToken = `mcp-bearer-${randomUUID().replaceAll('-', '')}`;
export const mcpHeaderValue = `mcp-header-${randomUUID().replaceAll('-', '')}`;
export const mcpMutateResponseDelayMs = 15_000;
export const contextCompactionRoundCount = 30;
export const contextCompactionTriggerTurns = new Set([4, 8]);
@@ -434,7 +407,6 @@ export const supervisorSwarmProjectMutationTools = new Set([
'file.write',
'game.generate_draft',
'game.run_local',
'mcp.call',
'project.create',
'project.export_package',
'project.git_commit',
@@ -977,7 +949,6 @@ export const isolatedRunnerState = {
cleanupPerformed: false,
streamOverrideCreated: false,
webSearchOverrideCreated: false,
mcpOverrideCreated: false,
configOverlayCreated: false,
sourceConfigCliCallCount: 0,
sourceAppDataDirectoryWatcher: null,
@@ -1107,21 +1078,6 @@ export const state = {
finalReplyFingerprint: null,
reportLeakCount: 0,
},
mcp: {
normalRunId: requestedRunId,
killRunId: `${requestedRunId}-kill`,
sessionId: null,
normalMarkerPath: null,
killMarkerPath: null,
normalActionIds: [],
killActionId: null,
oldRunnerBootId: null,
newRunnerBootId: null,
httpFixture: null,
httpPort: null,
publicLeakCount: 0,
reportLeakCount: 0,
},
userInput: {
requestId: null,
responseId: null,
File diff suppressed because it is too large Load Diff
@@ -70,7 +70,7 @@ export async function seedParallelReadDisposableProject() {
`beta evidence ${parallelReadBetaQuery}`,
'',
].join('\n');
const repositoryInstructions = `# Parallel read real E2E\n\n- This project is read-only for the current audit. Do not write files, run commands, call MCP, use Git, or delegate.\n- Verify ${parallelReadAlphaQuery} and ${parallelReadBetaQuery} with two separate project-wide text searches scoped to ${parallelReadCorpusPath}.\n- Submit both independent searches together in one planning turn before drawing a conclusion.\n- After both observations arrive, answer briefly without quoting repository instructions.\n- Never read or expose .env, ${configFileName}, or .agent/private-secret.txt.\n`;
const repositoryInstructions = `# Parallel read real E2E\n\n- This project is read-only for the current audit. Do not write files, run commands, use Git, or delegate.\n- Verify ${parallelReadAlphaQuery} and ${parallelReadBetaQuery} with two separate project-wide text searches scoped to ${parallelReadCorpusPath}.\n- Submit both independent searches together in one planning turn before drawing a conclusion.\n- After both observations arrive, answer briefly without quoting repository instructions.\n- Never read or expose .env, ${configFileName}, or .agent/private-secret.txt.\n`;
await Promise.all([
...writes,
fs.writeFile(path.join(corpusRoot, 'zzzz-evidence.txt'), evidenceBody),
@@ -83,7 +83,7 @@ export async function seedParallelReadDisposableProject() {
}
export function buildParallelReadTaskPrompt() {
return `对当前项目做一次严格只读的双证据核验:同时确认 ${parallelReadAlphaQuery}${parallelReadBetaQuery} 是否分别存在于 ${parallelReadCorpusPath}。必须实际执行两项彼此独立的项目全文搜索,并在同一个 planning 轮次一起提交;收到两项 observation 后再简短回答各自是否找到。不要修改项目,不要执行命令、MCP、Git 或委派。`;
return `对当前项目做一次严格只读的双证据核验:同时确认 ${parallelReadAlphaQuery}${parallelReadBetaQuery} 是否分别存在于 ${parallelReadCorpusPath}。必须实际执行两项彼此独立的项目全文搜索,并在同一个 planning 轮次一起提交;收到两项 observation 后再简短回答各自是否找到。不要修改项目,不要执行命令、Git 或委派。`;
}
export function assertParallelReadTaskPrompt(task) {
@@ -63,7 +63,7 @@ ${
- The review areas must remain non-overlapping, must leave project files unchanged, and their conclusions must be included in the player-ready judgment.
- Any work that changes a deliverable must read package.json, complete its declared verification, and return evidence before the player-ready handoff is accepted.
- The user receives one concise handoff only after all required evidence is accepted. Intermediate professional work stays internal.
- Do not use Git, MCP, or external generation for this repository contract.
- Do not use Git or external generation for this repository contract.
- Never read or expose .env, ${configFileName}, .agent/private-secret.txt, credentials, private runtime payloads, or absolute paths.
`;
}
@@ -80,7 +80,7 @@ ${
- A status label is not semantic proof. Before the release quality record is changed, its current content must be independently audited against this contract; any correction remains bound to the same acceptance criteria and artifact.
- Any work that changes a deliverable must read package.json, run its declared verification, and return evidence before the player-ready handoff is accepted.
- The user receives one concise handoff only after all required evidence is accepted. Intermediate professional work stays internal.
- Do not use Git, MCP, external generation, or dynamic child instances for this repository contract.
- Do not use Git, external generation, or dynamic child instances for this repository contract.
- Never read or expose .env, ${configFileName}, .agent/private-secret.txt, credentials, private runtime payloads, or absolute paths.
`;
}
@@ -97,7 +97,7 @@ ${
QUALITY_MARKER=${supervisorSwarmQualityMarker}
- If the initial quality evidence does not satisfy that contract, the Project Supervisor must send exactly one corrective assignment back to the same quality reviewer with the original criteria and artifact. A corrective quality assignment must update the file, run the package verification, and return evidence to the same parent run.
- Any professional assignment that changes a file must read package.json and run its declared verification before returning internal evidence.
- Do not use Git, MCP, external generation, dynamic child agents, or direct user-facing replies from professional agents.
- Do not use Git, external generation, dynamic child agents, or direct user-facing replies from professional agents.
- Never read or expose .env, ${configFileName}, .agent/private-secret.txt, credentials, private runtime payloads, or absolute paths.
`;
}
@@ -784,9 +784,6 @@ async function runConfigWizardRegressionChecks() {
apiKey: 'fixture-editor-key',
baseUrl: 'http://127.0.0.1:8082',
},
mcpServers: {
primary: { command: 'fixture-primary-command' },
},
};
const localConfig = {
llm: {
@@ -797,9 +794,6 @@ async function runConfigWizardRegressionChecks() {
agentLlm: {
planner: { model: 'planner-overlay-model' },
},
mcpServers: {
local: { command: 'fixture-local-command' },
},
};
fs.writeFileSync(
primaryConfigPath,
@@ -822,10 +816,6 @@ async function runConfigWizardRegressionChecks() {
assert.equal(overlayState.effectiveConfig.llm.model, 'old-overlay-model');
assert.equal(overlayState.effectiveConfig.llm.stream, true);
assert.equal(overlayState.effectiveConfig.llm.requestTimeoutMs, 12345);
assert.deepEqual(Object.keys(overlayState.effectiveConfig.mcpServers), [
'primary',
'local',
]);
assert.deepEqual(
overlayState.writeConfig.editorApi,
primaryConfig.editorApi,
@@ -862,7 +852,6 @@ async function runConfigWizardRegressionChecks() {
);
assert.equal(sanitizedLocalConfig.llm, undefined);
assert.deepEqual(sanitizedLocalConfig.agentLlm, localConfig.agentLlm);
assert.deepEqual(sanitizedLocalConfig.mcpServers, localConfig.mcpServers);
if (process.platform !== 'win32') {
assert.equal(fs.statSync(localConfigPath).mode & 0o777, 0o600);
}
@@ -10,8 +10,6 @@ const args = [
'app-server',
'--stdio',
'-c',
'mcp_servers={}',
'-c',
'web_search="disabled"',
'-c',
'agents.enabled=false',
@@ -405,12 +405,6 @@ export function mergeGameCreatorConfigLayers(baseConfig, overlayConfig) {
}
merged.agentLlm = agentLlm;
}
if (overlay.mcpServers !== null && overlay.mcpServers !== undefined) {
merged.mcpServers = mergePresentObjectProperties(
base.mcpServers,
overlay.mcpServers,
);
}
return merged;
}
+13 -142
View File
@@ -1718,7 +1718,6 @@ dependencies = [
"platform-llm",
"portable-pty",
"reqwest 0.12.28",
"rmcp",
"schemars 1.2.1",
"serde",
"serde_json",
@@ -2905,18 +2904,6 @@ dependencies = [
"libc",
]
[[package]]
name = "nix"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases 0.2.1",
"libc",
]
[[package]]
name = "nom"
version = "8.0.0"
@@ -3868,7 +3855,7 @@ dependencies = [
"lazy_static",
"libc",
"log",
"nix 0.28.0",
"nix",
"serial2",
"shared_library",
"shell-words",
@@ -3968,20 +3955,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "process-wrap"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55"
dependencies = [
"futures",
"indexmap 2.14.0",
"nix 0.31.3",
"tokio",
"tracing",
"windows 0.62.2",
]
[[package]]
name = "psl-types"
version = "2.0.11"
@@ -4314,19 +4287,15 @@ dependencies = [
"http-body",
"http-body-util",
"hyper",
"hyper-tls",
"hyper-util",
"js-sys",
"log",
"native-tls",
"percent-encoding",
"pin-project-lite",
"rustls-pki-types",
"serde",
"serde_json",
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-util",
"tower",
"tower-http",
@@ -4376,29 +4345,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rmcp"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14db48ee17a9ba61810ab1a9c1beb7d06d8136ae39ac25a1137f10d357af01af"
dependencies = [
"async-trait",
"chrono",
"futures",
"http",
"pin-project-lite",
"process-wrap",
"reqwest 0.13.4",
"serde",
"serde_json",
"sse-stream",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
"tokio-util",
"tracing",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
@@ -5024,19 +4970,6 @@ dependencies = [
"system-deps",
]
[[package]]
name = "sse-stream"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f24a9b78c40b90817bbcd1821c74ddfd74916aadd29403d001532a9195532d"
dependencies = [
"bytes",
"futures-util",
"http-body",
"http-body-util",
"pin-project-lite",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -5216,7 +5149,7 @@ dependencies = [
"tao-macros",
"unicode-segmentation",
"url",
"windows 0.61.3",
"windows",
"windows-core 0.61.2",
"windows-version",
"x11-dl",
@@ -5287,7 +5220,7 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
"windows 0.61.3",
"windows",
]
[[package]]
@@ -5467,7 +5400,7 @@ dependencies = [
"tauri-plugin",
"thiserror 2.0.18",
"url",
"windows 0.61.3",
"windows",
"zbus",
]
@@ -5493,7 +5426,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows 0.61.3",
"windows",
]
[[package]]
@@ -5518,7 +5451,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows 0.61.3",
"windows",
"wry",
]
@@ -5770,17 +5703,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.18"
@@ -6608,7 +6530,7 @@ checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"windows 0.61.3",
"windows",
"windows-core 0.61.2",
"windows-implement",
"windows-interface",
@@ -6632,7 +6554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
dependencies = [
"thiserror 2.0.18",
"windows 0.61.3",
"windows",
"windows-core 0.61.2",
]
@@ -6713,23 +6635,11 @@ version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections 0.2.0",
"windows-collections",
"windows-core 0.61.2",
"windows-future 0.2.1",
"windows-future",
"windows-link 0.1.3",
"windows-numerics 0.2.0",
]
[[package]]
name = "windows"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [
"windows-collections 0.3.2",
"windows-core 0.62.2",
"windows-future 0.3.2",
"windows-numerics 0.3.1",
"windows-numerics",
]
[[package]]
@@ -6741,15 +6651,6 @@ dependencies = [
"windows-core 0.61.2",
]
[[package]]
name = "windows-collections"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [
"windows-core 0.62.2",
]
[[package]]
name = "windows-core"
version = "0.61.2"
@@ -6784,18 +6685,7 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
"windows-threading 0.1.0",
]
[[package]]
name = "windows-future"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [
"windows-core 0.62.2",
"windows-link 0.2.1",
"windows-threading 0.2.1",
"windows-threading",
]
[[package]]
@@ -6842,16 +6732,6 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-numerics"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [
"windows-core 0.62.2",
"windows-link 0.2.1",
]
[[package]]
name = "windows-registry"
version = "0.6.1"
@@ -7001,15 +6881,6 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-version"
version = "0.1.7"
@@ -7268,7 +7139,7 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
"windows 0.61.3",
"windows",
"windows-core 0.61.2",
"windows-version",
"x11-dl",
@@ -33,7 +33,6 @@ oxc_ast_visit = "0.143.0"
oxc_parser = "0.143.0"
oxc_semantic = "0.143.0"
oxc_span = "0.143.0"
rmcp = { version = "2.2.0", default-features = false, features = ["client", "reqwest-native-tls", "transport-child-process", "transport-streamable-http-client-reqwest"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
@@ -3,7 +3,7 @@
## 身份与边界
- 当前 run 固定为 `source=agent-delegate``profile=standard`,父 Agent 是 `project-supervisor`。不得伪造、改写或猜测这些 Runtime 身份。
- 你不能委派或调度其他 Agent,不能创建 isolated child,不能调用 MCP、命令、进程、预览、画布、素材生成、写入/补丁/删除工具,也不能改变项目版本或审批事实。
- 你不能委派或调度其他 Agent,不能创建 isolated child,不能调用命令、进程、预览、画布、素材生成、写入/补丁/删除工具,也不能改变项目版本或审批事实。
- 你的原生工具目录只应包含 `file.read``file.list` 以及 Runtime 协议控制函数 `update_agent_plan``respond_to_user``user.input_request` 不属于你的工具目录。若需要用户决定,必须以终态信封首行 `AGC_NEEDS_USER_INPUT_V1` 退出本轮,下一行给出严格 JSON 信封 `{"questions":[{ ... }]}`,交由 Supervisor 转发。`questions` 恰好一个元素;元素字段只能是 `id``header``question``options` 四个,多写任何字段(例如 `answerFormat`)或省掉 `questions` 外壳都会被 Runtime 拒收,整条委派随即作废。`id` 是唯一 snake_case(小写字母开头,只含小写字母、数字、下划线);`header` 是决策卡标题,单行且不超过 12 字符;`question` 是决策卡正文,单行且不超过 400 字符;`options` 是 23 个 `{"label": ..., "description": ...}`,label 单行不超过 60 字符、description 单行不超过 240 字符。不要另起一行写答题说明或把选项复述进 `question`,作答方式由 Runtime 自己呈现。
- 只有 Runtime 广告并允许 `plan.submit_gdd` 时才可提交 GDD;不要假设未广告的工具存在,也不要把 GDD、审批或下游构建写进普通文本。
@@ -5,7 +5,7 @@ description: Run and interpret real AGC desktop and mobile browser evidence thro
# AGC Browser Playtest
Use `agc_browser_playtest` from the `agc_tools` MCP server. Do not replace it with static source inspection or a statement that the page should work.
Use the approved `agc_browser_playtest` client tool. Do not replace it with static source inspection or a statement that the page should work.
## Workflow
@@ -2,10 +2,3 @@ interface:
display_name: "浏览器试玩验收"
short_description: "调用客户端真实双视口浏览器工具试玩,并依据结构化运行证据持续修复"
default_prompt: "Use $agc-browser-playtest to run real desktop and mobile browser validation and fix observed issues."
dependencies:
tools:
- type: "mcp"
value: "agc_tools"
description: "陶泥儿客户端提供的受限双视口浏览器试玩工具"
transport: "stdio"
@@ -10,20 +10,22 @@ Let the client derive projections from real disk changes and trusted tool result
## Workflow
1. Write executable source to `index.html`, `style.css`, and `game.js` in the current cwd. Use only relative paths returned by approved tools for media.
2. Before using or deriving an existing asset, call `agc_list_registered_assets` and select its `localAssetId`; never infer a source from a filename or submit a local path, platform ID, object key, operation ID, or idempotency key as a generation argument.
3. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image.
4. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and an output name. The client requires the signed-in account, owns canvas/folder context and task identity, and returns only bounded queue state.
5. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable.
6. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand.
7. Do not create a version when no game file changed. The client compares content fingerprints and advances revision only after an actual source change.
8. Do not claim a resource or version is visible before the client projects it. If projection is missing, report the changed relative files and let the client re-read durable state.
9. Never move HTML, CSS, or JavaScript into documentation folders. They belong to the game-code projection; prose, design notes, and instructions remain documents.
2. Before using or deriving an existing registered asset, call `agc_list_registered_assets` and select its `localAssetId`. If the user points to an existing project file that is not listed, first call `agc_list_project_files`, then use `agc_import_account_assets` with its safe project-relative `localPaths` and re-read `agc_list_registered_assets`; never infer a source identity from a filename or fabricate a localAssetId.
3. Keep read scopes separate: `asset.list` is the current project manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is the authoritative canvas list. The account library is not the complete canvas list.
4. Use `canvas.asset_import` for safe account/canvas asset IDs or project-relative local paths. The client rechecks ownership and validates bytes; host absolute paths require native UI file-picker authorization.
5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image.
6. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and an output name. The client requires the signed-in account, owns canvas/folder context and task identity, and returns only bounded queue state.
7. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable.
8. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand.
9. Do not create a version when no game file changed. The client compares content fingerprints and advances revision only after an actual source change.
10. Do not claim a resource or version is visible before the client projects it. If projection is missing, report the changed relative files and let the client re-read durable state.
11. Never move HTML, CSS, or JavaScript into documentation folders. They belong to the game-code projection; prose, design notes, and instructions remain documents.
Call `agc_read_skill_resource` with `skillName="agc-client-projection"` and `relativePath="references/projection-contract.md"` when a request touches asset identity, revision behavior, or version history.
## Boundaries
- Platform provenance comes only from approved client tools.
- Platform provenance and manifest resource identity come only from approved client tools. Project-file discovery may report an unregistered path, but that path has no provenance until a client import/register transaction succeeds.
- The client owns permission checks, project identity, revision, project locks, paid submission, idempotency, operation recovery, download validation, warning projection, and manifest transactions.
- Browser evidence proves runtime behavior, not resource ownership.
- Source changes, resource registration, and version projection are distinct facts; report each accurately.
@@ -8,7 +8,9 @@ The client projects three distinct facts:
Do not collapse these facts. A playable file can exist before projection refresh, a registered image can exist without being used by the game, and browser success does not create platform provenance.
`agc_list_registered_assets` is the only Direct read path for manifest resource identity. Its relative path and stable identifiers are evidence; omitted prompt, model, provider route, signed URL, host path, and credentials are intentionally not available to Codex.
`agc_list_project_files` is the bounded Direct discovery path for real project files. It may report an unregistered media path with size/MIME metadata, but that observation is not a resource identity and carries no provenance. `agc_import_account_assets.localPaths` is the controlled bridge that validates and registers a project-local image. `agc_list_registered_assets` remains the authoritative Direct read path for manifest resource identity; only its stable identifiers may be passed to generation/derivation tools.
Read scopes remain separate: `asset.list` is the current project's local manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is authoritative for resources visible on that canvas. A library result must not be presented as the complete canvas list. `canvas.asset_import` accepts safe account/canvas asset IDs or project-relative local paths; receipts expose only bounded counts, safe IDs, relative paths, sources, redacted failures, and `revisionAdvanceCount`.
`agc_create_or_derive_resource` accepts only semantic intent. The client resolves `sourceLocalAssetId`, creates stable request identities, recovers matching pending operations, serializes paid submissions, writes supported media into the current canvas and same-name asset folder, validates downloaded bytes, commits the local manifest transaction, and returns redacted warnings. A tool error or timeout is not permission to generate again with a new identity.
@@ -11,17 +11,18 @@ Treat the current working directory as the only project root.
1. Inspect the existing files needed for the request before editing.
2. The current working directory is the `game/` directory. Read and edit `index.html`, `style.css`, and `game.js` there unless the existing project deliberately uses another in-game structure.
3. Platform media is exposed read-only through `../assets/` and approved `agc_tools`; do not infer or write project asset state from paths.
4. Treat the parent `.agent/` directory as client-owned durable state. Do not read it with native file or shell tools; use the approved AGC tools when project identity or registered asset evidence is needed. Never hand-edit manifests, revisions, versions, ledgers, receipts, or provenance records.
5. Reuse existing files and asset identities. Do not create a second project root, hidden harness, Supervisor workspace, or parallel implementation.
6. Make the smallest coherent change that satisfies the user request, then inspect the actual changed files.
3. To discover media or other existing project files outside the `game/` cwd, call `agc_list_project_files` with an optional project-relative scope. It returns safe project-relative paths (including `assets/` and `game/`) plus bounded metadata; an unregistered file is only a discovery candidate, not a manifest asset.
4. Platform media and project-local media are exposed read-only through approved `agc_tools`; when a user asks to use an unregistered PNG/JPEG/WEBP, pass the returned project-relative path to `agc_import_account_assets.localPaths`, then re-read `agc_list_registered_assets` for the formal identity. Do not infer provenance or fabricate an asset ID from a filename.
5. Treat the parent `.agent/` directory as client-owned durable state. Do not read it with native file or shell tools; use the approved AGC tools when project identity or registered asset evidence is needed. Never hand-edit manifests, revisions, versions, ledgers, receipts, or provenance records.
6. Reuse existing files and asset identities. Do not create a second project root, hidden harness, Supervisor workspace, or parallel implementation.
7. Make the smallest coherent change that satisfies the user request, then inspect the actual changed files.
Call `agc_read_skill_resource` with `skillName="agc-project-structure"` and `relativePath="references/structure-contract.md"` when deciding where a new file belongs or whether a state file may be edited.
## Boundaries
- Keep source edits inside the current `game/` directory.
- Do not write `../assets/`, `../.agent/`, or any parent/project path.
- Keep native source edits inside the current `game/` directory. Project-file discovery and local image import are the only approved operations that may name a project-root-relative path outside that cwd.
- Do not write `../assets/`, `../.agent/`, or any parent/project path with native file or shell tools. Use the approved import tool for a user-authorized local image, and never target control directories.
- Do not read credentials, `.env`, authentication files, browser profiles, or unrelated host paths.
- Do not create Supervisor, professional Agent, harness, or provider orchestration files.
- Do not claim that the client registered a resource or version; the client performs that projection after real file changes.
@@ -5,7 +5,8 @@
| `index.html` | Game source in the current cwd | Read and edit |
| `style.css` | Game source in the current cwd | Read and edit |
| `game.js` | Game source in the current cwd | Read and edit |
| `../assets/` | Project media | Read only through approved tools; do not write |
| `../assets/` / `assets/` | Project media | Discover with `agc_list_project_files`; import an unregistered PNG/JPEG/WEBP through `agc_import_account_assets.localPaths`; formal identity comes only after manifest registration |
| Other project-root-relative files | Existing project files | Discover with `agc_list_project_files` or `file.list`; do not treat a path as a registered asset or expose sensitive/control paths |
| `../.agent/` | AGC client state | Do not read or write with native tools |
Keep native write paths relative to the current `game/` cwd. Reject `..`, a drive prefix, a UNC prefix, or a leading slash when it would escape the game directory. Parent `../assets/` is read-only and may only be resolved through approved asset tools and their returned relative paths; it is never a native write target.
Keep native write paths relative to the current `game/` cwd. Reject `..`, a drive prefix, a UNC prefix, or a leading slash when it would escape the game directory. `agc_list_project_files` and `agc_import_account_assets.localPaths` accept only safe project-root-relative paths returned by the client; they never grant access to `.agent`, credentials, or arbitrary host paths. A discovered file becomes a formal resource only after the client validates and registers it.
@@ -10,13 +10,18 @@
"判断文件应放置的位置",
"读取项目状态证据"
],
"requiredTools": ["agc_tools.agc_read_skill_resource"],
"requiredTools": [
"agc_tools.agc_read_skill_resource",
"agc_tools.agc_list_project_files",
"agc_tools.agc_import_account_assets",
"agc_tools.agc_list_registered_assets"
],
"files": [
"SKILL.md",
"agents/openai.yaml",
"references/structure-contract.md"
],
"sha256": "9c4991858852b4513b5030892208301859a6e6d3a78e3595de28794d3d0fcd92"
"sha256": "f5478126d6018db71e155f95db8047d2b9103010743491ab07991cc2781078a1"
},
{
"name": "taonier-art-assets",
@@ -86,6 +91,9 @@
"requiredTools": [
"agc_tools.agc_read_skill_resource",
"agc_tools.agc_list_registered_assets",
"agc_tools.agc_list_project_files",
"agc_tools.agc_list_account_assets",
"agc_tools.agc_import_account_assets",
"agc_tools.agc_create_or_derive_resource",
"agc_tools.agc_remove_background"
],
@@ -94,7 +102,7 @@
"agents/openai.yaml",
"references/projection-contract.md"
],
"sha256": "c8cfa632290f1b3ac502894a7f9b865a73f764ed3a3fb2e8344cf4a93feeb05e"
"sha256": "790d0788a8b1585e95b7d2181b0d09af611b2c673596a56e46a853bea736a4da"
}
]
}
@@ -5,12 +5,12 @@ description: Prepare, recover, inspect, and integrate real Taonier platform game
# Taonier Art Assets
Use real platform assets only through `taonier_prepare_game_art` from the `agc_tools` MCP server.
Use real platform assets only through the approved `taonier_prepare_game_art` client tool.
## Workflow
1. Inspect existing `assets/` and registered project evidence before requesting new art. Reuse suitable assets when the user did not ask to regenerate them.
2. Call `taonier_prepare_game_art` only when the current intent requires new or recoverable platform art. Use `mode="regenerate"` only after the latest User message is a standalone reviewed immediate-confirmation command such as `请重新生成美术`; punctuation may end it, but no brief, condition, negation, alternative, cost qualifier, deferral, or other text may accompany it. Describe the desired style and gameplay constraints in an earlier non-billable turn, then obtain the standalone confirmation turn; otherwise use `mode="reuse-or-create"`. Quoted UI copy or examples, explanations, questions, historical wording, and model/MCP arguments do not authorize regeneration. Pass a concise game-specific visual brief that names the required gameplay entities, background exclusions, tiling needs, and viewport constraints. Do not call it for greetings, date questions, text-only code fixes, or layout changes that can reuse current art.
2. Call `taonier_prepare_game_art` only when the current intent requires new or recoverable platform art. Use `mode="regenerate"` only after the latest User message is a standalone reviewed immediate-confirmation command such as `请重新生成美术`; punctuation may end it, but no brief, condition, negation, alternative, cost qualifier, deferral, or other text may accompany it. Describe the desired style and gameplay constraints in an earlier non-billable turn, then obtain the standalone confirmation turn; otherwise use `mode="reuse-or-create"`. Quoted UI copy or examples, explanations, questions, historical wording, and model-selected arguments do not authorize regeneration. Pass a concise game-specific visual brief that names the required gameplay entities, background exclusions, tiling needs, and viewport constraints. Do not call it for greetings, date questions, text-only code fixes, or layout changes that can reuse current art.
3. Treat the tool result as authoritative. Read `mode`, `assetPaths`, `slicePaths`, `resources`, and every entry in both `warnings` and `sliceWarnings`. `resources` is the client's safe projection of registered Canvas identities; use only its returned relative paths and identities. Never invent a resource, slice, platform identity, warning-free result, or successful regeneration.
4. A newly created or explicitly regenerated standard package is complete only when `slicePaths` contains the four canonical independent slices. An empty or partial `slicePaths` result never satisfies an independent-asset requirement; stop and report the warning instead of guessing atlas coordinates or fabricating derivatives. A trusted legacy complete sheet may still be used without slices only when the current request does not require independent assets.
5. Inspect the returned background, complete sheet, and available slice previews before integrating them. Then use suitable returned runtime assets in the game's actual visible experience and confirm their visible use in desktop and mobile playtest evidence. `art-spec.png` is a reference specification, not a runtime background, character, prop, or effect. Background exclusions, seamless tiling, entity semantics, and final draw dimensions are visual/runtime acceptance checks; a prompt alone does not prove them. A hidden or side-panel preview does not count as gameplay use.
@@ -2,10 +2,3 @@ interface:
display_name: "陶泥儿美术素材"
short_description: "通过受控平台工具生成、恢复、登记并使用真实陶泥儿游戏美术素材"
default_prompt: "Use $taonier-art-assets to prepare and integrate real Taonier game art safely."
dependencies:
tools:
- type: "mcp"
value: "agc_tools"
description: "陶泥儿客户端提供的受控平台美术工具"
transport: "stdio"

Some files were not shown because too many files have changed in this diff Show More