Files
EJClaw/src/message-agent-executor-rules.ts
Eyejoker 3d36e15f67 Fix recurring Codex unavailable in paired rooms (#212)
* fix: avoid codex leases for claude readonly sessions

* fix: restore codex lease quality budget

* fix: preserve owner finalization on codex failure

* fix: wake owner after arbiter codex failure

* chore: format readonly codex lease fix
2026-06-03 01:14:39 +08:00

77 lines
2.3 KiB
TypeScript

import {
resolveFollowUpDispatch,
resolveNextTurnAction,
shouldRetrySilentOwnerExecution,
} from './message-runtime-rules.js';
import { isTerminalCodexAccountFailure } from './agent-error-detection.js';
import { parseVisibleVerdict } from './paired-verdict.js';
import type { PairedRoomRole, PairedTaskStatus } from './types.js';
export {
isRetryableClaudeSessionFailureAttempt,
resolveAttemptRetryAction,
resolveClaudeRetryTrigger,
resolveCodexRetryTrigger,
type AttemptRetryAction,
type AttemptRetryState as ExecutorAttemptState,
type AttemptStreamedTrigger as ExecutorStreamedTrigger,
} from './agent-attempt-retry.js';
export type PairedFollowUpQueueAction = 'pending' | 'none';
function isSilentCodexAccountFailure(summary?: string | null): boolean {
return isTerminalCodexAccountFailure(summary);
}
export function resolvePairedFollowUpQueueAction(args: {
completedRole: PairedRoomRole;
executionStatus: 'succeeded' | 'failed';
sawOutput: boolean;
taskStatus: PairedTaskStatus | null;
outputSummary?: string | null;
ownerFailureCount?: number | null;
}): PairedFollowUpQueueAction {
if (
args.executionStatus === 'failed' &&
args.sawOutput === false &&
isSilentCodexAccountFailure(args.outputSummary)
) {
if (args.completedRole === 'arbiter' && args.taskStatus === 'active') {
return 'pending';
}
if (args.completedRole !== 'owner') {
return 'none';
}
}
if (
shouldRetrySilentOwnerExecution({
completedRole: args.completedRole,
executionStatus: args.executionStatus,
sawOutput: args.sawOutput,
taskStatus: args.taskStatus,
})
) {
return 'pending';
}
const nextTurnAction = resolveNextTurnAction({
taskStatus: args.taskStatus,
lastTurnOutputRole: args.sawOutput ? args.completedRole : null,
lastTurnOutputVerdict:
args.sawOutput && args.outputSummary
? parseVisibleVerdict(args.outputSummary)
: null,
});
const dispatch = resolveFollowUpDispatch({
source: 'executor-recovery',
nextTurnAction,
completedRole: args.completedRole,
executionStatus: args.executionStatus,
sawOutput: args.sawOutput,
});
return dispatch.kind === 'enqueue' &&
dispatch.queueKind === 'paired-follow-up'
? 'pending'
: 'none';
}