refactor: split message executor target preparation (#190)
This commit is contained in:
@@ -109,11 +109,6 @@
|
|||||||
"maxFunctionLines": 314,
|
"maxFunctionLines": 314,
|
||||||
"owner": "main service entrypoint hotspot"
|
"owner": "main service entrypoint hotspot"
|
||||||
},
|
},
|
||||||
"src/message-agent-executor-target.ts": {
|
|
||||||
"maxFunctionLines": 274,
|
|
||||||
"maxComplexity": 48,
|
|
||||||
"owner": "message executor target hotspot"
|
|
||||||
},
|
|
||||||
"src/message-agent-executor.test.ts": {
|
"src/message-agent-executor.test.ts": {
|
||||||
"maxLines": 3847,
|
"maxLines": 3847,
|
||||||
"maxFunctionLines": 2500,
|
"maxFunctionLines": 2500,
|
||||||
|
|||||||
@@ -16,24 +16,24 @@ import {
|
|||||||
markPairedTurnRunning,
|
markPairedTurnRunning,
|
||||||
} from './db.js';
|
} from './db.js';
|
||||||
import { createScopedLogger } from './logger.js';
|
import { createScopedLogger } from './logger.js';
|
||||||
import { buildRoomMemoryBriefing } from './sqlite-memory-store.js';
|
import { resolveExecutionTarget } from './message-runtime-rules.js';
|
||||||
import type { PreparedPairedExecutionContext } from './paired-execution-context.js';
|
import type { PreparedPairedExecutionContext } from './paired-execution-context.js';
|
||||||
import { preparePairedExecutionContext } from './paired-execution-context.js';
|
import { preparePairedExecutionContext } from './paired-execution-context.js';
|
||||||
import {
|
import {
|
||||||
resolveRuntimePairedTurnIdentity,
|
resolveRuntimePairedTurnIdentity,
|
||||||
type PairedTurnIdentity,
|
type PairedTurnIdentity,
|
||||||
} from './paired-turn-identity.js';
|
} from './paired-turn-identity.js';
|
||||||
import { resolveExecutionTarget } from './message-runtime-rules.js';
|
|
||||||
import { buildRoomRoleContext } from './room-role-context.js';
|
import { buildRoomRoleContext } from './room-role-context.js';
|
||||||
import { getEffectiveChannelLease } from './service-routing.js';
|
import { getEffectiveChannelLease } from './service-routing.js';
|
||||||
|
import { buildRoomMemoryBriefing } from './sqlite-memory-store.js';
|
||||||
import { getTokenCount } from './token-rotation.js';
|
import { getTokenCount } from './token-rotation.js';
|
||||||
import { writeGroupsSnapshot, writeTasksSnapshot } from './agent-runner.js';
|
|
||||||
import type {
|
import type {
|
||||||
AgentType,
|
AgentType,
|
||||||
PairedRoomRole,
|
PairedRoomRole,
|
||||||
RegisteredGroup,
|
RegisteredGroup,
|
||||||
RoomRoleContext,
|
RoomRoleContext,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
import { writeGroupsSnapshot, writeTasksSnapshot } from './agent-runner.js';
|
||||||
|
|
||||||
interface ExecutionTargetDeps {
|
interface ExecutionTargetDeps {
|
||||||
assistantName: string;
|
assistantName: string;
|
||||||
@@ -79,77 +79,201 @@ export interface PreparedMessageAgentExecution {
|
|||||||
clearRoleSdkSessions: () => void;
|
clearRoleSdkSessions: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CurrentChannelLease = ReturnType<typeof getEffectiveChannelLease>;
|
||||||
|
type LatestOpenPairedTask = ReturnType<typeof getLatestOpenPairedTaskForChat>;
|
||||||
|
type ResolvedExecutionTarget = ReturnType<typeof resolveExecutionTarget>;
|
||||||
|
|
||||||
|
interface ResolvedRoleExecutionState {
|
||||||
|
reviewerMode: boolean;
|
||||||
|
arbiterMode: boolean;
|
||||||
|
effectiveGroup: RegisteredGroup;
|
||||||
|
isClaudeCodeAgent: boolean;
|
||||||
|
forceFreshClaudeReviewerSession: boolean;
|
||||||
|
shouldPersistSession: boolean;
|
||||||
|
currentSessionId: string | undefined;
|
||||||
|
provider: 'claude' | 'codex';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RuntimePairedTurnResolution {
|
||||||
|
preparedTurnTaskUpdatedAt: string | undefined;
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export async function prepareMessageAgentExecutionTarget(
|
export async function prepareMessageAgentExecutionTarget(
|
||||||
deps: ExecutionTargetDeps,
|
deps: ExecutionTargetDeps,
|
||||||
args: ExecutionTargetArgs,
|
args: ExecutionTargetArgs,
|
||||||
): Promise<PreparedMessageAgentExecution> {
|
): Promise<PreparedMessageAgentExecution> {
|
||||||
const { group, chatJid, runId, startSeq, endSeq } = args;
|
return new MessageAgentExecutionTargetPreparer(deps, args).run();
|
||||||
const isMain = group.isMain === true;
|
}
|
||||||
const sessions = deps.getSessions();
|
|
||||||
|
|
||||||
const currentLease = getEffectiveChannelLease(chatJid);
|
class MessageAgentExecutionTargetPreparer {
|
||||||
const pairedTask = currentLease.reviewer_agent_type
|
constructor(
|
||||||
? getLatestOpenPairedTaskForChat(chatJid)
|
private readonly deps: ExecutionTargetDeps,
|
||||||
|
private readonly args: ExecutionTargetArgs,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async run(): Promise<PreparedMessageAgentExecution> {
|
||||||
|
const currentLease = getEffectiveChannelLease(this.args.chatJid);
|
||||||
|
const pairedTask = this.resolveLatestPairedTask(currentLease);
|
||||||
|
const executionTarget = this.resolveTarget(currentLease, pairedTask);
|
||||||
|
const roleState = this.resolveRoleExecutionState(executionTarget);
|
||||||
|
const memoryBriefing = await this.resolveMemoryBriefing(
|
||||||
|
roleState.currentSessionId,
|
||||||
|
);
|
||||||
|
this.writeAgentSnapshots(executionTarget.roleAgentPlan.ownerAgentType);
|
||||||
|
const canRetryClaudeCredentials = this.canRetryClaudeCredentials(
|
||||||
|
roleState.isClaudeCodeAgent,
|
||||||
|
);
|
||||||
|
const roomRoleContext = buildRoomRoleContext(
|
||||||
|
currentLease,
|
||||||
|
executionTarget.effectiveServiceId,
|
||||||
|
executionTarget.activeRole,
|
||||||
|
);
|
||||||
|
const pairedExecutionContext = this.preparePairedContext(roomRoleContext);
|
||||||
|
const runtimeTurn = this.resolveRuntimePairedTurnIdentity({
|
||||||
|
pairedTask,
|
||||||
|
pairedExecutionContext,
|
||||||
|
activeRole: executionTarget.activeRole,
|
||||||
|
});
|
||||||
|
this.validateRuntimePairedTurnIdentity({
|
||||||
|
pairedTask,
|
||||||
|
pairedExecutionContext,
|
||||||
|
activeRole: executionTarget.activeRole,
|
||||||
|
runtimeTurn,
|
||||||
|
});
|
||||||
|
this.markRuntimePairedTurnRunning(
|
||||||
|
runtimeTurn.runtimePairedTurnIdentity,
|
||||||
|
executionTarget,
|
||||||
|
);
|
||||||
|
this.applyPairedEnvOverrides({
|
||||||
|
pairedExecutionContext,
|
||||||
|
runtimePairedTurnIdentity: runtimeTurn.runtimePairedTurnIdentity,
|
||||||
|
activeRole: executionTarget.activeRole,
|
||||||
|
isClaudeCodeAgent: roleState.isClaudeCodeAgent,
|
||||||
|
});
|
||||||
|
const log = this.createExecutionLogger({
|
||||||
|
currentLease,
|
||||||
|
pairedTask,
|
||||||
|
executionTarget,
|
||||||
|
roleState,
|
||||||
|
runtimePairedTurnIdentity: runtimeTurn.runtimePairedTurnIdentity,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentLease,
|
||||||
|
pairedTask,
|
||||||
|
activeRole: executionTarget.activeRole,
|
||||||
|
effectiveServiceId: executionTarget.effectiveServiceId,
|
||||||
|
effectiveAgentType: executionTarget.effectiveAgentType,
|
||||||
|
sessionFolder: executionTarget.sessionFolder,
|
||||||
|
reviewerMode: roleState.reviewerMode,
|
||||||
|
arbiterMode: roleState.arbiterMode,
|
||||||
|
effectiveGroup: roleState.effectiveGroup,
|
||||||
|
isClaudeCodeAgent: roleState.isClaudeCodeAgent,
|
||||||
|
forceFreshClaudeReviewerSession:
|
||||||
|
roleState.forceFreshClaudeReviewerSession,
|
||||||
|
shouldPersistSession: roleState.shouldPersistSession,
|
||||||
|
currentSessionId: roleState.currentSessionId,
|
||||||
|
provider: roleState.provider,
|
||||||
|
memoryBriefing,
|
||||||
|
canRetryClaudeCredentials,
|
||||||
|
roomRoleContext,
|
||||||
|
pairedExecutionContext,
|
||||||
|
runtimePairedTurnIdentity: runtimeTurn.runtimePairedTurnIdentity,
|
||||||
|
log,
|
||||||
|
clearRoleSdkSessions: this.createClearRoleSdkSessions(
|
||||||
|
pairedExecutionContext,
|
||||||
|
log,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveLatestPairedTask(
|
||||||
|
currentLease: CurrentChannelLease,
|
||||||
|
): LatestOpenPairedTask | null {
|
||||||
|
return currentLease.reviewer_agent_type
|
||||||
|
? getLatestOpenPairedTaskForChat(this.args.chatJid)
|
||||||
: null;
|
: null;
|
||||||
const executionTarget = resolveExecutionTarget({
|
}
|
||||||
|
|
||||||
|
private resolveTarget(
|
||||||
|
currentLease: CurrentChannelLease,
|
||||||
|
pairedTask: LatestOpenPairedTask | null,
|
||||||
|
): ResolvedExecutionTarget {
|
||||||
|
return resolveExecutionTarget({
|
||||||
lease: currentLease,
|
lease: currentLease,
|
||||||
pairedTaskStatus: pairedTask?.status,
|
pairedTaskStatus: pairedTask?.status,
|
||||||
groupFolder: group.folder,
|
groupFolder: this.args.group.folder,
|
||||||
groupAgentType: group.agentType,
|
groupAgentType: this.args.group.agentType,
|
||||||
forcedRole: args.forcedRole,
|
forcedRole: this.args.forcedRole,
|
||||||
forcedAgentType: args.forcedAgentType,
|
forcedAgentType: this.args.forcedAgentType,
|
||||||
});
|
});
|
||||||
const {
|
}
|
||||||
inferredRole,
|
|
||||||
canHonorForcedRole,
|
private resolveRoleExecutionState(
|
||||||
activeRole,
|
executionTarget: ResolvedExecutionTarget,
|
||||||
effectiveServiceId,
|
): ResolvedRoleExecutionState {
|
||||||
reviewerServiceId,
|
const reviewerMode = executionTarget.activeRole === 'reviewer';
|
||||||
arbiterServiceId,
|
const arbiterMode = executionTarget.activeRole === 'arbiter';
|
||||||
roleAgentPlan,
|
|
||||||
configuredAgentType,
|
|
||||||
effectiveAgentType,
|
|
||||||
sessionFolder,
|
|
||||||
} = executionTarget;
|
|
||||||
const reviewerMode = activeRole === 'reviewer';
|
|
||||||
const arbiterMode = activeRole === 'arbiter';
|
|
||||||
const effectiveGroup =
|
const effectiveGroup =
|
||||||
effectiveAgentType !== roleAgentPlan.ownerAgentType
|
executionTarget.effectiveAgentType !==
|
||||||
? { ...group, agentType: effectiveAgentType }
|
executionTarget.roleAgentPlan.ownerAgentType
|
||||||
: group;
|
? {
|
||||||
const isClaudeCodeAgent = effectiveAgentType === 'claude-code';
|
...this.args.group,
|
||||||
const unsafeHostPairedMode = isUnsafeHostPairedModeEnabled();
|
agentType: executionTarget.effectiveAgentType,
|
||||||
|
}
|
||||||
|
: this.args.group;
|
||||||
|
const isClaudeCodeAgent =
|
||||||
|
executionTarget.effectiveAgentType === 'claude-code';
|
||||||
const forceFreshClaudeReviewerSession =
|
const forceFreshClaudeReviewerSession =
|
||||||
reviewerMode &&
|
reviewerMode &&
|
||||||
isClaudeCodeAgent &&
|
isClaudeCodeAgent &&
|
||||||
unsafeHostPairedMode &&
|
isUnsafeHostPairedModeEnabled() &&
|
||||||
shouldForceFreshClaudeReviewerSessionInUnsafeHostMode();
|
shouldForceFreshClaudeReviewerSessionInUnsafeHostMode();
|
||||||
const shouldPersistSession =
|
const shouldPersistSession =
|
||||||
activeRole !== 'arbiter' &&
|
executionTarget.activeRole !== 'arbiter' &&
|
||||||
!args.forcedAgentType &&
|
!this.args.forcedAgentType &&
|
||||||
!forceFreshClaudeReviewerSession;
|
!forceFreshClaudeReviewerSession;
|
||||||
const storedSessionId = sessions[sessionFolder];
|
const storedSessionId =
|
||||||
|
this.deps.getSessions()[executionTarget.sessionFolder];
|
||||||
if (forceFreshClaudeReviewerSession && storedSessionId) {
|
if (forceFreshClaudeReviewerSession && storedSessionId) {
|
||||||
deps.clearSession(sessionFolder);
|
this.deps.clearSession(executionTarget.sessionFolder);
|
||||||
}
|
}
|
||||||
const currentSessionId =
|
const currentSessionId =
|
||||||
activeRole === 'arbiter' ||
|
executionTarget.activeRole === 'arbiter' ||
|
||||||
args.forcedAgentType ||
|
this.args.forcedAgentType ||
|
||||||
forceFreshClaudeReviewerSession
|
forceFreshClaudeReviewerSession
|
||||||
? undefined
|
? undefined
|
||||||
: storedSessionId;
|
: storedSessionId;
|
||||||
const provider = isClaudeCodeAgent ? 'claude' : 'codex';
|
return {
|
||||||
const memoryBriefing = currentSessionId
|
reviewerMode,
|
||||||
? undefined
|
arbiterMode,
|
||||||
: await buildRoomMemoryBriefing({
|
effectiveGroup,
|
||||||
groupFolder: group.folder,
|
isClaudeCodeAgent,
|
||||||
groupName: group.name,
|
forceFreshClaudeReviewerSession,
|
||||||
}).catch(() => undefined);
|
shouldPersistSession,
|
||||||
|
currentSessionId,
|
||||||
|
provider: isClaudeCodeAgent ? 'claude' : 'codex',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const tasks = getAllTasks(roleAgentPlan.ownerAgentType);
|
private async resolveMemoryBriefing(
|
||||||
|
currentSessionId: string | undefined,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
if (currentSessionId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return buildRoomMemoryBriefing({
|
||||||
|
groupFolder: this.args.group.folder,
|
||||||
|
groupName: this.args.group.name,
|
||||||
|
}).catch(() => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private writeAgentSnapshots(ownerAgentType: AgentType): void {
|
||||||
|
const tasks = getAllTasks(ownerAgentType);
|
||||||
writeTasksSnapshot(
|
writeTasksSnapshot(
|
||||||
group.folder,
|
this.args.group.folder,
|
||||||
isMain,
|
this.args.group.isMain === true,
|
||||||
tasks.map((task) => ({
|
tasks.map((task) => ({
|
||||||
id: task.id,
|
id: task.id,
|
||||||
groupFolder: task.group_folder,
|
groupFolder: task.group_folder,
|
||||||
@@ -160,196 +284,280 @@ export async function prepareMessageAgentExecutionTarget(
|
|||||||
next_run: task.next_run,
|
next_run: task.next_run,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
writeGroupsSnapshot(
|
writeGroupsSnapshot(
|
||||||
group.folder,
|
this.args.group.folder,
|
||||||
isMain,
|
this.args.group.isMain === true,
|
||||||
listAvailableGroups(deps.getRoomBindings()),
|
listAvailableGroups(this.deps.getRoomBindings()),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const claudeTokenCount = isClaudeCodeAgent ? getTokenCount() : 0;
|
private canRetryClaudeCredentials(isClaudeCodeAgent: boolean): boolean {
|
||||||
const canRetryClaudeCredentials = isClaudeCodeAgent && claudeTokenCount > 0;
|
return isClaudeCodeAgent && getTokenCount() > 0;
|
||||||
const roomRoleContext = buildRoomRoleContext(
|
}
|
||||||
currentLease,
|
|
||||||
effectiveServiceId,
|
private preparePairedContext(
|
||||||
activeRole,
|
roomRoleContext: RoomRoleContext | undefined,
|
||||||
);
|
): PreparedPairedExecutionContext | undefined {
|
||||||
const pairedExecutionContext = preparePairedExecutionContext({
|
return preparePairedExecutionContext({
|
||||||
group,
|
group: this.args.group,
|
||||||
chatJid,
|
chatJid: this.args.chatJid,
|
||||||
runId,
|
runId: this.args.runId,
|
||||||
roomRoleContext,
|
roomRoleContext,
|
||||||
hasHumanMessage: args.hasHumanMessage,
|
hasHumanMessage: this.args.hasHumanMessage,
|
||||||
pairedTurnIdentity: args.pairedTurnIdentity,
|
pairedTurnIdentity: this.args.pairedTurnIdentity,
|
||||||
});
|
});
|
||||||
const preparedTurnTaskUpdatedAt = pairedExecutionContext
|
}
|
||||||
? (pairedExecutionContext.claimedTaskUpdatedAt ??
|
|
||||||
pairedExecutionContext.task.updated_at)
|
private resolveRuntimePairedTurnIdentity(args: {
|
||||||
|
pairedTask: LatestOpenPairedTask | null;
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
activeRole: PairedRoomRole;
|
||||||
|
}): RuntimePairedTurnResolution {
|
||||||
|
const preparedTurnTaskUpdatedAt = args.pairedExecutionContext
|
||||||
|
? (args.pairedExecutionContext.claimedTaskUpdatedAt ??
|
||||||
|
args.pairedExecutionContext.task.updated_at)
|
||||||
: undefined;
|
: undefined;
|
||||||
const runtimePairedTurnIdentity =
|
const runtimePairedTurnIdentity =
|
||||||
args.pairedTurnIdentity ??
|
this.args.pairedTurnIdentity ??
|
||||||
(pairedExecutionContext
|
this.resolveCurrentRuntimePairedTurnIdentity({
|
||||||
? resolveRuntimePairedTurnIdentity({
|
...args,
|
||||||
taskId: pairedExecutionContext.task.id,
|
preparedTurnTaskUpdatedAt,
|
||||||
|
});
|
||||||
|
return { preparedTurnTaskUpdatedAt, runtimePairedTurnIdentity };
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveCurrentRuntimePairedTurnIdentity(args: {
|
||||||
|
pairedTask: LatestOpenPairedTask | null;
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
activeRole: PairedRoomRole;
|
||||||
|
preparedTurnTaskUpdatedAt: string | undefined;
|
||||||
|
}): PairedTurnIdentity | undefined {
|
||||||
|
if (args.pairedExecutionContext) {
|
||||||
|
return resolveRuntimePairedTurnIdentity({
|
||||||
|
taskId: args.pairedExecutionContext.task.id,
|
||||||
taskUpdatedAt:
|
taskUpdatedAt:
|
||||||
preparedTurnTaskUpdatedAt ?? pairedExecutionContext.task.updated_at,
|
args.preparedTurnTaskUpdatedAt ??
|
||||||
role: activeRole,
|
args.pairedExecutionContext.task.updated_at,
|
||||||
taskStatus: pairedExecutionContext.task.status,
|
role: args.activeRole,
|
||||||
hasHumanMessage: args.hasHumanMessage,
|
taskStatus: args.pairedExecutionContext.task.status,
|
||||||
})
|
hasHumanMessage: this.args.hasHumanMessage,
|
||||||
: pairedTask
|
});
|
||||||
? resolveRuntimePairedTurnIdentity({
|
|
||||||
taskId: pairedTask.id,
|
|
||||||
taskUpdatedAt: pairedTask.updated_at,
|
|
||||||
role: activeRole,
|
|
||||||
taskStatus: pairedTask.status,
|
|
||||||
hasHumanMessage: args.hasHumanMessage,
|
|
||||||
})
|
|
||||||
: undefined);
|
|
||||||
|
|
||||||
if (runtimePairedTurnIdentity) {
|
|
||||||
if (runtimePairedTurnIdentity.role !== activeRole) {
|
|
||||||
throw new Error(
|
|
||||||
`Paired turn ${runtimePairedTurnIdentity.turnId} cannot execute as ${activeRole}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (
|
if (!args.pairedTask) {
|
||||||
pairedExecutionContext &&
|
return undefined;
|
||||||
runtimePairedTurnIdentity.taskId !== pairedExecutionContext.task.id
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
`Paired turn ${runtimePairedTurnIdentity.turnId} task_id does not match the prepared execution context`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (
|
return resolveRuntimePairedTurnIdentity({
|
||||||
pairedExecutionContext &&
|
taskId: args.pairedTask.id,
|
||||||
runtimePairedTurnIdentity.taskUpdatedAt !== preparedTurnTaskUpdatedAt
|
taskUpdatedAt: args.pairedTask.updated_at,
|
||||||
) {
|
role: args.activeRole,
|
||||||
throw new Error(
|
taskStatus: args.pairedTask.status,
|
||||||
`Paired turn ${runtimePairedTurnIdentity.turnId} task_updated_at does not match the prepared execution context`,
|
hasHumanMessage: this.args.hasHumanMessage,
|
||||||
);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!pairedExecutionContext &&
|
|
||||||
pairedTask &&
|
|
||||||
runtimePairedTurnIdentity.taskId !== pairedTask.id
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
`Paired turn ${runtimePairedTurnIdentity.turnId} task_id does not match the latest paired task`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!pairedExecutionContext &&
|
|
||||||
pairedTask &&
|
|
||||||
runtimePairedTurnIdentity.taskUpdatedAt !== pairedTask.updated_at
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
`Paired turn ${runtimePairedTurnIdentity.turnId} task_updated_at does not match the latest paired task`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runtimePairedTurnIdentity) {
|
|
||||||
markPairedTurnRunning({
|
|
||||||
turnIdentity: runtimePairedTurnIdentity,
|
|
||||||
executorServiceId: effectiveServiceId,
|
|
||||||
executorAgentType: effectiveAgentType,
|
|
||||||
runId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pairedExecutionContext && !args.forcedAgentType) {
|
private validateRuntimePairedTurnIdentity(args: {
|
||||||
const roleConfig = getRoleModelConfig(activeRole);
|
pairedTask: LatestOpenPairedTask | null;
|
||||||
if (roleConfig.model) {
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
const modelKey = isClaudeCodeAgent ? 'CLAUDE_MODEL' : 'CODEX_MODEL';
|
activeRole: PairedRoomRole;
|
||||||
pairedExecutionContext.envOverrides[modelKey] = roleConfig.model;
|
runtimeTurn: RuntimePairedTurnResolution;
|
||||||
|
}): void {
|
||||||
|
const { runtimePairedTurnIdentity, preparedTurnTaskUpdatedAt } =
|
||||||
|
args.runtimeTurn;
|
||||||
|
if (!runtimePairedTurnIdentity) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (roleConfig.effort) {
|
if (runtimePairedTurnIdentity.role !== args.activeRole) {
|
||||||
const effortKey = isClaudeCodeAgent ? 'CLAUDE_EFFORT' : 'CODEX_EFFORT';
|
throw new Error(
|
||||||
pairedExecutionContext.envOverrides[effortKey] = roleConfig.effort;
|
`Paired turn ${runtimePairedTurnIdentity.turnId} cannot execute as ${args.activeRole}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
this.validatePreparedContextTurnIdentity({
|
||||||
if (pairedExecutionContext && runtimePairedTurnIdentity) {
|
pairedExecutionContext: args.pairedExecutionContext,
|
||||||
pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TURN_ID =
|
runtimePairedTurnIdentity,
|
||||||
runtimePairedTurnIdentity.turnId;
|
preparedTurnTaskUpdatedAt,
|
||||||
pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TURN_ROLE =
|
});
|
||||||
runtimePairedTurnIdentity.role;
|
this.validateLatestTaskTurnIdentity({
|
||||||
pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TURN_INTENT =
|
pairedTask: args.pairedTask,
|
||||||
runtimePairedTurnIdentity.intentKind;
|
pairedExecutionContext: args.pairedExecutionContext,
|
||||||
pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TASK_UPDATED_AT =
|
runtimePairedTurnIdentity,
|
||||||
runtimePairedTurnIdentity.taskUpdatedAt;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validatePreparedContextTurnIdentity(args: {
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity;
|
||||||
|
preparedTurnTaskUpdatedAt: string | undefined;
|
||||||
|
}): void {
|
||||||
|
if (!args.pairedExecutionContext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
args.runtimePairedTurnIdentity.taskId !==
|
||||||
|
args.pairedExecutionContext.task.id
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Paired turn ${args.runtimePairedTurnIdentity.turnId} task_id does not match the prepared execution context`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
args.runtimePairedTurnIdentity.taskUpdatedAt !==
|
||||||
|
args.preparedTurnTaskUpdatedAt
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Paired turn ${args.runtimePairedTurnIdentity.turnId} task_updated_at does not match the prepared execution context`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateLatestTaskTurnIdentity(args: {
|
||||||
|
pairedTask: LatestOpenPairedTask | null;
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity;
|
||||||
|
}): void {
|
||||||
|
if (args.pairedExecutionContext || !args.pairedTask) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (args.runtimePairedTurnIdentity.taskId !== args.pairedTask.id) {
|
||||||
|
throw new Error(
|
||||||
|
`Paired turn ${args.runtimePairedTurnIdentity.turnId} task_id does not match the latest paired task`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
args.runtimePairedTurnIdentity.taskUpdatedAt !==
|
||||||
|
args.pairedTask.updated_at
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Paired turn ${args.runtimePairedTurnIdentity.turnId} task_updated_at does not match the latest paired task`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private markRuntimePairedTurnRunning(
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity | undefined,
|
||||||
|
executionTarget: ResolvedExecutionTarget,
|
||||||
|
): void {
|
||||||
|
if (!runtimePairedTurnIdentity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
markPairedTurnRunning({
|
||||||
|
turnIdentity: runtimePairedTurnIdentity,
|
||||||
|
executorServiceId: executionTarget.effectiveServiceId,
|
||||||
|
executorAgentType: executionTarget.effectiveAgentType,
|
||||||
|
runId: this.args.runId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyPairedEnvOverrides(args: {
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity | undefined;
|
||||||
|
activeRole: PairedRoomRole;
|
||||||
|
isClaudeCodeAgent: boolean;
|
||||||
|
}): void {
|
||||||
|
this.applyRoleModelEnvOverrides(args);
|
||||||
|
this.applyRuntimePairedTurnEnvOverrides(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyRoleModelEnvOverrides(args: {
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
activeRole: PairedRoomRole;
|
||||||
|
isClaudeCodeAgent: boolean;
|
||||||
|
}): void {
|
||||||
|
if (!args.pairedExecutionContext || this.args.forcedAgentType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const roleConfig = getRoleModelConfig(args.activeRole);
|
||||||
|
if (roleConfig.model) {
|
||||||
|
const modelKey = args.isClaudeCodeAgent ? 'CLAUDE_MODEL' : 'CODEX_MODEL';
|
||||||
|
args.pairedExecutionContext.envOverrides[modelKey] = roleConfig.model;
|
||||||
|
}
|
||||||
|
if (roleConfig.effort) {
|
||||||
|
const effortKey = args.isClaudeCodeAgent
|
||||||
|
? 'CLAUDE_EFFORT'
|
||||||
|
: 'CODEX_EFFORT';
|
||||||
|
args.pairedExecutionContext.envOverrides[effortKey] = roleConfig.effort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyRuntimePairedTurnEnvOverrides(args: {
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined;
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity | undefined;
|
||||||
|
}): void {
|
||||||
|
if (!args.pairedExecutionContext || !args.runtimePairedTurnIdentity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
args.pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TURN_ID =
|
||||||
|
args.runtimePairedTurnIdentity.turnId;
|
||||||
|
args.pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TURN_ROLE =
|
||||||
|
args.runtimePairedTurnIdentity.role;
|
||||||
|
args.pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TURN_INTENT =
|
||||||
|
args.runtimePairedTurnIdentity.intentKind;
|
||||||
|
args.pairedExecutionContext.envOverrides.EJCLAW_PAIRED_TASK_UPDATED_AT =
|
||||||
|
args.runtimePairedTurnIdentity.taskUpdatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createExecutionLogger(args: {
|
||||||
|
currentLease: CurrentChannelLease;
|
||||||
|
pairedTask: LatestOpenPairedTask | null;
|
||||||
|
executionTarget: ResolvedExecutionTarget;
|
||||||
|
roleState: ResolvedRoleExecutionState;
|
||||||
|
runtimePairedTurnIdentity: PairedTurnIdentity | undefined;
|
||||||
|
}): ReturnType<typeof createScopedLogger> {
|
||||||
const log = createScopedLogger({
|
const log = createScopedLogger({
|
||||||
chatJid,
|
chatJid: this.args.chatJid,
|
||||||
groupName: group.name,
|
groupName: this.args.group.name,
|
||||||
groupFolder: group.folder,
|
groupFolder: this.args.group.folder,
|
||||||
runId,
|
runId: this.args.runId,
|
||||||
messageSeqStart: startSeq ?? undefined,
|
messageSeqStart: this.args.startSeq ?? undefined,
|
||||||
messageSeqEnd: endSeq ?? undefined,
|
messageSeqEnd: this.args.endSeq ?? undefined,
|
||||||
role: activeRole,
|
role: args.executionTarget.activeRole,
|
||||||
serviceId: effectiveServiceId,
|
serviceId: args.executionTarget.effectiveServiceId,
|
||||||
turnId: runtimePairedTurnIdentity?.turnId,
|
turnId: args.runtimePairedTurnIdentity?.turnId,
|
||||||
});
|
});
|
||||||
log.info(
|
log.info(
|
||||||
{
|
{
|
||||||
forcedRole: args.forcedRole,
|
forcedRole: this.args.forcedRole,
|
||||||
forcedAgentType: args.forcedAgentType ?? null,
|
forcedAgentType: this.args.forcedAgentType ?? null,
|
||||||
inferredRole,
|
inferredRole: args.executionTarget.inferredRole,
|
||||||
canHonorForcedRole,
|
canHonorForcedRole: args.executionTarget.canHonorForcedRole,
|
||||||
pairedTaskId: pairedTask?.id,
|
pairedTaskId: args.pairedTask?.id,
|
||||||
pairedTaskStatus: pairedTask?.status,
|
pairedTaskStatus: args.pairedTask?.status,
|
||||||
configuredAgentType,
|
configuredAgentType: args.executionTarget.configuredAgentType,
|
||||||
effectiveServiceId,
|
effectiveServiceId: args.executionTarget.effectiveServiceId,
|
||||||
effectiveAgentType,
|
effectiveAgentType: args.executionTarget.effectiveAgentType,
|
||||||
groupAgentType: group.agentType,
|
groupAgentType: this.args.group.agentType,
|
||||||
configuredReviewerAgentType: REVIEWER_AGENT_TYPE,
|
configuredReviewerAgentType: REVIEWER_AGENT_TYPE,
|
||||||
configuredArbiterAgentType: ARBITER_AGENT_TYPE,
|
configuredArbiterAgentType: ARBITER_AGENT_TYPE,
|
||||||
reviewerServiceId,
|
reviewerServiceId: args.executionTarget.reviewerServiceId,
|
||||||
arbiterServiceId,
|
arbiterServiceId: args.executionTarget.arbiterServiceId,
|
||||||
reviewerAgentType: currentLease.reviewer_agent_type,
|
reviewerAgentType: args.currentLease.reviewer_agent_type,
|
||||||
arbiterAgentType: currentLease.arbiter_agent_type,
|
arbiterAgentType: args.currentLease.arbiter_agent_type,
|
||||||
reviewerMode,
|
reviewerMode: args.roleState.reviewerMode,
|
||||||
arbiterMode,
|
arbiterMode: args.roleState.arbiterMode,
|
||||||
sessionFolder,
|
sessionFolder: args.executionTarget.sessionFolder,
|
||||||
resumedSession: currentSessionId ?? null,
|
resumedSession: args.roleState.currentSessionId ?? null,
|
||||||
},
|
},
|
||||||
'Resolved execution target for agent turn',
|
'Resolved execution target for agent turn',
|
||||||
);
|
);
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
const clearRoleSdkSessions = (): void => {
|
private createClearRoleSdkSessions(
|
||||||
|
pairedExecutionContext: PreparedPairedExecutionContext | undefined,
|
||||||
|
log: ReturnType<typeof createScopedLogger>,
|
||||||
|
): () => void {
|
||||||
|
return () => {
|
||||||
const configDir = pairedExecutionContext?.envOverrides?.CLAUDE_CONFIG_DIR;
|
const configDir = pairedExecutionContext?.envOverrides?.CLAUDE_CONFIG_DIR;
|
||||||
if (!configDir) return;
|
if (!configDir) return;
|
||||||
for (const sdkDir of ['.claude', '.codex']) {
|
for (const sdkDir of ['.claude', '.codex']) {
|
||||||
const sessionsDir = path.join(configDir, sdkDir, 'sessions');
|
const sessionsDir = path.join(configDir, sdkDir, 'sessions');
|
||||||
if (fs.existsSync(sessionsDir)) {
|
if (fs.existsSync(sessionsDir)) {
|
||||||
fs.rmSync(sessionsDir, { recursive: true, force: true });
|
fs.rmSync(sessionsDir, { recursive: true, force: true });
|
||||||
log.info({ sessionsDir }, 'Cleared SDK sessions for fresh Claude turn');
|
log.info(
|
||||||
|
{ sessionsDir },
|
||||||
|
'Cleared SDK sessions for fresh Claude turn',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
return {
|
|
||||||
currentLease,
|
|
||||||
pairedTask,
|
|
||||||
activeRole,
|
|
||||||
effectiveServiceId,
|
|
||||||
effectiveAgentType,
|
|
||||||
sessionFolder,
|
|
||||||
reviewerMode,
|
|
||||||
arbiterMode,
|
|
||||||
effectiveGroup,
|
|
||||||
isClaudeCodeAgent,
|
|
||||||
forceFreshClaudeReviewerSession,
|
|
||||||
shouldPersistSession,
|
|
||||||
currentSessionId,
|
|
||||||
provider,
|
|
||||||
memoryBriefing,
|
|
||||||
canRetryClaudeCredentials,
|
|
||||||
roomRoleContext,
|
|
||||||
pairedExecutionContext,
|
|
||||||
runtimePairedTurnIdentity,
|
|
||||||
log,
|
|
||||||
clearRoleSdkSessions,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user