detect owner codex bad request loops
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Database } from 'bun:sqlite';
|
||||
|
||||
import { normalizeServiceId } from '../config.js';
|
||||
import { CODEX_BAD_REQUEST_DETAIL_JSON } from '../codex-bad-request-signal.js';
|
||||
import type { PairedTurnIdentity } from '../paired-turn-identity.js';
|
||||
import { inferAgentTypeFromServiceShadow } from '../role-service-shadow.js';
|
||||
import type { AgentType, PairedRoomRole } from '../types.js';
|
||||
@@ -33,6 +34,13 @@ export interface PairedTurnAttemptRecord {
|
||||
last_error: string | null;
|
||||
}
|
||||
|
||||
export interface OwnerCodexBadRequestFailureSummary {
|
||||
taskId: string;
|
||||
failures: number;
|
||||
firstFailureAt: string;
|
||||
latestFailureAt: string;
|
||||
}
|
||||
|
||||
function resolveExecutorMetadata(args: {
|
||||
executorServiceId?: string | null;
|
||||
executorAgentType?: AgentType | null;
|
||||
@@ -423,6 +431,59 @@ export function getPairedTurnAttemptIdFromDatabase(
|
||||
return row?.attempt_id ?? null;
|
||||
}
|
||||
|
||||
export function getOwnerCodexBadRequestFailureSummaryForTaskFromDatabase(
|
||||
database: Database,
|
||||
args: {
|
||||
taskId: string;
|
||||
threshold: number;
|
||||
},
|
||||
): OwnerCodexBadRequestFailureSummary | null {
|
||||
const threshold = Math.max(1, Math.floor(args.threshold));
|
||||
const attempts = database
|
||||
.prepare(
|
||||
`
|
||||
SELECT state, last_error, created_at
|
||||
FROM paired_turn_attempts
|
||||
WHERE task_id = ?
|
||||
AND role = 'owner'
|
||||
AND executor_agent_type = 'codex'
|
||||
ORDER BY created_at DESC, attempt_no DESC
|
||||
`,
|
||||
)
|
||||
.all(args.taskId) as Array<{
|
||||
state: PairedTurnAttemptState;
|
||||
last_error: string | null;
|
||||
created_at: string;
|
||||
}>;
|
||||
|
||||
const consecutiveFailures = [];
|
||||
for (const attempt of attempts) {
|
||||
if (
|
||||
attempt.state === 'failed' &&
|
||||
attempt.last_error?.trim() === CODEX_BAD_REQUEST_DETAIL_JSON
|
||||
) {
|
||||
consecutiveFailures.push(attempt);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (consecutiveFailures.length < threshold) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const firstFailureAt =
|
||||
consecutiveFailures[consecutiveFailures.length - 1].created_at;
|
||||
const latestFailureAt = consecutiveFailures[0].created_at;
|
||||
|
||||
return {
|
||||
taskId: args.taskId,
|
||||
failures: consecutiveFailures.length,
|
||||
firstFailureAt,
|
||||
latestFailureAt,
|
||||
};
|
||||
}
|
||||
|
||||
export function setPairedTurnAttemptContinuationHandoffIdInDatabase(
|
||||
database: Database,
|
||||
args: {
|
||||
|
||||
@@ -43,7 +43,9 @@ import {
|
||||
} from './paired-state.js';
|
||||
import {
|
||||
clearPairedTurnAttemptsInDatabase,
|
||||
getOwnerCodexBadRequestFailureSummaryForTaskFromDatabase,
|
||||
getPairedTurnAttemptsForTurnFromDatabase,
|
||||
type OwnerCodexBadRequestFailureSummary,
|
||||
type PairedTurnAttemptRecord,
|
||||
} from './paired-turn-attempts.js';
|
||||
import {
|
||||
@@ -237,6 +239,16 @@ export function getPairedTurnAttempts(
|
||||
return getPairedTurnAttemptsForTurnFromDatabase(requireDatabase(), turnId);
|
||||
}
|
||||
|
||||
export function getOwnerCodexBadRequestFailureSummaryForTask(args: {
|
||||
taskId: string;
|
||||
threshold: number;
|
||||
}): OwnerCodexBadRequestFailureSummary | null {
|
||||
return getOwnerCodexBadRequestFailureSummaryForTaskFromDatabase(
|
||||
requireDatabase(),
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
export function upsertPairedWorkspace(workspace: PairedWorkspace): void {
|
||||
upsertPairedWorkspaceInDatabase(requireDatabase(), workspace);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user