Files
EJClaw/src/paired-execution-context-arbiter.ts
2026-04-09 13:52:42 +09:00

106 lines
2.9 KiB
TypeScript

import { ARBITER_DEADLOCK_THRESHOLD } from './config.js';
import { logger } from './logger.js';
import {
classifyArbiterVerdict,
transitionPairedTaskStatus,
} from './paired-execution-context-shared.js';
import type { PairedTask } from './types.js';
export function handleFailedArbiterExecution(args: {
task: PairedTask;
taskId: string;
}): void {
const { task, taskId } = args;
const now = new Date().toISOString();
const fallbackStatus =
task.status === 'in_arbitration' || task.status === 'arbiter_requested'
? 'arbiter_requested'
: task.status;
if (fallbackStatus !== task.status) {
transitionPairedTaskStatus({
taskId,
currentStatus: task.status,
nextStatus: fallbackStatus,
expectedUpdatedAt: task.updated_at,
updatedAt: now,
});
logger.warn(
{
taskId,
role: 'arbiter',
previousStatus: task.status,
nextStatus: fallbackStatus,
},
'Preserved arbiter task in arbitration-requested state after failed execution',
);
}
}
export function handleArbiterCompletion(args: {
task: PairedTask;
taskId: string;
summary?: string | null;
}): void {
const { task, taskId, summary } = args;
const now = new Date().toISOString();
const arbiterVerdict = classifyArbiterVerdict(summary);
logger.info(
{ taskId, arbiterVerdict, summary: summary?.slice(0, 200) },
'Arbiter verdict rendered',
);
switch (arbiterVerdict) {
case 'proceed':
case 'revise':
case 'reset':
transitionPairedTaskStatus({
taskId,
currentStatus: 'in_arbitration',
nextStatus: 'active',
expectedUpdatedAt: task.updated_at,
updatedAt: now,
patch: {
round_trip_count: Math.max(0, ARBITER_DEADLOCK_THRESHOLD - 1),
arbiter_verdict: arbiterVerdict,
},
});
logger.info(
{ taskId, arbiterVerdict },
'Arbiter resolved deadlock — resuming ping-pong',
);
return;
case 'escalate':
transitionPairedTaskStatus({
taskId,
currentStatus: 'in_arbitration',
nextStatus: 'completed',
expectedUpdatedAt: task.updated_at,
updatedAt: now,
patch: {
arbiter_verdict: 'escalate',
completion_reason: 'arbiter_escalated',
},
});
logger.info({ taskId }, 'Arbiter escalated to user — task completed');
return;
default:
transitionPairedTaskStatus({
taskId,
currentStatus: 'in_arbitration',
nextStatus: 'active',
expectedUpdatedAt: task.updated_at,
updatedAt: now,
patch: {
round_trip_count: Math.max(0, ARBITER_DEADLOCK_THRESHOLD - 1),
arbiter_verdict: 'unknown',
},
});
logger.warn(
{ taskId, summary: summary?.slice(0, 200) },
'Arbiter verdict unrecognized — falling back to proceed',
);
return;
}
}