diff --git a/src/ai/acp-transport.ts b/src/ai/acp-transport.ts index aab99f0e3..47bc39da9 100644 --- a/src/ai/acp-transport.ts +++ b/src/ai/acp-transport.ts @@ -62,9 +62,19 @@ interface AcpDebugEntry { data: unknown } +const MAX_LOG_AGE_MS = 5 * 60 * 1000 + export const acpDebugLog: AcpDebugEntry[] = [] +function pruneOldEntries() { + const cutoff = Date.now() - MAX_LOG_AGE_MS + while (acpDebugLog.length > 0 && acpDebugLog[0].ts < cutoff) { + acpDebugLog.shift() + } +} + export function getAcpDebugText(): string { + pruneOldEntries() return acpDebugLog.map((e) => `[${new Date(e.ts).toISOString()}] ${e.type}\n${JSON.stringify(e.data, null, 2)}` ).join('\n\n---\n\n') @@ -74,6 +84,22 @@ export function clearAcpDebugLog() { acpDebugLog.length = 0 } +export async function saveAcpDebugLog(): Promise { + pruneOldEntries() + const text = getAcpDebugText() + if (!text) return null + try { + const { writeTextFile } = await import('@tauri-apps/plugin-fs') + const { join, desktopDir } = await import('@tauri-apps/api/path') + const filename = `acp-debug-${new Date().toISOString().replace(/[:.]/g, '-')}.log` + const path = await join(await desktopDir(), filename) + await writeTextFile(path, text) + return path + } catch { + return null + } +} + export class ACPChatTransport implements ChatTransport { private session: ACPSession | null = null private agentDef: ACPAgentDef diff --git a/src/components/ChatPanel.vue b/src/components/ChatPanel.vue index 67eac88f7..98fe45154 100644 --- a/src/components/ChatPanel.vue +++ b/src/components/ChatPanel.vue @@ -2,7 +2,7 @@ import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from 'reka-ui' import { computed, markRaw, nextTick, ref, watch } from 'vue' -import { getAcpDebugText, clearAcpDebugLog } from '@/ai/acp-transport' +import { getAcpDebugText, clearAcpDebugLog, saveAcpDebugLog } from '@/ai/acp-transport' import { copyChatLog } from '@/ai/chat-debug' import { clearToolLogEntries, didHitStepLimit } from '@/ai/tools' import AcpPermissionDialog from '@/components/chat/AcpPermissionDialog.vue' @@ -92,6 +92,7 @@ async function handleCopyAcpLog() { const text = getAcpDebugText() if (!text) return await navigator.clipboard.writeText(text) + void saveAcpDebugLog() acpLogCopied.value = true setTimeout(() => { acpLogCopied.value = false }, 1500) }