diff --git a/config/max-lines-baseline.txt b/config/max-lines-baseline.txt index 17f573b47d1..4375803952e 100644 --- a/config/max-lines-baseline.txt +++ b/config/max-lines-baseline.txt @@ -167,7 +167,6 @@ inline src/main/window/attach-main-window-services.ts inline src/main/window/createMainWindow.test.ts inline src/main/window/createMainWindow.ts inline src/main/workspace-space-analysis.ts -inline src/preload/api-types.ts inline src/preload/index.ts inline src/relay/agent-hook-server.ts inline src/relay/dispatcher.ts diff --git a/config/tsconfig.tc.web.json b/config/tsconfig.tc.web.json index 1034cc16ced..9f83cddee95 100644 --- a/config/tsconfig.tc.web.json +++ b/config/tsconfig.tc.web.json @@ -5,6 +5,7 @@ "../src/renderer/src/**/*", "../src/renderer/src/**/*.tsx", "../src/preload/api-types.ts", + "../src/preload/api/**/*", "../src/shared/**/*", "../src/main/gitlab/mappers.ts", "../src/main/ipc/worktree-branch-name.ts", diff --git a/config/tsconfig.web.json b/config/tsconfig.web.json index 92a6b9f2831..5a43720b5aa 100644 --- a/config/tsconfig.web.json +++ b/config/tsconfig.web.json @@ -5,6 +5,7 @@ "../src/renderer/src/**/*", "../src/renderer/src/**/*.tsx", "../src/preload/api-types.ts", + "../src/preload/api/**/*", "../src/shared/**/*" ], "compilerOptions": { diff --git a/src/preload/api-types.ts b/src/preload/api-types.ts index 84ceb217cf7..d81d8c59f93 100644 --- a/src/preload/api-types.ts +++ b/src/preload/api-types.ts @@ -1,2746 +1,130 @@ -/* eslint-disable max-lines -- Why: the preload contract is intentionally centralized in one declaration file so renderer and preload stay in lockstep when IPC surfaces change. */ -import type { - CreateHostedReviewArgs, - CreateHostedReviewResult, - CreateStackedHostedReviewArgs, - CreateStackedHostedReviewResult, - HostedReviewCreationEligibility, - HostedReviewCreationEligibilityArgs, - HostedReviewForBranchArgs, - HostedReviewInfo, - HostedReviewProvider -} from '../shared/hosted-review' -import type { - BitbucketConnectArgs, - BitbucketConnectionStatus -} from '../shared/bitbucket-credentials' -import type { NativeFileDropPayload } from '../shared/native-file-drop' -import type { ComputerAwakeStatus } from '../shared/computer-awake-mode' -import type { BrowserFindSource } from '../shared/browser-find-source' -import type { - DashboardRevealAgentArgs, - DashboardSleepWorkspaceArgs, - DashboardSnapshot, - DashboardSpawnAgentArgs -} from '../shared/dashboard-snapshot' -import type { - TerminalPreviewConnectResult, - TerminalPreviewDataPayload -} from '../shared/terminal-preview' -import type { - TerminalTabCloseRequest, - TerminalTabCloseResponse -} from '../shared/terminal-tab-close' -import type { TerminalTabCreateReply } from '../shared/terminal-reveal-identity' -import type { - LocalLogTailChangedPayload, - LocalLogTailReadArgs, - LocalLogTailReadResult, - LocalLogTailWatchArgs -} from '../shared/local-log-tail-types' -import type { ReadClipboardTextOptions } from '../shared/clipboard-text' -import type { AppIdentity } from '../shared/app-identity' -import type { ReleaseChannel } from '../shared/release-channel' -import type { - ForgetRemovedWorktreesForExecutionHostArgs, - ForgetRemovedWorktreesForExecutionHostResult, - HostQualifiedKnownWorktreeResult, - HostQualifiedDetectedWorktreeResult, - LegacyDetectedWorktreeRequest, - ListKnownWorktreesForExecutionHostArgs, - ListDetectedWorktreesArgs, - ProviderRequestId -} from '../shared/detected-worktree-provider-contract' -import type { - HostRepoCatalogSnapshot, - ListReposForExecutionHostArgs -} from '../shared/host-repo-catalog-contract' -import type { - HostLineageSnapshot, - ListDesktopLineageForHostArgs -} from '../shared/host-lineage-contract' -import type { - WriteTerminalRenderDesyncEvidenceArgs, - WriteTerminalRenderDesyncEvidenceResult -} from '../shared/terminal-render-desync-evidence' -import type { MobileRelayStatus } from '../shared/mobile-relay-status' -import type { MobilePairingConnectionMode } from '../shared/mobile-pairing-connection-mode' -import type { RuntimePairingReach } from '../shared/runtime-pairing-reach' -import type { MobileRelayMintFailure } from '../shared/mobile-relay-mint-failure' -import type { VerifyAndAddRuntimeEnvironmentResult } from '../shared/remote-pairing-verification' -import type { - SshMutationExpectation, - SshConnectionState, - SshConfigHostListArgs, - SshConfigHostListResult, - SshConfigHostResolution, - SshConfigImportResult, - SshTargetAddResult, - SshTarget, - PortForwardEntry, - EnrichedDetectedPort -} from '../shared/ssh-types' -import type { - CreateLocalOrcaProfileArgs, - CreateLocalOrcaProfileResult, - CreateCloudLinkedOrcaProfileArgs, - CreateCloudLinkedOrcaProfileResult, - ConnectCurrentOrcaProfileResult, - FindOrcaProfileProjectsByPathArgs, - FindOrcaProfileProjectsByPathResult, - OrcaProfileListResult, - OrcaProfileAuthStatus, - RefreshCurrentOrcaProfileAuthResult, - SelectOrcaProfileOrgArgs, - SelectOrcaProfileOrgResult, - SignOutCurrentOrcaProfileResult, - SwitchOrcaProfileArgs, - SwitchOrcaProfileResult, - TransferOrcaProfileProjectArgs, - TransferOrcaProfileProjectResult, - OrcaProfileOrgInviteRevokeArgs, - OrcaProfileOrgMemberChangeRoleArgs, - OrcaProfileOrgMemberInviteArgs, - OrcaProfileOrgMemberMutationResult, - OrcaProfileOrgMemberRemoveArgs, - OrcaProfileOrgMembersListArgs, - OrcaProfileOrgMembersListResult -} from '../shared/orca-profiles' -import type { TerminalPaneSplitSource } from '../shared/feature-education-telemetry' -import type { TaskSourceContext } from '../shared/task-source-context' -import type { LinearIssueAttributeFilter } from '../shared/linear/issue-attribute-filter' -import type { ProjectExecutionRuntimeResolution } from '../shared/project-execution-runtime' -import type { StartupCommandDelivery } from '../shared/codex-startup-delivery' -import type { - AgentProviderSessionMetadata, - SleepingAgentLaunchConfig -} from '../shared/agent-session-resume' -import type { - PluginPanelActionOutcome, - PluginPanelEntry -} from '../shared/plugins/plugin-panel-bridge' -import type { PluginConsentRequest } from '../shared/plugins/plugin-consent-request' -import type { PluginLanguagePackRegistration } from '../shared/plugins/plugin-language-pack-artifact' -import type { PluginChangeEvent } from '../shared/plugins/plugin-change-event' -import type { PluginManifest } from '../shared/plugins/plugin-manifest' -import type { PluginMarketplaceGitSource } from '../shared/plugins/plugin-marketplace' -import type { - LocalhostWorktreeLabelResult, - LocalhostWorktreeLabelRoute -} from '../shared/localhost-worktree-labels' -import type { - FolderWorkspacePathStatus, - FolderWorkspacePathStatusRequest -} from '../shared/folder-workspace-path-status' -import type { - BaseRefDefaultResult, - BaseRefSearchResult, - BrowserCookieImportResult, - BrowserCertificateFailure, - BrowserCertificateProceedResult, - BrowserLoadError, - BrowserSessionProfile, - BrowserSessionProfileCreateOptions, - BrowserSessionProfileScope, - BrowserSessionProfileSource, - BrowserViewportOverride, - ClaudeRateLimitAccountsState, - ClassifiedError, - CodexRateLimitAccountsState, - AdoptProvisionedRootArgs, - CreateWorktreeArgs, - CreateWorktreeResult, - CustomPet, - DetectedWorktreeListResult, - DirEntry, - FilesystemPathFlavor, - ForceDeleteWorktreeBranchResult, - FsChangedPayload, - GhosttyImportPreview, - GlobalSettings, - GitBranchCompareResult, - GitCommitCompareResult, - GitConflictOperation, - GitDiffResult, - GitForkSyncExpectedUpstream, - GitForkSyncResult, - GitPushTarget, - GitStagingArea, - GitStatusResult, - GitUpstreamStatus, - GitHubAssignableUser, - GitHubCreateIssueResult, - GitHubPRFile, - GitHubPRFileContents, - GitHubPrStartPoint, - GitHubPRReviewCommentInput, - GitHubCommentResult, - GitHubOwnerRepo, - GitHubWorkItem, - GitHubWorkItemDetails, - GitHubViewer, - GitLabAssignableUser, - GitLabAuthDiagnostic, - GitLabCommentResult, - GitLabDiscussionResolveResult, - GitLabIssueInfo, - GitLabIssueUpdate, - GitLabJobTraceResult, - GitLabMRInlineCommentInput, - GitLabMRReviewersUpdateResult, - GitLabMRUpdate, - GitLabProjectRef, - GitLabRetryJobResult, - GitLabTodo, - GitLabViewer, - GitLabWorkItem, - GitLabWorkItemDetails, - GetGitLabRateLimitResult, - ListMergeRequestsResult, - MRInfo, - MRListState, - ListWorkItemsResult, - IssueInfo, - JiraComment, - JiraConnectionStatus, - JiraCreateField, - JiraCreateIssueArgs, - JiraIssue, - JiraIssueFilter, - JiraIssueType, - JiraProjectStatusOrder, - JiraIssueUpdate, - JiraPriority, - JiraProject, - JiraSiteSelection, - JiraTransition, - JiraUser, - JiraViewer, - LinearViewer, - LinearCollectionResult, - LinearConnectionStatus, - LinearCustomViewModel, - LinearCustomViewSummary, - LinearWorkspaceSelection, - LinearIssue, - LinearIssueUpdate, - LinearComment, - LinearWorkflowState, - LinearLabel, - LinearMember, - LinearProjectDetail, - LinearProjectSummary, - LinearTeam, - MarkdownDocument, - FloatingTerminalCwdRequest, - GitHubIssueUpdate, - GitHubReactionContent, - GitHubPRRefreshCandidate, - GitHubPRRefreshEnqueueResult, - GitHubPRRefreshEvent, - GitHubPRRefreshReason, - GetRateLimitResult, - NotificationDispatchRequest, - NotificationDispatchResult, - NotificationDeliveryProbeResult, - NotificationDismissResult, - NotificationPermissionStatusResult, - NotificationSoundResult, - OnboardingState, - OrcaHooks, - PathSource, - PersistedUIState, - PRCheckDetail, - PRCheckRunDetails, - PRComment, - PRInfo, - PRRefreshOutcome, - Project, - ProjectUpdateArgs, - Repo, - ProjectGroup, - ProjectHostSetup, - ProjectHostSetupCreateArgs, - ProjectHostSetupCreateResult, - ProjectHostSetupDeleteArgs, - ProjectHostSetupDeleteResult, - ProjectHostSetupExistingFolderArgs, - ProjectHostSetupResult, - ProjectHostSetupUpdateArgs, - ProjectHostSetupUpdateResult, - FolderWorkspace, - ProjectGroupImportResult, - ProjectGroupImportMode, - ShellHydrationFailureReason, - SparsePreset, - SearchOptions, - NestedRepoScanResult, - SearchResult, - StatsSummary, - MemorySnapshot, - TuiAgent, - ReleaseBuildListResult, - UpdateCheckOptions, - UpdateStatus, - Worktree, - WorktreeBaseStatusEvent, - WorktreeHeadIdentity, - WorktreeLineage, - WorkspaceLineage, - WorktreeMeta, - WorktreeRemoteBranchConflictEvent, - RemoveWorktreeResult, - WorktreeDefaultTabsLaunch, - WorktreeSetupLaunch, - WorktreeStartupLaunch, - WorkspaceSessionPatch, - WorkspaceSessionState, - LinuxPackageInstallInstructions -} from '../shared/types' -import type { PtyModelRestoreNeededEvent } from '../shared/pty-model-restore-marker' -import type { PtyListedSession } from '../shared/pty-listed-session' -import type { - PtyRendererDeliveryHealthReply, - PtyRendererDeliveryStateReport -} from '../shared/pty-renderer-delivery-health' -import type { TerminalViewAttributes } from '../shared/terminal-view-attributes' -import type { PtyMainDeliveryDiagnostics } from '../shared/pty-delivery-diagnostics' -import type { - WarpThemeImportPreview, - WarpThemeImportSource -} from '../shared/terminal-custom-themes' - -import type { SetupScriptImportCandidate } from '../shared/setup-script-imports' -import type { GitHistoryOptions, GitHistoryResult } from '../shared/git-history' -import type { PublicKnownRuntimeEnvironment } from '../shared/runtime-environments' -import type { EphemeralVmRecipeDoctorResult } from '../shared/ephemeral-vm-recipes' -import type { EphemeralVmRecipeResultWarning } from '../shared/ephemeral-vm-recipe-diagnostics' -import type { EphemeralVmRuntimeRecord } from '../shared/ephemeral-vm-runtimes' -import type { RuntimeAccessGrant } from '../shared/runtime-access-grants' -import type { RuntimeRpcResponse } from '../shared/runtime-rpc-envelope' -import type { ExecutionHostId } from '../shared/execution-host' -import type { FeatureInteractionId } from '../shared/feature-interactions' -import type { - AddIssueCommentBySlugArgs, - ClearProjectItemFieldArgs, - DeleteIssueCommentBySlugArgs, - GetProjectViewTableArgs, - GetProjectViewTableResult, - GitHubProjectCommentMutationResult, - GitHubProjectMutationResult, - ListAccessibleProjectsArgs, - ListAccessibleProjectsResult, - ListAssignableUsersBySlugArgs, - ListAssignableUsersBySlugResult, - ListIssueTypesBySlugArgs, - ListIssueTypesBySlugResult, - ListLabelsBySlugArgs, - ListLabelsBySlugResult, - ListProjectViewsArgs, - ListProjectViewsResult, - ProjectWorkItemDetailsBySlugArgs, - ProjectWorkItemDetailsBySlugResult, - ResolveProjectRefArgs, - ResolveProjectRefResult, - UpdateIssueBySlugArgs, - UpdateIssueCommentBySlugArgs, - UpdateIssueTypeBySlugArgs, - UpdatePullRequestBySlugArgs, - UpdateProjectItemFieldArgs -} from '../shared/github/project-types' -import type { - RichMarkdownContextMenuCommandPayload, - RichMarkdownContextMenuTableTarget -} from '../shared/rich-markdown-context-menu' -import type { - BrowserSetGrabModeArgs, - BrowserSetGrabModeResult, - BrowserAwaitGrabSelectionArgs, - BrowserGrabResult, - BrowserCancelGrabArgs, - BrowserCaptureSelectionScreenshotArgs, - BrowserCaptureSelectionScreenshotResult, - BrowserExtractHoverArgs, - BrowserExtractHoverResult -} from '../shared/browser-grab-types' -import type { - BrowserContextMenuDismissedEvent, - BrowserContextMenuRequestedEvent, - BrowserDownloadFinishedEvent, - BrowserDownloadProgressEvent, - BrowserDownloadRequestedEvent, - BrowserPermissionDeniedEvent, - BrowserPopupEvent -} from '../shared/browser-guest-events' import type { ElectronAPI } from '@electron-toolkit/preload' -import type { BrowserSetAnnotationViewportBridgeArgs } from '../shared/browser-annotation-viewport-bridge' -import type { CliInstallStatus } from '../shared/cli-install-types' -import type { E2EConfig } from '../shared/e2e-config' -import type { AgentHookInstallStatus } from '../shared/agent-hook-types' -import type { CodexConfigSyncStatus } from '../shared/codex-config-sync-types' import type { - AgentStatusClearIpcPayload, - AgentStatusIpcPayload, - MigrationUnsupportedPtyEntry -} from '../shared/agent-status-types' -import type { AgentInterruptInferenceRequest } from '../shared/agent-interrupt-intent' -import type { AgentQuestionAnsweredInferenceRequest } from '../shared/agent-question-answered-intent' -import type { TerminalSideEffectBatch } from '../shared/terminal-side-effect-facts' + ClaudeAccountsApi, + CodexAccountsApi, + CodexConfigSyncApi, + GrokAccountsApi, + MinimaxCredentialsApi +} from './api/agent-account-api' +import type { AgentHooksApi, HooksApi } from './api/agent-hook-api' +import type { SkillsApi } from './api/agent-skill-api' +import type { AgentAwakeApi, AgentStatusApi, AgentTrustApi } from './api/agent-status-api' import type { - RuntimeBrowserDriverState, - RuntimeMobileSessionTabMove, - RuntimeStatus, - RuntimeSyncWindowGraphResult, - RuntimeSyncWindowGraph, - RuntimeTerminalCreateRequestPayload, - RuntimeTerminalDriverState, - RuntimeTerminalPresentation -} from '../shared/runtime-types' + ClaudeUsageApi, + CodexUsageApi, + OpenCodeUsageApi, + RateLimitsApi +} from './api/agent-usage-api' +import type { AiVaultApi } from './api/ai-vault-api' +import type { AppApi, E2EApi, PlatformApi } from './api/app-api' +import type { AutomationsApi } from './api/automation-api' +import type { BrowserApi } from './api/browser-api' +import type { CliApi } from './api/cli-install-api' +import type { CrashReportsApi, FeedbackApi } from './api/crash-report-api' +import type { DashboardApi, TerminalPreviewApi } from './api/dashboard-api' +import type { EmulatorApi } from './api/emulator-api' +import type { EphemeralVmApi } from './api/ephemeral-vm-api' +import type { ExportApi, FilesystemApi } from './api/filesystem-api' +import type { GitInspectionApi } from './api/git-inspection-api' +import type { GitOperationApi } from './api/git-operation-api' +import type { GithubPullRequestApi } from './api/github-pull-request-api' +import type { GithubWorkItemApi } from './api/github-work-item-api' +import type { GitLabApi } from './api/gitlab-api' +import type { BitbucketApi, HostedReviewApi } from './api/hosted-review-api' +import type { JiraApi } from './api/jira-api' +import type { LinearApi } from './api/linear-api' +import type { MobileApi } from './api/mobile-api' +import type { NativeChatApi } from './api/native-chat-api' +import type { OnboardingApi, StarNagApi } from './api/onboarding-api' +import type { OrcaProfileApi } from './api/orca-profile-api' import type { - CommitMessageAgentCapability, - CommitMessageModelCapability -} from '../shared/commit-message-agent-spec' -import type { ResolvedSourceControlAiGenerationParams } from '../shared/source-control-ai' -import type { SourceControlAiSettings } from '../shared/source-control-ai-types' -import type { - ShellOpenExternalEditorRequest, - ShellOpenExternalEditorResult, - ShellOpenLocalPathResult -} from '../shared/shell-open-types' -import type { SkillDiscoveryResult, SkillDiscoveryTarget } from '../shared/skills' -import type { - SkillFreshnessInventory, - SkillUpdateRun, - SkillUpdateStartResult -} from '../shared/skill-freshness' -import type { - CrashReportBreadcrumbData, - CrashReportCopyDiagnosticsArgs, - CrashReportRecord, - CrashReportSubmitArgs, - CrashReportSubmitResult, - ReactErrorBoundaryReportArgs, - ReactErrorBoundaryReportResult -} from '../shared/crash-reporting' -import type { RendererHeapStatistics } from '../shared/renderer-heap-statistics' + ComputerUsePermissionsApi, + DeveloperPermissionsApi, + MacosTccPromptsApi, + NotificationsApi +} from './api/os-permission-api' +import type { PetApi } from './api/pet-api' +import type { PluginsApi } from './api/plugin-host-api' +import type { PreflightApi } from './api/preflight-api' +import type { PtyApi } from './api/pty-api' +import type { ProjectGroupsApi, ProjectsApi, RepositoryApi } from './api/repository-api' +import type { RuntimeApi } from './api/runtime-api' +import type { KeybindingsApi, SettingsApi } from './api/settings-api' +import type { ShellApi } from './api/shell-api' +import type { SpeechApi } from './api/speech-api' +import type { SshApi } from './api/ssh-api' +import type { DiagnosticsApi, MemoryApi, StatsApi, TelemetryApi } from './api/telemetry-api' +import type { UiCommandEventApi } from './api/ui-command-event-api' +import type { UiWindowApi } from './api/ui-window-api' +import type { UpdaterApi } from './api/updater-api' +import type { WorkspaceCleanupApi, WorkspaceSpaceApi } from './api/workspace-cleanup-api' +import type { LocalhostWorktreeLabelsApi, WorkspacePortsApi } from './api/workspace-port-api' +import type { WorkspaceSessionApi } from './api/workspace-session-api' +import type { FolderWorkspacesApi, SparsePresetsApi, WorktreeApi } from './api/worktree-api' -export type { - ShellOpenExternalEditorRequest, - ShellOpenExternalEditorResult, - ShellOpenLocalPathResult -} from '../shared/shell-open-types' - -type RuntimeEnvironmentSubscriptionHandle = { - unsubscribe: () => void - sendBinary: (bytes: Uint8Array) => void -} -import type { - RuntimeMobileMarkdownRequest, - RuntimeMobileMarkdownResponse -} from '../shared/mobile-markdown-document' -import type { - DeveloperPermissionId, - DeveloperPermissionRequestResult, - DeveloperPermissionState, - LocalNetworkConnectionTestResult -} from '../shared/developer-permissions-types' -import type { - ComputerUsePermissionId, - ComputerUsePermissionResetResult, - ComputerUsePermissionSetupResult, - ComputerUsePermissionStatusResult -} from '../shared/computer-use-permissions-types' -import type { ClaudeUsageBreakdownKind, ClaudeUsageSnapshot } from '../shared/claude-usage-types' -import type { - CodexRateLimitResetResult, - GrokAccountStatus, - RateLimitRuntimeTarget, - RateLimitState -} from '../shared/rate-limit-types' -import type { - SpeechErrorEvent, - SpeechLifecycleEvent, - SpeechModelManifest, - SpeechModelState, - SpeechTranscriptEvent -} from '../shared/speech-types' -import type { - WorkspaceSpaceAnalyzeResult, - WorkspaceSpaceScanProgress -} from '../shared/workspace-space-types' -import type { - WorkspacePortAdvertisedUrlChangedEvent, - WorkspacePortKillRequest, - WorkspacePortKillResult, - WorkspacePortScanRequest, - WorkspacePortScanResult -} from '../shared/workspace-ports' -import type { GhAuthDiagnostic } from '../shared/github/auth-types' -import type { CodexUsageBreakdownKind, CodexUsageSnapshot } from '../shared/codex-usage-types' -import type { - OpenCodeUsageBreakdownKind, - OpenCodeUsageSnapshot -} from '../shared/opencode-usage-types' -import type { - AiVaultDeleteSessionArgs, - AiVaultDeleteSessionResult -} from '../shared/ai-vault-session-deletion' -import type { - AiVaultFirstUserPromptArgs, - AiVaultFirstUserPromptResult, - AiVaultListArgs, - AiVaultListResult, - AiVaultSubagentListArgs, - AiVaultSubagentListResult -} from '../shared/ai-vault-types' -import type { - AiVaultSessionTitlesArgs, - AiVaultSessionTitlesResult -} from '../shared/ai-vault-session-title' -import type { - AiVaultPrepareSessionResumeArgs, - AiVaultPrepareSessionResumeResult -} from '../shared/ai-vault-resume-preparation' -import type { - AgentType, - NativeChatMessage, - NativeChatTurnLifecycle -} from '../shared/native-chat-types' -import type { TelemetryConsentState } from '../shared/telemetry-consent-types' -import type { AgentKind, LaunchSource, RequestKind } from '../shared/telemetry-events' -import type { AppStarSource } from '../shared/gh-star-source' -import type { - RemoteWorkspaceChangedEvent, - RemoteWorkspaceConnectedClient, - RemoteWorkspacePatchResult, - RemoteWorkspaceSnapshot -} from '../shared/remote-workspace-types' -import type { - Automation, - AutomationCreateInput, - AutomationDispatchRequest, - AutomationDispatchResult, - ExternalAutomationCreateInput, - ExternalAutomationActionInput, - ExternalAutomationManager, - ExternalAutomationRunsInput, - ExternalAutomationRunsPage, - ExternalAutomationUpdateInput, - AutomationRun, - AutomationPrecheckResult, - AutomationUpdateInput -} from '../shared/automations-types' -import type { - WorkspaceCleanupDismissArgs, - WorkspaceCleanupLocalProcessArgs, - WorkspaceCleanupLocalProcessResult, - WorkspaceCleanupScanArgs, - WorkspaceCleanupScanProgress, - WorkspaceCleanupScanResult -} from '../shared/workspace-cleanup' -import type { KeybindingActionId, KeybindingFileSnapshot } from '../shared/keybindings' - -type GitLabRepoSelectorArgs = { - repoPath: string - repoId?: string | null - sourceContext?: TaskSourceContext | null -} - -type GitHubRepoSelectorArgs = { - repoPath: string - repoId?: string | null - sourceContext?: TaskSourceContext | null -} - -export type BrowserApi = { - registerGuest: (args: { - browserPageId: string - workspaceId: string - worktreeId: string - sessionProfileId?: string | null - webContentsId: number - }) => Promise - isGuestRegistered: (args: { browserPageId: string; webContentsId: number }) => Promise - repairGuestRegistration: (args: { - browserPageId: string - workspaceId: string - worktreeId: string - sessionProfileId?: string | null - webContentsId: number - }) => Promise - unregisterGuest: (args: { browserPageId: string }) => Promise - openDevTools: (args: { browserPageId: string }) => Promise - setViewportOverride: (args: { - browserPageId: string - override: BrowserViewportOverride | null - }) => Promise - setAnnotationViewportBridge: (args: BrowserSetAnnotationViewportBridgeArgs) => Promise - onGuestLoadFailed: ( - callback: (args: { browserPageId: string; loadError: BrowserLoadError }) => void - ) => () => void - onCertificateFailureChanged: ( - callback: (event: { browserPageId: string; failure: BrowserCertificateFailure | null }) => void - ) => () => void - proceedCertificate: (args: { - browserPageId: string - challengeId: string - }) => Promise - onPermissionDenied: (callback: (event: BrowserPermissionDeniedEvent) => void) => () => void - onPopup: (callback: (event: BrowserPopupEvent) => void) => () => void - onDownloadRequested: (callback: (event: BrowserDownloadRequestedEvent) => void) => () => void - onDownloadProgress: (callback: (event: BrowserDownloadProgressEvent) => void) => () => void - onDownloadFinished: (callback: (event: BrowserDownloadFinishedEvent) => void) => () => void - onContextMenuRequested: ( - callback: (event: BrowserContextMenuRequestedEvent) => void - ) => () => void - onContextMenuDismissed: ( - callback: (event: BrowserContextMenuDismissedEvent) => void - ) => () => void - onNavigationUpdate: ( - callback: (event: { browserPageId: string; url: string; title: string }) => void - ) => () => void - onActivateView: ( - callback: (data: { worktreeId?: string; browserPageId?: string }) => void - ) => () => void - onPaneFocus: ( - callback: (data: { worktreeId: string | null; browserPageId: string }) => void - ) => () => void - onOpenLinkInOrcaTab: ( - callback: (event: { browserPageId: string; url: string }) => void - ) => () => void - cancelDownload: (args: { downloadId: string }) => Promise - setGrabMode: (args: BrowserSetGrabModeArgs) => Promise - awaitGrabSelection: (args: BrowserAwaitGrabSelectionArgs) => Promise - cancelGrab: (args: BrowserCancelGrabArgs) => Promise - captureSelectionScreenshot: ( - args: BrowserCaptureSelectionScreenshotArgs - ) => Promise - extractHoverPayload: (args: BrowserExtractHoverArgs) => Promise - onGrabModeToggle: (callback: (browserPageId: string) => void) => () => void - onGrabActionShortcut: ( - callback: (args: { browserPageId: string; key: 'c' | 's' }) => void - ) => () => void - sessionListProfiles: () => Promise - sessionCreateProfile: ( - args: { - scope: BrowserSessionProfileScope - label: string - } & BrowserSessionProfileCreateOptions - ) => Promise - sessionDeleteProfile: (args: { profileId: string }) => Promise - sessionImportCookies: (args: { profileId: string }) => Promise - sessionResolvePartition: (args: { profileId: string | null }) => Promise - sessionDetectBrowsers: () => Promise - sessionImportFromBrowser: (args: { - profileId: string - browserFamily: string - browserProfile?: string - }) => Promise - sessionClearDefaultCookies: () => Promise - notifyActiveTabChanged: (args: { browserPageId: string }) => Promise -} - -export type EmulatorApi = { - onPaneFocus: (callback: (data: { worktreeId: string }) => void) => () => void - onAutoAttach: ( - callback: (data: { - worktreeId: string - info: { deviceUdid: string; streamUrl: string; wsUrl: string; axUrl?: string } - }) => void - ) => () => void - startFrameStream: (args: { streamUrl: string; streamKey?: string }) => Promise<{ - streamId: string - }> - stopFrameStream: (args: { streamId: string }) => Promise - onFrameStreamFrame: ( - callback: (data: { streamId: string; bytes: ArrayBuffer }) => void - ) => () => void - onFrameStreamError: ( - callback: (data: { streamId: string; message: string }) => void - ) => () => void - startVideoStream: (args: { deviceId: string; streamId: string }) => Promise<{ streamId: string }> - stopVideoStream: (args: { streamId: string }) => Promise - onVideoStreamMeta: ( - callback: (data: { - streamId: string - deviceId: string - meta: { codecId: string; width: number; height: number } - }) => void - ) => () => void - onVideoStreamFrame: ( - callback: (data: { - streamId: string - deviceId: string - config: boolean - keyFrame: boolean - bytes: ArrayBuffer - }) => void - ) => () => void -} - -export type DetectedBrowserProfileInfo = { - name: string - directory: string -} - -export type DetectedBrowserInfo = { - family: BrowserSessionProfileSource['browserFamily'] - label: string - profiles: DetectedBrowserProfileInfo[] - selectedProfile: string -} - -export type PreflightStatus = { - git: { installed: boolean } - gh: { installed: boolean; authenticated: boolean } - /** Optional — older preload payloads predating GitLab support omit it; consumers gate on `glab?.installed`. */ - glab?: { installed: boolean; authenticated: boolean } - bitbucket?: { configured: boolean; authenticated: boolean; account: string | null } - azureDevOps?: { - configured: boolean - authenticated: boolean - account: string | null - baseUrl: string | null - tokenConfigured: boolean - } - gitea?: { - configured: boolean - authenticated: boolean - account: string | null - baseUrl: string | null - tokenConfigured: boolean - } -} - -export type RefreshAgentsResult = { - agents: string[] - addedPathSegments: string[] - shellHydrationOk: boolean - /** Drives agent_picks `on_path:false` triage (dashboard 1562016). `'shell_hydrate'` = detection saw the user's - * full shell PATH; `'sync_seed_only'` = hydration failed and detection ran against the `patchPackagedProcessPath` seed list. */ - pathSource: PathSource - /** Classified hydration outcome: `'none'` on success, else a failure mode when `shellHydrationOk` is false. */ - pathFailureReason: ShellHydrationFailureReason -} - -export type PreflightRuntimeContext = { - wslDistro?: string | null - wslDefault?: boolean - projectRuntime?: ProjectExecutionRuntimeResolution -} - -export type PreflightApi = { - check: (args?: PreflightRuntimeContext & { force?: boolean }) => Promise - detectAgents: (args?: PreflightRuntimeContext) => Promise - refreshAgents: (args?: PreflightRuntimeContext) => Promise - detectRemoteAgents: (args: { connectionId: string }) => Promise - detectRemoteWindowsTerminalCapabilities: (args: { connectionId: string }) => Promise<{ - wslAvailable: boolean - wslDistros: string[] - pwshAvailable: boolean - gitBashAvailable: boolean - hostPlatform: NodeJS.Platform | null - }> -} - -// Mirror of daemon's `DaemonSessionInfo` (src/main/daemon/types.ts); not imported — preload can't depend on main-only protocol types. -export type PtyManagementSession = { - sessionId: string - state: 'created' | 'spawning' | 'running' | 'exiting' | 'exited' - shellState: 'pending' | 'ready' | 'timed_out' | 'unsupported' - isAlive: boolean - pid: number | null - cwd: string | null - cols: number - rows: number - createdAt: number - protocolVersion: number -} - -// 'severed': macOS can no longer attribute daemon terminals to Orca, so Accessibility/ -// Automation grants silently stop applying until the daemon is restarted (STA-3491). -export type PtyManagementMacTccAttributionHealth = 'intact' | 'severed' | 'unknown' - -export type PtyManagementApi = { - // `degraded`: daemon is alive but can't spawn fresh PTYs, so new terminals run locally without daemon persistence. - listSessions: () => Promise<{ sessions: PtyManagementSession[]; degraded: boolean }> - killAll: () => Promise<{ - killedCount: number - remainingCount: number - killedSessionIds?: string[] - }> - killOne: (args: { sessionId: string }) => Promise<{ success: boolean }> - restart: () => Promise<{ success: boolean }> - macTccAttribution: () => Promise<{ health: PtyManagementMacTccAttributionHealth }> -} - -export type ExportApi = { - htmlToPdf: (args: { - html: string - title: string - }) => Promise< - { success: true; filePath: string } | { success: false; cancelled?: boolean; error?: string } - > -} - -export type StatsApi = { - getSummary: () => Promise -} - -// Diagnostics IPC payloads; mirror the runtime types in `src/main/observability/{index,bundle}.ts`. -export type DiagnosticsStatusPayload = { - readonly localFileEnabled: boolean - readonly bundleEnabled: boolean - readonly traceFilePath: string - readonly traceFamilySize: number - readonly disabledReason?: - | 'do_not_track' - | 'orca_telemetry_disabled' - | 'orca_diagnostics_disabled' - | 'ci' -} -export type DiagnosticsBundlePayload = { - readonly bundleSubmissionId: string - readonly bytes: number - readonly spanCount: number -} -export type DiagnosticsUploadPayload = - | { - readonly ticketId: string - } - | { - readonly canceled: true - } - -export type MemoryApi = { - getSnapshot: () => Promise -} - -type UsageProviderSnapshot = { - scanState: unknown - summary: { scope: string; range: string } - daily: unknown[] - modelBreakdown: unknown[] - recentSessions: unknown[] -} - -type UsageQueryArgs = Pick< - Snapshot['summary'], - 'scope' | 'range' -> - -type UsageProviderApi = { - getScanState: () => Promise - setEnabled: (args: { enabled: boolean }) => Promise - refresh: (args?: { force?: boolean }) => Promise - getSnapshot: (args: UsageQueryArgs & { limit?: number }) => Promise - getSummary: (args: UsageQueryArgs) => Promise - getDaily: (args: UsageQueryArgs) => Promise - getBreakdown: ( - args: UsageQueryArgs & { kind: BreakdownKind } - ) => Promise - getRecentSessions: ( - args: UsageQueryArgs & { limit?: number } - ) => Promise -} - -export type ClaudeUsageApi = UsageProviderApi - -export type CodexUsageApi = UsageProviderApi - -export type OpenCodeUsageApi = UsageProviderApi - -export type AiVaultApi = { - listSessions: (args?: AiVaultListArgs) => Promise - resolveSessionTitles: (args: AiVaultSessionTitlesArgs) => Promise - cancelListSessions: (args: { requestToken: string }) => Promise - prepareSessionResume: ( - args: AiVaultPrepareSessionResumeArgs - ) => Promise - /** Lists the Task subagent transcripts of one session, on demand. */ - listSubagentSessions: (args: AiVaultSubagentListArgs) => Promise - /** Full first user prompt for copy/reuse (re-parses one transcript). */ - getFirstUserPrompt: (args: AiVaultFirstUserPromptArgs) => Promise - /** Moves a deletable session's transcript to the OS trash; local sessions only. */ - deleteSession: (args: AiVaultDeleteSessionArgs) => Promise - /** Fires when any app window regains OS focus; returns an unsubscribe. */ - onWindowFocused: (callback: () => void) => () => void -} - -// notFound marks a not-yet-on-disk miss (retry-worthy) vs a real read/parse error (#8401). -export type NativeChatReadSessionResult = - | { - messages: NativeChatMessage[] - lifecycle?: NativeChatTurnLifecycle - } - | { error: string; notFound?: true } - -/** Messages appended to a live-tailed transcript since the previous emit. */ -export type NativeChatAppendedMessages = NativeChatMessage[] - -export type NativeChatSubscriptionFrame = - | { - type: 'snapshot' - messages: NativeChatMessage[] - hasMore: boolean - error?: string - lifecycle?: NativeChatTurnLifecycle - } - | { - type: 'replacement' - messages: NativeChatMessage[] - hasMore: boolean - lifecycle?: NativeChatTurnLifecycle - } - | { - type: 'appended' - messages: NativeChatMessage[] - lifecycle?: NativeChatTurnLifecycle - } - -/** Wire payload for the `nativeChat:appended` push channel. */ -export type NativeChatAppendedPayload = { - subscriptionId: string - frame: NativeChatSubscriptionFrame -} - -export type NativeChatSubscribeArgs = { - /** Unique per-caller id, echoed on every append so multiple live panes in - * one renderer don't cross-talk. */ - subscriptionId: string - agent: AgentType - sessionId: string - /** Authoritative transcript path from the agent hook (providerSession). */ - transcriptPath?: string - /** First snapshot size; later readSession calls grow this for pagination. */ - limit?: number -} - -export type NativeChatApi = { - /** Read the on-disk transcript for an agent + session id, windowed to the most recent `limit` - * turns. `transcriptPath` is the hook-reported authoritative path, preferred over the id glob. */ - readSession: ( - agent: AgentType, - sessionId: string, - limit?: number, - transcriptPath?: string - ) => Promise - /** Live-tail a transcript. The first frame is a bounded race-safe snapshot; - * later frames contain only newly appended messages. */ - subscribe: ( - args: NativeChatSubscribeArgs, - onFrame: (frame: NativeChatSubscriptionFrame) => void - ) => () => void -} - -export type AppApi = { - /** Returns the app identity currently exposed to native chrome and the titlebar. */ - getIdentity: () => Promise - /** Returns a URL base for feature-wall assets. In dev this is Vite /@fs; - * in packaged builds this is file:// resources. Renderer appends filenames. */ - getFeatureWallAssetBaseUrl: () => Promise - /** Relaunches the app (app.relaunch() + app.exit(0)) for settings that need a full restart to apply. */ - relaunch: () => Promise - /** Restarts Orca through the normal quit pipeline so daemon-backed terminal - * sessions survive and can reattach after the new process starts. */ - restart: () => Promise - /** Reloads the current app renderer through main so expected renderer - * teardown can be classified before Electron emits process-gone events. */ - reload: () => Promise - /** Stages the renderer's final state synchronously before unload. */ - stageBeforeUnloadSync: (args: { - sessions: { state: WorkspaceSessionState; hostId?: ExecutionHostId }[] - ui: Partial - }) => void - /** Resolves once the last staged checkpoint is durably written; rejects if that - * write failed, so a reload/restart can abort instead of losing the snapshot. */ - awaitBeforeUnloadCheckpoint: () => Promise - /** Resolves when the daemon PTY provider and hook receiver have either - * started or failed open for the first BrowserWindow. */ - awaitFirstWindowStartupServices: () => Promise - /** Reconciles legacy worker authority around persisted terminal reconnect. */ - recoverLegacyWorkerTerminalsForRendererStartup: () => Promise - /** Emits a startup benchmark marker when ORCA_STARTUP_DIAGNOSTICS is enabled. */ - startupDiagnostic: (event: string, details?: Record) => Promise - /** macOS active input mode, or layout ID when no IME is selected (e.g. `com.apple.keylayout.PolishPro`). - * Distinguishes CJK IMEs and Option-layer-composing layouts that look like US QWERTY (issue #1205). - * Returns null on non-Darwin or when the defaults read fails. */ - getKeyboardInputSourceId: () => Promise - /** Updates the macOS Dock unread badge. No-op on Windows/Linux. */ - setUnreadDockBadgeCount: (count: number) => Promise - /** Resolves the launch directory for global Floating Terminal tabs. */ - getFloatingTerminalCwd: (args?: FloatingTerminalCwdRequest) => Promise - /** Resolves Orca's app-owned directory for auto-created Floating Workspace - * markdown notes. */ - getFloatingMarkdownDirectory: () => Promise - /** Opens a native picker for markdown documents, rooted in the floating - * workspace, and authorizes the selected file for editor reads/writes. */ - pickFloatingMarkdownDocument: () => Promise - /** Opens a native directory picker and authorizes the selected directory - * for Floating Workspace markdown file creation. */ - pickFloatingWorkspaceDirectory: () => Promise - /** Persists flag-gated terminal render evidence under app-owned userData. */ - writeTerminalRenderDesyncEvidence: ( - args: WriteTerminalRenderDesyncEvidenceArgs - ) => Promise -} - -/** Panel contribution as surfaced by the main-process plugin service. */ -export type PluginHostPanel = { - id: string - title: string - /** Lucide icon name declared in the plugin manifest. */ - icon?: string - tabKey: `plugin:${string}` -} - -/** `pending` = awaiting (re-)consent; `idle` = enabled, worker not running - * (lazy); `restarting` = waiting for supervised backoff; `errored` = crashed past the restart budget or failed to start; - * `invalid` = unreadable manifest. */ -export type PluginHostStatus = - | 'running' - | 'restarting' - | 'idle' - | 'pending' - | 'disabled' - | 'errored' - | 'invalid' - -/** Wire shape of plugins:list — must stay assignable from the main-process - * projection in src/main/plugins/plugin-list-projection.ts. */ -export type PluginHostListEntry = { - pluginKey: string - consentFingerprint: string | null - name: string - version: string - publisher: string - description?: string - status: PluginHostStatus - needsReconsent: boolean - error?: string - isDev: boolean - official: boolean - bundled: boolean - capabilities: { kind: string; description: string }[] - panels: PluginHostPanel[] - commands: { - id: string - title: string - context: 'global' | 'worktree' - handler: { type: 'built-in'; action: string } | { type: 'worker' } - keybindings: { key: string; when: 'global' | 'worktree' }[] - }[] - hasWorker: boolean - vmRecipes?: { - id: string - name: string - description?: string - commands: { - phase: 'create' | 'suspend' | 'resume' | 'destroy' - command: string - }[] - }[] - restarts: number - blockedByKillList?: { reason: string; advisoryUrl?: string } - source?: { - kind: 'local-path' | 'git' | 'marketplace' | 'bundled' - reference: string - resolvedCommit: string | null - contentHash: string - marketplace?: { reference: string; resolvedCommit: string } - } -} - -export type PluginHostLogLine = { ts: number; level: 'info' | 'warn' | 'error'; line: string } - -export type PluginHostInstallSource = - | { kind: 'local-path'; path: string } - | { kind: 'git'; url: string; ref: string } - -export type PluginHostInstallResult = - | { - ok: true - pluginKey: string - version: string - contentHash: string - consentFingerprint: string - resolvedCommit: string | null - } - | { ok: false; error: string } - -export type PluginMarketplaceHostSourceState = { - id: string - source: PluginMarketplaceGitSource - addedAt: number - marketplace: { - name: string - owner: string - resolvedCommit: string - fetchedAt: number - } | null - stale: boolean - official: boolean - error?: string -} - -export type PluginMarketplaceHostListing = { - marketplaceSourceId: string - marketplaceName: string - marketplaceOwner: string - marketplaceCommit: string - pluginKey: string - source: PluginMarketplaceGitSource - description?: string - categories: string[] - official: boolean - bundled: boolean - blockedByKillList?: { reason: string; advisoryUrl?: string } -} - -export type PluginMarketplaceHostInstallPreview = { - marketplaceSourceId: string - marketplaceName: string - marketplaceOwner: string - marketplaceCommit: string - pluginKey: string - source: PluginMarketplaceGitSource - resolvedCommit: string - contentHash: string - consentFingerprint: string - manifest: PluginManifest - official: boolean - bundled: boolean - blockedByKillList?: { reason: string; advisoryUrl?: string } -} +// Flattens contracts that share one PreloadApi key: an intersection is not type-identical to the flat shape. +type Merged = { [K in keyof T]: T[K] } export type PreloadApi = { app: AppApi - orcaProfiles: { - list: () => Promise - authStatus: () => Promise - createLocal: (args?: CreateLocalOrcaProfileArgs) => Promise - createCloudLinked: ( - args?: CreateCloudLinkedOrcaProfileArgs - ) => Promise - switchProfile: (args: SwitchOrcaProfileArgs) => Promise - transferProject: ( - args: TransferOrcaProfileProjectArgs - ) => Promise - findProjectProfiles: ( - args: FindOrcaProfileProjectsByPathArgs - ) => Promise - connectCurrent: () => Promise - refreshAuth: () => Promise - signOutCurrent: () => Promise - selectOrg: (args: SelectOrcaProfileOrgArgs) => Promise - orgMembersList: ( - args: OrcaProfileOrgMembersListArgs - ) => Promise - orgMemberInvite: ( - args: OrcaProfileOrgMemberInviteArgs - ) => Promise - orgInviteRevoke: ( - args: OrcaProfileOrgInviteRevokeArgs - ) => Promise - orgMemberChangeRole: ( - args: OrcaProfileOrgMemberChangeRoleArgs - ) => Promise - orgMemberRemove: ( - args: OrcaProfileOrgMemberRemoveArgs - ) => Promise - } - platform: { - get: () => { - platform: NodeJS.Platform - osRelease: string - arch: string - /** Login shell or ComSpec when available. */ - shell: string - displayServer: 'wayland' | 'x11' | null - } - } - e2e: { - getConfig: () => E2EConfig - } - repos: { - list: () => Promise - listForExecutionHost?: (args: ListReposForExecutionHostArgs) => Promise - // Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`. - add: (args: { - path: string - kind?: 'git' | 'folder' - }) => Promise<{ repo: Repo } | { error: string }> - remove: (args: { repoId: string }) => Promise - // Forget a project on one execution host only, leaving the same repo id on other hosts intact. - removeForHost: (args: { repoId: string; hostId: string }) => Promise - reorder: (args: { orderedIds: string[] }) => Promise<{ status: 'applied' | 'rejected' }> - reorderForHost: (args: { - orderedIds: string[] - hostId: string - }) => Promise<{ status: 'applied' | 'rejected' }> - update: (args: { - repoId: string - hostId?: ExecutionHostId - updates: Partial< - Pick< - Repo, - | 'displayName' - | 'badgeColor' - | 'repoIcon' - | 'upstream' - | 'hookSettings' - | 'worktreeBaseRef' - | 'worktreeBasePath' - | 'kind' - | 'issueSourcePreference' - | 'externalWorktreeVisibility' - | 'externalWorktreeVisibilityPromptDismissedAt' - | 'externalWorktreeInboxBaselinePaths' - | 'importedExternalWorktreePaths' - | 'agentWorktreeVisibility' - | 'customWorktreeVisibilitySources' - | 'worktreeVisibilitySourcePreferences' - | 'projectGroupId' - | 'projectGroupOrder' - | 'forkSyncMode' - > - > & { - sourceControlAi?: Repo['sourceControlAi'] | null - externalWorktreeDiscoverySuppressedAt?: Repo['externalWorktreeDiscoverySuppressedAt'] | null - } - }) => Promise - pickFolder: () => Promise - pickFolders: () => Promise - pickDirectory: () => Promise - clone: (args: { url: string; destination: string }) => Promise - cloneRemote: (args: { connectionId: string; url: string; destination: string }) => Promise - createRemote: (args: { - connectionId: string - parentPath: string - name: string - kind: 'git' | 'folder' - }) => Promise<{ repo: Repo } | { error: string }> - cloneAbort: () => Promise - // Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`. - addRemote: (args: { - connectionId: string - remotePath: string - displayName?: string - kind?: 'git' | 'folder' - }) => Promise<{ repo: Repo } | { error: string }> - // Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`. - create: (args: { - parentPath: string - name: string - kind: 'git' | 'folder' - }) => Promise<{ repo: Repo } | { error: string }> - isGitAvailable: () => Promise - getDefaultCreateProjectParent: () => Promise - onCloneProgress: (callback: (data: { phase: string; percent: number }) => void) => () => void - getGitUsername: (args: { repoId: string }) => Promise - getBaseRefDefault: (args: { - repoId: string - hostId?: ExecutionHostId - }) => Promise - searchBaseRefs: (args: { - repoId: string - query: string - limit?: number - hostId?: ExecutionHostId - }) => Promise - searchBaseRefDetails: (args: { - repoId: string - query: string - limit?: number - hostId?: ExecutionHostId - }) => Promise - onChanged: (callback: () => void) => () => void - } - projects: { - list: () => Promise - update: (args: ProjectUpdateArgs) => Promise - listHostSetups: () => Promise - createHostSetup: (args: ProjectHostSetupCreateArgs) => Promise - setupExistingFolder: ( - args: ProjectHostSetupExistingFolderArgs - ) => Promise - updateHostSetup: (args: ProjectHostSetupUpdateArgs) => Promise - deleteHostSetup: (args: ProjectHostSetupDeleteArgs) => Promise - } - projectGroups: { - list: () => Promise - create: (args: { - name: string - parentPath?: string | null - connectionId?: string | null - parentGroupId?: string | null - createdFrom?: ProjectGroup['createdFrom'] - }) => Promise - update: (args: { - groupId: string - updates: Partial> - }) => Promise - delete: (args: { groupId: string }) => Promise - moveProject: (args: { - projectId: string - groupId: string | null - order?: number - }) => Promise - scanNested: (args: { - path: string - connectionId?: string - scanId?: string - options?: Record - }) => Promise - cancelNestedScan: (args: { scanId: string }) => Promise - onNestedScanProgress: ( - callback: (data: { scanId: string; scan: NestedRepoScanResult }) => void - ) => () => void - importNested: (args: { - parentPath: string - groupName: string - projectPaths: string[] - connectionId?: string - scanId?: string - mode: ProjectGroupImportMode - }) => Promise - } - folderWorkspaces: { - list: () => Promise - getPathStatus: (args: FolderWorkspacePathStatusRequest) => Promise - create: (args: { - projectGroupId: string - name?: string - folderPath?: string | null - connectionId?: string | null - linkedTask?: FolderWorkspace['linkedTask'] - createdWithAgent?: FolderWorkspace['createdWithAgent'] - pendingFirstAgentMessageRename?: boolean - }) => Promise - update: (args: { - folderWorkspaceId: string - updates: Partial< - Pick< - FolderWorkspace, - | 'name' - | 'folderPath' - | 'linkedTask' - | 'comment' - | 'isArchived' - | 'isUnread' - | 'isPinned' - | 'sortOrder' - | 'manualOrder' - | 'workspaceStatus' - | 'createdWithAgent' - | 'pendingFirstAgentMessageRename' - | 'firstAgentMessageRenameError' - | 'lastActivityAt' - | 'diffComments' - > - > - }) => Promise - delete: (args: { folderWorkspaceId: string }) => Promise - } - sparsePresets: { - list: (args: { repoId: string }) => Promise - save: (args: { - repoId: string - id?: string - name: string - directories: string[] - }) => Promise - remove: (args: { repoId: string; presetId: string }) => Promise - onChanged: (callback: (data: { repoId: string }) => void) => () => void - } - worktrees: { - list: (args: { repoId: string }) => Promise - listDetected: { - ( - args: ListDetectedWorktreesArgs - ): Promise - (args: LegacyDetectedWorktreeRequest): Promise - } - listKnownForExecutionHost?: ( - args: ListKnownWorktreesForExecutionHostArgs - ) => Promise - /** Retires the persisted metadata an authoritative scan proved gone, so it stops feeding the read above. */ - forgetRemovedForExecutionHost?: ( - args: ForgetRemovedWorktreesForExecutionHostArgs - ) => Promise - cancelListDetected?: (args: { providerRequestId: ProviderRequestId }) => Promise - listAll: () => Promise - create: (args: CreateWorktreeArgs) => Promise - adoptProvisionedRoot: (args: AdoptProvisionedRootArgs) => Promise - /** Two-phase progress for a background `create`, correlated by `creationId`. The remote/runtime - * create path emits nothing, so the surface falls back to an indeterminate spinner. */ - onCreateProgress: ( - callback: (data: { creationId?: string; phase: 'fetching' | 'creating' }) => void - ) => () => void - prefetchCreateBase: (args: { repoId: string; baseBranch?: string }) => Promise - resolvePrBase: (args: { - repoId: string - prNumber: number - headRefName?: string - baseRefName?: string - isCrossRepository?: boolean - }) => Promise - /** GitLab parallel of resolvePrBase. For same-project MRs returns - * `/`; for fork MRs fetches - * refs/merge-requests//head and returns the SHA. */ - resolveMrBase: (args: { - repoId: string - mrIid: number - sourceBranch?: string - targetBranch?: string - isCrossRepository?: boolean - }) => Promise< - | { baseBranch: string; compareBaseRef?: string; pushTarget?: GitPushTarget } - | { error: string } - > - remove: (args: { - worktreeId: string - hostId?: ExecutionHostId - force?: boolean - // Why (#11960): distinct from `force`, which the plain Delete confirmation - // already sets to skip the dirty-file prompt. Only an explicit Force Delete - // may waive the proof that every PTY stopped. - allowUnverifiedPtyStop?: boolean - skipArchive?: boolean - }) => Promise - // Forget a workspace from Orca only (no remote Git/FS work) — for workspaces pinned to a removed/disconnected SSH host. - forgetLocal: (args: { - worktreeId: string - hostId?: ExecutionHostId - }) => Promise - forceDeletePreservedBranch: (args: { - worktreeId: string - branchName: string - expectedHead: string - hostId?: ExecutionHostId - }) => Promise - updateMeta: (args: { worktreeId: string; updates: Partial }) => Promise - listLineage: () => Promise<{ - lineage: Record - workspaceLineage?: Record - }> - listLineageForHost?: (args: ListDesktopLineageForHostArgs) => Promise - updateLineage: (args: { - worktreeId: string - parentWorktreeId?: string - noParent?: boolean - }) => Promise - persistSortOrder: (args: { orderedIds: string[] }) => Promise - /** Full CLI output of the last branch auto-rename generation failure, held - * in main memory only — null after a restart or once the failure clears. */ - getBranchRenameFailureOutput: (args: { worktreeId: string }) => Promise - onChanged: (callback: (data: { repoId: string }) => void) => () => void - onGitStatusMetadataChanged: (callback: (data: { repoId: string }) => void) => () => void - onHeadIdentitiesChanged: ( - callback: (data: { repoId: string; identities: WorktreeHeadIdentity[] }) => void - ) => () => void - onBaseStatus: (callback: (data: WorktreeBaseStatusEvent) => void) => () => void - onRemoteBranchConflict: ( - callback: (data: WorktreeRemoteBranchConflictEvent) => void - ) => () => void - } - workspaceCleanup: { - scan: ( - args?: WorkspaceCleanupScanArgs, - onProgress?: (progress: WorkspaceCleanupScanProgress) => void - ) => Promise - dismiss: (args: WorkspaceCleanupDismissArgs) => Promise - clearDismissals: () => Promise - hasKillableLocalProcesses: ( - args: WorkspaceCleanupLocalProcessArgs - ) => Promise - } - workspaceSpace: { - analyze: () => Promise - cancel: () => Promise - onProgress: (callback: (progress: WorkspaceSpaceScanProgress) => void) => () => void - } - workspacePorts: { - scan: (args: WorkspacePortScanRequest) => Promise - kill: (args: WorkspacePortKillRequest) => Promise - onAdvertisedUrlChanged: ( - callback: (event: WorkspacePortAdvertisedUrlChangedEvent) => void - ) => () => void - } - pty: { - spawn: (opts: { - cols: number - rows: number - cwd?: string - cwdFallback?: 'worktree' - env?: Record - envToDelete?: string[] - command?: string - commandDelivery?: 'renderer' | 'provider' - launchConfig?: SleepingAgentLaunchConfig - resumeProviderSession?: AgentProviderSessionMetadata - launchToken?: string - launchAgent?: TuiAgent - startupCommandDelivery?: StartupCommandDelivery - connectionId?: string | null - worktreeId?: string - sessionId?: string - // Why: lets a single tab open in a different shell than the user's default. - shellOverride?: string - projectRuntime?: ProjectExecutionRuntimeResolution - terminalColorQueryReplies?: { foreground?: string; background?: string } - // Why: mark the PTY hidden before its first byte so the delivery gate owns spawn-time queries (terminal-query-authority.md §races). - initiallyHidden?: boolean - // Why: main sync-flushes the (worktreeId,tabId,leafId→ptyId) binding before pty:spawn returns to close a SIGKILL race (INVESTIGATION.md). - tabId?: string - leafId?: string - // Why: main fires `agent_started` only on spawn success, so launch metadata rides this field (telemetry-plan.md §Agent launch semantics). - telemetry?: { agent_kind: AgentKind; launch_source: LaunchSource; request_kind: RequestKind } - }) => Promise<{ - id: string - launchAgent?: TuiAgent - launchConfig?: SleepingAgentLaunchConfig - snapshot?: string - snapshotCols?: number - snapshotRows?: number - snapshotPrefixAnsi?: string - snapshotFrameAnsi?: string - snapshotFrameRestoreAnsi?: string - snapshotKittyKeyboardFlags?: number - snapshotSeq?: number - isReattach?: boolean - isAlternateScreen?: boolean - replay?: string - sessionExpired?: boolean - coldRestore?: { scrollback: string; cwd: string; cols?: number; rows?: number } - startupCwdFallback?: { kind: 'worktree'; cwd: string } - agentResumeUnavailable?: true - }> - write: (id: string, data: string) => void - writeAccepted: (id: string, data: string) => Promise - onWriteUnavailable?: (callback: (payload: { id: string }) => void) => () => void - resize: (id: string, cols: number, rows: number) => void - claimViewport: (id: string, cols: number, rows: number) => void - reportGeometry: (id: string, cols: number, rows: number) => void - signal: (id: string, signal: string) => void - clearBuffer: (id: string) => void - kill: (id: string, opts?: { keepHistory?: boolean }) => Promise - ackColdRestore: (id: string) => void - ackData: (id: string, charCount: number, processedChars?: number) => void - onDeliveryResyncRequest: (callback: (payload: { requestId: number }) => void) => () => void - respondDeliveryResync: (payload: { - requestId: number - processedCharsByPty: Record - }) => void - /** Renderer-initiated delivery health/heal lane over invoke — reaches main - * even when every main→renderer push channel is dead (field wedge). */ - reportRendererDeliveryState: ( - report: PtyRendererDeliveryStateReport - ) => Promise - /** Live pty:data listener count on the preload emitter (sync) — heal-time - * discriminator between a detached listener and a dead channel. */ - getPtyDataListenerCount: () => number - /** One-shot signal that this page's pty:data dispatcher is registered, so - * main can release sends held during the load/reload boot window. */ - rendererDispatcherReady: () => void - setActiveRendererPty: (id: string, active: boolean) => void - setRendererPtyVisible: (id: string, visible: boolean) => void - /** Hidden-delivery gate (Phase 4): hidden=true lets main drop renderer - * byte delivery after model ingestion; reveal restores from snapshots. */ - setHiddenRendererPty: (id: string, hidden: boolean) => void - /** Ref-counted-on-the-renderer delivery-interest signal that suppresses - * the hidden-delivery gate while any raw-byte consumer is registered. */ - setPtyDeliveryInterest: (id: string, interested: boolean) => void - /** View-attribute bridge (Phase 5 slice 2): app-global composed terminal - * appearance push backing main's hidden-PTY OSC/DSR color replies. */ - publishTerminalViewAttributes: (attributes: TerminalViewAttributes) => void - hasChildProcesses: (id: string) => Promise - getForegroundProcess: (id: string) => Promise - inspectProcess: (id: string) => Promise<{ - foregroundProcess: string | null - hasChildProcesses: boolean - unavailable?: true - }> - confirmForegroundProcess: (id: string) => Promise - getCwd: (id: string) => Promise - getSize: (id: string) => Promise<{ cols: number; rows: number } | null> - listSessions: () => Promise - getAuthoritativeBufferSnapshotCapabilities?: ( - ids: string[] - ) => Promise<{ id: string; authoritative: boolean | null }[]> - hasPty: (id: string) => Promise - getMainBufferSnapshot: ( - id: string, - opts?: { scrollbackRows?: number } - ) => Promise<{ - data: string - frameRestoreAnsi?: string - cols: number - rows: number - cwd?: string | null - seq?: number - /** Start of main's pending renderer-delivery queue at snapshot time - * (equals `seq` when empty) — bounds the renderer's post-restore - * duplicate window. */ - pendingDeliveryStartSeq?: number - source?: 'headless' | 'renderer' - alternateScreen?: boolean - /** Authoritative normal buffer paired with an alternate-screen frame. */ - scrollbackAnsi?: string - /** Trailing incomplete escape the emulator ingested; the restorer must - * write it after its post-replay resets, last before live chunks. */ - pendingEscapeTailAnsi?: string - /** Effective kitty flags the snapshot owner proved at `seq`. Absent means - * unknown; consumers must not turn that into a known `0`. */ - kittyKeyboardFlags?: number - } | null> - getRendererDeliveryDebugSnapshot: () => Promise<{ - pendingPtyCount: number - pendingChars: number - maxPendingCharsByPty: number - rendererInFlightPtyCount: number - rendererInFlightChars: number - maxRendererInFlightCharsByPty: number - activeRendererPtyCount: number - flushScheduled: boolean - peakPendingChars: number - peakMaxPendingCharsByPty: number - peakRendererInFlightChars: number - peakMaxRendererInFlightCharsByPty: number - ackGatedFlushSkipCount: number - hiddenDeliveryGatedPtyCount: number - hiddenDeliveryGatedVisiblePtyCount: number - hiddenDeliveryGatedActivePtyCount: number - deliveryInterestPtyCount: number - hiddenDeliveryDroppedChars: number - hiddenDeliveryDroppedChunks: number - pendingDroppedChars: number - diagnostics: PtyMainDeliveryDiagnostics - rendererLifecycleResetCount: number - lastLifecycleResetClearedChars: number - rendererPtyDispatcherReady: boolean - rendererDispatcherReadyForcedCount: number - }> - resetRendererDeliveryDebug: () => Promise - onData: ( - callback: (data: { - id: string - data: string - seq?: number - rawLength?: number - transformed?: boolean - background?: boolean - droppedOutput?: boolean - }) => void - ) => () => void - onReplay: (callback: (data: { id: string; data: string }) => void) => () => void - /** Out-of-band main→renderer signal that renderer-bound bytes were - * dropped (hidden-delivery gate / pending cap); the pane restores from - * the model snapshot. Never delivered in-band on pty:data. */ - onModelRestoreNeeded: (callback: (event: PtyModelRestoreNeededEvent) => void) => () => void - /** Batched derived side-effect facts for PTYs whose bytes transit local - * main. */ - onSideEffect: (callback: (batch: TerminalSideEffectBatch) => void) => () => void - /** Title-only replay snapshot for (re)attach; attention facts never replay. */ - getSideEffectSnapshot: (id: string) => Promise - onExit: ( - callback: (data: { id: string; code: number; preserveRendererBinding?: boolean }) => void - ) => () => void - onSpawned: (callback: (data: { id: string }) => void) => () => void - onSerializeBufferRequest: ( - callback: (data: { - requestId: string - ptyId: string - opts?: { scrollbackRows?: number; altScreenForcesZeroRows?: boolean } - }) => void - ) => () => void - onClearBufferRequest: (callback: (data: { ptyId: string }) => void) => () => void - sendSerializedBuffer: ( - requestId: string, - snapshot: { - data: string - cols: number - rows: number - seq?: number - lastTitle?: string - kittyKeyboardFlags?: number - } | null - ) => void - declarePendingPaneSerializer: (paneKey: string) => Promise - settlePaneSerializer: (paneKey: string, gen: number) => Promise - clearPendingPaneSerializer: (paneKey: string, gen: number) => Promise - reportRendererSerializerReady?: (ptyId: string) => Promise - management: PtyManagementApi - } - feedback: { - submit: (args: { - feedback: string - submitAnonymously?: boolean - githubLogin: string | null - githubEmail: string | null - images?: { contentType: string; data: Uint8Array }[] - }) => Promise< - { ok: true; imagesDelivered?: boolean } | { ok: false; status: number | null; error: string } - > - } - crashReports: { - getLatestPending: () => Promise - getLatestReport: () => Promise - dismiss: (args: { reportId: string }) => Promise - recordRendererError: ( - args: ReactErrorBoundaryReportArgs - ) => Promise - recordBreadcrumb: (args: { name: string; data?: CrashReportBreadcrumbData }) => void - submit: (args: CrashReportSubmitArgs) => Promise - copyLatestDiagnostics: ( - args?: CrashReportCopyDiagnosticsArgs - ) => Promise<{ ok: true } | { ok: false; error: string }> - /** Exact V8/Blink heap sizes; null when the runtime withholds them. */ - readHeapStatistics: () => RendererHeapStatistics | null - } + orcaProfiles: OrcaProfileApi + platform: PlatformApi + e2e: E2EApi + repos: RepositoryApi + projects: ProjectsApi + projectGroups: ProjectGroupsApi + folderWorkspaces: FolderWorkspacesApi + sparsePresets: SparsePresetsApi + worktrees: WorktreeApi + workspaceCleanup: WorkspaceCleanupApi + workspaceSpace: WorkspaceSpaceApi + workspacePorts: WorkspacePortsApi + pty: PtyApi + feedback: FeedbackApi + crashReports: CrashReportsApi export: ExportApi - gh: { - viewer: () => Promise - repoSlug: (args: { - repoPath: string - repoId?: string - }) => Promise<{ owner: string; repo: string; host?: string } | null> - repoUpstream: (args: { - repoPath: string - repoId?: string - }) => Promise<{ owner: string; repo: string; host?: string } | null> - prForBranch: (args: { - repoPath: string - repoId?: string - branch: string - linkedPRNumber?: number | null - fallbackPRNumber?: number | null - acceptMergedFallbackPR?: boolean - currentHeadOid?: string | null - }) => Promise - refreshPRNow: (args: { candidate: GitHubPRRefreshCandidate }) => Promise - enqueuePRRefresh: (args: { - candidate: GitHubPRRefreshCandidate - reason: GitHubPRRefreshReason - priority?: number - }) => Promise - reportVisiblePRRefreshCandidates: (args: { - candidates: GitHubPRRefreshCandidate[] - generation: number - }) => Promise - onPRRefreshEvent: (callback: (event: GitHubPRRefreshEvent) => void) => () => void - issue: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - number: number - }) => Promise - workItem: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - number: number - type?: 'issue' | 'pr' - }) => Promise | null> - workItemByOwnerRepo: (args: { - repoPath: string - repoId?: string - owner: string - repo: string - host?: string - number: number - type: 'issue' | 'pr' - }) => Promise | null> - workItemDetails: ( - args: GitHubRepoSelectorArgs & { - number: number - type?: 'issue' | 'pr' - } - ) => Promise - notifyWorkItemMutated: (args: { - repoPath: string - repoId?: string - type: 'issue' | 'pr' - number: number - }) => Promise - prFileContents: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - prRepo?: GitHubOwnerRepo | null - path: string - oldPath?: string - status: GitHubPRFile['status'] - headSha: string - baseSha: string - } - ) => Promise - listIssues: (args: { - repoPath: string - repoId?: string - limit?: number - }) => Promise - createIssue: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - title: string - body: string - labels?: string[] - assignees?: string[] - }) => Promise - countWorkItems: (args: { repoPath: string; repoId?: string; query?: string }) => Promise - listWorkItems: (args: { - repoPath: string - repoId?: string - limit?: number - query?: string - page?: number - noCache?: boolean - }) => Promise>> - prChecks: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - headSha?: string - prRepo?: GitHubOwnerRepo | null - noCache?: boolean - } - ) => Promise - prCheckDetails: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - checkRunId?: number - workflowRunId?: number - checkName?: string - url?: string | null - prRepo?: GitHubOwnerRepo | null - }) => Promise - rerunPRChecks: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - headSha?: string - failedOnly?: boolean - prRepo?: GitHubOwnerRepo | null - } - ) => Promise<{ ok: true; count: number } | { ok: false; error: string }> - prComments: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - prNumber: number - prRepo?: GitHubOwnerRepo | null - noCache?: boolean - }) => Promise - setPRCommentReaction: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - reactionSubjectId: string - content: GitHubReactionContent - reacted: boolean - prRepo?: GitHubOwnerRepo | null - }) => Promise - resolveReviewThread: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - threadId: string - resolve: boolean - prRepo?: GitHubOwnerRepo | null - }) => Promise - setPRFileViewed: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - prRepo?: GitHubOwnerRepo | null - pullRequestId: string - path: string - viewed: boolean - } - ) => Promise - updatePRTitle: (args: { - repoPath: string - repoId?: string - prNumber: number - title: string - prRepo?: GitHubOwnerRepo | null - }) => Promise - mergePR: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - method?: 'merge' | 'squash' | 'rebase' - prRepo?: GitHubOwnerRepo | null - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - setPRAutoMerge: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - enabled: boolean - method?: 'merge' | 'squash' | 'rebase' - prRepo?: GitHubOwnerRepo | null - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - updatePRState: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - updates: { state: 'open' | 'closed' } - prRepo?: GitHubOwnerRepo | null - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - requestPRReviewers: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - reviewers: string[] - prRepo?: GitHubOwnerRepo | null - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - removePRReviewers: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - reviewers: string[] - prRepo?: GitHubOwnerRepo | null - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - updateIssue: ( - args: GitHubRepoSelectorArgs & { - number: number - updates: GitHubIssueUpdate - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - addIssueComment: ( - args: GitHubRepoSelectorArgs & { - number: number - body: string - /** Why: scopes the cross-window cache invalidation so a PR and issue sharing the same number don't evict each other. */ - type?: 'issue' | 'pr' - prRepo?: GitHubOwnerRepo | null - } - ) => Promise - addPRReviewCommentReply: ( - args: GitHubRepoSelectorArgs & { - prNumber: number - commentId: number - body: string - threadId?: string - path?: string - line?: number - prRepo?: GitHubOwnerRepo | null - } - ) => Promise - addPRReviewComment: ( - args: GitHubPRReviewCommentInput & { - repoId?: string - sourceContext?: TaskSourceContext | null - } - ) => Promise - listLabels: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - }) => Promise - listAssignableUsers: (args: { - repoPath: string - repoId?: string - sourceContext?: TaskSourceContext | null - }) => Promise - /** Subscribe to local-mutation broadcasts so the work-item-drawer cache can invalidate across windows. Returns an unsubscribe. */ - onWorkItemMutated: ( - callback: (payload: { - repoPath: string - repoId?: string - type: 'issue' | 'pr' - number: number - }) => void - ) => () => void - checkOrcaStarred: () => Promise - starOrca: (source: AppStarSource) => Promise - /** - * GitHub API rate-limit snapshot. Does NOT consume quota (the - * `rate_limit` endpoint is exempt). Cached 30s server-side — pass - * `force: true` to bust after a known-expensive op. - */ - rateLimit: (args?: { force?: boolean }) => Promise - /** Explains scope_missing ProjectV2 failures — notably a shell `GITHUB_TOKEN` shadowing the keyring credential, where `gh auth refresh` is a no-op. */ - diagnoseAuth: (args?: { host?: string }) => Promise - // ── ProjectV2 (GitHub Projects) ───────────────────────────────── - listAccessibleProjects: ( - args?: ListAccessibleProjectsArgs - ) => Promise - resolveProjectRef: (args: ResolveProjectRefArgs) => Promise - listProjectViews: (args: ListProjectViewsArgs) => Promise - getProjectViewTable: (args: GetProjectViewTableArgs) => Promise - projectWorkItemDetailsBySlug: ( - args: ProjectWorkItemDetailsBySlugArgs - ) => Promise - updateProjectItemField: ( - args: UpdateProjectItemFieldArgs - ) => Promise - clearProjectItemField: (args: ClearProjectItemFieldArgs) => Promise - updateIssueBySlug: (args: UpdateIssueBySlugArgs) => Promise - updatePullRequestBySlug: ( - args: UpdatePullRequestBySlugArgs - ) => Promise - addIssueCommentBySlug: ( - args: AddIssueCommentBySlugArgs - ) => Promise - updateIssueCommentBySlug: ( - args: UpdateIssueCommentBySlugArgs - ) => Promise - deleteIssueCommentBySlug: ( - args: DeleteIssueCommentBySlugArgs - ) => Promise - listLabelsBySlug: (args: ListLabelsBySlugArgs) => Promise - listAssignableUsersBySlug: ( - args: ListAssignableUsersBySlugArgs - ) => Promise - listIssueTypesBySlug: (args: ListIssueTypesBySlugArgs) => Promise - updateIssueTypeBySlug: (args: UpdateIssueTypeBySlugArgs) => Promise - } - hostedReview: { - forBranch: (args: HostedReviewForBranchArgs) => Promise - getCreationEligibility: ( - args: HostedReviewCreationEligibilityArgs - ) => Promise - create: (args: CreateHostedReviewArgs) => Promise - createStacked: (args: CreateStackedHostedReviewArgs) => Promise - } - // ── GitLab — parallel to gh, MR/issue surface only in v1 ──────── - // Shapes mirror gh.* except where GitLab's API differs (MR states, host-qualified project path, `glab api -i` paging). - gl: { - viewer: () => Promise - diagnoseAuth: () => Promise - rateLimit: (args?: { - force?: boolean - host?: string | null - }) => Promise - projectSlug: (args: GitLabRepoSelectorArgs) => Promise - mrForBranch: ( - args: GitLabRepoSelectorArgs & { - branch: string - linkedMRIid?: number | null - } - ) => Promise - mr: (args: GitLabRepoSelectorArgs & { iid: number }) => Promise - listMRs: ( - args: GitLabRepoSelectorArgs & { - state?: MRListState - page?: number - perPage?: number - query?: string - } - ) => Promise - /** Combined MR + issue list filtered by state. Issues are skipped - * when state is 'merged' (issues don't merge). */ - listWorkItems: ( - args: GitLabRepoSelectorArgs & { - state?: MRListState - page?: number - perPage?: number - query?: string - } - ) => Promise - issue: (args: GitLabRepoSelectorArgs & { number: number }) => Promise - listIssues: ( - args: GitLabRepoSelectorArgs & { - state?: 'opened' | 'closed' | 'all' - assignee?: string - limit?: number - } - ) => Promise<{ items: GitLabWorkItem[]; error?: ClassifiedError }> - createIssue: ( - args: GitLabRepoSelectorArgs & { - title: string - body: string - } - ) => Promise<{ ok: true; number: number; url: string } | { ok: false; error: string }> - updateIssue: ( - args: GitLabRepoSelectorArgs & { - number: number - updates: GitLabIssueUpdate - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - addIssueComment: ( - args: GitLabRepoSelectorArgs & { - number: number - body: string - } - ) => Promise - listLabels: (args: GitLabRepoSelectorArgs) => Promise - listAssignableUsers: (args: GitLabRepoSelectorArgs) => Promise - /** Cross-project user-scoped todos (gitlab.com/dashboard/todos). */ - todos: (args: GitLabRepoSelectorArgs) => Promise - /** Aggregated dialog payload — body + discussions + pipeline jobs. */ - workItemDetails: ( - args: GitLabRepoSelectorArgs & { - iid: number - type: 'issue' | 'mr' - } - ) => Promise - closeMR: ( - args: GitLabRepoSelectorArgs & { - iid: number - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - reopenMR: ( - args: GitLabRepoSelectorArgs & { - iid: number - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - mergeMR: ( - args: GitLabRepoSelectorArgs & { - iid: number - method?: 'merge' | 'squash' | 'rebase' - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - updateMR: ( - args: GitLabRepoSelectorArgs & { - iid: number - updates: GitLabMRUpdate - } - ) => Promise<{ ok: true } | { ok: false; error: string }> - updateMRReviewers: ( - args: GitLabRepoSelectorArgs & { - iid: number - reviewerIds: number[] - projectRef?: GitLabProjectRef | null - } - ) => Promise - addMRComment: ( - args: GitLabRepoSelectorArgs & { - iid: number - body: string - } - ) => Promise - addMRInlineComment: ( - args: GitLabRepoSelectorArgs & { - iid: number - input: GitLabMRInlineCommentInput - projectRef?: GitLabProjectRef | null - } - ) => Promise - resolveMRDiscussion: ( - args: GitLabRepoSelectorArgs & { - iid: number - discussionId: string - resolved: boolean - } - ) => Promise - jobTrace: ( - args: GitLabRepoSelectorArgs & { - jobId: number - projectRef?: GitLabProjectRef | null - /** Bound the trace in main to a readable excerpt (see gitLabJobTraceToLogExcerpt). */ - logExcerpt?: boolean - } - ) => Promise - retryJob: ( - args: GitLabRepoSelectorArgs & { - jobId: number - projectRef?: GitLabProjectRef | null - } - ) => Promise - workItemByPath: ( - args: GitLabRepoSelectorArgs & { - host: string - path: string - iid: number - type: 'issue' | 'mr' - } - ) => Promise | null> - } - bitbucket: { - connect: ( - args: BitbucketConnectArgs - ) => Promise<{ ok: true; account: string | null } | { ok: false; error: string }> - disconnect: () => Promise - status: () => Promise - } - linear: { - connect: (args: { - apiKey: string - }) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }> - disconnect: (args?: { workspaceId?: string }) => Promise - selectWorkspace: (args: { - workspaceId: LinearWorkspaceSelection - }) => Promise - status: () => Promise - testConnection: (args?: { - workspaceId?: string - }) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }> - searchIssues: (args: { - query: string - limit?: number - workspaceId?: LinearWorkspaceSelection - }) => Promise - listIssues: (args?: { - filter?: 'assigned' | 'created' | 'all' | 'completed' - limit?: number - workspaceId?: LinearWorkspaceSelection - attributeFilter?: LinearIssueAttributeFilter - }) => Promise> - createIssue: (args: { - teamId: string - title: string - description?: string - workspaceId?: string - parentIssueId?: string - projectId?: string | null - stateId?: string - priority?: number - assigneeId?: string | null - labelIds?: string[] - }) => Promise< - | { ok: true; id: string; identifier: string; title: string; url: string } - | { ok: false; error: string } - > - getIssue: (args: { id: string; workspaceId?: string }) => Promise - updateIssue: (args: { - id: string - updates: LinearIssueUpdate - workspaceId?: string - }) => Promise<{ ok: true } | { ok: false; error: string }> - addIssueComment: (args: { - issueId: string - body: string - workspaceId?: string - }) => Promise<{ ok: true; id: string } | { ok: false; error: string }> - issueComments: (args: { issueId: string; workspaceId?: string }) => Promise - listTeams: (args?: { workspaceId?: LinearWorkspaceSelection }) => Promise - listProjects: (args?: { - query?: string - limit?: number - workspaceId?: LinearWorkspaceSelection - force?: boolean - }) => Promise> - createProject: (args: { - name: string - description?: string - content?: string - teamIds: string[] - workspaceId?: string - leadId?: string | null - memberIds?: string[] - labelIds?: string[] - priority?: number - startDate?: string - targetDate?: string - }) => Promise<{ ok: true; project: LinearProjectDetail } | { ok: false; error: string }> - getProject: (args: { - id: string - workspaceId: string - force?: boolean - }) => Promise - listProjectIssues: (args: { - projectId: string - limit?: number - workspaceId: string - force?: boolean - }) => Promise> - listCustomViews: (args: { - model: LinearCustomViewModel - limit?: number - workspaceId?: LinearWorkspaceSelection - force?: boolean - }) => Promise> - getCustomView: (args: { - viewId: string - model: LinearCustomViewModel - workspaceId: string - force?: boolean - }) => Promise - listCustomViewIssues: (args: { - viewId: string - limit?: number - workspaceId: string - force?: boolean - }) => Promise> - listCustomViewProjects: (args: { - viewId: string - limit?: number - workspaceId: string - force?: boolean - }) => Promise> - teamStates: (args: { teamId: string; workspaceId?: string }) => Promise - teamLabels: (args: { teamId: string; workspaceId?: string }) => Promise - teamMembers: (args: { teamId: string; workspaceId?: string }) => Promise - } - jira: { - connect: (args: { - siteUrl: string - email: string - apiToken: string - authType?: 'cloud' | 'server' - }) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }> - disconnect: (args?: { siteId?: string }) => Promise - selectSite: (args: { siteId: JiraSiteSelection }) => Promise - status: () => Promise - readStatus: () => Promise - testConnection: (args?: { - siteId?: string - }) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }> - searchIssues: (args: { - jql: string - limit?: number - siteId?: JiraSiteSelection - requestId?: string - }) => Promise - cancelSearchIssues: (args: { requestId: string }) => Promise - listIssues: (args?: { - filter?: JiraIssueFilter - limit?: number - siteId?: JiraSiteSelection - }) => Promise - getIssue: (args: { key: string; siteId?: string }) => Promise - lookupIssueSummary: (args: { - key: string - siteId: string - requestId?: string - }) => Promise - cancelIssueSummary: (args: { requestId: string }) => Promise - createIssue: ( - args: JiraCreateIssueArgs - ) => Promise<{ ok: true; id: string; key: string; url: string } | { ok: false; error: string }> - updateIssue: (args: { - key: string - updates: JiraIssueUpdate - siteId?: string - }) => Promise<{ ok: true } | { ok: false; error: string }> - addIssueComment: (args: { - key: string - body: string - siteId?: string - }) => Promise<{ ok: true; id: string } | { ok: false; error: string }> - issueComments: (args: { key: string; siteId?: string }) => Promise - listProjects: (args?: { siteId?: JiraSiteSelection }) => Promise - listIssueTypes: (args: { projectIdOrKey: string; siteId?: string }) => Promise - listCreateFields: (args: { - projectIdOrKey: string - issueTypeId: string - siteId?: string - }) => Promise - listPriorities: (args?: { siteId?: string }) => Promise - listAssignableUsers: (args: { - key: string - query?: string - siteId?: string - }) => Promise - listTransitions: (args: { key: string; siteId?: string }) => Promise - getProjectStatusOrder: (args: { - projectKey: string - siteId?: string - }) => Promise - } - starNag: { - onShow: ( - callback: (payload?: { mode?: 'gh' | 'web'; surface?: 'card' | 'toast' }) => void - ) => () => void - onHide: (callback: () => void) => () => void - dismiss: () => Promise - later: () => Promise - complete: () => Promise - disable: () => Promise - openWeb: () => Promise - starOrca: () => Promise - forceShow: () => Promise - agentValueMoment: () => Promise<{ status: 'ready'; mode: 'gh' | 'web' } | { status: 'skipped' }> - showAgentValueMoment: () => Promise - onboardingCompleted: () => Promise - } - /** Fire-and-forget track. Loose IPC typing on purpose — the main-side validator enforces; - * renderer sites should import `track()` from lib/telemetry.ts, not reach here. */ - telemetryTrack: (name: string, props: Record) => Promise - /** Flip the persisted opt-in preference. Subject to a per-session - * consent-mutation rate limit on the main side (≤5/session). */ - telemetrySetOptIn: (optedIn: boolean) => Promise - /** Diagnostic file controls (telemetry-error-tracking.md §User controls). Main does the FS/network - * work and retains upload payloads so the renderer can't read or substitute arbitrary bytes. */ - diagnostics: { - getStatus: () => Promise - collectBundle: (lookbackMinutes?: number) => Promise - openBundlePreview: (bundleSubmissionId: string) => Promise - discardBundlePreview: (bundleSubmissionId: string) => Promise - uploadBundle: (bundleSubmissionId: string) => Promise - deleteBundle: (ticketId: string) => Promise - } - /** Read-only effective consent state (+ reason if disabled) — env vars are main-side state the renderer can't read directly. */ - telemetryGetConsentState: () => Promise - /** Banner ✕ — persist `optedIn = true` silently. Separate channel from `telemetrySetOptIn`, - * whose `via` derivation would wrongly fire `telemetry_opted_in`. Same per-session rate limit. */ - telemetryAcknowledgeBanner: () => Promise - settings: { - get: () => Promise - /** Synchronous persisted-settings read for startup decisions that can't wait for async hydration. Blocking IPC — call sparingly. */ - getSync: () => GlobalSettings | null - set: (args: Partial) => Promise - setActiveRuntimeEnvironmentPreference: (args: { - environmentId: string | null - }) => Promise - updatePRBotAuthorOverride: (args: { author: string; isBot: boolean }) => Promise - listFonts: () => Promise - previewGhosttyImport: () => Promise - previewWarpThemeImport: (source: WarpThemeImportSource) => Promise - /** Subscribe to out-of-band settings updates (e.g. View > Appearance toggles) to stay in sync with main. */ - onChanged: (callback: (updates: Partial) => void) => () => void - } - agentAwake: { - getStatus: () => Promise - onChanged: (callback: (status: ComputerAwakeStatus) => void) => () => void - } - localhostWorktreeLabels: { - register: (args: LocalhostWorktreeLabelRoute) => Promise - } - keybindings: { - get: () => Promise - ensureFile: () => Promise - setAction: (args: { - actionId: KeybindingActionId - bindings: string[] | null - }) => Promise - reload: () => Promise - openFile: () => Promise - revealFile: () => Promise - onChanged: (callback: (snapshot: KeybindingFileSnapshot) => void) => () => void - } - codexAccounts: { - list: () => Promise - add: (args?: { - runtime?: 'host' | 'wsl' - wslDistro?: string | null - }) => Promise - reauthenticate: (args: { accountId: string }) => Promise - remove: (args: { accountId: string }) => Promise - select: (args: { - accountId: string | null - runtime?: 'host' | 'wsl' - wslDistro?: string | null - }) => Promise - /** Live PTYs whose baked CODEX_HOME still points at a deselected account. */ - listStalePanes: (args: { ptyIds: string[] }) => Promise< - { - ptyId: string - launchAccountId: string | null - activeAccountId: string | null - /** Optional for compatibility with a pre-reason main process. */ - reason?: 'account-change' | 'home-route-change' - }[] - > - /** The selection lane each PTY launched from, keyed by pty id; unrecorded panes are absent. */ - listRecordedPaneLanes: (args: { ptyIds: string[] }) => Promise> - /** Drops launch records so a dismissed prompt stays dismissed across restarts. */ - forgetStalePanes: (args: { ptyIds: string[] }) => Promise - } - claudeAccounts: { - list: () => Promise - add: (args?: { - runtime?: 'host' | 'wsl' - wslDistro?: string | null - }) => Promise - cancelPendingLogin: () => Promise - reauthenticate: (args: { accountId: string }) => Promise - remove: (args: { accountId: string }) => Promise - select: (args: { - accountId: string | null - runtime?: 'host' | 'wsl' - wslDistro?: string | null - }) => Promise - } - cli: { - getInstallStatus: () => Promise - install: () => Promise - remove: () => Promise - getWslInstallStatus: (args?: { distro?: string | null }) => Promise - installWsl: (args?: { distro?: string | null }) => Promise - removeWsl: (args?: { distro?: string | null }) => Promise - } - codexConfigSync: { - status: () => Promise - } - agentHooks: { - claudeStatus: () => Promise - openClaudeStatus: () => Promise - codexStatus: () => Promise - geminiStatus: () => Promise - antigravityStatus: () => Promise - ampStatus: () => Promise - cursorStatus: () => Promise - droidStatus: () => Promise - commandCodeStatus: () => Promise - grokStatus: () => Promise - copilotStatus: () => Promise - hermesStatus: () => Promise - devinStatus: () => Promise - } - agentTrust: { - markTrusted: (args: { - preset: 'cursor' | 'copilot' | 'codex' - workspacePath: string - connectionId?: string - }) => Promise - } + gh: Merged + hostedReview: HostedReviewApi + gl: GitLabApi + bitbucket: BitbucketApi + linear: LinearApi + jira: JiraApi + starNag: StarNagApi + telemetryTrack: TelemetryApi['telemetryTrack'] + telemetrySetOptIn: TelemetryApi['telemetrySetOptIn'] + diagnostics: DiagnosticsApi + telemetryGetConsentState: TelemetryApi['telemetryGetConsentState'] + telemetryAcknowledgeBanner: TelemetryApi['telemetryAcknowledgeBanner'] + settings: SettingsApi + agentAwake: AgentAwakeApi + localhostWorktreeLabels: LocalhostWorktreeLabelsApi + keybindings: KeybindingsApi + codexAccounts: CodexAccountsApi + claudeAccounts: ClaudeAccountsApi + cli: CliApi + codexConfigSync: CodexConfigSyncApi + agentHooks: AgentHooksApi + agentTrust: AgentTrustApi preflight: PreflightApi - notifications: { - dispatch: (args: NotificationDispatchRequest) => Promise - dismiss: (ids: string[]) => Promise - openSystemSettings: () => Promise - getPermissionStatus: () => Promise - probeDelivery: (args?: { force?: boolean }) => Promise - playSound: (options?: { force?: boolean; volume?: number }) => Promise - } - onboarding: { - get: () => Promise - // Why: main merges the checklist field-by-field, so a partial checklist is fine. - update: ( - updates: Partial> & { - checklist?: Partial - } - ) => Promise - } - dashboard: { - openPopout: (view?: 'board' | 'map') => Promise - publishSnapshot: (snapshot: DashboardSnapshot) => Promise - getPopoutOpen: () => Promise - onPopoutOpenChanged: (callback: (open: boolean) => void) => () => void - onSnapshotRequested: (callback: () => void) => () => void - onRevealAgent: (callback: (args: DashboardRevealAgentArgs) => void) => () => void - onAckAgent: (callback: (paneKey: string) => void) => () => void - onSpawnAgent: (callback: (args: DashboardSpawnAgentArgs) => void) => () => void - onSleepWorkspace: (callback: (args: DashboardSleepWorkspaceArgs) => void) => () => void - requestSnapshot: () => Promise - onSnapshot: (callback: (snapshot: DashboardSnapshot) => void) => () => void - onViewRequested: (callback: (view: 'board' | 'map') => void) => () => void - revealAgent: (args: DashboardRevealAgentArgs) => Promise - ackAgent: (paneKey: string) => Promise - spawnAgent: (args: DashboardSpawnAgentArgs) => Promise - sleepWorkspace: (args: DashboardSleepWorkspaceArgs) => Promise - } - terminalPreview: { - connect: ( - ptyId: string, - opts?: { scrollbackRows?: number } - ) => Promise - input: (ptyId: string, data: string) => Promise - /** Claim the PTY grid for the preview dialog; resolves to the size actually in effect. */ - fit: ( - ptyId: string, - cols: number, - rows: number - ) => Promise<{ cols: number; rows: number } | null> - ack: (ptyId: string, bytes: number) => Promise - unsubscribe: (ptyId: string) => Promise - onData: (callback: (payload: TerminalPreviewDataPayload) => void) => () => void - } - macosTccPrompts: { - /** Fires once macOS has raised its Nth consent dialog naming Orca (#9756). */ - onThreshold: (callback: (payload: { promptCount: number }) => void) => () => void - consumePending: () => Promise<{ claimId: number; promptCount: number } | null> - acknowledgePending: (claimId: number) => Promise - releasePending: (claimId: number) => Promise - dismiss: () => Promise - } - developerPermissions: { - getStatus: () => Promise - request: (args: { id: DeveloperPermissionId }) => Promise - openSettings: (args: { id: DeveloperPermissionId }) => Promise - testLocalNetworkConnection: (args: { - host: string - port: number - }) => Promise - } - computerUsePermissions: { - getStatus: () => Promise - openSetup: (args?: { - id?: ComputerUsePermissionId - }) => Promise - reset: () => Promise - } - shell: { - openPath: (path: string) => Promise - openInFileManager: (path: string) => Promise - openInExternalEditor: ( - request: ShellOpenExternalEditorRequest - ) => Promise - openUrl: (url: string) => Promise - openFilePath: (path: string) => Promise - openFileUri: (uri: string) => Promise - pathExists: (path: string) => Promise - pickAttachment: () => Promise - pickImage: () => Promise - pickRepoIconImage: () => Promise<{ dataUrl: string; fileName: string } | null> - pickAudio: () => Promise - pickDirectory: (args: { defaultPath?: string }) => Promise - copyFile: (args: { srcPath: string; destPath: string }) => Promise - } - skills: { - discover: (target?: SkillDiscoveryTarget) => Promise - freshnessInventory: () => Promise - startUpdateRun: (names: string[]) => Promise - cancelUpdateRun: () => Promise - acknowledgeUpdateRun: () => Promise - getUpdateRun: () => Promise - onUpdateRun: (callback: (run: SkillUpdateRun) => void) => () => void - } - pet: { - import: () => Promise - importPetBundle: () => Promise - read: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise - delete: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise - } + notifications: NotificationsApi + onboarding: OnboardingApi + dashboard: DashboardApi + terminalPreview: TerminalPreviewApi + macosTccPrompts: MacosTccPromptsApi + developerPermissions: DeveloperPermissionsApi + computerUsePermissions: ComputerUsePermissionsApi + shell: ShellApi + skills: SkillsApi + pet: PetApi browser: BrowserApi emulator: EmulatorApi - hooks: { - check: (args: { repoId: string; hostId?: ExecutionHostId }) => Promise<{ - status?: 'ok' | 'error' - hasHooks: boolean - hooks: OrcaHooks | null - mayNeedUpdate: boolean - }> - inspectSetupScriptImports: (args: { - repoId: string - hostId?: ExecutionHostId - }) => Promise - createIssueCommandRunner: (args: { - repoId: string - worktreePath: string - command: string - }) => Promise - readIssueCommand: (args: { repoId: string; hostId?: ExecutionHostId }) => Promise<{ - status?: 'ok' | 'error' - localContent: string | null - sharedContent: string | null - effectiveContent: string | null - localFilePath: string - source: 'local' | 'shared' | 'none' - }> - writeIssueCommand: (args: { - repoId: string - content: string - hostId?: ExecutionHostId - }) => Promise - } - ephemeralVm: { - listRecipes: (args: { repoId: string }) => Promise<{ - status: 'ok' | 'error' - repoPath: string | null - recipes: OrcaHooks['environmentRecipes'] - diagnostics: NonNullable - message?: string - }> - listRecipeCatalog: () => Promise< - { - repoId: string - repoName: string - repoPath: string - recipes: NonNullable - diagnostics: NonNullable - }[] - > - doctor: (args: { repoId: string; recipeId: string }) => Promise - provision: (args: { - repoId: string - recipeId: string - workspaceName?: string - projectId?: string - workspaceId?: string - branch?: string - ref?: string - provisionId?: string - }) => Promise< - | { - ok: true - connectionType: 'orca-server' - runtime: EphemeralVmRuntimeRecord - environment: PublicKnownRuntimeEnvironment - stderr: string - warnings: EphemeralVmRecipeResultWarning[] - } - | { - ok: true - connectionType: 'ssh' - runtime: EphemeralVmRuntimeRecord - sshTargetId: string - stderr: string - warnings: EphemeralVmRecipeResultWarning[] - } - | { ok: false; error: string; stderr: string; stdout: string } - > - cancelProvision: (args: { provisionId: string }) => Promise<{ cancelled: boolean }> - onProvisionEvent: ( - callback: (event: { provisionId: string; stream: 'stdout' | 'stderr'; chunk: string }) => void - ) => () => void - listRuntimes: () => Promise - attachWorkspace: (args: { - runtimeId: string - workspaceId: string - }) => Promise - suspendWorkspace: (args: { workspaceId: string }) => Promise - resumeWorkspace: (args: { workspaceId: string }) => Promise - cleanup: (args: { runtimeId: string }) => Promise - getCleanupCommand: (args: { runtimeId: string }) => Promise<{ - runtimeId: string - command: string | null - payloadJson: string - cleanupDisabled: boolean - message?: string - }> - } - cache: { - getGitHub: () => Promise<{ - pr: Record - issue: Record - }> - setGitHub: (args: { - cache: { - pr: Record - issue: Record - } - }) => Promise - } - session: { - // hostId defaults to the 'local' partition on main, so omitting it stays backward-compatible. - get: (hostId?: ExecutionHostId) => Promise - set: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => Promise - patch: (args: WorkspaceSessionPatch, hostId?: ExecutionHostId) => Promise - flush: () => Promise - readTerminalScrollback: (args: { ref: string }) => string | null - setSync: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => void - } - remoteWorkspace: { - get: (args: { targetId: string }) => Promise - setForConnectedTargets: (args: { - session?: WorkspaceSessionState - hydratedTargetIds?: string[] - }) => Promise<{ targetId: string; result: RemoteWorkspacePatchResult }[]> - listEnabledConnectedTargets: () => Promise - listConnectedClients: (args?: { - targetIds?: string[] - }) => Promise<{ targetId: string; clients: RemoteWorkspaceConnectedClient[] }[]> - clientId: () => Promise - onChanged: (callback: (event: RemoteWorkspaceChangedEvent) => void) => () => void - } - updater: { - getVersion: () => Promise - getStatus: () => Promise - check: (options?: UpdateCheckOptions) => Promise - download: () => Promise - quitAndInstall: () => Promise - dismissNudge: () => Promise - dismissAvailableUpdate: () => Promise - /** Desktop-only. Rejects unless the current status carries `linux-package-install` recovery. */ - getLinuxPackageInstallInstructions: () => Promise - /** Desktop-only. Reveals the revalidated cached package in the native file manager. */ - showLinuxPackage: () => Promise - listBuilds: (channel: ReleaseChannel) => Promise - - onStatus: (callback: (status: UpdateStatus) => void) => () => void - onClearDismissal: (callback: () => void) => () => void - } - notebook: { - runPythonCell: (args: { - filePath: string - code: string - preamble?: string - connectionId?: string | null - }) => Promise<{ stdout: string; stderr: string; exitCode: number | null; error?: string }> - } + hooks: HooksApi + ephemeralVm: EphemeralVmApi + cache: WorkspaceSessionApi['cache'] + session: WorkspaceSessionApi['session'] + remoteWorkspace: WorkspaceSessionApi['remoteWorkspace'] + updater: UpdaterApi + notebook: FilesystemApi['notebook'] stats: StatsApi memory: MemoryApi claudeUsage: ClaudeUsageApi @@ -2748,999 +132,74 @@ export type PreloadApi = { openCodeUsage: OpenCodeUsageApi aiVault: AiVaultApi nativeChat: NativeChatApi - fs: { - readDir: (args: { dirPath: string; connectionId?: string }) => Promise - readFile: (args: { - filePath: string - connectionId?: string - includeLocalLogMetadata?: boolean - }) => Promise<{ - content: string - isBinary: boolean - isImage?: boolean - mimeType?: string - fileIdentity?: string - }> - readLocalLogTail: (args: LocalLogTailReadArgs) => Promise - startLocalLogTail: (args: LocalLogTailWatchArgs) => Promise - stopLocalLogTail: (args: { subscriptionId: string }) => Promise - onLocalLogTailChanged: (callback: (payload: LocalLogTailChangedPayload) => void) => () => void - downloadFile: (args: { - filePath: string - connectionId: string - }) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }> - downloadFolder: (args: { - dirPath: string - connectionId: string - }) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }> - saveDownloadedFile: (args: { - suggestedName: string - content: string - encoding: 'utf8' | 'base64' - }) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }> - startDownloadedFile: (args: { - suggestedName: string - }) => Promise< - { canceled: true } | { canceled: false; transferId: string; destinationPath: string } - > - appendDownloadedFileChunk: (args: { - transferId: string - contentBase64: string - }) => Promise<{ ok: true }> - finishDownloadedFile: (args: { - transferId: string - }) => Promise<{ canceled: false; destinationPath: string }> - cancelDownloadedFile: (args: { transferId: string }) => Promise<{ ok: true }> - listMarkdownDocuments: (args: { - rootPath: string - connectionId?: string - }) => Promise - writeFile: ( - args: { filePath: string; content: string; connectionId?: string } & SshMutationExpectation - ) => Promise - createFile: ( - args: { filePath: string; connectionId?: string } & SshMutationExpectation - ) => Promise - createDir: ( - args: { dirPath: string; connectionId?: string } & SshMutationExpectation - ) => Promise - rename: ( - args: { oldPath: string; newPath: string; connectionId?: string } & SshMutationExpectation - ) => Promise - copy: ( - args: { - sourcePath: string - destinationPath: string - connectionId?: string - } & SshMutationExpectation - ) => Promise - deletePath: ( - args: { - targetPath: string - connectionId?: string - recursive?: boolean - } & SshMutationExpectation - ) => Promise - authorizeExternalPath: (args: { targetPath: string }) => Promise - stat: (args: { - filePath: string - connectionId?: string - }) => Promise<{ size: number; isDirectory: boolean; mtime: number }> - pathExists: (args: { filePath: string; connectionId?: string }) => Promise - listFiles: (args: { - rootPath: string - connectionId?: string - excludePaths?: string[] - requestToken?: string - }) => Promise - cancelListFiles: (args: { requestToken: string }) => Promise - search: (args: SearchOptions & { connectionId?: string }) => Promise - importExternalPaths: ( - args: { - sourcePaths: string[] - destDir: string - connectionId?: string - ensureDir?: boolean - } & SshMutationExpectation - ) => Promise<{ - results: ( - | { - sourcePath: string - status: 'imported' - destPath: string - kind: 'file' | 'directory' - renamed: boolean - } - | { - sourcePath: string - status: 'skipped' - reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported' - } - | { - sourcePath: string - status: 'failed' - reason: string - } - )[] - }> - stageExternalPathsForRuntimeUpload: (args: { sourcePaths: string[] }) => Promise<{ - sources: ( - | { - sourcePath: string - status: 'staged' - name: string - kind: 'file' | 'directory' - entries: ( - | { relativePath: string; kind: 'directory' } - | { relativePath: string; kind: 'file'; contentBase64: string } - )[] - } - | { - sourcePath: string - status: 'skipped' - reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported' - } - | { - sourcePath: string - status: 'failed' - reason: string - } - )[] - }> - resolveDroppedPathsForAgent: ( - args: { - paths: string[] - worktreePath: string - connectionId?: string - } & SshMutationExpectation - ) => Promise<{ - resolvedPaths: string[] - skipped: { - sourcePath: string - reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported' - }[] - failed: { sourcePath: string; reason: string }[] - }> - watchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise - unwatchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise - onFsChanged: (callback: (payload: FsChangedPayload) => void) => () => void - } - git: { - status: (args: { - worktreePath: string - connectionId?: string - includeIgnored?: boolean - bypassEffectiveUpstreamNegativeCache?: boolean - reuseLineStats?: boolean - /** Merge-base OID to measure the branch line total against; omit to skip the work. */ - branchLineTotalMergeBase?: string - requestToken?: string - }) => Promise - cancelStatus: (args: { requestToken: string }) => Promise - setStatusUpstreamRefWatch: (args: { - worktreeId: string - worktreePath: string - executionHostId: string - connectionId?: string - branch?: string - upstreamName?: string - }) => Promise - submoduleStatus: (args: { - worktreePath: string - submodulePath: string - connectionId?: string - area?: GitStagingArea - }) => Promise - checkIgnored: (args: { - worktreePath: string - paths: string[] - connectionId?: string - }) => Promise - findHugeFoldersToIgnore: (args: { worktreePath: string }) => Promise - appendGitignore: (args: { worktreePath: string; folderName: string }) => Promise - history: ( - args: { worktreePath: string; connectionId?: string } & GitHistoryOptions - ) => Promise - conflictOperation: (args: { - worktreePath: string - connectionId?: string - }) => Promise - abortMerge: (args: { worktreePath: string; connectionId?: string }) => Promise - abortRebase: (args: { worktreePath: string; connectionId?: string }) => Promise - diff: (args: { - worktreePath: string - filePath: string - staged: boolean - compareAgainstHead?: boolean - connectionId?: string - }) => Promise - branchCompare: (args: { - worktreePath: string - baseRef: string - connectionId?: string - }) => Promise - commitCompare: (args: { - worktreePath: string - commitId: string - connectionId?: string - }) => Promise - upstreamStatus: (args: { - worktreePath: string - connectionId?: string - pushTarget?: GitPushTarget - }) => Promise - fetch: (args: { - worktreePath: string - connectionId?: string - pushTarget?: GitPushTarget - }) => Promise - syncFork: (args: { - worktreePath: string - connectionId?: string - expectedUpstream: GitForkSyncExpectedUpstream - }) => Promise - push: (args: { - worktreePath: string - publish?: boolean - forceWithLease?: boolean - connectionId?: string - pushTarget?: GitPushTarget - }) => Promise - pull: (args: { - worktreePath: string - connectionId?: string - pushTarget?: GitPushTarget - }) => Promise - fastForward: (args: { - worktreePath: string - connectionId?: string - pushTarget?: GitPushTarget - }) => Promise - rebaseFromBase: (args: { - worktreePath: string - baseRef: string - connectionId?: string - }) => Promise - branchDiff: (args: { - worktreePath: string - compare: { - baseRef: string - baseOid: string - headOid: string - mergeBase: string - } - filePath: string - oldPath?: string - connectionId?: string - }) => Promise - commitDiff: (args: { - worktreePath: string - commitOid: string - parentOid?: string | null - filePath: string - oldPath?: string - connectionId?: string - }) => Promise - commit: (args: { - worktreePath: string - message: string - connectionId?: string - }) => Promise<{ success: boolean; error?: string }> - generateCommitMessage: (args: { - worktreePath: string - /** Raw (unstripped) worktree meta key; validated against worktreePath in main. */ - worktreeId?: string - repoId?: string - connectionId?: string - sourceControlAiResolvedParams?: ResolvedSourceControlAiGenerationParams - sourceControlAi?: SourceControlAiSettings - agentCmdOverrides?: Partial> - }) => Promise< - | { success: true; message: string; agentLabel?: string } - | { success: false; error: string; canceled?: boolean } - > - discoverCommitMessageModels: (args: { - agentId: string - worktreePath?: string - connectionId?: string - }) => Promise< - | { - success: true - capability: CommitMessageAgentCapability - models: CommitMessageModelCapability[] - defaultModelId: string - catalogOrigin: 'probe' | 'spec' - } - | { success: false; error: string } - > - cancelGenerateCommitMessage: (args: { - worktreePath: string - connectionId?: string - }) => Promise - generatePullRequestFields: (args: { - worktreePath: string - /** Raw (unstripped) worktree meta key; validated against worktreePath in main. */ - worktreeId?: string - repoId?: string - base: string - title: string - body: string - draft: boolean - provider?: HostedReviewProvider - useTemplate?: boolean - connectionId?: string - sourceControlAiResolvedParams?: ResolvedSourceControlAiGenerationParams - sourceControlAi?: SourceControlAiSettings - agentCmdOverrides?: Partial> - }) => Promise< - | { - success: true - fields: { base: string; title: string; body: string; draft: boolean } - agentLabel?: string - branchChangedByPreparation?: boolean - } - | { success: false; error: string; canceled?: boolean; branchChangedByPreparation?: boolean } - > - cancelGeneratePullRequestFields: (args: { - worktreePath: string - connectionId?: string - }) => Promise - stage: (args: { - worktreePath: string - filePath: string - connectionId?: string - }) => Promise - bulkStage: (args: { - worktreePath: string - filePaths: string[] - connectionId?: string - }) => Promise - unstage: (args: { - worktreePath: string - filePath: string - connectionId?: string - }) => Promise - bulkUnstage: (args: { - worktreePath: string - filePaths: string[] - connectionId?: string - }) => Promise - discard: (args: { - worktreePath: string - filePath: string - connectionId?: string - }) => Promise - bulkDiscard: (args: { - worktreePath: string - filePaths: string[] - connectionId?: string - }) => Promise - remoteFileUrl: (args: { - worktreePath: string - relativePath: string - line: number - connectionId?: string - }) => Promise - remoteCommitUrl: (args: { - worktreePath: string - sha: string - connectionId?: string - }) => Promise - } - ui: { - get: () => Promise - set: (args: Partial) => Promise - recordFeatureInteraction: (id: FeatureInteractionId) => Promise - onStateChanged: (callback: (ui: PersistedUIState) => void) => () => void - onOpenSettings: (callback: () => void) => () => void - /** Consumes a one-shot tray/menu-bar "open settings" intent queued before mount. */ - consumePendingOpenSettings: () => Promise - onOpenSetupGuide: (callback: () => void) => () => void - onOpenFeatureTour: (callback: () => void) => () => void - onOpenCrashReport: (callback: () => void) => () => void - onToggleLeftSidebar: (callback: () => void) => () => void - onToggleRightSidebar: (callback: () => void) => () => void - onToggleWorktreePalette: (callback: () => void) => () => void - onToggleFloatingTerminal: (callback: () => void) => () => void - onTerminalShortcutCaptured: ( - callback: (data: { actionId: KeybindingActionId }) => void - ) => () => void - onOpenQuickOpen: (callback: () => void) => () => void - onToggleQuickCommandsMenu: (callback: () => void) => () => void - onOpenNewWorkspace: (callback: () => void) => () => void - onDeleteCurrentWorkspace: (callback: () => void) => () => void - onOpenWorkspaceBoard: (callback: () => void) => () => void - onOpenTasks: (callback: () => void) => () => void - onJumpToWorktreeIndex: (callback: (index: number) => void) => () => void - onJumpToTabIndex: (callback: (index: number) => void) => () => void - onWorktreeHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void - onNewBrowserTab: (callback: () => void) => () => void - onNewMarkdownTab: (callback: () => void) => () => void - onNewSimulatorTab: (callback: () => void) => () => void - onRequestTabCreate: ( - callback: (data: { - requestId: string - url: string - worktreeId?: string - sessionProfileId?: string | null - sessionPartition?: string - activate?: boolean - }) => void - ) => () => void - replyTabCreate: (reply: { requestId: string; browserPageId?: string; error?: string }) => void - onRequestTabSetProfile: ( - callback: (data: { - requestId: string - browserPageId: string - profileId: string - sessionPartition?: string - }) => void - ) => () => void - replyTabSetProfile: (reply: { requestId: string; error?: string }) => void - onRequestTabClose: ( - callback: (data: { requestId: string; tabId: string | null; worktreeId?: string }) => void - ) => () => void - replyTabClose: (reply: { requestId: string; error?: string }) => void - onNewTerminalTab: (callback: () => void) => () => void - onFocusBrowserAddressBar: (callback: () => void) => () => void - onFindInBrowserPage: (source: BrowserFindSource, callback: () => void) => () => void - onReloadBrowserPage: (callback: () => void) => () => void - onBrowserHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void - onZoomBrowserPage: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void - onHardReloadBrowserPage: (callback: () => void) => () => void - onCloseActiveTab: (callback: () => void) => () => void - onCloseFloatingItem: (callback: (payload: { sourceId: string }) => void) => () => void - onSelectFloatingIndex: (callback: (payload: { index: number }) => void) => () => void - onSwitchTab: (callback: (direction: 1 | -1) => void) => () => void - onSwitchTabAcrossAllTypes: (callback: (direction: 1 | -1) => void) => () => void - onSwitchRecentTab: (callback: () => void) => () => void - onSwitchTerminalTab: (callback: (direction: 1 | -1) => void) => () => void - onCtrlTabKeyDown: (callback: (data: { shiftKey: boolean }) => void) => () => void - onCtrlTabKeyUp: (callback: () => void) => () => void - onToggleStatusBar: (callback: () => void) => () => void - onDictationKeyDown: (callback: () => void) => () => void - onExportPdfRequested: (callback: () => void) => () => void - onAppMenuPaste: (callback: () => void) => () => void - onAppMenuSelectionAction: (callback: (action: 'copy' | 'select-all') => void) => () => void - onEditableContextPaste: (callback: (data: { plainTextOnly: boolean }) => void) => () => void - onActivateWorktree: ( - callback: (data: { - repoId: string - worktreeId: string - setup?: WorktreeSetupLaunch - startup?: WorktreeStartupLaunch - defaultTabs?: WorktreeDefaultTabsLaunch - }) => void - ) => () => void - onCreateTerminal: ( - callback: (data: { - requestId?: string - worktreeId: string - command?: string - cwd?: string - env?: Record - launchConfig?: SleepingAgentLaunchConfig - resumeProviderSession?: AgentProviderSessionMetadata - launchToken?: string - launchAgent?: TuiAgent - viewMode?: 'terminal' | 'chat' - title?: string - ptyId?: string - activate?: boolean - focus?: boolean - presentation?: RuntimeTerminalPresentation - surfaceOwner?: false - tabId?: string - leafId?: string - splitFromLeafId?: string - splitDirection?: 'horizontal' | 'vertical' - splitTelemetrySource?: TerminalPaneSplitSource - }) => void - ) => () => void - onRequestTerminalCreate: ( - callback: (data: RuntimeTerminalCreateRequestPayload) => void - ) => () => void - onRequestTerminalTabMount: ( - callback: (data: { worktreeId: string; tabId?: string; ptyId?: string }) => void - ) => () => void - replyTerminalCreate: (reply: TerminalTabCreateReply) => void - onSplitTerminal: ( - callback: (data: { - tabId: string - paneRuntimeId: number - direction: 'horizontal' | 'vertical' - command?: string - telemetrySource?: TerminalPaneSplitSource - }) => void - ) => () => void - onRenameTerminal: ( - callback: (data: { tabId: string; title: string | null }) => void - ) => () => void - onFocusTerminal: ( - callback: (data: { - tabId: string - worktreeId: string - leafId?: string | null - ackPaneKeyOnSuccess?: string - flashFocusedPane?: boolean - scrollToBottomIfOutputSinceLastView?: boolean - }) => void - ) => () => void - onFocusEditorTab: ( - callback: (data: { tabId: string; worktreeId: string }) => void - ) => () => void - onCloseSessionTab: ( - callback: (data: { tabId: string; worktreeId: string }) => void - ) => () => void - onMoveSessionTab: ( - callback: (data: { worktreeId: string } & RuntimeMobileSessionTabMove) => void - ) => () => void - onOpenFileFromMobile: ( - callback: (data: { - worktreeId: string - filePath: string - relativePath: string - runtimeEnvironmentId?: string - }) => void - ) => () => void - onOpenDiffFromMobile: ( - callback: (data: { - worktreeId: string - filePath: string - relativePath: string - staged: boolean - runtimeEnvironmentId?: string - }) => void - ) => () => void - onMobileMarkdownRequest: ( - callback: (request: RuntimeMobileMarkdownRequest) => void - ) => () => void - respondMobileMarkdownRequest: (response: RuntimeMobileMarkdownResponse) => void - onCloseTerminal: ( - callback: (data: { tabId: string; paneRuntimeId?: number }) => void - ) => () => void - onTerminalTabCloseRequest: (callback: (request: TerminalTabCloseRequest) => void) => () => void - respondTerminalTabClose: (response: TerminalTabCloseResponse) => void - onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void - onResumeSleepingAgents: (callback: (data: { worktreeId: string }) => void) => () => void - onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void - onSystemResumed: (callback: () => void) => () => void - readClipboardText: (options?: ReadClipboardTextOptions) => Promise - readSelectionClipboardText: (options?: ReadClipboardTextOptions) => Promise - saveClipboardImageAsTempFile: (args?: { - connectionId?: string | null - runtimeEnvironmentId?: string | null - }) => Promise - writeClipboardText: (text: string) => Promise - writeTerminalClipboardText: (text: string) => Promise - writeSelectionClipboardText: (text: string) => Promise - writeClipboardImage: (dataUrl: string) => Promise - performNativePaste: (options?: { mode?: 'paste' | 'paste-and-match-style' }) => void - performNativeSelectionAction: (action: 'copy' | 'select-all') => void - writeClipboardFile: ( - args: - | { - filePath: string - connectionId?: string | null - } - | string - ) => Promise<{ ok: boolean; reason?: string }> - onFileDrop: (callback: (data: NativeFileDropPayload) => void) => () => void - getZoomLevel: () => number - setZoomLevel: (level: number) => void - syncTrafficLights: (zoomFactor: number) => void - setMarkdownEditorFocused: (focused: boolean) => void - setRichMarkdownContextMenuTarget: (target: RichMarkdownContextMenuTableTarget | null) => void - setTerminalInputFocused: (focused: boolean) => void - setFloatingFocus: (state: { panelFocused: boolean; terminalFocused: boolean }) => void - setShortcutRecorderFocused: (focused: boolean) => void - onRichMarkdownContextCommand: ( - callback: (payload: RichMarkdownContextMenuCommandPayload) => void - ) => () => void - onFullscreenChanged: (callback: (isFullScreen: boolean) => void) => () => void - minimize: () => void - maximize: () => void - isMaximized: () => Promise - onMaximizeChanged: (callback: (isMaximized: boolean) => void) => () => void - requestClose: () => void - popupMenu: () => void - onWindowCloseRequested: (callback: (data: { isQuitting: boolean }) => void) => () => void - confirmWindowClose: () => void - notifyWindowRevealed: () => void - } - runtime: { - syncWindowGraph: (graph: RuntimeSyncWindowGraph) => Promise - getStatus: () => Promise - call: (args: { method: string; params?: unknown }) => Promise> - getTerminalFitOverrides: () => Promise< - { ptyId: string; mode: 'mobile-fit' | 'remote-desktop-fit'; cols: number; rows: number }[] - > - getTerminalDrivers: () => Promise< - { - ptyId: string - driver: RuntimeTerminalDriverState - }[] - > - getBrowserDrivers: () => Promise< - { - browserPageId: string - driver: RuntimeBrowserDriverState - }[] - > - restoreTerminalFit: (ptyId: string) => Promise<{ restored: boolean }> - reclaimBrowserForDesktop: (browserPageId: string) => Promise<{ reclaimed: boolean }> - onTerminalFitOverrideChanged: ( - callback: (event: { - ptyId: string - mode: 'mobile-fit' | 'remote-desktop-fit' | 'desktop-fit' - cols: number - rows: number - }) => void - ) => () => void - onTerminalDriverChanged: ( - callback: (event: { ptyId: string; driver: RuntimeTerminalDriverState }) => void - ) => () => void - onNativeChatLaunchDraftResolved?: ( - callback: (event: { tabId: string; text: string; createdAt: number }) => void - ) => () => void - onBrowserDriverChanged: ( - callback: (event: { browserPageId: string; driver: RuntimeBrowserDriverState }) => void - ) => () => void - } - runtimeEnvironments: { - list: () => Promise - addFromPairingCode: (args: { - name: string - pairingCode: string - }) => Promise<{ environment: PublicKnownRuntimeEnvironment }> - verifyAndAddFromPairingCode: (args: { - name: string - pairingCode: string - allowLoopback?: boolean - }) => Promise - resolve: (args: { selector: string }) => Promise - remove: (args: { selector: string }) => Promise<{ removed: PublicKnownRuntimeEnvironment }> - disconnect: (args: { - selector: string - }) => Promise<{ disconnected: PublicKnownRuntimeEnvironment }> - connect: (args: { - selector: string - timeoutMs?: number - }) => Promise> - getStatus: (args: { - selector: string - timeoutMs?: number - }) => Promise> - // Why: system resume / browser online advance pending shared-control reconnect timers only. - retryConnectionsNow?: () => Promise - call: (args: { - selector: string - method: string - params?: unknown - timeoutMs?: number - expectedEnvironmentPairingRevision?: number - }) => Promise> - subscribe: ( - args: { - selector: string - method: string - params?: unknown - timeoutMs?: number - expectedEnvironmentPairingRevision?: number - }, - callbacks: { - onResponse: (response: RuntimeRpcResponse) => void - onBinary?: (bytes: Uint8Array) => void - onError?: (error: { code: string; message: string }) => void - onClose?: () => void - } - ) => Promise - } - rateLimits: { - get: () => Promise - refresh: () => Promise - refreshCodexForTarget: (target: RateLimitRuntimeTarget) => Promise - consumeCodexResetCredit: () => Promise - refreshClaudeForTarget: (target: RateLimitRuntimeTarget) => Promise - setPollingInterval: (ms: number) => Promise - fetchInactiveClaudeAccounts: () => Promise - fetchInactiveCodexAccounts: () => Promise - refreshMiniMax: () => Promise - refreshGrok: () => Promise - onUpdate: (callback: (state: RateLimitState) => void) => () => void - } - minimaxCredentials: { - getStatus: () => Promise<{ configured: boolean }> - saveCookie: (cookie: string) => Promise<{ configured: boolean }> - clearCookie: () => Promise<{ configured: boolean }> - } - grokAccounts: { - getStatus: () => Promise - } - ssh: { - listTargets: () => Promise - // Removed-target id → last known label, for a friendly host name on workspaces still pinned to a removed target. - listRemovedTargetLabels: () => Promise> - addTarget: (args: { target: Omit }) => Promise - updateTarget: (args: { - id: string - updates: Partial> - }) => Promise - removeTarget: (args: { id: string }) => Promise - importConfig: (args?: { reAdopt?: boolean }) => Promise - listConfigHosts: (args?: SshConfigHostListArgs) => Promise - resolveConfigHost: (args: { alias: string }) => Promise - connect: (args: { targetId: string }) => Promise - disconnect: (args: { targetId: string }) => Promise - terminateSessions: (args: { targetId: string }) => Promise - resetRelay: (args: { targetId: string }) => Promise - getState: (args: { targetId: string }) => Promise - needsPassphrasePrompt: (args: { targetId: string }) => Promise - testConnection: (args: { - targetId: string - }) => Promise<{ success: boolean; error?: string; state?: SshConnectionState }> - onStateChanged: ( - callback: (data: { targetId: string; state: SshConnectionState }) => void - ) => () => void - addPortForward: (args: { - targetId: string - localPort: number - remoteHost: string - remotePort: number - label?: string - }) => Promise - updatePortForward: (args: { - id: string - targetId: string - localPort: number - remoteHost: string - remotePort: number - label?: string - }) => Promise - removePortForward: (args: { id: string }) => Promise - listPortForwards: (args?: { targetId?: string }) => Promise - listDetectedPorts: (args: { targetId: string }) => Promise - onPortForwardsChanged: ( - callback: (data: { targetId: string; forwards: PortForwardEntry[] }) => void - ) => () => void - onDetectedPortsChanged: ( - callback: (data: { targetId: string; ports: EnrichedDetectedPort[] }) => void - ) => () => void - browseDir: (args: { targetId: string; dirPath: string }) => Promise<{ - entries: { name: string; isDirectory: boolean }[] - resolvedPath: string - pathFlavor: FilesystemPathFlavor - }> - onCredentialRequest: ( - callback: (data: { - requestId: string - targetId: string - kind: 'passphrase' | 'password' - detail: string - }) => void - ) => () => void - onCredentialResolved: (callback: (data: { requestId: string }) => void) => () => void - submitCredential: (args: { requestId: string; value: string | null }) => Promise - } - automations: { - list: () => Promise - listRuns: (args?: { automationId?: string }) => Promise - listExternalManagers: () => Promise - listExternalRuns: (input: ExternalAutomationRunsInput) => Promise - createExternal: (input: ExternalAutomationCreateInput) => Promise - updateExternal: (input: ExternalAutomationUpdateInput) => Promise - runExternalAction: (input: ExternalAutomationActionInput) => Promise - create: (input: AutomationCreateInput) => Promise - update: (args: { id: string; updates: AutomationUpdateInput }) => Promise - delete: (args: { id: string }) => Promise - runNow: (args: { id: string }) => Promise - runPrecheck: (args: { - automationId: string - runId: string - }) => Promise - markDispatchResult: (result: AutomationDispatchResult) => Promise - snapshotWorkspaceName: (args: { workspaceId: string; displayName: string }) => Promise - rendererReady: () => Promise - onDispatchRequested: (callback: (request: AutomationDispatchRequest) => void) => () => void - } - wsl: { - isAvailable: () => Promise - listDistros: () => Promise - } - pwsh: { - isAvailable: () => Promise - } - gitBash: { - isAvailable: () => Promise - } - plugins: { - list: () => Promise - listLanguagePacks: () => Promise - /** Records the consent-dialog answer; approval is keyed to the plugin's - * current capability and trusted-worker fingerprint. */ - consent: (args: PluginConsentRequest) => Promise - setEnabled: (args: { pluginKey: string; enabled: boolean }) => Promise - /** Returns the panel's CSP-wrapped HTML, or null when the plugin or - * panel is missing/disabled. Rendered only inside a sandboxed iframe. */ - readPanelEntry: (args: { - pluginKey: string - panelId: string - }) => Promise - invokeCommand: (args: { - pluginKey: string - commandId: string - args?: unknown - }) => Promise - /** Relays a sandboxed panel's bridge request to main, which enforces the - * plugin's consented capabilities before executing. */ - panelAction: (args: { - sessionToken: string - action: string - params?: unknown - }) => Promise - install: (source: PluginHostInstallSource) => Promise - listMarketplaces: () => Promise - addMarketplace: ( - source: PluginMarketplaceGitSource - ) => Promise - removeMarketplace: (args: { sourceId: string }) => Promise - refreshMarketplaces: (args?: { - sourceId?: string - }) => Promise - listMarketplacePlugins: () => Promise - previewMarketplacePlugin: (args: { - marketplaceSourceId: string - pluginKey: string - }) => Promise - installMarketplacePlugin: ( - preview: Pick< - PluginMarketplaceHostInstallPreview, - 'marketplaceSourceId' | 'marketplaceCommit' | 'pluginKey' | 'resolvedCommit' - > - ) => Promise - previewMarketplaceUpdate: (args: { - pluginKey: string - }) => Promise - rollbackMarketplacePlugin: (args: { pluginKey: string }) => Promise - remove: (args: { pluginKey: string }) => Promise - getLogs: (args: { pluginKey: string }) => Promise - /** Re-discovers after settings edits (feature flag, dev paths). */ - refresh: () => Promise - /** Fires whenever installed plugins, worker states, panels, or content packs change. */ - onChanged: (callback: (event: PluginChangeEvent) => void) => () => void - } - agentStatus: { - /** Listen for agent status updates forwarded from native hook receivers. */ - onSet: (callback: (data: AgentStatusIpcPayload) => void) => () => void - /** Listen for main-process cleanup that evicted cached hook status. */ - onClear: (callback: (data: AgentStatusClearIpcPayload) => void) => () => void - /** Return the current main-process hook cache after renderer hydration. */ - getSnapshot: () => Promise - inferInterrupt: (request: AgentInterruptInferenceRequest) => Promise - /** Guarded clear for an answered AskUserQuestion wait — the CLI emits no hook at answer time, so the renderer reports the submit keystroke. */ - inferQuestionAnswered: (request: AgentQuestionAnsweredInferenceRequest) => Promise - /** Listen for PTYs on a legacy numeric pane key that have registry-backed UUID pane proof. */ - onMigrationUnsupported: (callback: (entry: MigrationUnsupportedPtyEntry) => void) => () => void - onMigrationUnsupportedClear: (callback: (data: { ptyId: string }) => void) => () => void - onLegacyWorkerTerminalRecovery: ( - callback: (data: { - paneKey: string - resolution: 'adopted' | 'exited' | 'rolled_back' - ptyId?: string - }) => void - ) => () => void - getMigrationUnsupportedSnapshot: () => Promise - /** Drop a paneKey from the main-process hook cache and on-disk last-status file. Fire-and-forget. */ - drop: (paneKey: string) => void - /** Drop every cached hook status under one terminal tab prefix. Fire-and-forget. */ - dropByTabPrefix: (tabId: string) => void - /** Permanently retire one pane's hook authority while siblings stay live. */ - retirePaneAuthority: (paneKey: string) => void - /** Move hook authority when a live pane is detached into another tab. */ - transferPaneAuthority: (args: { - fromPaneKey: string - toPaneKey: string - ptyId?: string - }) => void - } - mobile: { - listNetworkInterfaces: () => Promise<{ - interfaces: { name: string; address: string; hasDefaultRoute?: boolean }[] - }> - getPairingQR: (args?: { - address?: string - connectionMode?: MobilePairingConnectionMode - rotate?: boolean - }) => Promise< - | { - available: false - reason?: string - guidance?: string - relayFailure?: MobileRelayMintFailure - } - | { - available: true - qrDataUrl: string | null - qrError?: 'encoding_failed' - pairingUrl: string - /** Null when no direct address was advertised — the QR pairs over Relay alone. */ - endpoint: string | null - deviceId: string - /** Mode the QR actually encodes. */ - connectionMode: MobilePairingConnectionMode - } - > - getWindowsFirewallStatus: (args?: { address?: string }) => Promise< - | { supported: false } - | { - supported: true - port: number - ruleAllowed: boolean - blockingRuleDetected: boolean - privateFirewallEnabled: boolean - networkCategory: 'private' | 'public' | 'domain' | 'unknown' - inspectionAvailable: boolean - } - > - repairWindowsFirewall: () => Promise< - { ok: true } | { ok: false; reason: 'cancelled' | 'failed' | 'unsupported' } - > - openWindowsNetworkSettings: () => Promise - getRuntimePairingUrl: (args?: { - address?: string - rotate?: boolean - reach?: RuntimePairingReach - }) => Promise< - | { available: false; reason?: 'network_exposure_failed'; guidance?: string } - | { - available: true - pairingUrl: string - webClientUrl: string | null - endpoint: string - deviceId: string - } - > - listDevices: () => Promise<{ - devices: { deviceId: string; name: string; pairedAt: number; lastSeenAt: number }[] - }> - revokeDevice: (args: { deviceId: string }) => Promise<{ revoked: boolean }> - listRuntimeAccessGrants: () => Promise<{ grants: RuntimeAccessGrant[] }> - revokeRuntimeAccess: (args: { deviceId: string }) => Promise<{ revoked: boolean }> - isWebSocketReady: () => Promise<{ ready: boolean; endpoint: string | null }> - getRelayStatus: () => Promise<{ status: MobileRelayStatus }> - onRelayStatusChanged: (callback: (status: MobileRelayStatus) => void) => () => void - /** Consumes an auth-failure notification that arrived before the renderer listener mounted. */ - consumePendingUnpairedDeviceAuthFailure?: () => Promise - /** Fires (throttled, once per session) when an unpaired phone repeatedly fails direct-transport auth. */ - onUnpairedDeviceAuthFailure?: (callback: () => void) => () => void - } - speech: { - getCatalog: () => Promise - getModelStates: () => Promise - getOpenAiApiKeyStatus: () => Promise<{ configured: boolean }> - saveOpenAiApiKey: (apiKey: string) => Promise<{ configured: boolean }> - clearOpenAiApiKey: () => Promise<{ configured: boolean }> - downloadModel: (modelId: string) => Promise - cancelDownload: (modelId: string) => Promise - deleteModel: (modelId: string) => Promise - startDictation: ( - modelId: string, - hotwords: string[] | undefined, - sessionId: string - ) => Promise - feedAudio: (samples: Float32Array, sampleRate: number, sessionId?: string) => Promise - stopDictation: (sessionId?: string) => Promise - onPartialTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void - onFinalTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void - onDownloadProgress: ( - callback: (data: { modelId: string; progress: number }) => void - ) => () => void - onReady: (callback: (data: SpeechLifecycleEvent) => void) => () => void - onStopped: (callback: (data: SpeechLifecycleEvent) => void) => () => void - onError: (callback: (data: SpeechErrorEvent) => void) => () => void - } + fs: FilesystemApi['fs'] + git: Merged + ui: Merged + runtime: RuntimeApi['runtime'] + runtimeEnvironments: RuntimeApi['runtimeEnvironments'] + rateLimits: RateLimitsApi + minimaxCredentials: MinimaxCredentialsApi + grokAccounts: GrokAccountsApi + ssh: SshApi + automations: AutomationsApi + wsl: RuntimeApi['wsl'] + pwsh: RuntimeApi['pwsh'] + gitBash: RuntimeApi['gitBash'] + plugins: PluginsApi + agentStatus: AgentStatusApi + mobile: MobileApi + speech: SpeechApi } +export type { ClaudeUsageApi, CodexUsageApi, OpenCodeUsageApi } from './api/agent-usage-api' +export type { AiVaultApi } from './api/ai-vault-api' +export type { AppApi } from './api/app-api' +export type { BrowserApi, DetectedBrowserInfo, DetectedBrowserProfileInfo } from './api/browser-api' +export type { EmulatorApi } from './api/emulator-api' +export type { ExportApi } from './api/filesystem-api' +export type { + NativeChatApi, + NativeChatAppendedMessages, + NativeChatAppendedPayload, + NativeChatReadSessionResult, + NativeChatSubscribeArgs, + NativeChatSubscriptionFrame +} from './api/native-chat-api' +export type { + PluginHostInstallResult, + PluginHostInstallSource, + PluginHostListEntry, + PluginHostLogLine, + PluginHostPanel, + PluginHostStatus, + PluginMarketplaceHostInstallPreview, + PluginMarketplaceHostListing, + PluginMarketplaceHostSourceState +} from './api/plugin-host-api' +export type { + PreflightApi, + PreflightRuntimeContext, + PreflightStatus, + RefreshAgentsResult +} from './api/preflight-api' +export type { + PtyManagementApi, + PtyManagementMacTccAttributionHealth, + PtyManagementSession +} from './api/pty-management-api' +export type { + ShellOpenExternalEditorRequest, + ShellOpenExternalEditorResult, + ShellOpenLocalPathResult +} from './api/shell-api' +export type { + DiagnosticsBundlePayload, + DiagnosticsStatusPayload, + DiagnosticsUploadPayload, + MemoryApi, + StatsApi +} from './api/telemetry-api' + declare global { // oxlint-disable-next-line typescript-eslint/consistent-type-definitions -- declaration merging requires interface interface Window { diff --git a/src/preload/api/agent-account-api.ts b/src/preload/api/agent-account-api.ts new file mode 100644 index 00000000000..43e8542cddd --- /dev/null +++ b/src/preload/api/agent-account-api.ts @@ -0,0 +1,62 @@ +import type { ClaudeRateLimitAccountsState, CodexRateLimitAccountsState } from '../../shared/types' +import type { CodexConfigSyncStatus } from '../../shared/codex-config-sync-types' +import type { GrokAccountStatus } from '../../shared/rate-limit-types' + +export type CodexAccountsApi = { + list: () => Promise + add: (args?: { + runtime?: 'host' | 'wsl' + wslDistro?: string | null + }) => Promise + reauthenticate: (args: { accountId: string }) => Promise + remove: (args: { accountId: string }) => Promise + select: (args: { + accountId: string | null + runtime?: 'host' | 'wsl' + wslDistro?: string | null + }) => Promise + /** Live PTYs whose baked CODEX_HOME still points at a deselected account. */ + listStalePanes: (args: { ptyIds: string[] }) => Promise< + { + ptyId: string + launchAccountId: string | null + activeAccountId: string | null + /** Optional for compatibility with a pre-reason main process. */ + reason?: 'account-change' | 'home-route-change' + }[] + > + /** The selection lane each PTY launched from, keyed by pty id; unrecorded panes are absent. */ + listRecordedPaneLanes: (args: { ptyIds: string[] }) => Promise> + /** Drops launch records so a dismissed prompt stays dismissed across restarts. */ + forgetStalePanes: (args: { ptyIds: string[] }) => Promise +} + +export type ClaudeAccountsApi = { + list: () => Promise + add: (args?: { + runtime?: 'host' | 'wsl' + wslDistro?: string | null + }) => Promise + cancelPendingLogin: () => Promise + reauthenticate: (args: { accountId: string }) => Promise + remove: (args: { accountId: string }) => Promise + select: (args: { + accountId: string | null + runtime?: 'host' | 'wsl' + wslDistro?: string | null + }) => Promise +} + +export type GrokAccountsApi = { + getStatus: () => Promise +} + +export type MinimaxCredentialsApi = { + getStatus: () => Promise<{ configured: boolean }> + saveCookie: (cookie: string) => Promise<{ configured: boolean }> + clearCookie: () => Promise<{ configured: boolean }> +} + +export type CodexConfigSyncApi = { + status: () => Promise +} diff --git a/src/preload/api/agent-hook-api.ts b/src/preload/api/agent-hook-api.ts new file mode 100644 index 00000000000..df782419c5c --- /dev/null +++ b/src/preload/api/agent-hook-api.ts @@ -0,0 +1,51 @@ +import type { OrcaHooks, WorktreeSetupLaunch } from '../../shared/types' +import type { ExecutionHostId } from '../../shared/execution-host' +import type { SetupScriptImportCandidate } from '../../shared/setup-script-imports' +import type { AgentHookInstallStatus } from '../../shared/agent-hook-types' + +export type HooksApi = { + check: (args: { repoId: string; hostId?: ExecutionHostId }) => Promise<{ + status?: 'ok' | 'error' + hasHooks: boolean + hooks: OrcaHooks | null + mayNeedUpdate: boolean + }> + inspectSetupScriptImports: (args: { + repoId: string + hostId?: ExecutionHostId + }) => Promise + createIssueCommandRunner: (args: { + repoId: string + worktreePath: string + command: string + }) => Promise + readIssueCommand: (args: { repoId: string; hostId?: ExecutionHostId }) => Promise<{ + status?: 'ok' | 'error' + localContent: string | null + sharedContent: string | null + effectiveContent: string | null + localFilePath: string + source: 'local' | 'shared' | 'none' + }> + writeIssueCommand: (args: { + repoId: string + content: string + hostId?: ExecutionHostId + }) => Promise +} + +export type AgentHooksApi = { + claudeStatus: () => Promise + openClaudeStatus: () => Promise + codexStatus: () => Promise + geminiStatus: () => Promise + antigravityStatus: () => Promise + ampStatus: () => Promise + cursorStatus: () => Promise + droidStatus: () => Promise + commandCodeStatus: () => Promise + grokStatus: () => Promise + copilotStatus: () => Promise + hermesStatus: () => Promise + devinStatus: () => Promise +} diff --git a/src/preload/api/agent-skill-api.ts b/src/preload/api/agent-skill-api.ts new file mode 100644 index 00000000000..489d48c3d5d --- /dev/null +++ b/src/preload/api/agent-skill-api.ts @@ -0,0 +1,16 @@ +import type { SkillDiscoveryResult, SkillDiscoveryTarget } from '../../shared/skills' +import type { + SkillFreshnessInventory, + SkillUpdateRun, + SkillUpdateStartResult +} from '../../shared/skill-freshness' + +export type SkillsApi = { + discover: (target?: SkillDiscoveryTarget) => Promise + freshnessInventory: () => Promise + startUpdateRun: (names: string[]) => Promise + cancelUpdateRun: () => Promise + acknowledgeUpdateRun: () => Promise + getUpdateRun: () => Promise + onUpdateRun: (callback: (run: SkillUpdateRun) => void) => () => void +} diff --git a/src/preload/api/agent-status-api.ts b/src/preload/api/agent-status-api.ts new file mode 100644 index 00000000000..6e61114df47 --- /dev/null +++ b/src/preload/api/agent-status-api.ts @@ -0,0 +1,52 @@ +import type { + AgentStatusClearIpcPayload, + AgentStatusIpcPayload, + MigrationUnsupportedPtyEntry +} from '../../shared/agent-status-types' +import type { AgentInterruptInferenceRequest } from '../../shared/agent-interrupt-intent' +import type { AgentQuestionAnsweredInferenceRequest } from '../../shared/agent-question-answered-intent' +import type { ComputerAwakeStatus } from '../../shared/computer-awake-mode' + +export type AgentStatusApi = { + /** Listen for agent status updates forwarded from native hook receivers. */ + onSet: (callback: (data: AgentStatusIpcPayload) => void) => () => void + /** Listen for main-process cleanup that evicted cached hook status. */ + onClear: (callback: (data: AgentStatusClearIpcPayload) => void) => () => void + /** Return the current main-process hook cache after renderer hydration. */ + getSnapshot: () => Promise + inferInterrupt: (request: AgentInterruptInferenceRequest) => Promise + /** Guarded clear for an answered AskUserQuestion wait — the CLI emits no hook at answer time, so the renderer reports the submit keystroke. */ + inferQuestionAnswered: (request: AgentQuestionAnsweredInferenceRequest) => Promise + /** Listen for PTYs on a legacy numeric pane key that have registry-backed UUID pane proof. */ + onMigrationUnsupported: (callback: (entry: MigrationUnsupportedPtyEntry) => void) => () => void + onMigrationUnsupportedClear: (callback: (data: { ptyId: string }) => void) => () => void + onLegacyWorkerTerminalRecovery: ( + callback: (data: { + paneKey: string + resolution: 'adopted' | 'exited' | 'rolled_back' + ptyId?: string + }) => void + ) => () => void + getMigrationUnsupportedSnapshot: () => Promise + /** Drop a paneKey from the main-process hook cache and on-disk last-status file. Fire-and-forget. */ + drop: (paneKey: string) => void + /** Drop every cached hook status under one terminal tab prefix. Fire-and-forget. */ + dropByTabPrefix: (tabId: string) => void + /** Permanently retire one pane's hook authority while siblings stay live. */ + retirePaneAuthority: (paneKey: string) => void + /** Move hook authority when a live pane is detached into another tab. */ + transferPaneAuthority: (args: { fromPaneKey: string; toPaneKey: string; ptyId?: string }) => void +} + +export type AgentTrustApi = { + markTrusted: (args: { + preset: 'cursor' | 'copilot' | 'codex' + workspacePath: string + connectionId?: string + }) => Promise +} + +export type AgentAwakeApi = { + getStatus: () => Promise + onChanged: (callback: (status: ComputerAwakeStatus) => void) => () => void +} diff --git a/src/preload/api/agent-usage-api.ts b/src/preload/api/agent-usage-api.ts new file mode 100644 index 00000000000..59322ad9bad --- /dev/null +++ b/src/preload/api/agent-usage-api.ts @@ -0,0 +1,59 @@ +import type { ClaudeUsageBreakdownKind, ClaudeUsageSnapshot } from '../../shared/claude-usage-types' +import type { CodexUsageBreakdownKind, CodexUsageSnapshot } from '../../shared/codex-usage-types' +import type { + OpenCodeUsageBreakdownKind, + OpenCodeUsageSnapshot +} from '../../shared/opencode-usage-types' +import type { + CodexRateLimitResetResult, + RateLimitRuntimeTarget, + RateLimitState +} from '../../shared/rate-limit-types' + +export type UsageProviderSnapshot = { + scanState: unknown + summary: { scope: string; range: string } + daily: unknown[] + modelBreakdown: unknown[] + recentSessions: unknown[] +} + +export type UsageQueryArgs = Pick< + Snapshot['summary'], + 'scope' | 'range' +> + +export type UsageProviderApi = { + getScanState: () => Promise + setEnabled: (args: { enabled: boolean }) => Promise + refresh: (args?: { force?: boolean }) => Promise + getSnapshot: (args: UsageQueryArgs & { limit?: number }) => Promise + getSummary: (args: UsageQueryArgs) => Promise + getDaily: (args: UsageQueryArgs) => Promise + getBreakdown: ( + args: UsageQueryArgs & { kind: BreakdownKind } + ) => Promise + getRecentSessions: ( + args: UsageQueryArgs & { limit?: number } + ) => Promise +} + +export type ClaudeUsageApi = UsageProviderApi + +export type CodexUsageApi = UsageProviderApi + +export type OpenCodeUsageApi = UsageProviderApi + +export type RateLimitsApi = { + get: () => Promise + refresh: () => Promise + refreshCodexForTarget: (target: RateLimitRuntimeTarget) => Promise + consumeCodexResetCredit: () => Promise + refreshClaudeForTarget: (target: RateLimitRuntimeTarget) => Promise + setPollingInterval: (ms: number) => Promise + fetchInactiveClaudeAccounts: () => Promise + fetchInactiveCodexAccounts: () => Promise + refreshMiniMax: () => Promise + refreshGrok: () => Promise + onUpdate: (callback: (state: RateLimitState) => void) => () => void +} diff --git a/src/preload/api/ai-vault-api.ts b/src/preload/api/ai-vault-api.ts new file mode 100644 index 00000000000..10cf8a4d515 --- /dev/null +++ b/src/preload/api/ai-vault-api.ts @@ -0,0 +1,37 @@ +import type { + AiVaultDeleteSessionArgs, + AiVaultDeleteSessionResult +} from '../../shared/ai-vault-session-deletion' +import type { + AiVaultFirstUserPromptArgs, + AiVaultFirstUserPromptResult, + AiVaultListArgs, + AiVaultListResult, + AiVaultSubagentListArgs, + AiVaultSubagentListResult +} from '../../shared/ai-vault-types' +import type { + AiVaultSessionTitlesArgs, + AiVaultSessionTitlesResult +} from '../../shared/ai-vault-session-title' +import type { + AiVaultPrepareSessionResumeArgs, + AiVaultPrepareSessionResumeResult +} from '../../shared/ai-vault-resume-preparation' + +export type AiVaultApi = { + listSessions: (args?: AiVaultListArgs) => Promise + resolveSessionTitles: (args: AiVaultSessionTitlesArgs) => Promise + cancelListSessions: (args: { requestToken: string }) => Promise + prepareSessionResume: ( + args: AiVaultPrepareSessionResumeArgs + ) => Promise + /** Lists the Task subagent transcripts of one session, on demand. */ + listSubagentSessions: (args: AiVaultSubagentListArgs) => Promise + /** Full first user prompt for copy/reuse (re-parses one transcript). */ + getFirstUserPrompt: (args: AiVaultFirstUserPromptArgs) => Promise + /** Moves a deletable session's transcript to the OS trash; local sessions only. */ + deleteSession: (args: AiVaultDeleteSessionArgs) => Promise + /** Fires when any app window regains OS focus; returns an unsubscribe. */ + onWindowFocused: (callback: () => void) => () => void +} diff --git a/src/preload/api/app-api.ts b/src/preload/api/app-api.ts new file mode 100644 index 00000000000..a03960fd531 --- /dev/null +++ b/src/preload/api/app-api.ts @@ -0,0 +1,82 @@ +import type { AppIdentity } from '../../shared/app-identity' +import type { E2EConfig } from '../../shared/e2e-config' +import type { ExecutionHostId } from '../../shared/execution-host' +import type { + WriteTerminalRenderDesyncEvidenceArgs, + WriteTerminalRenderDesyncEvidenceResult +} from '../../shared/terminal-render-desync-evidence' +import type { + FloatingTerminalCwdRequest, + MarkdownDocument, + PersistedUIState, + WorkspaceSessionState +} from '../../shared/types' + +export type AppApi = { + /** Returns the app identity currently exposed to native chrome and the titlebar. */ + getIdentity: () => Promise + /** Returns a URL base for feature-wall assets. In dev this is Vite /@fs; + * in packaged builds this is file:// resources. Renderer appends filenames. */ + getFeatureWallAssetBaseUrl: () => Promise + /** Relaunches the app (app.relaunch() + app.exit(0)) for settings that need a full restart to apply. */ + relaunch: () => Promise + /** Restarts Orca through the normal quit pipeline so daemon-backed terminal + * sessions survive and can reattach after the new process starts. */ + restart: () => Promise + /** Reloads the current app renderer through main so expected renderer + * teardown can be classified before Electron emits process-gone events. */ + reload: () => Promise + /** Stages the renderer's final state synchronously before unload. */ + stageBeforeUnloadSync: (args: { + sessions: { state: WorkspaceSessionState; hostId?: ExecutionHostId }[] + ui: Partial + }) => void + /** Resolves once the last staged checkpoint is durably written; rejects if that + * write failed, so a reload/restart can abort instead of losing the snapshot. */ + awaitBeforeUnloadCheckpoint: () => Promise + /** Resolves when the daemon PTY provider and hook receiver have either + * started or failed open for the first BrowserWindow. */ + awaitFirstWindowStartupServices: () => Promise + /** Reconciles legacy worker authority around persisted terminal reconnect. */ + recoverLegacyWorkerTerminalsForRendererStartup: () => Promise + /** Emits a startup benchmark marker when ORCA_STARTUP_DIAGNOSTICS is enabled. */ + startupDiagnostic: (event: string, details?: Record) => Promise + /** macOS active input mode, or layout ID when no IME is selected (e.g. `com.apple.keylayout.PolishPro`). + * Distinguishes CJK IMEs and Option-layer-composing layouts that look like US QWERTY (issue #1205). + * Returns null on non-Darwin or when the defaults read fails. */ + getKeyboardInputSourceId: () => Promise + /** Updates the macOS Dock unread badge. No-op on Windows/Linux. */ + setUnreadDockBadgeCount: (count: number) => Promise + /** Resolves the launch directory for global Floating Terminal tabs. */ + getFloatingTerminalCwd: (args?: FloatingTerminalCwdRequest) => Promise + /** Resolves Orca's app-owned directory for auto-created Floating Workspace + * markdown notes. */ + getFloatingMarkdownDirectory: () => Promise + /** Opens a native picker for markdown documents, rooted in the floating + * workspace, and authorizes the selected file for editor reads/writes. */ + pickFloatingMarkdownDocument: () => Promise + /** Opens a native directory picker and authorizes the selected directory + * for Floating Workspace markdown file creation. */ + pickFloatingWorkspaceDirectory: () => Promise + /** Persists flag-gated terminal render evidence under app-owned userData. */ + writeTerminalRenderDesyncEvidence: ( + args: WriteTerminalRenderDesyncEvidenceArgs + ) => Promise +} + +/** Panel contribution as surfaced by the main-process plugin service. */ + +export type PlatformApi = { + get: () => { + platform: NodeJS.Platform + osRelease: string + arch: string + /** Login shell or ComSpec when available. */ + shell: string + displayServer: 'wayland' | 'x11' | null + } +} + +export type E2EApi = { + getConfig: () => E2EConfig +} diff --git a/src/preload/api/automation-api.ts b/src/preload/api/automation-api.ts new file mode 100644 index 00000000000..90f13e4ce1e --- /dev/null +++ b/src/preload/api/automation-api.ts @@ -0,0 +1,37 @@ +import type { + Automation, + AutomationCreateInput, + AutomationDispatchRequest, + AutomationDispatchResult, + ExternalAutomationCreateInput, + ExternalAutomationActionInput, + ExternalAutomationManager, + ExternalAutomationRunsInput, + ExternalAutomationRunsPage, + ExternalAutomationUpdateInput, + AutomationRun, + AutomationPrecheckResult, + AutomationUpdateInput +} from '../../shared/automations-types' + +export type AutomationsApi = { + list: () => Promise + listRuns: (args?: { automationId?: string }) => Promise + listExternalManagers: () => Promise + listExternalRuns: (input: ExternalAutomationRunsInput) => Promise + createExternal: (input: ExternalAutomationCreateInput) => Promise + updateExternal: (input: ExternalAutomationUpdateInput) => Promise + runExternalAction: (input: ExternalAutomationActionInput) => Promise + create: (input: AutomationCreateInput) => Promise + update: (args: { id: string; updates: AutomationUpdateInput }) => Promise + delete: (args: { id: string }) => Promise + runNow: (args: { id: string }) => Promise + runPrecheck: (args: { + automationId: string + runId: string + }) => Promise + markDispatchResult: (result: AutomationDispatchResult) => Promise + snapshotWorkspaceName: (args: { workspaceId: string; displayName: string }) => Promise + rendererReady: () => Promise + onDispatchRequested: (callback: (request: AutomationDispatchRequest) => void) => () => void +} diff --git a/src/preload/api/browser-api.ts b/src/preload/api/browser-api.ts new file mode 100644 index 00000000000..b6591162d19 --- /dev/null +++ b/src/preload/api/browser-api.ts @@ -0,0 +1,132 @@ +import type { BrowserSetAnnotationViewportBridgeArgs } from '../../shared/browser-annotation-viewport-bridge' +import type { + BrowserSetGrabModeArgs, + BrowserSetGrabModeResult, + BrowserAwaitGrabSelectionArgs, + BrowserGrabResult, + BrowserCancelGrabArgs, + BrowserCaptureSelectionScreenshotArgs, + BrowserCaptureSelectionScreenshotResult, + BrowserExtractHoverArgs, + BrowserExtractHoverResult +} from '../../shared/browser-grab-types' +import type { + BrowserContextMenuDismissedEvent, + BrowserContextMenuRequestedEvent, + BrowserDownloadFinishedEvent, + BrowserDownloadProgressEvent, + BrowserDownloadRequestedEvent, + BrowserPermissionDeniedEvent, + BrowserPopupEvent +} from '../../shared/browser-guest-events' +import type { + BrowserCookieImportResult, + BrowserCertificateFailure, + BrowserCertificateProceedResult, + BrowserLoadError, + BrowserSessionProfile, + BrowserSessionProfileCreateOptions, + BrowserSessionProfileScope, + BrowserSessionProfileSource, + BrowserViewportOverride +} from '../../shared/types' + +export type BrowserApi = { + registerGuest: (args: { + browserPageId: string + workspaceId: string + worktreeId: string + sessionProfileId?: string | null + webContentsId: number + }) => Promise + isGuestRegistered: (args: { browserPageId: string; webContentsId: number }) => Promise + repairGuestRegistration: (args: { + browserPageId: string + workspaceId: string + worktreeId: string + sessionProfileId?: string | null + webContentsId: number + }) => Promise + unregisterGuest: (args: { browserPageId: string }) => Promise + openDevTools: (args: { browserPageId: string }) => Promise + setViewportOverride: (args: { + browserPageId: string + override: BrowserViewportOverride | null + }) => Promise + setAnnotationViewportBridge: (args: BrowserSetAnnotationViewportBridgeArgs) => Promise + onGuestLoadFailed: ( + callback: (args: { browserPageId: string; loadError: BrowserLoadError }) => void + ) => () => void + onCertificateFailureChanged: ( + callback: (event: { browserPageId: string; failure: BrowserCertificateFailure | null }) => void + ) => () => void + proceedCertificate: (args: { + browserPageId: string + challengeId: string + }) => Promise + onPermissionDenied: (callback: (event: BrowserPermissionDeniedEvent) => void) => () => void + onPopup: (callback: (event: BrowserPopupEvent) => void) => () => void + onDownloadRequested: (callback: (event: BrowserDownloadRequestedEvent) => void) => () => void + onDownloadProgress: (callback: (event: BrowserDownloadProgressEvent) => void) => () => void + onDownloadFinished: (callback: (event: BrowserDownloadFinishedEvent) => void) => () => void + onContextMenuRequested: ( + callback: (event: BrowserContextMenuRequestedEvent) => void + ) => () => void + onContextMenuDismissed: ( + callback: (event: BrowserContextMenuDismissedEvent) => void + ) => () => void + onNavigationUpdate: ( + callback: (event: { browserPageId: string; url: string; title: string }) => void + ) => () => void + onActivateView: ( + callback: (data: { worktreeId?: string; browserPageId?: string }) => void + ) => () => void + onPaneFocus: ( + callback: (data: { worktreeId: string | null; browserPageId: string }) => void + ) => () => void + onOpenLinkInOrcaTab: ( + callback: (event: { browserPageId: string; url: string }) => void + ) => () => void + cancelDownload: (args: { downloadId: string }) => Promise + setGrabMode: (args: BrowserSetGrabModeArgs) => Promise + awaitGrabSelection: (args: BrowserAwaitGrabSelectionArgs) => Promise + cancelGrab: (args: BrowserCancelGrabArgs) => Promise + captureSelectionScreenshot: ( + args: BrowserCaptureSelectionScreenshotArgs + ) => Promise + extractHoverPayload: (args: BrowserExtractHoverArgs) => Promise + onGrabModeToggle: (callback: (browserPageId: string) => void) => () => void + onGrabActionShortcut: ( + callback: (args: { browserPageId: string; key: 'c' | 's' }) => void + ) => () => void + sessionListProfiles: () => Promise + sessionCreateProfile: ( + args: { + scope: BrowserSessionProfileScope + label: string + } & BrowserSessionProfileCreateOptions + ) => Promise + sessionDeleteProfile: (args: { profileId: string }) => Promise + sessionImportCookies: (args: { profileId: string }) => Promise + sessionResolvePartition: (args: { profileId: string | null }) => Promise + sessionDetectBrowsers: () => Promise + sessionImportFromBrowser: (args: { + profileId: string + browserFamily: string + browserProfile?: string + }) => Promise + sessionClearDefaultCookies: () => Promise + notifyActiveTabChanged: (args: { browserPageId: string }) => Promise +} + +export type DetectedBrowserProfileInfo = { + name: string + directory: string +} + +export type DetectedBrowserInfo = { + family: BrowserSessionProfileSource['browserFamily'] + label: string + profiles: DetectedBrowserProfileInfo[] + selectedProfile: string +} diff --git a/src/preload/api/cli-install-api.ts b/src/preload/api/cli-install-api.ts new file mode 100644 index 00000000000..cadf7f5cfb7 --- /dev/null +++ b/src/preload/api/cli-install-api.ts @@ -0,0 +1,10 @@ +import type { CliInstallStatus } from '../../shared/cli-install-types' + +export type CliApi = { + getInstallStatus: () => Promise + install: () => Promise + remove: () => Promise + getWslInstallStatus: (args?: { distro?: string | null }) => Promise + installWsl: (args?: { distro?: string | null }) => Promise + removeWsl: (args?: { distro?: string | null }) => Promise +} diff --git a/src/preload/api/crash-report-api.ts b/src/preload/api/crash-report-api.ts new file mode 100644 index 00000000000..c99e1ccf767 --- /dev/null +++ b/src/preload/api/crash-report-api.ts @@ -0,0 +1,38 @@ +import type { + CrashReportBreadcrumbData, + CrashReportCopyDiagnosticsArgs, + CrashReportRecord, + CrashReportSubmitArgs, + CrashReportSubmitResult, + ReactErrorBoundaryReportArgs, + ReactErrorBoundaryReportResult +} from '../../shared/crash-reporting' +import type { RendererHeapStatistics } from '../../shared/renderer-heap-statistics' + +export type CrashReportsApi = { + getLatestPending: () => Promise + getLatestReport: () => Promise + dismiss: (args: { reportId: string }) => Promise + recordRendererError: ( + args: ReactErrorBoundaryReportArgs + ) => Promise + recordBreadcrumb: (args: { name: string; data?: CrashReportBreadcrumbData }) => void + submit: (args: CrashReportSubmitArgs) => Promise + copyLatestDiagnostics: ( + args?: CrashReportCopyDiagnosticsArgs + ) => Promise<{ ok: true } | { ok: false; error: string }> + /** Exact V8/Blink heap sizes; null when the runtime withholds them. */ + readHeapStatistics: () => RendererHeapStatistics | null +} + +export type FeedbackApi = { + submit: (args: { + feedback: string + submitAnonymously?: boolean + githubLogin: string | null + githubEmail: string | null + images?: { contentType: string; data: Uint8Array }[] + }) => Promise< + { ok: true; imagesDelivered?: boolean } | { ok: false; status: number | null; error: string } + > +} diff --git a/src/preload/api/dashboard-api.ts b/src/preload/api/dashboard-api.ts new file mode 100644 index 00000000000..aa8d6dabd8a --- /dev/null +++ b/src/preload/api/dashboard-api.ts @@ -0,0 +1,42 @@ +import type { + DashboardRevealAgentArgs, + DashboardSleepWorkspaceArgs, + DashboardSnapshot, + DashboardSpawnAgentArgs +} from '../../shared/dashboard-snapshot' +import type { + TerminalPreviewConnectResult, + TerminalPreviewDataPayload +} from '../../shared/terminal-preview' + +export type DashboardApi = { + openPopout: (view?: 'board' | 'map') => Promise + publishSnapshot: (snapshot: DashboardSnapshot) => Promise + getPopoutOpen: () => Promise + onPopoutOpenChanged: (callback: (open: boolean) => void) => () => void + onSnapshotRequested: (callback: () => void) => () => void + onRevealAgent: (callback: (args: DashboardRevealAgentArgs) => void) => () => void + onAckAgent: (callback: (paneKey: string) => void) => () => void + onSpawnAgent: (callback: (args: DashboardSpawnAgentArgs) => void) => () => void + onSleepWorkspace: (callback: (args: DashboardSleepWorkspaceArgs) => void) => () => void + requestSnapshot: () => Promise + onSnapshot: (callback: (snapshot: DashboardSnapshot) => void) => () => void + onViewRequested: (callback: (view: 'board' | 'map') => void) => () => void + revealAgent: (args: DashboardRevealAgentArgs) => Promise + ackAgent: (paneKey: string) => Promise + spawnAgent: (args: DashboardSpawnAgentArgs) => Promise + sleepWorkspace: (args: DashboardSleepWorkspaceArgs) => Promise +} + +export type TerminalPreviewApi = { + connect: ( + ptyId: string, + opts?: { scrollbackRows?: number } + ) => Promise + input: (ptyId: string, data: string) => Promise + /** Claim the PTY grid for the preview dialog; resolves to the size actually in effect. */ + fit: (ptyId: string, cols: number, rows: number) => Promise<{ cols: number; rows: number } | null> + ack: (ptyId: string, bytes: number) => Promise + unsubscribe: (ptyId: string) => Promise + onData: (callback: (payload: TerminalPreviewDataPayload) => void) => () => void +} diff --git a/src/preload/api/emulator-api.ts b/src/preload/api/emulator-api.ts new file mode 100644 index 00000000000..5781045bc67 --- /dev/null +++ b/src/preload/api/emulator-api.ts @@ -0,0 +1,37 @@ +export type EmulatorApi = { + onPaneFocus: (callback: (data: { worktreeId: string }) => void) => () => void + onAutoAttach: ( + callback: (data: { + worktreeId: string + info: { deviceUdid: string; streamUrl: string; wsUrl: string; axUrl?: string } + }) => void + ) => () => void + startFrameStream: (args: { streamUrl: string; streamKey?: string }) => Promise<{ + streamId: string + }> + stopFrameStream: (args: { streamId: string }) => Promise + onFrameStreamFrame: ( + callback: (data: { streamId: string; bytes: ArrayBuffer }) => void + ) => () => void + onFrameStreamError: ( + callback: (data: { streamId: string; message: string }) => void + ) => () => void + startVideoStream: (args: { deviceId: string; streamId: string }) => Promise<{ streamId: string }> + stopVideoStream: (args: { streamId: string }) => Promise + onVideoStreamMeta: ( + callback: (data: { + streamId: string + deviceId: string + meta: { codecId: string; width: number; height: number } + }) => void + ) => () => void + onVideoStreamFrame: ( + callback: (data: { + streamId: string + deviceId: string + config: boolean + keyFrame: boolean + bytes: ArrayBuffer + }) => void + ) => () => void +} diff --git a/src/preload/api/ephemeral-vm-api.ts b/src/preload/api/ephemeral-vm-api.ts new file mode 100644 index 00000000000..e3b6ea202fd --- /dev/null +++ b/src/preload/api/ephemeral-vm-api.ts @@ -0,0 +1,72 @@ +import type { OrcaHooks } from '../../shared/types' +import type { PublicKnownRuntimeEnvironment } from '../../shared/runtime-environments' +import type { EphemeralVmRecipeDoctorResult } from '../../shared/ephemeral-vm-recipes' +import type { EphemeralVmRecipeResultWarning } from '../../shared/ephemeral-vm-recipe-diagnostics' +import type { EphemeralVmRuntimeRecord } from '../../shared/ephemeral-vm-runtimes' + +export type EphemeralVmApi = { + listRecipes: (args: { repoId: string }) => Promise<{ + status: 'ok' | 'error' + repoPath: string | null + recipes: OrcaHooks['environmentRecipes'] + diagnostics: NonNullable + message?: string + }> + listRecipeCatalog: () => Promise< + { + repoId: string + repoName: string + repoPath: string + recipes: NonNullable + diagnostics: NonNullable + }[] + > + doctor: (args: { repoId: string; recipeId: string }) => Promise + provision: (args: { + repoId: string + recipeId: string + workspaceName?: string + projectId?: string + workspaceId?: string + branch?: string + ref?: string + provisionId?: string + }) => Promise< + | { + ok: true + connectionType: 'orca-server' + runtime: EphemeralVmRuntimeRecord + environment: PublicKnownRuntimeEnvironment + stderr: string + warnings: EphemeralVmRecipeResultWarning[] + } + | { + ok: true + connectionType: 'ssh' + runtime: EphemeralVmRuntimeRecord + sshTargetId: string + stderr: string + warnings: EphemeralVmRecipeResultWarning[] + } + | { ok: false; error: string; stderr: string; stdout: string } + > + cancelProvision: (args: { provisionId: string }) => Promise<{ cancelled: boolean }> + onProvisionEvent: ( + callback: (event: { provisionId: string; stream: 'stdout' | 'stderr'; chunk: string }) => void + ) => () => void + listRuntimes: () => Promise + attachWorkspace: (args: { + runtimeId: string + workspaceId: string + }) => Promise + suspendWorkspace: (args: { workspaceId: string }) => Promise + resumeWorkspace: (args: { workspaceId: string }) => Promise + cleanup: (args: { runtimeId: string }) => Promise + getCleanupCommand: (args: { runtimeId: string }) => Promise<{ + runtimeId: string + command: string | null + payloadJson: string + cleanupDisabled: boolean + message?: string + }> +} diff --git a/src/preload/api/filesystem-api.ts b/src/preload/api/filesystem-api.ts new file mode 100644 index 00000000000..fdc1ac73250 --- /dev/null +++ b/src/preload/api/filesystem-api.ts @@ -0,0 +1,208 @@ +import type { + DirEntry, + FsChangedPayload, + MarkdownDocument, + SearchOptions, + SearchResult +} from '../../shared/types' +import type { + LocalLogTailChangedPayload, + LocalLogTailReadArgs, + LocalLogTailReadResult, + LocalLogTailWatchArgs +} from '../../shared/local-log-tail-types' +import type { SshMutationExpectation } from '../../shared/ssh-types' + +export type ExportApi = { + htmlToPdf: (args: { + html: string + title: string + }) => Promise< + { success: true; filePath: string } | { success: false; cancelled?: boolean; error?: string } + > +} + +export type FilesystemApi = { + fs: { + readDir: (args: { dirPath: string; connectionId?: string }) => Promise + readFile: (args: { + filePath: string + connectionId?: string + includeLocalLogMetadata?: boolean + }) => Promise<{ + content: string + isBinary: boolean + isImage?: boolean + mimeType?: string + fileIdentity?: string + }> + readLocalLogTail: (args: LocalLogTailReadArgs) => Promise + startLocalLogTail: (args: LocalLogTailWatchArgs) => Promise + stopLocalLogTail: (args: { subscriptionId: string }) => Promise + onLocalLogTailChanged: (callback: (payload: LocalLogTailChangedPayload) => void) => () => void + downloadFile: (args: { + filePath: string + connectionId: string + }) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }> + downloadFolder: (args: { + dirPath: string + connectionId: string + }) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }> + saveDownloadedFile: (args: { + suggestedName: string + content: string + encoding: 'utf8' | 'base64' + }) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }> + startDownloadedFile: (args: { + suggestedName: string + }) => Promise< + { canceled: true } | { canceled: false; transferId: string; destinationPath: string } + > + appendDownloadedFileChunk: (args: { + transferId: string + contentBase64: string + }) => Promise<{ ok: true }> + finishDownloadedFile: (args: { + transferId: string + }) => Promise<{ canceled: false; destinationPath: string }> + cancelDownloadedFile: (args: { transferId: string }) => Promise<{ ok: true }> + listMarkdownDocuments: (args: { + rootPath: string + connectionId?: string + }) => Promise + writeFile: ( + args: { + filePath: string + content: string + connectionId?: string + } & SshMutationExpectation + ) => Promise + createFile: ( + args: { + filePath: string + connectionId?: string + } & SshMutationExpectation + ) => Promise + createDir: ( + args: { dirPath: string; connectionId?: string } & SshMutationExpectation + ) => Promise + rename: ( + args: { + oldPath: string + newPath: string + connectionId?: string + } & SshMutationExpectation + ) => Promise + copy: ( + args: { + sourcePath: string + destinationPath: string + connectionId?: string + } & SshMutationExpectation + ) => Promise + deletePath: ( + args: { + targetPath: string + connectionId?: string + recursive?: boolean + } & SshMutationExpectation + ) => Promise + authorizeExternalPath: (args: { targetPath: string }) => Promise + stat: (args: { + filePath: string + connectionId?: string + }) => Promise<{ size: number; isDirectory: boolean; mtime: number }> + pathExists: (args: { filePath: string; connectionId?: string }) => Promise + listFiles: (args: { + rootPath: string + connectionId?: string + excludePaths?: string[] + requestToken?: string + }) => Promise + cancelListFiles: (args: { requestToken: string }) => Promise + search: (args: SearchOptions & { connectionId?: string }) => Promise + importExternalPaths: ( + args: { + sourcePaths: string[] + destDir: string + connectionId?: string + ensureDir?: boolean + } & SshMutationExpectation + ) => Promise<{ + results: ( + | { + sourcePath: string + status: 'imported' + destPath: string + kind: 'file' | 'directory' + renamed: boolean + } + | { + sourcePath: string + status: 'skipped' + reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported' + } + | { + sourcePath: string + status: 'failed' + reason: string + } + )[] + }> + stageExternalPathsForRuntimeUpload: (args: { sourcePaths: string[] }) => Promise<{ + sources: ( + | { + sourcePath: string + status: 'staged' + name: string + kind: 'file' | 'directory' + entries: ( + | { relativePath: string; kind: 'directory' } + | { relativePath: string; kind: 'file'; contentBase64: string } + )[] + } + | { + sourcePath: string + status: 'skipped' + reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported' + } + | { + sourcePath: string + status: 'failed' + reason: string + } + )[] + }> + resolveDroppedPathsForAgent: ( + args: { + paths: string[] + worktreePath: string + connectionId?: string + } & SshMutationExpectation + ) => Promise<{ + resolvedPaths: string[] + skipped: { + sourcePath: string + reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported' + }[] + failed: { sourcePath: string; reason: string }[] + }> + watchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise + unwatchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise + onFsChanged: (callback: (payload: FsChangedPayload) => void) => () => void + } + notebook: { + runPythonCell: (args: { + filePath: string + code: string + preamble?: string + connectionId?: string | null + }) => Promise<{ + stdout: string + stderr: string + exitCode: number | null + error?: string + }> + } + export: ExportApi +} diff --git a/src/preload/api/git-inspection-api.ts b/src/preload/api/git-inspection-api.ts new file mode 100644 index 00000000000..c23bcdeecaa --- /dev/null +++ b/src/preload/api/git-inspection-api.ts @@ -0,0 +1,123 @@ +import type { + GitBranchCompareResult, + GitCommitCompareResult, + GitConflictOperation, + GitDiffResult, + GitPushTarget, + GitStagingArea, + GitStatusResult, + GitUpstreamStatus +} from '../../shared/types' +import type { GitHistoryOptions, GitHistoryResult } from '../../shared/git-history' +import type { + CommitMessageAgentCapability, + CommitMessageModelCapability +} from '../../shared/commit-message-agent-spec' + +export type GitInspectionApi = { + status: (args: { + worktreePath: string + connectionId?: string + includeIgnored?: boolean + bypassEffectiveUpstreamNegativeCache?: boolean + reuseLineStats?: boolean + /** Merge-base OID to measure the branch line total against; omit to skip the work. */ + branchLineTotalMergeBase?: string + requestToken?: string + }) => Promise + cancelStatus: (args: { requestToken: string }) => Promise + setStatusUpstreamRefWatch: (args: { + worktreeId: string + worktreePath: string + executionHostId: string + connectionId?: string + branch?: string + upstreamName?: string + }) => Promise + submoduleStatus: (args: { + worktreePath: string + submodulePath: string + connectionId?: string + area?: GitStagingArea + }) => Promise + checkIgnored: (args: { + worktreePath: string + paths: string[] + connectionId?: string + }) => Promise + findHugeFoldersToIgnore: (args: { worktreePath: string }) => Promise + history: ( + args: { worktreePath: string; connectionId?: string } & GitHistoryOptions + ) => Promise + conflictOperation: (args: { + worktreePath: string + connectionId?: string + }) => Promise + diff: (args: { + worktreePath: string + filePath: string + staged: boolean + compareAgainstHead?: boolean + connectionId?: string + }) => Promise + branchCompare: (args: { + worktreePath: string + baseRef: string + connectionId?: string + }) => Promise + commitCompare: (args: { + worktreePath: string + commitId: string + connectionId?: string + }) => Promise + upstreamStatus: (args: { + worktreePath: string + connectionId?: string + pushTarget?: GitPushTarget + }) => Promise + branchDiff: (args: { + worktreePath: string + compare: { + baseRef: string + baseOid: string + headOid: string + mergeBase: string + } + filePath: string + oldPath?: string + connectionId?: string + }) => Promise + commitDiff: (args: { + worktreePath: string + commitOid: string + parentOid?: string | null + filePath: string + oldPath?: string + connectionId?: string + }) => Promise + discoverCommitMessageModels: (args: { + agentId: string + worktreePath?: string + connectionId?: string + }) => Promise< + | { + success: true + capability: CommitMessageAgentCapability + models: CommitMessageModelCapability[] + defaultModelId: string + catalogOrigin: 'probe' | 'spec' + } + | { success: false; error: string } + > + remoteFileUrl: (args: { + worktreePath: string + relativePath: string + line: number + connectionId?: string + }) => Promise + remoteCommitUrl: (args: { + worktreePath: string + sha: string + connectionId?: string + }) => Promise +} diff --git a/src/preload/api/git-operation-api.ts b/src/preload/api/git-operation-api.ts new file mode 100644 index 00000000000..fee33db4efd --- /dev/null +++ b/src/preload/api/git-operation-api.ts @@ -0,0 +1,128 @@ +import type { + GitForkSyncExpectedUpstream, + GitForkSyncResult, + GitPushTarget, + TuiAgent +} from '../../shared/types' +import type { HostedReviewProvider } from '../../shared/hosted-review' +import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control-ai' +import type { SourceControlAiSettings } from '../../shared/source-control-ai-types' + +export type GitOperationApi = { + appendGitignore: (args: { worktreePath: string; folderName: string }) => Promise + abortMerge: (args: { worktreePath: string; connectionId?: string }) => Promise + abortRebase: (args: { worktreePath: string; connectionId?: string }) => Promise + fetch: (args: { + worktreePath: string + connectionId?: string + pushTarget?: GitPushTarget + }) => Promise + syncFork: (args: { + worktreePath: string + connectionId?: string + expectedUpstream: GitForkSyncExpectedUpstream + }) => Promise + push: (args: { + worktreePath: string + publish?: boolean + forceWithLease?: boolean + connectionId?: string + pushTarget?: GitPushTarget + }) => Promise + pull: (args: { + worktreePath: string + connectionId?: string + pushTarget?: GitPushTarget + }) => Promise + fastForward: (args: { + worktreePath: string + connectionId?: string + pushTarget?: GitPushTarget + }) => Promise + rebaseFromBase: (args: { + worktreePath: string + baseRef: string + connectionId?: string + }) => Promise + commit: (args: { + worktreePath: string + message: string + connectionId?: string + }) => Promise<{ success: boolean; error?: string }> + generateCommitMessage: (args: { + worktreePath: string + /** Raw (unstripped) worktree meta key; validated against worktreePath in main. */ + worktreeId?: string + repoId?: string + connectionId?: string + sourceControlAiResolvedParams?: ResolvedSourceControlAiGenerationParams + sourceControlAi?: SourceControlAiSettings + agentCmdOverrides?: Partial> + }) => Promise< + | { success: true; message: string; agentLabel?: string } + | { success: false; error: string; canceled?: boolean } + > + cancelGenerateCommitMessage: (args: { + worktreePath: string + connectionId?: string + }) => Promise + generatePullRequestFields: (args: { + worktreePath: string + /** Raw (unstripped) worktree meta key; validated against worktreePath in main. */ + worktreeId?: string + repoId?: string + base: string + title: string + body: string + draft: boolean + provider?: HostedReviewProvider + useTemplate?: boolean + connectionId?: string + sourceControlAiResolvedParams?: ResolvedSourceControlAiGenerationParams + sourceControlAi?: SourceControlAiSettings + agentCmdOverrides?: Partial> + }) => Promise< + | { + success: true + fields: { base: string; title: string; body: string; draft: boolean } + agentLabel?: string + branchChangedByPreparation?: boolean + } + | { + success: false + error: string + canceled?: boolean + branchChangedByPreparation?: boolean + } + > + cancelGeneratePullRequestFields: (args: { + worktreePath: string + connectionId?: string + }) => Promise + stage: (args: { worktreePath: string; filePath: string; connectionId?: string }) => Promise + bulkStage: (args: { + worktreePath: string + filePaths: string[] + connectionId?: string + }) => Promise + unstage: (args: { + worktreePath: string + filePath: string + connectionId?: string + }) => Promise + bulkUnstage: (args: { + worktreePath: string + filePaths: string[] + connectionId?: string + }) => Promise + discard: (args: { + worktreePath: string + filePath: string + connectionId?: string + }) => Promise + bulkDiscard: (args: { + worktreePath: string + filePaths: string[] + connectionId?: string + }) => Promise +} diff --git a/src/preload/api/github-pull-request-api.ts b/src/preload/api/github-pull-request-api.ts new file mode 100644 index 00000000000..245453cb18f --- /dev/null +++ b/src/preload/api/github-pull-request-api.ts @@ -0,0 +1,196 @@ +import type { AppStarSource } from '../../shared/gh-star-source' +import type { GhAuthDiagnostic } from '../../shared/github/auth-types' +import type { TaskSourceContext } from '../../shared/task-source-context' +import type { + GetRateLimitResult, + GitHubCommentResult, + GitHubOwnerRepo, + GitHubPRFile, + GitHubPRFileContents, + GitHubPRRefreshCandidate, + GitHubPRRefreshEnqueueResult, + GitHubPRRefreshEvent, + GitHubPRRefreshReason, + GitHubPRReviewCommentInput, + GitHubReactionContent, + GitHubViewer, + PRCheckDetail, + PRCheckRunDetails, + PRComment, + PRInfo, + PRRefreshOutcome +} from '../../shared/types' +import type { GitHubRepoSelectorArgs } from './github-work-item-api' + +export type GithubPullRequestApi = { + viewer: () => Promise + repoSlug: (args: { + repoPath: string + repoId?: string + }) => Promise<{ owner: string; repo: string; host?: string } | null> + repoUpstream: (args: { + repoPath: string + repoId?: string + }) => Promise<{ owner: string; repo: string; host?: string } | null> + prForBranch: (args: { + repoPath: string + repoId?: string + branch: string + linkedPRNumber?: number | null + fallbackPRNumber?: number | null + acceptMergedFallbackPR?: boolean + currentHeadOid?: string | null + }) => Promise + refreshPRNow: (args: { candidate: GitHubPRRefreshCandidate }) => Promise + enqueuePRRefresh: (args: { + candidate: GitHubPRRefreshCandidate + reason: GitHubPRRefreshReason + priority?: number + }) => Promise + reportVisiblePRRefreshCandidates: (args: { + candidates: GitHubPRRefreshCandidate[] + generation: number + }) => Promise + onPRRefreshEvent: (callback: (event: GitHubPRRefreshEvent) => void) => () => void + prFileContents: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + prRepo?: GitHubOwnerRepo | null + path: string + oldPath?: string + status: GitHubPRFile['status'] + headSha: string + baseSha: string + } + ) => Promise + prChecks: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + headSha?: string + prRepo?: GitHubOwnerRepo | null + noCache?: boolean + } + ) => Promise + prCheckDetails: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + checkRunId?: number + workflowRunId?: number + checkName?: string + url?: string | null + prRepo?: GitHubOwnerRepo | null + }) => Promise + rerunPRChecks: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + headSha?: string + failedOnly?: boolean + prRepo?: GitHubOwnerRepo | null + } + ) => Promise<{ ok: true; count: number } | { ok: false; error: string }> + prComments: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + prNumber: number + prRepo?: GitHubOwnerRepo | null + noCache?: boolean + }) => Promise + setPRCommentReaction: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + reactionSubjectId: string + content: GitHubReactionContent + reacted: boolean + prRepo?: GitHubOwnerRepo | null + }) => Promise + resolveReviewThread: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + threadId: string + resolve: boolean + prRepo?: GitHubOwnerRepo | null + }) => Promise + setPRFileViewed: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + prRepo?: GitHubOwnerRepo | null + pullRequestId: string + path: string + viewed: boolean + } + ) => Promise + updatePRTitle: (args: { + repoPath: string + repoId?: string + prNumber: number + title: string + prRepo?: GitHubOwnerRepo | null + }) => Promise + mergePR: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + method?: 'merge' | 'squash' | 'rebase' + prRepo?: GitHubOwnerRepo | null + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + setPRAutoMerge: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + enabled: boolean + method?: 'merge' | 'squash' | 'rebase' + prRepo?: GitHubOwnerRepo | null + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + updatePRState: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + updates: { state: 'open' | 'closed' } + prRepo?: GitHubOwnerRepo | null + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + requestPRReviewers: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + reviewers: string[] + prRepo?: GitHubOwnerRepo | null + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + removePRReviewers: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + reviewers: string[] + prRepo?: GitHubOwnerRepo | null + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + addPRReviewCommentReply: ( + args: GitHubRepoSelectorArgs & { + prNumber: number + commentId: number + body: string + threadId?: string + path?: string + line?: number + prRepo?: GitHubOwnerRepo | null + } + ) => Promise + addPRReviewComment: ( + args: GitHubPRReviewCommentInput & { + repoId?: string + sourceContext?: TaskSourceContext | null + } + ) => Promise + checkOrcaStarred: () => Promise + starOrca: (source: AppStarSource) => Promise + /** + * GitHub API rate-limit snapshot. Does NOT consume quota (the + * `rate_limit` endpoint is exempt). Cached 30s server-side — pass + * `force: true` to bust after a known-expensive op. + */ + rateLimit: (args?: { force?: boolean }) => Promise + /** Explains scope_missing ProjectV2 failures — notably a shell `GITHUB_TOKEN` shadowing the keyring credential, where `gh auth refresh` is a no-op. */ + diagnoseAuth: (args?: { host?: string }) => Promise +} diff --git a/src/preload/api/github-work-item-api.ts b/src/preload/api/github-work-item-api.ts new file mode 100644 index 00000000000..e302e4517a8 --- /dev/null +++ b/src/preload/api/github-work-item-api.ts @@ -0,0 +1,167 @@ +import type { + AddIssueCommentBySlugArgs, + ClearProjectItemFieldArgs, + DeleteIssueCommentBySlugArgs, + GetProjectViewTableArgs, + GetProjectViewTableResult, + GitHubProjectCommentMutationResult, + GitHubProjectMutationResult, + ListAccessibleProjectsArgs, + ListAccessibleProjectsResult, + ListAssignableUsersBySlugArgs, + ListAssignableUsersBySlugResult, + ListIssueTypesBySlugArgs, + ListIssueTypesBySlugResult, + ListLabelsBySlugArgs, + ListLabelsBySlugResult, + ListProjectViewsArgs, + ListProjectViewsResult, + ProjectWorkItemDetailsBySlugArgs, + ProjectWorkItemDetailsBySlugResult, + ResolveProjectRefArgs, + ResolveProjectRefResult, + UpdateIssueBySlugArgs, + UpdateIssueCommentBySlugArgs, + UpdateIssueTypeBySlugArgs, + UpdateProjectItemFieldArgs, + UpdatePullRequestBySlugArgs +} from '../../shared/github/project-types' +import type { TaskSourceContext } from '../../shared/task-source-context' +import type { + GitHubAssignableUser, + GitHubCommentResult, + GitHubCreateIssueResult, + GitHubIssueUpdate, + GitHubOwnerRepo, + GitHubWorkItem, + GitHubWorkItemDetails, + IssueInfo, + ListWorkItemsResult +} from '../../shared/types' + +export type GitHubRepoSelectorArgs = { + repoPath: string + repoId?: string | null + sourceContext?: TaskSourceContext | null +} + +export type GithubWorkItemApi = { + issue: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + number: number + }) => Promise + workItem: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + number: number + type?: 'issue' | 'pr' + }) => Promise | null> + workItemByOwnerRepo: (args: { + repoPath: string + repoId?: string + owner: string + repo: string + host?: string + number: number + type: 'issue' | 'pr' + }) => Promise | null> + workItemDetails: ( + args: GitHubRepoSelectorArgs & { + number: number + type?: 'issue' | 'pr' + } + ) => Promise + notifyWorkItemMutated: (args: { + repoPath: string + repoId?: string + type: 'issue' | 'pr' + number: number + }) => Promise + listIssues: (args: { repoPath: string; repoId?: string; limit?: number }) => Promise + createIssue: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + title: string + body: string + labels?: string[] + assignees?: string[] + }) => Promise + countWorkItems: (args: { repoPath: string; repoId?: string; query?: string }) => Promise + listWorkItems: (args: { + repoPath: string + repoId?: string + limit?: number + query?: string + page?: number + noCache?: boolean + }) => Promise>> + updateIssue: ( + args: GitHubRepoSelectorArgs & { + number: number + updates: GitHubIssueUpdate + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + addIssueComment: ( + args: GitHubRepoSelectorArgs & { + number: number + body: string + /** Why: scopes the cross-window cache invalidation so a PR and issue sharing the same number don't evict each other. */ + type?: 'issue' | 'pr' + prRepo?: GitHubOwnerRepo | null + } + ) => Promise + listLabels: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + }) => Promise + listAssignableUsers: (args: { + repoPath: string + repoId?: string + sourceContext?: TaskSourceContext | null + }) => Promise + /** Subscribe to local-mutation broadcasts so the work-item-drawer cache can invalidate across windows. Returns an unsubscribe. */ + onWorkItemMutated: ( + callback: (payload: { + repoPath: string + repoId?: string + type: 'issue' | 'pr' + number: number + }) => void + ) => () => void + // ── ProjectV2 (GitHub Projects) ───────────────────────────────── + listAccessibleProjects: ( + args?: ListAccessibleProjectsArgs + ) => Promise + resolveProjectRef: (args: ResolveProjectRefArgs) => Promise + listProjectViews: (args: ListProjectViewsArgs) => Promise + getProjectViewTable: (args: GetProjectViewTableArgs) => Promise + projectWorkItemDetailsBySlug: ( + args: ProjectWorkItemDetailsBySlugArgs + ) => Promise + updateProjectItemField: (args: UpdateProjectItemFieldArgs) => Promise + clearProjectItemField: (args: ClearProjectItemFieldArgs) => Promise + updateIssueBySlug: (args: UpdateIssueBySlugArgs) => Promise + updatePullRequestBySlug: ( + args: UpdatePullRequestBySlugArgs + ) => Promise + addIssueCommentBySlug: ( + args: AddIssueCommentBySlugArgs + ) => Promise + updateIssueCommentBySlug: ( + args: UpdateIssueCommentBySlugArgs + ) => Promise + deleteIssueCommentBySlug: ( + args: DeleteIssueCommentBySlugArgs + ) => Promise + listLabelsBySlug: (args: ListLabelsBySlugArgs) => Promise + listAssignableUsersBySlug: ( + args: ListAssignableUsersBySlugArgs + ) => Promise + listIssueTypesBySlug: (args: ListIssueTypesBySlugArgs) => Promise + updateIssueTypeBySlug: (args: UpdateIssueTypeBySlugArgs) => Promise +} diff --git a/src/preload/api/gitlab-api.ts b/src/preload/api/gitlab-api.ts new file mode 100644 index 00000000000..b1467a891f4 --- /dev/null +++ b/src/preload/api/gitlab-api.ts @@ -0,0 +1,172 @@ +import type { TaskSourceContext } from '../../shared/task-source-context' +import type { + ClassifiedError, + GetGitLabRateLimitResult, + GitLabAssignableUser, + GitLabAuthDiagnostic, + GitLabCommentResult, + GitLabDiscussionResolveResult, + GitLabIssueInfo, + GitLabIssueUpdate, + GitLabJobTraceResult, + GitLabMRInlineCommentInput, + GitLabMRReviewersUpdateResult, + GitLabMRUpdate, + GitLabProjectRef, + GitLabRetryJobResult, + GitLabTodo, + GitLabViewer, + GitLabWorkItem, + GitLabWorkItemDetails, + ListMergeRequestsResult, + MRInfo, + MRListState +} from '../../shared/types' + +export type GitLabRepoSelectorArgs = { + repoPath: string + repoId?: string | null + sourceContext?: TaskSourceContext | null +} + +// ── GitLab — parallel to gh, MR/issue surface only in v1 ──────── +// Shapes mirror gh.* except where GitLab's API differs (MR states, host-qualified project path, `glab api -i` paging). +export type GitLabApi = { + viewer: () => Promise + diagnoseAuth: () => Promise + rateLimit: (args?: { force?: boolean; host?: string | null }) => Promise + projectSlug: (args: GitLabRepoSelectorArgs) => Promise + mrForBranch: ( + args: GitLabRepoSelectorArgs & { + branch: string + linkedMRIid?: number | null + } + ) => Promise + mr: (args: GitLabRepoSelectorArgs & { iid: number }) => Promise + listMRs: ( + args: GitLabRepoSelectorArgs & { + state?: MRListState + page?: number + perPage?: number + query?: string + } + ) => Promise + /** Combined MR + issue list filtered by state. Issues are skipped + * when state is 'merged' (issues don't merge). */ + listWorkItems: ( + args: GitLabRepoSelectorArgs & { + state?: MRListState + page?: number + perPage?: number + query?: string + } + ) => Promise + issue: (args: GitLabRepoSelectorArgs & { number: number }) => Promise + listIssues: ( + args: GitLabRepoSelectorArgs & { + state?: 'opened' | 'closed' | 'all' + assignee?: string + limit?: number + } + ) => Promise<{ items: GitLabWorkItem[]; error?: ClassifiedError }> + createIssue: ( + args: GitLabRepoSelectorArgs & { + title: string + body: string + } + ) => Promise<{ ok: true; number: number; url: string } | { ok: false; error: string }> + updateIssue: ( + args: GitLabRepoSelectorArgs & { + number: number + updates: GitLabIssueUpdate + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + addIssueComment: ( + args: GitLabRepoSelectorArgs & { + number: number + body: string + } + ) => Promise + listLabels: (args: GitLabRepoSelectorArgs) => Promise + listAssignableUsers: (args: GitLabRepoSelectorArgs) => Promise + /** Cross-project user-scoped todos (gitlab.com/dashboard/todos). */ + todos: (args: GitLabRepoSelectorArgs) => Promise + /** Aggregated dialog payload — body + discussions + pipeline jobs. */ + workItemDetails: ( + args: GitLabRepoSelectorArgs & { + iid: number + type: 'issue' | 'mr' + } + ) => Promise + closeMR: ( + args: GitLabRepoSelectorArgs & { + iid: number + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + reopenMR: ( + args: GitLabRepoSelectorArgs & { + iid: number + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + mergeMR: ( + args: GitLabRepoSelectorArgs & { + iid: number + method?: 'merge' | 'squash' | 'rebase' + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + updateMR: ( + args: GitLabRepoSelectorArgs & { + iid: number + updates: GitLabMRUpdate + } + ) => Promise<{ ok: true } | { ok: false; error: string }> + updateMRReviewers: ( + args: GitLabRepoSelectorArgs & { + iid: number + reviewerIds: number[] + projectRef?: GitLabProjectRef | null + } + ) => Promise + addMRComment: ( + args: GitLabRepoSelectorArgs & { + iid: number + body: string + } + ) => Promise + addMRInlineComment: ( + args: GitLabRepoSelectorArgs & { + iid: number + input: GitLabMRInlineCommentInput + projectRef?: GitLabProjectRef | null + } + ) => Promise + resolveMRDiscussion: ( + args: GitLabRepoSelectorArgs & { + iid: number + discussionId: string + resolved: boolean + } + ) => Promise + jobTrace: ( + args: GitLabRepoSelectorArgs & { + jobId: number + projectRef?: GitLabProjectRef | null + /** Bound the trace in main to a readable excerpt (see gitLabJobTraceToLogExcerpt). */ + logExcerpt?: boolean + } + ) => Promise + retryJob: ( + args: GitLabRepoSelectorArgs & { + jobId: number + projectRef?: GitLabProjectRef | null + } + ) => Promise + workItemByPath: ( + args: GitLabRepoSelectorArgs & { + host: string + path: string + iid: number + type: 'issue' | 'mr' + } + ) => Promise | null> +} diff --git a/src/preload/api/hosted-review-api.ts b/src/preload/api/hosted-review-api.ts new file mode 100644 index 00000000000..7abe2d81f61 --- /dev/null +++ b/src/preload/api/hosted-review-api.ts @@ -0,0 +1,31 @@ +import type { + BitbucketConnectArgs, + BitbucketConnectionStatus +} from '../../shared/bitbucket-credentials' +import type { + CreateHostedReviewArgs, + CreateHostedReviewResult, + CreateStackedHostedReviewArgs, + CreateStackedHostedReviewResult, + HostedReviewCreationEligibility, + HostedReviewCreationEligibilityArgs, + HostedReviewForBranchArgs, + HostedReviewInfo +} from '../../shared/hosted-review' + +export type HostedReviewApi = { + forBranch: (args: HostedReviewForBranchArgs) => Promise + getCreationEligibility: ( + args: HostedReviewCreationEligibilityArgs + ) => Promise + create: (args: CreateHostedReviewArgs) => Promise + createStacked: (args: CreateStackedHostedReviewArgs) => Promise +} + +export type BitbucketApi = { + connect: ( + args: BitbucketConnectArgs + ) => Promise<{ ok: true; account: string | null } | { ok: false; error: string }> + disconnect: () => Promise + status: () => Promise +} diff --git a/src/preload/api/jira-api.ts b/src/preload/api/jira-api.ts new file mode 100644 index 00000000000..d452bd9728d --- /dev/null +++ b/src/preload/api/jira-api.ts @@ -0,0 +1,84 @@ +import type { + JiraComment, + JiraConnectionStatus, + JiraCreateField, + JiraCreateIssueArgs, + JiraIssue, + JiraIssueFilter, + JiraIssueType, + JiraIssueUpdate, + JiraPriority, + JiraProject, + JiraProjectStatusOrder, + JiraSiteSelection, + JiraTransition, + JiraUser, + JiraViewer +} from '../../shared/types' + +export type JiraApi = { + connect: (args: { + siteUrl: string + email: string + apiToken: string + authType?: 'cloud' | 'server' + }) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }> + disconnect: (args?: { siteId?: string }) => Promise + selectSite: (args: { siteId: JiraSiteSelection }) => Promise + status: () => Promise + readStatus: () => Promise + testConnection: (args?: { + siteId?: string + }) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }> + searchIssues: (args: { + jql: string + limit?: number + siteId?: JiraSiteSelection + requestId?: string + }) => Promise + cancelSearchIssues: (args: { requestId: string }) => Promise + listIssues: (args?: { + filter?: JiraIssueFilter + limit?: number + siteId?: JiraSiteSelection + }) => Promise + getIssue: (args: { key: string; siteId?: string }) => Promise + lookupIssueSummary: (args: { + key: string + siteId: string + requestId?: string + }) => Promise + cancelIssueSummary: (args: { requestId: string }) => Promise + createIssue: ( + args: JiraCreateIssueArgs + ) => Promise<{ ok: true; id: string; key: string; url: string } | { ok: false; error: string }> + updateIssue: (args: { + key: string + updates: JiraIssueUpdate + siteId?: string + }) => Promise<{ ok: true } | { ok: false; error: string }> + addIssueComment: (args: { + key: string + body: string + siteId?: string + }) => Promise<{ ok: true; id: string } | { ok: false; error: string }> + issueComments: (args: { key: string; siteId?: string }) => Promise + listProjects: (args?: { siteId?: JiraSiteSelection }) => Promise + listIssueTypes: (args: { projectIdOrKey: string; siteId?: string }) => Promise + listCreateFields: (args: { + projectIdOrKey: string + issueTypeId: string + siteId?: string + }) => Promise + listPriorities: (args?: { siteId?: string }) => Promise + listAssignableUsers: (args: { + key: string + query?: string + siteId?: string + }) => Promise + listTransitions: (args: { key: string; siteId?: string }) => Promise + getProjectStatusOrder: (args: { + projectKey: string + siteId?: string + }) => Promise +} diff --git a/src/preload/api/linear-api.ts b/src/preload/api/linear-api.ts new file mode 100644 index 00000000000..613837c4fa4 --- /dev/null +++ b/src/preload/api/linear-api.ts @@ -0,0 +1,128 @@ +import type { LinearIssueAttributeFilter } from '../../shared/linear/issue-attribute-filter' +import type { + LinearCollectionResult, + LinearComment, + LinearConnectionStatus, + LinearCustomViewModel, + LinearCustomViewSummary, + LinearIssue, + LinearIssueUpdate, + LinearLabel, + LinearMember, + LinearProjectDetail, + LinearProjectSummary, + LinearTeam, + LinearViewer, + LinearWorkflowState, + LinearWorkspaceSelection +} from '../../shared/types' + +export type LinearApi = { + connect: (args: { + apiKey: string + }) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }> + disconnect: (args?: { workspaceId?: string }) => Promise + selectWorkspace: (args: { + workspaceId: LinearWorkspaceSelection + }) => Promise + status: () => Promise + testConnection: (args?: { + workspaceId?: string + }) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }> + searchIssues: (args: { + query: string + limit?: number + workspaceId?: LinearWorkspaceSelection + }) => Promise + listIssues: (args?: { + filter?: 'assigned' | 'created' | 'all' | 'completed' + limit?: number + workspaceId?: LinearWorkspaceSelection + attributeFilter?: LinearIssueAttributeFilter + }) => Promise> + createIssue: (args: { + teamId: string + title: string + description?: string + workspaceId?: string + parentIssueId?: string + projectId?: string | null + stateId?: string + priority?: number + assigneeId?: string | null + labelIds?: string[] + }) => Promise< + | { ok: true; id: string; identifier: string; title: string; url: string } + | { ok: false; error: string } + > + getIssue: (args: { id: string; workspaceId?: string }) => Promise + updateIssue: (args: { + id: string + updates: LinearIssueUpdate + workspaceId?: string + }) => Promise<{ ok: true } | { ok: false; error: string }> + addIssueComment: (args: { + issueId: string + body: string + workspaceId?: string + }) => Promise<{ ok: true; id: string } | { ok: false; error: string }> + issueComments: (args: { issueId: string; workspaceId?: string }) => Promise + listTeams: (args?: { workspaceId?: LinearWorkspaceSelection }) => Promise + listProjects: (args?: { + query?: string + limit?: number + workspaceId?: LinearWorkspaceSelection + force?: boolean + }) => Promise> + createProject: (args: { + name: string + description?: string + content?: string + teamIds: string[] + workspaceId?: string + leadId?: string | null + memberIds?: string[] + labelIds?: string[] + priority?: number + startDate?: string + targetDate?: string + }) => Promise<{ ok: true; project: LinearProjectDetail } | { ok: false; error: string }> + getProject: (args: { + id: string + workspaceId: string + force?: boolean + }) => Promise + listProjectIssues: (args: { + projectId: string + limit?: number + workspaceId: string + force?: boolean + }) => Promise> + listCustomViews: (args: { + model: LinearCustomViewModel + limit?: number + workspaceId?: LinearWorkspaceSelection + force?: boolean + }) => Promise> + getCustomView: (args: { + viewId: string + model: LinearCustomViewModel + workspaceId: string + force?: boolean + }) => Promise + listCustomViewIssues: (args: { + viewId: string + limit?: number + workspaceId: string + force?: boolean + }) => Promise> + listCustomViewProjects: (args: { + viewId: string + limit?: number + workspaceId: string + force?: boolean + }) => Promise> + teamStates: (args: { teamId: string; workspaceId?: string }) => Promise + teamLabels: (args: { teamId: string; workspaceId?: string }) => Promise + teamMembers: (args: { teamId: string; workspaceId?: string }) => Promise +} diff --git a/src/preload/api/mobile-api.ts b/src/preload/api/mobile-api.ts new file mode 100644 index 00000000000..02e82f34044 --- /dev/null +++ b/src/preload/api/mobile-api.ts @@ -0,0 +1,86 @@ +import type { MobileRelayStatus } from '../../shared/mobile-relay-status' +import type { MobilePairingConnectionMode } from '../../shared/mobile-pairing-connection-mode' +import type { RuntimePairingReach } from '../../shared/runtime-pairing-reach' +import type { MobileRelayMintFailure } from '../../shared/mobile-relay-mint-failure' +import type { RuntimeAccessGrant } from '../../shared/runtime-access-grants' + +export type MobileApi = { + listNetworkInterfaces: () => Promise<{ + interfaces: { name: string; address: string; hasDefaultRoute?: boolean }[] + }> + getPairingQR: (args?: { + address?: string + connectionMode?: MobilePairingConnectionMode + rotate?: boolean + }) => Promise< + | { + available: false + reason?: string + guidance?: string + relayFailure?: MobileRelayMintFailure + } + | { + available: true + qrDataUrl: string | null + qrError?: 'encoding_failed' + pairingUrl: string + /** Null when no direct address was advertised — the QR pairs over Relay alone. */ + endpoint: string | null + deviceId: string + /** Mode the QR actually encodes. */ + connectionMode: MobilePairingConnectionMode + } + > + getWindowsFirewallStatus: (args?: { address?: string }) => Promise< + | { supported: false } + | { + supported: true + port: number + ruleAllowed: boolean + blockingRuleDetected: boolean + privateFirewallEnabled: boolean + networkCategory: 'private' | 'public' | 'domain' | 'unknown' + inspectionAvailable: boolean + } + > + repairWindowsFirewall: () => Promise< + { ok: true } | { ok: false; reason: 'cancelled' | 'failed' | 'unsupported' } + > + openWindowsNetworkSettings: () => Promise + getRuntimePairingUrl: (args?: { + address?: string + rotate?: boolean + reach?: RuntimePairingReach + }) => Promise< + | { + available: false + reason?: 'network_exposure_failed' + guidance?: string + } + | { + available: true + pairingUrl: string + webClientUrl: string | null + endpoint: string + deviceId: string + } + > + listDevices: () => Promise<{ + devices: { + deviceId: string + name: string + pairedAt: number + lastSeenAt: number + }[] + }> + revokeDevice: (args: { deviceId: string }) => Promise<{ revoked: boolean }> + listRuntimeAccessGrants: () => Promise<{ grants: RuntimeAccessGrant[] }> + revokeRuntimeAccess: (args: { deviceId: string }) => Promise<{ revoked: boolean }> + isWebSocketReady: () => Promise<{ ready: boolean; endpoint: string | null }> + getRelayStatus: () => Promise<{ status: MobileRelayStatus }> + onRelayStatusChanged: (callback: (status: MobileRelayStatus) => void) => () => void + /** Consumes an auth-failure notification that arrived before the renderer listener mounted. */ + consumePendingUnpairedDeviceAuthFailure?: () => Promise + /** Fires (throttled, once per session) when an unpaired phone repeatedly fails direct-transport auth. */ + onUnpairedDeviceAuthFailure?: (callback: () => void) => () => void +} diff --git a/src/preload/api/native-chat-api.ts b/src/preload/api/native-chat-api.ts new file mode 100644 index 00000000000..01eac44bde6 --- /dev/null +++ b/src/preload/api/native-chat-api.ts @@ -0,0 +1,71 @@ +import type { + AgentType, + NativeChatMessage, + NativeChatTurnLifecycle +} from '../../shared/native-chat-types' + +// notFound marks a not-yet-on-disk miss (retry-worthy) vs a real read/parse error (#8401). +export type NativeChatReadSessionResult = + | { + messages: NativeChatMessage[] + lifecycle?: NativeChatTurnLifecycle + } + | { error: string; notFound?: true } + +/** Messages appended to a live-tailed transcript since the previous emit. */ +export type NativeChatAppendedMessages = NativeChatMessage[] + +export type NativeChatSubscriptionFrame = + | { + type: 'snapshot' + messages: NativeChatMessage[] + hasMore: boolean + error?: string + lifecycle?: NativeChatTurnLifecycle + } + | { + type: 'replacement' + messages: NativeChatMessage[] + hasMore: boolean + lifecycle?: NativeChatTurnLifecycle + } + | { + type: 'appended' + messages: NativeChatMessage[] + lifecycle?: NativeChatTurnLifecycle + } + +/** Wire payload for the `nativeChat:appended` push channel. */ +export type NativeChatAppendedPayload = { + subscriptionId: string + frame: NativeChatSubscriptionFrame +} + +export type NativeChatSubscribeArgs = { + /** Unique per-caller id, echoed on every append so multiple live panes in + * one renderer don't cross-talk. */ + subscriptionId: string + agent: AgentType + sessionId: string + /** Authoritative transcript path from the agent hook (providerSession). */ + transcriptPath?: string + /** First snapshot size; later readSession calls grow this for pagination. */ + limit?: number +} + +export type NativeChatApi = { + /** Read the on-disk transcript for an agent + session id, windowed to the most recent `limit` + * turns. `transcriptPath` is the hook-reported authoritative path, preferred over the id glob. */ + readSession: ( + agent: AgentType, + sessionId: string, + limit?: number, + transcriptPath?: string + ) => Promise + /** Live-tail a transcript. The first frame is a bounded race-safe snapshot; + * later frames contain only newly appended messages. */ + subscribe: ( + args: NativeChatSubscribeArgs, + onFrame: (frame: NativeChatSubscriptionFrame) => void + ) => () => void +} diff --git a/src/preload/api/onboarding-api.ts b/src/preload/api/onboarding-api.ts new file mode 100644 index 00000000000..678a6b38294 --- /dev/null +++ b/src/preload/api/onboarding-api.ts @@ -0,0 +1,28 @@ +import type { OnboardingState } from '../../shared/types' + +export type OnboardingApi = { + get: () => Promise + // Why: main merges the checklist field-by-field, so a partial checklist is fine. + update: ( + updates: Partial> & { + checklist?: Partial + } + ) => Promise +} + +export type StarNagApi = { + onShow: ( + callback: (payload?: { mode?: 'gh' | 'web'; surface?: 'card' | 'toast' }) => void + ) => () => void + onHide: (callback: () => void) => () => void + dismiss: () => Promise + later: () => Promise + complete: () => Promise + disable: () => Promise + openWeb: () => Promise + starOrca: () => Promise + forceShow: () => Promise + agentValueMoment: () => Promise<{ status: 'ready'; mode: 'gh' | 'web' } | { status: 'skipped' }> + showAgentValueMoment: () => Promise + onboardingCompleted: () => Promise +} diff --git a/src/preload/api/orca-profile-api.ts b/src/preload/api/orca-profile-api.ts new file mode 100644 index 00000000000..16c2a575078 --- /dev/null +++ b/src/preload/api/orca-profile-api.ts @@ -0,0 +1,59 @@ +import type { + ConnectCurrentOrcaProfileResult, + CreateCloudLinkedOrcaProfileArgs, + CreateCloudLinkedOrcaProfileResult, + CreateLocalOrcaProfileArgs, + CreateLocalOrcaProfileResult, + FindOrcaProfileProjectsByPathArgs, + FindOrcaProfileProjectsByPathResult, + OrcaProfileAuthStatus, + OrcaProfileListResult, + OrcaProfileOrgInviteRevokeArgs, + OrcaProfileOrgMemberChangeRoleArgs, + OrcaProfileOrgMemberInviteArgs, + OrcaProfileOrgMemberMutationResult, + OrcaProfileOrgMemberRemoveArgs, + OrcaProfileOrgMembersListArgs, + OrcaProfileOrgMembersListResult, + RefreshCurrentOrcaProfileAuthResult, + SelectOrcaProfileOrgArgs, + SelectOrcaProfileOrgResult, + SignOutCurrentOrcaProfileResult, + SwitchOrcaProfileArgs, + SwitchOrcaProfileResult, + TransferOrcaProfileProjectArgs, + TransferOrcaProfileProjectResult +} from '../../shared/orca-profiles' + +export type OrcaProfileApi = { + list: () => Promise + authStatus: () => Promise + createLocal: (args?: CreateLocalOrcaProfileArgs) => Promise + createCloudLinked: ( + args?: CreateCloudLinkedOrcaProfileArgs + ) => Promise + switchProfile: (args: SwitchOrcaProfileArgs) => Promise + transferProject: ( + args: TransferOrcaProfileProjectArgs + ) => Promise + findProjectProfiles: ( + args: FindOrcaProfileProjectsByPathArgs + ) => Promise + connectCurrent: () => Promise + refreshAuth: () => Promise + signOutCurrent: () => Promise + selectOrg: (args: SelectOrcaProfileOrgArgs) => Promise + orgMembersList: (args: OrcaProfileOrgMembersListArgs) => Promise + orgMemberInvite: ( + args: OrcaProfileOrgMemberInviteArgs + ) => Promise + orgInviteRevoke: ( + args: OrcaProfileOrgInviteRevokeArgs + ) => Promise + orgMemberChangeRole: ( + args: OrcaProfileOrgMemberChangeRoleArgs + ) => Promise + orgMemberRemove: ( + args: OrcaProfileOrgMemberRemoveArgs + ) => Promise +} diff --git a/src/preload/api/os-permission-api.ts b/src/preload/api/os-permission-api.ts new file mode 100644 index 00000000000..fc452df1196 --- /dev/null +++ b/src/preload/api/os-permission-api.ts @@ -0,0 +1,54 @@ +import type { + ComputerUsePermissionId, + ComputerUsePermissionResetResult, + ComputerUsePermissionSetupResult, + ComputerUsePermissionStatusResult +} from '../../shared/computer-use-permissions-types' +import type { + DeveloperPermissionId, + DeveloperPermissionRequestResult, + DeveloperPermissionState, + LocalNetworkConnectionTestResult +} from '../../shared/developer-permissions-types' +import type { + NotificationDispatchRequest, + NotificationDispatchResult, + NotificationDeliveryProbeResult, + NotificationDismissResult, + NotificationPermissionStatusResult, + NotificationSoundResult +} from '../../shared/types' + +export type NotificationsApi = { + dispatch: (args: NotificationDispatchRequest) => Promise + dismiss: (ids: string[]) => Promise + openSystemSettings: () => Promise + getPermissionStatus: () => Promise + probeDelivery: (args?: { force?: boolean }) => Promise + playSound: (options?: { force?: boolean; volume?: number }) => Promise +} + +export type MacosTccPromptsApi = { + /** Fires once macOS has raised its Nth consent dialog naming Orca (#9756). */ + onThreshold: (callback: (payload: { promptCount: number }) => void) => () => void + consumePending: () => Promise<{ claimId: number; promptCount: number } | null> + acknowledgePending: (claimId: number) => Promise + releasePending: (claimId: number) => Promise + dismiss: () => Promise +} + +export type DeveloperPermissionsApi = { + getStatus: () => Promise + request: (args: { id: DeveloperPermissionId }) => Promise + openSettings: (args: { id: DeveloperPermissionId }) => Promise + testLocalNetworkConnection: (args: { + host: string + port: number + }) => Promise +} + +export type ComputerUsePermissionsApi = { + getStatus: () => Promise + openSetup: (args?: { id?: ComputerUsePermissionId }) => Promise + reset: () => Promise +} diff --git a/src/preload/api/pet-api.ts b/src/preload/api/pet-api.ts new file mode 100644 index 00000000000..e0031cb8e36 --- /dev/null +++ b/src/preload/api/pet-api.ts @@ -0,0 +1,8 @@ +import type { CustomPet } from '../../shared/types' + +export type PetApi = { + import: () => Promise + importPetBundle: () => Promise + read: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise + delete: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise +} diff --git a/src/preload/api/plugin-host-api.ts b/src/preload/api/plugin-host-api.ts new file mode 100644 index 00000000000..49222ae7a0b --- /dev/null +++ b/src/preload/api/plugin-host-api.ts @@ -0,0 +1,191 @@ +import type { + PluginPanelActionOutcome, + PluginPanelEntry +} from '../../shared/plugins/plugin-panel-bridge' +import type { PluginConsentRequest } from '../../shared/plugins/plugin-consent-request' +import type { PluginLanguagePackRegistration } from '../../shared/plugins/plugin-language-pack-artifact' +import type { PluginChangeEvent } from '../../shared/plugins/plugin-change-event' +import type { PluginManifest } from '../../shared/plugins/plugin-manifest' +import type { PluginMarketplaceGitSource } from '../../shared/plugins/plugin-marketplace' + +/** Panel contribution as surfaced by the main-process plugin service. */ +export type PluginHostPanel = { + id: string + title: string + /** Lucide icon name declared in the plugin manifest. */ + icon?: string + tabKey: `plugin:${string}` +} + +/** `pending` = awaiting (re-)consent; `idle` = enabled, worker not running + * (lazy); `restarting` = waiting for supervised backoff; `errored` = crashed past the restart budget or failed to start; + * `invalid` = unreadable manifest. */ +export type PluginHostStatus = + | 'running' + | 'restarting' + | 'idle' + | 'pending' + | 'disabled' + | 'errored' + | 'invalid' + +/** Wire shape of plugins:list — must stay assignable from the main-process + * projection in src/main/plugins/plugin-list-projection.ts. */ +export type PluginHostListEntry = { + pluginKey: string + consentFingerprint: string | null + name: string + version: string + publisher: string + description?: string + status: PluginHostStatus + needsReconsent: boolean + error?: string + isDev: boolean + official: boolean + bundled: boolean + capabilities: { kind: string; description: string }[] + panels: PluginHostPanel[] + commands: { + id: string + title: string + context: 'global' | 'worktree' + handler: { type: 'built-in'; action: string } | { type: 'worker' } + keybindings: { key: string; when: 'global' | 'worktree' }[] + }[] + hasWorker: boolean + vmRecipes?: { + id: string + name: string + description?: string + commands: { + phase: 'create' | 'suspend' | 'resume' | 'destroy' + command: string + }[] + }[] + restarts: number + blockedByKillList?: { reason: string; advisoryUrl?: string } + source?: { + kind: 'local-path' | 'git' | 'marketplace' | 'bundled' + reference: string + resolvedCommit: string | null + contentHash: string + marketplace?: { reference: string; resolvedCommit: string } + } +} + +export type PluginHostLogLine = { + ts: number + level: 'info' | 'warn' | 'error' + line: string +} + +export type PluginHostInstallSource = + | { kind: 'local-path'; path: string } + | { kind: 'git'; url: string; ref: string } + +export type PluginHostInstallResult = + | { + ok: true + pluginKey: string + version: string + contentHash: string + consentFingerprint: string + resolvedCommit: string | null + } + | { ok: false; error: string } + +export type PluginMarketplaceHostSourceState = { + id: string + source: PluginMarketplaceGitSource + addedAt: number + marketplace: { + name: string + owner: string + resolvedCommit: string + fetchedAt: number + } | null + stale: boolean + official: boolean + error?: string +} + +export type PluginMarketplaceHostListing = { + marketplaceSourceId: string + marketplaceName: string + marketplaceOwner: string + marketplaceCommit: string + pluginKey: string + source: PluginMarketplaceGitSource + description?: string + categories: string[] + official: boolean + bundled: boolean + blockedByKillList?: { reason: string; advisoryUrl?: string } +} + +export type PluginMarketplaceHostInstallPreview = { + marketplaceSourceId: string + marketplaceName: string + marketplaceOwner: string + marketplaceCommit: string + pluginKey: string + source: PluginMarketplaceGitSource + resolvedCommit: string + contentHash: string + consentFingerprint: string + manifest: PluginManifest + official: boolean + bundled: boolean + blockedByKillList?: { reason: string; advisoryUrl?: string } +} + +export type PluginsApi = { + list: () => Promise + listLanguagePacks: () => Promise + /** Records the consent-dialog answer; approval is keyed to the plugin's + * current capability and trusted-worker fingerprint. */ + consent: (args: PluginConsentRequest) => Promise + setEnabled: (args: { pluginKey: string; enabled: boolean }) => Promise + /** Returns the panel's CSP-wrapped HTML, or null when the plugin or + * panel is missing/disabled. Rendered only inside a sandboxed iframe. */ + readPanelEntry: (args: { pluginKey: string; panelId: string }) => Promise + invokeCommand: (args: { + pluginKey: string + commandId: string + args?: unknown + }) => Promise + /** Relays a sandboxed panel's bridge request to main, which enforces the + * plugin's consented capabilities before executing. */ + panelAction: (args: { + sessionToken: string + action: string + params?: unknown + }) => Promise + install: (source: PluginHostInstallSource) => Promise + listMarketplaces: () => Promise + addMarketplace: (source: PluginMarketplaceGitSource) => Promise + removeMarketplace: (args: { sourceId: string }) => Promise + refreshMarketplaces: (args?: { sourceId?: string }) => Promise + listMarketplacePlugins: () => Promise + previewMarketplacePlugin: (args: { + marketplaceSourceId: string + pluginKey: string + }) => Promise + installMarketplacePlugin: ( + preview: Pick< + PluginMarketplaceHostInstallPreview, + 'marketplaceSourceId' | 'marketplaceCommit' | 'pluginKey' | 'resolvedCommit' + > + ) => Promise + previewMarketplaceUpdate: (args: { + pluginKey: string + }) => Promise + rollbackMarketplacePlugin: (args: { pluginKey: string }) => Promise + remove: (args: { pluginKey: string }) => Promise + getLogs: (args: { pluginKey: string }) => Promise + /** Re-discovers after settings edits (feature flag, dev paths). */ + refresh: () => Promise + /** Fires whenever installed plugins, worker states, panels, or content packs change. */ + onChanged: (callback: (event: PluginChangeEvent) => void) => () => void +} diff --git a/src/preload/api/preflight-api.ts b/src/preload/api/preflight-api.ts new file mode 100644 index 00000000000..f63099a07ec --- /dev/null +++ b/src/preload/api/preflight-api.ts @@ -0,0 +1,55 @@ +import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime' +import type { PathSource, ShellHydrationFailureReason } from '../../shared/types' + +export type PreflightStatus = { + git: { installed: boolean } + gh: { installed: boolean; authenticated: boolean } + /** Optional — older preload payloads predating GitLab support omit it; consumers gate on `glab?.installed`. */ + glab?: { installed: boolean; authenticated: boolean } + bitbucket?: { configured: boolean; authenticated: boolean; account: string | null } + azureDevOps?: { + configured: boolean + authenticated: boolean + account: string | null + baseUrl: string | null + tokenConfigured: boolean + } + gitea?: { + configured: boolean + authenticated: boolean + account: string | null + baseUrl: string | null + tokenConfigured: boolean + } +} + +export type RefreshAgentsResult = { + agents: string[] + addedPathSegments: string[] + shellHydrationOk: boolean + /** Drives agent_picks `on_path:false` triage (dashboard 1562016). `'shell_hydrate'` = detection saw the user's + * full shell PATH; `'sync_seed_only'` = hydration failed and detection ran against the `patchPackagedProcessPath` seed list. */ + pathSource: PathSource + /** Classified hydration outcome: `'none'` on success, else a failure mode when `shellHydrationOk` is false. */ + pathFailureReason: ShellHydrationFailureReason +} + +export type PreflightRuntimeContext = { + wslDistro?: string | null + wslDefault?: boolean + projectRuntime?: ProjectExecutionRuntimeResolution +} + +export type PreflightApi = { + check: (args?: PreflightRuntimeContext & { force?: boolean }) => Promise + detectAgents: (args?: PreflightRuntimeContext) => Promise + refreshAgents: (args?: PreflightRuntimeContext) => Promise + detectRemoteAgents: (args: { connectionId: string }) => Promise + detectRemoteWindowsTerminalCapabilities: (args: { connectionId: string }) => Promise<{ + wslAvailable: boolean + wslDistros: string[] + pwshAvailable: boolean + gitBashAvailable: boolean + hostPlatform: NodeJS.Platform | null + }> +} diff --git a/src/preload/api/pty-api.ts b/src/preload/api/pty-api.ts new file mode 100644 index 00000000000..774196b6a01 --- /dev/null +++ b/src/preload/api/pty-api.ts @@ -0,0 +1,224 @@ +import type { + AgentProviderSessionMetadata, + SleepingAgentLaunchConfig +} from '../../shared/agent-session-resume' +import type { StartupCommandDelivery } from '../../shared/codex-startup-delivery' +import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime' +import type { PtyListedSession } from '../../shared/pty-listed-session' +import type { PtyMainDeliveryDiagnostics } from '../../shared/pty-delivery-diagnostics' +import type { PtyModelRestoreNeededEvent } from '../../shared/pty-model-restore-marker' +import type { + PtyRendererDeliveryHealthReply, + PtyRendererDeliveryStateReport +} from '../../shared/pty-renderer-delivery-health' +import type { AgentKind, LaunchSource, RequestKind } from '../../shared/telemetry-events' +import type { TerminalSideEffectBatch } from '../../shared/terminal-side-effect-facts' +import type { TerminalViewAttributes } from '../../shared/terminal-view-attributes' +import type { TuiAgent } from '../../shared/types' +import type { PtyManagementApi } from './pty-management-api' + +export type PtyApi = { + spawn: (opts: { + cols: number + rows: number + cwd?: string + cwdFallback?: 'worktree' + env?: Record + envToDelete?: string[] + command?: string + commandDelivery?: 'renderer' | 'provider' + launchConfig?: SleepingAgentLaunchConfig + resumeProviderSession?: AgentProviderSessionMetadata + launchToken?: string + launchAgent?: TuiAgent + startupCommandDelivery?: StartupCommandDelivery + connectionId?: string | null + worktreeId?: string + sessionId?: string + // Why: lets a single tab open in a different shell than the user's default. + shellOverride?: string + projectRuntime?: ProjectExecutionRuntimeResolution + terminalColorQueryReplies?: { foreground?: string; background?: string } + // Why: mark the PTY hidden before its first byte so the delivery gate owns spawn-time queries (terminal-query-authority.md §races). + initiallyHidden?: boolean + // Why: main sync-flushes the (worktreeId,tabId,leafId→ptyId) binding before pty:spawn returns to close a SIGKILL race (INVESTIGATION.md). + tabId?: string + leafId?: string + // Why: main fires `agent_started` only on spawn success, so launch metadata rides this field (telemetry-plan.md §Agent launch semantics). + telemetry?: { agent_kind: AgentKind; launch_source: LaunchSource; request_kind: RequestKind } + }) => Promise<{ + id: string + launchAgent?: TuiAgent + launchConfig?: SleepingAgentLaunchConfig + snapshot?: string + snapshotCols?: number + snapshotRows?: number + snapshotPrefixAnsi?: string + snapshotFrameAnsi?: string + snapshotFrameRestoreAnsi?: string + snapshotKittyKeyboardFlags?: number + snapshotSeq?: number + isReattach?: boolean + isAlternateScreen?: boolean + replay?: string + sessionExpired?: boolean + coldRestore?: { scrollback: string; cwd: string; cols?: number; rows?: number } + startupCwdFallback?: { kind: 'worktree'; cwd: string } + agentResumeUnavailable?: true + }> + write: (id: string, data: string) => void + writeAccepted: (id: string, data: string) => Promise + onWriteUnavailable?: (callback: (payload: { id: string }) => void) => () => void + resize: (id: string, cols: number, rows: number) => void + claimViewport: (id: string, cols: number, rows: number) => void + reportGeometry: (id: string, cols: number, rows: number) => void + signal: (id: string, signal: string) => void + clearBuffer: (id: string) => void + kill: (id: string, opts?: { keepHistory?: boolean }) => Promise + ackColdRestore: (id: string) => void + ackData: (id: string, charCount: number, processedChars?: number) => void + onDeliveryResyncRequest: (callback: (payload: { requestId: number }) => void) => () => void + respondDeliveryResync: (payload: { + requestId: number + processedCharsByPty: Record + }) => void + /** Renderer-initiated delivery health/heal lane over invoke — reaches main + * even when every main→renderer push channel is dead (field wedge). */ + reportRendererDeliveryState: ( + report: PtyRendererDeliveryStateReport + ) => Promise + /** Live pty:data listener count on the preload emitter (sync) — heal-time + * discriminator between a detached listener and a dead channel. */ + getPtyDataListenerCount: () => number + /** One-shot signal that this page's pty:data dispatcher is registered, so + * main can release sends held during the load/reload boot window. */ + rendererDispatcherReady: () => void + setActiveRendererPty: (id: string, active: boolean) => void + setRendererPtyVisible: (id: string, visible: boolean) => void + /** Hidden-delivery gate (Phase 4): hidden=true lets main drop renderer + * byte delivery after model ingestion; reveal restores from snapshots. */ + setHiddenRendererPty: (id: string, hidden: boolean) => void + /** Ref-counted-on-the-renderer delivery-interest signal that suppresses + * the hidden-delivery gate while any raw-byte consumer is registered. */ + setPtyDeliveryInterest: (id: string, interested: boolean) => void + /** View-attribute bridge (Phase 5 slice 2): app-global composed terminal + * appearance push backing main's hidden-PTY OSC/DSR color replies. */ + publishTerminalViewAttributes: (attributes: TerminalViewAttributes) => void + hasChildProcesses: (id: string) => Promise + getForegroundProcess: (id: string) => Promise + inspectProcess: (id: string) => Promise<{ + foregroundProcess: string | null + hasChildProcesses: boolean + unavailable?: true + }> + confirmForegroundProcess: (id: string) => Promise + getCwd: (id: string) => Promise + getSize: (id: string) => Promise<{ cols: number; rows: number } | null> + listSessions: () => Promise + getAuthoritativeBufferSnapshotCapabilities?: ( + ids: string[] + ) => Promise<{ id: string; authoritative: boolean | null }[]> + hasPty: (id: string) => Promise + getMainBufferSnapshot: ( + id: string, + opts?: { scrollbackRows?: number } + ) => Promise<{ + data: string + frameRestoreAnsi?: string + cols: number + rows: number + cwd?: string | null + seq?: number + /** Start of main's pending renderer-delivery queue at snapshot time + * (equals `seq` when empty) — bounds the renderer's post-restore + * duplicate window. */ + pendingDeliveryStartSeq?: number + source?: 'headless' | 'renderer' + alternateScreen?: boolean + /** Authoritative normal buffer paired with an alternate-screen frame. */ + scrollbackAnsi?: string + /** Trailing incomplete escape the emulator ingested; the restorer must + * write it after its post-replay resets, last before live chunks. */ + pendingEscapeTailAnsi?: string + /** Effective kitty flags the snapshot owner proved at `seq`. Absent means + * unknown; consumers must not turn that into a known `0`. */ + kittyKeyboardFlags?: number + } | null> + getRendererDeliveryDebugSnapshot: () => Promise<{ + pendingPtyCount: number + pendingChars: number + maxPendingCharsByPty: number + rendererInFlightPtyCount: number + rendererInFlightChars: number + maxRendererInFlightCharsByPty: number + activeRendererPtyCount: number + flushScheduled: boolean + peakPendingChars: number + peakMaxPendingCharsByPty: number + peakRendererInFlightChars: number + peakMaxRendererInFlightCharsByPty: number + ackGatedFlushSkipCount: number + hiddenDeliveryGatedPtyCount: number + hiddenDeliveryGatedVisiblePtyCount: number + hiddenDeliveryGatedActivePtyCount: number + deliveryInterestPtyCount: number + hiddenDeliveryDroppedChars: number + hiddenDeliveryDroppedChunks: number + pendingDroppedChars: number + diagnostics: PtyMainDeliveryDiagnostics + rendererLifecycleResetCount: number + lastLifecycleResetClearedChars: number + rendererPtyDispatcherReady: boolean + rendererDispatcherReadyForcedCount: number + }> + resetRendererDeliveryDebug: () => Promise + onData: ( + callback: (data: { + id: string + data: string + seq?: number + rawLength?: number + transformed?: boolean + background?: boolean + droppedOutput?: boolean + }) => void + ) => () => void + onReplay: (callback: (data: { id: string; data: string }) => void) => () => void + /** Out-of-band main→renderer signal that renderer-bound bytes were + * dropped (hidden-delivery gate / pending cap); the pane restores from + * the model snapshot. Never delivered in-band on pty:data. */ + onModelRestoreNeeded: (callback: (event: PtyModelRestoreNeededEvent) => void) => () => void + /** Batched derived side-effect facts for PTYs whose bytes transit local + * main. */ + onSideEffect: (callback: (batch: TerminalSideEffectBatch) => void) => () => void + /** Title-only replay snapshot for (re)attach; attention facts never replay. */ + getSideEffectSnapshot: (id: string) => Promise + onExit: ( + callback: (data: { id: string; code: number; preserveRendererBinding?: boolean }) => void + ) => () => void + onSpawned: (callback: (data: { id: string }) => void) => () => void + onSerializeBufferRequest: ( + callback: (data: { + requestId: string + ptyId: string + opts?: { scrollbackRows?: number; altScreenForcesZeroRows?: boolean } + }) => void + ) => () => void + onClearBufferRequest: (callback: (data: { ptyId: string }) => void) => () => void + sendSerializedBuffer: ( + requestId: string, + snapshot: { + data: string + cols: number + rows: number + seq?: number + lastTitle?: string + kittyKeyboardFlags?: number + } | null + ) => void + declarePendingPaneSerializer: (paneKey: string) => Promise + settlePaneSerializer: (paneKey: string, gen: number) => Promise + clearPendingPaneSerializer: (paneKey: string, gen: number) => Promise + reportRendererSerializerReady?: (ptyId: string) => Promise + management: PtyManagementApi +} diff --git a/src/preload/api/pty-management-api.ts b/src/preload/api/pty-management-api.ts new file mode 100644 index 00000000000..10fef2d2303 --- /dev/null +++ b/src/preload/api/pty-management-api.ts @@ -0,0 +1,30 @@ +// Mirror of daemon's `DaemonSessionInfo` (src/main/daemon/types.ts); not imported — preload can't depend on main-only protocol types. +export type PtyManagementSession = { + sessionId: string + state: 'created' | 'spawning' | 'running' | 'exiting' | 'exited' + shellState: 'pending' | 'ready' | 'timed_out' | 'unsupported' + isAlive: boolean + pid: number | null + cwd: string | null + cols: number + rows: number + createdAt: number + protocolVersion: number +} + +// 'severed': macOS can no longer attribute daemon terminals to Orca, so Accessibility/ +// Automation grants silently stop applying until the daemon is restarted (STA-3491). +export type PtyManagementMacTccAttributionHealth = 'intact' | 'severed' | 'unknown' + +export type PtyManagementApi = { + // `degraded`: daemon is alive but can't spawn fresh PTYs, so new terminals run locally without daemon persistence. + listSessions: () => Promise<{ sessions: PtyManagementSession[]; degraded: boolean }> + killAll: () => Promise<{ + killedCount: number + remainingCount: number + killedSessionIds?: string[] + }> + killOne: (args: { sessionId: string }) => Promise<{ success: boolean }> + restart: () => Promise<{ success: boolean }> + macTccAttribution: () => Promise<{ health: PtyManagementMacTccAttributionHealth }> +} diff --git a/src/preload/api/repository-api.ts b/src/preload/api/repository-api.ts new file mode 100644 index 00000000000..1734c35bdc7 --- /dev/null +++ b/src/preload/api/repository-api.ts @@ -0,0 +1,169 @@ +import type { ExecutionHostId } from '../../shared/execution-host' +import type { + HostRepoCatalogSnapshot, + ListReposForExecutionHostArgs +} from '../../shared/host-repo-catalog-contract' +import type { + BaseRefDefaultResult, + BaseRefSearchResult, + NestedRepoScanResult, + Project, + ProjectGroup, + ProjectGroupImportMode, + ProjectGroupImportResult, + ProjectHostSetup, + ProjectHostSetupCreateArgs, + ProjectHostSetupCreateResult, + ProjectHostSetupDeleteArgs, + ProjectHostSetupDeleteResult, + ProjectHostSetupExistingFolderArgs, + ProjectHostSetupResult, + ProjectHostSetupUpdateArgs, + ProjectHostSetupUpdateResult, + ProjectUpdateArgs, + Repo +} from '../../shared/types' + +export type RepositoryApi = { + list: () => Promise + listForExecutionHost?: (args: ListReposForExecutionHostArgs) => Promise + // Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`. + add: (args: { + path: string + kind?: 'git' | 'folder' + }) => Promise<{ repo: Repo } | { error: string }> + remove: (args: { repoId: string }) => Promise + // Forget a project on one execution host only, leaving the same repo id on other hosts intact. + removeForHost: (args: { repoId: string; hostId: string }) => Promise + reorder: (args: { orderedIds: string[] }) => Promise<{ status: 'applied' | 'rejected' }> + reorderForHost: (args: { + orderedIds: string[] + hostId: string + }) => Promise<{ status: 'applied' | 'rejected' }> + update: (args: { + repoId: string + hostId?: ExecutionHostId + updates: Partial< + Pick< + Repo, + | 'displayName' + | 'badgeColor' + | 'repoIcon' + | 'upstream' + | 'hookSettings' + | 'worktreeBaseRef' + | 'worktreeBasePath' + | 'kind' + | 'issueSourcePreference' + | 'externalWorktreeVisibility' + | 'externalWorktreeVisibilityPromptDismissedAt' + | 'externalWorktreeInboxBaselinePaths' + | 'importedExternalWorktreePaths' + | 'agentWorktreeVisibility' + | 'customWorktreeVisibilitySources' + | 'worktreeVisibilitySourcePreferences' + | 'projectGroupId' + | 'projectGroupOrder' + | 'forkSyncMode' + > + > & { + sourceControlAi?: Repo['sourceControlAi'] | null + externalWorktreeDiscoverySuppressedAt?: Repo['externalWorktreeDiscoverySuppressedAt'] | null + } + }) => Promise + pickFolder: () => Promise + pickFolders: () => Promise + pickDirectory: () => Promise + clone: (args: { url: string; destination: string }) => Promise + cloneRemote: (args: { connectionId: string; url: string; destination: string }) => Promise + createRemote: (args: { + connectionId: string + parentPath: string + name: string + kind: 'git' | 'folder' + }) => Promise<{ repo: Repo } | { error: string }> + cloneAbort: () => Promise + // Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`. + addRemote: (args: { + connectionId: string + remotePath: string + displayName?: string + kind?: 'git' | 'folder' + }) => Promise<{ repo: Repo } | { error: string }> + // Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`. + create: (args: { + parentPath: string + name: string + kind: 'git' | 'folder' + }) => Promise<{ repo: Repo } | { error: string }> + isGitAvailable: () => Promise + getDefaultCreateProjectParent: () => Promise + onCloneProgress: (callback: (data: { phase: string; percent: number }) => void) => () => void + getGitUsername: (args: { repoId: string }) => Promise + getBaseRefDefault: (args: { + repoId: string + hostId?: ExecutionHostId + }) => Promise + searchBaseRefs: (args: { + repoId: string + query: string + limit?: number + hostId?: ExecutionHostId + }) => Promise + searchBaseRefDetails: (args: { + repoId: string + query: string + limit?: number + hostId?: ExecutionHostId + }) => Promise + onChanged: (callback: () => void) => () => void +} + +export type ProjectsApi = { + list: () => Promise + update: (args: ProjectUpdateArgs) => Promise + listHostSetups: () => Promise + createHostSetup: (args: ProjectHostSetupCreateArgs) => Promise + setupExistingFolder: (args: ProjectHostSetupExistingFolderArgs) => Promise + updateHostSetup: (args: ProjectHostSetupUpdateArgs) => Promise + deleteHostSetup: (args: ProjectHostSetupDeleteArgs) => Promise +} + +export type ProjectGroupsApi = { + list: () => Promise + create: (args: { + name: string + parentPath?: string | null + connectionId?: string | null + parentGroupId?: string | null + createdFrom?: ProjectGroup['createdFrom'] + }) => Promise + update: (args: { + groupId: string + updates: Partial> + }) => Promise + delete: (args: { groupId: string }) => Promise + moveProject: (args: { + projectId: string + groupId: string | null + order?: number + }) => Promise + scanNested: (args: { + path: string + connectionId?: string + scanId?: string + options?: Record + }) => Promise + cancelNestedScan: (args: { scanId: string }) => Promise + onNestedScanProgress: ( + callback: (data: { scanId: string; scan: NestedRepoScanResult }) => void + ) => () => void + importNested: (args: { + parentPath: string + groupName: string + projectPaths: string[] + connectionId?: string + scanId?: string + mode: ProjectGroupImportMode + }) => Promise +} diff --git a/src/preload/api/runtime-api.ts b/src/preload/api/runtime-api.ts new file mode 100644 index 00000000000..1acc43f5f2c --- /dev/null +++ b/src/preload/api/runtime-api.ts @@ -0,0 +1,116 @@ +import type { + RuntimeBrowserDriverState, + RuntimeStatus, + RuntimeSyncWindowGraph, + RuntimeSyncWindowGraphResult, + RuntimeTerminalDriverState +} from '../../shared/runtime-types' +import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope' +import type { PublicKnownRuntimeEnvironment } from '../../shared/runtime-environments' +import type { VerifyAndAddRuntimeEnvironmentResult } from '../../shared/remote-pairing-verification' + +export type RuntimeEnvironmentSubscriptionHandle = { + unsubscribe: () => void + sendBinary: (bytes: Uint8Array) => void +} + +export type RuntimeApi = { + runtime: { + syncWindowGraph: (graph: RuntimeSyncWindowGraph) => Promise + getStatus: () => Promise + call: (args: { method: string; params?: unknown }) => Promise> + getTerminalFitOverrides: () => Promise< + { ptyId: string; mode: 'mobile-fit' | 'remote-desktop-fit'; cols: number; rows: number }[] + > + getTerminalDrivers: () => Promise< + { + ptyId: string + driver: RuntimeTerminalDriverState + }[] + > + getBrowserDrivers: () => Promise< + { + browserPageId: string + driver: RuntimeBrowserDriverState + }[] + > + restoreTerminalFit: (ptyId: string) => Promise<{ restored: boolean }> + reclaimBrowserForDesktop: (browserPageId: string) => Promise<{ reclaimed: boolean }> + onTerminalFitOverrideChanged: ( + callback: (event: { + ptyId: string + mode: 'mobile-fit' | 'remote-desktop-fit' | 'desktop-fit' + cols: number + rows: number + }) => void + ) => () => void + onTerminalDriverChanged: ( + callback: (event: { ptyId: string; driver: RuntimeTerminalDriverState }) => void + ) => () => void + onNativeChatLaunchDraftResolved?: ( + callback: (event: { tabId: string; text: string; createdAt: number }) => void + ) => () => void + onBrowserDriverChanged: ( + callback: (event: { browserPageId: string; driver: RuntimeBrowserDriverState }) => void + ) => () => void + } + runtimeEnvironments: { + list: () => Promise + addFromPairingCode: (args: { + name: string + pairingCode: string + }) => Promise<{ environment: PublicKnownRuntimeEnvironment }> + verifyAndAddFromPairingCode: (args: { + name: string + pairingCode: string + allowLoopback?: boolean + }) => Promise + resolve: (args: { selector: string }) => Promise + remove: (args: { selector: string }) => Promise<{ removed: PublicKnownRuntimeEnvironment }> + disconnect: (args: { + selector: string + }) => Promise<{ disconnected: PublicKnownRuntimeEnvironment }> + connect: (args: { + selector: string + timeoutMs?: number + }) => Promise> + getStatus: (args: { + selector: string + timeoutMs?: number + }) => Promise> + // Why: system resume / browser online advance pending shared-control reconnect timers only. + retryConnectionsNow?: () => Promise + call: (args: { + selector: string + method: string + params?: unknown + timeoutMs?: number + expectedEnvironmentPairingRevision?: number + }) => Promise> + subscribe: ( + args: { + selector: string + method: string + params?: unknown + timeoutMs?: number + expectedEnvironmentPairingRevision?: number + }, + callbacks: { + onResponse: (response: RuntimeRpcResponse) => void + onBinary?: (bytes: Uint8Array) => void + onError?: (error: { code: string; message: string }) => void + onClose?: () => void + } + ) => Promise + } + wsl: { + isAvailable: () => Promise + listDistros: () => Promise + } + pwsh: { + isAvailable: () => Promise + } + gitBash: { + isAvailable: () => Promise + } +} diff --git a/src/preload/api/settings-api.ts b/src/preload/api/settings-api.ts new file mode 100644 index 00000000000..b50843160dd --- /dev/null +++ b/src/preload/api/settings-api.ts @@ -0,0 +1,35 @@ +import type { KeybindingActionId, KeybindingFileSnapshot } from '../../shared/keybindings' +import type { + WarpThemeImportPreview, + WarpThemeImportSource +} from '../../shared/terminal-custom-themes' +import type { GhosttyImportPreview, GlobalSettings } from '../../shared/types' + +export type SettingsApi = { + get: () => Promise + /** Synchronous persisted-settings read for startup decisions that can't wait for async hydration. Blocking IPC — call sparingly. */ + getSync: () => GlobalSettings | null + set: (args: Partial) => Promise + setActiveRuntimeEnvironmentPreference: (args: { + environmentId: string | null + }) => Promise + updatePRBotAuthorOverride: (args: { author: string; isBot: boolean }) => Promise + listFonts: () => Promise + previewGhosttyImport: () => Promise + previewWarpThemeImport: (source: WarpThemeImportSource) => Promise + /** Subscribe to out-of-band settings updates (e.g. View > Appearance toggles) to stay in sync with main. */ + onChanged: (callback: (updates: Partial) => void) => () => void +} + +export type KeybindingsApi = { + get: () => Promise + ensureFile: () => Promise + setAction: (args: { + actionId: KeybindingActionId + bindings: string[] | null + }) => Promise + reload: () => Promise + openFile: () => Promise + revealFile: () => Promise + onChanged: (callback: (snapshot: KeybindingFileSnapshot) => void) => () => void +} diff --git a/src/preload/api/shell-api.ts b/src/preload/api/shell-api.ts new file mode 100644 index 00000000000..d466ac26163 --- /dev/null +++ b/src/preload/api/shell-api.ts @@ -0,0 +1,32 @@ +import type { + ShellOpenExternalEditorRequest, + ShellOpenExternalEditorResult, + ShellOpenLocalPathResult +} from '../../shared/shell-open-types' + +export type { + ShellOpenExternalEditorRequest, + ShellOpenExternalEditorResult, + ShellOpenLocalPathResult +} from '../../shared/shell-open-types' + +export type ShellApi = { + openPath: (path: string) => Promise + openInFileManager: (path: string) => Promise + openInExternalEditor: ( + request: ShellOpenExternalEditorRequest + ) => Promise + openUrl: (url: string) => Promise + openFilePath: (path: string) => Promise + openFileUri: (uri: string) => Promise + pathExists: (path: string) => Promise + pickAttachment: () => Promise + pickImage: () => Promise + pickRepoIconImage: () => Promise<{ + dataUrl: string + fileName: string + } | null> + pickAudio: () => Promise + pickDirectory: (args: { defaultPath?: string }) => Promise + copyFile: (args: { srcPath: string; destPath: string }) => Promise +} diff --git a/src/preload/api/speech-api.ts b/src/preload/api/speech-api.ts new file mode 100644 index 00000000000..fb866dace15 --- /dev/null +++ b/src/preload/api/speech-api.ts @@ -0,0 +1,33 @@ +import type { + SpeechErrorEvent, + SpeechLifecycleEvent, + SpeechModelManifest, + SpeechModelState, + SpeechTranscriptEvent +} from '../../shared/speech-types' + +export type SpeechApi = { + getCatalog: () => Promise + getModelStates: () => Promise + getOpenAiApiKeyStatus: () => Promise<{ configured: boolean }> + saveOpenAiApiKey: (apiKey: string) => Promise<{ configured: boolean }> + clearOpenAiApiKey: () => Promise<{ configured: boolean }> + downloadModel: (modelId: string) => Promise + cancelDownload: (modelId: string) => Promise + deleteModel: (modelId: string) => Promise + startDictation: ( + modelId: string, + hotwords: string[] | undefined, + sessionId: string + ) => Promise + feedAudio: (samples: Float32Array, sampleRate: number, sessionId?: string) => Promise + stopDictation: (sessionId?: string) => Promise + onPartialTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void + onFinalTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void + onDownloadProgress: ( + callback: (data: { modelId: string; progress: number }) => void + ) => () => void + onReady: (callback: (data: SpeechLifecycleEvent) => void) => () => void + onStopped: (callback: (data: SpeechLifecycleEvent) => void) => () => void + onError: (callback: (data: SpeechErrorEvent) => void) => () => void +} diff --git a/src/preload/api/ssh-api.ts b/src/preload/api/ssh-api.ts new file mode 100644 index 00000000000..b25f1713e16 --- /dev/null +++ b/src/preload/api/ssh-api.ts @@ -0,0 +1,78 @@ +import type { + EnrichedDetectedPort, + PortForwardEntry, + SshConfigHostListArgs, + SshConfigHostListResult, + SshConfigHostResolution, + SshConfigImportResult, + SshConnectionState, + SshTarget, + SshTargetAddResult +} from '../../shared/ssh-types' +import type { FilesystemPathFlavor } from '../../shared/types' + +export type SshApi = { + listTargets: () => Promise + // Removed-target id → last known label, for a friendly host name on workspaces still pinned to a removed target. + listRemovedTargetLabels: () => Promise> + addTarget: (args: { target: Omit }) => Promise + updateTarget: (args: { + id: string + updates: Partial> + }) => Promise + removeTarget: (args: { id: string }) => Promise + importConfig: (args?: { reAdopt?: boolean }) => Promise + listConfigHosts: (args?: SshConfigHostListArgs) => Promise + resolveConfigHost: (args: { alias: string }) => Promise + connect: (args: { targetId: string }) => Promise + disconnect: (args: { targetId: string }) => Promise + terminateSessions: (args: { targetId: string }) => Promise + resetRelay: (args: { targetId: string }) => Promise + getState: (args: { targetId: string }) => Promise + needsPassphrasePrompt: (args: { targetId: string }) => Promise + testConnection: (args: { + targetId: string + }) => Promise<{ success: boolean; error?: string; state?: SshConnectionState }> + onStateChanged: ( + callback: (data: { targetId: string; state: SshConnectionState }) => void + ) => () => void + addPortForward: (args: { + targetId: string + localPort: number + remoteHost: string + remotePort: number + label?: string + }) => Promise + updatePortForward: (args: { + id: string + targetId: string + localPort: number + remoteHost: string + remotePort: number + label?: string + }) => Promise + removePortForward: (args: { id: string }) => Promise + listPortForwards: (args?: { targetId?: string }) => Promise + listDetectedPorts: (args: { targetId: string }) => Promise + onPortForwardsChanged: ( + callback: (data: { targetId: string; forwards: PortForwardEntry[] }) => void + ) => () => void + onDetectedPortsChanged: ( + callback: (data: { targetId: string; ports: EnrichedDetectedPort[] }) => void + ) => () => void + browseDir: (args: { targetId: string; dirPath: string }) => Promise<{ + entries: { name: string; isDirectory: boolean }[] + resolvedPath: string + pathFlavor: FilesystemPathFlavor + }> + onCredentialRequest: ( + callback: (data: { + requestId: string + targetId: string + kind: 'passphrase' | 'password' + detail: string + }) => void + ) => () => void + onCredentialResolved: (callback: (data: { requestId: string }) => void) => () => void + submitCredential: (args: { requestId: string; value: string | null }) => Promise +} diff --git a/src/preload/api/telemetry-api.ts b/src/preload/api/telemetry-api.ts new file mode 100644 index 00000000000..9fc70f908b7 --- /dev/null +++ b/src/preload/api/telemetry-api.ts @@ -0,0 +1,63 @@ +import type { TelemetryConsentState } from '../../shared/telemetry-consent-types' +import type { MemorySnapshot, StatsSummary } from '../../shared/types' + +export type StatsApi = { + getSummary: () => Promise +} + +// Diagnostics IPC payloads; mirror the runtime types in `src/main/observability/{index,bundle}.ts`. +export type DiagnosticsStatusPayload = { + readonly localFileEnabled: boolean + readonly bundleEnabled: boolean + readonly traceFilePath: string + readonly traceFamilySize: number + readonly disabledReason?: + | 'do_not_track' + | 'orca_telemetry_disabled' + | 'orca_diagnostics_disabled' + | 'ci' +} +export type DiagnosticsBundlePayload = { + readonly bundleSubmissionId: string + readonly bytes: number + readonly spanCount: number +} +export type DiagnosticsUploadPayload = + | { + readonly ticketId: string + } + | { + readonly canceled: true + } + +export type MemoryApi = { + getSnapshot: () => Promise +} + +export type DiagnosticsApi = { + getStatus: () => Promise + collectBundle: (lookbackMinutes?: number) => Promise + openBundlePreview: (bundleSubmissionId: string) => Promise + discardBundlePreview: (bundleSubmissionId: string) => Promise + uploadBundle: (bundleSubmissionId: string) => Promise + deleteBundle: (ticketId: string) => Promise +} + +export type TelemetryApi = { + /** Fire-and-forget track. Loose IPC typing on purpose — the main-side validator enforces; + * renderer sites should import `track()` from lib/telemetry.ts, not reach here. */ + telemetryTrack: (name: string, props: Record) => Promise + /** Flip the persisted opt-in preference. Subject to a per-session + * consent-mutation rate limit on the main side (≤5/session). */ + telemetrySetOptIn: (optedIn: boolean) => Promise + /** Diagnostic file controls (telemetry-error-tracking.md §User controls). Main does the FS/network + * work and retains upload payloads so the renderer can't read or substitute arbitrary bytes. */ + diagnostics: DiagnosticsApi + /** Read-only effective consent state (+ reason if disabled) — env vars are main-side state the renderer can't read directly. */ + telemetryGetConsentState: () => Promise + /** Banner ✕ — persist `optedIn = true` silently. Separate channel from `telemetrySetOptIn`, + * whose `via` derivation would wrongly fire `telemetry_opted_in`. Same per-session rate limit. */ + telemetryAcknowledgeBanner: () => Promise + stats: StatsApi + memory: MemoryApi +} diff --git a/src/preload/api/ui-command-event-api.ts b/src/preload/api/ui-command-event-api.ts new file mode 100644 index 00000000000..e46c09139b7 --- /dev/null +++ b/src/preload/api/ui-command-event-api.ts @@ -0,0 +1,203 @@ +import type { + PersistedUIState, + TuiAgent, + WorktreeDefaultTabsLaunch, + WorktreeSetupLaunch, + WorktreeStartupLaunch +} from '../../shared/types' +import type { FeatureInteractionId } from '../../shared/feature-interactions' +import type { KeybindingActionId } from '../../shared/keybindings' +import type { BrowserFindSource } from '../../shared/browser-find-source' +import type { + AgentProviderSessionMetadata, + SleepingAgentLaunchConfig +} from '../../shared/agent-session-resume' +import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry' +import type { + RuntimeMobileSessionTabMove, + RuntimeTerminalCreateRequestPayload, + RuntimeTerminalPresentation +} from '../../shared/runtime-types' +import type { + RuntimeMobileMarkdownRequest, + RuntimeMobileMarkdownResponse +} from '../../shared/mobile-markdown-document' +import type { TerminalTabCreateReply } from '../../shared/terminal-reveal-identity' +import type { + TerminalTabCloseRequest, + TerminalTabCloseResponse +} from '../../shared/terminal-tab-close' + +export type UiCommandEventApi = { + get: () => Promise + set: (args: Partial) => Promise + recordFeatureInteraction: (id: FeatureInteractionId) => Promise + onStateChanged: (callback: (ui: PersistedUIState) => void) => () => void + onOpenSettings: (callback: () => void) => () => void + /** Consumes a one-shot tray/menu-bar "open settings" intent queued before mount. */ + consumePendingOpenSettings: () => Promise + onOpenSetupGuide: (callback: () => void) => () => void + onOpenFeatureTour: (callback: () => void) => () => void + onOpenCrashReport: (callback: () => void) => () => void + onToggleLeftSidebar: (callback: () => void) => () => void + onToggleRightSidebar: (callback: () => void) => () => void + onToggleWorktreePalette: (callback: () => void) => () => void + onToggleFloatingTerminal: (callback: () => void) => () => void + onTerminalShortcutCaptured: ( + callback: (data: { actionId: KeybindingActionId }) => void + ) => () => void + onOpenQuickOpen: (callback: () => void) => () => void + onToggleQuickCommandsMenu: (callback: () => void) => () => void + onOpenNewWorkspace: (callback: () => void) => () => void + onDeleteCurrentWorkspace: (callback: () => void) => () => void + onOpenWorkspaceBoard: (callback: () => void) => () => void + onOpenTasks: (callback: () => void) => () => void + onJumpToWorktreeIndex: (callback: (index: number) => void) => () => void + onJumpToTabIndex: (callback: (index: number) => void) => () => void + onWorktreeHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void + onNewBrowserTab: (callback: () => void) => () => void + onNewMarkdownTab: (callback: () => void) => () => void + onNewSimulatorTab: (callback: () => void) => () => void + onRequestTabCreate: ( + callback: (data: { + requestId: string + url: string + worktreeId?: string + sessionProfileId?: string | null + sessionPartition?: string + activate?: boolean + }) => void + ) => () => void + replyTabCreate: (reply: { requestId: string; browserPageId?: string; error?: string }) => void + onRequestTabSetProfile: ( + callback: (data: { + requestId: string + browserPageId: string + profileId: string + sessionPartition?: string + }) => void + ) => () => void + replyTabSetProfile: (reply: { requestId: string; error?: string }) => void + onRequestTabClose: ( + callback: (data: { requestId: string; tabId: string | null; worktreeId?: string }) => void + ) => () => void + replyTabClose: (reply: { requestId: string; error?: string }) => void + onNewTerminalTab: (callback: () => void) => () => void + onFocusBrowserAddressBar: (callback: () => void) => () => void + onFindInBrowserPage: (source: BrowserFindSource, callback: () => void) => () => void + onReloadBrowserPage: (callback: () => void) => () => void + onBrowserHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void + onZoomBrowserPage: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void + onHardReloadBrowserPage: (callback: () => void) => () => void + onCloseActiveTab: (callback: () => void) => () => void + onCloseFloatingItem: (callback: (payload: { sourceId: string }) => void) => () => void + onSelectFloatingIndex: (callback: (payload: { index: number }) => void) => () => void + onSwitchTab: (callback: (direction: 1 | -1) => void) => () => void + onSwitchTabAcrossAllTypes: (callback: (direction: 1 | -1) => void) => () => void + onSwitchRecentTab: (callback: () => void) => () => void + onSwitchTerminalTab: (callback: (direction: 1 | -1) => void) => () => void + onCtrlTabKeyDown: (callback: (data: { shiftKey: boolean }) => void) => () => void + onCtrlTabKeyUp: (callback: () => void) => () => void + onToggleStatusBar: (callback: () => void) => () => void + onDictationKeyDown: (callback: () => void) => () => void + onExportPdfRequested: (callback: () => void) => () => void + onAppMenuPaste: (callback: () => void) => () => void + onAppMenuSelectionAction: (callback: (action: 'copy' | 'select-all') => void) => () => void + onEditableContextPaste: (callback: (data: { plainTextOnly: boolean }) => void) => () => void + onActivateWorktree: ( + callback: (data: { + repoId: string + worktreeId: string + setup?: WorktreeSetupLaunch + startup?: WorktreeStartupLaunch + defaultTabs?: WorktreeDefaultTabsLaunch + }) => void + ) => () => void + onCreateTerminal: ( + callback: (data: { + requestId?: string + worktreeId: string + command?: string + cwd?: string + env?: Record + launchConfig?: SleepingAgentLaunchConfig + resumeProviderSession?: AgentProviderSessionMetadata + launchToken?: string + launchAgent?: TuiAgent + viewMode?: 'terminal' | 'chat' + title?: string + ptyId?: string + activate?: boolean + focus?: boolean + presentation?: RuntimeTerminalPresentation + surfaceOwner?: false + tabId?: string + leafId?: string + splitFromLeafId?: string + splitDirection?: 'horizontal' | 'vertical' + splitTelemetrySource?: TerminalPaneSplitSource + }) => void + ) => () => void + onRequestTerminalCreate: ( + callback: (data: RuntimeTerminalCreateRequestPayload) => void + ) => () => void + onRequestTerminalTabMount: ( + callback: (data: { worktreeId: string; tabId?: string; ptyId?: string }) => void + ) => () => void + replyTerminalCreate: (reply: TerminalTabCreateReply) => void + onSplitTerminal: ( + callback: (data: { + tabId: string + paneRuntimeId: number + direction: 'horizontal' | 'vertical' + command?: string + telemetrySource?: TerminalPaneSplitSource + }) => void + ) => () => void + onRenameTerminal: ( + callback: (data: { tabId: string; title: string | null }) => void + ) => () => void + onFocusTerminal: ( + callback: (data: { + tabId: string + worktreeId: string + leafId?: string | null + ackPaneKeyOnSuccess?: string + flashFocusedPane?: boolean + scrollToBottomIfOutputSinceLastView?: boolean + }) => void + ) => () => void + onFocusEditorTab: (callback: (data: { tabId: string; worktreeId: string }) => void) => () => void + onCloseSessionTab: (callback: (data: { tabId: string; worktreeId: string }) => void) => () => void + onMoveSessionTab: ( + callback: (data: { worktreeId: string } & RuntimeMobileSessionTabMove) => void + ) => () => void + onOpenFileFromMobile: ( + callback: (data: { + worktreeId: string + filePath: string + relativePath: string + runtimeEnvironmentId?: string + }) => void + ) => () => void + onOpenDiffFromMobile: ( + callback: (data: { + worktreeId: string + filePath: string + relativePath: string + staged: boolean + runtimeEnvironmentId?: string + }) => void + ) => () => void + onMobileMarkdownRequest: (callback: (request: RuntimeMobileMarkdownRequest) => void) => () => void + respondMobileMarkdownRequest: (response: RuntimeMobileMarkdownResponse) => void + onCloseTerminal: ( + callback: (data: { tabId: string; paneRuntimeId?: number }) => void + ) => () => void + onTerminalTabCloseRequest: (callback: (request: TerminalTabCloseRequest) => void) => () => void + respondTerminalTabClose: (response: TerminalTabCloseResponse) => void + onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void + onResumeSleepingAgents: (callback: (data: { worktreeId: string }) => void) => () => void + onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void + onSystemResumed: (callback: () => void) => () => void +} diff --git a/src/preload/api/ui-window-api.ts b/src/preload/api/ui-window-api.ts new file mode 100644 index 00000000000..fc6aef6991b --- /dev/null +++ b/src/preload/api/ui-window-api.ts @@ -0,0 +1,51 @@ +import type { ReadClipboardTextOptions } from '../../shared/clipboard-text' +import type { NativeFileDropPayload } from '../../shared/native-file-drop' +import type { + RichMarkdownContextMenuCommandPayload, + RichMarkdownContextMenuTableTarget +} from '../../shared/rich-markdown-context-menu' + +export type UiWindowApi = { + readClipboardText: (options?: ReadClipboardTextOptions) => Promise + readSelectionClipboardText: (options?: ReadClipboardTextOptions) => Promise + saveClipboardImageAsTempFile: (args?: { + connectionId?: string | null + runtimeEnvironmentId?: string | null + }) => Promise + writeClipboardText: (text: string) => Promise + writeTerminalClipboardText: (text: string) => Promise + writeSelectionClipboardText: (text: string) => Promise + writeClipboardImage: (dataUrl: string) => Promise + performNativePaste: (options?: { mode?: 'paste' | 'paste-and-match-style' }) => void + performNativeSelectionAction: (action: 'copy' | 'select-all') => void + writeClipboardFile: ( + args: + | { + filePath: string + connectionId?: string | null + } + | string + ) => Promise<{ ok: boolean; reason?: string }> + onFileDrop: (callback: (data: NativeFileDropPayload) => void) => () => void + getZoomLevel: () => number + setZoomLevel: (level: number) => void + syncTrafficLights: (zoomFactor: number) => void + setMarkdownEditorFocused: (focused: boolean) => void + setRichMarkdownContextMenuTarget: (target: RichMarkdownContextMenuTableTarget | null) => void + setTerminalInputFocused: (focused: boolean) => void + setFloatingFocus: (state: { panelFocused: boolean; terminalFocused: boolean }) => void + setShortcutRecorderFocused: (focused: boolean) => void + onRichMarkdownContextCommand: ( + callback: (payload: RichMarkdownContextMenuCommandPayload) => void + ) => () => void + onFullscreenChanged: (callback: (isFullScreen: boolean) => void) => () => void + minimize: () => void + maximize: () => void + isMaximized: () => Promise + onMaximizeChanged: (callback: (isMaximized: boolean) => void) => () => void + requestClose: () => void + popupMenu: () => void + onWindowCloseRequested: (callback: (data: { isQuitting: boolean }) => void) => () => void + confirmWindowClose: () => void + notifyWindowRevealed: () => void +} diff --git a/src/preload/api/updater-api.ts b/src/preload/api/updater-api.ts new file mode 100644 index 00000000000..f6a43da9834 --- /dev/null +++ b/src/preload/api/updater-api.ts @@ -0,0 +1,25 @@ +import type { + LinuxPackageInstallInstructions, + ReleaseBuildListResult, + UpdateCheckOptions, + UpdateStatus +} from '../../shared/types' +import type { ReleaseChannel } from '../../shared/release-channel' + +export type UpdaterApi = { + getVersion: () => Promise + getStatus: () => Promise + check: (options?: UpdateCheckOptions) => Promise + download: () => Promise + quitAndInstall: () => Promise + dismissNudge: () => Promise + dismissAvailableUpdate: () => Promise + /** Desktop-only. Rejects unless the current status carries `linux-package-install` recovery. */ + getLinuxPackageInstallInstructions: () => Promise + /** Desktop-only. Reveals the revalidated cached package in the native file manager. */ + showLinuxPackage: () => Promise + listBuilds: (channel: ReleaseChannel) => Promise + + onStatus: (callback: (status: UpdateStatus) => void) => () => void + onClearDismissal: (callback: () => void) => () => void +} diff --git a/src/preload/api/workspace-cleanup-api.ts b/src/preload/api/workspace-cleanup-api.ts new file mode 100644 index 00000000000..748d8216540 --- /dev/null +++ b/src/preload/api/workspace-cleanup-api.ts @@ -0,0 +1,30 @@ +import type { + WorkspaceCleanupDismissArgs, + WorkspaceCleanupLocalProcessArgs, + WorkspaceCleanupLocalProcessResult, + WorkspaceCleanupScanArgs, + WorkspaceCleanupScanProgress, + WorkspaceCleanupScanResult +} from '../../shared/workspace-cleanup' +import type { + WorkspaceSpaceAnalyzeResult, + WorkspaceSpaceScanProgress +} from '../../shared/workspace-space-types' + +export type WorkspaceCleanupApi = { + scan: ( + args?: WorkspaceCleanupScanArgs, + onProgress?: (progress: WorkspaceCleanupScanProgress) => void + ) => Promise + dismiss: (args: WorkspaceCleanupDismissArgs) => Promise + clearDismissals: () => Promise + hasKillableLocalProcesses: ( + args: WorkspaceCleanupLocalProcessArgs + ) => Promise +} + +export type WorkspaceSpaceApi = { + analyze: () => Promise + cancel: () => Promise + onProgress: (callback: (progress: WorkspaceSpaceScanProgress) => void) => () => void +} diff --git a/src/preload/api/workspace-port-api.ts b/src/preload/api/workspace-port-api.ts new file mode 100644 index 00000000000..8c0166695ac --- /dev/null +++ b/src/preload/api/workspace-port-api.ts @@ -0,0 +1,23 @@ +import type { + LocalhostWorktreeLabelResult, + LocalhostWorktreeLabelRoute +} from '../../shared/localhost-worktree-labels' +import type { + WorkspacePortAdvertisedUrlChangedEvent, + WorkspacePortKillRequest, + WorkspacePortKillResult, + WorkspacePortScanRequest, + WorkspacePortScanResult +} from '../../shared/workspace-ports' + +export type WorkspacePortsApi = { + scan: (args: WorkspacePortScanRequest) => Promise + kill: (args: WorkspacePortKillRequest) => Promise + onAdvertisedUrlChanged: ( + callback: (event: WorkspacePortAdvertisedUrlChangedEvent) => void + ) => () => void +} + +export type LocalhostWorktreeLabelsApi = { + register: (args: LocalhostWorktreeLabelRoute) => Promise +} diff --git a/src/preload/api/workspace-session-api.ts b/src/preload/api/workspace-session-api.ts new file mode 100644 index 00000000000..35eb17486d7 --- /dev/null +++ b/src/preload/api/workspace-session-api.ts @@ -0,0 +1,50 @@ +import type { + IssueInfo, + PRInfo, + WorkspaceSessionPatch, + WorkspaceSessionState +} from '../../shared/types' +import type { ExecutionHostId } from '../../shared/execution-host' +import type { + RemoteWorkspaceChangedEvent, + RemoteWorkspaceConnectedClient, + RemoteWorkspacePatchResult, + RemoteWorkspaceSnapshot +} from '../../shared/remote-workspace-types' + +export type WorkspaceSessionApi = { + session: { + // hostId defaults to the 'local' partition on main, so omitting it stays backward-compatible. + get: (hostId?: ExecutionHostId) => Promise + set: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => Promise + patch: (args: WorkspaceSessionPatch, hostId?: ExecutionHostId) => Promise + flush: () => Promise + readTerminalScrollback: (args: { ref: string }) => string | null + setSync: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => void + } + cache: { + getGitHub: () => Promise<{ + pr: Record + issue: Record + }> + setGitHub: (args: { + cache: { + pr: Record + issue: Record + } + }) => Promise + } + remoteWorkspace: { + get: (args: { targetId: string }) => Promise + setForConnectedTargets: (args: { + session?: WorkspaceSessionState + hydratedTargetIds?: string[] + }) => Promise<{ targetId: string; result: RemoteWorkspacePatchResult }[]> + listEnabledConnectedTargets: () => Promise + listConnectedClients: (args?: { + targetIds?: string[] + }) => Promise<{ targetId: string; clients: RemoteWorkspaceConnectedClient[] }[]> + clientId: () => Promise + onChanged: (callback: (event: RemoteWorkspaceChangedEvent) => void) => () => void + } +} diff --git a/src/preload/api/worktree-api.ts b/src/preload/api/worktree-api.ts new file mode 100644 index 00000000000..96089d10e88 --- /dev/null +++ b/src/preload/api/worktree-api.ts @@ -0,0 +1,179 @@ +import type { + ForgetRemovedWorktreesForExecutionHostArgs, + ForgetRemovedWorktreesForExecutionHostResult, + HostQualifiedDetectedWorktreeResult, + HostQualifiedKnownWorktreeResult, + LegacyDetectedWorktreeRequest, + ListDetectedWorktreesArgs, + ListKnownWorktreesForExecutionHostArgs, + ProviderRequestId +} from '../../shared/detected-worktree-provider-contract' +import type { ExecutionHostId } from '../../shared/execution-host' +import type { + FolderWorkspacePathStatus, + FolderWorkspacePathStatusRequest +} from '../../shared/folder-workspace-path-status' +import type { + HostLineageSnapshot, + ListDesktopLineageForHostArgs +} from '../../shared/host-lineage-contract' +import type { + AdoptProvisionedRootArgs, + CreateWorktreeArgs, + CreateWorktreeResult, + DetectedWorktreeListResult, + FolderWorkspace, + ForceDeleteWorktreeBranchResult, + GitHubPrStartPoint, + GitPushTarget, + RemoveWorktreeResult, + SparsePreset, + WorkspaceLineage, + Worktree, + WorktreeBaseStatusEvent, + WorktreeHeadIdentity, + WorktreeLineage, + WorktreeMeta, + WorktreeRemoteBranchConflictEvent +} from '../../shared/types' + +export type WorktreeApi = { + list: (args: { repoId: string }) => Promise + listDetected: { + ( + args: ListDetectedWorktreesArgs + ): Promise + (args: LegacyDetectedWorktreeRequest): Promise + } + listKnownForExecutionHost?: ( + args: ListKnownWorktreesForExecutionHostArgs + ) => Promise + /** Retires the persisted metadata an authoritative scan proved gone, so it stops feeding the read above. */ + forgetRemovedForExecutionHost?: ( + args: ForgetRemovedWorktreesForExecutionHostArgs + ) => Promise + cancelListDetected?: (args: { providerRequestId: ProviderRequestId }) => Promise + listAll: () => Promise + create: (args: CreateWorktreeArgs) => Promise + adoptProvisionedRoot: (args: AdoptProvisionedRootArgs) => Promise + /** Two-phase progress for a background `create`, correlated by `creationId`. The remote/runtime + * create path emits nothing, so the surface falls back to an indeterminate spinner. */ + onCreateProgress: ( + callback: (data: { creationId?: string; phase: 'fetching' | 'creating' }) => void + ) => () => void + prefetchCreateBase: (args: { repoId: string; baseBranch?: string }) => Promise + resolvePrBase: (args: { + repoId: string + prNumber: number + headRefName?: string + baseRefName?: string + isCrossRepository?: boolean + }) => Promise + /** GitLab parallel of resolvePrBase. For same-project MRs returns + * `/`; for fork MRs fetches + * refs/merge-requests//head and returns the SHA. */ + resolveMrBase: (args: { + repoId: string + mrIid: number + sourceBranch?: string + targetBranch?: string + isCrossRepository?: boolean + }) => Promise< + { baseBranch: string; compareBaseRef?: string; pushTarget?: GitPushTarget } | { error: string } + > + remove: (args: { + worktreeId: string + hostId?: ExecutionHostId + force?: boolean + // Why (#11960): distinct from `force`, which the plain Delete confirmation + // already sets to skip the dirty-file prompt. Only an explicit Force Delete + // may waive the proof that every PTY stopped. + allowUnverifiedPtyStop?: boolean + skipArchive?: boolean + }) => Promise + // Forget a workspace from Orca only (no remote Git/FS work) — for workspaces pinned to a removed/disconnected SSH host. + forgetLocal: (args: { + worktreeId: string + hostId?: ExecutionHostId + }) => Promise + forceDeletePreservedBranch: (args: { + worktreeId: string + branchName: string + expectedHead: string + hostId?: ExecutionHostId + }) => Promise + updateMeta: (args: { worktreeId: string; updates: Partial }) => Promise + listLineage: () => Promise<{ + lineage: Record + workspaceLineage?: Record + }> + listLineageForHost?: (args: ListDesktopLineageForHostArgs) => Promise + updateLineage: (args: { + worktreeId: string + parentWorktreeId?: string + noParent?: boolean + }) => Promise + persistSortOrder: (args: { orderedIds: string[] }) => Promise + /** Full CLI output of the last branch auto-rename generation failure, held + * in main memory only — null after a restart or once the failure clears. */ + getBranchRenameFailureOutput: (args: { worktreeId: string }) => Promise + onChanged: (callback: (data: { repoId: string }) => void) => () => void + onGitStatusMetadataChanged: (callback: (data: { repoId: string }) => void) => () => void + onHeadIdentitiesChanged: ( + callback: (data: { repoId: string; identities: WorktreeHeadIdentity[] }) => void + ) => () => void + onBaseStatus: (callback: (data: WorktreeBaseStatusEvent) => void) => () => void + onRemoteBranchConflict: ( + callback: (data: WorktreeRemoteBranchConflictEvent) => void + ) => () => void +} + +export type FolderWorkspacesApi = { + list: () => Promise + getPathStatus: (args: FolderWorkspacePathStatusRequest) => Promise + create: (args: { + projectGroupId: string + name?: string + folderPath?: string | null + connectionId?: string | null + linkedTask?: FolderWorkspace['linkedTask'] + createdWithAgent?: FolderWorkspace['createdWithAgent'] + pendingFirstAgentMessageRename?: boolean + }) => Promise + update: (args: { + folderWorkspaceId: string + updates: Partial< + Pick< + FolderWorkspace, + | 'name' + | 'folderPath' + | 'linkedTask' + | 'comment' + | 'isArchived' + | 'isUnread' + | 'isPinned' + | 'sortOrder' + | 'manualOrder' + | 'workspaceStatus' + | 'createdWithAgent' + | 'pendingFirstAgentMessageRename' + | 'firstAgentMessageRenameError' + | 'lastActivityAt' + | 'diffComments' + > + > + }) => Promise + delete: (args: { folderWorkspaceId: string }) => Promise +} + +export type SparsePresetsApi = { + list: (args: { repoId: string }) => Promise + save: (args: { + repoId: string + id?: string + name: string + directories: string[] + }) => Promise + remove: (args: { repoId: string; presetId: string }) => Promise + onChanged: (callback: (data: { repoId: string }) => void) => () => void +}