diff --git a/src/message-runtime-rules.ts b/src/message-runtime-rules.ts index 3ff1dc6..d7e3ac9 100644 --- a/src/message-runtime-rules.ts +++ b/src/message-runtime-rules.ts @@ -39,28 +39,30 @@ export function pairedCursorKey( return isReviewerTurn ? `${chatJid}:reviewer` : chatJid; } -/** - * Auto-resolve the cursor key based on current paired task status. - * Non-paired rooms always return chatJid. - * Paired rooms return role-specific keys so owner/reviewer/arbiter - * cursors are independent. - */ +/** Map task status to the active role. */ +export function resolveActiveRole( + taskStatus?: string | null, +): 'owner' | 'reviewer' | 'arbiter' { + switch (taskStatus) { + case 'review_ready': + case 'in_review': + return 'reviewer'; + case 'arbiter_requested': + case 'in_arbitration': + return 'arbiter'; + default: + return 'owner'; + } +} + +/** Cursor key for a role. Owner uses chatJid, others use chatJid:role. */ export function resolveCursorKey( chatJid: string, taskStatus?: string | null, ): string { if (!isPairedRoomJid(chatJid)) return chatJid; - if (!taskStatus) return chatJid; - switch (taskStatus) { - case 'review_ready': - case 'in_review': - return `${chatJid}:reviewer`; - case 'arbiter_requested': - case 'in_arbitration': - return `${chatJid}:arbiter`; - default: - return chatJid; - } + const role = resolveActiveRole(taskStatus); + return role === 'owner' ? chatJid : `${chatJid}:${role}`; } export function createImplicitContinuationTracker(idleTimeout: number) { diff --git a/src/message-runtime.ts b/src/message-runtime.ts index 7f02e7e..5d2a9fc 100644 --- a/src/message-runtime.ts +++ b/src/message-runtime.ts @@ -41,6 +41,7 @@ import { isTriggerAllowed, loadSenderAllowlist } from './sender-allowlist.js'; import { advanceLastAgentCursor, createImplicitContinuationTracker, + resolveActiveRole, resolveCursorKey, filterLoopingPairedBotMessages, getProcessableMessages, @@ -536,18 +537,13 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { const arbiterChannel = foundArbiterChannel || channel; // Resolve the correct Discord channel for a given task status. - const resolveChannel = (taskStatus?: string | null): Channel => { - switch (taskStatus) { - case 'review_ready': - case 'in_review': - return reviewerChannel; - case 'arbiter_requested': - case 'in_arbitration': - return arbiterChannel; - default: - return channel; - } + const roleToChannel: Record = { + owner: channel, + reviewer: reviewerChannel, + arbiter: arbiterChannel, }; + const resolveChannel = (taskStatus?: string | null): Channel => + roleToChannel[resolveActiveRole(taskStatus)] ?? channel; if (isPairedRoomJid(chatJid)) { logger.info( @@ -832,8 +828,7 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { // Arbiter turns use a dedicated context prompt; regular turns use formatted messages. const isArbiterTurn = - taskStatus === 'arbiter_requested' || - taskStatus === 'in_arbitration'; + taskStatus === 'arbiter_requested' || taskStatus === 'in_arbitration'; let prompt: string; if (isArbiterTurn && pendingTaskForChannel) { const arbiterMsgs = labelPairedSenders(