refactor: extract resolveEffectiveAgentType/resolveSessionFolder as SSOT, deduplicate CONTAINER_IMAGE

This commit is contained in:
Eyejoker
2026-03-30 23:44:49 +09:00
parent e649432a40
commit 3e2954ebcf
3 changed files with 57 additions and 36 deletions

View File

@@ -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 ─────────────────────────────────────────────────────────

View File

@@ -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}`,
);

View File

@@ -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<string, number>();