import { accessSync, constants, existsSync } from 'node:fs'; import { delimiter, join } from 'node:path'; import { TorchError } from '../kernel/errors.mjs'; import { profileCapabilityIssues, unsupportedCapability, validateRuntimeAdapter } from './runtime.mjs'; function findExecutable(name, env = process.env) { for (const directory of (env.PATH ?? '').split(delimiter).filter(Boolean)) { const candidate = join(directory, name); try { accessSync(candidate, constants.X_OK); return candidate; } catch { /* keep searching */ } } return null; } function text(value, name) { if (typeof value !== 'string' || !value.trim()) { throw new TorchError(`${name} must be a non-empty string`, { code: 'INVALID_RUNTIME_INPUT', details: { field: name }, }); } return value.trim(); } function optionalModelArgs(model) { return typeof model === 'string' && model.trim() ? ['--model', model.trim()] : []; } function commandRecord(command, args, cwd) { return { command, args, cwd, mutatesRuntime: true }; } function mcpArgs(mcp) { if (!mcp) return []; if (typeof mcp.name !== 'string' || !/^[a-z0-9_-]+$/.test(mcp.name) || typeof mcp.command !== 'string' || !Array.isArray(mcp.args)) { throw new TorchError('Codex MCP launch configuration is invalid', { code: 'INVALID_RUNTIME_INPUT' }); } return [ '-c', `mcp_servers.${mcp.name}.command=${JSON.stringify(mcp.command)}`, '-c', `mcp_servers.${mcp.name}.args=${JSON.stringify(mcp.args)}`, ]; } function mcpName(areaId) { return `torch-${areaId.toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-|-$/g, '')}`; } function launchPolicyArgs(policy = {}) { const issues = profileCapabilityIssues({ name: 'codex', capabilities: { launchPolicy: { fields: { sandbox: ['read-only', 'workspace-write', 'danger-full-access'], approval: ['approve-for-me', 'on-request', 'never'], }, conflicts: [{ field: 'approval', value: 'approve-for-me', with: 'sandbox' }], }, } }, { launchPolicy: policy }); if (issues.length) { throw new TorchError('Codex launch policy is invalid or unsupported', { code: 'RUNTIME_PROFILE_INVALID', details: { adapter: 'codex', issues }, }); } const args = []; if (policy.sandbox !== undefined) args.push('--sandbox', policy.sandbox); if (policy.approval === 'approve-for-me') args.push('--approve-for-me'); else if (policy.approval !== undefined) args.push('--ask-for-approval', policy.approval); return args; } const MAX_STARTUP_OUTPUT_BYTES = 16 * 1024; const MAX_STARTUP_EVENT_COUNT = 64; const MAX_DIAGNOSTIC_TEXT_LENGTH = 512; function parseJsonLines(output) { const records = []; for (const line of String(output ?? '').split('\n').map((value) => value.trim()).filter(Boolean)) { try { records.push(JSON.parse(line)); } catch { /* non-JSON diagnostics are not events */ } } return records; } function redactDiagnosticText(value, limit = MAX_DIAGNOSTIC_TEXT_LENGTH) { if (typeof value !== 'string') return null; const compact = value.replace(/\s+/g, ' ').trim(); if (!compact) return null; return compact .replace(/\bBearer\s+[-A-Za-z0-9._~+/]+=*/gi, 'Bearer [REDACTED]') .replace(/((?:api[_ -]?key|access[_ -]?token|refresh[_ -]?token|token|password|secret|credential|authorization|cookie|prompt|private reasoning)\s*(?:=|:|\s)\s*)([^\s,;]+)/gi, '$1[REDACTED]') .replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/gi, '$1[REDACTED]@') .slice(0, limit); } function boundedStartupOutput(output) { const text = typeof output === 'string' ? output : String(output ?? ''); if (Buffer.byteLength(text, 'utf8') > MAX_STARTUP_OUTPUT_BYTES) { return { records: null, reason: 'stdout-exceeds-bound' }; } const lines = text.split('\n').map((value) => value.trim()).filter(Boolean); if (lines.length > MAX_STARTUP_EVENT_COUNT) return { records: null, reason: 'stdout-too-many-events' }; const records = []; for (const line of lines) { try { records.push(JSON.parse(line)); } catch { return { records: null, reason: 'stdout-malformed-json' }; } } return { records, reason: null }; } function startupFailureEvent(record) { if (!record || typeof record !== 'object' || Array.isArray(record)) return null; if (record.type === 'error' || record.type === 'turn.failed' || record.type === 'turn.error') { const error = record.error && typeof record.error === 'object' && !Array.isArray(record.error) ? record.error : record; return { code: error.code ?? record.code, category: error.category ?? record.category, message: error.message ?? record.message, }; } return null; } function safeDiagnosticCode(value) { return typeof value === 'string' && /^[A-Z][A-Z0-9_.-]{0,79}$/.test(value) ? value : null; } function safeDiagnosticCategory(value) { return ['model-rejection', 'authentication', 'quota'].includes(value) ? value : null; } function categoryFromDiagnosticCode(code) { if (!code) return null; if (/(?:MODEL|UNSUPPORTED|UNAVAILABLE|NOT_FOUND)/.test(code)) return 'model-rejection'; if (/(?:AUTH|UNAUTHORIZED|FORBIDDEN|CREDENTIAL)/.test(code)) return 'authentication'; if (/(?:QUOTA|RATE_LIMIT|RATE-LIMIT|LIMIT_EXCEEDED)/.test(code)) return 'quota'; return null; } export function diagnoseCodexStartupFailure({ stdout, stderr } = {}) { const parsed = boundedStartupOutput(stdout); const safeStderr = redactDiagnosticText(stderr); if (!parsed.records) { return { schema: 'torch.dev/runtime-startup-diagnostic/v1alpha1', outcome: 'unknown', reason: parsed.reason, stderr: safeStderr, }; } const failure = parsed.records.map(startupFailureEvent).find(Boolean); if (!failure) { return { schema: 'torch.dev/runtime-startup-diagnostic/v1alpha1', outcome: 'unknown', reason: 'stdout-no-failure-event', stderr: safeStderr, }; } const message = redactDiagnosticText(failure.message); const code = safeDiagnosticCode(failure.code); const category = safeDiagnosticCategory(failure.category); const modelRejection = /\b(model|unsupported|not supported|unavailable|not available|does not exist)\b/i.test(message ?? '') || /MODEL|UNSUPPORTED/i.test(code ?? ''); return { schema: 'torch.dev/runtime-startup-diagnostic/v1alpha1', outcome: 'known', category: category ?? categoryFromDiagnosticCode(code) ?? (modelRejection ? 'model-rejection' : 'runtime-error'), ...(code ? { code } : {}), ...(message ? { message } : {}), stderr: safeStderr, }; } export class CodexRuntimeAdapter { constructor({ env = process.env, runner = null, executable = 'codex', nodeExecutable = process.execPath, } = {}) { this.name = 'codex'; this.env = env; this.runner = runner; this.executable = executable; this.nodeExecutable = nodeExecutable; this.configuration = Object.freeze({ model: 'gpt-6-luna', reasoning: 'high', launchPolicy: Object.freeze({ approval: 'approve-for-me' }), }); this.capabilities = Object.freeze({ detect: true, configure: 'plan-only', modelSelection: true, reasoningSelection: true, perInvocationCostCeiling: Object.freeze({ createSession: Object.freeze([]), resumeSession: Object.freeze([]) }), launchPolicy: Object.freeze({ fields: Object.freeze({ sandbox: Object.freeze(['read-only', 'workspace-write', 'danger-full-access']), approval: Object.freeze(['approve-for-me', 'on-request', 'never']), }), conflicts: Object.freeze([ Object.freeze({ field: 'approval', value: 'approve-for-me', with: 'sandbox' }), ]), }), createSession: true, resumeSession: true, sendOrSteer: 'native-with-durable-fallback', getStatus: false, listSessions: false, stopSession: false, captureRuntimeId: true, installHooks: false, removeHooks: false, liveSteering: true, durableMessaging: true, processModel: 'resumable-turn', }); validateRuntimeAdapter(this); } detect() { const path = this.executable.includes('/') && existsSync(this.executable) ? this.executable : findExecutable(this.executable, this.env); return { available: Boolean(path), executable: path, adapter: this.name }; } configure({ repositoryRoot, areaId, mcpEntry } = {}) { const root = text(repositoryRoot, 'repositoryRoot'); const id = text(areaId, 'areaId'); const entry = text(mcpEntry, 'mcpEntry'); return { adapter: this.name, mutationPerformed: false, mcp: { name: mcpName(id), command: this.nodeExecutable, args: [entry, '--root', root, '--area', id], cwd: root, }, note: 'Pass this identity-bound MCP server as an invocation-scoped Codex configuration.', }; } createSession({ areaId, worktree, promptFile, firstMessage, model, reasoning, launchPolicy, mcp } = {}) { const id = text(areaId, 'areaId'); const cwd = text(worktree, 'worktree'); const args = [ '--cd', cwd, ...launchPolicyArgs(launchPolicy ?? this.configuration.launchPolicy), ...mcpArgs(mcp), ...((reasoning ?? this.configuration.reasoning) ? ['-c', `model_reasoning_effort=${JSON.stringify(text(reasoning ?? this.configuration.reasoning, 'reasoning'))}`] : []), 'exec', '--json', ...optionalModelArgs(model ?? this.configuration.model), `${text(firstMessage, 'firstMessage')}\n\nRead and follow the TORCH domain prompt at ${text(promptFile, 'promptFile')}.`, ]; return { adapter: this.name, areaId: id, runtimeSessionId: null, requiresRuntimeIdCapture: true, completionState: 'idle', launch: commandRecord(this.executable, args, cwd), }; } resumeSession({ areaId, runtimeSessionId, worktree, message, instructionText, instructionDigest, model, reasoning, launchPolicy, mcp } = {}) { const id = text(areaId, 'areaId'); const sessionId = text(runtimeSessionId, 'runtimeSessionId'); const cwd = text(worktree, 'worktree'); const currentInstructions = instructionText ? `\n\nAUTHORITATIVE CURRENT TORCH INSTRUCTIONS (sha256 ${text(instructionDigest, 'instructionDigest')}; replace stale briefing or conversation memory):\n${text(instructionText, 'instructionText')}` : ''; const args = [ '--cd', cwd, ...launchPolicyArgs(launchPolicy ?? this.configuration.launchPolicy), ...mcpArgs(mcp), ...((reasoning ?? this.configuration.reasoning) ? ['-c', `model_reasoning_effort=${JSON.stringify(text(reasoning ?? this.configuration.reasoning, 'reasoning'))}`] : []), 'exec', 'resume', '--json', ...optionalModelArgs(model ?? this.configuration.model), sessionId, `${text(message, 'message')}${currentInstructions}`, ]; return { adapter: this.name, areaId: id, runtimeSessionId: sessionId, requiresRuntimeIdCapture: false, completionState: 'idle', launch: commandRecord(this.executable, args, cwd), }; } sendOrSteer({ controlPlane, sender = 'session-manager', recipient, body, references, runtimeSessionId } = {}) { if (!controlPlane) { throw new TorchError('Codex messaging requires the TORCH durable control plane', { code: 'CONTROL_PLANE_REQUIRED', }); } const message = controlPlane.sendMessage({ sender, recipient, body, references }); if (this.runner && runtimeSessionId) { try { const result = this.runner(this.executable, [ 'queue', '--thread', text(runtimeSessionId, 'runtimeSessionId'), '--message', text(body, 'body'), ]); if (result?.status === undefined || result.status === 0) { return { delivery: 'native+durable', liveSteeringAttempted: true, runtimeSessionId, message }; } } catch { /* the durable message remains available for polling */ } } return { delivery: 'durable', liveSteeringAttempted: Boolean(this.runner && runtimeSessionId), message }; } captureRuntimeId({ areaId, stdout, output, runtimeSessionId } = {}) { if (runtimeSessionId) { return { areaId: text(areaId, 'areaId'), runtimeSessionId: text(runtimeSessionId, 'runtimeSessionId') }; } const event = parseJsonLines(stdout ?? output).find((candidate) => candidate?.type === 'thread.started' && (candidate.thread_id || candidate.threadId)); const captured = event?.thread_id ?? event?.threadId; if (!captured) { throw new TorchError('Codex launch output did not contain a thread.started event', { code: 'RUNTIME_ID_NOT_CAPTURED', details: { adapter: this.name }, }); } return { areaId: text(areaId, 'areaId'), runtimeSessionId: text(captured, 'runtimeSessionId') }; } diagnoseStartupFailure({ stdout, stderr } = {}) { return diagnoseCodexStartupFailure({ stdout, stderr }); } getStatus() { return unsupportedCapability(this.name, 'getStatus'); } listSessions() { return unsupportedCapability(this.name, 'listSessions'); } stopSession() { return unsupportedCapability(this.name, 'stopSession'); } installHooks() { return unsupportedCapability(this.name, 'installHooks'); } removeHooks() { return unsupportedCapability(this.name, 'removeHooks'); } } export function createCodexAdapter(options) { return new CodexRuntimeAdapter(options); }