fix: recover Codex rotation auth failures

- mark Codex bearer/refresh failures as terminal auth-expired states

- sync refreshed session auth back to rotation slots and revive refreshed dead_auth slots

- stop paired arbiter retry loops when Codex accounts are unavailable

- add regression coverage for rotation leases, follow-up suppression, and arbiter closure
This commit is contained in:
ejclaw
2026-06-02 00:21:16 +09:00
parent 1ff6b3434f
commit 03d4c81192
21 changed files with 1082 additions and 52 deletions

View File

@@ -1,15 +1,52 @@
import { logger } from './logger.js';
import {
classifyAgentError,
classifyCodexAuthError,
} from './agent-error-detection.js';
import { transitionPairedTaskStatus } from './paired-task-status.js';
import { classifyArbiterVerdict } from './paired-verdict.js';
import type { PairedTask } from './types.js';
const ARBITER_RESOLUTION_ROUND_TRIP_COUNT = 0;
function isTerminalCodexAccountFailure(summary?: string | null): boolean {
if (!summary) return false;
if (classifyCodexAuthError(summary).category !== 'none') return true;
if (classifyAgentError(summary).category === 'rate-limit') return true;
return /all\s+codex(?:\s+rotation)?\s+accounts/i.test(summary);
}
export function handleFailedArbiterExecution(args: {
task: PairedTask;
taskId: string;
summary?: string | null;
}): void {
const { task, taskId } = args;
const { task, taskId, summary } = args;
if (isTerminalCodexAccountFailure(summary)) {
const now = new Date().toISOString();
transitionPairedTaskStatus({
taskId,
currentStatus: task.status,
nextStatus: 'completed',
expectedUpdatedAt: task.updated_at,
updatedAt: now,
patch: {
arbiter_verdict: 'escalate',
arbiter_requested_at: null,
completion_reason: 'arbiter_codex_unavailable',
},
});
logger.warn(
{
taskId,
role: 'arbiter',
status: task.status,
summary: summary?.slice(0, 200),
},
'Completed arbiter task after terminal Codex account failure instead of re-arming arbitration loop',
);
return;
}
const now = new Date().toISOString();
const fallbackStatus =
task.status === 'in_arbitration' || task.status === 'arbiter_requested'