style: sync pre-commit formatting fallout

This commit is contained in:
Eyejoker
2026-03-28 05:41:16 +09:00
parent ba9f6871b6
commit e1fdc47552
17 changed files with 261 additions and 175 deletions

View File

@@ -5,7 +5,6 @@ import {
classifyClaudeAuthError,
detectClaudeProviderFailureMessage,
isClaudeOrgAccessDeniedMessage,
shouldRotateClaudeToken,
} from './agent-error-detection.js';
@@ -64,5 +63,4 @@ describe('agent-error-detection', () => {
expect(shouldRotateClaudeToken('overloaded')).toBe(false);
expect(shouldRotateClaudeToken('success-null-result')).toBe(false);
});
});

View File

@@ -63,7 +63,8 @@ vi.mock('./group-folder.js', () => ({
folder: string,
serviceId: string,
taskId: string,
) => `${process.env.EJ_TEST_ROOT}/task-sessions/${folder}/${serviceId}/${taskId}`,
) =>
`${process.env.EJ_TEST_ROOT}/task-sessions/${folder}/${serviceId}/${taskId}`,
}));
vi.mock('os', async () => {

View File

@@ -273,7 +273,10 @@ function prepareCodexSessionEnvironment(args: {
: [
readPlatformPrompt('codex', args.projectRoot),
isReviewService(SERVICE_ID)
? readOptionalPromptFile(args.projectRoot, 'codex-review-platform.md')
? readOptionalPromptFile(
args.projectRoot,
'codex-review-platform.md',
)
: undefined,
args.isPairedRoom
? readPairedRoomPrompt('codex', args.projectRoot)

View File

@@ -137,12 +137,10 @@ export function isSessionCommandSenderAllowed(sender: string): boolean {
}
// Delta handoff: cross-provider session continuity with probe + fallback
export const DELTA_HANDOFF_ENABLED =
getEnv('DELTA_HANDOFF_ENABLED') === 'true';
export const DELTA_HANDOFF_ENABLED = getEnv('DELTA_HANDOFF_ENABLED') === 'true';
// Comma-separated list of group folders for canary testing
const rawDeltaHandoffCanaryGroups =
getEnv('DELTA_HANDOFF_CANARY_GROUPS') || '';
const rawDeltaHandoffCanaryGroups = getEnv('DELTA_HANDOFF_CANARY_GROUPS') || '';
export const DELTA_HANDOFF_CANARY_GROUPS = new Set(
rawDeltaHandoffCanaryGroups
.split(',')

View File

@@ -361,7 +361,9 @@ function createSchema(database: Database.Database): void {
}
try {
database.exec(`ALTER TABLE work_items ADD COLUMN service_id TEXT DEFAULT ''`);
database.exec(
`ALTER TABLE work_items ADD COLUMN service_id TEXT DEFAULT ''`,
);
} catch {
/* column already exists */
}
@@ -884,9 +886,7 @@ export function getRecentChatMessages(
LIMIT ?
) ORDER BY timestamp
`;
const rows = db
.prepare(sql)
.all(chatJid, limit) as Array<
const rows = db.prepare(sql).all(chatJid, limit) as Array<
NewMessage & {
is_from_me?: boolean | number;
is_bot_message?: boolean | number;
@@ -1834,7 +1834,9 @@ function parseLastAgentSeqState(
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`Invalid last_agent_seq JSON for ${serviceId}: not an object`);
throw new Error(
`Invalid last_agent_seq JSON for ${serviceId}: not an object`,
);
}
const cursors: Record<string, string> = {};

View File

@@ -19,7 +19,8 @@ beforeEach(() => {
});
// Helper to create a message
const createMessage = (overrides: Partial<{
const createMessage = (
overrides: Partial<{
id: string;
chat_jid: string;
sender: string;
@@ -28,7 +29,8 @@ const createMessage = (overrides: Partial<{
timestamp: string;
is_from_me: boolean;
is_bot_message: boolean;
}> = {}) => ({
}> = {},
) => ({
id: overrides.id ?? 'msg-1',
chat_jid: overrides.chat_jid ?? 'dc:test-room',
sender: overrides.sender ?? 'user1',
@@ -41,7 +43,13 @@ const createMessage = (overrides: Partial<{
// Helper to setup chat metadata
const setupChat = (jid: string) => {
storeChatMetadata(jid, new Date().toISOString(), 'Test Chat', 'discord', true);
storeChatMetadata(
jid,
new Date().toISOString(),
'Test Chat',
'discord',
true,
);
};
describe('isPairedRoomJid', () => {
@@ -103,34 +111,40 @@ describe('getLastBotFinalMessage', () => {
setupChat(jid);
// Store older bot message (from any bot)
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'First bot message',
timestamp: new Date(now.getTime() - 1000).toISOString(),
is_from_me: true,
is_bot_message: true,
}));
}),
);
// Store newer bot message (from different bot - is_from_me=0 but is_bot_message=1)
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-2',
chat_jid: jid,
content: 'Second bot message',
timestamp: now.toISOString(),
is_from_me: false, // Different bot
is_bot_message: true,
}));
}),
);
// Store human message (should not be returned)
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-3',
chat_jid: jid,
content: 'Human message',
timestamp: new Date(now.getTime() + 1000).toISOString(),
is_from_me: false,
is_bot_message: false,
}));
}),
);
const lastMessages = getLastBotFinalMessage(jid, 'claude-code', 1);
expect(lastMessages).toHaveLength(1);
@@ -183,21 +197,27 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message (from any bot)
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'DONE — Task completed successfully',
timestamp: new Date().toISOString(),
is_from_me: false, // Different bot
is_bot_message: true,
}));
}),
);
// Verify it's a paired room
expect(isPairedRoomJid(jid)).toBe(true);
// Verify duplicate detection works via the actual runtime function
expect(isDuplicateOfLastBotFinal(jid, 'DONE — Task completed successfully')).toBe(true);
expect(isDuplicateOfLastBotFinal(jid, 'donetask completed successfully')).toBe(true); // Normalized match
expect(
isDuplicateOfLastBotFinal(jid, 'DONETask completed successfully'),
).toBe(true);
expect(
isDuplicateOfLastBotFinal(jid, 'done — task completed successfully'),
).toBe(true); // Normalized match
});
it('non-paired room: duplicate check is bypassed', () => {
@@ -214,14 +234,16 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'DONE — Task completed',
timestamp: new Date().toISOString(),
is_from_me: true,
is_bot_message: true,
}));
}),
);
// Verify it's NOT a paired room
expect(isPairedRoomJid(jid)).toBe(false);
@@ -252,17 +274,21 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'First message content',
timestamp: new Date().toISOString(),
is_from_me: true,
is_bot_message: true,
}));
}),
);
// Verify different content is not a duplicate
expect(isDuplicateOfLastBotFinal(jid, 'Different message content')).toBe(false);
expect(isDuplicateOfLastBotFinal(jid, 'Different message content')).toBe(
false,
);
expect(isDuplicateOfLastBotFinal(jid, 'First message content')).toBe(true);
});
@@ -288,17 +314,21 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message from "codex" (is_from_me=0)
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'DONE — Analysis complete',
timestamp: new Date().toISOString(),
is_from_me: false, // Other bot
is_bot_message: true,
}));
}),
);
// Verify claude service detects this as duplicate (cross-bot detection)
expect(isDuplicateOfLastBotFinal(jid, 'DONE — Analysis complete')).toBe(true);
expect(isDuplicateOfLastBotFinal(jid, 'DONE — Analysis complete')).toBe(
true,
);
});
it('normalization handles whitespace and case differences', () => {
@@ -323,18 +353,27 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message with specific formatting
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'DONE — Task completed\n\nSuccessfully',
timestamp: new Date().toISOString(),
is_from_me: true,
is_bot_message: true,
}));
}),
);
// Same content with different whitespace should be detected as duplicate
expect(isDuplicateOfLastBotFinal(jid, 'done — task completed successfully')).toBe(true);
expect(isDuplicateOfLastBotFinal(jid, ' DONE Task completed Successfully ')).toBe(true);
expect(
isDuplicateOfLastBotFinal(jid, 'donetask completed successfully'),
).toBe(true);
expect(
isDuplicateOfLastBotFinal(
jid,
' DONE — Task completed Successfully ',
),
).toBe(true);
// Different content should not be duplicate
expect(isDuplicateOfLastBotFinal(jid, 'FAILED — Task failed')).toBe(false);
@@ -362,14 +401,16 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message (simulating previous delivery)
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'DONE — Task completed successfully',
timestamp: new Date().toISOString(),
is_from_me: true,
is_bot_message: true,
}));
}),
);
// Create a work item with the same content (duplicate)
const workItem = createProducedWorkItem({
@@ -416,14 +457,16 @@ describe('isDuplicateOfLastBotFinal (runtime function)', () => {
});
// Store a bot message
storeMessage(createMessage({
storeMessage(
createMessage({
id: 'msg-1',
chat_jid: jid,
content: 'First message',
timestamp: new Date().toISOString(),
is_from_me: true,
is_bot_message: true,
}));
}),
);
// Create a work item with DIFFERENT content (non-duplicate)
const workItem = createProducedWorkItem({

View File

@@ -75,7 +75,12 @@ export function resolveServiceGroupSessionsPath(
assertValidGroupFolder(folder);
assertValidRuntimeSegment(serviceId, 'service ID');
const sessionsBaseDir = path.resolve(DATA_DIR, 'sessions');
const sessionsPath = path.resolve(sessionsBaseDir, folder, 'services', serviceId);
const sessionsPath = path.resolve(
sessionsBaseDir,
folder,
'services',
serviceId,
);
ensureWithinBase(sessionsBaseDir, sessionsPath);
return sessionsPath;
}

View File

@@ -71,7 +71,10 @@ import { Channel, NewMessage, RegisteredGroup } from './types.js';
import { logger } from './logger.js';
import { normalizeStoredSeqCursor } from './message-cursor.js';
import { initCodexTokenRotation } from './codex-token-rotation.js';
import { hasAvailableClaudeToken, initTokenRotation } from './token-rotation.js';
import {
hasAvailableClaudeToken,
initTokenRotation,
} from './token-rotation.js';
import {
shouldStartTokenRefreshLoop,
startTokenRefreshLoop,
@@ -442,7 +445,10 @@ async function main(): Promise<void> {
editFormattedTrackedChannelMessage(channels, jid, messageId, rawText),
});
} else {
logger.info({ serviceId: SERVICE_ID }, 'Skipping scheduler for review service');
logger.info(
{ serviceId: SERVICE_ID },
'Skipping scheduler for review service',
);
}
startIpcWatcher({
sendMessage: (jid, text) => {
@@ -536,7 +542,11 @@ async function main(): Promise<void> {
}
restoreDefaultChannelLease(lease.chatJid);
logger.info(
{ chatJid: lease.chatJid, serviceId: SERVICE_ID, elapsedMin: Math.round(elapsed / 60_000) },
{
chatJid: lease.chatJid,
serviceId: SERVICE_ID,
elapsedMin: Math.round(elapsed / 60_000),
},
'Claude token available and failover hold period elapsed, restored default channel lease',
);
}

View File

@@ -44,7 +44,8 @@ vi.mock('./logger.js', () => ({
}));
vi.mock('./agent-error-detection.js', async (importOriginal) => {
const actual = await importOriginal<typeof import('./agent-error-detection.js')>();
const actual =
await importOriginal<typeof import('./agent-error-detection.js')>();
return {
...actual,
classifyRotationTrigger: vi.fn((error?: string | null) => {
@@ -528,7 +529,8 @@ describe('runAgentForGroup Claude rotation', () => {
await onOutput?.({
status: 'success',
phase: 'final',
result: 'You\u2019re out of extra usage \u00b7 resets 4am (Asia/Seoul)',
result:
'You\u2019re out of extra usage \u00b7 resets 4am (Asia/Seoul)',
});
return {
status: 'success',

View File

@@ -20,12 +20,19 @@ import {
shouldResetSessionOnAgentFailure,
shouldRetryFreshSessionOnAgentFailure,
} from './session-recovery.js';
import { CODEX_MAIN_SERVICE_ID, CODEX_REVIEW_SERVICE_ID, SERVICE_SESSION_SCOPE } from './config.js';
import {
CODEX_MAIN_SERVICE_ID,
CODEX_REVIEW_SERVICE_ID,
SERVICE_SESSION_SCOPE,
} from './config.js';
import {
buildSuppressTokenPrompt,
classifySuppressTokenOutput,
} from './output-suppression.js';
import { activateCodexFailover, getEffectiveChannelLease } from './service-routing.js';
import {
activateCodexFailover,
getEffectiveChannelLease,
} from './service-routing.js';
import {
evaluateStreamedOutput,
type StreamedOutputState,
@@ -111,7 +118,8 @@ export async function runAgentForGroup(
const canRotateToken = isClaudeCodeAgent && getTokenCount() > 1;
const currentLease = getEffectiveChannelLease(chatJid);
const reviewerMode = currentLease.reviewer_service_id === SERVICE_SESSION_SCOPE;
const reviewerMode =
currentLease.reviewer_service_id === SERVICE_SESSION_SCOPE;
const effectivePrompt = buildSuppressTokenPrompt(prompt, suppressToken, {
reviewerMode,
});
@@ -569,11 +577,7 @@ export async function runAgentForGroup(
}
if (primaryAttempt.error) {
if (
canRotateToken &&
provider === 'claude' &&
!primaryAttempt.sawOutput
) {
if (canRotateToken && provider === 'claude' && !primaryAttempt.sawOutput) {
const errMsg = getErrorMessage(primaryAttempt.error);
const trigger = primaryAttempt.streamedTriggerReason
? {
@@ -666,11 +670,7 @@ export async function runAgentForGroup(
}
if (output.status === 'error') {
if (
canRotateToken &&
provider === 'claude' &&
!primaryAttempt.sawOutput
) {
if (canRotateToken && provider === 'claude' && !primaryAttempt.sawOutput) {
const trigger = primaryAttempt.streamedTriggerReason
? {
shouldRetry: true,

View File

@@ -12,7 +12,8 @@ vi.mock('./agent-runner.js', () => ({
}));
vi.mock('./output-suppression.js', async (importOriginal) => {
const actual = await importOriginal<typeof import('./output-suppression.js')>();
const actual =
await importOriginal<typeof import('./output-suppression.js')>();
return {
...actual,
createSuppressToken: vi.fn(() => '__TEST_SUPPRESS__'),

View File

@@ -24,7 +24,11 @@ import {
SERVICE_ID,
} from './config.js';
import { GroupQueue, GroupRunContext } from './group-queue.js';
import { findChannel, formatMessages, normalizeMessageForDedupe } from './router.js';
import {
findChannel,
formatMessages,
normalizeMessageForDedupe,
} from './router.js';
import { isTriggerAllowed, loadSenderAllowlist } from './sender-allowlist.js';
import {
advanceLastAgentCursor,
@@ -52,7 +56,10 @@ import { shouldServiceProcessChat } from './service-routing.js';
* Check if a message is a duplicate of the last bot final message in a paired room.
* Exported for testing purposes.
*/
export function isDuplicateOfLastBotFinal(chatJid: string, text: string): boolean {
export function isDuplicateOfLastBotFinal(
chatJid: string,
text: string,
): boolean {
// Only check in paired rooms (both claude and codex registered)
if (!isPairedRoomJid(chatJid)) {
return false;
@@ -115,7 +122,10 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
* Check if a message is a duplicate of the last bot final message in a paired room.
* Returns true if duplicate (should be suppressed).
*/
const checkDuplicateOfLastBotFinal = (chatJid: string, text: string): boolean => {
const checkDuplicateOfLastBotFinal = (
chatJid: string,
text: string,
): boolean => {
return isDuplicateOfLastBotFinal(chatJid, text);
};
@@ -129,7 +139,10 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
const replaceMessageId = options?.replaceMessageId ?? null;
// Check for duplicate in paired rooms before attempting delivery
const isDuplicate = checkDuplicateOfLastBotFinal(item.chat_jid, item.result_payload);
const isDuplicate = checkDuplicateOfLastBotFinal(
item.chat_jid,
item.result_payload,
);
if (isDuplicate) {
// Mark as delivered without sending, and don't open continuation
@@ -263,7 +276,9 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
const isClaudeCodeAgent =
(group.agentType || 'claude-code') === 'claude-code';
const suppressToken =
isClaudeService() || isReviewService() ? createSuppressToken() : undefined;
isClaudeService() || isReviewService()
? createSuppressToken()
: undefined;
const deferTypingUntilVisible = Boolean(suppressToken) && isClaudeService();
const turnController = new MessageTurnController({
@@ -277,7 +292,8 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
deferTypingUntilVisible,
suppressToken,
clearSession: () => deps.clearSession(group.folder),
requestClose: (reason) => deps.queue.closeStdin(chatJid, { runId, reason }),
requestClose: (reason) =>
deps.queue.closeStdin(chatJid, { runId, reason }),
deliverFinalText: async (text) => {
try {
const workItem = createProducedWorkItem({
@@ -352,7 +368,9 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
}
};
const processClaimedHandoff = async (handoff: ServiceHandoff): Promise<void> => {
const processClaimedHandoff = async (
handoff: ServiceHandoff,
): Promise<void> => {
const group = deps.getRegisteredGroups()[handoff.chat_jid];
if (!group) {
failServiceHandoff(handoff.id, 'Group not registered on target service');

View File

@@ -19,8 +19,7 @@ export function shouldEnableSuppressOutputForService(
if (!serviceId) return false;
const normalized = normalizeServiceId(serviceId);
return (
normalized === CLAUDE_SERVICE_ID ||
normalized === CODEX_REVIEW_SERVICE_ID
normalized === CLAUDE_SERVICE_ID || normalized === CODEX_REVIEW_SERVICE_ID
);
}
@@ -29,7 +28,10 @@ export function classifySuppressTokenOutput(
suppressToken: string | undefined,
): 'exact' | 'mixed' | 'none' {
const trimmed = rawText.trim();
if ((suppressToken && trimmed === suppressToken) || EXACT_ANY_SUPPRESS_TOKEN_PATTERN.test(trimmed)) {
if (
(suppressToken && trimmed === suppressToken) ||
EXACT_ANY_SUPPRESS_TOKEN_PATTERN.test(trimmed)
) {
return 'exact';
}
if (suppressToken && rawText.includes(suppressToken)) {

View File

@@ -99,8 +99,5 @@ export function findChannel(
* - Collapse consecutive whitespace/newlines into single space
*/
export function normalizeMessageForDedupe(text: string): string {
return text
.trim()
.replace(/\s+/g, ' ')
.toLowerCase();
return text.trim().replace(/\s+/g, ' ').toLowerCase();
}

View File

@@ -85,7 +85,9 @@ function getDefaultLease(chatJid: string): EffectiveChannelLease {
return {
chat_jid: chatJid,
owner_service_id:
SERVICE_AGENT_TYPE === 'codex' ? CODEX_MAIN_SERVICE_ID : CLAUDE_SERVICE_ID,
SERVICE_AGENT_TYPE === 'codex'
? CODEX_MAIN_SERVICE_ID
: CLAUDE_SERVICE_ID,
reviewer_service_id: null,
activated_at: null,
reason: null,
@@ -106,7 +108,9 @@ export function refreshChannelOwnerCache(force = false): void {
lastLeaseRefreshAt = now;
}
export function getEffectiveChannelLease(chatJid: string): EffectiveChannelLease {
export function getEffectiveChannelLease(
chatJid: string,
): EffectiveChannelLease {
refreshChannelOwnerCache();
const row = leaseCache.get(chatJid);
if (row) {
@@ -180,7 +184,10 @@ export function getActiveCodexFailoverLeases(): ActiveFailoverLease[] {
normalizeServiceId(row.reviewer_service_id || '') ===
CODEX_MAIN_SERVICE_ID,
)
.map((row) => ({ chatJid: row.chat_jid, activatedAt: row.activated_at ?? null }));
.map((row) => ({
chatJid: row.chat_jid,
activatedAt: row.activated_at ?? null,
}));
}
/** @deprecated Use getActiveCodexFailoverLeases() instead */

View File

@@ -25,7 +25,8 @@ const {
}));
vi.mock('./agent-error-detection.js', async (importOriginal) => {
const actual = await importOriginal<typeof import('./agent-error-detection.js')>();
const actual =
await importOriginal<typeof import('./agent-error-detection.js')>();
return {
...actual,
classifyRotationTrigger: vi.fn((error?: string | null) => {
@@ -566,8 +567,7 @@ Check the run.
created_at: '2026-02-22T00:00:00.000Z',
});
(runAgentProcessMock as any)
.mockImplementationOnce(
(runAgentProcessMock as any).mockImplementationOnce(
async (
_group: unknown,
_input: unknown,

View File

@@ -535,8 +535,7 @@ async function runTask(
}
};
const provider =
context.taskAgentType === 'codex' ? 'codex' : 'claude';
const provider = context.taskAgentType === 'codex' ? 'codex' : 'claude';
{
const attempt = await runTaskAttempt(provider);