fix: keep finals separate after silent retries

This commit is contained in:
Eyejoker
2026-03-23 03:54:19 +09:00
parent 290cd95e51
commit 562067352a
2 changed files with 59 additions and 57 deletions

View File

@@ -697,7 +697,10 @@ describe('createMessageRuntime', () => {
'중복 최종 답변입니다.', '중복 최종 답변입니다.',
); );
expect(channel.sendMessage).toHaveBeenCalledTimes(1); expect(channel.sendMessage).toHaveBeenCalledTimes(1);
expect(channel.sendMessage).toHaveBeenCalledWith(chatJid, '최종 답변입니다.'); expect(channel.sendMessage).toHaveBeenCalledWith(
chatJid,
'최종 답변입니다.',
);
} finally { } finally {
vi.useRealTimers(); vi.useRealTimers();
} }
@@ -1537,7 +1540,10 @@ describe('createMessageRuntime', () => {
'아직 진행 중.\n\n10초', '아직 진행 중.\n\n10초',
); );
expect(channel.sendMessage).toHaveBeenCalledTimes(1); expect(channel.sendMessage).toHaveBeenCalledTimes(1);
expect(channel.sendMessage).toHaveBeenCalledWith(chatJid, '아직 진행 중.'); expect(channel.sendMessage).toHaveBeenCalledWith(
chatJid,
'아직 진행 중.',
);
} finally { } finally {
vi.useRealTimers(); vi.useRealTimers();
} }
@@ -1564,16 +1570,14 @@ describe('createMessageRuntime', () => {
}, },
]); ]);
vi.mocked(agentRunner.runAgentProcess).mockImplementation( vi.mocked(agentRunner.runAgentProcess).mockImplementation(async () => {
async () => { await vi.advanceTimersByTimeAsync(1_100);
await vi.advanceTimersByTimeAsync(1_100); return {
return { status: 'success',
status: 'success', result: null,
result: null, newSessionId: 'session-quiet-budget',
newSessionId: 'session-quiet-budget', };
}; });
},
);
const runtime = createMessageRuntime({ const runtime = createMessageRuntime({
assistantName: 'Andy', assistantName: 'Andy',

View File

@@ -569,7 +569,8 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
const MAX_SILENT_ROLLOVERS = 2; const MAX_SILENT_ROLLOVERS = 2;
const MAX_TOTAL_SILENT_WAIT_MS = const MAX_TOTAL_SILENT_WAIT_MS =
QUIET_RUN_ROLLOVER_MS * (MAX_SILENT_ROLLOVERS + 1); QUIET_RUN_ROLLOVER_MS * (MAX_SILENT_ROLLOVERS + 1);
const FAILURE_FINAL_TEXT = '요청을 완료하지 못했습니다. 다시 시도해 주세요.'; const FAILURE_FINAL_TEXT =
'요청을 완료하지 못했습니다. 다시 시도해 주세요.';
const queueItemBaseCursor = normalizeStoredSeqCursor( const queueItemBaseCursor = normalizeStoredSeqCursor(
deps.getLastAgentTimestamps()[chatJid] || '0', deps.getLastAgentTimestamps()[chatJid] || '0',
chatJid, chatJid,
@@ -630,11 +631,7 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
!requiresTrigger || !requiresTrigger ||
(hasTrigger && (hasTrigger &&
(msg.is_from_me || (msg.is_from_me ||
isTriggerAllowed( isTriggerAllowed(chatJid, msg.sender, loadSenderAllowlist())))
chatJid,
msg.sender,
loadSenderAllowlist(),
)))
); );
}, },
}, },
@@ -680,13 +677,11 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
); );
type VisiblePhase = 'silent' | 'progress' | 'final'; type VisiblePhase = 'silent' | 'progress' | 'final';
type PendingFollowUp = type PendingFollowUp = {
| { queuedAt: number;
queuedAt: number; textLength: number;
textLength: number; filename: string;
filename: string; } | null;
}
| null;
let idleTimer: ReturnType<typeof setTimeout> | null = null; let idleTimer: ReturnType<typeof setTimeout> | null = null;
let hadError = false; let hadError = false;
let producedDeliverySucceeded = true; let producedDeliverySucceeded = true;
@@ -934,38 +929,41 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
idleTimer = null; idleTimer = null;
return; return;
} }
idleTimer = setTimeout(() => { idleTimer = setTimeout(
const closeReason = () => {
!hasVisibleOutput() && pendingFollowUp const closeReason =
? 'follow-up-no-output-preemption' !hasVisibleOutput() && pendingFollowUp
: 'idle-timeout'; ? 'follow-up-no-output-preemption'
quietStopReason ??= : 'idle-timeout';
!hasVisibleOutput() && pendingFollowUp quietStopReason ??=
? 'follow-up-no-output-timeout' !hasVisibleOutput() && pendingFollowUp
: 'idle-timeout'; ? 'follow-up-no-output-timeout'
if (!hasVisibleOutput() && pendingFollowUp) { : 'idle-timeout';
logger.warn( if (!hasVisibleOutput() && pendingFollowUp) {
{ logger.warn(
group: group.name, {
chatJid, group: group.name,
runId, chatJid,
followUpQueuedAt: new Date( runId,
pendingFollowUp.queuedAt, followUpQueuedAt: new Date(
).toISOString(), pendingFollowUp.queuedAt,
followUpQueuedTextLength: pendingFollowUp.textLength, ).toISOString(),
followUpQueuedFilename: pendingFollowUp.filename, followUpQueuedTextLength: pendingFollowUp.textLength,
followUpWaitMs: Date.now() - pendingFollowUp.queuedAt, followUpQueuedFilename: pendingFollowUp.filename,
}, followUpWaitMs: Date.now() - pendingFollowUp.queuedAt,
'Idle timeout reached while a queued follow-up still had no visible output', },
); 'Idle timeout reached while a queued follow-up still had no visible output',
} else { );
logger.debug( } else {
{ group: group.name, chatJid, runId, closeReason }, logger.debug(
'Idle timeout, closing agent stdin', { group: group.name, chatJid, runId, closeReason },
); 'Idle timeout, closing agent stdin',
} );
deps.queue.closeStdin(chatJid, { runId, reason: closeReason }); }
}, hasVisibleOutput() ? deps.idleTimeout : QUIET_RUN_ROLLOVER_MS); deps.queue.closeStdin(chatJid, { runId, reason: closeReason });
},
hasVisibleOutput() ? deps.idleTimeout : QUIET_RUN_ROLLOVER_MS,
);
}; };
const noteFollowUpQueued = (meta?: { const noteFollowUpQueued = (meta?: {