({
setMessages,
readFileState,
discoveredSkillNames,
loadedNestedMemoryPaths,
getAppState,
setAppState,
setConversationId,
}: {
setMessages: (updater: (prev: Message[]) => Message[]) => void
readFileState: FileStateCache
discoveredSkillNames?: Set<string>
loadedNestedMemoryPaths?: Set<string>
getAppState?: () => AppState
setAppState?: (f: (prev: AppState) => AppState) => void
setConversationId?: (id: UUID) => void
})
| 47 | import { clearSessionCaches } from './caches.js' |
| 48 | |
| 49 | export async function clearConversation({ |
| 50 | setMessages, |
| 51 | readFileState, |
| 52 | discoveredSkillNames, |
| 53 | loadedNestedMemoryPaths, |
| 54 | getAppState, |
| 55 | setAppState, |
| 56 | setConversationId, |
| 57 | }: { |
| 58 | setMessages: (updater: (prev: Message[]) => Message[]) => void |
| 59 | readFileState: FileStateCache |
| 60 | discoveredSkillNames?: Set<string> |
| 61 | loadedNestedMemoryPaths?: Set<string> |
| 62 | getAppState?: () => AppState |
| 63 | setAppState?: (f: (prev: AppState) => AppState) => void |
| 64 | setConversationId?: (id: UUID) => void |
| 65 | }): Promise<void> { |
| 66 | // Execute SessionEnd hooks before clearing (bounded by |
| 67 | // CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS, default 1.5s) |
| 68 | const sessionEndTimeoutMs = getSessionEndHookTimeoutMs() |
| 69 | await executeSessionEndHooks('clear', { |
| 70 | getAppState, |
| 71 | setAppState, |
| 72 | signal: AbortSignal.timeout(sessionEndTimeoutMs), |
| 73 | timeoutMs: sessionEndTimeoutMs, |
| 74 | }) |
| 75 | |
| 76 | // Signal to inference that this conversation's cache can be evicted. |
| 77 | const lastRequestId = getLastMainRequestId() |
| 78 | if (lastRequestId) { |
| 79 | logEvent('tengu_cache_eviction_hint', { |
| 80 | scope: |
| 81 | 'conversation_clear' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 82 | last_request_id: |
| 83 | lastRequestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 84 | }) |
| 85 | } |
| 86 | |
| 87 | // Compute preserved tasks up front so their per-agent state survives the |
| 88 | // cache wipe below. A task is preserved unless it explicitly has |
| 89 | // isBackgrounded === false. Main-session tasks (Ctrl+B) are preserved — |
| 90 | // they write to an isolated per-task transcript and run under an agent |
| 91 | // context, so they're safe across session ID regeneration. See |
| 92 | // LocalMainSessionTask.ts startBackgroundSession. |
| 93 | const preservedAgentIds = new Set<string>() |
| 94 | const preservedLocalAgents: LocalAgentTaskState[] = [] |
| 95 | const shouldKillTask = (task: AppState['tasks'][string]): boolean => |
| 96 | 'isBackgrounded' in task && task.isBackgrounded === false |
| 97 | if (getAppState) { |
| 98 | for (const task of Object.values(getAppState().tasks)) { |
| 99 | if (shouldKillTask(task)) continue |
| 100 | if (isLocalAgentTask(task)) { |
| 101 | preservedAgentIds.add(task.agentId) |
| 102 | preservedLocalAgents.push(task) |
| 103 | } else if (isInProcessTeammateTask(task)) { |
| 104 | preservedAgentIds.add(task.identity.agentId) |
| 105 | } |
| 106 | } |
no test coverage detected