import type { Session } from '@opencode-ai/sdk/v2'; import type { Part } from '@opencode-ai/sdk/v2/client'; import { notifyGitStatusInvalidated } from '@/lib/gitStatusInvalidation'; import type { WorktreeMetadata } from '@/types/worktree'; export type SessionDeleteRequest = { sessions: Session[]; dateLabel?: string; mode?: 'session' | 'worktree'; worktree?: WorktreeMetadata | null; }; export type SessionCreateRequest = { worktreeMode?: 'main' | 'create' | 'reuse'; parentID?: string | null; projectId?: string | null; }; type DeleteListener = (request: SessionDeleteRequest) => void; type CreateListener = (request: SessionCreateRequest) => void; type DirectoryListener = () => void; type GitRefreshHint = { directory: string; paths?: string[] }; type GitRefreshListener = (hint: GitRefreshHint) => void; const deleteListeners = new Set(); const createListeners = new Set(); const directoryListeners = new Set(); const gitRefreshListeners = new Set(); const gitMutatingTools = new Set(['bash', 'edit', 'write', 'apply_patch', 'patch']); const normalizeToolName = (tool: string): string => { const parts = tool.trim().toLowerCase().split('.').filter(Boolean); return parts[parts.length - 1] ?? ''; }; export const sessionEvents = { onDeleteRequest(listener: DeleteListener) { deleteListeners.add(listener); return () => { deleteListeners.delete(listener); }; }, requestDelete(payload: SessionDeleteRequest) { if (!payload.sessions.length && payload.mode !== 'worktree') { return; } deleteListeners.forEach((listener) => listener(payload)); }, onCreateRequest(listener: CreateListener) { createListeners.add(listener); return () => { createListeners.delete(listener); }; }, requestCreate(payload?: SessionCreateRequest) { const request = payload ?? {}; createListeners.forEach((listener) => listener(request)); }, onDirectoryRequest(listener: DirectoryListener) { directoryListeners.add(listener); return () => { directoryListeners.delete(listener); }; }, requestDirectoryDialog() { directoryListeners.forEach((listener) => listener()); }, onGitRefreshHint(listener: GitRefreshListener) { gitRefreshListeners.add(listener); return () => { gitRefreshListeners.delete(listener); }; }, requestGitRefresh(hint: GitRefreshHint) { if (!hint.directory.trim()) { return; } notifyGitStatusInvalidated(hint.directory); gitRefreshListeners.forEach((listener) => listener(hint)); }, requestGitRefreshForToolTransition(directory: string, previousPart: Part | undefined, nextPart: Part) { if (nextPart.type !== 'tool' || nextPart.state.status !== 'completed') { return; } if (previousPart?.type === 'tool' && previousPart.state.status === 'completed') { return; } if (!gitMutatingTools.has(normalizeToolName(nextPart.tool))) { return; } sessionEvents.requestGitRefresh({ directory }); }, };