From 3e2954ebcfde2b051056c3c766c8cac73a7b503f Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Mon, 30 Mar 2026 23:44:49 +0900 Subject: [PATCH] refactor: extract resolveEffectiveAgentType/resolveSessionFolder as SSOT, deduplicate CONTAINER_IMAGE --- src/container-runner.ts | 3 +- src/message-agent-executor.ts | 52 ++++++++++++----------------------- src/message-runtime-rules.ts | 38 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/src/container-runner.ts b/src/container-runner.ts index d4a9945..46b6109 100644 --- a/src/container-runner.ts +++ b/src/container-runner.ts @@ -40,8 +40,7 @@ import type { RegisteredGroup } from './types.js'; // ── Config ──────────────────────────────────────────────────────── -const CONTAINER_IMAGE = - process.env.REVIEWER_CONTAINER_IMAGE || 'ejclaw-reviewer:latest'; +import { REVIEWER_CONTAINER_IMAGE as CONTAINER_IMAGE } from './config.js'; // ── Types ───────────────────────────────────────────────────────── diff --git a/src/message-agent-executor.ts b/src/message-agent-executor.ts index 7b56c7d..c742be8 100644 --- a/src/message-agent-executor.ts +++ b/src/message-agent-executor.ts @@ -23,7 +23,11 @@ import { completePairedExecutionContext, preparePairedExecutionContext, } from './paired-execution-context.js'; -import { resolveActiveRole } from './message-runtime-rules.js'; +import { + resolveActiveRole, + resolveEffectiveAgentType, + resolveSessionFolder, +} from './message-runtime-rules.js'; import { buildRoomRoleContext } from './room-role-context.js'; import { classifyRotationTrigger, @@ -35,11 +39,8 @@ import { shouldRetryFreshSessionOnAgentFailure, } from './session-recovery.js'; import { - ARBITER_AGENT_TYPE, - CODEX_MAIN_SERVICE_ID, CODEX_REVIEW_SERVICE_ID, SERVICE_SESSION_SCOPE, - REVIEWER_AGENT_TYPE, isClaudeService, getRoleModelConfig, } from './config.js'; @@ -107,34 +108,20 @@ export async function runAgentForGroup( const reviewerMode = activeRole === 'reviewer'; const arbiterMode = activeRole === 'arbiter'; - // Override agent type based on role config (OWNER_AGENT_TYPE / REVIEWER_AGENT_TYPE). - // When the reviewer uses a different agent type than the group default, - // swap in the configured type for the duration of this execution. - const reviewerAgentTypeOverride = - reviewerMode && REVIEWER_AGENT_TYPE !== (group.agentType || 'claude-code'); - const arbiterAgentTypeOverride = - arbiterMode && - ARBITER_AGENT_TYPE != null && - ARBITER_AGENT_TYPE !== (group.agentType || 'claude-code'); - const effectiveAgentType = arbiterAgentTypeOverride - ? ARBITER_AGENT_TYPE! - : reviewerAgentTypeOverride - ? REVIEWER_AGENT_TYPE - : group.agentType || 'claude-code'; - const effectiveGroup = arbiterAgentTypeOverride - ? { ...group, agentType: ARBITER_AGENT_TYPE! } - : reviewerAgentTypeOverride - ? { ...group, agentType: REVIEWER_AGENT_TYPE } + const effectiveAgentType = resolveEffectiveAgentType( + activeRole, + group.agentType, + ); + const effectiveGroup = + effectiveAgentType !== (group.agentType || 'claude-code') + ? { ...group, agentType: effectiveAgentType } : group; const isClaudeCodeAgent = effectiveAgentType === 'claude-code'; - - // When the reviewer/arbiter uses a different agent type than the group default, - // use a separate session key so owner, reviewer, and arbiter sessions don't collide. - const sessionFolder = arbiterAgentTypeOverride - ? `${group.folder}:arbiter` - : reviewerAgentTypeOverride - ? `${group.folder}:reviewer` - : group.folder; + const sessionFolder = resolveSessionFolder( + group.folder, + activeRole, + group.agentType, + ); const sessionId = sessions[sessionFolder]; const memoryBriefing = sessionId ? undefined @@ -478,10 +465,7 @@ export async function runAgentForGroup( groupFolder: group.folder, runId, provider: effectiveAgentType, - reviewerMode, - arbiterMode, - reviewerAgentTypeOverride, - arbiterAgentTypeOverride, + role: activeRole, }, `Using provider: ${effectiveAgentType}`, ); diff --git a/src/message-runtime-rules.ts b/src/message-runtime-rules.ts index 8afa52c..201c0c3 100644 --- a/src/message-runtime-rules.ts +++ b/src/message-runtime-rules.ts @@ -4,6 +4,12 @@ import { normalizeStoredSeqCursor } from './message-cursor.js'; import { isTriggerAllowed, loadSenderAllowlist } from './sender-allowlist.js'; import { isTaskStatusControlMessage } from './task-watch-status.js'; import { + ARBITER_AGENT_TYPE, + OWNER_AGENT_TYPE, + REVIEWER_AGENT_TYPE, +} from './config.js'; +import { + type AgentType, type Channel, type NewMessage, type RegisteredGroup, @@ -57,6 +63,38 @@ export function resolveCursorKey( return role === 'owner' ? chatJid : `${chatJid}:${role}`; } +/** Resolve the effective agent type for a role, considering per-role overrides. */ +export function resolveEffectiveAgentType( + role: 'owner' | 'reviewer' | 'arbiter', + groupAgentType: AgentType | undefined, +): AgentType { + const groupDefault: AgentType = groupAgentType || 'claude-code'; + switch (role) { + case 'reviewer': + return REVIEWER_AGENT_TYPE !== groupDefault + ? REVIEWER_AGENT_TYPE + : groupDefault; + case 'arbiter': + return ARBITER_AGENT_TYPE != null && ARBITER_AGENT_TYPE !== groupDefault + ? ARBITER_AGENT_TYPE + : groupDefault; + default: + return groupDefault; + } +} + +/** Session folder key for a role. Owner uses groupFolder, others use groupFolder:role. */ +export function resolveSessionFolder( + groupFolder: string, + role: 'owner' | 'reviewer' | 'arbiter', + groupAgentType: AgentType | undefined, +): string { + const effectiveType = resolveEffectiveAgentType(role, groupAgentType); + const groupDefault: AgentType = groupAgentType || 'claude-code'; + if (effectiveType === groupDefault) return groupFolder; + return `${groupFolder}:${role}`; +} + export function createImplicitContinuationTracker(idleTimeout: number) { const implicitContinuationUntil = new Map();