Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 60 additions & 55 deletions packages/cli/src/acp-integration/session/Session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33247,65 +33247,70 @@ describe('Session', () => {
expect(mockChat.sendMessageStream).toHaveBeenCalledTimes(2);
});

it('does not count a failed Guard compression or block later automatic work', async () => {
rebuildSessionWithGuard();
installPendingTodoTool();
queuePendingTodoThenNaturalStops();
const noCompression = {
originalTokenCount: 50,
newTokenCount: 50,
compressionStatus: core.CompressionStatus.NOOP,
};
mockLlmClient.tryCompressChat
.mockResolvedValueOnce(noCompression)
.mockResolvedValueOnce(noCompression)
.mockResolvedValueOnce({
originalTokenCount: 120,
newTokenCount: 120,
compressionStatus:
core.CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY,
})
.mockResolvedValue(noCompression);
it.each([
core.CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY,
core.CompressionStatus.COMPRESSION_FAILED_API_ERROR,
])(
'does not count a failed Guard compression status %s or block later automatic work',
async (compressionStatus) => {
rebuildSessionWithGuard();
installPendingTodoTool();
queuePendingTodoThenNaturalStops();
const noCompression = {
originalTokenCount: 50,
newTokenCount: 50,
compressionStatus: core.CompressionStatus.NOOP,
};
mockLlmClient.tryCompressChat
.mockResolvedValueOnce(noCompression)
.mockResolvedValueOnce(noCompression)
.mockResolvedValueOnce({
originalTokenCount: 120,
newTokenCount: 120,
compressionStatus,
})
.mockResolvedValue(noCompression);

await runGuardPrompt();
await runGuardPrompt();

expect(mockChat.sendMessageStream).toHaveBeenCalledTimes(2);
expect(
vi
.mocked(mockClient.sessionUpdate)
.mock.calls.some(
([params]) =>
params.update.sessionUpdate === 'agent_message_chunk' &&
params.update._meta?.['source'] === 'todo_stop_guard',
),
).toBe(false);
expect(mockChat.sendMessageStream).toHaveBeenCalledTimes(2);
expect(
vi
.mocked(mockClient.sessionUpdate)
.mock.calls.some(
([params]) =>
params.update.sessionUpdate === 'agent_message_chunk' &&
params.update._meta?.['source'] === 'todo_stop_guard',
),
).toBe(false);

const callback =
mockBackgroundTaskRegistry.setNotificationCallback.mock.calls.at(
-1,
)?.[0] as (
displayText: string,
modelText: string,
meta: { agentId: string; status: string },
) => void;
callback('independent background done', '<task-notification />', {
agentId: 'after-guard-compression-failure',
status: 'completed',
});
const callback =
mockBackgroundTaskRegistry.setNotificationCallback.mock.calls.at(
-1,
)?.[0] as (
displayText: string,
modelText: string,
meta: { agentId: string; status: string },
) => void;
callback('independent background done', '<task-notification />', {
agentId: 'after-guard-compression-failure',
status: 'completed',
});

await vi.waitFor(() => {
expect(mockChat.sendMessageStream).toHaveBeenCalledTimes(3);
});
expect(
vi
.mocked(mockClient.sessionUpdate)
.mock.calls.some(
([params]) =>
params.update.sessionUpdate === 'agent_message_chunk' &&
params.update._meta?.['source'] === 'todo_stop_guard',
),
).toBe(false);
});
await vi.waitFor(() => {
expect(mockChat.sendMessageStream).toHaveBeenCalledTimes(3);
});
expect(
vi
.mocked(mockClient.sessionUpdate)
.mock.calls.some(
([params]) =>
params.update.sessionUpdate === 'agent_message_chunk' &&
params.update._meta?.['source'] === 'todo_stop_guard',
),
).toBe(false);
},
);

it('keeps external Stop hook continuation when Guard compression throws', async () => {
rebuildSessionWithGuard();
Expand Down
10 changes: 1 addition & 9 deletions packages/cli/src/acp-integration/session/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
AuthType,
ApprovalMode,
CompressionStatus,
isCompressionFailureStatus,
RUNTIME_SNAPSHOT_PREFIX,
detectLoopSentinel,
detectAutonomousSentinel,
Expand Down Expand Up @@ -451,15 +452,6 @@ function isTodoStopGuardPromptText(text: unknown): text is string {
);
}

function isCompressionFailureStatus(status: CompressionStatus): boolean {
return (
status === CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT ||
status === CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR ||
status === CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY ||
status === CompressionStatus.COMPRESSION_FAILED_OUTPUT_TRUNCATED
);
}

/** Finalizes preparations without allowing ACP cleanup to change the stream outcome. */
async function finalizeToolCallPreparations(
tracker: ToolCallPreparationTracker,
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/ca.js
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,12 @@ export default {
"La compressió de l'historial del xat no ha reduït la mida. Això pot indicar problemes amb el missatge de compressió.",
'Could not compress chat history due to a token counting error.':
"No s'ha pogut comprimir l'historial del xat per un error de recompte de tokens.",
'Could not compress chat history because the compression summary was empty.':
"No s'ha pogut comprimir l'historial del xat perquè el resum de compressió era buit.",
'Could not compress chat history because the compression summary was truncated.':
"No s'ha pogut comprimir l'historial del xat perquè el resum de compressió s'ha truncat.",
'Could not compress chat history due to an API error.':
"No s'ha pogut comprimir l'historial del xat per un error de l'API.",
// ============================================================================
// Ordres - Directori
// ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/de.js
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,12 @@ export default {
'Chatverlauf-Komprimierung hat die Größe nicht reduziert. Dies kann auf Probleme mit dem Komprimierungs-Prompt hindeuten.',
'Could not compress chat history due to a token counting error.':
'Chatverlauf konnte aufgrund eines Token-Zählfehlers nicht komprimiert werden.',
'Could not compress chat history because the compression summary was empty.':
'Chatverlauf konnte nicht komprimiert werden, da die Komprimierungszusammenfassung leer war.',
'Could not compress chat history because the compression summary was truncated.':
'Chatverlauf konnte nicht komprimiert werden, da die Komprimierungszusammenfassung abgeschnitten wurde.',
'Could not compress chat history due to an API error.':
'Chatverlauf konnte aufgrund eines API-Fehlers nicht komprimiert werden.',
// ============================================================================
// Commands - Directory
// ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,12 @@ export default {
'Chat history compression did not reduce size. This may indicate issues with the compression prompt.',
'Could not compress chat history due to a token counting error.':
'Could not compress chat history due to a token counting error.',
'Could not compress chat history because the compression summary was empty.':
'Could not compress chat history because the compression summary was empty.',
'Could not compress chat history because the compression summary was truncated.':
'Could not compress chat history because the compression summary was truncated.',
'Could not compress chat history due to an API error.':
'Could not compress chat history due to an API error.',
// ============================================================================
// Commands - Directory
// ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,12 @@ export default {
"La compression de l'historique du chat n'a pas réduit la taille. Cela peut indiquer des problèmes avec l'invite de compression.",
'Could not compress chat history due to a token counting error.':
"Impossible de compresser l'historique du chat en raison d'une erreur de comptage de tokens.",
'Could not compress chat history because the compression summary was empty.':
"Impossible de compresser l'historique du chat, car le résumé de compression était vide.",
'Could not compress chat history because the compression summary was truncated.':
"Impossible de compresser l'historique du chat, car le résumé de compression a été tronqué.",
'Could not compress chat history due to an API error.':
"Impossible de compresser l'historique du chat en raison d'une erreur d'API.",
// ============================================================================
// Commandes - Répertoire
// ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,12 @@ export default {
'チャット履歴の圧縮でサイズが減少しませんでした。圧縮プロンプトに問題がある可能性があります',
'Could not compress chat history due to a token counting error.':
'トークンカウントエラーのため、チャット履歴を圧縮できませんでした',
'Could not compress chat history because the compression summary was empty.':
'圧縮サマリーが空だったため、チャット履歴を圧縮できませんでした',
'Could not compress chat history because the compression summary was truncated.':
'圧縮サマリーが切り詰められたため、チャット履歴を圧縮できませんでした',
'Could not compress chat history due to an API error.':
'API エラーのため、チャット履歴を圧縮できませんでした',
// Directory
'Configuration is not available.': '設定が利用できません',
'Please provide at least one path to add.':
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/pt.js
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,12 @@ export default {
'A compressão do histórico do chat não reduziu o tamanho. Isso pode indicar problemas com o prompt de compressão.',
'Could not compress chat history due to a token counting error.':
'Não foi possível comprimir o histórico do chat devido a um erro de contagem de tokens.',
'Could not compress chat history because the compression summary was empty.':
'Não foi possível comprimir o histórico do chat porque o resumo da compressão estava vazio.',
'Could not compress chat history because the compression summary was truncated.':
'Não foi possível comprimir o histórico do chat porque o resumo da compressão foi truncado.',
'Could not compress chat history due to an API error.':
'Não foi possível comprimir o histórico do chat devido a um erro da API.',
// ============================================================================
// Commands - Directory
// ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/ru.js
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,12 @@ export default {
'Сжатие истории чата не уменьшило размер. Это может указывать на проблемы с промптом сжатия.',
'Could not compress chat history due to a token counting error.':
'Не удалось сжать историю чата из-за ошибки подсчета токенов.',
'Could not compress chat history because the compression summary was empty.':
'Не удалось сжать историю чата, потому что сводка сжатия была пустой.',
'Could not compress chat history because the compression summary was truncated.':
'Не удалось сжать историю чата, потому что сводка сжатия была усечена.',
'Could not compress chat history due to an API error.':
'Не удалось сжать историю чата из-за ошибки API.',
// ============================================================================
// Команды - Директория
// ============================================================================
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/zh-TW.js
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,12 @@ export default {
'聊天歷史壓縮未能減小大小。這可能表明壓縮提示存在問題。',
'Could not compress chat history due to a token counting error.':
'由於 token 計數錯誤,無法壓縮聊天歷史。',
'Could not compress chat history because the compression summary was empty.':
'由於壓縮摘要為空,無法壓縮聊天歷史。',
'Could not compress chat history because the compression summary was truncated.':
'由於壓縮摘要被截斷,無法壓縮聊天歷史。',
'Could not compress chat history due to an API error.':
'由於 API 錯誤,無法壓縮聊天歷史。',
'Configuration is not available.': '配置不可用。',
'Please provide at least one path to add.': '請提供至少一個要添加的路徑。',
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.':
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/i18n/locales/zh.js
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,12 @@ export default {
'聊天历史压缩未能减小大小。这可能表明压缩提示存在问题。',
'Could not compress chat history due to a token counting error.':
'由于 token 计数错误,无法压缩聊天历史。',
'Could not compress chat history because the compression summary was empty.':
'由于压缩摘要为空,无法压缩聊天历史。',
'Could not compress chat history because the compression summary was truncated.':
'由于压缩摘要被截断,无法压缩聊天历史。',
'Could not compress chat history due to an API error.':
'由于 API 错误,无法压缩聊天历史。',
// ============================================================================
// Commands - Directory
// ============================================================================
Expand Down
75 changes: 75 additions & 0 deletions packages/cli/src/ui/commands/compressCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,31 @@ describe('compressCommand', () => {
expect(context.ui.setPendingItem).toHaveBeenCalledWith(null);
});

it('should keep compression failure statuses in the interactive history', async () => {
const failedResult: ChatCompressionInfo = {
originalTokenCount: 100000,
newTokenCount: 100000,
compressionStatus: CompressionStatus.COMPRESSION_FAILED_API_ERROR,
};
mockTryCompressChat.mockResolvedValue(failedResult);

await compressCommand.action!(context, '');

expect(context.ui.addItem).toHaveBeenCalledWith(
{
type: MessageType.COMPRESSION,
compression: {
isPending: false,
compressionStatus: CompressionStatus.COMPRESSION_FAILED_API_ERROR,
originalTokenCount: 100000,
newTokenCount: 100000,
compressionKind: 'summarize',
},
},
expect.any(Number),
);
});

// Issue #9309: after /compress-fast the summarize banner is measured on a
// different scale (local history-only estimate vs the fast banner's
// API-reported baseline), so the compression item must carry per-side
Expand Down Expand Up @@ -166,6 +191,56 @@ describe('compressCommand', () => {
);
});

it('should return an error in non-interactive mode for compression failure statuses', async () => {
const failedResult: ChatCompressionInfo = {
originalTokenCount: 100000,
newTokenCount: 100000,
compressionStatus: CompressionStatus.COMPRESSION_FAILED_API_ERROR,
};
mockTryCompressChat.mockResolvedValue(failedResult);
const ctx = createMockCommandContext({
executionMode: 'non_interactive',
services: context.services,
});

await expect(compressCommand.action!(ctx, '')).resolves.toEqual({
type: 'message',
messageType: 'error',
content: 'Could not compress chat history due to an API error.',
});
});

it('should yield an ACP error for compression failure statuses', async () => {
const failedResult: ChatCompressionInfo = {
originalTokenCount: 100000,
newTokenCount: 100000,
compressionStatus: CompressionStatus.COMPRESSION_FAILED_API_ERROR,
};
mockTryCompressChat.mockResolvedValue(failedResult);
const ctx = createMockCommandContext({
executionMode: 'acp',
services: context.services,
});

const result = await compressCommand.action!(ctx, '');
expect(result?.type).toBe('stream_messages');

const messages = [];
if (result?.type === 'stream_messages') {
for await (const message of result.messages) {
messages.push(message);
}
}

expect(messages).toEqual([
{ messageType: 'info', content: 'Compressing context...' },
{
messageType: 'error',
content: 'Could not compress chat history due to an API error.',
},
]);
});

it('should mark estimated counts in the non-interactive message', async () => {
// Asymmetric flags mirror the real post-/compress-fast scenario and catch
// a swapped flag-argument mutation at the formatTokenCount call sites.
Expand Down
Loading
Loading