Merge remote-tracking branch 'origin/master' into fix/agc-recent-project-status-retry

# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
This commit is contained in:
2026-09-23 16:54:56 +08:00
110 changed files with 564 additions and 34788 deletions
-10
View File
@@ -15,20 +15,10 @@
"skill-pack:test": "node --test scripts/check-skill-pack.test.mjs",
"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",
"swarm": "node scripts/run-cli-with-config.mjs --swarm-chat",
"config": "node scripts/game-creator-config-wizard.mjs",
"test:chat": "node scripts/agent-swarm-test-chat.mjs --task \"制作一个可直接试玩的原创植物塔防小游戏:玩家选择并放置原创守卫阻挡敌人,完成波次后可以进入下一关并重新开始。主题、单位名称与视觉语言必须原创,不使用任何现有游戏角色、单位名、Logo 或受保护视觉语言。请自主完成正式产物、静态检查和双视口试玩验证。\" --no-open",
"test:chat:manual": "node scripts/agent-swarm-test-chat.mjs",
"agent-run": "node scripts/run-cli-with-config.mjs --agent-run",
"agent-run:smoke": "node scripts/smoke-agent-run-local-provider.mjs",
"agent-runtime:real-e2e": "node scripts/agent-runtime-real-e2e.mjs",
"agent-runtime:collaboration-policy-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-collaboration-policy-mixed-recovery",
"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-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",
"agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-final-reply-transient-retry",
"agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-tool-plan-handoff-runner-kill",
File diff suppressed because it is too large Load Diff
@@ -1,69 +1,8 @@
import { createHash } from '../dependencies.mjs';
import { isPlainObject } from '../harness/config.mjs';
import {
interactiveCliOutput,
writeInteractiveCliLine,
} from '../harness/process.mjs';
import {
shutdownWaiters,
state,
userInputAnswerText,
} from '../runtime-state.mjs';
import { shutdownWaiters, state } from '../runtime-state.mjs';
import { disposableProjectPathVariants } from './runtime.mjs';
export async function answerRemainingInteractiveQuestions(session) {
let answeredPromptCount = 1;
const deadline = Date.now() + 60_000;
while (Date.now() < deadline) {
const output = interactiveCliOutput(session);
if (output.includes(`[\u5df2\u56de\u7b54] ${state.userInput.requestId}`))
return;
const promptCount = output.split('或直接输入其他答案:').length - 1;
while (answeredPromptCount < promptCount && answeredPromptCount < 3) {
writeInteractiveCliLine(session, userInputAnswerText);
answeredPromptCount += 1;
}
if (output.includes('[待确认]')) {
throw codedError('user-input-unexpected-tool-confirmation');
}
if (session.closed) throw codedError('user-input-cli-closed-before-answer');
await sleep(100);
}
throw codedError('user-input-answer-timeout');
}
export function parseSingleSwarmTurnReport(output, codePrefix) {
const reportLines = output
.split(/\r?\n/u)
.filter((line) => line.startsWith('[turn.report] '));
assert(reportLines.length === 1, `${codePrefix}-turn-report-count-invalid`);
const report = JSON.parse(reportLines[0].slice('[turn.report] '.length));
assert(
isPlainObject(report) &&
JSON.stringify(Object.keys(report).sort()) ===
JSON.stringify(
[
'schemaVersion',
'outcome',
'parentAgentId',
'sessionId',
'parentRunId',
'runtimeCount',
'busyRuntimeCount',
'pendingTaskCount',
'runningTaskCount',
'waitingForConfirmationCount',
'waitingForUserInputCount',
'newAssistantMessageCount',
'finalReplyChars',
'reconciliationAgentCount',
].sort(),
),
`${codePrefix}-turn-report-shape-invalid`,
);
return report;
}
export function isFailedTask(task) {
return (
['failed', 'cancelled', 'budget-exhausted'].includes(task.status) ||
@@ -2419,24 +2419,6 @@ export function finalMessageId(agentId, sessionId, runId) {
).slice(0, 32)}`;
}
export function runtimePublicStatusMessageId(
agentId,
sessionId,
runId,
status,
) {
const correlationId = runtimeMessageCorrelationId(
agentId,
sessionId,
runId,
).slice(0, 32);
const statusFingerprint = createHash('sha256')
.update(status)
.digest('hex')
.slice(0, 16);
return `runtime-public-status-${correlationId}-${statusFingerprint}`;
}
export function backgroundTaskMessageId(agentId, sessionId, runId, source) {
const fingerprint = createHash('sha256')
.update(`${agentId}\n${sessionId}\n${runId}\n${source}`)
File diff suppressed because it is too large Load Diff
@@ -37,26 +37,16 @@ import {
state,
steerRunnerKillAppDataSentinelFileName,
steerRunnerKillAppDataSentinelSchema,
supervisorAutonomousPlayableAppDataSentinelFileName,
supervisorAutonomousPlayableAppDataSentinelSchema,
supervisorSwarmAppDataSentinelFileName,
supervisorSwarmAppDataSentinelSchema,
supervisorSwarmAutonomousChatAppDataSentinelFileName,
supervisorSwarmAutonomousChatAppDataSentinelSchema,
supervisorSwarmCollaborationPolicyAppDataSentinelFileName,
supervisorSwarmCollaborationPolicyAppDataSentinelSchema,
supervisorSwarmFinalReplyTransientRetryAppDataSentinelFileName,
supervisorSwarmFinalReplyTransientRetryAppDataSentinelSchema,
supervisorSwarmRequiredAgentIds,
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName,
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema,
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelFileName,
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelSchema,
supervisorSwarmTransientRetryAppDataSentinelFileName,
supervisorSwarmTransientRetryAppDataSentinelSchema,
supervisorSwarmTransientRetryTargetAgentId,
userInputAppDataSentinelFileName,
userInputAppDataSentinelSchema,
webSearchAppDataSentinelFileName,
webSearchAppDataSentinelSchema,
windowsProcessHandleHelperSource,
@@ -68,21 +58,14 @@ 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 } from '../suites/supervisor-autonomous-playable.mjs';
import {
isSupervisorSwarmAutonomousChatSuite,
isSupervisorSwarmCollaborationPolicyMixedRecoverySuite,
isSupervisorSwarmFinalReplyTransientRetrySuite,
isSupervisorSwarmInitialTransientRetrySuite,
isSupervisorSwarmInteractiveChatSuite,
isSupervisorSwarmMixedHarnessSuite,
isSupervisorSwarmStaticIsolatedAutonomousChatSuite,
isSupervisorSwarmSuite,
isSupervisorSwarmToolPlanHandoffRunnerKillSuite,
isSupervisorSwarmTransientRetrySuite,
rebuildSupervisorSwarmTranscriptScanner,
} from '../suites/supervisor-swarm.mjs';
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
import {
isWebSearchSuite,
sameEffectiveAgentLlmWithoutWebSearch,
@@ -100,19 +83,10 @@ import { appendBounded, runProcess } from './process.mjs';
import { decodeUtf8Fatal, isIsolatedRunnerSuite } from './reporting.mjs';
import { killRunnerOnce, readRunnerStatus, runnerBootId } from './runtime.mjs';
const platformSessionFixtureEnv = 'GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE';
const platformSessionFixtureMaxBytes = 16 * 1024;
const isolatedPlatformSessionFixtureName =
'.deterministic-platform-session.json';
const platformSessionFixtureSchema =
'genarrative-agc-platform-session-fixture.v1';
export function isolatedSuiteProtectsSourceAppData() {
return (
isSupervisorSwarmTransientRetrySuite() ||
isSupervisorSwarmToolPlanHandoffRunnerKillSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmInteractiveChatSuite()
isSupervisorSwarmToolPlanHandoffRunnerKillSuite()
);
}
@@ -120,27 +94,6 @@ export function isolatedSuiteUsesSiblingAppData() {
return isWebSearchSuite() || isolatedSuiteProtectsSourceAppData();
}
export function sameSupervisorPlayableProviderBinding(left, right) {
const scalarFields = [
'providerAgentMode',
'providerModel',
'providerApiKind',
'providerReasoningEffort',
'providerBaseUrlSha256',
];
return (
left != null &&
right != null &&
scalarFields.every((field) => left[field] === right[field]) &&
Array.isArray(left.boundAgentIds) &&
Array.isArray(right.boundAgentIds) &&
left.boundAgentIds.length === right.boundAgentIds.length &&
left.boundAgentIds.every(
(agentId, index) => agentId === right.boundAgentIds[index],
)
);
}
export function isolatedSuiteAppDataProfile() {
if (isSteerRunnerKillSuite()) {
return {
@@ -174,14 +127,6 @@ export function isolatedSuiteAppDataProfile() {
codePrefix: 'context-compaction-appdata',
};
}
if (isUserInputRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-user-input-',
sentinelName: userInputAppDataSentinelFileName,
sentinelSchema: userInputAppDataSentinelSchema,
codePrefix: 'user-input-appdata',
};
}
if (isScopedAgentsSuite()) {
return {
prefix: '.agent-runtime-real-e2e-scoped-agents-',
@@ -236,41 +181,6 @@ export function isolatedSuiteAppDataProfile() {
codePrefix: 'supervisor-swarm-transient-retry-appdata',
};
}
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-autonomous-playable-',
sentinelName: supervisorAutonomousPlayableAppDataSentinelFileName,
sentinelSchema: supervisorAutonomousPlayableAppDataSentinelSchema,
codePrefix: 'supervisor-autonomous-playable-appdata',
};
}
if (isSupervisorSwarmAutonomousChatSuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-',
sentinelName: supervisorSwarmAutonomousChatAppDataSentinelFileName,
sentinelSchema: supervisorSwarmAutonomousChatAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-autonomous-chat-appdata',
};
}
if (isSupervisorSwarmStaticIsolatedAutonomousChatSuite()) {
return {
prefix:
'.agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-',
sentinelName:
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName,
sentinelSchema:
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-static-isolated-autonomous-chat-appdata',
};
}
if (isSupervisorSwarmCollaborationPolicyMixedRecoverySuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-',
sentinelName: supervisorSwarmCollaborationPolicyAppDataSentinelFileName,
sentinelSchema: supervisorSwarmCollaborationPolicyAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-collaboration-policy-appdata',
};
}
if (isSupervisorSwarmSuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-',
@@ -505,120 +415,6 @@ export async function verifySourceAppDataDirectoryUntouched() {
state.isolatedRunner.sourceAppDataDirectoryUntouched = true;
}
async function readPlatformSessionFixtureForIsolatedSuite(sourceConfigDir) {
const rawPath = process.env[platformSessionFixtureEnv];
assert(
isNonEmptyString(rawPath) && path.isAbsolute(rawPath),
'supervisor-autonomous-playable-platform-session-fixture-missing',
);
const sourceRealPath = await fs.realpath(sourceConfigDir);
const requestedPath = path.resolve(rawPath);
const requestedMetadata = await fs.lstat(requestedPath).catch((error) => {
if (error?.code === 'ENOENT') return null;
throw error;
});
assert(
requestedMetadata?.isFile() && !requestedMetadata.isSymbolicLink(),
'supervisor-autonomous-playable-platform-session-fixture-not-regular',
);
assert(
requestedMetadata.size <= platformSessionFixtureMaxBytes,
'supervisor-autonomous-playable-platform-session-fixture-too-large',
);
const realPath = await fs.realpath(requestedPath);
assert(
isPathInside(sourceRealPath, realPath),
'supervisor-autonomous-playable-platform-session-fixture-outside-config',
);
const bytes = await fs.readFile(realPath);
assert(
bytes.length <= platformSessionFixtureMaxBytes,
'supervisor-autonomous-playable-platform-session-fixture-too-large',
);
let fixture;
try {
fixture = JSON.parse(
decodeUtf8Fatal(bytes, 'platform-session-fixture-invalid-utf8'),
);
} catch (error) {
throw codedError(
'supervisor-autonomous-playable-platform-session-fixture-invalid',
error,
);
}
const expectedKeys = [
'schemaVersion',
'userId',
'accessToken',
'apiBaseUrl',
'generation',
];
assert(
isPlainObject(fixture) &&
JSON.stringify(Object.keys(fixture).sort()) ===
JSON.stringify([...expectedKeys].sort()) &&
fixture.schemaVersion === platformSessionFixtureSchema &&
isNonEmptyString(fixture.userId) &&
isNonEmptyString(fixture.accessToken) &&
isNonEmptyString(fixture.apiBaseUrl) &&
Number.isSafeInteger(fixture.generation) &&
fixture.generation > 0,
'supervisor-autonomous-playable-platform-session-fixture-invalid',
);
return {
sourcePath: realPath,
bytes,
fixture,
sha256: createHash('sha256').update(bytes).digest('hex'),
};
}
async function installPlatformSessionFixtureIntoIsolatedAppData(
sourceConfigDir,
appDataDir,
) {
if (!isSupervisorAutonomousPlayableLaneDefenseSuite()) return;
const source =
await readPlatformSessionFixtureForIsolatedSuite(sourceConfigDir);
const isolatedPath = path.join(
appDataDir,
isolatedPlatformSessionFixtureName,
);
await fs.copyFile(
source.sourcePath,
isolatedPath,
fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE,
);
await fs.chmod(isolatedPath, 0o600).catch(() => {});
const isolatedMetadata = await fs.lstat(isolatedPath);
assert(
isolatedMetadata.isFile() &&
!isolatedMetadata.isSymbolicLink() &&
isolatedMetadata.size === source.bytes.length,
'supervisor-autonomous-playable-platform-session-fixture-copy-invalid',
);
const isolatedBytes = await fs.readFile(isolatedPath);
assert(
createHash('sha256').update(isolatedBytes).digest('hex') === source.sha256,
'supervisor-autonomous-playable-platform-session-fixture-copy-mismatch',
);
state.isolatedRunner.platformSessionFixtureSourcePath = source.sourcePath;
state.isolatedRunner.platformSessionFixturePath = isolatedPath;
state.isolatedRunner.platformSessionFixtureSha256 = source.sha256;
state.isolatedRunner.platformSessionFixturePreviousEnv =
Object.prototype.hasOwnProperty.call(process.env, platformSessionFixtureEnv)
? process.env[platformSessionFixtureEnv]
: undefined;
process.env[platformSessionFixtureEnv] = isolatedPath;
state.formalConfigPathTranscriptScanner?.addSecrets(
absolutePathVariants(source.sourcePath, isolatedPath),
);
const previousLeakCount = state.transcriptScanner?.count ?? 0;
state.secrets = [...new Set([...state.secrets, source.fixture.accessToken])];
rebuildSupervisorSwarmTranscriptScanner();
state.transcriptScanner.count = previousLeakCount;
}
export async function prepareIsolatedSuiteAppData({
streamAgentId = null,
webSearchAgentId = null,
@@ -773,8 +569,7 @@ export async function prepareIsolatedSuiteAppData({
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorSwarmSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
isSupervisorSwarmSuite()
? 'private-copy'
: 'hardlink';
try {
@@ -891,10 +686,6 @@ export async function prepareIsolatedSuiteAppData({
state.secrets = [...suiteSecrets];
rebuildSupervisorSwarmTranscriptScanner();
state.transcriptScanner.count = previousLeakCount;
await installPlatformSessionFixtureIntoIsolatedAppData(
sourceConfigDir,
appDataDir,
);
const unexpectedEndpoint = await fs
.lstat(path.join(appDataDir, runnerEndpointFileName))
.catch((error) => {
@@ -903,46 +694,6 @@ export async function prepareIsolatedSuiteAppData({
});
assert(!unexpectedEndpoint, `${profile.codePrefix}-endpoint-preexisted`);
state.runtimeConfigDir = appDataDir;
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const expectedBinding = state.config.providerBinding;
const agentIds = [projectSupervisorAgentId];
const bindings = agentIds.map((agentId) => {
const effective = effectiveAgentLlmConfig(isolatedConfig.config, agentId);
assert(
['apiKey', 'baseUrl', 'model', 'apiKind', 'reasoningEffort'].every(
(key) => isNonEmptyString(effective[key]),
),
'supervisor-autonomous-playable-effective-provider-incomplete',
);
return {
providerModel: effective.model.trim(),
providerApiKind: effective.apiKind.trim(),
providerReasoningEffort: effective.reasoningEffort.trim(),
providerBaseUrlSha256: hashValue(effective.baseUrl.trim()),
};
});
const effectiveBinding = {
...bindings[0],
boundAgentIds: [...agentIds].sort(),
};
assert(
expectedBinding &&
bindings.every(
(binding) => JSON.stringify(binding) === JSON.stringify(bindings[0]),
) &&
sameSupervisorPlayableProviderBinding(
effectiveBinding,
expectedBinding,
),
'supervisor-autonomous-playable-effective-provider-binding-mismatch',
);
state.supervisorAutonomousPlayable.expectedProviderBinding = {
...expectedBinding,
};
state.supervisorAutonomousPlayable.effectiveProviderBinding =
effectiveBinding;
}
if (streamAgentId) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
@@ -981,22 +732,6 @@ export async function prepareIsolatedSuiteAppData({
);
state.webSearch.effectiveEnabled = true;
}
if (isUserInputRuntimeSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
projectSupervisorAgentId,
);
assert(
isolatedEffective.model === 'gpt-5.5' &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'user-input-effective-gpt-5-5-config-invalid',
);
}
if (isScopedAgentsSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
@@ -1073,9 +808,8 @@ export async function prepareIsolatedSuiteAppData({
effective.requestTimeoutMs > 0 &&
Number.isSafeInteger(effective.maxRetries) &&
effective.maxRetries >=
(isSupervisorSwarmInteractiveChatSuite() ||
(isSupervisorSwarmTransientRetrySuite() &&
agentId !== supervisorSwarmTransientRetryTargetAgentId)
agentId !== supervisorSwarmTransientRetryTargetAgentId
? 0
: 1) &&
effective.maxRetries <= 3 &&
@@ -1095,24 +829,6 @@ export async function prepareIsolatedSuiteAppData({
{ llm: isolatedConfig.config.llm },
'',
);
if (isSupervisorSwarmMixedHarnessSuite()) {
assert(
globalEffective.model === 'gpt-5.5' &&
globalEffective.apiKind === 'openai_chat' &&
isNonEmptyString(globalEffective.reasoningEffort) &&
Number.isSafeInteger(globalEffective.requestTimeoutMs) &&
globalEffective.requestTimeoutMs > 0 &&
Number.isSafeInteger(globalEffective.maxRetries) &&
globalEffective.maxRetries >= 0 &&
globalEffective.maxRetries <= 3 &&
Number.isSafeInteger(globalEffective.retryBackoffMs) &&
globalEffective.retryBackoffMs > 0 &&
['apiKey', 'baseUrl', 'model'].every((key) =>
isNonEmptyString(globalEffective[key]),
),
'supervisor-swarm-mixed-default-provider-policy-invalid',
);
}
const configuredAgentIds = Object.keys(
isPlainObject(isolatedConfig.config.agentLlm)
? isolatedConfig.config.agentLlm
@@ -2000,31 +1716,6 @@ export async function verifyIsolatedSuiteConfigLinksUnchanged() {
'isolated-source-config-changed-during-suite',
);
}
const fixture = state.isolatedRunner;
if (
fixture.platformSessionFixtureSourcePath &&
fixture.platformSessionFixturePath &&
fixture.platformSessionFixtureSha256
) {
const [sourceMetadata, isolatedMetadata, sourceBytes, isolatedBytes] =
await Promise.all([
fs.lstat(fixture.platformSessionFixtureSourcePath),
fs.lstat(fixture.platformSessionFixturePath),
fs.readFile(fixture.platformSessionFixtureSourcePath),
fs.readFile(fixture.platformSessionFixturePath),
]);
assert(
sourceMetadata.isFile() &&
!sourceMetadata.isSymbolicLink() &&
isolatedMetadata.isFile() &&
!isolatedMetadata.isSymbolicLink() &&
createHash('sha256').update(sourceBytes).digest('hex') ===
fixture.platformSessionFixtureSha256 &&
createHash('sha256').update(isolatedBytes).digest('hex') ===
fixture.platformSessionFixtureSha256,
'supervisor-autonomous-playable-platform-session-fixture-changed',
);
}
}
export async function verifySourceConfigLinkCountsRestored() {
@@ -2064,22 +1755,7 @@ export async function removeIsolatedSuiteAppData() {
} catch (error) {
ownershipError = error;
}
try {
await fs.rm(appDataDir, { recursive: true, force: false });
} finally {
if (state.isolatedRunner.platformSessionFixtureSourcePath) {
const previous = state.isolatedRunner.platformSessionFixturePreviousEnv;
if (previous === undefined) {
delete process.env[platformSessionFixtureEnv];
} else {
process.env[platformSessionFixtureEnv] = previous;
}
}
state.isolatedRunner.platformSessionFixturePath = null;
state.isolatedRunner.platformSessionFixtureSourcePath = null;
state.isolatedRunner.platformSessionFixtureSha256 = null;
state.isolatedRunner.platformSessionFixturePreviousEnv = undefined;
}
await fs.rm(appDataDir, { recursive: true, force: false });
state.runtimeConfigDir = state.options.configDir;
try {
await verifySourceConfigLinkCountsRestored();
@@ -13,15 +13,10 @@ import {
responseStreamSuite,
scopedAgentsSuite,
steerRunnerKillSuite,
supervisorAutonomousPlayableLaneDefenseSuite,
supervisorSwarmAutonomousChatSuite,
supervisorSwarmCollaborationPolicyMixedRecoverySuite,
supervisorSwarmFinalReplyTransientRetrySuite,
supervisorSwarmStaticIsolatedAutonomousChatSuite,
supervisorSwarmSuite,
supervisorSwarmToolPlanHandoffRunnerKillSuite,
supervisorSwarmTransientRetrySuite,
userInputRuntimeSuite,
webSearchSuite,
} from '../runtime-state.mjs';
import { collectApiKeys, isPathInside } from './io.mjs';
@@ -58,7 +53,6 @@ export function parseArguments(args) {
suite === responseStreamSuite ||
suite === webSearchSuite ||
suite === contextCompactionSuite ||
suite === userInputRuntimeSuite ||
suite === scopedAgentsSuite ||
suite === projectSkillSuite ||
suite === parallelReadSuite ||
@@ -66,10 +60,6 @@ export function parseArguments(args) {
suite === supervisorSwarmTransientRetrySuite ||
suite === supervisorSwarmFinalReplyTransientRetrySuite ||
suite === supervisorSwarmToolPlanHandoffRunnerKillSuite ||
suite === supervisorSwarmAutonomousChatSuite ||
suite === supervisorAutonomousPlayableLaneDefenseSuite ||
suite === supervisorSwarmStaticIsolatedAutonomousChatSuite ||
suite === supervisorSwarmCollaborationPolicyMixedRecoverySuite ||
suite === steerRunnerKillSuite ||
processSessionSuites.has(suite),
'unsupported-suite',
@@ -2,7 +2,6 @@ import {
assert,
codedError,
hashValue,
sleep,
throwIfShutdownRequested,
} from '../assertions/core.mjs';
import { fs, path, spawn, withLoopbackNoProxy } from '../dependencies.mjs';
@@ -13,14 +12,9 @@ import {
manifestPath,
state,
} from '../runtime-state.mjs';
import {
isSupervisorSwarmTransientRetrySuite,
recordSupervisorSwarmChatSessionFailureDiagnostic,
} from '../suites/supervisor-swarm.mjs';
import { isSupervisorSwarmTransientRetrySuite } from '../suites/supervisor-swarm.mjs';
import { isIsolatedRunnerSuite } from './reporting.mjs';
export const activeInteractiveCliSessions = new Set();
export async function prepareCliBinary() {
const cargo = process.platform === 'win32' ? 'cargo.exe' : 'cargo';
await runProcess(
@@ -84,8 +78,8 @@ export function buildCliChildEnvironment() {
NO_COLOR: '1',
RUST_BACKTRACE: '0',
};
// The deterministic playable suite copies its account fixture into the
// sibling isolated AppData directory. Set the path explicitly here so
// The isolated runner copies its account fixture into the sibling isolated
// AppData directory. Set the path explicitly here so
// every CLI and the Runner it launches use the isolated copy, even if the
// parent harness environment was restored or changed after setup.
if (state.isolatedRunner.platformSessionFixturePath) {
@@ -144,197 +138,6 @@ export function codedProcessError(code, details) {
return error;
}
export function startInteractiveCli(args) {
assert(Boolean(state.cliBinary), 'interactive-cli-binary-not-ready');
assert(Boolean(state.runtimeConfigDir), 'interactive-config-dir-not-ready');
if (
isIsolatedRunnerSuite() &&
state.options?.configDir &&
path.resolve(state.runtimeConfigDir) ===
path.resolve(state.options.configDir)
) {
state.isolatedRunner.sourceConfigCliCallCount += 1;
}
const child = spawn(
state.cliBinary,
[...args, '--config-dir', state.runtimeConfigDir],
{
cwd: appRoot,
env: buildCliChildEnvironment(),
stdio: ['pipe', 'pipe', 'pipe'],
},
);
return createInteractiveCliSession(child);
}
export function createInteractiveCliSession(child) {
activeCommandChildren.add(child);
const session = {
child,
stdout: Buffer.alloc(0),
stderr: Buffer.alloc(0),
exited: false,
exitInfo: null,
exitPromise: null,
closed: false,
closeInfo: null,
closePromise: null,
stdioClosed: false,
stdioCloseInfo: null,
spawnError: null,
stdinError: null,
};
activeInteractiveCliSessions.add(session);
session.exitPromise = new Promise((resolve) => {
const settle = (result) => {
if (session.exited) return;
activeCommandChildren.delete(child);
session.exited = true;
session.closed = true;
session.exitInfo = result;
session.closeInfo = result;
resolve(result);
};
child.once('error', (error) => {
session.spawnError = error;
settle({ code: null, signal: null, error });
});
child.once('exit', (code, signal) => {
settle({ code, signal, error: null });
});
});
session.closePromise = new Promise((resolve) => {
child.once('close', (code, signal) => {
activeCommandChildren.delete(child);
activeInteractiveCliSessions.delete(session);
session.stdioClosed = true;
session.stdioCloseInfo = {
code,
signal,
error: session.spawnError,
};
resolve(session.stdioCloseInfo);
});
});
child.stdin?.on('error', (error) => {
session.stdinError ??= error;
});
child.stdout.on('data', (chunk) => {
state.transcriptScanner?.scan('interactive-stdout', chunk);
state.projectPathTranscriptScanner?.scan('interactive-stdout', chunk);
state.formalConfigPathTranscriptScanner?.scan('interactive-stdout', chunk);
session.stdout = appendBounded(session.stdout, chunk, commandOutputLimit);
});
child.stderr.on('data', (chunk) => {
state.transcriptScanner?.scan('interactive-stderr', chunk);
state.projectPathTranscriptScanner?.scan('interactive-stderr', chunk);
state.formalConfigPathTranscriptScanner?.scan('interactive-stderr', chunk);
session.stderr = appendBounded(session.stderr, chunk, commandOutputLimit);
});
return session;
}
export function interactiveCliOutput(session) {
return `${session.stdout.toString('utf8')}\n${session.stderr.toString('utf8')}`;
}
export function writeInteractiveCliLine(session, line) {
assert(!session.closed, 'interactive-cli-already-closed');
assert(session.child.stdin.writable, 'interactive-cli-stdin-not-writable');
session.child.stdin.write(`${line}\n`);
}
export async function waitForInteractiveCliOutput(
session,
predicate,
code,
timeoutMs,
{ allowAfterProcessExit = false } = {},
) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const output = interactiveCliOutput(session);
if (predicate(output)) return output;
if (session.exited && !allowAfterProcessExit) {
if (session === state.supervisorSwarmCliSession) {
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
}
throw codedError(`${code}-cli-exited`);
}
if (session.stdioClosed) {
if (session === state.supervisorSwarmCliSession) {
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
}
throw codedError(`${code}-cli-stdio-closed`);
}
await sleep(50);
}
throw codedError(code);
}
export async function waitForInteractiveCliExit(session, timeoutMs) {
const result = await Promise.race([
session.exitPromise,
sleep(timeoutMs).then(() => null),
]);
if (!result) throw codedError('interactive-cli-exit-timeout');
if (result.error) throw codedError('interactive-cli-process-error');
assert(
result.code === 0 && result.signal === null,
'interactive-cli-exit-invalid',
);
return result;
}
export async function closeInteractiveCli(session) {
if (!session) return null;
if (session.exited) return session.exitInfo;
if (
session.child.stdin.writable &&
!session.child.stdin.writableEnded &&
!session.child.stdin.destroyed
) {
session.child.stdin.write('/quit\n');
}
let result = await Promise.race([
session.exitPromise,
sleep(3_000).then(() => null),
]);
if (!result && !session.exited) {
session.child.kill('SIGTERM');
result = await Promise.race([
session.exitPromise,
sleep(2_000).then(() => null),
]);
}
if (!result && !session.exited) {
session.child.kill('SIGKILL');
result = await Promise.race([
session.exitPromise,
sleep(5_000).then(() => null),
]);
}
assert(Boolean(result), 'interactive-cli-cleanup-timeout');
return result;
}
export async function waitForInteractiveCliStdioClose(session, timeoutMs) {
if (!session || session.stdioClosed) return session?.stdioCloseInfo ?? null;
const result = await Promise.race([
session.closePromise,
sleep(timeoutMs).then(() => null),
]);
if (!result) throw codedError('interactive-cli-stdio-close-timeout');
return result;
}
export function destroyInteractiveCliOutputStreams(session) {
if (!session) return;
for (const stream of [session.child.stdout, session.child.stderr]) {
if (stream && !stream.destroyed) stream.destroy();
}
}
export async function runProcess(
program,
args,
@@ -406,214 +209,6 @@ export async function runProcess(
});
}
export async function listSystemProcessIdentities() {
if (process.platform === 'win32') {
const systemRoot = process.env.SystemRoot ?? process.env.SYSTEMROOT;
assert(
typeof systemRoot === 'string' && path.isAbsolute(systemRoot),
'owned-process-snapshot-system-root-invalid',
);
const powershell = path.join(
systemRoot,
'System32/WindowsPowerShell/v1.0/powershell.exe',
);
const metadata = await fs.lstat(powershell);
assert(
metadata.isFile() && !metadata.isSymbolicLink(),
'owned-process-snapshot-powershell-invalid',
);
const result = await runProcess(
powershell,
[
'-NoProfile',
'-NonInteractive',
'-Command',
'$processes = @(Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CreationDate,Name); $processes | ConvertTo-Json -Compress',
],
{
cwd: appRoot,
timeoutMs: 30_000,
env: { ...process.env, NO_COLOR: '1', RUST_BACKTRACE: '0' },
},
);
const parsed = JSON.parse(result.stdout);
return (Array.isArray(parsed) ? parsed : [parsed])
.map((record) => ({
pid: Number(record?.ProcessId),
parentPid: Number(record?.ParentProcessId),
startedAt: String(record?.CreationDate ?? ''),
name: String(record?.Name ?? ''),
}))
.filter(validSystemProcessIdentity);
}
assert(
process.platform === 'linux' || process.platform === 'darwin',
'owned-process-snapshot-platform-unsupported',
);
const result = await runProcess(
'ps',
['-A', '-o', 'pid=', '-o', 'ppid=', '-o', 'lstart=', '-o', 'comm='],
{ cwd: appRoot, timeoutMs: 30_000 },
);
return result.stdout
.split(/\r?\n/u)
.map((line) => line.trim())
.filter(Boolean)
.map((line) => {
const fields = line.split(/\s+/u);
return {
pid: Number(fields[0]),
parentPid: Number(fields[1]),
startedAt: fields.slice(2, 7).join(' '),
name: fields.slice(7).join(' '),
};
})
.filter(validSystemProcessIdentity);
}
function validSystemProcessIdentity(record) {
return (
Number.isSafeInteger(record?.pid) &&
record.pid > 0 &&
Number.isSafeInteger(record.parentPid) &&
record.parentPid >= 0 &&
typeof record.startedAt === 'string' &&
record.startedAt.length > 0 &&
typeof record.name === 'string' &&
record.name.length > 0
);
}
export function buildOwnedProcessCleanupSnapshot(
processRecords,
{ rootPids = [], runnerPid = null, helperPids = [] } = {},
) {
assert(
Array.isArray(processRecords) &&
Array.isArray(rootPids) &&
Array.isArray(helperPids),
'owned-process-snapshot-input-invalid',
);
const records = processRecords.filter(validSystemProcessIdentity);
const byPid = new Map(records.map((record) => [record.pid, record]));
const childrenByParent = new Map();
for (const record of records) {
const children = childrenByParent.get(record.parentPid) ?? [];
children.push(record.pid);
childrenByParent.set(record.parentPid, children);
}
const normalizedRunnerPid = Number.isSafeInteger(runnerPid)
? runnerPid
: null;
const helperPidSet = new Set(
helperPids.filter((pid) => Number.isSafeInteger(pid) && pid > 0),
);
const roots = [
...new Set(
[...rootPids, normalizedRunnerPid, ...helperPidSet].filter(
(pid) => Number.isSafeInteger(pid) && pid > 0,
),
),
];
assert(roots.length > 0, 'owned-process-snapshot-root-missing');
const ownedPids = new Set();
const queue = [...roots];
while (queue.length > 0) {
const pid = queue.shift();
if (ownedPids.has(pid)) continue;
ownedPids.add(pid);
queue.push(...(childrenByParent.get(pid) ?? []));
}
const identities = [...ownedPids]
.map((pid) => byPid.get(pid))
.filter(Boolean)
.map((record) => ({
pid: record.pid,
startedAt: record.startedAt,
name: record.name,
kind: ownedProcessKind(record, normalizedRunnerPid, helperPidSet),
}))
.sort((left, right) => left.pid - right.pid);
return {
identities,
observedCounts: countOwnedProcessKinds(identities),
};
}
function ownedProcessKind(record, runnerPid, helperPids) {
if (record.pid === runnerPid) return 'runner';
if (helperPids.has(record.pid)) return 'helper';
const name = path.basename(record.name).toLowerCase();
if (/^node(?:\.exe)?$/u.test(name)) return 'node';
if (/^(?:chrome|chromium|msedge|google-chrome)(?:\.exe)?$/u.test(name)) {
return 'browser';
}
return 'command';
}
function countOwnedProcessKinds(identities) {
const counts = {
runner: 0,
helper: 0,
node: 0,
browser: 0,
command: 0,
total: identities.length,
};
for (const identity of identities) counts[identity.kind] += 1;
return counts;
}
export function inspectOwnedProcessCleanupResiduals(
snapshot,
processRecords,
{ activeCommandChildCount = 0, activeInteractiveCliSessionCount = 0 } = {},
) {
assert(
Array.isArray(snapshot?.identities) && Array.isArray(processRecords),
'owned-process-residual-input-invalid',
);
const currentByPid = new Map(
processRecords
.filter(validSystemProcessIdentity)
.map((record) => [record.pid, record]),
);
const residualIdentities = snapshot.identities.filter((identity) => {
const current = currentByPid.get(identity.pid);
return (
current?.startedAt === identity.startedAt &&
current?.name === identity.name
);
});
return {
residualCounts: countOwnedProcessKinds(residualIdentities),
activeCommandChildCount,
activeInteractiveCliSessionCount,
clean:
residualIdentities.length === 0 &&
activeCommandChildCount === 0 &&
activeInteractiveCliSessionCount === 0,
};
}
export async function captureOwnedProcessCleanupSnapshot(options) {
return buildOwnedProcessCleanupSnapshot(
await listSystemProcessIdentities(),
options,
);
}
export async function verifyOwnedProcessCleanupSnapshot(snapshot) {
return inspectOwnedProcessCleanupResiduals(
snapshot,
await listSystemProcessIdentities(),
{
activeCommandChildCount: activeCommandChildren.size,
activeInteractiveCliSessionCount: activeInteractiveCliSessions.size,
},
);
}
export function appendBounded(current, chunk, limit) {
const combined = Buffer.concat([current, chunk]);
return combined.length <= limit
@@ -28,12 +28,10 @@ import {
isGoalRuntimeSuite,
} from '../suites/goal.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
import {
isSupervisorSwarmSuite,
supervisorSwarmVerificationFixtureSource,
} from '../suites/supervisor-swarm.mjs';
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
import { isWebSearchSuite } from '../suites/web-search.mjs';
import { createSentinelOwnedTempDirectory } from './app-data.mjs';
import { effectiveAgentLlmConfig } from './config.mjs';
@@ -41,19 +39,15 @@ import { runProcess } from './process.mjs';
import { isIsolatedRunnerSuite } from './reporting.mjs';
export function requiredAgentIdsForSuite() {
return isUserInputRuntimeSuite()
? [projectSupervisorAgentId]
: isSupervisorAutonomousPlayableLaneDefenseSuite()
? [projectSupervisorAgentId]
: isSupervisorSwarmSuite()
? [
projectSupervisorAgentId,
supervisorSwarmDesignAgentId,
supervisorSwarmQualityAgentId,
]
: isIsolatedRunnerSuite()
? [mainAgentId]
: [mainAgentId, 'quality-review'];
return isSupervisorSwarmSuite()
? [
projectSupervisorAgentId,
supervisorSwarmDesignAgentId,
supervisorSwarmQualityAgentId,
]
: isIsolatedRunnerSuite()
? [mainAgentId]
: [mainAgentId, 'quality-review'];
}
export function expectedProviderBindingForSuite(config) {
@@ -120,11 +114,9 @@ export async function checkPrerequisites(config) {
return {
llmConfigured,
providerBinding,
chromeAvailable:
!isIsolatedRunnerSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
? Boolean(await findSupportedBrowser())
: false,
chromeAvailable: !isIsolatedRunnerSuite()
? Boolean(await findSupportedBrowser())
: false,
editorApiConfigured,
};
}
@@ -267,8 +259,7 @@ export async function seedDisposableProject({
? supervisorSwarmVerificationFixtureSource()
: isGoalRuntimeSuite() ||
isResponseStreamSuite() ||
isWebSearchSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
isWebSearchSuite()
? goalRevisionOneVerificationFixtureSource()
: goalRevisionTwoVerificationFixtureSource(),
),
@@ -405,23 +396,6 @@ export function seededGameHtml() {
`;
}
export function productionDefaultGameIndexHtml() {
return `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Genarrative Game Draft</title>
<style>
body { margin: 0; display: grid; min-height: 100vh; place-items: center; background: #101827; color: #d9e7ff; font: 16px system-ui, sans-serif; }
main { width: min(720px, calc(100vw - 32px)); }
</style>
</head>
<body><main>还没有生成游戏。回到聊天输入创意并确认生成后,这里会写入可试玩原型。</main></body>
</html>
`;
}
export function buildTaskPrompt(suite) {
const editorAssetOutcome =
suite === 'full'
@@ -20,9 +20,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 } from '../suites/supervisor-autonomous-playable.mjs';
import { isSupervisorSwarmSuite } from '../suites/supervisor-swarm.mjs';
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
import { isWebSearchSuite } from '../suites/web-search.mjs';
import { isPathInside, readJson } from './io.mjs';
@@ -88,17 +86,12 @@ export function buildSummary() {
config: state.config,
blocked: state.blocked,
run: {
agentId:
isUserInputRuntimeSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmSuite()
? projectSupervisorAgentId
: mainAgentId,
agentId: isSupervisorSwarmSuite()
? projectSupervisorAgentId
: mainAgentId,
runIdHash: hashValue(state.initialRunId),
sessionIdHash: hashValue(state.initialSessionId),
runnerKilled: isSupervisorAutonomousPlayableLaneDefenseSuite()
? false
: state.runnerKilled,
runnerKilled: state.runnerKilled,
resumed: state.resumed,
identityStable: state.identityStable,
},
@@ -110,11 +103,9 @@ export function buildSummary() {
projectPathReportLeakCount: state.projectPathReportLeakCount,
...(isWebSearchSuite() ||
isContextCompactionSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmSuite() ||
isSteerRunnerKillSuite()
? {
@@ -408,11 +399,9 @@ export function isIsolatedRunnerSuite() {
isResponseStreamSuite() ||
isWebSearchSuite() ||
isContextCompactionSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmSuite()
);
}
@@ -87,11 +87,7 @@ import {
import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
import {
confirmSupervisorSwarmPendingActionsInChat,
isSupervisorSwarmInteractiveChatSuite,
isSupervisorSwarmSuite,
} from '../suites/supervisor-swarm.mjs';
import { isSupervisorSwarmSuite } from '../suites/supervisor-swarm.mjs';
import { killRunnerPidOnce, verifyOwnedRunnerForKill } from './app-data.mjs';
import { isPlainObject } from './config.mjs';
import {
@@ -1038,13 +1034,6 @@ export async function confirmPendingActions(
allowedTools = null,
shouldConfirm = () => true,
) {
if (isSupervisorSwarmInteractiveChatSuite()) {
await confirmSupervisorSwarmPendingActionsInChat(
allowedTools,
shouldConfirm,
);
return;
}
for (const pending of await findPendingActions()) {
if (state.confirmedActionIds.has(pending.actionId)) continue;
if (!shouldConfirm(pending)) continue;
@@ -43,12 +43,6 @@ export const contextCompactionAppDataSentinelFileName =
export const contextCompactionAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-context-compaction-appdata.v1';
export const userInputAppDataSentinelFileName =
'.agent-runtime-real-e2e-user-input-appdata.json';
export const userInputAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-user-input-appdata.v1';
export const scopedAgentsAppDataSentinelFileName =
'.agent-runtime-real-e2e-scoped-agents-appdata.json';
@@ -109,30 +103,6 @@ export const supervisorSwarmToolPlanHandoffCheckpointReachedFileName =
export const supervisorSwarmToolPlanHandoffCheckpointReachedSchema =
'game-creator-tool-plan-handoff-runner-kill-checkpoint-reached.v1';
export const supervisorSwarmAutonomousChatAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-appdata.json';
export const supervisorSwarmAutonomousChatAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-appdata.v1';
export const supervisorAutonomousPlayableAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-autonomous-playable-appdata.json';
export const supervisorAutonomousPlayableAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-autonomous-playable-appdata.v1';
export const supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-appdata.json';
export const supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-appdata.v1';
export const supervisorSwarmCollaborationPolicyAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-appdata.json';
export const supervisorSwarmCollaborationPolicyAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-appdata.v1';
export const mainAgentId = 'code-prototype';
export const projectSupervisorAgentId = 'project-supervisor';
@@ -179,8 +149,6 @@ export const webSearchSuite = 'web-search';
export const contextCompactionSuite = 'context-compaction';
export const userInputRuntimeSuite = 'user-input-runtime';
export const scopedAgentsSuite = 'scoped-agents';
export const projectSkillSuite = 'project-skill';
@@ -213,18 +181,6 @@ export const supervisorSwarmTransientRetryBackoffMs = 30_000;
export const supervisorSwarmTransientRetryPreDueGuardMs = 1_500;
export const supervisorSwarmAutonomousChatSuite =
'supervisor-swarm-autonomous-chat';
export const supervisorAutonomousPlayableLaneDefenseSuite =
'supervisor-autonomous-playable-lane-defense';
export const supervisorSwarmStaticIsolatedAutonomousChatSuite =
'supervisor-swarm-static-isolated-autonomous-chat';
export const supervisorSwarmCollaborationPolicyMixedRecoverySuite =
'supervisor-swarm-collaboration-policy-mixed-recovery';
export const runtimeContextBundleSchemaVersion =
'game-creator-runtime-context-bundle.v5';
@@ -234,17 +190,6 @@ export const providerRequestLifecycleSchemaVersion =
export const providerActionBatchSchemaVersion =
'game-creator-provider-action-batch.v2';
export const runProfileBindingSchemaVersion =
'game-creator-run-profile-binding.v1';
export const autonomousCompletionContractSchemaVersion =
'game-creator-autonomous-completion-contract.v2';
export const autonomousPlaytestReceiptSchemaVersion =
'game-creator-autonomous-playtest-receipt.v2';
export const autonomousGameBuildRunProfile = 'autonomous-game-build';
export const supervisorCollaborationPolicySchemaVersion =
'game-creator-supervisor-collaboration-policy.v1';
@@ -287,12 +232,6 @@ export const contextCompactionTriggerTurns = new Set([4, 8]);
export const contextCompactionConstraintCanary = `GENARRATIVE_CONTEXT_CONSTRAINT_${randomUUID().replaceAll('-', '').slice(0, 20)}`;
export const userInputAnswerCanary = `GENARRATIVE_USER_CHOICE_${randomUUID()
.replaceAll('-', '')
.slice(0, 20)}`;
export const userInputAnswerText = `选择轻量像素风,优先保证移动端轮廓和动作可读性;确认标记 ${userInputAnswerCanary}`;
export const scopedAgentsRootCanary = `GENARRATIVE_SCOPE_ROOT_${randomUUID()
.replaceAll('-', '')
.slice(0, 20)}`;
@@ -461,143 +400,6 @@ export const supervisorSwarmConfirmedTools = [
'project.verify',
];
export const supervisorSwarmAutonomousTask =
'请把这个试玩项目推进到可以交给首批玩家体验的状态。以仓库现有的正式交付要求、临时检查要求和实际验证结果为准;按阶段生效的临时检查必须在各自生效后全部完成,不能把后续检查并入前置检查或漏掉。完成后简短说明交付内容、验证结论和仍需关注的问题。';
export const supervisorAutonomousPlayableLaneDefenseTask =
'做一个植物大战僵尸式的塔防游戏,要能选择植物、阻挡敌人、正常闯关,并且生成后可以直接试玩。';
export const supervisorAutonomousPlayableSourceFieldMaxChars = 8_000;
export const supervisorAutonomousPlayableSourceTotalMaxChars = 10_000;
export const supervisorAutonomousPlayableSafeStatuses = new Set([
'cancelling',
'budget-exhausted',
'cancelled',
'completed',
'failed',
'idle',
'needs-reconciliation',
'paused',
'pausing',
'pending',
'running',
'waiting-for-confirmation',
'waiting-for-user-input',
]);
export const supervisorAutonomousPlayableSafePhases = new Set([
'action',
'budget-exhausted',
'cancelled',
'cancelling',
'completion-contract-failed',
'completed',
'conversation-write-failed',
'executing',
'failed',
'finalizing',
'idle',
'needs-reconciliation',
'observation',
'parent-terminal',
'paused',
'pausing',
'planning',
'provider-action-batch',
'queued',
'response',
'running',
'waiting-for-agent',
'waiting-for-confirmation',
'waiting-for-delegate-receipts',
'waiting-for-isolated-join',
'waiting-for-process-session',
'waiting-for-provider-retry',
'waiting-for-user-input',
]);
export const supervisorAutonomousPlayableSafeDeliveryStatuses = new Set([
'claimed-by-parent',
'dispatched',
'ready',
'suppressed',
]);
export const supervisorAutonomousPlayableSafeTerminalStatuses = new Set([
'budget-exhausted',
'cancelled',
'completed',
'failed',
]);
export const supervisorAutonomousPlayableSafeTurnOutcomes = new Set([
'failed',
'incomplete',
'needs-reconciliation',
'settled',
]);
export const laneDefensePlaytestRequiredAssertions = [
'state-surface-valid',
'level-positive',
'start-control-clicked',
'start-sequence-advanced',
'start-phase-playing',
'defender-option-control-clicked',
'defender-selection-sequence-advanced',
'defender-selection-recorded',
'lane-cell-control-clicked',
'defender-placement-sequence-advanced',
'defender-count-increased',
'enemies-present-after-placement',
'speed-up-control-clicked',
'battle-sequence-advanced',
'battle-sequence-monotonic',
'enemy-position-changed',
'enemy-health-decreased',
'phase-won',
'next-level-control-clicked',
'next-level-sequence-advanced',
'level-increased',
'restart-control-clicked',
'restart-sequence-advanced',
'restart-phase-ready-or-playing',
];
export const supervisorSwarmAutonomousRoutingTerms = [
supervisorSwarmDesignAgentId,
supervisorSwarmQualityAgentId,
'agent.delegate',
'agent.run_status',
'同一个 planning 轮次',
'同轮',
'并行',
'返工',
'repair',
'一次',
'两个 Agent',
'两个专业',
'Runner',
'pidfd',
'actionId',
'delegationId',
];
export const supervisorSwarmCollaborationPolicyControlTerms = [
'.agent/collaboration-policy.json',
supervisorCollaborationPolicySchemaVersion,
'requiredInitialWave',
'minStaticDelegates',
'requiredStaticAgentIds',
'minIsolatedChildren',
'minIsolatedGroupsBeforeClaim',
'orchestratorOnlyAfterDelegation',
'policyFingerprint',
'contractFingerprint',
];
export const supervisorSwarmIsolatedReviews = [
{
path: 'e2e/isolated-a/evidence.txt',
@@ -625,17 +427,6 @@ export const supervisorSwarmIsolatedReviews = [
},
];
export const supervisorSwarmIsolatedReviewGroups = [
supervisorSwarmIsolatedReviews.slice(0, 2),
supervisorSwarmIsolatedReviews.slice(2),
];
export const supervisorSwarmInitialIsolatedReviews =
supervisorSwarmIsolatedReviewGroups[0];
export const supervisorSwarmFollowupIsolatedReviews =
supervisorSwarmIsolatedReviewGroups[1];
export const webSearchBaselineApiUrl =
'https://api.github.com/repos/nodejs/node/releases/latest';
@@ -703,8 +494,6 @@ export const pollIntervalMs = 750;
export const runTimeoutMs = 30 * 60 * 1000;
export const supervisorAutonomousPlayableRunTimeoutMs = 60 * 60 * 1000;
export const supervisorSwarmTerminalSidecarCleanupTimeoutMs = 10_000;
export const processRunnerKillStartTimeoutMs = 5 * 60 * 1000;
@@ -938,10 +727,6 @@ export class BlockedError extends Error {
export const isolatedRunnerState = {
appDataDir: null,
platformSessionFixturePath: null,
platformSessionFixtureSourcePath: null,
platformSessionFixtureSha256: null,
platformSessionFixturePreviousEnv: undefined,
ownerToken: null,
createdAt: 0,
current: null,
@@ -968,9 +753,6 @@ export const state = {
linuxPidfdPythonPath: null,
windowsProcessHandlePowerShellPath: null,
cleanupInProgress: false,
userInputCliSession: null,
supervisorSwarmCliSession: null,
supervisorAutonomousPlayableCliSession: null,
status: 'FAIL',
suite: null,
options: null,
@@ -1082,23 +864,6 @@ export const state = {
finalReplyFingerprint: null,
reportLeakCount: 0,
},
userInput: {
requestId: null,
responseId: null,
actionId: null,
questionMessageId: null,
answerMessageId: null,
questionCount: 0,
optionCount: 0,
providerStartedBeforeKill: 0,
providerStartedAfterRestart: 0,
conversationCountBeforeKill: 0,
conversationCountAfterRestart: 0,
oldRunnerBootId: null,
newRunnerBootId: null,
privateValues: [],
reportLeakCount: 0,
},
scopedAgents: {
effectiveModel: null,
effectiveApiKind: null,
@@ -1120,20 +885,6 @@ export const state = {
privateValues: [],
reportLeakCount: 0,
},
supervisorAutonomousPlayable: {
freshInitBaselineUsed: false,
initialGameIndexSha256: null,
expectedProviderBinding: null,
effectiveProviderBinding: null,
stdinWriteCount: 0,
stdinEnded: false,
stdinBytes: 0,
turnReport: null,
cliOutput: '',
privateValues: [],
reportLeakCount: 0,
ownedProcessCleanupSnapshot: null,
},
supervisorSwarm: {
effectiveModel: null,
effectiveApiKind: null,
@@ -1215,47 +966,7 @@ export const state = {
toolPlanHandoffLifecycleClosedExactlyOnce: false,
toolPlanHandoffAuditIdempotent: false,
toolPlanHandoffRecoveredPlanFingerprintMatched: false,
autonomousTaskRecipeFree: false,
autonomousRepositoryRecipeFree: false,
interactiveCliUsed: false,
chatSessionUnexpectedlyClosed: false,
chatSessionFailureKind: 'none',
chatSessionExitCode: 'none',
chatSessionCloseSignal: 'none',
chatSessionProcessErrorCode: 'none',
chatSessionStderrChars: 0,
chatSessionStderrSha256: 'none',
turnReport: null,
mixedSpawnActionId: null,
mixedSpawnRequestHash: null,
mixedFollowupSpawnActionId: null,
mixedFollowupSpawnRequestHash: null,
staticIsolatedProviderRequestIds: [],
staticIsolatedProviderOverlapObserved: false,
preKillMixedIdentity: null,
collaborationPolicyWritten: false,
collaborationPolicySnapshotInitialRecord: null,
collaborationPolicySnapshotInitialBytes: null,
collaborationPolicySnapshotInitialBytesSha256: null,
collaborationPolicySnapshotInitialIdentityHash: null,
collaborationPolicySnapshotBindingInitialRecord: null,
collaborationPolicySnapshotBindingInitialBytes: null,
collaborationPolicySnapshotBindingInitialBytesSha256: null,
chatSessionFailureDiagnostic: null,
collaborationPolicyDriftFixtureWritten: false,
collaborationPolicyDriftFixturePolicyFingerprint: null,
collaborationPolicyDriftedMinIsolatedGroupsBeforeClaim: null,
initialBatchRecoveryBoundaryObserved: false,
initialBatchRecoveryOldRunnerBootId: null,
initialBatchRecoveryNewRunnerBootId: null,
initialBatchRecoveryPreKillIdentity: null,
initialBatchRecoveryPostRecoveryIdentity: null,
initialBatchRecoveryPreKillSideEffects: null,
initialBatchRecoveryPostRecoverySideEffects: null,
initialBatchRecoveryIdentityStable: false,
initialBatchRecoveryConfirmationRestoredCount: 0,
initialBatchRecoveryPidfdClaimCount: 0,
initialBatchRecoveryPidfdSignalCount: 0,
},
confirmedActionIds: new Set(),
cleanupPerformed: false,
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,3 @@
export * from './supervisor-swarm/chat-session.mjs';
export * from './supervisor-swarm/collaboration-assertions.mjs';
export * from './supervisor-swarm/collaboration-policy.mjs';
export * from './supervisor-swarm/evidence-schema.mjs';
@@ -1,108 +0,0 @@
import {
assert,
codedError,
commandPassedMarker,
findPendingActions,
readRuntime,
safeProcessFailureDiagnostic,
state,
supervisorSwarmConfirmedTools,
supervisorSwarmDesignContent,
supervisorSwarmDesignPath,
supervisorSwarmQualityContent,
supervisorSwarmQualityPath,
supervisorSwarmWeakQualityContent,
visibleText,
waitForInteractiveCliOutput,
writeInteractiveCliLine,
} from './shared.mjs';
export function supervisorSwarmVerificationFixtureSource() {
return `import fs from 'node:fs';\nconst html = fs.readFileSync('game/index.html', 'utf8');\nconst agents = fs.readFileSync('AGENTS.md', 'utf8');\nconst optionalExact = (file, expected) => !fs.existsSync(file) || fs.readFileSync(file, 'utf8') === expected;\nconst passed = html.includes(${JSON.stringify(visibleText)}) && html.includes('<canvas') && html.includes('requestAnimationFrame') && agents.includes('SUPERVISOR_SWARM_REPOSITORY_CONTEXT') && optionalExact(${JSON.stringify(supervisorSwarmDesignPath)}, ${JSON.stringify(supervisorSwarmDesignContent)}) && [${JSON.stringify(supervisorSwarmWeakQualityContent)}, ${JSON.stringify(supervisorSwarmQualityContent)}].includes(fs.readFileSync(${JSON.stringify(supervisorSwarmQualityPath)}, 'utf8'));\nif (!passed) process.exit(1);\nconsole.log(${JSON.stringify(commandPassedMarker)});\n`;
}
export function recordSupervisorSwarmChatSessionFailureDiagnostic(session) {
const diagnostic = session
? safeProcessFailureDiagnostic({
...session.closeInfo,
stdout: session.stdout,
stderr: session.stderr,
})
: {
failureKind: 'missing',
exitCode: 'none',
signal: 'none',
processErrorCode: 'none',
stderrChars: 0,
stderrSha256: 'none',
};
state.supervisorSwarm.chatSessionFailureDiagnostic = diagnostic;
return diagnostic;
}
export function supervisorSwarmChatSessionFailureEvidence() {
const diagnostic = state.supervisorSwarm.chatSessionFailureDiagnostic;
return {
chatSessionUnexpectedlyClosed: Boolean(diagnostic),
chatSessionFailureKind: diagnostic?.failureKind ?? 'none',
chatSessionExitCode: diagnostic?.exitCode ?? 'none',
chatSessionCloseSignal: diagnostic?.signal ?? 'none',
chatSessionProcessErrorCode: diagnostic?.processErrorCode ?? 'none',
chatSessionStderrChars: diagnostic?.stderrChars ?? 0,
chatSessionStderrSha256: diagnostic?.stderrSha256 ?? 'none',
};
}
export function requireOpenSupervisorSwarmChatSession() {
const session = state.supervisorSwarmCliSession;
if (session && !session.closed) return session;
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
throw codedError('supervisor-swarm-chat-session-missing');
}
export async function confirmSupervisorSwarmPendingActionsInChat(
allowedTools,
shouldConfirm,
) {
const session = requireOpenSupervisorSwarmChatSession();
const allowed = new Set(
allowedTools ? [...allowedTools] : supervisorSwarmConfirmedTools,
);
const candidates = (await findPendingActions()).filter((pending) => {
if (state.confirmedActionIds.has(pending.actionId)) return false;
if (!shouldConfirm(pending)) return false;
assert(
allowed.has(pending.tool),
`pending-tool-not-allowed-in-scenario:${pending.tool}`,
);
return true;
});
if (candidates.length === 0) return;
const output = await waitForInteractiveCliOutput(
session,
(value) =>
candidates.some((pending) =>
value.includes(
`[待确认] agent=${pending.agentId} run=${pending.runId} action=${pending.actionId} tool=${pending.tool}`,
),
),
'supervisor-swarm-chat-confirmation-prompt-timeout',
120_000,
);
const pending = candidates.find((candidate) =>
output.includes(
`[待确认] agent=${candidate.agentId} run=${candidate.runId} action=${candidate.actionId} tool=${candidate.tool}`,
),
);
assert(pending, 'supervisor-swarm-chat-prompted-action-missing');
const runtime = await readRuntime(pending.agentId);
assert(runtime.runId === pending.runId, 'pending-run-mismatch');
writeInteractiveCliLine(session, 'approve');
await waitForInteractiveCliOutput(
session,
(output) => output.includes(`[已批准] ${pending.actionId}`),
'supervisor-swarm-chat-confirmation-result-timeout',
120_000,
);
state.confirmedActionIds.add(pending.actionId);
}
@@ -8,30 +8,6 @@ export function supervisorSwarmEvidenceFieldTemplate() {
return {
scenario: 'project-supervisor-dual-delegate-single-repair-runner-recovery',
targetAgentId: projectSupervisorAgentId,
autonomousModeEnabled: false,
autonomousTaskRecipeFree: false,
autonomousRepositoryRecipeFree: false,
interactiveCliUsed: false,
chatSessionUnexpectedlyClosed: false,
chatSessionFailureKind: 'none',
chatSessionExitCode: 'none',
chatSessionCloseSignal: 'none',
chatSessionProcessErrorCode: 'none',
chatSessionStderrChars: 0,
chatSessionStderrSha256: 'none',
turnReportCaptured: false,
turnReportOutcome: 'not-requested',
turnReportParentIdentityStable: false,
turnReportRuntimeCount: 0,
turnReportBusyRuntimeCount: 0,
turnReportPendingTaskCount: 0,
turnReportRunningTaskCount: 0,
turnReportWaitingForConfirmationCount: 0,
turnReportWaitingForUserInputCount: 0,
turnReportNewAssistantMessageCount: 0,
turnReportFinalReplyChars: 0,
turnReportReconciliationAgentCount: 0,
turnReportPrivateLeakCount: 0,
providerModel: null,
providerApiKind: null,
providerReasoningEffort: null,
@@ -124,46 +100,7 @@ export function supervisorSwarmEvidenceFieldTemplate() {
duplicateCollaborationPolicySnapshotBindingCount: 0,
collaborationPolicySnapshotResidualArtifactCount: 0,
collaborationPolicySnapshotBindingResidualArtifactCount: 0,
initialBatchRecoveryRequired: false,
initialBatchRecoveryBoundaryObserved: false,
initialBatchRecoveryBatchIdStable: false,
initialBatchRecoveryPolicyFingerprintStable: false,
initialBatchRecoveryContractFingerprintStable: false,
initialBatchRecoveryAllActionIdsStable: false,
initialBatchRecoveryProviderStartedIdentitySetStable: false,
initialBatchRecoveryWaitingConfirmationStable: false,
initialBatchRecoveryZeroSideEffects: false,
initialBatchRecoveryPreKillDeliveryCount: 0,
initialBatchRecoveryPostRecoveryDeliveryCount: 0,
initialBatchRecoveryPreKillGroupCount: 0,
initialBatchRecoveryPostRecoveryGroupCount: 0,
initialBatchRecoveryPreKillChildCount: 0,
initialBatchRecoveryPostRecoveryChildCount: 0,
initialBatchRecoveryPreKillProjectRevision: 0,
initialBatchRecoveryPostRecoveryProjectRevision: 0,
initialBatchRecoveryPreKillProjectModificationCount: 0,
initialBatchRecoveryPostRecoveryProjectModificationCount: 0,
initialBatchRecoveryPreKillActionExecutionCount: 0,
initialBatchRecoveryPostRecoveryActionExecutionCount: 0,
initialBatchRecoveryPreKillActionReceiptCount: 0,
initialBatchRecoveryPostRecoveryActionReceiptCount: 0,
initialBatchRecoveryConfirmationRestoredCount: 0,
initialBatchRecoveryRunnerBootChanged: false,
initialBatchRecoveryPidfdClaimCount: 0,
initialBatchRecoveryPidfdSignalCount: 0,
nativeDualDelegatePlanCount: 0,
mixedModeEnabled: false,
mixedSpawnActionCaptured: false,
mixedFollowupSpawnActionCaptured: false,
mixedSpawnRequestHashStable: false,
mixedSpawnConfirmationRequiredCount: 0,
mixedSpawnApprovalCount: 0,
mixedSpawnConfirmationOrderValid: false,
nativeMixedCollaborationPlanCount: 0,
nativeFollowupIsolatedPlanCount: 0,
staticIsolatedProviderOverlapObserved: false,
staticIsolatedProviderRequestIdentityCount: 0,
mixedParentIdentityStable: false,
initialDeliveryCount: 0,
repairDeliveryCount: 0,
repairDeliveryStatus: 'absent',
@@ -1,12 +1,4 @@
import {
recordSupervisorSwarmChatSessionFailureDiagnostic,
requireOpenSupervisorSwarmChatSession,
} from './chat-session.mjs';
import {
captureSupervisorSwarmCollaborationPolicySnapshotAndDrift,
captureSupervisorSwarmInitialProviderBatch,
restartSupervisorSwarmRunnerAtInitialBatchBoundary,
} from './collaboration-policy.mjs';
import { captureSupervisorSwarmInitialProviderBatch } from './collaboration-policy.mjs';
import { validateSupervisorSwarmEvidence } from './evidence-validation.mjs';
import {
driveSupervisorSwarmRuntimeToCompletion,
@@ -21,28 +13,19 @@ import {
import {
assert,
claimOwnedRunner,
codedError,
ensureOwnedRunnerStableKillSupport,
hashValue,
isNonEmptyString,
isPlainObject,
prepareCliBinary,
projectSupervisorAgentId,
readRuntime,
requestedRunId,
runCli,
sleep,
startInteractiveCli,
state,
supervisorSwarmSessionId,
waitForInteractiveCliExit,
waitForInteractiveCliOutput,
writeInteractiveCliLine,
} from './shared.mjs';
import {
isSupervisorSwarmFinalReplyTransientRetrySuite,
isSupervisorSwarmInitialTransientRetrySuite,
isSupervisorSwarmInteractiveChatSuite,
isSupervisorSwarmToolPlanHandoffRunnerKillSuite,
} from './suite-selection.mjs';
import { captureSupervisorSwarmToolPlanHandoffRunnerKillCheckpoint } from './tool-plan-handoff.mjs';
@@ -62,42 +45,20 @@ export async function runSupervisorSwarmE2e() {
chars: [...task].length,
sha256: hashValue(task),
};
if (isSupervisorSwarmInteractiveChatSuite()) {
state.isolatedRunner.launchAttempted = true;
state.supervisorSwarmCliSession = startInteractiveCli([
'--swarm-chat',
state.initialRunId = requestedRunId;
state.initialSessionId = supervisorSwarmSessionId;
await runCli(
[
'--agent-enqueue',
'--init',
state.projectRoot,
]);
await waitForInteractiveCliOutput(
state.supervisorSwarmCliSession,
(output) =>
output.includes('Agent Swarm Chat') &&
output.includes(`父 Agent${projectSupervisorAgentId}`),
'supervisor-swarm-autonomous-chat-banner-timeout',
30_000,
);
state.supervisorSwarm.interactiveCliUsed = true;
writeInteractiveCliLine(state.supervisorSwarmCliSession, task);
const started = await waitForSupervisorSwarmAutonomousParentRuntime(task);
state.initialRunId = started.runId;
state.initialSessionId = started.sessionId;
} else {
state.initialRunId = requestedRunId;
state.initialSessionId = supervisorSwarmSessionId;
await runCli(
[
'--agent-enqueue',
'--init',
state.projectRoot,
projectSupervisorAgentId,
state.initialRunId,
task,
],
{ timeoutMs: 120_000 },
);
state.isolatedRunner.launchAttempted = true;
}
projectSupervisorAgentId,
state.initialRunId,
task,
],
{ timeoutMs: 120_000 },
);
state.isolatedRunner.launchAttempted = true;
await claimOwnedRunner();
const runtime = await readRuntime(projectSupervisorAgentId);
assert(
@@ -114,86 +75,11 @@ export async function runSupervisorSwarmE2e() {
await captureSupervisorSwarmToolPlanHandoffRunnerKillCheckpoint();
}
await captureSupervisorSwarmInitialProviderBatch();
await captureSupervisorSwarmCollaborationPolicySnapshotAndDrift();
await restartSupervisorSwarmRunnerAtInitialBatchBoundary();
await driveSupervisorSwarmToRepairKillBoundary();
if (isSupervisorSwarmFinalReplyTransientRetrySuite()) {
await captureSupervisorSwarmTransientRetryCheckpoint();
}
await driveSupervisorSwarmRuntimeToCompletion();
if (isSupervisorSwarmInteractiveChatSuite()) {
await captureSupervisorSwarmAutonomousTurnReport();
writeInteractiveCliLine(state.supervisorSwarmCliSession, '/quit');
await waitForInteractiveCliExit(state.supervisorSwarmCliSession, 30_000);
state.supervisorSwarmCliSession = null;
}
state.evidence = await validateSupervisorSwarmEvidence();
assert(state.evidence.secretLeakCount === 0, 'loaded-key-leak-detected');
}
export async function waitForSupervisorSwarmAutonomousParentRuntime(task) {
const deadline = Date.now() + 120_000;
while (Date.now() < deadline) {
const runtime = await readRuntime(projectSupervisorAgentId).catch(
() => null,
);
if (
runtime?.agentId === projectSupervisorAgentId &&
runtime.sessionId === supervisorSwarmSessionId &&
isNonEmptyString(runtime.runId) &&
runtime.currentTask === task
) {
return runtime;
}
if (state.supervisorSwarmCliSession?.closed) {
recordSupervisorSwarmChatSessionFailureDiagnostic(
state.supervisorSwarmCliSession,
);
throw codedError('supervisor-swarm-autonomous-chat-closed-before-run');
}
await sleep(50);
}
throw codedError('supervisor-swarm-autonomous-parent-runtime-timeout');
}
export async function captureSupervisorSwarmAutonomousTurnReport() {
const session = requireOpenSupervisorSwarmChatSession();
const output = await waitForInteractiveCliOutput(
session,
(value) => value.includes('[turn.report] '),
'supervisor-swarm-autonomous-turn-report-timeout',
60_000,
);
const reportLines = output
.split(/\r?\n/u)
.filter((line) => line.startsWith('[turn.report] '));
assert(
reportLines.length === 1,
'supervisor-swarm-autonomous-turn-report-count-invalid',
);
const report = JSON.parse(reportLines[0].slice('[turn.report] '.length));
assert(
isPlainObject(report) &&
JSON.stringify(Object.keys(report).sort()) ===
JSON.stringify(
[
'schemaVersion',
'outcome',
'parentAgentId',
'sessionId',
'parentRunId',
'runtimeCount',
'busyRuntimeCount',
'pendingTaskCount',
'runningTaskCount',
'waitingForConfirmationCount',
'waitingForUserInputCount',
'newAssistantMessageCount',
'finalReplyChars',
'reconciliationAgentCount',
].sort(),
),
'supervisor-swarm-autonomous-turn-report-shape-invalid',
);
state.supervisorSwarm.turnReport = report;
}
@@ -1,5 +1,4 @@
import {
supervisorSwarmMixedSpawnRequestHashesStable,
supervisorSwarmParentDeliveries,
supervisorSwarmRelevantRunKeys,
} from './collaboration-assertions.mjs';
@@ -8,7 +7,6 @@ import {
duplicateSupervisorSwarmCollaborationPolicySnapshotCount,
inspectSupervisorSwarmCollaborationPolicySnapshot,
inspectSupervisorSwarmCollaborationPolicySnapshotBinding,
readSupervisorSwarmCollaborationPolicySnapshotBindingFile,
readSupervisorSwarmCollaborationPolicySnapshotFile,
supervisorSwarmCollaborationPolicyDriftObservationCount,
supervisorSwarmExpectedCollaborationPolicySnapshot,
@@ -59,17 +57,9 @@ import {
supervisorSwarmQualityAgentId,
supervisorSwarmQualityContent,
supervisorSwarmQualityPath,
supervisorSwarmSessionId,
supervisorSwarmWeakQualityContent,
} from './shared.mjs';
import {
isSupervisorSwarmCollaborationPolicyMixedRecoverySuite,
isSupervisorSwarmFinalReplyTransientRetrySuite,
isSupervisorSwarmMixedHarnessSuite,
isSupervisorSwarmMultiIsolatedHarnessSuite,
supervisorSwarmExpectedIsolatedReviewGroups,
supervisorSwarmInitialIsolatedReviewsForSuite,
} from './suite-selection.mjs';
import { isSupervisorSwarmFinalReplyTransientRetrySuite } from './suite-selection.mjs';
export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
const persistence = await readSupervisorSwarmPersistence({
@@ -125,127 +115,6 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
record.recordType === 'agent.runtime.provider_request.lifecycle' &&
relevantRuns.has(`${record.agentId}\0${record.runId}`),
);
const indexedAgentDb = persistence.agentDb.map((record, index) => ({
record,
index,
}));
const initialBatchActions =
state.supervisorSwarm.initialProviderBatch?.actions ?? [];
const mixedSpawnAction = initialBatchActions.find(
(action) => action.actionId === state.supervisorSwarm.mixedSpawnActionId,
);
const matchesMixedInitialAction = (record, action) =>
action != null &&
record.agentId === projectSupervisorAgentId &&
record.runId === state.initialRunId &&
record.actionId === action.actionId &&
record.actionFingerprint === action.actionFingerprint &&
record.tool === action.tool;
const mixedSpawnConfirmationRequirements = indexedAgentDb.filter(
({ record }) =>
record.recordType ===
'agent.runtime.provider_action_batch.confirmation_required' &&
record.sessionId === supervisorSwarmSessionId &&
record.batchId === state.supervisorSwarm.initialProviderBatch?.batchId &&
matchesMixedInitialAction(record, mixedSpawnAction),
);
const mixedSpawnApprovals = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_confirmation.approved' &&
record.sessionId === supervisorSwarmSessionId &&
record.confirmedRunId === state.initialRunId &&
matchesMixedInitialAction(record, mixedSpawnAction),
);
const mixedDelegateExecutionIndexes = initialBatchActions
.filter((action) => action.tool === 'agent.delegate')
.flatMap((action) =>
indexedAgentDb
.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_action.executing' &&
matchesMixedInitialAction(record, action),
)
.map(({ index }) => index),
);
const mixedSpawnReceiptIndexes = indexedAgentDb
.filter(
({ record }) =>
record.recordType === 'agent.runtime.action_receipt' &&
record.sessionId === supervisorSwarmSessionId &&
record.status === 'ok' &&
matchesMixedInitialAction(record, mixedSpawnAction),
)
.map(({ index }) => index);
const mixedInitialSpawnConfirmationOrderValid =
mixedSpawnConfirmationRequirements.length === 1 &&
mixedSpawnApprovals.length === 1 &&
mixedDelegateExecutionIndexes.length === 2 &&
mixedSpawnReceiptIndexes.length === 1 &&
mixedSpawnConfirmationRequirements[0].index <
mixedSpawnApprovals[0].index &&
mixedDelegateExecutionIndexes.every(
(index) => mixedSpawnApprovals[0].index < index,
) &&
mixedSpawnApprovals[0].index < mixedSpawnReceiptIndexes[0];
const matchesMixedFollowupIdentity = (record) =>
isNonEmptyString(state.supervisorSwarm.mixedFollowupSpawnActionId) &&
record.agentId === projectSupervisorAgentId &&
(record.runId === state.initialRunId ||
record.confirmedRunId === state.initialRunId) &&
record.actionId === state.supervisorSwarm.mixedFollowupSpawnActionId;
const matchesMixedFollowupAction = (record) =>
matchesMixedFollowupIdentity(record) &&
(record.tool === 'agent.spawn_isolated' ||
record.commandId === 'agent.spawn_isolated');
const mixedFollowupConfirmationRequirements = indexedAgentDb.filter(
({ record }) =>
record.recordType ===
'agent.runtime.provider_action_batch.confirmation_required' &&
record.sessionId === supervisorSwarmSessionId &&
matchesMixedFollowupAction(record),
);
const mixedFollowupApprovals = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_confirmation.approved' &&
record.sessionId === supervisorSwarmSessionId &&
matchesMixedFollowupAction(record),
);
const mixedFollowupSideEffects = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.agent.spawn_isolated' &&
matchesMixedFollowupIdentity(record),
);
const mixedFollowupReceiptIndexes = indexedAgentDb
.filter(
({ record }) =>
record.recordType === 'agent.runtime.action_receipt' &&
record.status === 'ok' &&
matchesMixedFollowupAction(record),
)
.map(({ index }) => index);
const mixedFollowupObservationIndexes = indexedAgentDb
.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_observation' &&
record.status === 'ok' &&
matchesMixedFollowupAction(record),
)
.map(({ index }) => index);
const mixedFollowupSpawnConfirmationOrderValid =
mixedFollowupConfirmationRequirements.length === 1 &&
mixedFollowupApprovals.length === 1 &&
mixedFollowupSideEffects.length === 1 &&
mixedFollowupReceiptIndexes.length === 1 &&
mixedFollowupObservationIndexes.length === 1 &&
mixedFollowupConfirmationRequirements[0].index <
mixedFollowupApprovals[0].index &&
mixedFollowupApprovals[0].index < mixedFollowupSideEffects[0].index &&
mixedFollowupSideEffects[0].index < mixedFollowupReceiptIndexes[0] &&
mixedFollowupReceiptIndexes[0] < mixedFollowupObservationIndexes[0];
const mixedSpawnConfirmationOrderValid =
mixedInitialSpawnConfirmationOrderValid &&
(!isSupervisorSwarmMultiIsolatedHarnessSuite() ||
mixedFollowupSpawnConfirmationOrderValid);
const toleratePartialRead = async (
surface,
reader,
@@ -278,7 +147,6 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
isolatedEvidenceContents,
changedFiles,
collaborationPolicySnapshotFile,
collaborationPolicySnapshotBindingFile,
collaborationPolicySidecar,
finalReplyResponseStream,
] = await Promise.all([
@@ -359,14 +227,6 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
: null,
null,
),
toleratePartialRead(
'collaboration-policy-snapshot-binding',
() =>
supervisorSwarmInitialBatchBindsPolicySnapshot()
? readSupervisorSwarmCollaborationPolicySnapshotBindingFile()
: null,
null,
),
toleratePartialRead(
'collaboration-policy-sidecar',
() => readJson(supervisorSwarmCollaborationPolicyPath()),
@@ -469,63 +329,12 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
partialCollaborationPolicySnapshots[0],
supervisorSwarmExpectedCollaborationPolicySnapshot(),
);
const partialCollaborationPolicySnapshotBytesStable =
isNonEmptyString(
state.supervisorSwarm.collaborationPolicySnapshotInitialBytes,
) &&
collaborationPolicySnapshotFile?.bytes ===
state.supervisorSwarm.collaborationPolicySnapshotInitialBytes &&
hashValue(collaborationPolicySnapshotFile.bytes) ===
state.supervisorSwarm.collaborationPolicySnapshotInitialBytesSha256;
const partialCollaborationPolicySnapshotFieldsStable =
isPlainObject(
state.supervisorSwarm.collaborationPolicySnapshotInitialRecord,
) &&
isPlainObject(collaborationPolicySnapshotFile?.snapshot) &&
JSON.stringify(collaborationPolicySnapshotFile.snapshot) ===
JSON.stringify(
state.supervisorSwarm.collaborationPolicySnapshotInitialRecord,
) &&
JSON.stringify(collaborationPolicySnapshotFile.snapshot) ===
JSON.stringify(partialCollaborationPolicySnapshots[0]);
const partialCollaborationPolicySnapshotFingerprintStable =
partialCollaborationPolicySnapshotFieldsStable &&
collaborationPolicySnapshotFile.snapshot.snapshotFingerprint ===
state.supervisorSwarm.collaborationPolicySnapshotInitialRecord
.snapshotFingerprint &&
partialCollaborationPolicySnapshotInspection.identityHash ===
state.supervisorSwarm.collaborationPolicySnapshotInitialIdentityHash;
const partialCollaborationPolicySnapshotPolicyFingerprintStable =
partialCollaborationPolicySnapshotFieldsStable &&
collaborationPolicySnapshotFile.snapshot.policyFingerprint ===
state.supervisorSwarm.collaborationPolicySnapshotInitialRecord
.policyFingerprint;
const partialCollaborationPolicySnapshotBindingInspection =
inspectSupervisorSwarmCollaborationPolicySnapshotBinding(
partialCollaborationPolicySnapshotBindings[0],
collaborationPolicySnapshotFile?.snapshot ??
partialCollaborationPolicySnapshots[0],
);
const partialCollaborationPolicySnapshotBindingBytesStable =
isNonEmptyString(
state.supervisorSwarm.collaborationPolicySnapshotBindingInitialBytes,
) &&
collaborationPolicySnapshotBindingFile?.bytes ===
state.supervisorSwarm.collaborationPolicySnapshotBindingInitialBytes &&
hashValue(collaborationPolicySnapshotBindingFile.bytes) ===
state.supervisorSwarm
.collaborationPolicySnapshotBindingInitialBytesSha256;
const partialCollaborationPolicySnapshotBindingFieldsStable =
isPlainObject(
state.supervisorSwarm.collaborationPolicySnapshotBindingInitialRecord,
) &&
isPlainObject(collaborationPolicySnapshotBindingFile?.binding) &&
JSON.stringify(collaborationPolicySnapshotBindingFile.binding) ===
JSON.stringify(
state.supervisorSwarm.collaborationPolicySnapshotBindingInitialRecord,
) &&
JSON.stringify(collaborationPolicySnapshotBindingFile.binding) ===
JSON.stringify(partialCollaborationPolicySnapshotBindings[0]);
const partialCollaborationPolicyDriftObservationCount =
supervisorSwarmCollaborationPolicyDriftObservationCount(
persistence.agentDb,
@@ -538,62 +347,9 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
duplicateSupervisorSwarmCollaborationPolicySnapshotBindingCount(
partialCollaborationPolicySnapshotBindings,
);
const partialChatSessionFailureDiagnostic =
state.supervisorSwarm.chatSessionFailureDiagnostic;
const partialIdentityBefore =
state.supervisorSwarm.initialBatchRecoveryPreKillIdentity;
const partialIdentityAfter =
state.supervisorSwarm.initialBatchRecoveryPostRecoveryIdentity;
const partialSideEffectsBefore =
state.supervisorSwarm.initialBatchRecoveryPreKillSideEffects;
const partialSideEffectsAfter =
state.supervisorSwarm.initialBatchRecoveryPostRecoverySideEffects;
const partialRecoveryRequired =
isSupervisorSwarmCollaborationPolicyMixedRecoverySuite();
const zeroSideEffectFields = [
'deliveryCount',
'claimCount',
'isolatedGroupCount',
'isolatedChildCount',
'isolatedResultCount',
'isolatedJoinDeliveryCount',
'delegatedChildTaskCount',
'projectRevision',
'projectModifiedPathCount',
'projectMutationActionCount',
'initialActionExecutionCount',
'initialActionReceiptCount',
'initialActionSideEffectCount',
'recoveryFailedCount',
];
const partialRecoveryZeroSideEffects =
partialRecoveryRequired &&
partialSideEffectsBefore != null &&
partialSideEffectsAfter != null &&
zeroSideEffectFields.every(
(field) =>
partialSideEffectsBefore[field] === 0 &&
partialSideEffectsAfter[field] === 0,
);
return buildSupervisorSwarmEvidence({
...baseEvidence,
scenario: partialRecoveryRequired
? 'project-supervisor-collaboration-policy-mixed-initial-batch-recovery'
: baseEvidence.scenario,
interactiveCliUsed: state.supervisorSwarm.interactiveCliUsed,
chatSessionUnexpectedlyClosed: Boolean(partialChatSessionFailureDiagnostic),
chatSessionFailureKind:
partialChatSessionFailureDiagnostic?.failureKind ?? 'none',
chatSessionExitCode:
partialChatSessionFailureDiagnostic?.exitCode ?? 'none',
chatSessionCloseSignal:
partialChatSessionFailureDiagnostic?.signal ?? 'none',
chatSessionProcessErrorCode:
partialChatSessionFailureDiagnostic?.processErrorCode ?? 'none',
chatSessionStderrChars:
partialChatSessionFailureDiagnostic?.stderrChars ?? 0,
chatSessionStderrSha256:
partialChatSessionFailureDiagnostic?.stderrSha256 ?? 'none',
scenario: baseEvidence.scenario,
providerModel: state.supervisorSwarm.effectiveModel,
providerApiKind: state.supervisorSwarm.effectiveApiKind,
providerReasoningEffort: state.supervisorSwarm.effectiveReasoningEffort,
@@ -701,12 +457,8 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
?.orchestratorOnlyAfterDelegation ?? false,
initialCollaborationContractCountsMatched:
partialCollaborationContract?.initialStaticAgentIds?.length === 2 &&
partialCollaborationContract?.isolatedSpawnCount ===
(isSupervisorSwarmMixedHarnessSuite() ? 1 : 0) &&
partialCollaborationContract?.isolatedChildCount ===
(isSupervisorSwarmMixedHarnessSuite()
? supervisorSwarmInitialIsolatedReviewsForSuite().length
: 0),
partialCollaborationContract?.isolatedSpawnCount === 0 &&
partialCollaborationContract?.isolatedChildCount === 0,
initialCollaborationPolicyFingerprint:
partialCollaborationContract?.policyFingerprint ?? null,
initialCollaborationContractFingerprint:
@@ -750,19 +502,6 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
partialCollaborationPolicySnapshotInspection.identityHashMatched,
collaborationPolicySnapshotBoundAtValid:
partialCollaborationPolicySnapshotInspection.boundAtValid,
collaborationPolicySnapshotBytesStable:
partialCollaborationPolicySnapshotBytesStable,
collaborationPolicySnapshotFieldsStable:
partialCollaborationPolicySnapshotFieldsStable,
collaborationPolicySnapshotFingerprintStable:
partialCollaborationPolicySnapshotFingerprintStable,
collaborationPolicySnapshotPolicyFingerprintStable:
partialCollaborationPolicySnapshotPolicyFingerprintStable,
collaborationPolicySnapshotStable:
partialCollaborationPolicySnapshotBytesStable &&
partialCollaborationPolicySnapshotFieldsStable &&
partialCollaborationPolicySnapshotFingerprintStable &&
partialCollaborationPolicySnapshotPolicyFingerprintStable,
collaborationPolicySnapshotBindingCaptured:
partialCollaborationPolicySnapshotRequired &&
partialCollaborationPolicySnapshotBindingInspection.exactShape,
@@ -781,16 +520,6 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
partialCollaborationPolicySnapshotBindingInspection.boundAtMatched,
collaborationPolicySnapshotBindingSnapshotMatched:
partialCollaborationPolicySnapshotBindingInspection.snapshotMatched,
collaborationPolicySnapshotBindingBytesStable:
partialCollaborationPolicySnapshotBindingBytesStable,
collaborationPolicySnapshotBindingFieldsStable:
partialCollaborationPolicySnapshotBindingFieldsStable,
collaborationPolicySnapshotBindingStable:
partialCollaborationPolicySnapshotBindingBytesStable &&
partialCollaborationPolicySnapshotBindingFieldsStable &&
partialCollaborationPolicySnapshotBindingInspection.snapshotMatched,
collaborationPolicyDriftFixtureWritten:
state.supervisorSwarm.collaborationPolicyDriftFixtureWritten,
collaborationPolicySidecarMinIsolatedGroupsBeforeClaim:
collaborationPolicySidecar?.minIsolatedGroupsBeforeClaim ?? 0,
collaborationPolicyDriftStatusObserved:
@@ -805,142 +534,6 @@ export async function collectPartialSupervisorSwarmEvidence(baseEvidence) {
residualSidecars.collaborationPolicySnapshotArtifacts,
collaborationPolicySnapshotBindingResidualArtifactCount:
residualSidecars.collaborationPolicySnapshotBindingArtifacts,
initialBatchRecoveryRequired: partialRecoveryRequired,
initialBatchRecoveryBoundaryObserved:
state.supervisorSwarm.initialBatchRecoveryBoundaryObserved,
initialBatchRecoveryBatchIdStable:
partialRecoveryRequired &&
partialIdentityBefore?.batchId === partialIdentityAfter?.batchId,
initialBatchRecoveryPolicyFingerprintStable:
partialRecoveryRequired &&
partialIdentityBefore?.policyFingerprint ===
partialIdentityAfter?.policyFingerprint,
initialBatchRecoveryContractFingerprintStable:
partialRecoveryRequired &&
partialIdentityBefore?.contractFingerprint ===
partialIdentityAfter?.contractFingerprint,
initialBatchRecoveryAllActionIdsStable:
partialRecoveryRequired &&
JSON.stringify(
partialIdentityBefore?.actions?.map((action) => action.actionId),
) ===
JSON.stringify(
partialIdentityAfter?.actions?.map((action) => action.actionId),
),
initialBatchRecoveryProviderStartedIdentitySetStable:
partialRecoveryRequired &&
JSON.stringify(partialSideEffectsBefore?.providerStartedIdentities) ===
JSON.stringify(partialSideEffectsAfter?.providerStartedIdentities),
initialBatchRecoveryWaitingConfirmationStable:
partialRecoveryRequired &&
partialIdentityBefore?.status === 'waiting-confirmation' &&
partialIdentityBefore?.nextActionIndex === 0 &&
partialIdentityAfter?.status === 'waiting-confirmation' &&
partialIdentityAfter?.nextActionIndex === 0,
initialBatchRecoveryZeroSideEffects: partialRecoveryZeroSideEffects,
initialBatchRecoveryPreKillDeliveryCount:
partialSideEffectsBefore?.deliveryCount ?? 0,
initialBatchRecoveryPostRecoveryDeliveryCount:
partialSideEffectsAfter?.deliveryCount ?? 0,
initialBatchRecoveryPreKillGroupCount:
partialSideEffectsBefore?.isolatedGroupCount ?? 0,
initialBatchRecoveryPostRecoveryGroupCount:
partialSideEffectsAfter?.isolatedGroupCount ?? 0,
initialBatchRecoveryPreKillChildCount:
partialSideEffectsBefore?.isolatedChildCount ?? 0,
initialBatchRecoveryPostRecoveryChildCount:
partialSideEffectsAfter?.isolatedChildCount ?? 0,
initialBatchRecoveryPreKillProjectRevision:
partialSideEffectsBefore?.projectRevision ?? 0,
initialBatchRecoveryPostRecoveryProjectRevision:
partialSideEffectsAfter?.projectRevision ?? 0,
initialBatchRecoveryPreKillProjectModificationCount:
partialSideEffectsBefore?.projectModifiedPathCount ?? 0,
initialBatchRecoveryPostRecoveryProjectModificationCount:
partialSideEffectsAfter?.projectModifiedPathCount ?? 0,
initialBatchRecoveryPreKillActionExecutionCount:
partialSideEffectsBefore?.initialActionExecutionCount ?? 0,
initialBatchRecoveryPostRecoveryActionExecutionCount:
partialSideEffectsAfter?.initialActionExecutionCount ?? 0,
initialBatchRecoveryPreKillActionReceiptCount:
partialSideEffectsBefore?.initialActionReceiptCount ?? 0,
initialBatchRecoveryPostRecoveryActionReceiptCount:
partialSideEffectsAfter?.initialActionReceiptCount ?? 0,
initialBatchRecoveryConfirmationRestoredCount:
state.supervisorSwarm.initialBatchRecoveryConfirmationRestoredCount,
initialBatchRecoveryRunnerBootChanged:
partialRecoveryRequired &&
isNonEmptyString(
state.supervisorSwarm.initialBatchRecoveryOldRunnerBootId,
) &&
isNonEmptyString(
state.supervisorSwarm.initialBatchRecoveryNewRunnerBootId,
) &&
state.supervisorSwarm.initialBatchRecoveryOldRunnerBootId !==
state.supervisorSwarm.initialBatchRecoveryNewRunnerBootId,
initialBatchRecoveryPidfdClaimCount:
state.supervisorSwarm.initialBatchRecoveryPidfdClaimCount,
initialBatchRecoveryPidfdSignalCount:
state.supervisorSwarm.initialBatchRecoveryPidfdSignalCount,
mixedModeEnabled: isSupervisorSwarmMixedHarnessSuite(),
mixedSpawnActionCaptured: isNonEmptyString(
state.supervisorSwarm.mixedSpawnActionId,
),
mixedFollowupSpawnActionCaptured: isNonEmptyString(
state.supervisorSwarm.mixedFollowupSpawnActionId,
),
mixedSpawnRequestHashStable: supervisorSwarmMixedSpawnRequestHashesStable(
isolatedRecords.groups,
),
mixedSpawnConfirmationRequiredCount:
mixedSpawnConfirmationRequirements.length +
mixedFollowupConfirmationRequirements.length,
mixedSpawnApprovalCount:
mixedSpawnApprovals.length + mixedFollowupApprovals.length,
mixedSpawnConfirmationOrderValid,
nativeMixedCollaborationPlanCount: persistence.agentDb.filter(
(record) =>
record.recordType === 'agent.runtime.tool_plan.protocol' &&
record.agentId === projectSupervisorAgentId &&
record.runId === state.initialRunId &&
Array.isArray(record.functionNames) &&
record.functionNames.filter(
(name) => name === 'runtime_tool_agent_delegate',
).length === 2 &&
record.functionNames.filter(
(name) => name === 'runtime_tool_agent_spawn_isolated',
).length === 1,
).length,
nativeFollowupIsolatedPlanCount:
isSupervisorSwarmMultiIsolatedHarnessSuite()
? persistence.agentDb.filter(
(record) =>
record.recordType === 'agent.runtime.tool_plan.protocol' &&
record.agentId === projectSupervisorAgentId &&
record.runId === state.initialRunId &&
Array.isArray(record.functionNames) &&
record.functionNames.filter(
(name) => name === 'runtime_tool_agent_delegate',
).length === 0 &&
record.functionNames.filter(
(name) => name === 'runtime_tool_agent_spawn_isolated',
).length === 1,
).length
: 0,
staticIsolatedProviderOverlapObserved:
state.supervisorSwarm.staticIsolatedProviderOverlapObserved,
staticIsolatedProviderRequestIdentityCount: new Set(
state.supervisorSwarm.staticIsolatedProviderRequestIds,
).size,
mixedParentIdentityStable:
isolatedRecords.groups.length ===
supervisorSwarmExpectedIsolatedReviewGroups().length &&
isolatedRecords.groups.every(
(group) =>
group.parentAgentId === projectSupervisorAgentId &&
group.parentSessionId === supervisorSwarmSessionId &&
group.parentRunId === state.initialRunId,
),
initialDeliveryCount: initial.length,
repairDeliveryCount: repairs.length,
repairDeliveryStatus: observedRepairStatus,
@@ -6,10 +6,8 @@ import {
codedError,
collectPartialRuntimeJsonlSurface,
fs,
hashJsonValue,
hashValue,
isNonEmptyString,
isolatedJoinDeliveryTarget,
isPlainObject,
listFiles,
path,
@@ -586,104 +584,6 @@ export function supervisorSwarmTaskIdentity(task) {
};
}
export function supervisorSwarmIsolatedGroupIdentity(group) {
return {
schemaVersion: group.schemaVersion,
parentAgentId: group.parentAgentId,
parentSessionId: group.parentSessionId,
parentRunId: group.parentRunId,
parentActionId: group.parentActionId,
delegationGroupId: group.delegationGroupId,
joinRunId: group.joinRunId,
depth: group.depth,
joinMode: group.joinMode,
requestSha256: hashJsonValue(group.request ?? null),
instanceIds: [...(group.instanceIds ?? [])].sort(),
createdAt: group.createdAt,
};
}
export function supervisorSwarmIsolatedInstanceIdentity(instance) {
return {
schemaVersion: instance.schemaVersion,
parentAgentId: instance.parentAgentId,
parentSessionId: instance.parentSessionId,
parentRunId: instance.parentRunId,
parentActionId: instance.parentActionId,
delegationGroupId: instance.delegationGroupId,
delegationId: instance.delegationId,
childIndex: instance.childIndex,
instanceId: instance.instanceId,
templateAgentId: instance.templateAgentId,
sessionId: instance.sessionId,
runId: instance.runId,
depth: instance.depth,
taskSha256: hashValue(instance.task ?? ''),
acceptanceCriteriaSha256: hashValue(
JSON.stringify(instance.acceptanceCriteria ?? []),
),
expectedArtifacts: instance.expectedArtifacts,
writeScopes: instance.writeScopes,
createdAt: instance.createdAt,
};
}
export function supervisorSwarmIsolatedResultIdentity(record) {
const result = record.result ?? {};
return {
schemaVersion: record.schemaVersion,
delegationGroupId: record.delegationGroupId,
childIndex: record.childIndex,
delegationId: result.delegationId,
instanceId: result.instanceId,
templateAgentId: result.templateAgentId,
runId: result.runId,
status: result.status,
summarySha256: hashValue(result.summary ?? ''),
artifacts: result.artifacts,
evidenceSha256: hashValue(JSON.stringify(result.evidence ?? [])),
verifiedRevision: result.verifiedRevision ?? null,
errorSha256: result.error == null ? null : hashValue(result.error),
recordedAt: record.recordedAt,
};
}
export function supervisorSwarmIsolatedJoinIdentity(delivery) {
return {
schemaVersion: delivery.schemaVersion,
parentAgentId: delivery.parentAgentId,
parentRunId: delivery.parentRunId,
delegationGroupId: delivery.delegationGroupId,
joinRunId: delivery.joinRunId,
status: delivery.status,
deliveryTarget: isolatedJoinDeliveryTarget(delivery),
queuedRunId: delivery.queuedRunId ?? null,
claimedByActionId: delivery.claimedByActionId ?? null,
updatedAt: delivery.updatedAt,
};
}
export function supervisorSwarmIsolatedJoinClaimIdentity(claim) {
return {
schemaVersion: claim.schemaVersion,
parentAgentId: claim.parentAgentId,
parentRunId: claim.parentRunId,
actionId: claim.actionId,
status: claim.status,
joins: (claim.joins ?? [])
.map((join) => ({
parentActionId: join.parentActionId,
delegationGroupId: join.delegationGroupId,
joinRunId: join.joinRunId,
source: join.source,
}))
.sort((left, right) =>
left.delegationGroupId.localeCompare(right.delegationGroupId),
),
updatedAt: claim.updatedAt,
};
}
export function supervisorSwarmParentIsolatedRecords(persistence) {
const groups = (persistence.isolatedGroups ?? []).filter(
(group) =>
@@ -22,8 +22,6 @@ import {
} from './shared.mjs';
import {
isSupervisorSwarmFinalReplyTransientRetrySuite,
isSupervisorSwarmMixedHarnessSuite,
isSupervisorSwarmMultiIsolatedHarnessSuite,
isSupervisorSwarmTransientRetrySuite,
} from './suite-selection.mjs';
@@ -403,12 +401,10 @@ export function validateSupervisorSwarmNativeProtocol(
toolPlanRepairEvidenceHasNoFatalLocalRepair(repairEvidence),
'supervisor-swarm-native-tool-protocol-required',
);
const mixed = isSupervisorSwarmMixedHarnessSuite();
const expectedActionFunctionCount = mixed ? 3 : 2;
const expectedActionFunctionCount = 2;
const allowedInitialFunctionNames = new Set([
'update_agent_plan',
'runtime_tool_agent_delegate',
...(mixed ? ['runtime_tool_agent_spawn_isolated'] : []),
]);
const initialMultiCall = protocols.filter((record) => {
if (!Array.isArray(record.functionNames)) return false;
@@ -428,7 +424,7 @@ export function validateSupervisorSwarmNativeProtocol(
).length === 2 &&
actionFunctionNames.filter(
(name) => name === 'runtime_tool_agent_spawn_isolated',
).length === (mixed ? 1 : 0) &&
).length === 0 &&
record.functionNames.every((name) =>
allowedInitialFunctionNames.has(name),
) &&
@@ -445,40 +441,6 @@ export function validateSupervisorSwarmNativeProtocol(
initialMultiCall.length === 1,
'supervisor-swarm-native-collaboration-plan-count-invalid',
);
const followupIsolatedPlans = isSupervisorSwarmMultiIsolatedHarnessSuite()
? protocols.filter((record) => {
if (!Array.isArray(record.functionNames)) return false;
const actionFunctionNames = record.functionNames.filter((name) =>
name.startsWith('runtime_tool_'),
);
return (
record.agentId === projectSupervisorAgentId &&
record.sessionId === supervisorSwarmSessionId &&
record.runId === state.initialRunId &&
record.loopIteration >
state.supervisorSwarm.initialProviderBatch.loopIteration &&
actionFunctionNames.length === 1 &&
actionFunctionNames[0] === 'runtime_tool_agent_spawn_isolated' &&
record.functionNames.every((name) =>
['update_agent_plan', 'runtime_tool_agent_spawn_isolated'].includes(
name,
),
) &&
record.functionNames.filter((name) => name === 'update_agent_plan')
.length <= 1 &&
hasValidToolPlanProtocolCallProjection(record) &&
record.functionCallCount === record.functionNames.length &&
Array.isArray(record.callIdSha256s) &&
record.callIdSha256s.length === record.functionCallCount &&
new Set(record.callIdSha256s).size === record.callIdSha256s.length
);
})
: [];
assert(
!isSupervisorSwarmMultiIsolatedHarnessSuite() ||
followupIsolatedPlans.length === 1,
'supervisor-swarm-native-followup-isolated-plan-count-invalid',
);
return {
toolPlanProtocolCount: relevant.filter(
(record) => record.recordType === 'agent.runtime.tool_plan.protocol',
@@ -496,10 +458,6 @@ export function validateSupervisorSwarmNativeProtocol(
(record) => record.protocol === 'text_json',
).length,
nativeDualDelegatePlanCount: initialMultiCall.length,
nativeMixedCollaborationPlanCount: isSupervisorSwarmMixedHarnessSuite()
? initialMultiCall.length
: 0,
nativeFollowupIsolatedPlanCount: followupIsolatedPlans.length,
};
}
@@ -637,9 +595,7 @@ export function validateSupervisorSwarmActionPersistence(
}
const initialBatchActions =
state.supervisorSwarm.initialProviderBatch?.actions ?? [];
const expectedInitialActionCount = isSupervisorSwarmMixedHarnessSuite()
? 3
: 2;
const expectedInitialActionCount = 2;
assert(
initialBatchActions.length === expectedInitialActionCount,
'supervisor-swarm-initial-batch-action-count-invalid',
@@ -651,284 +607,20 @@ export function validateSupervisorSwarmActionPersistence(
record.actionId === action.actionId &&
record.actionFingerprint === action.actionFingerprint &&
record.tool === action.tool;
let mixedSpawnConfirmationRequiredCount = 0;
let mixedSpawnApprovalCount = 0;
let mixedSpawnConfirmationOrderValid = false;
let mixedInitialSpawnTerminalObservationIndex = -1;
if (isSupervisorSwarmMixedHarnessSuite()) {
const delegateActions = initialBatchActions.filter(
(action) => action.tool === 'agent.delegate',
);
const spawnActions = initialBatchActions.filter(
assert(
initialBatchActions.every(
(action) =>
action.tool === 'agent.spawn_isolated' &&
action.actionId === state.supervisorSwarm.mixedSpawnActionId,
);
const spawnAction = spawnActions[0];
const indexedAgentDb = agentDb.map((record, index) => ({ record, index }));
const initialActionTimeline = (action) => {
const auto = action.tool === 'agent.delegate';
const executions = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_action.executing' &&
record.executionMode === 'auto' &&
matchesInitialBatchAction(record, action),
);
const autoObserved = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_action.observed' &&
record.executionMode === 'auto' &&
record.observationStatus === 'ok' &&
matchesInitialBatchAction(record, action),
);
const receipts = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.action_receipt' &&
record.sessionId === supervisorSwarmSessionId &&
record.executionMode === (auto ? 'auto' : 'confirmation') &&
record.status === 'ok' &&
matchesInitialBatchAction(record, action),
);
const terminalObservations = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_observation' &&
record.status === 'ok' &&
record.decision === (auto ? 'auto' : 'approved') &&
matchesInitialBatchAction(record, action),
);
let sideEffects = [];
if (auto) {
const actionDeliveries = deliveries.filter(
(delivery) =>
delivery.parentAgentId === projectSupervisorAgentId &&
delivery.parentSessionId === supervisorSwarmSessionId &&
delivery.parentRunId === state.initialRunId &&
delivery.parentActionId === action.actionId &&
delivery.repairOfDelegationId == null,
);
sideEffects =
actionDeliveries.length === 1
? indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.agent.delegate' &&
record.parentRunId === state.initialRunId &&
record.delegationId === actionDeliveries[0].delegationId &&
record.targetAgentId === actionDeliveries[0].targetAgentId &&
record.runId === actionDeliveries[0].targetRunId,
)
: [];
} else {
sideEffects = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.agent.spawn_isolated' &&
record.agentId === projectSupervisorAgentId &&
executing.filter((record) => matchesInitialBatchAction(record, action))
.length === 1 &&
receipts.filter(
(record) =>
record.sessionId === supervisorSwarmSessionId &&
record.runId === state.initialRunId &&
record.actionId === action.actionId,
);
}
return {
action,
auto,
executions,
autoObserved,
receipts,
terminalObservations,
sideEffects,
};
};
const actionTimelines = initialBatchActions
.map(initialActionTimeline)
.sort(
(left, right) => left.action.actionIndex - right.action.actionIndex,
);
const delegateTimelines = actionTimelines.filter(({ auto }) => auto);
const spawnTimeline = actionTimelines.find(({ auto }) => !auto);
const confirmationRequired = spawnAction
? indexedAgentDb.filter(
({ record }) =>
record.recordType ===
'agent.runtime.provider_action_batch.confirmation_required' &&
record.agentId === projectSupervisorAgentId &&
record.sessionId === supervisorSwarmSessionId &&
record.runId === state.initialRunId &&
record.batchId ===
state.supervisorSwarm.initialProviderBatch.batchId &&
record.actionCount === expectedInitialActionCount &&
record.actionIndex === spawnAction.actionIndex &&
matchesInitialBatchAction(record, spawnAction),
)
: [];
const approvals = spawnAction
? indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_confirmation.approved' &&
record.sessionId === supervisorSwarmSessionId &&
record.confirmedRunId === state.initialRunId &&
record.commandId === 'agent.spawn_isolated' &&
matchesInitialBatchAction(record, spawnAction),
)
: [];
const actionLifecycleOrderValid = actionTimelines.every((timeline) => {
if (
timeline.sideEffects.length !== 1 ||
timeline.receipts.length !== 1 ||
timeline.terminalObservations.length !== 1
) {
return false;
}
if (timeline.auto) {
return (
timeline.executions.length === 1 &&
timeline.autoObserved.length === 1 &&
timeline.executions[0].index < timeline.sideEffects[0].index &&
timeline.sideEffects[0].index < timeline.autoObserved[0].index &&
timeline.autoObserved[0].index < timeline.receipts[0].index &&
timeline.receipts[0].index < timeline.terminalObservations[0].index
);
}
return (
timeline.executions.length === 0 &&
timeline.autoObserved.length === 0 &&
approvals.length === 1 &&
approvals[0].index < timeline.sideEffects[0].index &&
timeline.sideEffects[0].index < timeline.receipts[0].index &&
timeline.receipts[0].index < timeline.terminalObservations[0].index
);
});
const batchActionOrderValid = actionTimelines.every((timeline, index) => {
if (timeline.action.actionIndex !== index) return false;
if (index === 0) return true;
const previousEnd =
actionTimelines[index - 1].terminalObservations[0]?.index;
const currentStart = timeline.auto
? timeline.executions[0]?.index
: timeline.sideEffects[0]?.index;
return (
Number.isSafeInteger(previousEnd) &&
Number.isSafeInteger(currentStart) &&
previousEnd < currentStart
);
});
mixedSpawnConfirmationRequiredCount = confirmationRequired.length;
mixedSpawnApprovalCount = approvals.length;
mixedInitialSpawnTerminalObservationIndex =
spawnTimeline?.terminalObservations[0]?.index ?? -1;
mixedSpawnConfirmationOrderValid =
confirmationRequired.length === 1 &&
approvals.length === 1 &&
confirmationRequired[0].index < approvals[0].index &&
actionTimelines.every(
(timeline) =>
approvals[0].index <
(timeline.auto
? timeline.executions[0]?.index
: timeline.sideEffects[0]?.index),
) &&
actionLifecycleOrderValid &&
batchActionOrderValid;
assert(
delegateActions.length === 2 &&
new Set(delegateActions.map((action) => action.actionId)).size === 2 &&
spawnActions.length === 1 &&
spawnAction.executionMode === 'confirmation' &&
delegateActions.every((action) => action.executionMode === 'auto') &&
delegateTimelines.length === 2 &&
spawnTimeline != null &&
mixedSpawnConfirmationRequiredCount === 1 &&
mixedSpawnApprovalCount === 1 &&
mixedSpawnConfirmationOrderValid,
'supervisor-swarm-mixed-initial-batch-action-persistence-invalid',
);
} else {
assert(
initialBatchActions.every(
(action) =>
executing.filter((record) =>
record.status === 'ok' &&
matchesInitialBatchAction(record, action),
).length === 1 &&
receipts.filter(
(record) =>
record.sessionId === supervisorSwarmSessionId &&
record.status === 'ok' &&
matchesInitialBatchAction(record, action),
).length === 1,
),
'supervisor-swarm-initial-batch-action-persistence-invalid',
);
}
if (isSupervisorSwarmMultiIsolatedHarnessSuite()) {
const followupActionId = state.supervisorSwarm.mixedFollowupSpawnActionId;
assert(
isNonEmptyString(followupActionId) &&
followupActionId !== state.supervisorSwarm.mixedSpawnActionId,
'supervisor-swarm-followup-spawn-action-missing',
);
const indexedAgentDb = agentDb.map((record, index) => ({ record, index }));
const matchesFollowupIdentity = (record) =>
record.agentId === projectSupervisorAgentId &&
(record.runId === state.initialRunId ||
record.confirmedRunId === state.initialRunId) &&
record.actionId === followupActionId;
const matchesFollowupToolRecord = (record) =>
matchesFollowupIdentity(record) &&
(record.tool === 'agent.spawn_isolated' ||
record.commandId === 'agent.spawn_isolated');
const confirmationRequired = indexedAgentDb.filter(
({ record }) =>
record.recordType ===
'agent.runtime.provider_action_batch.confirmation_required' &&
record.sessionId === supervisorSwarmSessionId &&
matchesFollowupToolRecord(record),
);
const approvals = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_confirmation.approved' &&
record.sessionId === supervisorSwarmSessionId &&
matchesFollowupToolRecord(record),
);
const sideEffects = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.agent.spawn_isolated' &&
record.sessionId === supervisorSwarmSessionId &&
matchesFollowupIdentity(record),
);
const followupReceipts = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.action_receipt' &&
record.sessionId === supervisorSwarmSessionId &&
record.executionMode === 'confirmation' &&
record.status === 'ok' &&
matchesFollowupToolRecord(record),
);
const terminalObservations = indexedAgentDb.filter(
({ record }) =>
record.recordType === 'agent.runtime.tool_observation' &&
record.status === 'ok' &&
record.decision === 'approved' &&
matchesFollowupToolRecord(record),
);
const followupOrderValid =
confirmationRequired.length === 1 &&
approvals.length === 1 &&
sideEffects.length === 1 &&
followupReceipts.length === 1 &&
terminalObservations.length === 1 &&
mixedInitialSpawnTerminalObservationIndex >= 0 &&
mixedInitialSpawnTerminalObservationIndex <
confirmationRequired[0].index &&
confirmationRequired[0].index < approvals[0].index &&
approvals[0].index < sideEffects[0].index &&
sideEffects[0].index < followupReceipts[0].index &&
followupReceipts[0].index < terminalObservations[0].index;
assert(
followupOrderValid,
'supervisor-swarm-followup-spawn-action-persistence-invalid',
);
mixedSpawnConfirmationRequiredCount += confirmationRequired.length;
mixedSpawnApprovalCount += approvals.length;
mixedSpawnConfirmationOrderValid &&= followupOrderValid;
}
).length === 1,
),
'supervisor-swarm-initial-batch-action-persistence-invalid',
);
const matchesRecoveredRepairPending = (record) =>
record.agentId === state.supervisorSwarm.repairTargetAgentId &&
record.runId === state.supervisorSwarm.repairTargetRunId &&
@@ -957,9 +649,6 @@ export function validateSupervisorSwarmActionPersistence(
duplicateReceiptCount,
duplicateExecutingActionIdCount,
isolatedMutationActionCount: isolatedMutationActions.length,
mixedSpawnConfirmationRequiredCount,
mixedSpawnApprovalCount,
mixedSpawnConfirmationOrderValid,
recoveredRepairPendingActionCount:
recoveredRepairConfirmationLifecycle.receipts.length,
failedRepairActionCount: repairAttempts.failedRepairActions.length,
@@ -6,14 +6,10 @@ import {
assertSupervisorSwarmRuntimeHealthy,
assertSupervisorSwarmWeakQualityDelivery,
observeSupervisorSwarmInitialProviderOverlap,
observeSupervisorSwarmStaticIsolatedProviderOverlap,
supervisorSwarmMixedIsolatedClaimsReady,
supervisorSwarmParentDeliveries,
supervisorSwarmRelevantRunKeys,
supervisorSwarmRepairAttemptEvidence,
validateSupervisorSwarmMixedIsolatedPersistence,
} from './collaboration-assertions.mjs';
import { validateSupervisorSwarmIsolatedSpawnInput } from './collaboration-policy.mjs';
import {
readSupervisorSwarmResidualSidecarCounts,
supervisorSwarmResidualSidecarsEmpty,
@@ -33,8 +29,6 @@ import {
codedError,
confirmPendingActions,
findPendingActions,
hashValue,
isNonEmptyString,
isPlainObject,
killRunnerOnce,
path,
@@ -45,7 +39,6 @@ import {
sleep,
state,
supervisorSwarmConfirmedTools,
supervisorSwarmIsolatedReviews,
supervisorSwarmQualityAgentId,
supervisorSwarmQualityPath,
supervisorSwarmSessionId,
@@ -55,11 +48,7 @@ import {
} from './shared.mjs';
import {
isSupervisorSwarmInitialTransientRetrySuite,
isSupervisorSwarmInteractiveChatSuite,
isSupervisorSwarmMixedHarnessSuite,
isSupervisorSwarmMultiIsolatedHarnessSuite,
isSupervisorSwarmTransientRetrySuite,
supervisorSwarmExpectedIsolatedReviewGroups,
} from './suite-selection.mjs';
export async function confirmSupervisorSwarmPendingActions(
@@ -68,50 +57,9 @@ export async function confirmSupervisorSwarmPendingActions(
) {
const before = state.confirmedActionIds.size;
const allowedTools = new Set(supervisorSwarmConfirmedTools);
if (isSupervisorSwarmMixedHarnessSuite()) {
allowedTools.add('agent.spawn_isolated');
}
await confirmPendingActions(allowedTools, (pending) => {
const runKey = `${pending.agentId}\0${pending.runId}`;
const deferred = deferredRunKeys.has(runKey);
if (pending.tool === 'agent.spawn_isolated') {
const initialSpawn =
pending.actionId === state.supervisorSwarm.mixedSpawnActionId;
if (!initialSpawn) {
assert(
isSupervisorSwarmMultiIsolatedHarnessSuite(),
'supervisor-swarm-followup-spawn-outside-multi-group-suite',
);
const requestHash = validateSupervisorSwarmIsolatedSpawnInput(
pending.action?.input,
1,
'supervisor-swarm-mixed-followup',
supervisorSwarmExpectedIsolatedReviewGroups(),
);
assert(
isNonEmptyString(state.supervisorSwarm.mixedSpawnActionId) &&
pending.actionId !== state.supervisorSwarm.mixedSpawnActionId &&
(state.supervisorSwarm.mixedFollowupSpawnActionId == null ||
state.supervisorSwarm.mixedFollowupSpawnActionId ===
pending.actionId) &&
(state.supervisorSwarm.mixedFollowupSpawnRequestHash == null ||
state.supervisorSwarm.mixedFollowupSpawnRequestHash ===
requestHash),
'supervisor-swarm-followup-spawn-identity-invalid',
);
state.supervisorSwarm.mixedFollowupSpawnActionId = pending.actionId;
state.supervisorSwarm.mixedFollowupSpawnRequestHash = requestHash;
}
assert(
isSupervisorSwarmMixedHarnessSuite() &&
pending.agentId === projectSupervisorAgentId &&
pending.runId === state.initialRunId &&
(initialSpawn ||
pending.actionId ===
state.supervisorSwarm.mixedFollowupSpawnActionId),
'supervisor-swarm-unexpected-spawn-confirmation',
);
}
assert(
confirmRunKeys.has(runKey) || deferred,
'supervisor-swarm-unexpected-pending-run',
@@ -121,14 +69,7 @@ export async function confirmSupervisorSwarmPendingActions(
pending.agentId === projectSupervisorAgentId &&
pending.runId === state.initialRunId;
assert(
!isParentRun ||
pending.tool === 'project.verify' ||
(isSupervisorSwarmMixedHarnessSuite() &&
pending.tool === 'agent.spawn_isolated' &&
[
state.supervisorSwarm.mixedSpawnActionId,
state.supervisorSwarm.mixedFollowupSpawnActionId,
].includes(pending.actionId)),
!isParentRun || pending.tool === 'project.verify',
'supervisor-swarm-parent-pending-tool-invalid',
);
return true;
@@ -255,9 +196,6 @@ export async function restartSupervisorSwarmRunnerAtRepairBoundary(
repair,
repairPending,
) {
const mixedState = isSupervisorSwarmMixedHarnessSuite()
? validateSupervisorSwarmMixedIsolatedPersistence(persistence)
: null;
const initial = supervisorSwarmParentDeliveries(
persistence.deliveries,
).filter((delivery) => delivery.repairOfDelegationId == null);
@@ -290,11 +228,6 @@ export async function restartSupervisorSwarmRunnerAtRepairBoundary(
(delivery) =>
task.agentId === delivery.targetAgentId &&
task.runId === delivery.targetRunId,
) ||
(mixedState?.instances ?? []).some(
(instance) =>
task.agentId === instance.instanceId &&
task.runId === instance.runId,
),
)
.map(supervisorSwarmTaskIdentity)
@@ -312,14 +245,10 @@ export async function restartSupervisorSwarmRunnerAtRepairBoundary(
.map(supervisorSwarmClaimIdentity)
.sort((left, right) => left.actionId.localeCompare(right.actionId));
assert(
preKillTaskIdentities.length === (mixedState ? 7 : 4) &&
preKillClaimIdentities.length === 1,
preKillTaskIdentities.length === 4 && preKillClaimIdentities.length === 1,
'supervisor-swarm-pre-kill-work-or-claim-identity-invalid',
);
const relevantRuns = supervisorSwarmRelevantRunKeys(
parentDeliveries,
mixedState?.instances ?? [],
);
const relevantRuns = supervisorSwarmRelevantRunKeys(parentDeliveries);
const preKillProviderStartedIdentities =
supervisorSwarmProviderStartedIdentities(persistence.agentDb, relevantRuns);
const preKillParentContext = persistence.contextBundles.find(
@@ -353,12 +282,6 @@ export async function restartSupervisorSwarmRunnerAtRepairBoundary(
.sort((left, right) => left.delegationId.localeCompare(right.delegationId));
state.supervisorSwarm.preKillClaimIdentities = preKillClaimIdentities;
state.supervisorSwarm.preKillTaskIdentities = preKillTaskIdentities;
state.supervisorSwarm.preKillMixedIdentity = mixedState
? {
isolated: mixedState.identity,
parentContextSha256: hashValue(JSON.stringify(preKillParentContext)),
}
: null;
state.supervisorSwarm.runnerKillBoundaryObserved = true;
await killRunnerOnce();
@@ -437,21 +360,11 @@ export async function restartSupervisorSwarmRunnerAtRepairBoundary(
candidate.tool === repairPending.tool,
);
if (recoveredPending) {
const currentMixedState = isSupervisorSwarmMixedHarnessSuite()
? validateSupervisorSwarmMixedIsolatedPersistence(after)
: null;
const currentParentContext = after.contextBundles.find(
(bundle) =>
bundle.agentId === projectSupervisorAgentId &&
bundle.sessionId === supervisorSwarmSessionId &&
bundle.runId === state.initialRunId,
);
const currentProviderStartedIdentities =
supervisorSwarmProviderStartedIdentities(
after.agentDb,
supervisorSwarmRelevantRunKeys(
supervisorSwarmParentDeliveries(after.deliveries),
currentMixedState?.instances ?? [],
),
);
assert(
@@ -468,15 +381,7 @@ export async function restartSupervisorSwarmRunnerAtRepairBoundary(
JSON.stringify(currentProviderStartedIdentities) ===
JSON.stringify(
state.supervisorSwarm.preKillProviderStartedIdentities,
) &&
(!currentMixedState ||
(JSON.stringify(currentMixedState.identity) ===
JSON.stringify(
state.supervisorSwarm.preKillMixedIdentity?.isolated,
) &&
hashValue(JSON.stringify(currentParentContext)) ===
state.supervisorSwarm.preKillMixedIdentity
?.parentContextSha256)),
),
'supervisor-swarm-recovery-identity-or-provider-replay-invalid',
);
state.identityStable = true;
@@ -500,84 +405,18 @@ export async function driveSupervisorSwarmToRepairKillBoundary() {
(delivery) => delivery.repairOfDelegationId == null,
);
assert(initial.length <= 2, 'supervisor-swarm-extra-initial-delivery');
if (
isSupervisorSwarmMixedHarnessSuite() &&
isNonEmptyString(state.supervisorSwarm.mixedSpawnActionId) &&
!state.confirmedActionIds.has(state.supervisorSwarm.mixedSpawnActionId)
) {
const pending = await findPendingActions();
if (
pending.some(
(candidate) =>
candidate.agentId === projectSupervisorAgentId &&
candidate.runId === state.initialRunId &&
candidate.actionId === state.supervisorSwarm.mixedSpawnActionId &&
candidate.tool === 'agent.spawn_isolated',
)
) {
await confirmSupervisorSwarmPendingActions(
new Set([`${projectSupervisorAgentId}\0${state.initialRunId}`]),
new Set(
initial.map(
(delivery) =>
`${delivery.targetAgentId}\0${delivery.targetRunId}`,
),
),
);
}
}
if (isSupervisorSwarmMultiIsolatedHarnessSuite()) {
const pending = await findPendingActions();
const followupSpawnPending = pending.find(
(candidate) =>
candidate.agentId === projectSupervisorAgentId &&
candidate.runId === state.initialRunId &&
candidate.tool === 'agent.spawn_isolated' &&
candidate.actionId !== state.supervisorSwarm.mixedSpawnActionId,
);
if (
followupSpawnPending &&
!state.confirmedActionIds.has(followupSpawnPending.actionId)
) {
await confirmSupervisorSwarmPendingActions(
new Set([`${projectSupervisorAgentId}\0${state.initialRunId}`]),
new Set(
parentDeliveries.map(
(delivery) =>
`${delivery.targetAgentId}\0${delivery.targetRunId}`,
),
),
);
}
}
if (initial.length === 2) {
const { quality } = assertSupervisorSwarmInitialDeliveries(initial);
observeSupervisorSwarmInitialProviderOverlap(
persistence.agentDb,
initial,
);
const isolatedInstances =
supervisorSwarmParentIsolatedRecords(persistence).instances;
observeSupervisorSwarmStaticIsolatedProviderOverlap(
persistence.agentDb,
initial,
isolatedInstances,
);
if (
state.supervisorSwarm.initialProviderOverlapObserved &&
(!isSupervisorSwarmMixedHarnessSuite() ||
state.supervisorSwarm.staticIsolatedProviderOverlapObserved)
) {
if (state.supervisorSwarm.initialProviderOverlapObserved) {
const confirmRunKeys = new Set(
initial.map(
(delivery) => `${delivery.targetAgentId}\0${delivery.targetRunId}`,
),
);
if (isSupervisorSwarmInteractiveChatSuite()) {
confirmRunKeys.add(
`${projectSupervisorAgentId}\0${state.initialRunId}`,
);
}
await confirmSupervisorSwarmPendingActions(
confirmRunKeys,
new Set(
@@ -685,13 +524,6 @@ export async function driveSupervisorSwarmToRepairKillBoundary() {
}
return;
}
if (isSupervisorSwarmMixedHarnessSuite()) {
if (!supervisorSwarmMixedIsolatedClaimsReady(persistence)) {
await sleep(50);
continue;
}
validateSupervisorSwarmMixedIsolatedPersistence(persistence);
}
await restartSupervisorSwarmRunnerAtRepairBoundary(
persistence,
repair,
@@ -790,29 +622,6 @@ export async function driveSupervisorSwarmRuntimeToCompletion() {
delivery.delegationId === task.delegationId,
),
);
const isolatedRecords = supervisorSwarmParentIsolatedRecords(persistence);
observeSupervisorSwarmStaticIsolatedProviderOverlap(
persistence.agentDb,
deliveries.filter((delivery) => delivery.repairOfDelegationId == null),
isolatedRecords.instances,
);
const mixedReady =
!isSupervisorSwarmMixedHarnessSuite() ||
(isolatedRecords.groups.length ===
supervisorSwarmExpectedIsolatedReviewGroups().length &&
isolatedRecords.instances.length ===
supervisorSwarmIsolatedReviews.length &&
isolatedRecords.results.length ===
supervisorSwarmIsolatedReviews.length &&
isolatedRecords.results.every(
(record) => record.result?.status === 'completed',
) &&
isolatedRecords.joinDeliveries.length ===
supervisorSwarmExpectedIsolatedReviewGroups().length &&
isolatedRecords.joinDeliveries.every(
(delivery) => delivery.status === 'claimed-by-parent',
) &&
validateSupervisorSwarmMixedIsolatedPersistence(persistence));
const terminalStateObserved =
deliveries.length === 3 &&
deliveries.every((delivery) => delivery.status === 'claimed-by-parent') &&
@@ -820,7 +629,6 @@ export async function driveSupervisorSwarmRuntimeToCompletion() {
childTasks.every(
(task) => task.status === 'completed' && task.phase === 'completed',
) &&
Boolean(mixedReady) &&
parentRuntime?.runId === state.initialRunId &&
parentRuntime?.sessionId === supervisorSwarmSessionId &&
parentRuntime?.status === 'idle' &&

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