From c362ccd5cc8fff94c59d4a0c8f178628dc5e7596 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Wed, 30 Sep 2026 15:59:00 -0400 Subject: [PATCH] TASK-conversation-runtime-diagnostics: extract Codex startup failures --- src/adapters/codex.mjs | 76 +++++++++++++++++++++++++++++++++++++++ src/runtime/lifecycle.mjs | 10 +++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/adapters/codex.mjs b/src/adapters/codex.mjs index 2652312..e106e09 100644 --- a/src/adapters/codex.mjs +++ b/src/adapters/codex.mjs @@ -69,6 +69,10 @@ function launchPolicyArgs(policy = {}) { 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)) { @@ -77,6 +81,74 @@ function parseJsonLines(output) { 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)\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, 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; +} + +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 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: modelRejection ? 'model-rejection' : 'runtime-error', + ...(code ? { code } : {}), + ...(message ? { message } : {}), + stderr: safeStderr, + }; +} + export class CodexRuntimeAdapter { constructor({ env = process.env, @@ -227,6 +299,10 @@ export class CodexRuntimeAdapter { 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'); } diff --git a/src/runtime/lifecycle.mjs b/src/runtime/lifecycle.mjs index 1047820..9852603 100644 --- a/src/runtime/lifecycle.mjs +++ b/src/runtime/lifecycle.mjs @@ -483,13 +483,21 @@ export function startFleet({ plan, controlPlane, executor, adapters } = {}) { writeCombinedPrompt(action); const result = executor({ ...action.launch, areaId: action.areaId, runtimeSessionId: action.runtimeSessionId }); if (result?.status !== undefined && result.status !== 0) { + const runtimeAdapter = runtimes.get(action.runtime); + const diagnostic = typeof runtimeAdapter?.diagnoseStartupFailure === 'function' + ? runtimeAdapter.diagnoseStartupFailure({ stdout: result?.stdout, stderr: result?.stderr }) : null; controlPlane.reportStatus({ areaId: action.areaId, state: 'offline', runtime: action.runtime, runtimeSessionId: action.runtimeSessionId, summary: 'Runtime launch failed.', }); throw new TorchError(`Fleet startup failed for ${action.areaId}`, { code: 'FLEET_START_FAILED', - details: { areaId: action.areaId, status: result.status, stderr: result.stderr ?? null, started }, + details: { + areaId: action.areaId, status: result.status, + stderr: diagnostic ? diagnostic.stderr : result.stderr ?? null, + ...(diagnostic ? { diagnostic } : {}), + started, + }, }); } let runtimeSessionId = action.runtimeSessionId;