diff --git a/src/config.ts b/src/config.ts index e77b3a7..ae928b9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -22,6 +22,8 @@ export function normalizeServiceId(serviceId: string): string { } export const SERVICE_SESSION_SCOPE = normalizeServiceId(SERVICE_ID); +export const CURRENT_RUNTIME_AGENT_TYPE = + SERVICE_SESSION_SCOPE === CLAUDE_SERVICE_ID ? 'claude-code' : 'codex'; export function isClaudeService(serviceId: string = SERVICE_ID): boolean { return normalizeServiceId(serviceId) === CLAUDE_SERVICE_ID; diff --git a/src/db.test.ts b/src/db.test.ts index fbf8233..701ce63 100644 --- a/src/db.test.ts +++ b/src/db.test.ts @@ -92,6 +92,7 @@ import { CLAUDE_SERVICE_ID, CODEX_MAIN_SERVICE_ID, CODEX_REVIEW_SERVICE_ID, + OWNER_AGENT_TYPE, SERVICE_ID, SERVICE_SESSION_SCOPE, normalizeServiceId, @@ -1152,6 +1153,34 @@ describe('paired task state', () => { }); }); + it('backfills configured owner agent type when creating a paired task with a raw non-shadow owner service id', () => { + createPairedTask({ + id: 'paired-task-raw-owner-service', + chat_jid: 'dc:paired-raw-owner-service', + group_folder: 'paired-raw-owner-service', + owner_service_id: 'andy', + reviewer_service_id: CLAUDE_SERVICE_ID, + title: null, + source_ref: 'HEAD', + plan_notes: null, + review_requested_at: null, + round_trip_count: 0, + status: 'active', + arbiter_verdict: null, + arbiter_requested_at: null, + completion_reason: null, + created_at: '2026-03-28T00:00:00.000Z', + updated_at: '2026-03-28T00:00:00.000Z', + }); + + expect(getPairedTaskById('paired-task-raw-owner-service')).toMatchObject({ + owner_service_id: 'andy', + reviewer_service_id: CLAUDE_SERVICE_ID, + owner_agent_type: OWNER_AGENT_TYPE, + reviewer_agent_type: 'claude-code', + }); + }); + it('preserves raw legacy paired task service ids during init when registered group metadata is present', () => { const tempDir = fs.mkdtempSync('/tmp/ejclaw-paired-task-legacy-groups-'); const dbPath = path.join(tempDir, 'messages.db'); diff --git a/src/db/canonical-role-metadata.ts b/src/db/canonical-role-metadata.ts index 4915f80..14fd2ee 100644 --- a/src/db/canonical-role-metadata.ts +++ b/src/db/canonical-role-metadata.ts @@ -1,4 +1,9 @@ -import { ARBITER_AGENT_TYPE, SERVICE_SESSION_SCOPE } from '../config.js'; +import { + ARBITER_AGENT_TYPE, + OWNER_AGENT_TYPE, + REVIEWER_AGENT_TYPE, + SERVICE_SESSION_SCOPE, +} from '../config.js'; import { inferAgentTypeFromServiceShadow, inferRoleFromServiceShadow, @@ -12,6 +17,7 @@ interface RequiredRoleMetadataInput { role: PairedRoomRole; storedAgentType?: string | null; storedServiceId?: string | null; + fallbackAgentType?: AgentType | null; } interface OptionalRoleMetadataInput extends RequiredRoleMetadataInput {} @@ -31,7 +37,8 @@ function resolveRequiredAgentType(input: RequiredRoleMetadataInput): AgentType { ); } - const agentType = persistedAgentType ?? inferredAgentType; + const agentType = + persistedAgentType ?? inferredAgentType ?? input.fallbackAgentType ?? null; if (agentType) { return agentType; } @@ -100,12 +107,14 @@ export function canonicalizePairedTaskMetadata(input: { role: 'owner', storedAgentType: input.owner_agent_type, storedServiceId: input.owner_service_id, + fallbackAgentType: OWNER_AGENT_TYPE, }); const reviewerAgentType = resolveRequiredAgentType({ context, role: 'reviewer', storedAgentType: input.reviewer_agent_type, storedServiceId: input.reviewer_service_id, + fallbackAgentType: REVIEWER_AGENT_TYPE, }); return { diff --git a/src/db/paired-state.ts b/src/db/paired-state.ts index 5daaf0c..2f2584b 100644 --- a/src/db/paired-state.ts +++ b/src/db/paired-state.ts @@ -1,6 +1,10 @@ import { Database } from 'bun:sqlite'; -import { SERVICE_ID, normalizeServiceId } from '../config.js'; +import { + CURRENT_RUNTIME_AGENT_TYPE, + SERVICE_ID, + normalizeServiceId, +} from '../config.js'; import { buildPairedTurnIdentity, resolvePairedTurnRole, @@ -602,6 +606,7 @@ export function claimPairedTurnReservationInDatabase( const currentAttempt = markPairedTurnRunningInDatabase(database, { turnIdentity, executorServiceId: CURRENT_SERVICE_ID, + executorAgentType: CURRENT_RUNTIME_AGENT_TYPE, runId: args.runId, }); if (!currentAttempt) {