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:
@@ -9,7 +9,7 @@ import path from 'path';
|
||||
|
||||
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 { logger } from '../src/logger.js';
|
||||
import { emitStatus } from './status.js';
|
||||
@@ -156,7 +156,7 @@ export async function run(args: string[]): Promise<void> {
|
||||
timestamp,
|
||||
requiresTriggerInt,
|
||||
isMainInt,
|
||||
SERVICE_AGENT_TYPE,
|
||||
'claude-code',
|
||||
);
|
||||
|
||||
db.close();
|
||||
|
||||
@@ -530,7 +530,13 @@ export function prepareContainerSessionEnvironment(args: {
|
||||
memoryBriefing?: string;
|
||||
role?: 'reviewer' | 'arbiter';
|
||||
}): void {
|
||||
const { sessionDir, chatJid, isMain, memoryBriefing, role = 'reviewer' } = args;
|
||||
const {
|
||||
sessionDir,
|
||||
chatJid,
|
||||
isMain,
|
||||
memoryBriefing,
|
||||
role = 'reviewer',
|
||||
} = args;
|
||||
const projectRoot = process.cwd();
|
||||
|
||||
fs.mkdirSync(sessionDir, { recursive: true });
|
||||
@@ -546,9 +552,9 @@ export function prepareContainerSessionEnvironment(args: {
|
||||
// Build CLAUDE.md with role-appropriate prompts (reviewer or arbiter)
|
||||
const claudePlatformPrompt = readPlatformPrompt('claude-code', projectRoot);
|
||||
const claudePairedRoomPrompt = isPairedRoomJid(chatJid)
|
||||
? (role === 'arbiter'
|
||||
? readArbiterPrompt(projectRoot)
|
||||
: readPairedRoomPrompt('claude-code', projectRoot))
|
||||
? role === 'arbiter'
|
||||
? readArbiterPrompt(projectRoot)
|
||||
: readPairedRoomPrompt('claude-code', projectRoot)
|
||||
: undefined;
|
||||
const globalDir = path.join(GROUPS_DIR, 'global');
|
||||
const globalClaudeMdPath = path.join(globalDir, 'CLAUDE.md');
|
||||
|
||||
@@ -75,7 +75,10 @@ export async function runAgentProcess(
|
||||
// ── Reviewer container mode ─────────────────────────────────────
|
||||
// Reviewers always run inside a Docker container with read-only source
|
||||
// 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 =
|
||||
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.
|
||||
const sessionDir = envOverrides?.CLAUDE_CONFIG_DIR;
|
||||
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({
|
||||
sessionDir,
|
||||
chatJid: input.chatJid,
|
||||
|
||||
@@ -8,19 +8,12 @@ export const ASSISTANT_NAME = getEnv('ASSISTANT_NAME') || 'Andy';
|
||||
export const ASSISTANT_HAS_OWN_NUMBER =
|
||||
getEnv('ASSISTANT_HAS_OWN_NUMBER') === 'true';
|
||||
const ASSISTANT_SLUG = ASSISTANT_NAME.trim().toLowerCase();
|
||||
const rawServiceAgentType = getEnv('SERVICE_AGENT_TYPE');
|
||||
export const SERVICE_ID = getEnv('SERVICE_ID') || ASSISTANT_SLUG;
|
||||
export const CLAUDE_SERVICE_ID = getEnv('CLAUDE_SERVICE_ID') || 'claude';
|
||||
export const CODEX_MAIN_SERVICE_ID =
|
||||
getEnv('CODEX_MAIN_SERVICE_ID') || 'codex-main';
|
||||
export const CODEX_REVIEW_SERVICE_ID =
|
||||
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 {
|
||||
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. */
|
||||
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;
|
||||
|
||||
/** Number of consecutive owner↔reviewer round trips before arbiter is auto-requested. */
|
||||
|
||||
35
src/db.ts
35
src/db.ts
@@ -9,7 +9,6 @@ import {
|
||||
CODEX_REVIEW_SERVICE_ID,
|
||||
DATA_DIR,
|
||||
normalizeServiceId,
|
||||
SERVICE_AGENT_TYPE,
|
||||
SERVICE_ID,
|
||||
SERVICE_SESSION_SCOPE,
|
||||
STORE_DIR,
|
||||
@@ -729,7 +728,7 @@ function createSchema(database: Database.Database): void {
|
||||
`INSERT INTO sessions_new (group_folder, agent_type, session_id)
|
||||
SELECT group_folder, ?, session_id FROM sessions`,
|
||||
)
|
||||
.run(SERVICE_AGENT_TYPE);
|
||||
.run('claude-code');
|
||||
database.exec(`
|
||||
DROP TABLE sessions;
|
||||
ALTER TABLE sessions_new RENAME TO sessions;
|
||||
@@ -1151,7 +1150,7 @@ export function hasRecentRestartAnnouncement(
|
||||
|
||||
export function getOpenWorkItem(
|
||||
chatJid: string,
|
||||
agentType: AgentType = SERVICE_AGENT_TYPE,
|
||||
agentType: AgentType = 'claude-code',
|
||||
serviceId: string = SERVICE_SESSION_SCOPE,
|
||||
): WorkItem | undefined {
|
||||
return db
|
||||
@@ -1176,7 +1175,7 @@ export function createProducedWorkItem(input: {
|
||||
result_payload: string;
|
||||
}): WorkItem {
|
||||
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 result = db
|
||||
.prepare(
|
||||
@@ -1267,7 +1266,7 @@ export function createTask(
|
||||
task.id,
|
||||
task.group_folder,
|
||||
task.chat_jid,
|
||||
task.agent_type || SERVICE_AGENT_TYPE,
|
||||
task.agent_type || 'claude-code',
|
||||
task.ci_provider ?? null,
|
||||
task.ci_metadata ?? null,
|
||||
task.max_duration_ms ?? null,
|
||||
@@ -1596,7 +1595,10 @@ export function setRouterStateForService(
|
||||
|
||||
// --- Session accessors ---
|
||||
|
||||
export function getSession(groupFolder: string): string | undefined {
|
||||
export function getSession(
|
||||
groupFolder: string,
|
||||
agentType: AgentType = 'claude-code',
|
||||
): string | undefined {
|
||||
const serviceScopedRow = db
|
||||
.prepare(
|
||||
'SELECT session_id FROM service_sessions WHERE group_folder = ? AND service_id = ?',
|
||||
@@ -1612,26 +1614,33 @@ export function getSession(groupFolder: string): string | undefined {
|
||||
.prepare(
|
||||
'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;
|
||||
}
|
||||
|
||||
export function setSession(groupFolder: string, sessionId: string): void {
|
||||
export function setSession(
|
||||
groupFolder: string,
|
||||
sessionId: string,
|
||||
agentType: AgentType = 'claude-code',
|
||||
): void {
|
||||
db.prepare(
|
||||
'INSERT OR REPLACE INTO service_sessions (group_folder, service_id, session_id) VALUES (?, ?, ?)',
|
||||
).run(groupFolder, SERVICE_SESSION_SCOPE, sessionId);
|
||||
db.prepare(
|
||||
'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(
|
||||
'DELETE FROM service_sessions WHERE group_folder = ? AND service_id = ?',
|
||||
).run(groupFolder, SERVICE_SESSION_SCOPE);
|
||||
db.prepare(
|
||||
'DELETE FROM sessions WHERE group_folder = ? AND agent_type = ?',
|
||||
).run(groupFolder, SERVICE_AGENT_TYPE);
|
||||
).run(groupFolder, agentType);
|
||||
}
|
||||
|
||||
export function deleteAllSessionsForGroup(groupFolder: string): void {
|
||||
@@ -1662,7 +1671,7 @@ export function getAllSessions(): Record<string, string> {
|
||||
.prepare(
|
||||
'SELECT group_folder, session_id FROM sessions WHERE agent_type = ?',
|
||||
)
|
||||
.all(SERVICE_AGENT_TYPE) as Array<{
|
||||
.all('claude-code') as Array<{
|
||||
group_folder: string;
|
||||
session_id: string;
|
||||
}>;
|
||||
@@ -2125,7 +2134,7 @@ export function listPairedWorkspacesForTask(taskId: string): PairedWorkspace[] {
|
||||
*/
|
||||
export function getLastBotFinalMessage(
|
||||
chatJid: string,
|
||||
_agentType: AgentType = SERVICE_AGENT_TYPE,
|
||||
_agentType: AgentType = 'claude-code',
|
||||
limit: number = 1,
|
||||
): Array<{ content: string; timestamp: string }> {
|
||||
const rows = db
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
POLL_INTERVAL,
|
||||
REVIEWER_AGENT_TYPE,
|
||||
SERVICE_ID,
|
||||
SERVICE_AGENT_TYPE,
|
||||
isSessionCommandSenderAllowed,
|
||||
STATUS_CHANNEL_ID,
|
||||
STATUS_UPDATE_INTERVAL,
|
||||
@@ -194,7 +193,7 @@ function loadState(): void {
|
||||
logger.info(
|
||||
{
|
||||
groupCount: Object.keys(registeredGroups).length,
|
||||
agentType: SERVICE_AGENT_TYPE,
|
||||
agentType: 'unified',
|
||||
},
|
||||
'State loaded',
|
||||
);
|
||||
@@ -520,7 +519,7 @@ async function main(): Promise<void> {
|
||||
await startUnifiedDashboard({
|
||||
assistantName: ASSISTANT_NAME,
|
||||
serviceId: SERVICE_ID,
|
||||
serviceAgentType: SERVICE_AGENT_TYPE,
|
||||
serviceAgentType: 'claude-code',
|
||||
statusChannelId: STATUS_CHANNEL_ID,
|
||||
statusUpdateInterval: STATUS_UPDATE_INTERVAL,
|
||||
usageUpdateInterval: USAGE_UPDATE_INTERVAL,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { CronExpressionParser } from 'cron-parser';
|
||||
import {
|
||||
DATA_DIR,
|
||||
IPC_POLL_INTERVAL,
|
||||
SERVICE_AGENT_TYPE,
|
||||
TIMEZONE,
|
||||
} from './config.js';
|
||||
import { readJsonFile } from './utils.js';
|
||||
@@ -433,7 +432,7 @@ export async function processTaskIpc(
|
||||
id: taskId,
|
||||
group_folder: targetFolder,
|
||||
chat_jid: resolvedTargetJid,
|
||||
agent_type: targetGroupEntry.agentType || SERVICE_AGENT_TYPE,
|
||||
agent_type: targetGroupEntry.agentType || 'claude-code',
|
||||
ci_provider: data.ci_provider ?? null,
|
||||
ci_metadata: data.ci_metadata ?? null,
|
||||
max_duration_ms: isWatchCiTask({ prompt: data.prompt })
|
||||
@@ -453,7 +452,7 @@ export async function processTaskIpc(
|
||||
sourceGroup,
|
||||
targetFolder,
|
||||
contextMode,
|
||||
agentType: targetGroupEntry.agentType || SERVICE_AGENT_TYPE,
|
||||
agentType: targetGroupEntry.agentType || 'claude-code',
|
||||
},
|
||||
'Task created via IPC',
|
||||
);
|
||||
|
||||
@@ -14,7 +14,6 @@ vi.mock('./agent-runner.js', () => ({
|
||||
vi.mock('./config.js', () => ({
|
||||
DATA_DIR: '/tmp/ejclaw-test-data',
|
||||
SERVICE_ID: 'claude',
|
||||
SERVICE_AGENT_TYPE: 'claude-code',
|
||||
SERVICE_SESSION_SCOPE: 'claude',
|
||||
CODEX_MAIN_SERVICE_ID: 'codex-main',
|
||||
CODEX_REVIEW_SERVICE_ID: 'codex-review',
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
CODEX_REVIEW_SERVICE_ID,
|
||||
isSessionCommandSenderAllowed,
|
||||
REVIEWER_AGENT_TYPE,
|
||||
SERVICE_AGENT_TYPE,
|
||||
SERVICE_ID,
|
||||
} from './config.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)
|
||||
const lastMessages = getLastBotFinalMessage(chatJid, SERVICE_AGENT_TYPE, 1);
|
||||
const lastMessages = getLastBotFinalMessage(chatJid, 'claude-code', 1);
|
||||
if (lastMessages.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -63,13 +63,8 @@ export function readPairedRoomPrompt(
|
||||
return prompt || undefined;
|
||||
}
|
||||
|
||||
export function getArbiterPromptPath(
|
||||
projectRoot = process.cwd(),
|
||||
): string {
|
||||
return path.join(
|
||||
getPlatformPromptsDir(projectRoot),
|
||||
ARBITER_PROMPT_FILE,
|
||||
);
|
||||
export function getArbiterPromptPath(projectRoot = process.cwd()): string {
|
||||
return path.join(getPlatformPromptsDir(projectRoot), ARBITER_PROMPT_FILE);
|
||||
}
|
||||
|
||||
export function readArbiterPrompt(
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
CODEX_REVIEW_SERVICE_ID,
|
||||
OWNER_AGENT_TYPE,
|
||||
REVIEWER_SERVICE_ID_FOR_TYPE,
|
||||
SERVICE_AGENT_TYPE,
|
||||
SERVICE_ID,
|
||||
isArbiterEnabled,
|
||||
normalizeServiceId,
|
||||
@@ -99,9 +98,7 @@ function getDefaultLease(chatJid: string): EffectiveChannelLease {
|
||||
return {
|
||||
chat_jid: chatJid,
|
||||
owner_service_id:
|
||||
SERVICE_AGENT_TYPE === 'codex'
|
||||
? CODEX_MAIN_SERVICE_ID
|
||||
: CLAUDE_SERVICE_ID,
|
||||
CLAUDE_SERVICE_ID,
|
||||
reviewer_service_id: null,
|
||||
arbiter_service_id: null,
|
||||
activated_at: null,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { getErrorMessage } from './utils.js';
|
||||
import {
|
||||
ASSISTANT_NAME,
|
||||
SCHEDULER_POLL_INTERVAL,
|
||||
SERVICE_AGENT_TYPE,
|
||||
TIMEZONE,
|
||||
} from './config.js';
|
||||
import {
|
||||
@@ -201,7 +200,7 @@ function resolveTaskExecutionContext(
|
||||
|
||||
const isMain = group.isMain === true;
|
||||
const taskAgentType =
|
||||
task.agent_type || deps.serviceAgentType || SERVICE_AGENT_TYPE;
|
||||
task.agent_type || deps.serviceAgentType || 'claude-code';
|
||||
const sessions = deps.getSessions();
|
||||
const runtimeTaskId = getTaskRuntimeTaskId(task);
|
||||
const useTaskScopedSession = shouldUseTaskScopedSession(task);
|
||||
@@ -641,7 +640,8 @@ async function runTask(
|
||||
|
||||
// Try token rotation before suspending
|
||||
if (error) {
|
||||
const isCodex = SERVICE_AGENT_TYPE === 'codex';
|
||||
const effectiveAgentType = context.taskAgentType;
|
||||
const isCodex = effectiveAgentType === 'codex';
|
||||
if (isCodex) {
|
||||
const trigger = detectCodexRotationTrigger(error);
|
||||
if (trigger.shouldRotate) {
|
||||
@@ -650,7 +650,7 @@ async function runTask(
|
||||
logger.info(
|
||||
{
|
||||
taskId: task.id,
|
||||
agent: SERVICE_AGENT_TYPE,
|
||||
agent: effectiveAgentType,
|
||||
reason: trigger.reason,
|
||||
},
|
||||
'Task rate-limited, rotated token — will retry on next schedule',
|
||||
@@ -668,7 +668,7 @@ async function runTask(
|
||||
logger.info(
|
||||
{
|
||||
taskId: task.id,
|
||||
agent: SERVICE_AGENT_TYPE,
|
||||
agent: effectiveAgentType,
|
||||
reason: trigger.reason,
|
||||
},
|
||||
'Task rate-limited, rotated token — will retry on next schedule',
|
||||
|
||||
Reference in New Issue
Block a user