// Database types for renderer
interface Connection {
  id: string
  name: string
  custom_name: string | null
  status: 'active' | 'archived'
  path: string
  color: string | null // JSON-serialised ConnectionColorQuad
  pinned: number // 0 = not pinned, 1 = pinned
  created_at: string
  updated_at: string
}

interface ConnectionMember {
  id: string
  connection_id: string
  worktree_id: string
  project_id: string
  symlink_name: string
  added_at: string
}

interface ConnectionWithMembers extends Connection {
  members: (ConnectionMember & {
    worktree_name: string
    worktree_branch: string
    worktree_path: string
    project_name: string
  })[]
}

interface Project {
  id: string
  name: string
  path: string
  description: string | null
  tags: string | null
  language: string | null
  custom_icon: string | null
  setup_script: string | null
  run_script: string | null
  archive_script: string | null
  auto_assign_port: boolean
  sort_order: number
  created_at: string
  last_accessed_at: string
}

interface Worktree {
  id: string
  project_id: string
  name: string
  branch_name: string
  path: string
  status: 'active' | 'archived'
  is_default: boolean
  branch_renamed: number // 0 = auto-named (city), 1 = user/auto renamed
  last_message_at: number | null // epoch ms of last AI message activity
  session_titles: string // JSON array of session title strings
  last_model_provider_id: string | null
  last_model_id: string | null
  last_model_variant: string | null
  last_agent_sdk: 'opencode' | 'claude-code' | 'codex' | 'terminal' | null
  attachments: string // JSON array of Attachment objects
  pinned: number // 0 = not pinned, 1 = pinned
  context: string | null
  github_pr_number: number | null
  github_pr_url: string | null
  created_at: string
  last_accessed_at: string
}

interface DiffComment {
  id: string
  worktreeId: string
  filePath: string
  side: 'original' | 'modified'
  lineNumber: number
  compareBranch: string | null
  staged: boolean
  body: string
  resolved: boolean
  createdAt: number
  updatedAt: number
}

interface Session {
  id: string
  worktree_id: string | null
  project_id: string
  connection_id: string | null
  name: string | null
  status: 'active' | 'completed' | 'error' | 'archived'
  opencode_session_id: string | null
  agent_sdk: 'opencode' | 'claude-code' | 'codex' | 'terminal'
  mode: 'build' | 'plan'
  model_provider_id: string | null
  model_id: string | null
  model_variant: string | null
  first_message_at: number | null
  created_at: string
  updated_at: string
  completed_at: string | null
}

interface Setting {
  key: string
  value: string
}

interface SessionMessage {
  id: string
  session_id: string
  role: 'user' | 'assistant' | 'system'
  content: string
  opencode_message_id: string | null
  opencode_message_json: string | null
  opencode_parts_json: string | null
  opencode_timeline_json: string | null
  created_at: string
}

type SessionActivityKind =
  | 'tool.started'
  | 'tool.updated'
  | 'tool.completed'
  | 'tool.failed'
  | 'approval.requested'
  | 'approval.resolved'
  | 'user-input.requested'
  | 'user-input.resolved'
  | 'task.started'
  | 'task.updated'
  | 'task.completed'
  | 'plan.ready'
  | 'plan.resolved'
  | 'session.error'
  | 'session.retry'
  | 'session.info'

type SessionActivityTone = 'tool' | 'approval' | 'info' | 'error'

interface SessionActivity {
  id: string
  session_id: string
  agent_session_id: string | null
  thread_id: string | null
  turn_id: string | null
  item_id: string | null
  request_id: string | null
  kind: SessionActivityKind
  tone: SessionActivityTone
  summary: string
  payload_json: string | null
  sequence: number | null
  created_at: string
}

interface SessionWithWorktree extends Session {
  worktree_name?: string
  worktree_branch_name?: string
  worktree_status?: 'active' | 'archived' | null
  project_name?: string
}

interface SessionSearchOptions {
  keyword?: string
  project_id?: string
  worktree_id?: string
  dateFrom?: string
  dateTo?: string
  includeArchived?: boolean
  statusFilter?: 'all' | 'active' | 'archived' | 'closed'
}

type OnboardingStatus = 'ready' | 'warning' | 'missing'
type OnboardingEnvironmentReason = 'installed' | 'missing' | 'outdated'
type OnboardingAgentReason = 'ready' | 'missing' | 'login_required' | 'auth_unknown'
type OnboardingAgentAuthStatus = 'authenticated' | 'unauthenticated' | 'unknown' | 'not_applicable'

interface OnboardingEnvironmentCheck {
  id: 'git' | 'node' | 'homebrew' | 'xcode-cli'
  status: OnboardingStatus
  reason: OnboardingEnvironmentReason
  version: string | null
}

interface OnboardingAgentStatus {
  id: 'claude-code' | 'codex' | 'opencode'
  status: OnboardingStatus
  reason: OnboardingAgentReason
  installed: boolean
  selectable: boolean
  version: string | null
  authStatus: OnboardingAgentAuthStatus
}

interface OnboardingDoctorResult {
  platform: NodeJS.Platform
  environmentChecks: OnboardingEnvironmentCheck[]
  agents: OnboardingAgentStatus[]
  recommendedAgent: 'claude-code' | 'codex' | 'opencode' | 'terminal'
}

declare global {
  interface GhosttyTerminalConfig {
    fontFamily?: string
    fontSize?: number
    background?: string
    foreground?: string
    cursorStyle?: 'block' | 'bar' | 'underline'
    cursorColor?: string
    shell?: string
    scrollbackLimit?: number
    palette?: Record<number, string>
    selectionBackground?: string
    selectionForeground?: string
  }

  interface Space {
    id: string
    name: string
    icon_type: string
    icon_value: string
    sort_order: number
    created_at: string
  }

  interface ProjectSpaceAssignment {
    project_id: string
    space_id: string
  }

  interface Window {
    db: {
      setting: {
        get: (key: string) => Promise<string | null>
        set: (key: string, value: string) => Promise<boolean>
        delete: (key: string) => Promise<boolean>
        getAll: () => Promise<Setting[]>
      }
      project: {
        create: (data: {
          name: string
          path: string
          description?: string | null
          tags?: string[] | null
        }) => Promise<Project>
        get: (id: string) => Promise<Project | null>
        getByPath: (path: string) => Promise<Project | null>
        getAll: () => Promise<Project[]>
        update: (
          id: string,
          data: {
            name?: string
            description?: string | null
            tags?: string[] | null
            language?: string | null
            custom_icon?: string | null
            setup_script?: string | null
            run_script?: string | null
            archive_script?: string | null
            auto_assign_port?: boolean
            last_accessed_at?: string
          }
        ) => Promise<Project | null>
        delete: (id: string) => Promise<boolean>
        touch: (id: string) => Promise<boolean>
        reorder: (orderedIds: string[]) => Promise<boolean>
        sortByLastMessage: () => Promise<string[]>
      }
      worktree: {
        create: (data: {
          project_id: string
          name: string
          branch_name: string
          path: string
        }) => Promise<Worktree>
        get: (id: string) => Promise<Worktree | null>
        getByProject: (projectId: string) => Promise<Worktree[]>
        getActiveByProject: (projectId: string) => Promise<Worktree[]>
        getRecentlyActive: (cutoffMs: number) => Promise<Worktree[]>
        update: (
          id: string,
          data: {
            name?: string
            status?: 'active' | 'archived'
            last_message_at?: number | null
            last_accessed_at?: string
            last_agent_sdk?: 'opencode' | 'claude-code' | 'codex' | 'terminal' | null
          }
        ) => Promise<Worktree | null>
        delete: (id: string) => Promise<boolean>
        archive: (id: string) => Promise<Worktree | null>
        touch: (id: string) => Promise<boolean>
        appendSessionTitle: (
          worktreeId: string,
          title: string
        ) => Promise<{ success: boolean; error?: string }>
        updateModel: (params: {
          worktreeId: string
          modelProviderId: string
          modelId: string
          modelVariant: string | null
        }) => Promise<{ success: boolean; error?: string }>
        addAttachment: (
          worktreeId: string,
          attachment: { type: 'jira' | 'figma'; url: string; label: string }
        ) => Promise<{ success: boolean; error?: string }>
        removeAttachment: (
          worktreeId: string,
          attachmentId: string
        ) => Promise<{ success: boolean; error?: string }>
        attachPR: (
          worktreeId: string,
          prNumber: number,
          prUrl: string
        ) => Promise<{ success: boolean; error?: string }>
        detachPR: (worktreeId: string) => Promise<{ success: boolean; error?: string }>
        setPinned: (
          worktreeId: string,
          pinned: boolean
        ) => Promise<{ success: boolean; error?: string }>
        getPinned: () => Promise<Worktree[]>
      }
      diffComment: {
        list: (
          worktreeId: string,
          options?: { filePath?: string; compareBranch?: string | null; staged?: boolean }
        ) => Promise<DiffComment[]>
        create: (data: {
          worktreeId: string
          filePath: string
          side: 'original' | 'modified'
          lineNumber: number
          compareBranch?: string | null
          staged?: boolean
          body: string
        }) => Promise<DiffComment>
        update: (
          id: string,
          data: { body?: string; resolved?: boolean }
        ) => Promise<DiffComment | null>
        delete: (id: string) => Promise<boolean>
      }
      session: {
        create: (data: {
          worktree_id: string | null
          project_id: string
          connection_id?: string | null
          name?: string | null
          opencode_session_id?: string | null
          agent_sdk?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
          model_provider_id?: string | null
          model_id?: string | null
          model_variant?: string | null
        }) => Promise<Session>
        get: (id: string) => Promise<Session | null>
        getByWorktree: (worktreeId: string) => Promise<Session[]>
        getByProject: (projectId: string) => Promise<Session[]>
        getActiveByWorktree: (worktreeId: string) => Promise<Session[]>
        update: (
          id: string,
          data: {
            name?: string | null
            status?: 'active' | 'completed' | 'error' | 'archived'
            opencode_session_id?: string | null
            agent_sdk?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
            mode?: 'build' | 'plan'
            model_provider_id?: string | null
            model_id?: string | null
            model_variant?: string | null
            updated_at?: string
            completed_at?: string | null
          }
        ) => Promise<Session | null>
        delete: (id: string) => Promise<boolean>
        restore: (id: string) => Promise<Session | null>
        search: (options: SessionSearchOptions) => Promise<SessionWithWorktree[]>
        getDraft: (sessionId: string) => Promise<string | null>
        updateDraft: (sessionId: string, draft: string | null) => Promise<void>
        getByConnection: (connectionId: string) => Promise<Session[]>
        getActiveByConnection: (connectionId: string) => Promise<Session[]>
      }
      sessionMessage: {
        list: (sessionId: string) => Promise<SessionMessage[]>
      }
      sessionActivity: {
        list: (sessionId: string) => Promise<SessionActivity[]>
      }
      space: {
        list: () => Promise<Space[]>
        create: (data: { name: string; icon_type?: string; icon_value?: string }) => Promise<Space>
        update: (
          id: string,
          data: {
            name?: string
            icon_type?: string
            icon_value?: string
            sort_order?: number
          }
        ) => Promise<Space | null>
        delete: (id: string) => Promise<boolean>
        assignProject: (projectId: string, spaceId: string) => Promise<boolean>
        removeProject: (projectId: string, spaceId: string) => Promise<boolean>
        getProjectIds: (spaceId: string) => Promise<string[]>
        getAllAssignments: () => Promise<ProjectSpaceAssignment[]>
        reorder: (orderedIds: string[]) => Promise<boolean>
      }
      schemaVersion: () => Promise<number>
      tableExists: (tableName: string) => Promise<boolean>
      getIndexes: () => Promise<{ name: string; tbl_name: string }[]>
    }
    projectOps: {
      openDirectoryDialog: () => Promise<string | null>
      isGitRepository: (path: string) => Promise<boolean>
      validateProject: (path: string) => Promise<{
        success: boolean
        path?: string
        name?: string
        error?: string
      }>
      showInFolder: (path: string) => Promise<void>
      openPath: (path: string) => Promise<string>
      copyToClipboard: (text: string) => Promise<void>
      readFromClipboard: () => Promise<string>
      detectLanguage: (projectPath: string) => Promise<string | null>
      findXcworkspace: (projectPath: string) => Promise<string | null>
      isAndroidProject: (projectPath: string) => Promise<boolean>
      loadLanguageIcons: () => Promise<Record<string, string>>
      initRepository: (path: string) => Promise<{ success: boolean; error?: string }>
      pickProjectIcon: (projectId: string) => Promise<{
        success: boolean
        filename?: string
        error?: string
      }>
      removeProjectIcon: (projectId: string) => Promise<{
        success: boolean
        error?: string
      }>
      getProjectIconPath: (filename: string) => Promise<string | null>
    }
    worktreeOps: {
      hasCommits: (projectPath: string) => Promise<boolean>
      create: (params: { projectId: string; projectPath: string; projectName: string }) => Promise<{
        success: boolean
        worktree?: Worktree
        error?: string
      }>
      delete: (params: {
        worktreeId: string
        worktreePath: string
        branchName: string
        projectPath: string
        archive: boolean
      }) => Promise<{
        success: boolean
        error?: string
      }>
      sync: (params: { projectId: string; projectPath: string }) => Promise<{
        success: boolean
        error?: string
      }>
      exists: (worktreePath: string) => Promise<boolean>
      openInTerminal: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      openInEditor: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      getBranches: (projectPath: string) => Promise<{
        success: boolean
        branches?: string[]
        currentBranch?: string
        error?: string
      }>
      branchExists: (projectPath: string, branchName: string) => Promise<boolean>
      duplicate: (params: {
        projectId: string
        projectPath: string
        projectName: string
        sourceBranch: string
        sourceWorktreePath: string
      }) => Promise<{
        success: boolean
        worktree?: Worktree
        error?: string
      }>
      renameBranch: (
        worktreeId: string,
        worktreePath: string,
        oldBranch: string,
        newBranch: string
      ) => Promise<{ success: boolean; error?: string }>
      createFromBranch: (
        projectId: string,
        projectPath: string,
        projectName: string,
        branchName: string,
        prNumber?: number
      ) => Promise<{
        success: boolean
        worktree?: Worktree
        error?: string
      }>
      // Subscribe to branch-renamed events (auto-rename from main process)
      onBranchRenamed: (
        callback: (data: { worktreeId: string; newBranch: string }) => void
      ) => () => void
      getContext: (worktreeId: string) => Promise<{
        success: boolean
        context?: string | null
        error?: string
      }>
      updateContext: (
        worktreeId: string,
        context: string | null
      ) => Promise<{ success: boolean; error?: string }>
    }
    systemOps: {
      getLogDir: () => Promise<string>
      getAppVersion: () => Promise<string>
      getAppPaths: () => Promise<{
        userData: string
        home: string
        logs: string
      }>
      isLogMode: () => Promise<boolean>
      detectAgentRuntimes: () => Promise<{ opencode: boolean; claude: boolean; codex: boolean }>
      setKeepAwakeEnabled: (enabled: boolean) => Promise<{ success: boolean }>
      setSessionQueuedState: (sessionId: string, queued: boolean) => Promise<{ success: boolean }>
      runOnboardingDoctor: () => Promise<OnboardingDoctorResult>
      detectKeybindingImportSources: () => Promise<
        Array<{
          id: 'vscode' | 'cursor'
          path: string
          exists: boolean
          available: boolean
        }>
      >
      parseKeybindingImportSource: (source: 'vscode' | 'cursor') => Promise<{
        source: 'vscode' | 'cursor'
        path: string
        parsedRows: number
        entries: Array<{
          shortcutId: string
          binding: { key: string; modifiers: Array<'ctrl' | 'meta' | 'alt' | 'shift'> }
          sourceCommand: string
          sourceKey: string
        }>
        unmapped: string[]
        contextScoped: number
        errors: string[]
      }>
      checkFullDiskAccess: (force?: boolean) => Promise<{ supported: boolean; granted: boolean }>
      openFullDiskAccessSettings: () => Promise<{ success: boolean; error?: string }>
      openCommandInTerminal: (
        command: string,
        options?: { cwd?: string }
      ) => Promise<{ success: boolean; error?: string }>
      quitApp: () => Promise<void>
      openInApp: (appName: string, path: string) => Promise<{ success: boolean; error?: string }>
      openInChrome: (
        url: string,
        customCommand?: string
      ) => Promise<{ success: boolean; error?: string }>
      onNewSessionShortcut: (callback: () => void) => () => void
      onCloseSessionShortcut: (callback: () => void) => () => void
      onFileSearchShortcut: (callback: () => void) => () => void
      onNotificationNavigate: (
        callback: (data: { projectId: string; worktreeId: string; sessionId: string }) => void
      ) => () => void
      onWindowFocused: (callback: () => void) => () => void
      updateMenuState: (state: {
        hasActiveSession: boolean
        hasActiveWorktree: boolean
        canUndo?: boolean
        canRedo?: boolean
      }) => Promise<void>
      onMenuAction: (channel: string, callback: () => void) => () => void
      isPackaged: () => Promise<boolean>
      installServerToPath: () => Promise<{ success: boolean; path?: string; error?: string }>
      uninstallServerFromPath: () => Promise<{ success: boolean; error?: string }>
      getPlatform: () => Promise<string>
      setZoomLevel: (level: number) => Promise<{ success: boolean; level?: number }>
      getZoomLevel: () => Promise<number>
      onZoomChanged: (callback: (level: number) => void) => () => void
    }
    loggingOps: {
      createResponseLog: (sessionId: string) => Promise<string>
      appendResponseLog: (filePath: string, data: unknown) => Promise<void>
    }
    agentOps: {
      // Connect to agent runtime for a worktree (lazy starts server if needed)
      connect: (
        worktreePath: string,
        hiveSessionId: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult<{ sessionId?: string }>>
      // Reconnect to existing agent session
      reconnect: (
        worktreePath: string,
        sessionId: string,
        hiveSessionId: string
      ) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{
          sessionStatus?: 'idle' | 'busy' | 'retry'
          revertMessageID?: string | null
        }>
      >
      // Send a prompt (response streams via onStream)
      // Accepts either a string message or a MessagePart[] array for rich content (text + file attachments)
      prompt: (
        worktreePath: string,
        sessionId: string,
        messageOrParts: string | MessagePart[],
        model?: { providerID: string; modelID: string; variant?: string },
        options?: {
          codexFastMode?: boolean
          mode?: 'build' | 'plan'
          goalMode?: boolean
          successCriteria?: string
        }
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Abort a streaming session
      abort: (
        worktreePath: string,
        sessionId: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult<{ aborted?: boolean }>>
      // Disconnect session (may kill server if last session for worktree)
      disconnect: (
        worktreePath: string,
        sessionId: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Get messages from an agent session
      getMessages: (
        worktreePath: string,
        sessionId: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult<{ messages: unknown[] }>>
      // List available models from all configured providers
      listModels: (opts?: {
        runtimeId?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
      }) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{ providers: Record<string, unknown> }>
      >
      // Set the selected model for prompts
      setModel: (model: {
        providerID: string
        modelID: string
        variant?: string
        runtimeId?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
      }) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Get model info (name, context limit)
      modelInfo: (
        worktreePath: string,
        modelId: string,
        runtimeId?: 'opencode' | 'claude-code' | 'codex' | 'terminal'
      ) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{
          model?: { id: string; name: string; limit: { context: number } }
        }>
      >
      // Reply to a pending question from the AI
      questionReply: (
        requestId: string,
        answers: string[][],
        worktreePath?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Reject/dismiss a pending question from the AI
      questionReject: (
        requestId: string,
        worktreePath?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Approve a pending plan (ExitPlanMode) — unblocks the SDK to implement
      planApprove: (
        worktreePath: string,
        hiveSessionId: string,
        requestId?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Reject a pending plan with user feedback — Claude will revise
      planReject: (
        worktreePath: string,
        hiveSessionId: string,
        feedback: string,
        requestId?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Reply to a pending permission request (allow once, allow always, or reject)
      permissionReply: (
        requestId: string,
        reply: 'once' | 'always' | 'reject',
        worktreePath?: string,
        message?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // List all pending permission requests
      permissionList: (
        worktreePath?: string
      ) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{ permissions: PermissionRequest[] }>
      >
      // Reply to a pending command approval request (approve or deny, optionally add to allowlist/blocklist)
      commandApprovalReply: (
        requestId: string,
        approved: boolean,
        remember?: 'allow' | 'block',
        pattern?: string,
        worktreePath?: string,
        patterns?: string[]
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Get session info (revert state)
      sessionInfo: (
        worktreePath: string,
        sessionId: string
      ) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{
          revertMessageID?: string | null
          revertDiff?: string | null
        }>
      >
      // Undo the last assistant turn/message range
      undo: (
        worktreePath: string,
        sessionId: string
      ) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{
          revertMessageID?: string
          restoredPrompt?: string
          revertDiff?: string | null
        }>
      >
      // Redo the last undone message range
      redo: (
        worktreePath: string,
        sessionId: string
      ) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{ revertMessageID?: string | null }>
      >
      // Send a slash command to a session via the SDK command endpoint
      command: (
        worktreePath: string,
        sessionId: string,
        command: string,
        args: string,
        model?: { providerID: string; modelID: string; variant?: string }
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // List available slash commands from the SDK
      commands: (
        worktreePath: string,
        sessionId?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult<{ commands: AgentCommand[] }>>
      steer: (
        worktreePath: string,
        sessionId: string,
        messageOrParts: string | MessagePart[],
        model?: { providerID: string; modelID: string; variant?: string },
        options?: { codexFastMode?: boolean }
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Rename a session's title via the agent PATCH API
      renameSession: (
        sessionId: string,
        title: string,
        worktreePath?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult>
      // Get SDK capabilities for the current session
      capabilities: (sessionId?: string) => Promise<
        import('../shared/types/agent-ipc').AgentIpcResult<{
          capabilities?: {
            supportsUndo: boolean
            supportsRedo: boolean
            supportsSteer: boolean
            supportsCommands: boolean
            supportsPermissionRequests: boolean
            supportsQuestionPrompts: boolean
            supportsModelSelection: boolean
            supportsReconnect: boolean
            supportsPartialStreaming: boolean
          }
        }>
      >
      // Fork an existing session at an optional message boundary
      fork: (
        worktreePath: string,
        sessionId: string,
        messageId?: string
      ) => Promise<import('../shared/types/agent-ipc').AgentIpcResult<{ sessionId?: string }>>
      // Get unified timeline (durable data from DB)
      getTimeline: (
        sessionId: string
      ) => Promise<import('../shared/lib/timeline-types').TimelineResult>
      // Subscribe to streaming events
      onStream: (callback: (event: CanonicalAgentEvent) => void) => () => void
    }
    fileTreeOps: {
      // Scan a directory and return the file tree
      scan: (dirPath: string) => Promise<{
        success: boolean
        tree?: FileTreeNode[]
        error?: string
      }>
      // Scan a directory and return a flat list of all files (via git ls-files)
      scanFlat: (dirPath: string) => Promise<{
        success: boolean
        files?: FlatFile[]
        error?: string
      }>
      // Lazy load children for a directory
      loadChildren: (
        dirPath: string,
        rootPath: string
      ) => Promise<{
        success: boolean
        children?: FileTreeNode[]
        error?: string
      }>
      // Start watching a directory for changes
      watch: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Stop watching a directory
      unwatch: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Subscribe to file tree change events
      onChange: (callback: (event: FileTreeChangeEvent) => void) => () => void
    }
    fileOps: {
      readFile: (filePath: string) => Promise<{
        success: boolean
        content?: string
        error?: string
      }>
      readArchive: (filePath: string) => Promise<{
        success: boolean
        content?: string
        error?: string
      }>
      writeFile: (
        filePath: string,
        content: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      readPrompt: (promptName: string) => Promise<{
        success: boolean
        content?: string
        error?: string
      }>
      readImageAsBase64: (filePath: string) => Promise<{
        success: boolean
        data?: string
        mimeType?: string
        error?: string
      }>
      getPathForFile: (file: File) => string
    }
    settingsOps: {
      detectEditors: () => Promise<DetectedApp[]>
      detectTerminals: () => Promise<DetectedApp[]>
      openWithEditor: (
        worktreePath: string,
        editorId: string,
        customCommand?: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      openWithTerminal: (
        worktreePath: string,
        terminalId: string,
        customCommand?: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      onSettingsUpdated: (callback: (data: unknown) => void) => () => void
    }
    voiceOps: {
      detectRuntime: (
        config?: import('../shared/types/voice').VoiceRuntimeConfig
      ) => Promise<import('../shared/types/voice').VoiceRuntimeInfo>
      ensureRuntime: (
        config?: import('../shared/types/voice').VoiceRuntimeConfig
      ) => Promise<import('../shared/types/voice').VoiceRuntimeInfo>
      startRuntime: (
        config?: import('../shared/types/voice').VoiceRuntimeConfig
      ) => Promise<import('../shared/types/voice').VoiceRuntimeInfo>
      stopRuntime: () => Promise<{ success: boolean; error?: string }>
      getRuntimeLogs: () => Promise<{ installLog?: string; serverLog?: string }>
      getMicrophonePermissionStatus: () => Promise<
        import('../shared/types/voice').VoicePermissionStatus
      >
      requestMicrophonePermission: () => Promise<
        import('../shared/types/voice').VoicePermissionStatus
      >
      connectTranscription: (
        options: import('../shared/types/voice').VoiceTranscriptionSessionOptions
      ) => Promise<import('../shared/types/voice').VoiceTranscriptionSession>
      sendAudioChunk: (sessionId: string, chunk: ArrayBuffer) => Promise<void>
      finishUtterance: (sessionId: string) => Promise<void>
      disconnectTranscription: (sessionId: string) => Promise<void>
      onRuntimeProgress: (
        callback: (progress: import('../shared/types/voice').VoiceRuntimeProgress) => void
      ) => () => void
      onTranscript: (
        callback: (event: import('../shared/types/voice').VoiceTranscriptEvent) => void
      ) => () => void
      onVoiceError: (
        callback: (event: import('../shared/types/voice').VoiceErrorEvent) => void
      ) => () => void
    }
    scriptOps: {
      runSetup: (
        commands: string[],
        cwd: string,
        worktreeId: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      runProject: (
        commands: string[],
        cwd: string,
        worktreeId: string
      ) => Promise<{
        success: boolean
        pid?: number
        error?: string
      }>
      kill: (worktreeId: string) => Promise<{
        success: boolean
        error?: string
      }>
      runArchive: (
        commands: string[],
        cwd: string
      ) => Promise<{
        success: boolean
        output: string
        error?: string
      }>
      onOutput: (channel: string, callback: (event: ScriptOutputEvent) => void) => () => void
      offOutput: (channel: string) => void
      getPort: (cwd: string) => Promise<{ port: number | null }>
    }
    terminalOps: {
      create: (
        worktreeId: string,
        cwd: string,
        shell?: string
      ) => Promise<{ success: boolean; cols?: number; rows?: number; error?: string }>
      write: (worktreeId: string, data: string) => void
      resize: (worktreeId: string, cols: number, rows: number) => Promise<void>
      destroy: (worktreeId: string) => Promise<void>
      getCwd: (id: string) => Promise<string | null>
      onData: (worktreeId: string, callback: (data: string) => void) => () => void
      onExit: (worktreeId: string, callback: (code: number) => void) => () => void
      getConfig: () => Promise<GhosttyTerminalConfig>

      // Native Ghostty backend methods
      ghosttyInit: () => Promise<{ success: boolean; version?: string; error?: string }>
      ghosttyIsAvailable: () => Promise<{
        available: boolean
        initialized: boolean
        platform: string
      }>
      ghosttyCreateSurface: (
        worktreeId: string,
        rect: { x: number; y: number; w: number; h: number },
        opts?: { cwd?: string; shell?: string; scaleFactor?: number; fontSize?: number }
      ) => Promise<{ success: boolean; surfaceId?: number; error?: string }>
      ghosttySetFrame: (
        worktreeId: string,
        rect: { x: number; y: number; w: number; h: number }
      ) => Promise<void>
      ghosttySetSize: (worktreeId: string, width: number, height: number) => Promise<void>
      ghosttyKeyEvent: (
        worktreeId: string,
        event: {
          action: number
          keycode: number
          mods: number
          consumedMods?: number
          text?: string
          unshiftedCodepoint?: number
          composing?: boolean
        }
      ) => Promise<boolean>
      ghosttyMouseButton: (
        worktreeId: string,
        state: number,
        button: number,
        mods: number
      ) => Promise<void>
      ghosttyMousePos: (worktreeId: string, x: number, y: number, mods: number) => Promise<void>
      ghosttyMouseScroll: (
        worktreeId: string,
        dx: number,
        dy: number,
        mods: number
      ) => Promise<void>
      ghosttySetFocus: (worktreeId: string, focused: boolean) => Promise<void>
      ghosttyDestroySurface: (worktreeId: string) => Promise<void>
      ghosttyShutdown: () => Promise<void>
    }
    gitOps: {
      // Get file statuses for a worktree
      getFileStatuses: (worktreePath: string) => Promise<{
        success: boolean
        files?: GitFileStatus[]
        error?: string
      }>
      // Stage a file
      stageFile: (
        worktreePath: string,
        filePath: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Unstage a file
      unstageFile: (
        worktreePath: string,
        filePath: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Discard changes in a file
      discardChanges: (
        worktreePath: string,
        filePath: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Add to .gitignore
      addToGitignore: (
        worktreePath: string,
        pattern: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Open file in default editor
      openInEditor: (filePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Show file in Finder
      showInFinder: (filePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Subscribe to git status change events
      onStatusChanged: (callback: (event: GitStatusChangedEvent) => void) => () => void
      // Start watching a worktree for git changes (filesystem + .git metadata)
      watchWorktree: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Stop watching a worktree
      unwatchWorktree: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Start watching a worktree's .git/HEAD for branch changes (lightweight, sidebar use)
      watchBranch: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Stop watching a worktree's branch
      unwatchBranch: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Subscribe to branch change events (lightweight, from branch-watcher)
      onBranchChanged: (callback: (event: { worktreePath: string }) => void) => () => void
      // Get branch info (name, tracking, ahead/behind)
      getBranchInfo: (worktreePath: string) => Promise<{
        success: boolean
        branch?: GitBranchInfo
        error?: string
      }>
      // Stage all modified and untracked files
      stageAll: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Unstage all staged files
      unstageAll: (worktreePath: string) => Promise<{
        success: boolean
        error?: string
      }>
      // Commit staged changes
      commit: (
        worktreePath: string,
        message: string
      ) => Promise<{
        success: boolean
        commitHash?: string
        error?: string
      }>
      // Push to remote
      push: (
        worktreePath: string,
        remote?: string,
        branch?: string,
        force?: boolean
      ) => Promise<{
        success: boolean
        pushed?: boolean
        error?: string
      }>
      // Pull from remote
      pull: (
        worktreePath: string,
        remote?: string,
        branch?: string,
        rebase?: boolean
      ) => Promise<{
        success: boolean
        updated?: boolean
        error?: string
      }>
      // Get diff for a file
      getDiff: (
        worktreePath: string,
        filePath: string,
        staged: boolean,
        isUntracked: boolean,
        contextLines?: number
      ) => Promise<{
        success: boolean
        diff?: string
        fileName?: string
        error?: string
      }>
      // List all branches with their worktree checkout status
      listBranchesWithStatus: (projectPath: string) => Promise<{
        success: boolean
        branches: Array<{
          name: string
          isRemote: boolean
          isCheckedOut: boolean
          worktreePath?: string
        }>
        error?: string
      }>
      // Merge a branch into the current branch
      merge: (
        worktreePath: string,
        sourceBranch: string
      ) => Promise<{
        success: boolean
        error?: string
        conflicts?: string[]
      }>
      // Get raw file content from disk
      getFileContent: (
        worktreePath: string,
        filePath: string
      ) => Promise<{
        success: boolean
        content: string | null
        error?: string
      }>
      // Get raw file content as base64 from disk (for binary/image files)
      getFileContentBase64: (
        worktreePath: string,
        filePath: string
      ) => Promise<{
        success: boolean
        data?: string
        mimeType?: string
        error?: string
      }>
      // Get remote URL for a worktree
      getRemoteUrl: (
        worktreePath: string,
        remote?: string
      ) => Promise<{
        success: boolean
        url: string | null
        remote: string | null
        error?: string
      }>
      // Get diff stat (additions/deletions per file) for all uncommitted changes
      getDiffStat: (worktreePath: string) => Promise<{
        success: boolean
        files?: GitDiffStatFile[]
        error?: string
      }>
      // Merge a PR on GitHub via gh CLI and sync the local target branch
      prMerge: (
        worktreePath: string,
        prNumber: number
      ) => Promise<{ success: boolean; error?: string }>
      // Check if a branch has been fully merged into HEAD
      isBranchMerged: (
        worktreePath: string,
        branch: string
      ) => Promise<{ success: boolean; isMerged: boolean }>
      // Delete a local branch
      deleteBranch: (
        worktreePath: string,
        branchName: string
      ) => Promise<{ success: boolean; error?: string }>
      // List open pull requests from GitHub via gh CLI
      listPRs: (projectPath: string) => Promise<{
        success: boolean
        prs: Array<{
          number: number
          title: string
          author: string
          headRefName: string
        }>
        error?: string
      }>
      // Get the state of a specific PR via gh CLI
      getPRState: (
        projectPath: string,
        prNumber: number
      ) => Promise<{
        success: boolean
        state?: string
        title?: string
        error?: string
      }>
      // Fetch inline review comments for a PR
      getPRReviewComments: (
        projectPath: string,
        prNumber: number
      ) => Promise<{
        success: boolean
        comments?: Array<{
          id: number
          body: string
          bodyHTML: string
          path: string
          line: number | null
          originalLine: number | null
          side: 'LEFT' | 'RIGHT'
          diffHunk: string
          user: { login: string; avatarUrl: string }
          createdAt: string
          updatedAt: string
          inReplyToId: number | null
          pullRequestReviewId: number | null
          subjectType: 'line' | 'file'
        }>
        baseBranch?: string
        error?: string
      }>
      // Get file content from a specific git ref (HEAD, index)
      getRefContent: (
        worktreePath: string,
        ref: string,
        filePath: string
      ) => Promise<{
        success: boolean
        content?: string
        error?: string
      }>
      // Get file content as base64 from a specific git ref (for binary/image files)
      getRefContentBase64: (
        worktreePath: string,
        ref: string,
        filePath: string
      ) => Promise<{
        success: boolean
        data?: string
        mimeType?: string
        error?: string
      }>
      // Stage a single hunk by applying a patch to the index
      stageHunk: (
        worktreePath: string,
        patch: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Unstage a single hunk by reverse-applying a patch from the index
      unstageHunk: (
        worktreePath: string,
        patch: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Revert a single hunk in the working tree
      revertHunk: (
        worktreePath: string,
        patch: string
      ) => Promise<{
        success: boolean
        error?: string
      }>
      // Get list of files changed between current worktree and a branch
      getBranchDiffFiles: (
        worktreePath: string,
        branch: string
      ) => Promise<{
        success: boolean
        files?: { relativePath: string; status: string }[]
        error?: string
      }>
      // Get unified diff between current worktree and a branch for a specific file
      getBranchFileDiff: (
        worktreePath: string,
        branch: string,
        filePath: string
      ) => Promise<{
        success: boolean
        diff?: string
        error?: string
      }>
      getBranchBaseContent: (
        worktreePath: string,
        branch: string,
        filePath: string
      ) => Promise<{
        success: boolean
        content?: string | null
        error?: string
      }>
      getBranchBaseContentBase64: (
        worktreePath: string,
        branch: string,
        filePath: string
      ) => Promise<{
        success: boolean
        content?: string | null
        error?: string
      }>
    }
    updaterOps: {
      checkForUpdate: (options?: { manual?: boolean }) => Promise<void>
      downloadUpdate: () => Promise<void>
      installUpdate: () => Promise<void>
      setChannel: (channel: string) => Promise<void>
      getVersion: () => Promise<string>
      onChecking: (callback: () => void) => () => void
      onUpdateAvailable: (
        callback: (data: {
          version: string
          releaseNotes?: string
          releaseDate?: string
          isManualCheck?: boolean
        }) => void
      ) => () => void
      onUpdateNotAvailable: (
        callback: (data: { version: string; isManualCheck?: boolean }) => void
      ) => () => void
      onProgress: (
        callback: (data: {
          percent: number
          bytesPerSecond: number
          transferred: number
          total: number
        }) => void
      ) => () => void
      onUpdateDownloaded: (
        callback: (data: { version: string; releaseNotes?: string }) => void
      ) => () => void
      onError: (
        callback: (data: { message: string; isManualCheck?: boolean }) => void
      ) => () => void
    }
    connectionOps: {
      create: (
        worktreeIds: string[]
      ) => Promise<{ success: boolean; connection?: ConnectionWithMembers; error?: string }>
      delete: (connectionId: string) => Promise<{ success: boolean; error?: string }>
      addMember: (
        connectionId: string,
        worktreeId: string
      ) => Promise<{ success: boolean; member?: ConnectionMember; error?: string }>
      removeMember: (
        connectionId: string,
        worktreeId: string
      ) => Promise<{ success: boolean; connectionDeleted?: boolean; error?: string }>
      getAll: () => Promise<{
        success: boolean
        connections?: ConnectionWithMembers[]
        error?: string
      }>
      get: (
        connectionId: string
      ) => Promise<{ success: boolean; connection?: ConnectionWithMembers; error?: string }>
      openInTerminal: (connectionPath: string) => Promise<{ success: boolean; error?: string }>
      openInEditor: (connectionPath: string) => Promise<{ success: boolean; error?: string }>
      removeWorktreeFromAll: (worktreeId: string) => Promise<{ success: boolean; error?: string }>
      rename: (
        connectionId: string,
        customName: string | null
      ) => Promise<{ success: boolean; connection?: ConnectionWithMembers; error?: string }>
      setPinned: (
        connectionId: string,
        pinned: boolean
      ) => Promise<{ success: boolean; error?: string }>
      getPinned: () => Promise<ConnectionWithMembers[]>
    }
    usageOps: {
      fetch: () => Promise<import('../shared/types/usage').UsageResult>
      fetchOpenai: () => Promise<import('../shared/types/usage').OpenAIUsageResult>
    }
    usageAnalyticsOps: {
      fetchDashboard: (
        filters: import('../shared/types/usage-analytics').UsageAnalyticsFilters
      ) => Promise<import('../shared/types/usage-analytics').UsageAnalyticsDashboardResult>
      fetchSessionSummary: (
        sessionId: string
      ) => Promise<import('../shared/types/usage-analytics').UsageAnalyticsSessionSummaryResult>
      resync: () => Promise<import('../shared/types/usage-analytics').UsageAnalyticsResyncResult>
    }
    analyticsOps: {
      track: (event: string, properties?: Record<string, unknown>) => Promise<void>
      setEnabled: (enabled: boolean) => Promise<void>
      isEnabled: () => Promise<boolean>
    }
    fieldOps: {
      reportWorktreeSwitch: (
        input: import('../shared/types/field-event').WorktreeSwitchInput
      ) => void
      reportFileOpen: (input: import('../shared/types/field-event').FileOpenInput) => void
      reportFileFocus: (input: import('../shared/types/field-event').FileFocusInput) => void
      reportFileSelection: (input: import('../shared/types/field-event').FileSelectionInput) => void
      /** Phase 22A debug: fetch the last Field Context injected for a session. */
      getLastInjection: (sessionId: string) => Promise<{
        preview: string
        timestamp: number
        approxTokens: number
      } | null>
      /** Phase 22B.1 debug: fetch the episodic memory summary for a worktree. */
      getEpisodicMemory: (worktreeId: string) => Promise<{
        worktreeId: string
        summaryMarkdown: string
        compactorId: string
        version: number
        compactedAt: number
        sourceEventCount: number
        sourceSince: number
        sourceUntil: number
      } | null>
      /** Phase 22C.1 debug: fetch project + user memory.md files for a worktree. */
      getSemanticMemory: (worktreeId: string) => Promise<{
        project: { path: string; mtimeMs: number; size: number; markdown: string | null }
        user: { path: string; mtimeMs: number; size: number; markdown: string | null }
        lastReadAt: number
      } | null>
      /** Phase 24C debug: fetch latest checkpoint (raw row + verifier-evaluated block). */
      getCheckpoint: (worktreeId: string) => Promise<{
        verified: {
          createdAt: number
          ageMinutes: number
          source: 'abort' | 'shutdown'
          summary: string
          currentGoal: string | null
          nextAction: string | null
          blockingReason: string | null
          hotFiles: string[]
          warnings: string[]
        } | null
        raw: {
          id: string
          createdAt: number
          worktreeId: string
          sessionId: string
          branch: string | null
          repoHead: string | null
          source: 'abort' | 'shutdown'
          summary: string
          currentGoal: string | null
          nextAction: string | null
          blockingReason: string | null
          hotFiles: string[]
          hotFileDigests: Record<string, string | null> | null
          packetHash: string
        } | null
      } | null>
      /** v1.4.1: Pinned Facts — read user-authored permanent facts for a worktree. */
      getPinnedFacts: (worktreeId: string) => Promise<{
        worktreeId: string
        contentMd: string
        updatedAt: number
        createdAt: number
      } | null>
      /** v1.4.1: Pinned Facts — upsert. Throws if contentMd exceeds 2000 chars. */
      updatePinnedFacts: (input: { worktreeId: string; contentMd: string }) => Promise<{
        worktreeId: string
        contentMd: string
        updatedAt: number
        createdAt: number
      }>
      /**
       * v1.4.2: Episodic Memory — force-regenerate the rolling summary for a
       * worktree. Null when the compactor declined.
       */
      regenerateEpisodic: (worktreeId: string) => Promise<{
        worktreeId: string
        summaryMarkdown: string
        compactorId: string
        version: number
        compactedAt: number
        sourceEventCount: number
        sourceSince: number
        sourceUntil: number
      } | null>
      /** v1.4.2: Episodic Memory — clear the rolling summary for a worktree. */
      clearEpisodic: (worktreeId: string) => Promise<{ deleted: boolean }>
    }
    skillOps: {
      listHubs: () => Promise<{
        success: boolean
        hubs: import('../shared/types/skill').SkillHub[]
        error?: string
      }>
      addHub: (args: {
        repo: string
        ref?: string
        name?: string
      }) => Promise<import('../shared/types/skill').AddHubResult>
      removeHub: (hubId: string) => Promise<import('../shared/types/skill').RemoveHubResult>
      refreshHub: (
        hubId: import('../shared/types/skill').HubId
      ) => Promise<import('../shared/types/skill').RefreshHubResult>
      listSkills: (hubId: import('../shared/types/skill').HubId) => Promise<{
        success: boolean
        skills: import('../shared/types/skill').Skill[]
        error?: string
      }>
      listInstalled: (scope: import('../shared/types/skill').SkillScope) => Promise<{
        success: boolean
        skills: import('../shared/types/skill').InstalledSkill[]
        error?: string
      }>
      install: (
        hubId: import('../shared/types/skill').HubId,
        skillId: string,
        providers: import('../shared/types/skill').SkillProvider[],
        scope: {
          kind: import('../shared/types/skill').SkillScope['kind']
          path?: string
        },
        overwrite?: boolean
      ) => Promise<import('../shared/types/skill').InstallSkillBatchResult>
      uninstall: (
        skillId: string,
        scope: import('../shared/types/skill').SkillScope
      ) => Promise<import('../shared/types/skill').UninstallSkillResult>
      readContent: (
        absPath: string
      ) => Promise<import('../shared/types/skill').ReadSkillContentResult>
      openLocation: (absPath: string) => Promise<{ success: boolean; error?: string }>
      detectProviders: () => Promise<{
        success: true
        availability: import('../shared/types/skill').ProviderAvailability
      }>
    }

    hubOps: {
      getStatus: () => Promise<HubStatusSnapshot>
      start: () => Promise<{ success: boolean; status?: HubStatusSnapshot; error?: string }>
      stop: () => Promise<{ success: boolean; status?: HubStatusSnapshot; error?: string }>
      startTunnel: () => Promise<{ success: boolean; tunnel: HubTunnelStatus }>
      stopTunnel: () => Promise<{ success: boolean }>
      setAuthMode: (mode: HubAuthMode) => Promise<{ success: boolean }>
      getCfAccessEmails: () => Promise<{ emails: string[] }>
      setCfAccessEmails: (emails: string[]) => Promise<{ success: boolean }>
      createUser: (args: {
        setupKey: string
        username: string
        password: string
      }) => Promise<{ success: boolean; error?: string }>
      changePassword: (args: {
        username: string
        oldPassword: string
        newPassword: string
      }) => Promise<{ success: boolean; error?: string }>
      listTokens: () => Promise<{ tokens: HubTokenRow[] }>
      createToken: (
        name: string
      ) => Promise<
        | { success: true; name: string; token: string; prefix: string }
        | { success: false; error: string }
      >
      revokeToken: (id: number) => Promise<{ success: boolean }>
      onStatusChanged: (cb: (status: HubStatusSnapshot) => void) => () => void
    }
  }

  type HubAuthMode = 'password' | 'cf_access' | 'hybrid'

  type HubTunnelStatus =
    | { state: 'stopped' }
    | { state: 'starting' }
    | { state: 'running'; url: string }
    | { state: 'error'; message: string }

  interface HubStatusSnapshot {
    enabled: boolean
    port: number | null
    host: string | null
    authMode: HubAuthMode
    tunnel: HubTunnelStatus
    hasAdmin: boolean
    setupKey: string | null
  }

  interface HubTokenRow {
    id: number
    name: string
    prefix: string
    createdAt: number
    lastUsed: number | null
    lastDeviceId: string | null
    disabled: boolean
  }

  interface GitDiffStatFile {
    path: string
    additions: number
    deletions: number
    binary: boolean
  }

  // Message part type for prompt API (text + file attachments)
  type MessagePart =
    | { type: 'text'; text: string }
    | { type: 'file'; mime: string; url: string; filename?: string }

  // Script output event type
  interface ScriptOutputEvent {
    type: 'command-start' | 'output' | 'error' | 'done'
    command?: string
    data?: string
    exitCode?: number
  }

  // Agent command type (slash commands)
  interface AgentCommand {
    name: string
    description?: string
    template: string
    agent?: string
    model?: string
    source?: 'command' | 'mcp' | 'skill'
    subtask?: boolean
    hints?: string[]
  }

  // OpenCode permission request type
  interface PermissionRequest {
    id: string
    sessionID: string
    permission: string
    patterns: string[]
    metadata: Record<string, unknown>
    always: string[]
    tool?: {
      messageID: string
      callID: string
    }
  }

  // Command approval request type (for command filter system)
  interface CommandApprovalRequest {
    id: string
    sessionID: string
    toolName: string
    commandStr: string
    input: Record<string, unknown>
    patternSuggestions: string[]
    tool?: {
      messageID: string
      callID: string
    }
  }

  // Agent stream event type (canonical agent protocol)
  interface CanonicalAgentEvent {
    type: string
    sessionId: string
    eventId: string
    sessionSequence: number
    runEpoch: number
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    data: any
    childSessionId?: string
    sourceChannel?: 'agent:stream'
    /** session.status event payload -- only present when type === 'session.status' */
    statusPayload?: {
      type: 'idle' | 'busy' | 'retry'
      attempt?: number
      message?: string
      next?: number
    }
  }

  // File tree node type
  interface FileTreeNode {
    name: string
    path: string
    relativePath: string
    isDirectory: boolean
    isSymlink?: boolean
    extension: string | null
    children?: FileTreeNode[]
  }

  // Flat file entry for search index (no tree structure)
  interface FlatFile {
    name: string
    path: string
    relativePath: string
    extension: string | null
  }

  type FileEventType = 'add' | 'addDir' | 'unlink' | 'unlinkDir' | 'change'

  interface FileTreeChangeEventItem {
    eventType: FileEventType
    changedPath: string
    relativePath: string
  }

  // File tree change event type (batched)
  interface FileTreeChangeEvent {
    worktreePath: string
    events: FileTreeChangeEventItem[]
  }

  // Git status types
  type GitStatusCode = 'M' | 'A' | 'D' | '?' | 'C' | ''

  interface GitFileStatus {
    path: string
    relativePath: string
    status: GitStatusCode
    staged: boolean
  }

  interface GitStatusChangedEvent {
    worktreePath: string
  }

  interface GitBranchInfo {
    name: string
    tracking: string | null
    ahead: number
    behind: number
  }

  interface DetectedApp {
    id: string
    name: string
    command: string
    available: boolean
  }
}

export {}
