feat: unify 3 bot services into single process

- Add UNIFIED_MODE flag (default on, disable with UNIFIED_MODE=0)
- Register all 3 Discord bots in one process (claude, codex, review)
- Load all registered groups regardless of agent_type (codex/owner priority)
- Start credential proxy and container cleanup at unified startup
- shouldServiceProcessChat returns true in unified mode
- Add findChannelByName for role-based response routing
- Backward compatible: UNIFIED_MODE=0 restores per-service behavior
This commit is contained in:
Eyejoker
2026-03-29 18:22:57 +09:00
parent fc9f2867b9
commit d01f98bd61
20 changed files with 118 additions and 759 deletions

View File

@@ -51,7 +51,7 @@ import {
} from './codex-token-rotation.js';
import type { CodexRotationReason } from './agent-error-detection.js';
import { getTokenCount } from './token-rotation.js';
import type { PairedReviewerVerdict, RegisteredGroup } from './types.js';
import type { RegisteredGroup } from './types.js';
// ── Main executor ─────────────────────────────────────────────────
@@ -143,21 +143,6 @@ export async function runAgentForGroup(
});
let pairedExecutionStatus: 'succeeded' | 'failed' = 'failed';
let pairedExecutionSummary: string | null = null;
let pairedReviewerVerdict: PairedReviewerVerdict | null = null;
let pairedReviewerVerdictNote: string | null = null;
let reviewerGateValidationFailed = false;
const formatReviewerGateFailureMessage = (): string => {
const taskId = pairedExecutionContext?.task.id ?? 'unknown-task';
const gateTurnKind =
pairedExecutionContext?.gateTurnKind ?? 'implementation_start';
return [
'Reviewer gate turn requires a visible structured verdict.',
`- Task: ${taskId}`,
`- Gate: ${gateTurnKind}`,
'- Allowed verdicts: done, done_with_concerns, blocked',
].join('\n');
};
const shouldHandoffToCodex = (
reason: AgentTriggerReason,
@@ -386,41 +371,6 @@ export async function runAgentForGroup(
sawVisibleOutput: true,
};
}
if (
pairedExecutionContext?.requiresVisibleVerdict &&
(output.phase === undefined || output.phase === 'final')
) {
const structuredOutput = getStructuredAgentOutput(output);
const reviewerVerdict = structuredOutput?.verdict ?? null;
if (
structuredOutput?.visibility === 'silent' ||
reviewerVerdict === null
) {
reviewerGateValidationFailed = true;
pairedReviewerVerdict =
structuredOutput?.visibility === 'silent' ? 'silent' : null;
pairedReviewerVerdictNote = null;
const failureMessage = formatReviewerGateFailureMessage();
pairedExecutionSummary = failureMessage.slice(0, 500);
await onOutput({
status: 'success',
result: null,
output: {
visibility: 'public',
text: failureMessage,
},
phase: 'final',
});
return;
}
if (structuredOutput) {
pairedReviewerVerdict = reviewerVerdict;
pairedReviewerVerdictNote =
structuredOutput.visibility === 'public'
? structuredOutput.text
: null;
}
}
await onOutput(output);
}
: undefined;
@@ -915,26 +865,6 @@ export async function runAgentForGroup(
return 'error';
}
if (
pairedExecutionContext?.requiresVisibleVerdict &&
(reviewerGateValidationFailed || pairedReviewerVerdict === null)
) {
if (!reviewerGateValidationFailed && onOutput) {
const failureMessage = formatReviewerGateFailureMessage();
pairedExecutionSummary = failureMessage.slice(0, 500);
await onOutput({
status: 'success',
result: null,
output: {
visibility: 'public',
text: failureMessage,
},
phase: 'final',
});
}
return 'error';
}
pairedExecutionStatus = 'succeeded';
return 'success';
} finally {
@@ -943,8 +873,6 @@ export async function runAgentForGroup(
taskId: pairedExecutionContext.task.id,
status: pairedExecutionStatus,
summary: pairedExecutionSummary,
reviewerVerdict: pairedReviewerVerdict,
reviewerVerdictNote: pairedReviewerVerdictNote,
});
}
}