import { getOwnerCodexBadRequestFailureSummaryForTask } from './db.js'; import { getEnv } from './env.js'; import { logger } from './logger.js'; import type { PairedTurnIdentity } from './paired-turn-identity.js'; import type { AgentType, Channel, PairedRoomRole, VisiblePhase, } from './types.js'; const DEFAULT_CODEX_BAD_REQUEST_REPEAT_THRESHOLD = 3; export function getCodexBadRequestRepeatThreshold(): number { const raw = getEnv('CODEX_BAD_REQUEST_REPEAT_THRESHOLD'); if (!raw) { return DEFAULT_CODEX_BAD_REQUEST_REPEAT_THRESHOLD; } const parsed = Number.parseInt(raw, 10); if (!Number.isFinite(parsed) || parsed < 2) { return DEFAULT_CODEX_BAD_REQUEST_REPEAT_THRESHOLD; } return parsed; } export async function notifyOwnerCodexBadRequestObservation(args: { chatJid: string; runId: string; groupFolder: string; channel: Pick; outputStatus: 'success' | 'error'; visiblePhase: VisiblePhase; deliveryRole: PairedRoomRole | null; agentType: AgentType; pairedTurnIdentity: PairedTurnIdentity | null; threshold?: number; }): Promise { if ( args.outputStatus !== 'error' || args.visiblePhase !== 'silent' || args.agentType !== 'codex' ) { return false; } const role = args.pairedTurnIdentity?.role ?? args.deliveryRole; if (role !== 'owner' || !args.pairedTurnIdentity) { return false; } const threshold = args.threshold ?? getCodexBadRequestRepeatThreshold(); const summary = getOwnerCodexBadRequestFailureSummaryForTask({ taskId: args.pairedTurnIdentity.taskId, threshold, }); if (!summary || summary.failures !== threshold) { return false; } const message = [ `🟑 owner Codex μ„Έμ…˜μ΄ {"detail":"Bad Request"}둜 ${summary.failures}회 연속 무좜λ ₯ μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.`, 'μžλ™λ³΅κ΅¬λŠ” 아직 λΉ„ν™œμ„±ν™”λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. ν˜„μž¬λŠ” κ°μ§€λ§Œ μˆ˜ν–‰ν•˜λ©°, ν•„μš”ν•˜λ©΄ `/clear`둜 μ„Έμ…˜μ„ μ΄ˆκΈ°ν™”ν•΄ μ£Όμ„Έμš”.', `task=${summary.taskId}`, `latest=${summary.latestFailureAt}`, ].join('\n'); try { await args.channel.sendMessage(args.chatJid, message); } catch (err) { logger.warn( { err, chatJid: args.chatJid, runId: args.runId, groupFolder: args.groupFolder, taskId: summary.taskId, }, 'Failed to send owner Codex Bad Request observation notice', ); return false; } logger.warn( { chatJid: args.chatJid, runId: args.runId, groupFolder: args.groupFolder, taskId: summary.taskId, failures: summary.failures, firstFailureAt: summary.firstFailureAt, latestFailureAt: summary.latestFailureAt, }, 'Detected repeated owner Codex Bad Request failures without visible output', ); return true; }