import { spawn as nodeSpawn, type SpawnOptions } from 'node:child_process'; import { createHash } from 'node:crypto'; import { constants, existsSync } from 'node:fs'; import { open } from 'node:fs/promises'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { FUSION_CANDIDATE_OUTPUT_RECOVERY_PATH_ENV, FUSION_CHILD_MAX_PROVIDER_REQUESTS, FUSION_CHILD_MAX_TOOL_CALLS, FUSION_CHILD_RESULT_PREFIX, FUSION_CHILD_RESULT_SCHEMA_VERSION, FUSION_CHILD_SETTLEMENT_PREFIX, FUSION_CHILD_SETTLEMENT_SCHEMA_VERSION, FUSION_RESEARCH_ENABLED_ENV, FUSION_RUNTIME_GUARD_PREFIX, FUSION_RUNTIME_GUARD_SCHEMA_VERSION, FUSION_SOURCE_POLICY_PATH_ENV, FUSION_SOURCE_POLICY_SHA256_ENV, FUSION_TOOL_CALL_LOG_PATH_ENV, FUSION_TOOL_CALL_SEAL_SCHEMA_VERSION, FUSION_TOOL_CALL_SEAL_SUFFIX, FUSION_CHILD_MAX_TOTAL_TOOL_RESULT_BYTES, buildFusionChildSettlement, isRecoverableFusionChildErrorRecord, serializeFusionChildResultRecords, type FusionChildOutputContractMetadata, type FusionChildOutputRecoveryRole, type FusionChildResultMetadata, type FusionChildSettlementFailureReason, type FusionChildSettlementRecord, type FusionRuntimeGuardCode, type FusionRuntimeGuardRecord, } from './child-protocol.js'; import { FUSION_CLAUDE_CACHE_BREAKPOINT_LIMIT, FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION, FUSION_CLAUDE_CACHE_RETENTION_ENV, type FusionClaudeCacheObservation, type FusionClaudeCacheRetention, } from './claude-cache.js'; import { FUSION_FORBIDDEN_TOOLS, FUSION_NO_TOOLS_CAPABILITY, FUSION_INSPECT_TOOLS, FUSION_RESEARCH_TOOLS, FUSION_TOOL_CALL_LOG_SCHEMA_VERSION, FUSION_WEB_FETCH_TOOL_NAME, FusionError, addFusionUsage, cloneFusionUsage, createEmptyFusionUsage, type FusionCapability, type FusionCandidateOutputRecovery, type FusionChildRunResult, type FusionErrorDetails, type FusionStage, type FusionToolCallLogRecord, type FusionToolCallTrace, type FusionUsage, type ResolvedFusionModel, } from './types.js'; import { isJsonObject, parseJsonText } from '../common.js'; import { FUSION_CANDIDATE_MAX_OUTPUT_BYTES, fusionJsonRenderedTextBytes, } from './output-contract.js'; import { canonicalizeFusionPublicUrl, readFusionSourcePolicyFile } from './source-policy.js'; import { assertWindowsCommandLineWithinLimit, piLaunchArgv, resolvePiLaunch, type PiLaunchDependencies, } from '../pi-launch.js'; import { resolveAnthropicAttributionExtensionPath } from '../anthropic-attribution-path.js'; // The response cap now applies to one final full answer, not cumulative Pi JSON events. export const FUSION_CHILD_STDOUT_LIMIT_BYTES = 32 * 1024 * 1024; export const FUSION_CHILD_STDERR_LIMIT_BYTES = 16 * 1024 * 1024; export const FUSION_CHILD_TIMEOUT_MS = 50 * 60 * 1000; /** * Stale-action watchdog threshold. * * Activity is one stdout or stderr byte from the child. The child extension emits its * compact metadata frame only at `message_end`, and Pi text mode writes stdout only for * the final assistant message, so a single slow model turn is genuinely silent on both * streams. The threshold must therefore exceed the longest plausible single turn, not the * longest plausible tool call: a value tuned to tool latency would kill healthy children * mid-reasoning. Thirty-five minutes leaves a wide margin over observed turn latency * while remaining materially below the 50-minute absolute cap, so the stale-action * watchdog retains an independent purpose instead of becoming an unreachable duplicate * timeout. */ export const FUSION_CHILD_IDLE_TIMEOUT_MS = 35 * 60 * 1000; export const FUSION_CHILD_KILL_GRACE_MS = 3000; export const FUSION_CHILD_SIGKILL_WAIT_MS = 5000; const FUSION_PI_CHILD_O_NOFOLLOW = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0; export const FUSION_CHILD_REMOVED_ENV_KEYS = [ 'PI_SESSION_ID', 'PI_SESSION_FILE', 'PI_PROVIDER', 'PI_MODEL', 'PI_REASONING_LEVEL', 'OPENROUTER_API_KEY', 'OPENROUTER_BASE_URL', 'OPENAI_API_KEY', 'OPENAI_BASE_URL', 'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'AZURE_OPENAI_API_KEY', 'AZURE_OPENAI_BASE_URL', 'AZURE_OPENAI_ENDPOINT', 'AZURE_OPENAI_RESOURCE_NAME', 'AZURE_OPENAI_API_VERSION', 'AZURE_OPENAI_DEPLOYMENT_NAME_MAP', 'AZURE_OPENAI_AD_TOKEN', 'PI_API_KEY', 'PI_API_BASE_URL', 'PI_AUTH_FILE', FUSION_TOOL_CALL_LOG_PATH_ENV, FUSION_CANDIDATE_OUTPUT_RECOVERY_PATH_ENV, FUSION_RESEARCH_ENABLED_ENV, FUSION_SOURCE_POLICY_PATH_ENV, FUSION_SOURCE_POLICY_SHA256_ENV, ] as const; interface FusionReadableStream { on(event: 'data', listener: (data: Buffer | string) => void): unknown; off(event: 'data', listener: (data: Buffer | string) => void): unknown; } interface FusionWritableStream { write(data: Buffer, callback: (error?: Error | null) => void): boolean; end(callback?: () => void): unknown; once(event: 'error', listener: (error: Error) => void): unknown; off(event: 'error', listener: (error: Error) => void): unknown; } export interface FusionChildProcess { pid?: number | undefined; stdin?: FusionWritableStream | null | undefined; stdout?: FusionReadableStream | null | undefined; stderr?: FusionReadableStream | null | undefined; kill(signal?: NodeJS.Signals): boolean; once(event: 'error', listener: (error: Error) => void): unknown; once( event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void, ): unknown; off(event: 'error', listener: (error: Error) => void): unknown; off( event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void, ): unknown; } export type FusionChildSpawn = ( command: string, args: string[], options: SpawnOptions, ) => FusionChildProcess; export type FusionKillProcess = (pid: number, signal?: NodeJS.Signals | number) => boolean; export interface RunPiChildOptions { stage: FusionStage; slot?: 1 | 2 | 3; attempt: number; cwd: string; model: ResolvedFusionModel; capability?: FusionCapability | undefined; systemPrompt: string; userPrompt: string; signal?: AbortSignal | undefined; spawn?: FusionChildSpawn | undefined; killProcess?: FusionKillProcess | undefined; platform?: NodeJS.Platform | undefined; env?: NodeJS.ProcessEnv | undefined; stdoutLimitBytes?: number | undefined; childExtensionPath?: string | undefined; stderrLimitBytes?: number | undefined; timeoutMs?: number | undefined; idleTimeoutMs?: number | undefined; killGraceMs?: number | undefined; sigkillWaitMs?: number | undefined; piLaunchDependencies?: PiLaunchDependencies | undefined; toolCallLogPath?: string | undefined; sourcePolicy?: { path: string; sha256: string } | undefined; candidateOutputRecoveryPath?: string | undefined; } interface CloseRecord { code: number | null; signal: NodeJS.Signals | null; } interface ProcessState { primaryError: FusionError | undefined; cleanupErrors: string[]; terminationStarted: boolean; termTimer: NodeJS.Timeout | undefined; waitTimer: NodeJS.Timeout | undefined; timeoutTimer: NodeJS.Timeout | undefined; idleTimer: NodeJS.Timeout | undefined; settled: boolean; } interface ObservedChildSnapshot { usage: FusionUsage; provider?: string; model?: string; qualifiedId?: string; } export class FusionChildRunError extends FusionError { readonly events: Buffer; readonly response: Buffer; readonly stderr: Buffer; readonly exitCode: number | null; readonly signalName: NodeJS.Signals | null; readonly usage: FusionUsage; readonly provider: string | undefined; readonly modelName: string | undefined; readonly qualifiedId: string | undefined; readonly outputRecovery: FusionCandidateOutputRecovery | undefined; constructor( error: FusionError, events: Buffer, response: Buffer, stderr: Buffer, close: CloseRecord, observed: ObservedChildSnapshot, outputRecovery?: FusionCandidateOutputRecovery, ) { const details: FusionErrorDetails = { code: error.code, transient: error.transient, childCreated: error.childCreated, }; if (error.stage !== undefined) details.stage = error.stage; if (error.slot !== undefined) details.slot = error.slot; if (error.attempt !== undefined) details.attempt = error.attempt; if (error.artifactDir !== undefined) details.artifactDir = error.artifactDir; super(error.message, details); this.name = 'FusionChildRunError'; this.events = events; this.response = response; this.stderr = stderr; this.exitCode = close.code; this.signalName = close.signal; this.usage = cloneFusionUsage(observed.usage); this.provider = observed.provider; this.modelName = observed.model; this.qualifiedId = observed.qualifiedId; this.outputRecovery = outputRecovery; } } export function fusionPiChildEnv( env: NodeJS.ProcessEnv = process.env, provider?: string | undefined, ): NodeJS.ProcessEnv { const out: NodeJS.ProcessEnv = { ...env }; const removed = new Set(FUSION_CHILD_REMOVED_ENV_KEYS); for (const inheritedKey of Object.keys(out)) { if (removed.has(inheritedKey.toUpperCase())) Reflect.deleteProperty(out, inheritedKey); } out['PI_SKIP_VERSION_CHECK'] = '1'; if (provider === 'anthropic' && out[FUSION_CLAUDE_CACHE_RETENTION_ENV] === undefined) { out[FUSION_CLAUDE_CACHE_RETENTION_ENV] = 'long'; } return out; } export function resolveFusionChildExtensionPath( moduleUrl = import.meta.url, pathExists: (path: string) => boolean = existsSync, ): string { const modulePath = fileURLToPath(moduleUrl); const extension = modulePath.endsWith('.ts') ? 'fusion-child.ts' : 'fusion-child.js'; const candidate = resolve(dirname(modulePath), '../../../extensions', extension); if (!pathExists(candidate)) { throw new Error(`Fusion child metadata extension is missing: ${candidate}`); } return candidate; } /** * Provider whose isolated children require the package-owned attribution and * exact-match system-prompt sanitization extension. */ export const FUSION_SANITIZED_PROVIDER = 'anthropic'; export function assertFusionToolPolicyDisjoint( allowlist: readonly string[] = FUSION_INSPECT_TOOLS, denylist: readonly string[] = FUSION_FORBIDDEN_TOOLS, ): void { for (const forbidden of denylist) { if (allowlist.includes(forbidden)) { throw new FusionError( `fusion inspect capability would enable the forbidden tool ${forbidden}`, { code: 'orchestration_failed', childCreated: false }, ); } } } function researchToolAllowlist(): readonly string[] { return FUSION_RESEARCH_TOOLS; } function fusionToolArgv(capability: FusionCapability): string[] { if (capability === 'reason') return ['--no-tools']; if (capability === 'inspect') { assertFusionToolPolicyDisjoint(FUSION_INSPECT_TOOLS); return [ '--no-builtin-tools', '--tools', FUSION_INSPECT_TOOLS.join(','), '--exclude-tools', FUSION_FORBIDDEN_TOOLS.join(','), ]; } if (capability === 'research') { const allowlist = researchToolAllowlist(); assertFusionToolPolicyDisjoint(allowlist); return [ '--no-builtin-tools', '--tools', allowlist.join(','), '--exclude-tools', FUSION_FORBIDDEN_TOOLS.join(','), ]; } throw new FusionError(`fusion capability ${String(capability)} is not supported`, { code: 'orchestration_failed', childCreated: false, }); } /** * Extensions explicitly loaded into a child, in deterministic order. * * `--no-extensions` disables discovery but still honours explicit `--extension` * paths, so this list is the complete set a child receives. The metadata * extension is always present. For Claude routes the sanitizer loads first so * the package-owned attribution provider establishes the Claude Code OAuth * request shape first, the sanitizer preserves that shape while removing only * rejected prompt lines, and the private runtime governor observes the final * payload. Non-Anthropic child argv remains unchanged. */ export function fusionChildExtensionPaths( model: ResolvedFusionModel, childExtensionPath: string, resolveAttribution: () => string = resolveAnthropicAttributionExtensionPath, ): readonly string[] { if (model.provider !== FUSION_SANITIZED_PROVIDER) return [childExtensionPath]; return [resolveAttribution(), childExtensionPath]; } export function buildFusionPiChildArgv( model: ResolvedFusionModel, systemPrompt: string, childExtensionPath = resolveFusionChildExtensionPath(), capability: FusionCapability = FUSION_NO_TOOLS_CAPABILITY, resolveAttribution: () => string = resolveAnthropicAttributionExtensionPath, ): string[] { const extensionArgs = fusionChildExtensionPaths( model, childExtensionPath, resolveAttribution, ).flatMap((path) => ['--extension', path]); return [ '--mode', 'text', '--no-session', ...fusionToolArgv(capability), '--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes', '--no-context-files', ...extensionArgs, '--provider', model.provider, '--model', model.model, '--thinking', model.thinkingLevel, '--system-prompt', systemPrompt, ]; } const SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/; const FUSION_CHILD_RESULT_PREFIX_BYTES = Buffer.from(FUSION_CHILD_RESULT_PREFIX, 'utf8'); const FUSION_CHILD_SETTLEMENT_PREFIX_BYTES = Buffer.from(FUSION_CHILD_SETTLEMENT_PREFIX, 'utf8'); const FUSION_RUNTIME_GUARD_PREFIX_BYTES = Buffer.from(FUSION_RUNTIME_GUARD_PREFIX, 'utf8'); const PI_EXTENSION_ERROR_PREFIX_BYTES = Buffer.from('Extension error (', 'utf8'); interface ParsedFusionChildStderr { records: FusionChildResultMetadata[]; events: Buffer; diagnostics: Buffer; } function assertClosedRecord( value: unknown, keys: readonly string[], label: string, ): Record { if (!isJsonObject(value) || Array.isArray(value)) throw new Error(`${label} must be an object`); const actual = Object.keys(value).sort(); const expected = [...keys].sort(); if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) { throw new Error(`${label} keys mismatch: expected ${expected.join(', ')}`); } return value; } function assertClosedRecordWithOptional( value: unknown, requiredKeys: readonly string[], optionalKeys: readonly string[], label: string, ): Record { if (!isJsonObject(value) || Array.isArray(value)) throw new Error(`${label} must be an object`); const allowed = new Set([...requiredKeys, ...optionalKeys]); const actual = Object.keys(value).sort(); const missing = requiredKeys.filter((key) => !Object.hasOwn(value, key)); const unknownKeys = actual.filter((key) => !allowed.has(key)); if (missing.length > 0 || unknownKeys.length > 0) { throw new Error( `${label} keys mismatch: required ${[...requiredKeys].sort().join(', ')}; optional ${[ ...optionalKeys, ] .sort() .join(', ')}`, ); } return value; } function requireNonBlankString( record: Record, key: string, label: string, ): string { const value = record[key]; if (typeof value !== 'string' || value.trim().length === 0) throw new Error(`${label}.${key} must be a non-blank string`); return value; } function requireSha256(record: Record, key: string, label: string): string { const value = record[key]; if (typeof value !== 'string' || !SHA256_HEX_PATTERN.test(value)) throw new Error(`${label}.${key} must be a lowercase SHA-256 hex digest`); return value; } function requireUsageInteger( record: Record, key: string, label: string, ): number { const value = record[key]; if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) throw new Error(`${label}.${key} must be a non-negative safe integer`); return value; } function requirePositiveSafeInteger( record: Record, key: string, label: string, ): number { const value = requireUsageInteger(record, key, label); if (value === 0) throw new Error(`${label}.${key} must be a positive safe integer`); return value; } function requireCostNumber( record: Record, key: string, label: string, ): number { const value = record[key]; if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) throw new Error(`${label}.${key} must be a non-negative finite number`); return value; } function parseCompactUsage(value: unknown): FusionUsage { const record = assertClosedRecordWithOptional( value, ['input', 'output', 'cacheRead', 'cacheWrite', 'totalTokens', 'cost'], ['cacheWrite1h', 'reasoning'], 'fusion child usage', ); const cost = assertClosedRecord( record['cost'], ['input', 'output', 'cacheRead', 'cacheWrite', 'total'], 'fusion child usage.cost', ); const output = requireUsageInteger(record, 'output', 'fusion child usage'); const cacheWrite = requireUsageInteger(record, 'cacheWrite', 'fusion child usage'); const cacheWrite1h = record['cacheWrite1h'] === undefined ? undefined : requireUsageInteger(record, 'cacheWrite1h', 'fusion child usage'); const reasoning = record['reasoning'] === undefined ? undefined : requireUsageInteger(record, 'reasoning', 'fusion child usage'); if (cacheWrite1h !== undefined && cacheWrite1h > cacheWrite) { throw new Error('fusion child usage.cacheWrite1h must not exceed cacheWrite'); } if (reasoning !== undefined && reasoning > output) { throw new Error('fusion child usage.reasoning must not exceed output'); } return { input: requireUsageInteger(record, 'input', 'fusion child usage'), output, cacheRead: requireUsageInteger(record, 'cacheRead', 'fusion child usage'), cacheWrite, ...(cacheWrite1h === undefined ? {} : { cacheWrite1h }), ...(reasoning === undefined ? {} : { reasoning }), totalTokens: requireUsageInteger(record, 'totalTokens', 'fusion child usage'), cost: { input: requireCostNumber(cost, 'input', 'fusion child usage.cost'), output: requireCostNumber(cost, 'output', 'fusion child usage.cost'), cacheRead: requireCostNumber(cost, 'cacheRead', 'fusion child usage.cost'), cacheWrite: requireCostNumber(cost, 'cacheWrite', 'fusion child usage.cost'), total: requireCostNumber(cost, 'total', 'fusion child usage.cost'), }, }; } const FUSION_CLAUDE_CACHE_RETENTIONS = new Set([ 'none', 'short', 'long', ]); function parseFusionClaudeCacheObservation( value: unknown, provider: string, ): FusionClaudeCacheObservation { const record = assertClosedRecord( value, [ 'schema_version', 'applicability', 'source', 'requested_retention', 'effective_retention', 'breakpoint_count', 'request_ordinal', ], 'fusion child cache observation', ); if (record['schema_version'] !== FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION) { throw new Error('fusion child cache observation schema_version mismatch'); } const applicability = record['applicability']; const source = record['source']; const requested = record['requested_retention']; const effective = record['effective_retention']; const breakpointCount = requireUsageInteger( record, 'breakpoint_count', 'fusion child cache observation', ); const requestOrdinal = requirePositiveSafeInteger( record, 'request_ordinal', 'fusion child cache observation', ); if (requestOrdinal > FUSION_CHILD_MAX_PROVIDER_REQUESTS) { throw new Error('fusion child cache observation exceeds the provider request limit'); } if (breakpointCount > FUSION_CLAUDE_CACHE_BREAKPOINT_LIMIT) { throw new Error('fusion child cache observation exceeds the Anthropic breakpoint limit'); } if (provider !== 'anthropic') { if ( applicability !== 'not_applicable' || source !== 'not_applicable' || requested !== null || effective !== null || breakpointCount !== 0 ) { throw new Error('non-Anthropic fusion child has contradictory cache observation'); } return { schema_version: FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION, applicability: 'not_applicable', source: 'not_applicable', requested_retention: null, effective_retention: null, breakpoint_count: 0, request_ordinal: requestOrdinal, }; } if (applicability !== 'anthropic') { throw new Error('Anthropic fusion child cache observation is not applicable'); } if (source !== 'default' && source !== FUSION_CLAUDE_CACHE_RETENTION_ENV) { throw new Error('Anthropic fusion child cache observation source is invalid'); } if ( typeof requested !== 'string' || !FUSION_CLAUDE_CACHE_RETENTIONS.has(requested as FusionClaudeCacheRetention) || typeof effective !== 'string' || !FUSION_CLAUDE_CACHE_RETENTIONS.has(effective as FusionClaudeCacheRetention) ) { throw new Error('Anthropic fusion child cache retention is invalid'); } const requestedRetention = requested as FusionClaudeCacheRetention; const effectiveRetention = effective as FusionClaudeCacheRetention; if (source === 'default' && requestedRetention !== 'long') { throw new Error('default Anthropic cache policy did not request long retention'); } if ((effectiveRetention === 'none') !== (breakpointCount === 0)) { throw new Error('Anthropic fusion child cache retention/breakpoint evidence mismatch'); } if (requestedRetention === 'none' && effectiveRetention !== 'none') { throw new Error('disabled Anthropic cache policy reported active breakpoints'); } if (effectiveRetention === 'long' && requestedRetention !== 'long') { throw new Error('Anthropic long cache retention was not requested'); } return { schema_version: FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION, applicability: 'anthropic', source, requested_retention: requestedRetention, effective_retention: effectiveRetention, breakpoint_count: breakpointCount, request_ordinal: requestOrdinal, }; } const FUSION_OUTPUT_RECOVERY_ROLES = new Set([ 'none', 'oversized_original', 'replacement', ]); function parseChildOutputContract(value: unknown): FusionChildOutputContractMetadata { const record = assertClosedRecord( value, ['json_rendered_bytes', 'candidate_limit_bytes', 'recovery_role'], 'fusion child result.output_contract', ); const candidateLimitValue = record['candidate_limit_bytes']; const candidateLimit = candidateLimitValue === null ? null : requirePositiveSafeInteger( record, 'candidate_limit_bytes', 'fusion child result.output_contract', ); if (candidateLimit !== null && candidateLimit !== FUSION_CANDIDATE_MAX_OUTPUT_BYTES) { throw new Error('fusion child result candidate output limit mismatches the shared contract'); } const recoveryRole = record['recovery_role']; if ( typeof recoveryRole !== 'string' || !FUSION_OUTPUT_RECOVERY_ROLES.has(recoveryRole as FusionChildOutputRecoveryRole) ) { throw new Error('fusion child result.output_contract.recovery_role is invalid'); } if (recoveryRole !== 'none' && candidateLimit === null) { throw new Error('fusion child result output recovery has no candidate contract'); } return { json_rendered_bytes: requireUsageInteger( record, 'json_rendered_bytes', 'fusion child result.output_contract', ), candidate_limit_bytes: candidateLimit, recovery_role: recoveryRole as FusionChildOutputRecoveryRole, }; } function parseChildResultMetadata(value: unknown): FusionChildResultMetadata { const record = assertClosedRecord( value, [ 'schema_version', 'provider', 'model', 'stop_reason', 'text_blocks', 'text_sha256', 'usage', 'cache_observation', 'output_contract', ], 'fusion child result', ); if (record['schema_version'] !== FUSION_CHILD_RESULT_SCHEMA_VERSION) throw new Error('fusion child result schema_version mismatch'); const textBlocksValue = record['text_blocks']; if (!Array.isArray(textBlocksValue)) throw new Error('fusion child result.text_blocks must be an array'); const textBlocks = textBlocksValue.map((value, index) => { const label = `fusion child result.text_blocks[${String(index)}]`; const block = assertClosedRecord(value, ['utf8_bytes', 'sha256'], label); return { utf8_bytes: requireUsageInteger(block, 'utf8_bytes', label), sha256: requireSha256(block, 'sha256', label), }; }); const usage = parseCompactUsage(record['usage']); const provider = requireNonBlankString(record, 'provider', 'fusion child result'); return { schema_version: FUSION_CHILD_RESULT_SCHEMA_VERSION, provider, model: requireNonBlankString(record, 'model', 'fusion child result'), stop_reason: requireNonBlankString(record, 'stop_reason', 'fusion child result'), text_blocks: textBlocks, text_sha256: requireSha256(record, 'text_sha256', 'fusion child result'), usage, cache_observation: parseFusionClaudeCacheObservation(record['cache_observation'], provider), output_contract: parseChildOutputContract(record['output_contract']), }; } const FUSION_RUNTIME_GUARD_CODES = new Set([ 'provider_request_limit', 'provider_payload_invalid', 'claude_cache_policy', 'tool_call_limit', ]); export function parseFusionRuntimeGuard(stderr: Buffer): FusionRuntimeGuardRecord | undefined { const frames: FusionRuntimeGuardRecord[] = []; let cursor = 0; for (;;) { const frameStart = stderr.indexOf(FUSION_RUNTIME_GUARD_PREFIX_BYTES, cursor); if (frameStart < 0) break; const payloadStart = frameStart + FUSION_RUNTIME_GUARD_PREFIX_BYTES.length; const newline = stderr.indexOf(10, payloadStart); if (newline < 0) throw new Error('fusion runtime guard frame is not newline-terminated'); const bytes = stderr.subarray(payloadStart, newline); const text = bytes.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(bytes)) { throw new Error('fusion runtime guard frame is not valid UTF-8'); } const record = assertClosedRecord( parseJsonText(text), [ 'schema_version', 'code', 'provider', 'model', 'request_ordinal', 'tool_call_count', 'payload_bytes', 'payload_sha256', 'message', ], 'fusion runtime guard', ); if (record['schema_version'] !== FUSION_RUNTIME_GUARD_SCHEMA_VERSION) { throw new Error('fusion runtime guard schema_version mismatch'); } const code = requireNonBlankString(record, 'code', 'fusion runtime guard'); if (!FUSION_RUNTIME_GUARD_CODES.has(code as FusionRuntimeGuardCode)) { throw new Error(`fusion runtime guard code is unsupported: ${code}`); } const frame: FusionRuntimeGuardRecord = { schema_version: FUSION_RUNTIME_GUARD_SCHEMA_VERSION, code: code as FusionRuntimeGuardCode, provider: requireNonBlankString(record, 'provider', 'fusion runtime guard'), model: requireNonBlankString(record, 'model', 'fusion runtime guard'), request_ordinal: requirePositiveSafeInteger( record, 'request_ordinal', 'fusion runtime guard', ), tool_call_count: requireUsageInteger(record, 'tool_call_count', 'fusion runtime guard'), payload_bytes: requireUsageInteger(record, 'payload_bytes', 'fusion runtime guard'), payload_sha256: requireSha256(record, 'payload_sha256', 'fusion runtime guard'), message: requireNonBlankString(record, 'message', 'fusion runtime guard'), }; const emptyPayloadHash = createHash('sha256').update(Buffer.alloc(0)).digest('hex'); if (frame.code === 'claude_cache_policy' || frame.code === 'provider_payload_invalid') { if (frame.payload_bytes !== 0 || frame.payload_sha256 !== emptyPayloadHash) { throw new Error('fusion runtime guard invalid-payload evidence mismatch'); } } if ( frame.code === 'provider_request_limit' && frame.request_ordinal <= FUSION_CHILD_MAX_PROVIDER_REQUESTS ) { throw new Error('fusion runtime guard provider request limit was not exceeded'); } if (frame.code === 'tool_call_limit') { if (frame.tool_call_count <= FUSION_CHILD_MAX_TOOL_CALLS) { throw new Error('fusion runtime guard tool call limit was not exceeded'); } if (frame.payload_bytes !== 0 || frame.payload_sha256 !== emptyPayloadHash) { throw new Error('fusion runtime guard tool call limit payload evidence mismatch'); } } frames.push(frame); cursor = newline + 1; } if (frames.length > 1) throw new Error('fusion child emitted multiple runtime guard frames'); return frames[0]; } const FUSION_CHILD_SETTLEMENT_FAILURE_REASONS = new Set([ 'no_records', 'final_not_stop', 'invalid_non_final', 'runtime_guard', 'cache_observation', 'output_recovery', ]); export function parseFusionChildSettlement( stderr: Buffer, ): FusionChildSettlementRecord | undefined { const frames: FusionChildSettlementRecord[] = []; let cursor = 0; for (;;) { const frameStart = stderr.indexOf(FUSION_CHILD_SETTLEMENT_PREFIX_BYTES, cursor); if (frameStart < 0) break; const payloadStart = frameStart + FUSION_CHILD_SETTLEMENT_PREFIX_BYTES.length; const newline = stderr.indexOf(10, payloadStart); if (newline < 0) throw new Error('fusion child settlement frame is not newline-terminated'); const bytes = stderr.subarray(payloadStart, newline); const text = bytes.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(bytes)) { throw new Error('fusion child settlement frame is not valid UTF-8'); } const record = assertClosedRecord( parseJsonText(text), [ 'schema_version', 'status', 'record_count', 'records_sha256', 'final_record_index', 'final_text_sha256', 'recovered_error_ordinals', 'recovered_output_cap_ordinals', 'failure_reason', ], 'fusion child settlement', ); if (record['schema_version'] !== FUSION_CHILD_SETTLEMENT_SCHEMA_VERSION) { throw new Error('fusion child settlement schema_version mismatch'); } const status = record['status']; if (status !== 'complete' && status !== 'failed') { throw new Error('fusion child settlement.status is invalid'); } const recordCount = requireUsageInteger(record, 'record_count', 'fusion child settlement'); const finalIndexValue = record['final_record_index']; const finalRecordIndex = finalIndexValue === null ? null : requireUsageInteger(record, 'final_record_index', 'fusion child settlement'); const finalHashValue = record['final_text_sha256']; const finalTextSha256 = finalHashValue === null ? null : requireSha256(record, 'final_text_sha256', 'fusion child settlement'); const recoveredValue = record['recovered_error_ordinals']; if (!Array.isArray(recoveredValue)) { throw new Error('fusion child settlement.recovered_error_ordinals must be an array'); } const recoveredErrorOrdinals = recoveredValue.map((value, index) => { if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) { throw new Error( `fusion child settlement.recovered_error_ordinals[${String(index)}] must be a non-negative safe integer`, ); } return value; }); for (let index = 0; index < recoveredErrorOrdinals.length; index += 1) { const ordinal = recoveredErrorOrdinals[index]; if ( ordinal === undefined || ordinal >= recordCount - 1 || (index > 0 && ordinal <= (recoveredErrorOrdinals[index - 1] ?? -1)) ) { throw new Error('fusion child settlement recovered-error ordinals are not canonical'); } } const recoveredOutputValue = record['recovered_output_cap_ordinals']; if (!Array.isArray(recoveredOutputValue)) { throw new Error('fusion child settlement.recovered_output_cap_ordinals must be an array'); } const recoveredOutputCapOrdinals = recoveredOutputValue.map((value, index) => { if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) { throw new Error( `fusion child settlement.recovered_output_cap_ordinals[${String(index)}] must be a non-negative safe integer`, ); } return value; }); for (let index = 0; index < recoveredOutputCapOrdinals.length; index += 1) { const ordinal = recoveredOutputCapOrdinals[index]; if ( ordinal === undefined || ordinal >= recordCount - 1 || (index > 0 && ordinal <= (recoveredOutputCapOrdinals[index - 1] ?? -1)) ) { throw new Error('fusion child settlement recovered-output ordinals are not canonical'); } } const failureValue = record['failure_reason']; let failureReason: FusionChildSettlementFailureReason | null; if (failureValue === null) failureReason = null; else if ( typeof failureValue === 'string' && FUSION_CHILD_SETTLEMENT_FAILURE_REASONS.has( failureValue as FusionChildSettlementFailureReason, ) ) { failureReason = failureValue as FusionChildSettlementFailureReason; } else { throw new Error('fusion child settlement.failure_reason is invalid'); } if ((status === 'complete') !== (failureReason === null)) { throw new Error('fusion child settlement status/failure_reason mismatch'); } if (recordCount === 0) { if (finalRecordIndex !== null || finalTextSha256 !== null) { throw new Error('fusion child settlement empty stream has final-record evidence'); } } else if (finalRecordIndex !== recordCount - 1 || finalTextSha256 === null) { throw new Error('fusion child settlement final-record evidence mismatch'); } frames.push({ schema_version: FUSION_CHILD_SETTLEMENT_SCHEMA_VERSION, status, record_count: recordCount, records_sha256: requireSha256(record, 'records_sha256', 'fusion child settlement'), final_record_index: finalRecordIndex, final_text_sha256: finalTextSha256, recovered_error_ordinals: recoveredErrorOrdinals, recovered_output_cap_ordinals: recoveredOutputCapOrdinals, failure_reason: failureReason, }); cursor = newline + 1; } if (frames.length > 1) throw new Error('fusion child emitted multiple settlement frames'); return frames[0]; } function assertFusionChildSettlementOrdering(stderr: Buffer): void { const settlementStart = stderr.indexOf(FUSION_CHILD_SETTLEMENT_PREFIX_BYTES); if (settlementStart < 0) return; if (stderr.indexOf(FUSION_CHILD_RESULT_PREFIX_BYTES, settlementStart) >= 0) { throw new Error('fusion child emitted result metadata after terminal settlement'); } } function stripChildControlFrames(stderr: Buffer): Buffer { const prefixes = [FUSION_CHILD_SETTLEMENT_PREFIX_BYTES, FUSION_RUNTIME_GUARD_PREFIX_BYTES]; const diagnostics: Buffer[] = []; let cursor = 0; for (;;) { let next = -1; let prefix: Buffer | undefined; for (const candidate of prefixes) { const found = stderr.indexOf(candidate, cursor); if (found >= 0 && (next < 0 || found < next)) { next = found; prefix = candidate; } } if (next < 0 || prefix === undefined) { if (cursor < stderr.length) diagnostics.push(stderr.subarray(cursor)); break; } if (next > cursor) diagnostics.push(stderr.subarray(cursor, next)); const newline = stderr.indexOf(10, next + prefix.length); if (newline < 0) throw new Error('fusion child control frame is not newline-terminated'); cursor = newline + 1; } return Buffer.concat(diagnostics); } export function assertFusionRuntimeGuardMatchesModel( guard: FusionRuntimeGuardRecord, model: ResolvedFusionModel, ): void { const routeUnknown = guard.provider === 'unknown' && guard.model === 'unknown'; if (!routeUnknown && (guard.provider !== model.provider || guard.model !== model.model)) { throw new Error( `fusion runtime guard route mismatch: expected ${model.qualifiedId}, observed ${guard.provider}/${guard.model}`, ); } if (routeUnknown && guard.code !== 'provider_payload_invalid') { throw new Error('fusion runtime guard omitted the route for a route-bound refusal'); } } export function parseFusionChildStderr(stderr: Buffer): ParsedFusionChildStderr { // Validate every package-owned side frame even though result metadata is parsed // independently below. Malformed refusal/settlement evidence must never degrade // into opaque diagnostics. parseFusionRuntimeGuard(stderr); const settlement = parseFusionChildSettlement(stderr); assertFusionChildSettlementOrdering(stderr); const records: FusionChildResultMetadata[] = []; const diagnostics: Buffer[] = []; let cursor = 0; for (;;) { const frameStart = stderr.indexOf(FUSION_CHILD_RESULT_PREFIX_BYTES, cursor); if (frameStart < 0) { if (cursor < stderr.length) diagnostics.push(stderr.subarray(cursor)); break; } if (frameStart > cursor) diagnostics.push(stderr.subarray(cursor, frameStart)); const payloadStart = frameStart + FUSION_CHILD_RESULT_PREFIX_BYTES.length; const newline = stderr.indexOf(10, payloadStart); if (newline < 0) throw new Error('fusion child metadata frame is not newline-terminated'); const payloadBytes = stderr.subarray(payloadStart, newline); const payloadText = payloadBytes.toString('utf8'); if (!Buffer.from(payloadText, 'utf8').equals(payloadBytes)) throw new Error('fusion child metadata frame is not valid UTF-8'); let parsed: unknown; try { parsed = parseJsonText(payloadText); } catch (error) { throw new Error( `fusion child metadata frame is invalid JSON: ${error instanceof Error ? error.message : String(error)}`, ); } records.push(parseChildResultMetadata(parsed)); cursor = newline + 1; } const resultEvents = serializeFusionChildResultRecords(records); const events = settlement === undefined ? resultEvents : Buffer.concat([resultEvents, Buffer.from(`${JSON.stringify(settlement)}\n`, 'utf8')]); return { records, events, diagnostics: stripChildControlFrames(Buffer.concat(diagnostics)), }; } function parseToolCallLogRecord(value: unknown, label: string): FusionToolCallLogRecord { const record = assertClosedRecordWithOptional( value, [ 'schema_version', 'ordinal', 'tool_name', 'arguments_sha256', 'arguments_bytes', 'result_bytes', 'result_sha256', 'status', 'duration_ms', ], ['url', 'rejected_url_sha256', 'final_url', 'http_status', 'response_bytes', 'content_sha256'], label, ); if (record['schema_version'] !== FUSION_TOOL_CALL_LOG_SCHEMA_VERSION) { throw new Error(`${label}.schema_version mismatch`); } const status = record['status']; if (status !== 'ok' && status !== 'error') throw new Error(`${label}.status is invalid`); const parsedRecord: FusionToolCallLogRecord = { schema_version: FUSION_TOOL_CALL_LOG_SCHEMA_VERSION, ordinal: requireUsageInteger(record, 'ordinal', label), tool_name: requireNonBlankString(record, 'tool_name', label), arguments_sha256: requireSha256(record, 'arguments_sha256', label), arguments_bytes: requireUsageInteger(record, 'arguments_bytes', label), result_bytes: requireUsageInteger(record, 'result_bytes', label), result_sha256: requireSha256(record, 'result_sha256', label), status, duration_ms: requireUsageInteger(record, 'duration_ms', label), }; if (record['url'] !== undefined) parsedRecord.url = requireNonBlankString(record, 'url', label); if (record['rejected_url_sha256'] !== undefined) parsedRecord.rejected_url_sha256 = requireSha256(record, 'rejected_url_sha256', label); if (record['final_url'] !== undefined) parsedRecord.final_url = requireNonBlankString(record, 'final_url', label); if (record['http_status'] !== undefined) parsedRecord.http_status = requireUsageInteger(record, 'http_status', label); if (record['response_bytes'] !== undefined) parsedRecord.response_bytes = requireUsageInteger(record, 'response_bytes', label); if (record['content_sha256'] !== undefined) parsedRecord.content_sha256 = requireSha256(record, 'content_sha256', label); return parsedRecord; } export function parseFusionToolCallLog(bytes: Buffer): FusionToolCallTrace { if (bytes.length === 0) { return { bytes, records: [], summary: { count: 0, total_result_bytes: 0, trace_complete: true }, }; } if (bytes.at(-1) !== 10) { throw new Error('fusion tool-call log has trailing partial line'); } const text = bytes.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(bytes)) { throw new Error('fusion tool-call log is not valid UTF-8'); } const lines = text.split('\n'); lines.pop(); const records = lines.map((line, index) => { let parsed: unknown; try { parsed = parseJsonText(line); } catch (error) { throw new Error( `fusion tool-call log line ${String(index)} is invalid JSON: ${error instanceof Error ? error.message : String(error)}`, ); } return parseToolCallLogRecord(parsed, `fusion tool-call log line ${String(index)}`); }); const seen = new Set(); for (const [index, record] of records.entries()) { if (seen.has(record.ordinal)) { throw new Error(`fusion tool-call log duplicate ordinal ${String(record.ordinal)}`); } seen.add(record.ordinal); if (record.ordinal !== index) { throw new Error( `fusion tool-call log ordinal gap: expected ${String(index)}, observed ${String(record.ordinal)}`, ); } } return { bytes, records, summary: { count: records.length, total_result_bytes: records.reduce((sum, record) => sum + record.result_bytes, 0), trace_complete: true, }, }; } async function assertCompletedToolPolicy( trace: FusionToolCallTrace, capability: FusionCapability, sourcePolicy: { path: string; sha256: string } | undefined, ): Promise { const allowed = capability === 'inspect' ? FUSION_INSPECT_TOOLS : capability === 'research' ? FUSION_RESEARCH_TOOLS : []; const allowedSet = new Set(allowed); const declared = capability === 'research' && sourcePolicy !== undefined ? new Set( (await readFusionSourcePolicyFile(sourcePolicy.path, sourcePolicy.sha256)).sources.map( (source) => source.canonical_url, ), ) : undefined; for (const record of trace.records) { if (!allowedSet.has(record.tool_name)) { throw new Error(`fusion child used non-allowlisted tool ${record.tool_name}`); } if (capability === 'research' && record.tool_name === FUSION_WEB_FETCH_TOOL_NAME) { if (sourcePolicy === undefined || declared === undefined) throw new Error('fusion research source policy missing during audit'); if (record.status === 'ok') { if (record.url === undefined) throw new Error('fusion research fetch audit is missing url'); const canonicalUrl = canonicalizeFusionPublicUrl(record.url); if (record.url !== canonicalUrl) throw new Error('fusion research fetch audit URL was not canonical'); if (!declared.has(canonicalUrl)) throw new Error('fusion research fetch audit URL was not declared'); if (record.rejected_url_sha256 !== undefined) { throw new Error( 'fusion research successful fetch audit must not include rejected_url_sha256', ); } if (record.final_url === undefined) throw new Error('fusion research fetch audit is missing final_url'); if (record.http_status === undefined) throw new Error('fusion research fetch audit is missing http_status'); if (record.response_bytes === undefined) throw new Error('fusion research fetch audit is missing response_bytes'); if (record.content_sha256 === undefined) throw new Error('fusion research fetch audit is missing content_sha256'); } else { if (record.url !== undefined || record.final_url !== undefined) { throw new Error('fusion research rejected fetch audit must not persist raw URL'); } if (record.rejected_url_sha256 === undefined) { throw new Error('fusion research rejected fetch audit is missing rejected_url_sha256'); } } } } } function isNotFound(error: unknown): boolean { return isJsonObject(error) && error['code'] === 'ENOENT'; } async function readFusionToolCallLog(path: string): Promise { let handle: Awaited>; try { handle = await open(path, constants.O_RDONLY | FUSION_PI_CHILD_O_NOFOLLOW); } catch (error) { // The child extension creates this file before tools can run, so a missing file // means the audit trail was never established - not that zero tools were used. Those // must stay distinguishable: silently accepting absence would let a run whose activity // was never recorded report success, defeating the purpose of the log. if (isNotFound(error)) { throw new Error( `fusion tool-call log is missing at ${path}; the inspect child never initialized its audit trail`, ); } if (isJsonObject(error) && error['code'] === 'ELOOP') { throw new Error( `fusion tool-call log at ${path} is a symlink; refusing to trust a redirected audit trail`, ); } throw error; } try { // The audit trail must be a real file inside the run directory. A symlink here would let // anything able to pre-create the path redirect the parent's read elsewhere, so the file // is opened with O_NOFOLLOW and then fstat-checked before its bytes are trusted. const stats = await handle.stat(); if (!stats.isFile()) { throw new Error( `fusion tool-call log at ${path} is not a regular file; refusing to trust a redirected audit trail`, ); } return parseFusionToolCallLog(await handle.readFile()); } finally { await handle.close(); } } async function assertFusionToolCallLogSeal( path: string, trace: FusionToolCallTrace, ): Promise { const sealPath = `${path}${FUSION_TOOL_CALL_SEAL_SUFFIX}`; let handle: Awaited>; try { handle = await open(sealPath, constants.O_RDONLY | FUSION_PI_CHILD_O_NOFOLLOW); } catch (error) { if (isNotFound(error)) throw new Error('fusion tool-call audit completion seal is missing'); if (isJsonObject(error) && error['code'] === 'ELOOP') { throw new Error('fusion tool-call audit completion seal is a symlink'); } throw error; } try { const stats = await handle.stat(); if (!stats.isFile()) throw new Error('fusion tool-call audit completion seal is not a regular file'); if (stats.size > 4096) throw new Error('fusion tool-call audit completion seal is oversized'); const bytes = await handle.readFile(); if (bytes.at(-1) !== 10) throw new Error('fusion tool-call audit completion seal is partial'); const text = bytes.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(bytes)) { throw new Error('fusion tool-call audit completion seal is not UTF-8'); } const parsed = parseJsonText(text); if (!isJsonObject(parsed) || Array.isArray(parsed)) { throw new Error('fusion tool-call audit completion seal must be an object'); } const keys = Object.keys(parsed).sort(); const expected = [ 'log_sha256', 'record_count', 'schema_version', 'status', 'total_result_bytes', ]; if (keys.join('\0') !== expected.join('\0')) { throw new Error('fusion tool-call audit completion seal keys mismatch'); } if (parsed['schema_version'] !== FUSION_TOOL_CALL_SEAL_SCHEMA_VERSION) { throw new Error('fusion tool-call audit completion seal schema mismatch'); } if (parsed['status'] !== 'complete') { throw new Error('fusion tool-call audit completion seal reports a failed audit'); } const recordCount = requireUsageInteger(parsed, 'record_count', 'fusion tool-call audit seal'); const totalResultBytes = requireUsageInteger( parsed, 'total_result_bytes', 'fusion tool-call audit seal', ); const logSha256 = requireSha256(parsed, 'log_sha256', 'fusion tool-call audit seal'); if (recordCount !== trace.summary.count) { throw new Error('fusion tool-call audit completion seal record count mismatch'); } if (recordCount > FUSION_CHILD_MAX_TOOL_CALLS) { throw new Error( `fusion tool-call audit exceeds tool-call limit ${String(FUSION_CHILD_MAX_TOOL_CALLS)}`, ); } if (totalResultBytes !== trace.summary.total_result_bytes) { throw new Error('fusion tool-call audit completion seal result-byte total mismatch'); } if (logSha256 !== sha256Buffer(trace.bytes)) { throw new Error('fusion tool-call audit completion seal log hash mismatch'); } if (totalResultBytes > FUSION_CHILD_MAX_TOTAL_TOOL_RESULT_BYTES) { throw new Error( `fusion tool-call audit exceeds aggregate result-byte limit ${String(FUSION_CHILD_MAX_TOTAL_TOOL_RESULT_BYTES)}`, ); } } finally { await handle.close(); } } function sha256Buffer(bytes: Buffer): string { return createHash('sha256').update(bytes).digest('hex'); } function reconstructFinalText(response: Buffer, record: FusionChildResultMetadata): string { const blocks: Buffer[] = []; let cursor = 0; for (const [index, block] of record.text_blocks.entries()) { const end = cursor + block.utf8_bytes; if (end > response.length) throw new Error(`Pi final text block ${String(index)} is shorter than its metadata length`); const bytes = response.subarray(cursor, end); if (sha256Buffer(bytes) !== block.sha256) throw new Error(`Pi final text block ${String(index)} hash mismatch`); blocks.push(bytes); if (response.at(end) !== 10) throw new Error(`Pi final text block ${String(index)} lacks its print-mode newline`); cursor = end + 1; } if (cursor !== response.length) throw new Error('Pi final text stdout contains bytes outside declared text blocks'); const joined = Buffer.concat(blocks); if (sha256Buffer(joined) !== record.text_sha256) throw new Error('Pi final text aggregate hash mismatch'); const text = joined.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(joined)) throw new Error('Pi final text is not valid UTF-8'); if (fusionJsonRenderedTextBytes(text) !== record.output_contract.json_rendered_bytes) { throw new Error('Pi final text JSON-rendered byte count mismatch'); } if (text.trim().length === 0) throw new Error('Pi assistant response is empty'); return text; } type ParsedCandidateOutputRecovery = Omit; async function readCandidateOutputRecovery( path: string, evidence: ParsedCandidateOutputRecovery, ): Promise { let handle: Awaited>; try { handle = await open(path, constants.O_RDONLY | FUSION_PI_CHILD_O_NOFOLLOW); } catch (error) { if (isNotFound(error)) throw new Error('fusion oversized candidate response artifact is missing'); if (isJsonObject(error) && error['code'] === 'ELOOP') { throw new Error('fusion oversized candidate response artifact is a symlink'); } throw error; } try { const stats = await handle.stat(); if (!stats.isFile()) { throw new Error('fusion oversized candidate response artifact is not a regular file'); } if (stats.size > FUSION_CHILD_STDOUT_LIMIT_BYTES) { throw new Error( `fusion oversized candidate response artifact exceeds ${String(FUSION_CHILD_STDOUT_LIMIT_BYTES)} bytes`, ); } const bytes = await handle.readFile(); if (sha256Buffer(bytes) !== evidence.original_text_sha256) { throw new Error('fusion oversized candidate response artifact hash mismatch'); } const text = bytes.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(bytes)) { throw new Error('fusion oversized candidate response artifact is not valid UTF-8'); } if (fusionJsonRenderedTextBytes(text) !== evidence.original_json_rendered_bytes) { throw new Error('fusion oversized candidate response JSON-rendered byte count mismatch'); } if (evidence.original_json_rendered_bytes <= evidence.limit_bytes) { throw new Error('fusion output recovery original did not exceed the candidate contract'); } return { ...evidence, original_text: text }; } finally { await handle.close(); } } function parsedCandidateOutputRecovery( records: readonly FusionChildResultMetadata[], status: FusionCandidateOutputRecovery['status'], ): ParsedCandidateOutputRecovery | undefined { const originalRecordIndex = records.findIndex( (record) => record.output_contract.recovery_role === 'oversized_original', ); if (originalRecordIndex < 0) return undefined; const original = records[originalRecordIndex]; if (original === undefined) throw new Error('Pi output recovery original record disappeared'); const replacementRecordIndex = records.findIndex( (record) => record.output_contract.recovery_role === 'replacement', ); const replacement = replacementRecordIndex < 0 ? undefined : records[replacementRecordIndex]; return { kind: 'same_session_compression', limit_bytes: FUSION_CANDIDATE_MAX_OUTPUT_BYTES, original_record_index: originalRecordIndex, replacement_record_index: replacementRecordIndex < 0 ? null : replacementRecordIndex, original_json_rendered_bytes: original.output_contract.json_rendered_bytes, replacement_json_rendered_bytes: replacement?.output_contract.json_rendered_bytes ?? null, original_text_sha256: original.text_sha256, status, }; } export class FusionPiCompactResultParser { private readonly expectedProvider: string; private readonly expectedModel: string; constructor(expectedProvider: string, expectedModel: string) { this.expectedProvider = expectedProvider; this.expectedModel = expectedModel; } snapshot(stderr: Buffer): ObservedChildSnapshot { try { const parsed = parseFusionChildStderr(stderr); return this.observedFromRecords(parsed.records); } catch { return { usage: createEmptyFusionUsage() }; } } finishOutputRecoveryFailure( response: Buffer, stderr: Buffer, ): { usage: FusionUsage; provider: string; model: string; qualifiedId: string; events: Buffer; diagnostics: Buffer; outputRecovery: ParsedCandidateOutputRecovery; } { const parsed = parseFusionChildStderr(stderr); const settlement = parseFusionChildSettlement(stderr); if (settlement === undefined) throw new Error('Pi child emitted no terminal result settlement'); const expectedSettlement = buildFusionChildSettlement(parsed.records); if (JSON.stringify(settlement) !== JSON.stringify(expectedSettlement)) { throw new Error('Pi child terminal result settlement does not match the metadata stream'); } if (settlement.status !== 'failed' || settlement.failure_reason !== 'output_recovery') { throw new Error('Pi child did not report a failed candidate output recovery'); } if (parsed.diagnostics.includes(PI_EXTENSION_ERROR_PREFIX_BYTES)) { throw new Error('Pi child reported an extension error diagnostic'); } const final = parsed.records.at(-1); if (final === undefined) throw new Error('Pi child emitted no compact result metadata'); for (const record of parsed.records) this.assertModel(record); this.assertCacheObservationOrdinals(parsed.records); this.assertTranscriptStopReasons(parsed.records); reconstructFinalText(response, final); const outputRecovery = parsedCandidateOutputRecovery(parsed.records, 'failed'); if (outputRecovery === undefined) { throw new Error('Pi child output-recovery failure omitted the oversized original'); } const observed = this.observedFromRecords(parsed.records); return { usage: observed.usage, provider: final.provider, model: final.model, qualifiedId: `${final.provider}/${final.model}`, events: parsed.events, diagnostics: parsed.diagnostics, outputRecovery, }; } finish( response: Buffer, stderr: Buffer, ): { text: string; usage: FusionUsage; firstRequestUsage: FusionUsage; providerRequestCount: number; provider: string; model: string; qualifiedId: string; events: Buffer; diagnostics: Buffer; outputRecovery: ParsedCandidateOutputRecovery | undefined; } { const parsed = parseFusionChildStderr(stderr); const runtimeGuard = parseFusionRuntimeGuard(stderr); if (runtimeGuard !== undefined) { throw new Error(`Pi child runtime guard refused the run: ${runtimeGuard.message}`); } const settlement = parseFusionChildSettlement(stderr); if (settlement === undefined) throw new Error('Pi child emitted no terminal result settlement'); const expectedSettlement = buildFusionChildSettlement(parsed.records); if (JSON.stringify(settlement) !== JSON.stringify(expectedSettlement)) { throw new Error('Pi child terminal result settlement does not match the metadata stream'); } if (parsed.diagnostics.includes(PI_EXTENSION_ERROR_PREFIX_BYTES)) { throw new Error('Pi child reported an extension error diagnostic'); } const final = parsed.records.at(-1); if (final === undefined) throw new Error('Pi child emitted no compact result metadata'); for (const record of parsed.records) this.assertModel(record); this.assertCacheObservationOrdinals(parsed.records); this.assertTranscriptStopReasons(parsed.records); if (settlement.status !== 'complete') { throw new Error( `Pi child terminal result settlement failed: ${settlement.failure_reason ?? 'unknown'}`, ); } const observed = this.observedFromRecords(parsed.records); const text = reconstructFinalText(response, final); return { text, usage: observed.usage, firstRequestUsage: cloneFusionUsage(parsed.records[0]?.usage ?? createEmptyFusionUsage()), providerRequestCount: parsed.records.length, provider: final.provider, model: final.model, qualifiedId: `${final.provider}/${final.model}`, events: parsed.events, diagnostics: parsed.diagnostics, outputRecovery: parsedCandidateOutputRecovery(parsed.records, 'completed'), }; } private assertModel(record: FusionChildResultMetadata): void { if (record.provider !== this.expectedProvider || record.model !== this.expectedModel) { throw new Error( `Pi assistant model mismatch: expected ${this.expectedProvider}/${this.expectedModel}, observed ${record.provider}/${record.model}`, ); } } private assertCacheObservationOrdinals(records: readonly FusionChildResultMetadata[]): void { let priorOrdinal = 0; for (const [index, record] of records.entries()) { const ordinal = record.cache_observation.request_ordinal; if (ordinal <= priorOrdinal) { throw new Error( `Pi child cache observation ordinal is not increasing at result ${String(index)}: previous ${String(priorOrdinal)}, observed ${String(ordinal)}`, ); } priorOrdinal = ordinal; } } private assertTranscriptStopReasons(records: readonly FusionChildResultMetadata[]): void { for (const [index, record] of records.entries()) { const isFinal = index === records.length - 1; if (isFinal) { if (record.stop_reason !== 'stop') { throw new Error(this.stopReasonError('final', 'stop', record.stop_reason, true)); } } else if ( record.stop_reason !== 'toolUse' && !isRecoverableFusionChildErrorRecord(record) && record.output_contract.recovery_role !== 'oversized_original' ) { throw new Error( this.stopReasonError( `non-final record ${index}`, 'toolUse, a settled zero-usage retry marker, or one oversized original', record.stop_reason, true, ), ); } } } private stopReasonError( position: string, expected: string, observed: string, includeStopDetail: boolean, ): string { const prefix = `Pi ${position} stop reason is not ${expected}: ${observed}`; if (!includeStopDetail) return prefix; switch (observed) { case 'length': return `${prefix} (model output was truncated)`; case 'error': return `${prefix} (Pi reported an error stop)`; case 'aborted': return `${prefix} (Pi reported an aborted stop)`; case 'pending': return `${prefix} (Pi reported a pending stop)`; default: return prefix; } } private observedFromRecords( records: readonly FusionChildResultMetadata[], ): ObservedChildSnapshot { const usage = createEmptyFusionUsage(); for (const record of records) addFusionUsage(usage, record.usage); const final = records.at(-1); if (final === undefined) return { usage }; return { usage, provider: final.provider, model: final.model, qualifiedId: `${final.provider}/${final.model}`, }; } } function appendCapped( chunks: Buffer[], currentBytes: number, chunk: Buffer, limit: number, ): { bytes: number; accepted: Buffer; exceeded: boolean } { if (currentBytes >= limit) return { bytes: currentBytes, accepted: Buffer.alloc(0), exceeded: true }; const remaining = limit - currentBytes; if (chunk.length <= remaining) { chunks.push(chunk); return { bytes: currentBytes + chunk.length, accepted: chunk, exceeded: false }; } const accepted = chunk.subarray(0, remaining); if (accepted.length > 0) chunks.push(accepted); return { bytes: limit, accepted, exceeded: true }; } function codeOf(error: unknown): string | undefined { return isJsonObject(error) && typeof error['code'] === 'string' ? error['code'] : undefined; } function isTransientSpawnCode(code: string | undefined): boolean { return code === 'EAGAIN' || code === 'EMFILE' || code === 'ENFILE'; } function childError( message: string, code: FusionError['code'], input: Pick, transient = false, childCreated = true, ): FusionError { const details: FusionErrorDetails = { code, stage: input.stage, attempt: input.attempt, transient, childCreated, }; if (input.slot !== undefined) details.slot = input.slot; return new FusionError(message, details); } function withCleanupErrors(error: FusionError, cleanupErrors: readonly string[]): FusionError { if (cleanupErrors.length === 0) return error; const details: FusionErrorDetails = { code: error.code, transient: error.transient, childCreated: error.childCreated, }; if (error.stage !== undefined) details.stage = error.stage; if (error.slot !== undefined) details.slot = error.slot; if (error.attempt !== undefined) details.attempt = error.attempt; if (error.artifactDir !== undefined) details.artifactDir = error.artifactDir; return new FusionError( `${error.message}; process cleanup issues: ${cleanupErrors.join('; ')}`, details, ); } function defaultSpawn(command: string, args: string[], options: SpawnOptions): FusionChildProcess { return nodeSpawn(command, args, options); } /** * Termination timers must keep the event loop alive. * * The SIGTERM grace, SIGKILL wait, overall timeout, and idle timeout timers * are the only things that settle the run promise when a child stops emitting events. An * unref'd timer lets the loop drain first, leaving the promise pending forever * ("Promise resolution is still pending but the event loop has already * resolved"). Every timer stored here is cleared in the `finally` of * `runPiChild` via `cleanupTimers`, so keeping them referenced cannot leak. */ function trackTimer(timer: NodeJS.Timeout): NodeJS.Timeout { return timer; } function rememberCleanupErrors( state: ProcessState, signal: NodeJS.Signals, errors: readonly string[], ): void { for (const error of errors) state.cleanupErrors.push(`${signal}: ${error}`); } function terminateChild( child: FusionChildProcess, state: ProcessState, platform: NodeJS.Platform, killProcess: FusionKillProcess, killGraceMs: number, sigkillWaitMs: number, settleSyntheticClose: (close: CloseRecord) => void, ): void { if (state.settled || state.terminationStarted) return; state.terminationStarted = true; const termResult = sendSignal(child, platform, killProcess, 'SIGTERM'); rememberCleanupErrors(state, 'SIGTERM', termResult.errors); if (!termResult.sent && state.primaryError === undefined) { state.primaryError = new FusionError( `Pi child SIGTERM failed: ${termResult.errors.join('; ')}`, { code: 'child_exit_failed', childCreated: true, }, ); } state.termTimer = trackTimer( setTimeout(() => { if (state.settled) return; const killResult = sendSignal(child, platform, killProcess, 'SIGKILL'); rememberCleanupErrors(state, 'SIGKILL', killResult.errors); if (!killResult.sent && state.primaryError === undefined) { state.primaryError = new FusionError( `Pi child SIGKILL failed: ${killResult.errors.join('; ')}`, { code: 'child_exit_failed', childCreated: true, }, ); } }, killGraceMs), ); state.waitTimer = trackTimer( setTimeout(() => { if (state.settled) return; const message = 'Pi child did not emit close after SIGKILL wait'; state.cleanupErrors.push(message); if (state.primaryError === undefined) { state.primaryError = new FusionError(message, { code: 'child_exit_failed', childCreated: true, }); } settleSyntheticClose({ code: null, signal: 'SIGKILL' }); }, killGraceMs + sigkillWaitMs), ); } function sendSignal( child: FusionChildProcess, platform: NodeJS.Platform, killProcess: FusionKillProcess, signal: NodeJS.Signals, ): { sent: boolean; errors: readonly string[] } { const errors: string[] = []; const pid = child.pid; if (platform !== 'win32' && pid !== undefined) { try { if (killProcess(-pid, signal)) return { sent: true, errors }; errors.push('process group kill returned false'); } catch (error) { errors.push( `process group kill failed: ${error instanceof Error ? error.message : String(error)}`, ); } } try { if (child.kill(signal)) return { sent: true, errors }; errors.push('child kill returned false'); } catch (error) { errors.push(`child kill failed: ${error instanceof Error ? error.message : String(error)}`); } return { sent: false, errors }; } function cleanupTimers(state: ProcessState): void { if (state.termTimer !== undefined) clearTimeout(state.termTimer); if (state.waitTimer !== undefined) clearTimeout(state.waitTimer); if (state.timeoutTimer !== undefined) clearTimeout(state.timeoutTimer); if (state.idleTimer !== undefined) clearTimeout(state.idleTimer); state.termTimer = undefined; state.waitTimer = undefined; state.timeoutTimer = undefined; state.idleTimer = undefined; } async function writePromptToStdin(child: FusionChildProcess, prompt: string): Promise { const stdin = child.stdin; if (stdin === undefined || stdin === null) throw new Error('Pi child stdin pipe is unavailable'); await new Promise((resolve, reject) => { let settled = false; const fail = (error: Error) => { if (settled) return; settled = true; stdin.off('error', fail); reject(error); }; const finish = () => { if (settled) return; settled = true; stdin.off('error', fail); resolve(); }; stdin.once('error', fail); stdin.write(Buffer.from(prompt, 'utf8'), (error?: Error | null) => { if (error !== undefined && error !== null) { fail(error); return; } stdin.end(finish); }); }); } export async function runPiChild(options: RunPiChildOptions): Promise { if (options.signal?.aborted) { throw childError( 'Pi child launch cancelled before spawn', 'child_cancelled', options, false, false, ); } const spawnImpl = options.spawn ?? defaultSpawn; const killProcess = options.killProcess ?? process.kill.bind(process); const platform = options.platform ?? process.platform; const capability = options.capability ?? FUSION_NO_TOOLS_CAPABILITY; const env = fusionPiChildEnv(options.env ?? process.env, options.model.provider); if (options.candidateOutputRecoveryPath !== undefined) { if (options.stage !== 'candidate') { throw childError( 'candidate output recovery may be enabled only for candidate children', 'orchestration_failed', options, false, false, ); } env[FUSION_CANDIDATE_OUTPUT_RECOVERY_PATH_ENV] = options.candidateOutputRecoveryPath; } if (capability !== 'reason') { if (options.toolCallLogPath === undefined) { throw childError( `fusion ${capability} child requires a tool-call log path`, 'orchestration_failed', options, false, false, ); } env[FUSION_TOOL_CALL_LOG_PATH_ENV] = options.toolCallLogPath; if (capability === 'research') { if (options.sourcePolicy === undefined) { throw childError( 'fusion research child requires a source-policy path and hash', 'orchestration_failed', options, false, false, ); } env[FUSION_RESEARCH_ENABLED_ENV] = '1'; env[FUSION_SOURCE_POLICY_PATH_ENV] = options.sourcePolicy.path; env[FUSION_SOURCE_POLICY_SHA256_ENV] = options.sourcePolicy.sha256; } } const stdoutLimit = options.stdoutLimitBytes ?? FUSION_CHILD_STDOUT_LIMIT_BYTES; const stderrLimit = options.stderrLimitBytes ?? FUSION_CHILD_STDERR_LIMIT_BYTES; const timeoutMs = options.timeoutMs ?? FUSION_CHILD_TIMEOUT_MS; const idleTimeoutMs = options.idleTimeoutMs ?? FUSION_CHILD_IDLE_TIMEOUT_MS; const killGraceMs = options.killGraceMs ?? FUSION_CHILD_KILL_GRACE_MS; const sigkillWaitMs = options.sigkillWaitMs ?? FUSION_CHILD_SIGKILL_WAIT_MS; const argv = buildFusionPiChildArgv( options.model, options.systemPrompt, options.childExtensionPath ?? resolveFusionChildExtensionPath(), capability, ); const parser = new FusionPiCompactResultParser(options.model.provider, options.model.model); const stdoutChunks: Buffer[] = []; const stderrChunks: Buffer[] = []; let stdoutBytes = 0; let stderrBytes = 0; const state: ProcessState = { primaryError: undefined, cleanupErrors: [], terminationStarted: false, termTimer: undefined, waitTimer: undefined, timeoutTimer: undefined, idleTimer: undefined, settled: false, }; let child: FusionChildProcess; try { const launchDeps = options.piLaunchDependencies === undefined ? { platform } : { ...options.piLaunchDependencies, platform }; const launch = resolvePiLaunch(launchDeps); assertWindowsCommandLineWithinLimit(launch, argv, platform, `fusion-${options.stage}`); child = spawnImpl(launch.executable, piLaunchArgv(launch, argv), { cwd: options.cwd, detached: platform !== 'win32', shell: false, stdio: ['pipe', 'pipe', 'pipe'], env, windowsHide: true, }); } catch (error) { const code = codeOf(error); throw childError( `Pi child spawn failed: ${error instanceof Error ? error.message : String(error)}`, 'child_spawn_failed', options, isTransientSpawnCode(code), false, ); } let settleClose: (close: CloseRecord) => void = () => undefined; const closePromise = new Promise((resolve) => { settleClose = (close) => { if (state.settled) return; state.settled = true; resolve(close); }; }); const resetIdleTimer = () => { if (state.settled) return; if (state.idleTimer !== undefined) clearTimeout(state.idleTimer); state.idleTimer = trackTimer( setTimeout(() => { if (state.settled) return; if (state.primaryError === undefined) { state.primaryError = childError( `Pi child produced no output for ${String(idleTimeoutMs)}ms (stalled)`, 'child_timeout', options, ); } terminateChild( child, state, platform, killProcess, killGraceMs, sigkillWaitMs, settleClose, ); }, idleTimeoutMs), ); }; const abortListener = () => { if (state.settled) return; if (state.primaryError === undefined) { state.primaryError = childError('Pi child cancelled', 'child_cancelled', options); } terminateChild(child, state, platform, killProcess, killGraceMs, sigkillWaitMs, settleClose); }; const stdoutListener = (data: Buffer | string) => { resetIdleTimer(); const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf8'); const appended = appendCapped(stdoutChunks, stdoutBytes, chunk, stdoutLimit); stdoutBytes = appended.bytes; if (appended.exceeded && state.primaryError === undefined) { state.primaryError = childError( `Pi child final response exceeded ${String(stdoutLimit)} bytes`, 'child_output_cap', options, ); terminateChild(child, state, platform, killProcess, killGraceMs, sigkillWaitMs, settleClose); } }; const stderrListener = (data: Buffer | string) => { resetIdleTimer(); const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf8'); const appended = appendCapped(stderrChunks, stderrBytes, chunk, stderrLimit); stderrBytes = appended.bytes; if (appended.exceeded && state.primaryError === undefined) { state.primaryError = childError( `Pi child stderr exceeded ${String(stderrLimit)} bytes`, 'child_output_cap', options, ); terminateChild(child, state, platform, killProcess, killGraceMs, sigkillWaitMs, settleClose); } }; const errorListener = (error: Error) => { if (state.settled) return; if (state.primaryError === undefined) { const code = codeOf(error); const childCreated = child.pid !== undefined; state.primaryError = childError( `Pi child process error: ${error.message}`, 'child_spawn_failed', options, isTransientSpawnCode(code), childCreated, ); } if (child.pid === undefined) settleClose({ code: null, signal: null }); }; const closeListener = (code: number | null, signal: NodeJS.Signals | null) => { settleClose({ code, signal }); }; child.stdout?.on('data', stdoutListener); child.stderr?.on('data', stderrListener); child.once('error', errorListener); child.once('close', closeListener); options.signal?.addEventListener('abort', abortListener, { once: true }); if (options.signal?.aborted) abortListener(); resetIdleTimer(); state.timeoutTimer = trackTimer( setTimeout(() => { if (state.primaryError === undefined) { state.primaryError = childError( `Pi child timed out after ${String(timeoutMs)}ms`, 'child_timeout', options, ); } terminateChild(child, state, platform, killProcess, killGraceMs, sigkillWaitMs, settleClose); }, timeoutMs), ); try { try { if (state.primaryError === undefined) await writePromptToStdin(child, options.userPrompt); } catch (error) { if (state.primaryError === undefined) { state.primaryError = childError( `Pi child stdin write failed: ${error instanceof Error ? error.message : String(error)}`, 'child_stdin_failed', options, ); } terminateChild(child, state, platform, killProcess, killGraceMs, sigkillWaitMs, settleClose); } const close = await closePromise; const response = Buffer.concat(stdoutChunks); const rawStderr = Buffer.concat(stderrChunks); const observed = parser.snapshot(rawStderr); let compactEvents: Buffer = Buffer.alloc(0); let diagnostics: Buffer = rawStderr; try { const decoded = parseFusionChildStderr(rawStderr); compactEvents = decoded.events; diagnostics = decoded.diagnostics; } catch { // A primary process/cap error remains authoritative; malformed metadata is // surfaced below when the child otherwise exits successfully. } const readPartialOutputRecovery = async (): Promise< FusionCandidateOutputRecovery | undefined > => { let parsedRecords: readonly FusionChildResultMetadata[]; try { parsedRecords = parseFusionChildStderr(rawStderr).records; } catch { return undefined; } const evidence = parsedCandidateOutputRecovery(parsedRecords, 'failed'); if (evidence === undefined) return undefined; if (options.candidateOutputRecoveryPath === undefined) { throw new Error('fusion child emitted output-recovery evidence without an artifact path'); } return readCandidateOutputRecovery(options.candidateOutputRecoveryPath, evidence); }; const primary = state.primaryError; if (primary !== undefined) { let outputRecovery: FusionCandidateOutputRecovery | undefined; try { outputRecovery = await readPartialOutputRecovery(); } catch (error) { state.cleanupErrors.push( `output recovery evidence invalid: ${error instanceof Error ? error.message : String(error)}`, ); } throw new FusionChildRunError( withCleanupErrors(primary, state.cleanupErrors), compactEvents, response, diagnostics, close, observed, outputRecovery, ); } let terminalSettlement: FusionChildSettlementRecord | undefined; try { terminalSettlement = parseFusionChildSettlement(rawStderr); } catch (error) { throw new FusionChildRunError( withCleanupErrors( childError( `Pi child terminal settlement invalid: ${error instanceof Error ? error.message : String(error)}`, 'child_event_invalid', options, ), state.cleanupErrors, ), compactEvents, response, diagnostics, close, observed, ); } if (terminalSettlement?.failure_reason === 'output_recovery') { try { const failure = parser.finishOutputRecoveryFailure(response, rawStderr); if (options.candidateOutputRecoveryPath === undefined) { throw new Error( 'fusion child output-recovery failure omitted the configured artifact path', ); } const outputRecovery = await readCandidateOutputRecovery( options.candidateOutputRecoveryPath, failure.outputRecovery, ); const recoveryMessage = outputRecovery.replacement_json_rendered_bytes === null ? `Pi child could not complete its one allowed same-session candidate compression continuation; the ${String(outputRecovery.original_json_rendered_bytes)}-byte original is preserved and nothing was truncated` : `Pi child compressed candidate response is still ${String(outputRecovery.replacement_json_rendered_bytes)} JSON-rendered bytes, exceeding the ${String(outputRecovery.limit_bytes)}-byte output contract; both responses are preserved and nothing was truncated`; throw new FusionChildRunError( withCleanupErrors( childError(recoveryMessage, 'child_output_cap', options), state.cleanupErrors, ), failure.events, response, failure.diagnostics, close, { usage: failure.usage, provider: failure.provider, model: failure.model, qualifiedId: failure.qualifiedId, }, outputRecovery, ); } catch (error) { if (error instanceof FusionChildRunError) throw error; throw new FusionChildRunError( withCleanupErrors( childError( `Pi child output-recovery evidence invalid: ${error instanceof Error ? error.message : String(error)}`, 'child_event_invalid', options, ), state.cleanupErrors, ), compactEvents, response, diagnostics, close, observed, ); } } if (close.code !== 0 || close.signal !== null) { let runtimeGuard: FusionRuntimeGuardRecord | undefined; try { runtimeGuard = parseFusionRuntimeGuard(rawStderr); if (runtimeGuard !== undefined) { assertFusionRuntimeGuardMatchesModel(runtimeGuard, options.model); } } catch (error) { throw new FusionChildRunError( withCleanupErrors( childError( `Pi child runtime guard evidence invalid: ${error instanceof Error ? error.message : String(error)}`, 'child_event_invalid', options, ), state.cleanupErrors, ), compactEvents, response, diagnostics, close, observed, ); } throw new FusionChildRunError( withCleanupErrors( childError( runtimeGuard?.message ?? `Pi child exited with code ${close.code === null ? 'null' : String(close.code)}${close.signal === null ? '' : ` (${close.signal})`}`, runtimeGuard === undefined ? 'child_exit_failed' : runtimeGuard.code === 'claude_cache_policy' ? 'child_cache_policy_invalid' : runtimeGuard.code === 'provider_payload_invalid' ? 'child_runtime_payload_invalid' : 'child_runtime_limit_exceeded', options, ), state.cleanupErrors, ), compactEvents, response, diagnostics, close, observed, ); } let parsed: ReturnType; try { parsed = parser.finish(response, rawStderr); } catch (error) { throw new FusionChildRunError( withCleanupErrors( childError( `Pi child compact result invalid: ${error instanceof Error ? error.message : String(error)}`, 'child_event_invalid', options, ), state.cleanupErrors, ), compactEvents, response, diagnostics, close, observed, ); } let outputRecovery: FusionCandidateOutputRecovery | undefined; if (parsed.outputRecovery !== undefined) { if (options.candidateOutputRecoveryPath === undefined) { throw new FusionChildRunError( childError( 'Pi child output-recovery evidence has no configured artifact path', 'child_event_invalid', options, ), parsed.events, response, parsed.diagnostics, close, observed, ); } try { outputRecovery = await readCandidateOutputRecovery( options.candidateOutputRecoveryPath, parsed.outputRecovery, ); } catch (error) { throw new FusionChildRunError( childError( `Pi child output-recovery artifact invalid: ${error instanceof Error ? error.message : String(error)}`, 'child_event_invalid', options, ), parsed.events, response, parsed.diagnostics, close, observed, ); } } let toolCallTrace: FusionToolCallTrace | undefined; if (capability !== 'reason') { // The launch path above refuses to spawn a tool-enabled child without a log path, so // this is unreachable. Assert rather than defaulting: a `?? ''` here would silently // read an empty path if that guard were ever refactored away, turning a missing // audit trail into a successful run. const logPath = options.toolCallLogPath; if (logPath === undefined) { throw childError( `fusion ${capability} child completed without a tool-call log path`, 'orchestration_failed', options, ); } try { toolCallTrace = await readFusionToolCallLog(logPath); await assertFusionToolCallLogSeal(logPath, toolCallTrace); await assertCompletedToolPolicy(toolCallTrace, capability, options.sourcePolicy); } catch (error) { throw new FusionChildRunError( withCleanupErrors( childError( `Pi child tool-call log invalid: ${error instanceof Error ? error.message : String(error)}`, 'child_event_invalid', options, ), state.cleanupErrors, ), compactEvents, response, diagnostics, close, observed, outputRecovery, ); } } const result: FusionChildRunResult = { stage: options.stage, attempt: options.attempt, provider: parsed.provider, model: parsed.model, qualifiedId: parsed.qualifiedId, text: parsed.text, usage: parsed.usage, firstRequestUsage: parsed.firstRequestUsage, providerRequestCount: parsed.providerRequestCount, events: parsed.events, stderr: parsed.diagnostics, exitCode: close.code, signal: close.signal, }; if (options.slot !== undefined) result.slot = options.slot; if (outputRecovery !== undefined) result.outputRecovery = outputRecovery; if (toolCallTrace !== undefined) result.toolCallTrace = toolCallTrace; return result; } finally { cleanupTimers(state); options.signal?.removeEventListener('abort', abortListener); child.stdout?.off('data', stdoutListener); child.stderr?.off('data', stderrListener); child.off('error', errorListener); child.off('close', closeListener); } }