Files
EJClaw/src/session-auto-healer.ts
2026-04-29 22:20:06 +09:00

98 lines
2.7 KiB
TypeScript

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<Channel, 'sendMessage'>;
outputStatus: 'success' | 'error';
visiblePhase: VisiblePhase;
deliveryRole: PairedRoomRole | null;
agentType: AgentType;
pairedTurnIdentity: PairedTurnIdentity | null;
threshold?: number;
}): Promise<boolean> {
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;
}