Files
Genarrative/apps/ai-game-creator-shell/scripts/agent-runtime-real-e2e/harness/app-data.mjs
T
AIGameCreator App be89296492 拆分 AI 游戏创作客户端大型模块
拆分 App 认证、壳层、运行配置与项目摘要模块
拆分 Tauri 项目能力与 Rust 测试领域模块
拆分界面测试与 Agent Runtime 真实 E2E 套件
补充源码扫描和客户端模块化文档约定
2026-07-21 22:53:29 +08:00

1503 lines
52 KiB
JavaScript

import { assert, codedError, hashValue, sleep } from '../assertions/core.mjs';
import { isNonEmptyString } from '../assertions/runtime.mjs';
import {
createHash,
fs,
fsConstants,
path,
randomUUID,
spawn,
watchFileSystem,
} from '../dependencies.mjs';
import {
appRoot,
BlockedError,
configFileName,
contextCompactionAppDataSentinelFileName,
contextCompactionAppDataSentinelSchema,
goalAppDataSentinelFileName,
goalAppDataSentinelSchema,
linuxPidfdHelperSource,
localConfigFileName,
mainAgentId,
mcpAppDataSentinelFileName,
mcpAppDataSentinelSchema,
parallelReadAppDataSentinelFileName,
parallelReadAppDataSentinelSchema,
projectSkillAppDataSentinelFileName,
projectSkillAppDataSentinelSchema,
projectSupervisorAgentId,
responseStreamAppDataSentinelFileName,
responseStreamAppDataSentinelSchema,
runnerEndpointFileName,
scopedAgentsAppDataSentinelFileName,
scopedAgentsAppDataSentinelSchema,
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,
} from '../runtime-state.mjs';
import { isContextCompactionSuite } from '../suites/context-compaction.mjs';
import { isGoalRuntimeSuite } from '../suites/goal.mjs';
import { isMcpRuntimeSuite } from '../suites/mcp.mjs';
import { isParallelReadSuite } from '../suites/parallel-read.mjs';
import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
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,
} from '../suites/web-search.mjs';
import {
effectiveAgentLlmConfig,
isPlainObject,
loadConfig,
mergeConfigPatch,
safeEffectiveAgentLlmPolicy,
sameEffectiveAgentLlm,
sameEffectiveAgentLlmWithoutStream,
} from './config.mjs';
import { collectApiKeys, isPathInside, readJson } from './io.mjs';
import { appendBounded, runProcess } from './process.mjs';
import { decodeUtf8Fatal, isIsolatedRunnerSuite } from './reporting.mjs';
import { killRunnerOnce, readRunnerStatus, runnerBootId } from './runtime.mjs';
export function isolatedSuiteProtectsSourceAppData() {
return (
isSupervisorSwarmTransientRetrySuite() ||
isSupervisorSwarmToolPlanHandoffRunnerKillSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmInteractiveChatSuite()
);
}
export function isolatedSuiteUsesSiblingAppData() {
return isWebSearchSuite() || isolatedSuiteProtectsSourceAppData();
}
export function isolatedSuiteAppDataProfile() {
if (isSteerRunnerKillSuite()) {
return {
prefix: '.agent-runtime-real-e2e-steer-runner-kill-',
sentinelName: steerRunnerKillAppDataSentinelFileName,
sentinelSchema: steerRunnerKillAppDataSentinelSchema,
codePrefix: 'steer-runner-kill-appdata',
};
}
if (isGoalRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-goal-',
sentinelName: goalAppDataSentinelFileName,
sentinelSchema: goalAppDataSentinelSchema,
codePrefix: 'goal-appdata',
};
}
if (isWebSearchSuite()) {
return {
prefix: '.agent-runtime-real-e2e-web-search-',
sentinelName: webSearchAppDataSentinelFileName,
sentinelSchema: webSearchAppDataSentinelSchema,
codePrefix: 'web-search-appdata',
};
}
if (isContextCompactionSuite()) {
return {
prefix: '.agent-runtime-real-e2e-context-compaction-',
sentinelName: contextCompactionAppDataSentinelFileName,
sentinelSchema: contextCompactionAppDataSentinelSchema,
codePrefix: 'context-compaction-appdata',
};
}
if (isMcpRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-mcp-',
sentinelName: mcpAppDataSentinelFileName,
sentinelSchema: mcpAppDataSentinelSchema,
codePrefix: 'mcp-appdata',
};
}
if (isUserInputRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-user-input-',
sentinelName: userInputAppDataSentinelFileName,
sentinelSchema: userInputAppDataSentinelSchema,
codePrefix: 'user-input-appdata',
};
}
if (isScopedAgentsSuite()) {
return {
prefix: '.agent-runtime-real-e2e-scoped-agents-',
sentinelName: scopedAgentsAppDataSentinelFileName,
sentinelSchema: scopedAgentsAppDataSentinelSchema,
codePrefix: 'scoped-agents-appdata',
};
}
if (isProjectSkillSuite()) {
return {
prefix: '.agent-runtime-real-e2e-project-skill-',
sentinelName: projectSkillAppDataSentinelFileName,
sentinelSchema: projectSkillAppDataSentinelSchema,
codePrefix: 'project-skill-appdata',
};
}
if (isParallelReadSuite()) {
return {
prefix: '.agent-runtime-real-e2e-parallel-read-',
sentinelName: parallelReadAppDataSentinelFileName,
sentinelSchema: parallelReadAppDataSentinelSchema,
codePrefix: 'parallel-read-appdata',
};
}
if (isSupervisorSwarmFinalReplyTransientRetrySuite()) {
return {
prefix:
'.agent-runtime-real-e2e-supervisor-swarm-final-reply-transient-retry-',
sentinelName:
supervisorSwarmFinalReplyTransientRetryAppDataSentinelFileName,
sentinelSchema:
supervisorSwarmFinalReplyTransientRetryAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-final-reply-transient-retry-appdata',
};
}
if (isSupervisorSwarmToolPlanHandoffRunnerKillSuite()) {
return {
prefix:
'.agent-runtime-real-e2e-supervisor-swarm-tool-plan-handoff-runner-kill-',
sentinelName:
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelFileName,
sentinelSchema:
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-tool-plan-handoff-runner-kill-appdata',
};
}
if (isSupervisorSwarmInitialTransientRetrySuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-transient-retry-',
sentinelName: supervisorSwarmTransientRetryAppDataSentinelFileName,
sentinelSchema: supervisorSwarmTransientRetryAppDataSentinelSchema,
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-',
sentinelName: supervisorSwarmAppDataSentinelFileName,
sentinelSchema: supervisorSwarmAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-appdata',
};
}
assert(
isResponseStreamSuite(),
'isolated-appdata-used-outside-isolated-suite',
);
return {
prefix: '.agent-runtime-real-e2e-response-stream-',
sentinelName: responseStreamAppDataSentinelFileName,
sentinelSchema: responseStreamAppDataSentinelSchema,
codePrefix: 'response-stream-appdata',
};
}
export async function createSentinelOwnedTempDirectory({
prefix,
sentinelName,
sentinel,
codePrefix,
}) {
const directory = await fs.mkdtemp(prefix);
try {
if (process.platform !== 'win32') await fs.chmod(directory, 0o700);
await fs.writeFile(
path.join(directory, sentinelName),
`${JSON.stringify(sentinel)}\n`,
{ flag: 'wx', mode: 0o600 },
);
return directory;
} catch (error) {
try {
await fs.rm(directory, { recursive: true, force: true });
} catch (cleanupError) {
throw codedError(
`${codePrefix}-sentinel-create-cleanup-failed`,
cleanupError,
);
}
throw codedError(`${codePrefix}-sentinel-create-failed`, error);
}
}
export async function captureSourceRunnerEndpointSnapshot(sourceConfigDir) {
const endpointPath = path.join(sourceConfigDir, runnerEndpointFileName);
const metadata = await fs.lstat(endpointPath).catch((error) => {
if (error?.code === 'ENOENT') return null;
throw error;
});
if (!metadata) return { exists: false, fingerprint: null };
assert(
metadata.isFile() && !metadata.isSymbolicLink(),
'source-runner-endpoint-not-regular-file',
);
const endpoint = await readJson(endpointPath);
const stableEndpoint = { ...endpoint };
delete stableEndpoint.heartbeatAt;
return {
exists: true,
fingerprint: hashValue(JSON.stringify(stableEndpoint)),
};
}
export async function verifySourceRunnerEndpointUnchanged() {
const sourceConfigDir = await fs.realpath(state.options.configDir);
const current = await captureSourceRunnerEndpointSnapshot(sourceConfigDir);
assert(
JSON.stringify(current) ===
JSON.stringify(state.isolatedRunner.sourceEndpointSnapshot),
'source-runner-endpoint-changed-during-isolated-suite',
);
state.isolatedRunner.sourceRunnerEndpointUnchanged = true;
}
export function closeSourceAppDataDirectoryGuard() {
const watcher = state.isolatedRunner.sourceAppDataDirectoryWatcher;
if (!watcher) return;
watcher.close();
state.isolatedRunner.sourceAppDataDirectoryWatcher = null;
}
export function sourceAppDataDirectoryEventIsViolation(
fileName,
profilePrefix,
sourceEndpointSnapshot,
) {
const name = Buffer.isBuffer(fileName)
? fileName.toString('utf8')
: String(fileName ?? '');
return (
name.startsWith(profilePrefix) ||
(sourceEndpointSnapshot?.exists === false &&
name === runnerEndpointFileName)
);
}
export function startSourceAppDataDirectoryGuard(sourceConfigDir, profile) {
if (!isolatedSuiteProtectsSourceAppData()) return;
assert(
!state.isolatedRunner.sourceAppDataDirectoryWatcher,
'source-appdata-directory-guard-already-started',
);
const watcher = watchFileSystem(
sourceConfigDir,
{ persistent: false },
(_eventType, fileName) => {
if (
sourceAppDataDirectoryEventIsViolation(
fileName,
profile.prefix,
state.isolatedRunner.sourceEndpointSnapshot,
)
) {
state.isolatedRunner.sourceAppDataDirectoryViolationCount += 1;
}
},
);
watcher.on('error', () => {
state.isolatedRunner.sourceAppDataDirectoryViolationCount += 1;
});
state.isolatedRunner.sourceAppDataDirectoryWatcher = watcher;
}
export async function verifySourceAppDataDirectoryUntouched() {
if (!isolatedSuiteProtectsSourceAppData()) return;
closeSourceAppDataDirectoryGuard();
const sourceConfigDir = await fs.realpath(state.options.configDir);
const profile = isolatedSuiteAppDataProfile();
const entries = await fs.readdir(sourceConfigDir);
assert(
state.isolatedRunner.sourceAppDataDirectoryViolationCount === 0 &&
!entries.some((name) => name.startsWith(profile.prefix)),
'source-appdata-directory-touched-by-suite',
);
state.isolatedRunner.sourceAppDataDirectoryUntouched = true;
}
export async function prepareIsolatedSuiteAppData({
streamAgentId = null,
webSearchAgentId = null,
mcpConfigFactory = null,
configOverlay = null,
} = {}) {
assert(
isIsolatedRunnerSuite(),
'isolated-appdata-used-outside-isolated-suite',
);
assert(
[streamAgentId, webSearchAgentId, mcpConfigFactory, configOverlay].filter(
Boolean,
).length <= 1,
'isolated-appdata-multiple-overlays-forbidden',
);
if (configOverlay) {
assert(
isPlainObject(configOverlay) &&
collectApiKeys(configOverlay).length === 0,
'isolated-appdata-config-overlay-invalid',
);
}
const profile = isolatedSuiteAppDataProfile();
const suiteSecrets = new Set(state.secrets);
const sourceConfigDir = await fs.realpath(state.options.configDir);
state.isolatedRunner.sourceEndpointSnapshot =
await captureSourceRunnerEndpointSnapshot(sourceConfigDir);
const ownerToken = randomUUID();
const createdAt = Date.now();
const appDataParent = isolatedSuiteUsesSiblingAppData()
? path.dirname(sourceConfigDir)
: sourceConfigDir;
const appDataDir = await createSentinelOwnedTempDirectory({
prefix: path.join(appDataParent, profile.prefix),
sentinelName: profile.sentinelName,
sentinel: {
schemaVersion: profile.sentinelSchema,
token: ownerToken,
ownerPid: process.pid,
createdAt,
},
codePrefix: profile.codePrefix,
});
state.isolatedRunner.appDataDir = appDataDir;
state.isolatedRunner.ownerToken = ownerToken;
state.isolatedRunner.createdAt = createdAt;
if (isolatedSuiteProtectsSourceAppData()) {
const realAppDataDir = await fs.realpath(appDataDir);
assert(
!isPathInside(sourceConfigDir, realAppDataDir) &&
path.dirname(realAppDataDir) === path.dirname(sourceConfigDir),
`${profile.codePrefix}-source-appdata-write-boundary-invalid`,
);
startSourceAppDataDirectoryGuard(sourceConfigDir, profile);
}
const mcpOverlay = mcpConfigFactory
? await mcpConfigFactory(appDataDir)
: null;
if (mcpOverlay) {
assert(
isPlainObject(mcpOverlay) &&
isPlainObject(mcpOverlay.mcpServers) &&
Object.keys(mcpOverlay.mcpServers).length > 0 &&
Array.isArray(mcpOverlay.secrets) &&
mcpOverlay.secrets.every(isNonEmptyString),
'mcp-config-overlay-invalid',
);
for (const secret of mcpOverlay.secrets) suiteSecrets.add(secret);
}
const sourceConfigs = [];
for (const name of [configFileName, localConfigFileName]) {
const sourcePath = path.join(sourceConfigDir, name);
const metadata = await fs.lstat(sourcePath).catch((error) => {
if (error?.code === 'ENOENT') return null;
throw error;
});
if (!metadata) {
assert(
name !== configFileName,
`${profile.codePrefix}-source-config-missing`,
);
continue;
}
assert(
metadata.isFile() && !metadata.isSymbolicLink(),
`${profile.codePrefix}-source-config-not-regular-file`,
);
const sourceContent = await fs.readFile(sourcePath);
let sourceConfig;
try {
sourceConfig = JSON.parse(
decodeUtf8Fatal(
sourceContent,
`${profile.codePrefix}-linked-config-invalid-utf8`,
),
);
} catch (error) {
throw codedError(
`${profile.codePrefix}-linked-config-json-invalid`,
error,
);
}
assert(
isPlainObject(sourceConfig),
`${profile.codePrefix}-linked-config-root-invalid`,
);
for (const secret of collectApiKeys(sourceConfig)) suiteSecrets.add(secret);
sourceConfigs.push({
name,
sourcePath,
metadata,
sourceContent,
config: sourceConfig,
});
}
const mergedSourceConfig = {};
for (const source of sourceConfigs) {
mergeConfigPatch(mergedSourceConfig, source.config);
}
const overlayAgentId =
streamAgentId ?? webSearchAgentId ?? (mcpOverlay ? mainAgentId : null);
const sourceEffective = overlayAgentId
? effectiveAgentLlmConfig(mergedSourceConfig, overlayAgentId)
: null;
let activeConfigSource = null;
if (
overlayAgentId &&
(mcpOverlay || webSearchAgentId || sourceEffective.stream !== true)
) {
const sameEffective = mcpOverlay
? sameEffectiveAgentLlm
: webSearchAgentId
? sameEffectiveAgentLlmWithoutWebSearch
: sameEffectiveAgentLlmWithoutStream;
activeConfigSource =
sourceConfigs.find(
(source) =>
source.name === configFileName &&
sameEffective(
effectiveAgentLlmConfig(source.config, overlayAgentId),
sourceEffective,
),
) ??
sourceConfigs.find((source) =>
sameEffective(
effectiveAgentLlmConfig(source.config, overlayAgentId),
sourceEffective,
),
);
assert(
Boolean(activeConfigSource),
mcpOverlay
? 'mcp-source-config-cannot-accept-mcp-only-overlay'
: webSearchAgentId
? 'web-search-source-config-cannot-accept-search-only-overlay'
: 'response-stream-source-config-cannot-accept-stream-only-overlay',
);
}
for (const source of sourceConfigs) {
const linkedName = configOverlay
? `.source-${source.name}`
: activeConfigSource
? source === activeConfigSource
? configFileName
: `.source-${source.name}`
: source.name;
const linkedPath = path.join(appDataDir, linkedName);
const storageMode =
isWebSearchSuite() ||
isMcpRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorSwarmSuite()
? 'private-copy'
: 'hardlink';
try {
if (storageMode === 'private-copy') {
await fs.copyFile(
source.sourcePath,
linkedPath,
fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE,
);
await fs.chmod(linkedPath, 0o600);
} else {
// Existing isolated suites share credential-bearing config inodes.
await fs.link(source.sourcePath, linkedPath);
}
} catch (error) {
throw codedError(`${profile.codePrefix}-config-replica-failed`, error);
}
const linkedMetadata = await fs.lstat(linkedPath);
const privateCopyValid =
storageMode === 'private-copy' &&
(linkedMetadata.dev !== source.metadata.dev ||
linkedMetadata.ino !== source.metadata.ino) &&
(linkedMetadata.mode & 0o077) === 0;
const hardlinkValid =
storageMode === 'hardlink' &&
linkedMetadata.dev === source.metadata.dev &&
linkedMetadata.ino === source.metadata.ino;
assert(
linkedMetadata.isFile() &&
!linkedMetadata.isSymbolicLink() &&
(privateCopyValid || hardlinkValid),
`${profile.codePrefix}-config-replica-identity-invalid`,
);
state.isolatedRunner.configLinks.push({
storageMode,
sourceName: source.name,
linkedName,
sourcePath: source.sourcePath,
linkedPath,
dev: source.metadata.dev,
ino: source.metadata.ino,
linkedDev: linkedMetadata.dev,
linkedIno: linkedMetadata.ino,
nlink: source.metadata.nlink,
sourceMode: source.metadata.mode,
sourceSize: source.metadata.size,
sourceMtimeMs: source.metadata.mtimeMs,
sourceCtimeMs: source.metadata.ctimeMs,
sha256: createHash('sha256').update(source.sourceContent).digest('hex'),
});
}
if (configOverlay) {
const primaryConfigPath = path.join(appDataDir, configFileName);
const localOverlayPath = path.join(appDataDir, localConfigFileName);
await fs.writeFile(
primaryConfigPath,
`${JSON.stringify(mergedSourceConfig)}\n`,
{ flag: 'wx', mode: 0o600 },
);
await fs.writeFile(localOverlayPath, `${JSON.stringify(configOverlay)}\n`, {
flag: 'wx',
mode: 0o600,
});
const [primaryMetadata, overlayMetadata] = await Promise.all([
fs.lstat(primaryConfigPath),
fs.lstat(localOverlayPath),
]);
assert(
primaryMetadata.isFile() &&
!primaryMetadata.isSymbolicLink() &&
overlayMetadata.isFile() &&
!overlayMetadata.isSymbolicLink() &&
(primaryMetadata.mode & 0o077) === 0 &&
(overlayMetadata.mode & 0o077) === 0,
`${profile.codePrefix}-materialized-config-invalid`,
);
state.isolatedRunner.configOverlayCreated = true;
} else {
assert(
state.isolatedRunner.configLinks.some(
(link) => link.linkedName === configFileName,
),
`${profile.codePrefix}-primary-config-link-missing`,
);
}
if (activeConfigSource) {
const overrideKey = webSearchAgentId ? 'webSearchEnabled' : 'stream';
const overlay = mcpOverlay
? { mcpServers: mcpOverlay.mcpServers }
: { agentLlm: { [overlayAgentId]: { [overrideKey]: true } } };
if (mcpOverlay) {
assert(
JSON.stringify(Object.keys(overlay)) ===
JSON.stringify(['mcpServers']) &&
Object.keys(overlay.mcpServers).length === 2,
'mcp-overlay-shape-invalid',
);
} else {
assert(
collectApiKeys(overlay).length === 0 &&
JSON.stringify(Object.keys(overlay)) ===
JSON.stringify(['agentLlm']) &&
JSON.stringify(Object.keys(overlay.agentLlm)) ===
JSON.stringify([overlayAgentId]) &&
JSON.stringify(Object.keys(overlay.agentLlm[overlayAgentId])) ===
JSON.stringify([overrideKey]),
webSearchAgentId
? 'web-search-overlay-shape-invalid'
: 'response-stream-overlay-shape-invalid',
);
}
await fs.writeFile(
path.join(appDataDir, localConfigFileName),
`${JSON.stringify(overlay)}\n`,
{ flag: 'wx', mode: 0o600 },
);
if (mcpOverlay) {
state.isolatedRunner.mcpOverrideCreated = true;
} else if (webSearchAgentId) {
state.isolatedRunner.webSearchOverrideCreated = true;
} else {
state.isolatedRunner.streamOverrideCreated = true;
}
}
const previousLeakCount = state.transcriptScanner?.count ?? 0;
state.secrets = [...suiteSecrets];
rebuildSupervisorSwarmTranscriptScanner();
state.transcriptScanner.count = previousLeakCount;
const unexpectedEndpoint = await fs
.lstat(path.join(appDataDir, runnerEndpointFileName))
.catch((error) => {
if (error?.code === 'ENOENT') return null;
throw error;
});
assert(!unexpectedEndpoint, `${profile.codePrefix}-endpoint-preexisted`);
state.runtimeConfigDir = appDataDir;
if (streamAgentId) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
streamAgentId,
);
assert(
isolatedEffective.stream === true &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'response-stream-effective-llm-config-invalid',
);
state.responseStream.effectiveStreamEnabled = true;
}
if (webSearchAgentId) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
webSearchAgentId,
);
if (isolatedEffective.apiKind === 'anthropic') {
state.webSearch.gatewayDiagnosis = 'anthropic-web-search-unsupported';
throw new BlockedError(['web-search-anthropic-unsupported']);
}
assert(
isolatedEffective.webSearchEnabled === true &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'web-search-effective-llm-config-invalid',
);
state.webSearch.effectiveEnabled = true;
}
if (mcpOverlay) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
mainAgentId,
);
assert(
Object.keys(isolatedConfig.config.mcpServers ?? {}).length === 2 &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'mcp-effective-runtime-config-invalid',
);
}
if (isUserInputRuntimeSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
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(
isolatedConfig.config,
mainAgentId,
);
assert(
isolatedEffective.model === 'gpt-5.5' &&
isolatedEffective.apiKind === 'openai_chat' &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'scoped-agents-effective-openai-chat-gpt-5-5-config-invalid',
);
state.scopedAgents.effectiveModel = isolatedEffective.model;
state.scopedAgents.effectiveApiKind = isolatedEffective.apiKind;
}
if (isProjectSkillSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
mainAgentId,
);
assert(
isolatedEffective.model === 'gpt-5.5' &&
isolatedEffective.apiKind === 'openai_chat' &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'project-skill-effective-openai-chat-gpt-5-5-config-invalid',
);
state.projectSkill.effectiveModel = isolatedEffective.model;
state.projectSkill.effectiveApiKind = isolatedEffective.apiKind;
}
if (isParallelReadSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
mainAgentId,
);
assert(
isolatedEffective.model === 'gpt-5.5' &&
isolatedEffective.apiKind === 'openai_chat' &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'parallel-read-effective-openai-chat-gpt-5-5-config-invalid',
);
state.parallelRead.effectiveModel = isolatedEffective.model;
state.parallelRead.effectiveApiKind = isolatedEffective.apiKind;
}
if (isSupervisorSwarmSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const requiredAgentIds = supervisorSwarmRequiredAgentIds;
const requiredEffectiveConfigs = requiredAgentIds.map((agentId) => [
agentId,
effectiveAgentLlmConfig(isolatedConfig.config, agentId),
]);
assert(
requiredEffectiveConfigs.every(
([agentId, effective]) =>
(isSupervisorSwarmToolPlanHandoffRunnerKillSuite()
? isNonEmptyString(effective.model)
: effective.model === 'gpt-5.5') &&
effective.apiKind === 'openai_chat' &&
isNonEmptyString(effective.reasoningEffort) &&
Number.isSafeInteger(effective.requestTimeoutMs) &&
effective.requestTimeoutMs > 0 &&
Number.isSafeInteger(effective.maxRetries) &&
effective.maxRetries >=
(isSupervisorSwarmInteractiveChatSuite() ||
(isSupervisorSwarmTransientRetrySuite() &&
agentId !== supervisorSwarmTransientRetryTargetAgentId)
? 0
: 1) &&
effective.maxRetries <= 3 &&
Number.isSafeInteger(effective.retryBackoffMs) &&
effective.retryBackoffMs > 0 &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof effective[key] === 'string' &&
effective[key].trim().length > 0,
),
),
isSupervisorSwarmToolPlanHandoffRunnerKillSuite()
? 'supervisor-swarm-effective-openai-chat-configured-model-invalid'
: 'supervisor-swarm-effective-openai-chat-gpt-5-5-config-invalid',
);
const globalEffective = effectiveAgentLlmConfig(
{ 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
: {},
).filter(isNonEmptyString);
const policyAgentIds = [
...new Set([...requiredAgentIds, ...configuredAgentIds]),
].sort();
const effectivePolicies = policyAgentIds.map((agentId) => [
agentId,
effectiveAgentLlmConfig(isolatedConfig.config, agentId),
]);
const supervisorEffective = requiredEffectiveConfigs.find(
([agentId]) => agentId === projectSupervisorAgentId,
)[1];
state.supervisorSwarm.effectiveModel = supervisorEffective.model;
state.supervisorSwarm.effectiveApiKind = supervisorEffective.apiKind;
state.supervisorSwarm.effectiveReasoningEffort =
supervisorEffective.reasoningEffort;
state.supervisorSwarm.effectiveRequestTimeoutMs =
supervisorEffective.requestTimeoutMs;
state.supervisorSwarm.effectiveMaxRetries = supervisorEffective.maxRetries;
state.supervisorSwarm.effectiveRetryBackoffMs =
supervisorEffective.retryBackoffMs;
state.supervisorSwarm.effectiveDefaultPolicy =
safeEffectiveAgentLlmPolicy(globalEffective);
state.supervisorSwarm.effectiveAgentPolicies = Object.fromEntries(
effectivePolicies.map(([agentId, effective]) => [
agentId,
safeEffectiveAgentLlmPolicy(effective),
]),
);
}
if (isSteerRunnerKillSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
mainAgentId,
);
assert(
isolatedEffective.model === 'gpt-5.5' &&
isolatedEffective.apiKind === 'openai_chat' &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'steer-runner-kill-effective-openai-chat-gpt-5-5-config-invalid',
);
state.steerRunnerKill.effectiveModel = isolatedEffective.model;
state.steerRunnerKill.effectiveApiKind = isolatedEffective.apiKind;
}
}
export async function readIsolatedAppDataSentinel() {
const runner = state.isolatedRunner;
const profile = isolatedSuiteAppDataProfile();
assert(
isNonEmptyString(runner.appDataDir) && isNonEmptyString(runner.ownerToken),
'isolated-appdata-ownership-missing',
);
const sentinelPath = path.join(runner.appDataDir, profile.sentinelName);
const metadata = await fs.lstat(sentinelPath);
const sentinel = await readJson(sentinelPath);
assert(
metadata.isFile() &&
!metadata.isSymbolicLink() &&
sentinel.schemaVersion === profile.sentinelSchema &&
sentinel.token === runner.ownerToken &&
sentinel.ownerPid === process.pid &&
sentinel.createdAt === runner.createdAt,
'isolated-appdata-ownership-invalid',
);
return sentinel;
}
export async function inspectOwnedRunnerIdentity(status) {
await readIsolatedAppDataSentinel();
const runner = state.isolatedRunner;
const pid = Number(status?.pid ?? status?.status?.pid);
const bootId = runnerBootId(status);
assert(
status?.running === true &&
Number.isSafeInteger(pid) &&
pid > 1 &&
pid !== process.pid &&
isNonEmptyString(bootId),
'isolated-owned-runner-status-invalid',
);
const endpointPath = path.join(runner.appDataDir, runnerEndpointFileName);
const endpointMetadata = await fs.lstat(endpointPath);
const endpoint = await readJson(endpointPath);
assert(
endpointMetadata.isFile() &&
!endpointMetadata.isSymbolicLink() &&
endpoint.pid === pid &&
endpoint.bootId === bootId &&
endpoint.protocolVersion === status.protocolVersion &&
endpoint.port === status.port &&
Number.isSafeInteger(endpoint.heartbeatAt) &&
endpoint.heartbeatAt >= runner.createdAt &&
isNonEmptyString(endpoint.token) &&
endpoint.token.length >= 32,
'isolated-owned-runner-endpoint-identity-invalid',
);
return {
pid,
bootId,
protocolVersion: endpoint.protocolVersion,
port: endpoint.port,
processIdentity: await captureOwnedRunnerProcessIdentity(pid),
};
}
export async function claimOwnedRunner(status = null) {
const liveStatus = status ?? (await readRunnerStatus());
const identity = await inspectOwnedRunnerIdentity(liveStatus);
const current = state.isolatedRunner.current;
if (current) {
assert(
current.pid === identity.pid &&
current.bootId === identity.bootId &&
current.processIdentity.fingerprint ===
identity.processIdentity.fingerprint &&
current.killHandle?.closed === false,
'isolated-owned-runner-identity-changed-after-claim',
);
return current;
}
const killHandle = await openOwnedRunnerKillHandle(identity.pid);
try {
const rechecked = await inspectOwnedRunnerIdentity(
await readRunnerStatus(),
);
assert(
rechecked.pid === identity.pid &&
rechecked.bootId === identity.bootId &&
rechecked.protocolVersion === identity.protocolVersion &&
rechecked.port === identity.port &&
rechecked.processIdentity.fingerprint ===
identity.processIdentity.fingerprint,
'isolated-owned-runner-identity-changed-during-pidfd-claim',
);
} catch (error) {
await closeOwnedRunnerKillHandle(killHandle).catch(() => {});
throw error;
}
state.isolatedRunner.current = { ...identity, killHandle };
state.isolatedRunner.pidfdClaimCount += 1;
return state.isolatedRunner.current;
}
export async function verifyOwnedRunnerForKill() {
const claimed = state.isolatedRunner.current;
assert(claimed, 'isolated-owned-runner-not-claimed');
const current = await inspectOwnedRunnerIdentity(await readRunnerStatus());
assert(
current.pid === claimed.pid &&
current.bootId === claimed.bootId &&
current.protocolVersion === claimed.protocolVersion &&
current.port === claimed.port &&
current.processIdentity.fingerprint ===
claimed.processIdentity.fingerprint &&
claimed.killHandle?.pid === claimed.pid &&
claimed.killHandle.closed === false,
'isolated-owned-runner-identity-changed-before-kill',
);
return claimed;
}
export async function ensureOwnedRunnerStableKillSupport() {
assert(
process.platform === 'linux',
'isolated-runner-stable-kill-handle-platform-unsupported',
);
const python = await findControlledLinuxPython();
const probe =
'import os, signal; assert hasattr(os, "pidfd_open") and hasattr(signal, "pidfd_send_signal"); fd = os.pidfd_open(os.getpid(), 0); os.close(fd)';
try {
await runProcess(python, ['-I', '-S', '-c', probe], {
cwd: appRoot,
timeoutMs: 30_000,
env: { LANG: 'C', LC_ALL: 'C', PATH: '/usr/bin:/bin' },
});
} catch (error) {
throw codedError('isolated-runner-pidfd-support-unavailable', error);
}
}
export async function findControlledLinuxPython() {
if (state.linuxPidfdPythonPath) return state.linuxPidfdPythonPath;
for (const candidate of ['/usr/bin/python3', '/usr/local/bin/python3']) {
const resolved = await fs.realpath(candidate).catch(() => null);
const metadata = resolved
? await fs.stat(resolved).catch(() => null)
: null;
if (metadata?.isFile() && (metadata.mode & 0o111) !== 0) {
state.linuxPidfdPythonPath = resolved;
return resolved;
}
}
throw codedError('isolated-runner-controlled-python-unavailable');
}
export async function openOwnedRunnerKillHandle(pid) {
assert(
process.platform === 'linux' && Number.isSafeInteger(pid) && pid > 1,
'isolated-runner-pidfd-open-precondition-invalid',
);
const python = await findControlledLinuxPython();
const child = spawn(
python,
['-I', '-S', '-c', linuxPidfdHelperSource, String(pid)],
{
cwd: appRoot,
env: { LANG: 'C', LC_ALL: 'C', PATH: '/usr/bin:/bin' },
stdio: ['pipe', 'pipe', 'pipe'],
},
);
const handle = {
pid,
child,
stdout: Buffer.alloc(0),
stderr: Buffer.alloc(0),
closed: false,
};
child.stdout.on('data', (chunk) => {
handle.stdout = appendBounded(handle.stdout, chunk, 4_096);
});
child.stderr.on('data', (chunk) => {
handle.stderr = appendBounded(handle.stderr, chunk, 4_096);
});
await waitForOwnedRunnerKillHandleReady(handle);
return handle;
}
export async function waitForOwnedRunnerKillHandleReady(handle) {
await new Promise((resolve, reject) => {
const timer = setTimeout(() => {
handle.child.kill('SIGKILL');
reject(codedError('isolated-runner-pidfd-open-timeout'));
}, 10_000);
const settle = (callback) => {
clearTimeout(timer);
handle.child.stdout.off('data', onData);
handle.child.off('error', onError);
handle.child.off('close', onClose);
callback();
};
const onData = () => {
if (handle.stdout.includes(Buffer.from('PIDFD_READY\n'))) {
settle(resolve);
}
};
const onError = (error) =>
settle(() =>
reject(codedError('isolated-runner-pidfd-helper-spawn-failed', error)),
);
const onClose = () =>
settle(() => reject(codedError('isolated-runner-pidfd-open-failed')));
handle.child.stdout.on('data', onData);
handle.child.on('error', onError);
handle.child.on('close', onClose);
onData();
});
}
export async function closeOwnedRunnerKillHandle(handle) {
if (!handle || handle.closed) return;
handle.closed = true;
if (handle.child.exitCode !== null || handle.child.signalCode !== null)
return;
handle.child.stdin.end('CLOSE\n');
const result = await waitForChildClose(handle.child, 10_000);
assert(result.code === 0, 'isolated-runner-pidfd-close-failed');
}
export async function signalOwnedRunnerKillHandle(handle) {
assert(
handle &&
handle.closed === false &&
handle.child.exitCode === null &&
handle.child.signalCode === null,
'isolated-runner-pidfd-handle-not-live',
);
handle.closed = true;
handle.child.stdin.end('KILL\n');
const result = await waitForChildClose(handle.child, 15_000);
assert(
result.code === 0 && handle.stdout.includes(Buffer.from('PIDFD_EXITED\n')),
'isolated-runner-pidfd-sigkill-failed',
);
}
export async function waitForChildClose(child, timeoutMs) {
if (child.exitCode !== null || child.signalCode !== null) {
return { code: child.exitCode, signal: child.signalCode };
}
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
child.kill('SIGKILL');
reject(codedError('isolated-runner-pidfd-helper-timeout'));
}, timeoutMs);
const onError = (error) => {
clearTimeout(timer);
child.off('close', onClose);
reject(codedError('isolated-runner-pidfd-helper-failed', error));
};
const onClose = (code, signal) => {
clearTimeout(timer);
child.off('error', onError);
resolve({ code, signal });
};
child.once('error', onError);
child.once('close', onClose);
});
}
export async function captureOwnedRunnerProcessIdentity(pid) {
const expectedAppData = state.isolatedRunner.appDataDir;
assert(
isNonEmptyString(expectedAppData) && Boolean(state.cliBinary),
'isolated-runner-process-identity-context-missing',
);
if (process.platform === 'linux') {
const [executable, expectedExecutable, stat, commandLine] =
await Promise.all([
fs.realpath(`/proc/${pid}/exe`),
fs.realpath(state.cliBinary),
fs.readFile(`/proc/${pid}/stat`, 'utf8'),
fs.readFile(`/proc/${pid}/cmdline`),
]);
const closeParenthesis = stat.lastIndexOf(')');
const fields = stat
.slice(closeParenthesis + 1)
.trim()
.split(/\s+/u);
const startTime = fields[19];
const argv = commandLine.toString('utf8').split('\0').filter(Boolean);
const configIndex = argv.indexOf('--config-dir');
assert(
closeParenthesis > 0 &&
isNonEmptyString(startTime) &&
executable === expectedExecutable &&
argv.includes('--agent-runner') &&
configIndex >= 0 &&
argv[configIndex + 1] === expectedAppData,
'isolated-runner-linux-process-identity-invalid',
);
return {
kind: 'linux-proc',
fingerprint: hashValue(JSON.stringify({ executable, startTime, argv })),
};
}
if (process.platform === 'darwin') {
const result = await runProcess(
'/bin/ps',
['-p', String(pid), '-o', 'lstart=', '-o', 'command='],
{ cwd: appRoot, timeoutMs: 30_000 },
);
assert(
result.stdout.includes(path.basename(state.cliBinary)) &&
result.stdout.includes('--agent-runner') &&
result.stdout.includes(expectedAppData),
'isolated-runner-darwin-process-identity-invalid',
);
return {
kind: 'darwin-ps',
fingerprint: hashValue(result.stdout.trim()),
};
}
if (process.platform === 'win32') {
const script = `$process = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}"; if ($null -eq $process) { exit 3 }; $process | Select-Object ProcessId,CreationDate,ExecutablePath,CommandLine | ConvertTo-Json -Compress`;
const result = await runProcess(
'powershell.exe',
['-NoProfile', '-NonInteractive', '-Command', script],
{ cwd: appRoot, timeoutMs: 30_000 },
);
const value = JSON.parse(result.stdout);
const executable = path.resolve(String(value.ExecutablePath ?? ''));
const expectedExecutable = path.resolve(state.cliBinary);
const commandLine = String(value.CommandLine ?? '');
assert(
Number(value.ProcessId) === pid &&
executable.toLowerCase() === expectedExecutable.toLowerCase() &&
isNonEmptyString(value.CreationDate) &&
commandLine.includes('--agent-runner') &&
commandLine.includes(expectedAppData),
'isolated-runner-windows-process-identity-invalid',
);
return {
kind: 'windows-cim',
fingerprint: hashValue(
JSON.stringify({
pid,
creationDate: value.CreationDate,
executable: executable.toLowerCase(),
commandLine,
}),
),
};
}
throw codedError('isolated-runner-process-identity-platform-unsupported');
}
export function isProcessAlive(pid) {
if (!Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid)
return false;
try {
process.kill(pid, 0);
return true;
} catch (error) {
return error?.code === 'EPERM';
}
}
export async function killRunnerPidOnce(pid, ownedRunner) {
if (isIsolatedRunnerSuite()) {
assert(ownedRunner, 'isolated-runner-pid-kill-fallback-forbidden');
}
if (ownedRunner) {
const claimed = state.isolatedRunner.current;
assert(
claimed?.pid === pid && claimed.killHandle?.pid === pid,
'isolated-runner-pidfd-identity-missing',
);
await signalOwnedRunnerKillHandle(claimed.killHandle);
state.isolatedRunner.pidfdSignalCount += 1;
state.runnerKilled = true;
state.isolatedRunner.current = null;
return;
}
try {
process.kill(pid, 'SIGKILL');
} catch (error) {
throw codedError('runner-sigkill-failed', error);
}
state.runnerKilled = true;
const deadline = Date.now() + 10_000;
while (Date.now() < deadline) {
if (!isProcessAlive(pid)) {
if (ownedRunner) state.isolatedRunner.current = null;
return;
}
await sleep(50);
}
throw codedError('runner-still-alive-after-sigkill');
}
export async function stopClaimedOwnedRunnerWithoutEndpoint() {
const claimed = state.isolatedRunner.current;
if (!claimed) return;
if (!isProcessAlive(claimed.pid)) {
await closeOwnedRunnerKillHandle(claimed.killHandle);
state.isolatedRunner.current = null;
return;
}
let currentIdentity;
try {
currentIdentity = await captureOwnedRunnerProcessIdentity(claimed.pid);
} catch (error) {
if (!isProcessAlive(claimed.pid)) {
await closeOwnedRunnerKillHandle(claimed.killHandle);
state.isolatedRunner.current = null;
return;
}
throw error;
}
assert(
currentIdentity.fingerprint === claimed.processIdentity.fingerprint,
'isolated-owned-runner-identity-changed-without-endpoint',
);
await killRunnerPidOnce(claimed.pid, true);
}
export async function stopOwnedIsolatedRunner() {
await readIsolatedAppDataSentinel();
const endpointPath = path.join(
state.isolatedRunner.appDataDir,
runnerEndpointFileName,
);
const endpointMetadata = await fs.lstat(endpointPath).catch((error) => {
if (error?.code === 'ENOENT') return null;
throw error;
});
if (!endpointMetadata) {
assert(
state.isolatedRunner.current || !state.isolatedRunner.launchAttempted,
'isolated-owned-runner-endpoint-missing-before-stable-claim',
);
await stopClaimedOwnedRunnerWithoutEndpoint();
return;
}
assert(
endpointMetadata.isFile() && !endpointMetadata.isSymbolicLink(),
'isolated-owned-runner-endpoint-not-regular-file',
);
const endpoint = await readJson(endpointPath);
const status = await readRunnerStatus();
if (status?.running !== true) {
assert(
!isProcessAlive(Number(endpoint.pid)),
'isolated-owned-runner-live-pid-without-identity',
);
await closeOwnedRunnerKillHandle(state.isolatedRunner.current?.killHandle);
state.isolatedRunner.current = null;
return;
}
await claimOwnedRunner(status);
await killRunnerOnce();
}
export async function verifyIsolatedSuiteConfigLinksUnchanged() {
for (const link of state.isolatedRunner.configLinks) {
const [sourceMetadata, linkedMetadata, sourceContent, linkedContent] =
await Promise.all([
fs.lstat(link.sourcePath),
fs.lstat(link.linkedPath),
fs.readFile(link.sourcePath),
fs.readFile(link.linkedPath),
]);
const sourceHash = createHash('sha256').update(sourceContent).digest('hex');
const linkedHash = createHash('sha256').update(linkedContent).digest('hex');
const replicaIdentityValid =
link.storageMode === 'private-copy'
? linkedMetadata.dev === link.linkedDev &&
linkedMetadata.ino === link.linkedIno &&
(linkedMetadata.dev !== sourceMetadata.dev ||
linkedMetadata.ino !== sourceMetadata.ino) &&
(linkedMetadata.mode & 0o077) === 0
: linkedMetadata.dev === link.dev && linkedMetadata.ino === link.ino;
const sourceMetadataStable =
sourceMetadata.mode === link.sourceMode &&
sourceMetadata.size === link.sourceSize &&
sourceMetadata.mtimeMs === link.sourceMtimeMs &&
(link.storageMode !== 'private-copy' ||
(sourceMetadata.ctimeMs === link.sourceCtimeMs &&
sourceMetadata.nlink === link.nlink));
assert(
sourceMetadata.isFile() &&
!sourceMetadata.isSymbolicLink() &&
linkedMetadata.isFile() &&
!linkedMetadata.isSymbolicLink() &&
sourceMetadata.dev === link.dev &&
sourceMetadata.ino === link.ino &&
sourceMetadataStable &&
replicaIdentityValid &&
sourceHash === link.sha256 &&
linkedHash === link.sha256,
'isolated-source-config-changed-during-suite',
);
}
}
export async function verifySourceConfigLinkCountsRestored() {
for (const link of state.isolatedRunner.configLinks) {
const metadata = await fs.lstat(link.sourcePath);
assert(
metadata.isFile() &&
!metadata.isSymbolicLink() &&
metadata.dev === link.dev &&
metadata.ino === link.ino &&
metadata.nlink === link.nlink,
'isolated-source-config-link-count-not-restored',
);
}
}
export async function removeIsolatedSuiteAppData() {
await readIsolatedAppDataSentinel();
const profile = isolatedSuiteAppDataProfile();
const [sourceConfigDir, appDataDir] = await Promise.all([
fs.realpath(state.options.configDir),
fs.realpath(state.isolatedRunner.appDataDir),
]);
const cleanupPathValid = isolatedSuiteUsesSiblingAppData()
? !isPathInside(sourceConfigDir, appDataDir) &&
path.dirname(appDataDir) === path.dirname(sourceConfigDir)
: isPathInside(sourceConfigDir, appDataDir);
assert(
cleanupPathValid && path.basename(appDataDir).startsWith(profile.prefix),
'isolated-appdata-cleanup-path-invalid',
);
let ownershipError = null;
try {
await verifySourceAppDataDirectoryUntouched();
await verifyIsolatedSuiteConfigLinksUnchanged();
await verifySourceRunnerEndpointUnchanged();
} catch (error) {
ownershipError = error;
}
await fs.rm(appDataDir, { recursive: true, force: false });
state.runtimeConfigDir = state.options.configDir;
try {
await verifySourceConfigLinkCountsRestored();
state.isolatedRunner.sourceConfigLinksVerified = true;
} catch (error) {
ownershipError ??= error;
}
if (ownershipError) throw ownershipError;
return true;
}