refactor: remove SERVICE_AGENT_TYPE legacy constant

SERVICE_AGENT_TYPE was always 'claude-code' in the unified service,
making it a misleading constant. Each group already has its own
agentType field.

- Removed SERVICE_AGENT_TYPE from config.ts
- Session functions now accept agentType parameter (default: 'claude-code')
- Task scheduler uses task-level agent_type for token rotation decisions
- All fallback defaults changed to 'claude-code' literal
- Logging uses 'unified' instead of the misleading type
This commit is contained in:
Eyejoker
2026-03-30 23:10:48 +09:00
parent 5562e30ce1
commit 4a5edaceb6
12 changed files with 56 additions and 54 deletions

View File

@@ -9,7 +9,7 @@ import path from 'path';
import Database from 'better-sqlite3'; import Database from 'better-sqlite3';
import { SERVICE_AGENT_TYPE, STORE_DIR } from '../src/config.js'; import { STORE_DIR } from '../src/config.js';
import { isValidGroupFolder } from '../src/group-folder.js'; import { isValidGroupFolder } from '../src/group-folder.js';
import { logger } from '../src/logger.js'; import { logger } from '../src/logger.js';
import { emitStatus } from './status.js'; import { emitStatus } from './status.js';
@@ -156,7 +156,7 @@ export async function run(args: string[]): Promise<void> {
timestamp, timestamp,
requiresTriggerInt, requiresTriggerInt,
isMainInt, isMainInt,
SERVICE_AGENT_TYPE, 'claude-code',
); );
db.close(); db.close();

View File

@@ -530,7 +530,13 @@ export function prepareContainerSessionEnvironment(args: {
memoryBriefing?: string; memoryBriefing?: string;
role?: 'reviewer' | 'arbiter'; role?: 'reviewer' | 'arbiter';
}): void { }): void {
const { sessionDir, chatJid, isMain, memoryBriefing, role = 'reviewer' } = args; const {
sessionDir,
chatJid,
isMain,
memoryBriefing,
role = 'reviewer',
} = args;
const projectRoot = process.cwd(); const projectRoot = process.cwd();
fs.mkdirSync(sessionDir, { recursive: true }); fs.mkdirSync(sessionDir, { recursive: true });
@@ -546,9 +552,9 @@ export function prepareContainerSessionEnvironment(args: {
// Build CLAUDE.md with role-appropriate prompts (reviewer or arbiter) // Build CLAUDE.md with role-appropriate prompts (reviewer or arbiter)
const claudePlatformPrompt = readPlatformPrompt('claude-code', projectRoot); const claudePlatformPrompt = readPlatformPrompt('claude-code', projectRoot);
const claudePairedRoomPrompt = isPairedRoomJid(chatJid) const claudePairedRoomPrompt = isPairedRoomJid(chatJid)
? (role === 'arbiter' ? role === 'arbiter'
? readArbiterPrompt(projectRoot) ? readArbiterPrompt(projectRoot)
: readPairedRoomPrompt('claude-code', projectRoot)) : readPairedRoomPrompt('claude-code', projectRoot)
: undefined; : undefined;
const globalDir = path.join(GROUPS_DIR, 'global'); const globalDir = path.join(GROUPS_DIR, 'global');
const globalClaudeMdPath = path.join(globalDir, 'CLAUDE.md'); const globalClaudeMdPath = path.join(globalDir, 'CLAUDE.md');

View File

@@ -75,7 +75,10 @@ export async function runAgentProcess(
// ── Reviewer container mode ───────────────────────────────────── // ── Reviewer container mode ─────────────────────────────────────
// Reviewers always run inside a Docker container with read-only source // Reviewers always run inside a Docker container with read-only source
// mount for kernel-level write protection. Docker is required. // mount for kernel-level write protection. Docker is required.
if (envOverrides?.EJCLAW_REVIEWER_RUNTIME === '1' || envOverrides?.EJCLAW_ARBITER_RUNTIME === '1') { if (
envOverrides?.EJCLAW_REVIEWER_RUNTIME === '1' ||
envOverrides?.EJCLAW_ARBITER_RUNTIME === '1'
) {
const ownerWorkspaceDir = const ownerWorkspaceDir =
envOverrides?.EJCLAW_WORK_DIR || group.workDir || process.cwd(); envOverrides?.EJCLAW_WORK_DIR || group.workDir || process.cwd();
@@ -83,7 +86,10 @@ export async function runAgentProcess(
// so the Claude SDK inside the container has platform & paired room prompts. // so the Claude SDK inside the container has platform & paired room prompts.
const sessionDir = envOverrides?.CLAUDE_CONFIG_DIR; const sessionDir = envOverrides?.CLAUDE_CONFIG_DIR;
if (sessionDir) { if (sessionDir) {
const containerRole = envOverrides?.EJCLAW_ARBITER_RUNTIME === '1' ? 'arbiter' as const : 'reviewer' as const; const containerRole =
envOverrides?.EJCLAW_ARBITER_RUNTIME === '1'
? ('arbiter' as const)
: ('reviewer' as const);
prepareContainerSessionEnvironment({ prepareContainerSessionEnvironment({
sessionDir, sessionDir,
chatJid: input.chatJid, chatJid: input.chatJid,

View File

@@ -8,19 +8,12 @@ export const ASSISTANT_NAME = getEnv('ASSISTANT_NAME') || 'Andy';
export const ASSISTANT_HAS_OWN_NUMBER = export const ASSISTANT_HAS_OWN_NUMBER =
getEnv('ASSISTANT_HAS_OWN_NUMBER') === 'true'; getEnv('ASSISTANT_HAS_OWN_NUMBER') === 'true';
const ASSISTANT_SLUG = ASSISTANT_NAME.trim().toLowerCase(); const ASSISTANT_SLUG = ASSISTANT_NAME.trim().toLowerCase();
const rawServiceAgentType = getEnv('SERVICE_AGENT_TYPE');
export const SERVICE_ID = getEnv('SERVICE_ID') || ASSISTANT_SLUG; export const SERVICE_ID = getEnv('SERVICE_ID') || ASSISTANT_SLUG;
export const CLAUDE_SERVICE_ID = getEnv('CLAUDE_SERVICE_ID') || 'claude'; export const CLAUDE_SERVICE_ID = getEnv('CLAUDE_SERVICE_ID') || 'claude';
export const CODEX_MAIN_SERVICE_ID = export const CODEX_MAIN_SERVICE_ID =
getEnv('CODEX_MAIN_SERVICE_ID') || 'codex-main'; getEnv('CODEX_MAIN_SERVICE_ID') || 'codex-main';
export const CODEX_REVIEW_SERVICE_ID = export const CODEX_REVIEW_SERVICE_ID =
getEnv('CODEX_REVIEW_SERVICE_ID') || 'codex-review'; getEnv('CODEX_REVIEW_SERVICE_ID') || 'codex-review';
export const SERVICE_AGENT_TYPE: AgentType =
rawServiceAgentType === 'codex' || rawServiceAgentType === 'claude-code'
? rawServiceAgentType
: ASSISTANT_SLUG === 'codex'
? 'codex'
: 'claude-code';
export function normalizeServiceId(serviceId: string): string { export function normalizeServiceId(serviceId: string): string {
if (serviceId === 'codex') { if (serviceId === 'codex') {
@@ -119,7 +112,7 @@ export const ARBITER_AGENT_TYPE: AgentType | undefined =
/** Service ID for the arbiter. Re-uses codex-review bot by default when arbiter is enabled. */ /** Service ID for the arbiter. Re-uses codex-review bot by default when arbiter is enabled. */
export const ARBITER_SERVICE_ID = ARBITER_AGENT_TYPE export const ARBITER_SERVICE_ID = ARBITER_AGENT_TYPE
? (getEnv('ARBITER_SERVICE_ID') || CODEX_REVIEW_SERVICE_ID) ? getEnv('ARBITER_SERVICE_ID') || CODEX_REVIEW_SERVICE_ID
: null; : null;
/** Number of consecutive owner↔reviewer round trips before arbiter is auto-requested. */ /** Number of consecutive owner↔reviewer round trips before arbiter is auto-requested. */

View File

@@ -9,7 +9,6 @@ import {
CODEX_REVIEW_SERVICE_ID, CODEX_REVIEW_SERVICE_ID,
DATA_DIR, DATA_DIR,
normalizeServiceId, normalizeServiceId,
SERVICE_AGENT_TYPE,
SERVICE_ID, SERVICE_ID,
SERVICE_SESSION_SCOPE, SERVICE_SESSION_SCOPE,
STORE_DIR, STORE_DIR,
@@ -729,7 +728,7 @@ function createSchema(database: Database.Database): void {
`INSERT INTO sessions_new (group_folder, agent_type, session_id) `INSERT INTO sessions_new (group_folder, agent_type, session_id)
SELECT group_folder, ?, session_id FROM sessions`, SELECT group_folder, ?, session_id FROM sessions`,
) )
.run(SERVICE_AGENT_TYPE); .run('claude-code');
database.exec(` database.exec(`
DROP TABLE sessions; DROP TABLE sessions;
ALTER TABLE sessions_new RENAME TO sessions; ALTER TABLE sessions_new RENAME TO sessions;
@@ -1151,7 +1150,7 @@ export function hasRecentRestartAnnouncement(
export function getOpenWorkItem( export function getOpenWorkItem(
chatJid: string, chatJid: string,
agentType: AgentType = SERVICE_AGENT_TYPE, agentType: AgentType = 'claude-code',
serviceId: string = SERVICE_SESSION_SCOPE, serviceId: string = SERVICE_SESSION_SCOPE,
): WorkItem | undefined { ): WorkItem | undefined {
return db return db
@@ -1176,7 +1175,7 @@ export function createProducedWorkItem(input: {
result_payload: string; result_payload: string;
}): WorkItem { }): WorkItem {
const now = new Date().toISOString(); const now = new Date().toISOString();
const agentType = input.agent_type || SERVICE_AGENT_TYPE; const agentType = input.agent_type || 'claude-code';
const serviceId = input.service_id || SERVICE_SESSION_SCOPE; const serviceId = input.service_id || SERVICE_SESSION_SCOPE;
const result = db const result = db
.prepare( .prepare(
@@ -1267,7 +1266,7 @@ export function createTask(
task.id, task.id,
task.group_folder, task.group_folder,
task.chat_jid, task.chat_jid,
task.agent_type || SERVICE_AGENT_TYPE, task.agent_type || 'claude-code',
task.ci_provider ?? null, task.ci_provider ?? null,
task.ci_metadata ?? null, task.ci_metadata ?? null,
task.max_duration_ms ?? null, task.max_duration_ms ?? null,
@@ -1596,7 +1595,10 @@ export function setRouterStateForService(
// --- Session accessors --- // --- Session accessors ---
export function getSession(groupFolder: string): string | undefined { export function getSession(
groupFolder: string,
agentType: AgentType = 'claude-code',
): string | undefined {
const serviceScopedRow = db const serviceScopedRow = db
.prepare( .prepare(
'SELECT session_id FROM service_sessions WHERE group_folder = ? AND service_id = ?', 'SELECT session_id FROM service_sessions WHERE group_folder = ? AND service_id = ?',
@@ -1612,26 +1614,33 @@ export function getSession(groupFolder: string): string | undefined {
.prepare( .prepare(
'SELECT session_id FROM sessions WHERE group_folder = ? AND agent_type = ?', 'SELECT session_id FROM sessions WHERE group_folder = ? AND agent_type = ?',
) )
.get(groupFolder, SERVICE_AGENT_TYPE) as { session_id: string } | undefined; .get(groupFolder, agentType) as { session_id: string } | undefined;
return row?.session_id; return row?.session_id;
} }
export function setSession(groupFolder: string, sessionId: string): void { export function setSession(
groupFolder: string,
sessionId: string,
agentType: AgentType = 'claude-code',
): void {
db.prepare( db.prepare(
'INSERT OR REPLACE INTO service_sessions (group_folder, service_id, session_id) VALUES (?, ?, ?)', 'INSERT OR REPLACE INTO service_sessions (group_folder, service_id, session_id) VALUES (?, ?, ?)',
).run(groupFolder, SERVICE_SESSION_SCOPE, sessionId); ).run(groupFolder, SERVICE_SESSION_SCOPE, sessionId);
db.prepare( db.prepare(
'INSERT OR REPLACE INTO sessions (group_folder, agent_type, session_id) VALUES (?, ?, ?)', 'INSERT OR REPLACE INTO sessions (group_folder, agent_type, session_id) VALUES (?, ?, ?)',
).run(groupFolder, SERVICE_AGENT_TYPE, sessionId); ).run(groupFolder, agentType, sessionId);
} }
export function deleteSession(groupFolder: string): void { export function deleteSession(
groupFolder: string,
agentType: AgentType = 'claude-code',
): void {
db.prepare( db.prepare(
'DELETE FROM service_sessions WHERE group_folder = ? AND service_id = ?', 'DELETE FROM service_sessions WHERE group_folder = ? AND service_id = ?',
).run(groupFolder, SERVICE_SESSION_SCOPE); ).run(groupFolder, SERVICE_SESSION_SCOPE);
db.prepare( db.prepare(
'DELETE FROM sessions WHERE group_folder = ? AND agent_type = ?', 'DELETE FROM sessions WHERE group_folder = ? AND agent_type = ?',
).run(groupFolder, SERVICE_AGENT_TYPE); ).run(groupFolder, agentType);
} }
export function deleteAllSessionsForGroup(groupFolder: string): void { export function deleteAllSessionsForGroup(groupFolder: string): void {
@@ -1662,7 +1671,7 @@ export function getAllSessions(): Record<string, string> {
.prepare( .prepare(
'SELECT group_folder, session_id FROM sessions WHERE agent_type = ?', 'SELECT group_folder, session_id FROM sessions WHERE agent_type = ?',
) )
.all(SERVICE_AGENT_TYPE) as Array<{ .all('claude-code') as Array<{
group_folder: string; group_folder: string;
session_id: string; session_id: string;
}>; }>;
@@ -2125,7 +2134,7 @@ export function listPairedWorkspacesForTask(taskId: string): PairedWorkspace[] {
*/ */
export function getLastBotFinalMessage( export function getLastBotFinalMessage(
chatJid: string, chatJid: string,
_agentType: AgentType = SERVICE_AGENT_TYPE, _agentType: AgentType = 'claude-code',
limit: number = 1, limit: number = 1,
): Array<{ content: string; timestamp: string }> { ): Array<{ content: string; timestamp: string }> {
const rows = db const rows = db

View File

@@ -9,7 +9,6 @@ import {
POLL_INTERVAL, POLL_INTERVAL,
REVIEWER_AGENT_TYPE, REVIEWER_AGENT_TYPE,
SERVICE_ID, SERVICE_ID,
SERVICE_AGENT_TYPE,
isSessionCommandSenderAllowed, isSessionCommandSenderAllowed,
STATUS_CHANNEL_ID, STATUS_CHANNEL_ID,
STATUS_UPDATE_INTERVAL, STATUS_UPDATE_INTERVAL,
@@ -194,7 +193,7 @@ function loadState(): void {
logger.info( logger.info(
{ {
groupCount: Object.keys(registeredGroups).length, groupCount: Object.keys(registeredGroups).length,
agentType: SERVICE_AGENT_TYPE, agentType: 'unified',
}, },
'State loaded', 'State loaded',
); );
@@ -520,7 +519,7 @@ async function main(): Promise<void> {
await startUnifiedDashboard({ await startUnifiedDashboard({
assistantName: ASSISTANT_NAME, assistantName: ASSISTANT_NAME,
serviceId: SERVICE_ID, serviceId: SERVICE_ID,
serviceAgentType: SERVICE_AGENT_TYPE, serviceAgentType: 'claude-code',
statusChannelId: STATUS_CHANNEL_ID, statusChannelId: STATUS_CHANNEL_ID,
statusUpdateInterval: STATUS_UPDATE_INTERVAL, statusUpdateInterval: STATUS_UPDATE_INTERVAL,
usageUpdateInterval: USAGE_UPDATE_INTERVAL, usageUpdateInterval: USAGE_UPDATE_INTERVAL,

View File

@@ -6,7 +6,6 @@ import { CronExpressionParser } from 'cron-parser';
import { import {
DATA_DIR, DATA_DIR,
IPC_POLL_INTERVAL, IPC_POLL_INTERVAL,
SERVICE_AGENT_TYPE,
TIMEZONE, TIMEZONE,
} from './config.js'; } from './config.js';
import { readJsonFile } from './utils.js'; import { readJsonFile } from './utils.js';
@@ -433,7 +432,7 @@ export async function processTaskIpc(
id: taskId, id: taskId,
group_folder: targetFolder, group_folder: targetFolder,
chat_jid: resolvedTargetJid, chat_jid: resolvedTargetJid,
agent_type: targetGroupEntry.agentType || SERVICE_AGENT_TYPE, agent_type: targetGroupEntry.agentType || 'claude-code',
ci_provider: data.ci_provider ?? null, ci_provider: data.ci_provider ?? null,
ci_metadata: data.ci_metadata ?? null, ci_metadata: data.ci_metadata ?? null,
max_duration_ms: isWatchCiTask({ prompt: data.prompt }) max_duration_ms: isWatchCiTask({ prompt: data.prompt })
@@ -453,7 +452,7 @@ export async function processTaskIpc(
sourceGroup, sourceGroup,
targetFolder, targetFolder,
contextMode, contextMode,
agentType: targetGroupEntry.agentType || SERVICE_AGENT_TYPE, agentType: targetGroupEntry.agentType || 'claude-code',
}, },
'Task created via IPC', 'Task created via IPC',
); );

View File

@@ -14,7 +14,6 @@ vi.mock('./agent-runner.js', () => ({
vi.mock('./config.js', () => ({ vi.mock('./config.js', () => ({
DATA_DIR: '/tmp/ejclaw-test-data', DATA_DIR: '/tmp/ejclaw-test-data',
SERVICE_ID: 'claude', SERVICE_ID: 'claude',
SERVICE_AGENT_TYPE: 'claude-code',
SERVICE_SESSION_SCOPE: 'claude', SERVICE_SESSION_SCOPE: 'claude',
CODEX_MAIN_SERVICE_ID: 'codex-main', CODEX_MAIN_SERVICE_ID: 'codex-main',
CODEX_REVIEW_SERVICE_ID: 'codex-review', CODEX_REVIEW_SERVICE_ID: 'codex-review',

View File

@@ -26,7 +26,6 @@ import {
CODEX_REVIEW_SERVICE_ID, CODEX_REVIEW_SERVICE_ID,
isSessionCommandSenderAllowed, isSessionCommandSenderAllowed,
REVIEWER_AGENT_TYPE, REVIEWER_AGENT_TYPE,
SERVICE_AGENT_TYPE,
SERVICE_ID, SERVICE_ID,
} from './config.js'; } from './config.js';
import { GroupQueue, GroupRunContext } from './group-queue.js'; import { GroupQueue, GroupRunContext } from './group-queue.js';
@@ -75,7 +74,7 @@ export function isDuplicateOfLastBotFinal(
} }
// Get the last bot final message from DB (any bot, not just this service) // Get the last bot final message from DB (any bot, not just this service)
const lastMessages = getLastBotFinalMessage(chatJid, SERVICE_AGENT_TYPE, 1); const lastMessages = getLastBotFinalMessage(chatJid, 'claude-code', 1);
if (lastMessages.length === 0) { if (lastMessages.length === 0) {
return false; return false;
} }

View File

@@ -63,13 +63,8 @@ export function readPairedRoomPrompt(
return prompt || undefined; return prompt || undefined;
} }
export function getArbiterPromptPath( export function getArbiterPromptPath(projectRoot = process.cwd()): string {
projectRoot = process.cwd(), return path.join(getPlatformPromptsDir(projectRoot), ARBITER_PROMPT_FILE);
): string {
return path.join(
getPlatformPromptsDir(projectRoot),
ARBITER_PROMPT_FILE,
);
} }
export function readArbiterPrompt( export function readArbiterPrompt(

View File

@@ -5,7 +5,6 @@ import {
CODEX_REVIEW_SERVICE_ID, CODEX_REVIEW_SERVICE_ID,
OWNER_AGENT_TYPE, OWNER_AGENT_TYPE,
REVIEWER_SERVICE_ID_FOR_TYPE, REVIEWER_SERVICE_ID_FOR_TYPE,
SERVICE_AGENT_TYPE,
SERVICE_ID, SERVICE_ID,
isArbiterEnabled, isArbiterEnabled,
normalizeServiceId, normalizeServiceId,
@@ -99,9 +98,7 @@ function getDefaultLease(chatJid: string): EffectiveChannelLease {
return { return {
chat_jid: chatJid, chat_jid: chatJid,
owner_service_id: owner_service_id:
SERVICE_AGENT_TYPE === 'codex' CLAUDE_SERVICE_ID,
? CODEX_MAIN_SERVICE_ID
: CLAUDE_SERVICE_ID,
reviewer_service_id: null, reviewer_service_id: null,
arbiter_service_id: null, arbiter_service_id: null,
activated_at: null, activated_at: null,

View File

@@ -7,7 +7,6 @@ import { getErrorMessage } from './utils.js';
import { import {
ASSISTANT_NAME, ASSISTANT_NAME,
SCHEDULER_POLL_INTERVAL, SCHEDULER_POLL_INTERVAL,
SERVICE_AGENT_TYPE,
TIMEZONE, TIMEZONE,
} from './config.js'; } from './config.js';
import { import {
@@ -201,7 +200,7 @@ function resolveTaskExecutionContext(
const isMain = group.isMain === true; const isMain = group.isMain === true;
const taskAgentType = const taskAgentType =
task.agent_type || deps.serviceAgentType || SERVICE_AGENT_TYPE; task.agent_type || deps.serviceAgentType || 'claude-code';
const sessions = deps.getSessions(); const sessions = deps.getSessions();
const runtimeTaskId = getTaskRuntimeTaskId(task); const runtimeTaskId = getTaskRuntimeTaskId(task);
const useTaskScopedSession = shouldUseTaskScopedSession(task); const useTaskScopedSession = shouldUseTaskScopedSession(task);
@@ -641,7 +640,8 @@ async function runTask(
// Try token rotation before suspending // Try token rotation before suspending
if (error) { if (error) {
const isCodex = SERVICE_AGENT_TYPE === 'codex'; const effectiveAgentType = context.taskAgentType;
const isCodex = effectiveAgentType === 'codex';
if (isCodex) { if (isCodex) {
const trigger = detectCodexRotationTrigger(error); const trigger = detectCodexRotationTrigger(error);
if (trigger.shouldRotate) { if (trigger.shouldRotate) {
@@ -650,7 +650,7 @@ async function runTask(
logger.info( logger.info(
{ {
taskId: task.id, taskId: task.id,
agent: SERVICE_AGENT_TYPE, agent: effectiveAgentType,
reason: trigger.reason, reason: trigger.reason,
}, },
'Task rate-limited, rotated token — will retry on next schedule', 'Task rate-limited, rotated token — will retry on next schedule',
@@ -668,7 +668,7 @@ async function runTask(
logger.info( logger.info(
{ {
taskId: task.id, taskId: task.id,
agent: SERVICE_AGENT_TYPE, agent: effectiveAgentType,
reason: trigger.reason, reason: trigger.reason,
}, },
'Task rate-limited, rotated token — will retry on next schedule', 'Task rate-limited, rotated token — will retry on next schedule',