| |
| |
| |
| |
| import { feature } from 'bun:bundle' |
| import { randomUUID, type UUID } from 'crypto' |
| import { |
| getLastMainRequestId, |
| getOriginalCwd, |
| getSessionId, |
| regenerateSessionId, |
| } from '../../bootstrap/state.js' |
| import { |
| type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| logEvent, |
| } from '../../services/analytics/index.js' |
| import type { AppState } from '../../state/AppState.js' |
| import { isInProcessTeammateTask } from '../../tasks/InProcessTeammateTask/types.js' |
| import { |
| isLocalAgentTask, |
| type LocalAgentTaskState, |
| } from '../../tasks/LocalAgentTask/LocalAgentTask.js' |
| import { isLocalShellTask } from '../../tasks/LocalShellTask/guards.js' |
| import { asAgentId } from '../../types/ids.js' |
| import type { Message } from '../../types/message.js' |
| import { createEmptyAttributionState } from '../../utils/commitAttribution.js' |
| import type { FileStateCache } from '../../utils/fileStateCache.js' |
| import { |
| executeSessionEndHooks, |
| getSessionEndHookTimeoutMs, |
| } from '../../utils/hooks.js' |
| import { logError } from '../../utils/log.js' |
| import { clearAllPlanSlugs } from '../../utils/plans.js' |
| import { setCwd } from '../../utils/Shell.js' |
| import { processSessionStartHooks } from '../../utils/sessionStart.js' |
| import { |
| clearSessionMetadata, |
| getAgentTranscriptPath, |
| resetSessionFilePointer, |
| saveWorktreeState, |
| } from '../../utils/sessionStorage.js' |
| import { |
| evictTaskOutput, |
| initTaskOutputAsSymlink, |
| } from '../../utils/task/diskOutput.js' |
| import { getCurrentWorktreeSession } from '../../utils/worktree.js' |
| import { clearSessionCaches } from './caches.js' |
|
|
| export async function clearConversation({ |
| 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 |
| }): Promise<void> { |
| |
| |
| const sessionEndTimeoutMs = getSessionEndHookTimeoutMs() |
| await executeSessionEndHooks('clear', { |
| getAppState, |
| setAppState, |
| signal: AbortSignal.timeout(sessionEndTimeoutMs), |
| timeoutMs: sessionEndTimeoutMs, |
| }) |
|
|
| |
| const lastRequestId = getLastMainRequestId() |
| if (lastRequestId) { |
| logEvent('tengu_cache_eviction_hint', { |
| scope: |
| 'conversation_clear' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| last_request_id: |
| lastRequestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| }) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| const preservedAgentIds = new Set<string>() |
| const preservedLocalAgents: LocalAgentTaskState[] = [] |
| const shouldKillTask = (task: AppState['tasks'][string]): boolean => |
| 'isBackgrounded' in task && task.isBackgrounded === false |
| if (getAppState) { |
| for (const task of Object.values(getAppState().tasks)) { |
| if (shouldKillTask(task)) continue |
| if (isLocalAgentTask(task)) { |
| preservedAgentIds.add(task.agentId) |
| preservedLocalAgents.push(task) |
| } else if (isInProcessTeammateTask(task)) { |
| preservedAgentIds.add(task.identity.agentId) |
| } |
| } |
| } |
|
|
| setMessages(() => []) |
|
|
| |
| if (feature('PROACTIVE') || feature('KAIROS')) { |
| |
| const { setContextBlocked } = require('../../proactive/index.js') |
| |
| setContextBlocked(false) |
| } |
|
|
| |
| if (setConversationId) { |
| setConversationId(randomUUID()) |
| } |
|
|
| |
| |
| |
| clearSessionCaches(preservedAgentIds) |
|
|
| setCwd(getOriginalCwd()) |
| readFileState.clear() |
| discoveredSkillNames?.clear() |
| loadedNestedMemoryPaths?.clear() |
|
|
| |
| if (setAppState) { |
| setAppState(prev => { |
| |
| |
| const nextTasks: AppState['tasks'] = {} |
| for (const [taskId, task] of Object.entries(prev.tasks)) { |
| if (!shouldKillTask(task)) { |
| nextTasks[taskId] = task |
| continue |
| } |
| |
| try { |
| if (task.status === 'running') { |
| if (isLocalShellTask(task)) { |
| task.shellCommand?.kill() |
| task.shellCommand?.cleanup() |
| if (task.cleanupTimeoutId) { |
| clearTimeout(task.cleanupTimeoutId) |
| } |
| } |
| if ('abortController' in task) { |
| task.abortController?.abort() |
| } |
| if ('unregisterCleanup' in task) { |
| task.unregisterCleanup?.() |
| } |
| } |
| } catch (error) { |
| logError(error) |
| } |
| void evictTaskOutput(taskId) |
| } |
|
|
| return { |
| ...prev, |
| tasks: nextTasks, |
| attribution: createEmptyAttributionState(), |
| |
| |
| standaloneAgentContext: undefined, |
| fileHistory: { |
| snapshots: [], |
| trackedFiles: new Set(), |
| snapshotSequence: 0, |
| }, |
| |
| |
| |
| mcp: { |
| clients: [], |
| tools: [], |
| commands: [], |
| resources: {}, |
| pluginReconnectKey: prev.mcp.pluginReconnectKey, |
| }, |
| } |
| }) |
| } |
|
|
| |
| clearAllPlanSlugs() |
|
|
| |
| |
| clearSessionMetadata() |
|
|
| |
| |
| regenerateSessionId({ setCurrentAsParent: true }) |
| |
| if (process.env.USER_TYPE === 'ant' && process.env.CLAUDE_CODE_SESSION_ID) { |
| process.env.CLAUDE_CODE_SESSION_ID = getSessionId() |
| } |
| await resetSessionFilePointer() |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| for (const task of preservedLocalAgents) { |
| if (task.status !== 'running') continue |
| void initTaskOutputAsSymlink( |
| task.id, |
| getAgentTranscriptPath(asAgentId(task.agentId)), |
| ) |
| } |
|
|
| |
| |
| |
| |
| if (feature('COORDINATOR_MODE')) { |
| |
| const { saveMode } = require('../../utils/sessionStorage.js') |
| const { |
| isCoordinatorMode, |
| } = require('../../coordinator/coordinatorMode.js') |
| |
| saveMode(isCoordinatorMode() ? 'coordinator' : 'normal') |
| } |
| const worktreeSession = getCurrentWorktreeSession() |
| if (worktreeSession) { |
| saveWorktreeState(worktreeSession) |
| } |
|
|
| |
| const hookMessages = await processSessionStartHooks('clear') |
|
|
| |
| if (hookMessages.length > 0) { |
| setMessages(() => hookMessages) |
| } |
| } |
|
|