refactor(preload): split the preload contract into per-domain api modules (#14403)

`src/preload/api-types.ts` was 3,752 raw lines (3,533 counted, 11.8x the
300-line budget) behind an `eslint-disable max-lines`. Almost all of it was a
single `PreloadApi` object type whose ~83 namespace properties were declared
inline, so any IPC surface change meant editing one 2,600-line type.

Give each namespace a named type in its own module under `src/preload/api/`
(`pty-api.ts`, `filesystem-api.ts`, `github-pull-request-api.ts`, ...) and
recompose `PreloadApi` from those names. `api-types.ts` keeps the `declare
global` Window augmentation and re-exports every moved name, so all 52 import
sites are untouched.

Two shapes needed care to stay type-identical rather than merely compatible:

- Three keys (`gh`, `git`, `ui`) are composed from two modules each. A plain
  intersection is NOT identical to the original flat object literal, so those
  use a `Merged<T>` mapped type; a negative control confirmed that dropping it
  fails the parity assertion.
- Keys whose module groups several namespaces use indexed access
  (`fs: FilesystemApi['fs']`) to preserve exact identity and source order.

`config/tsconfig.web.json` and `tsconfig.tc.web.json` enumerate files by path,
so they need `src/preload/api/**/*` alongside the existing `api-types.ts` seed
or the web projects fail TS6307.

Verified by exact type identity, not assignability: 41 assertions of the form
`Equals<Now.X, Before.X>` against a frozen pre-split snapshot, covering every
exported name, plus a per-key pass over all 83 `PreloadApi` keys. All three
projects typecheck clean with those assertions active.

Verification note: these tsconfigs are `composite: true`, and `tsc --noEmit`
will reuse a stale `.tsbuildinfo` and report clean for a state that genuinely
fails. Every result above was produced after deleting the buildinfo, including
a negative control confirming the gate still fails on deliberate drift.

Drops the `max-lines` bypass and its baseline entry (ratchet 346 -> 345).
This commit is contained in:
Neil
2026-08-13 20:52:04 -07:00
committed by GitHub
parent 583ab1601b
commit 4221f8d429
51 changed files with 4040 additions and 3723 deletions
-1
View File
@@ -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
+1
View File
@@ -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",
+1
View File
@@ -5,6 +5,7 @@
"../src/renderer/src/**/*",
"../src/renderer/src/**/*.tsx",
"../src/preload/api-types.ts",
"../src/preload/api/**/*",
"../src/shared/**/*"
],
"compilerOptions": {
+181 -3722
View File
File diff suppressed because it is too large Load Diff
+62
View File
@@ -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<CodexRateLimitAccountsState>
add: (args?: {
runtime?: 'host' | 'wsl'
wslDistro?: string | null
}) => Promise<CodexRateLimitAccountsState>
reauthenticate: (args: { accountId: string }) => Promise<CodexRateLimitAccountsState>
remove: (args: { accountId: string }) => Promise<CodexRateLimitAccountsState>
select: (args: {
accountId: string | null
runtime?: 'host' | 'wsl'
wslDistro?: string | null
}) => Promise<CodexRateLimitAccountsState>
/** 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<Record<string, string>>
/** Drops launch records so a dismissed prompt stays dismissed across restarts. */
forgetStalePanes: (args: { ptyIds: string[] }) => Promise<void>
}
export type ClaudeAccountsApi = {
list: () => Promise<ClaudeRateLimitAccountsState>
add: (args?: {
runtime?: 'host' | 'wsl'
wslDistro?: string | null
}) => Promise<ClaudeRateLimitAccountsState>
cancelPendingLogin: () => Promise<boolean>
reauthenticate: (args: { accountId: string }) => Promise<ClaudeRateLimitAccountsState>
remove: (args: { accountId: string }) => Promise<ClaudeRateLimitAccountsState>
select: (args: {
accountId: string | null
runtime?: 'host' | 'wsl'
wslDistro?: string | null
}) => Promise<ClaudeRateLimitAccountsState>
}
export type GrokAccountsApi = {
getStatus: () => Promise<GrokAccountStatus>
}
export type MinimaxCredentialsApi = {
getStatus: () => Promise<{ configured: boolean }>
saveCookie: (cookie: string) => Promise<{ configured: boolean }>
clearCookie: () => Promise<{ configured: boolean }>
}
export type CodexConfigSyncApi = {
status: () => Promise<CodexConfigSyncStatus>
}
+51
View File
@@ -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<SetupScriptImportCandidate[]>
createIssueCommandRunner: (args: {
repoId: string
worktreePath: string
command: string
}) => Promise<WorktreeSetupLaunch>
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<void>
}
export type AgentHooksApi = {
claudeStatus: () => Promise<AgentHookInstallStatus>
openClaudeStatus: () => Promise<AgentHookInstallStatus>
codexStatus: () => Promise<AgentHookInstallStatus>
geminiStatus: () => Promise<AgentHookInstallStatus>
antigravityStatus: () => Promise<AgentHookInstallStatus>
ampStatus: () => Promise<AgentHookInstallStatus>
cursorStatus: () => Promise<AgentHookInstallStatus>
droidStatus: () => Promise<AgentHookInstallStatus>
commandCodeStatus: () => Promise<AgentHookInstallStatus>
grokStatus: () => Promise<AgentHookInstallStatus>
copilotStatus: () => Promise<AgentHookInstallStatus>
hermesStatus: () => Promise<AgentHookInstallStatus>
devinStatus: () => Promise<AgentHookInstallStatus>
}
+16
View File
@@ -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<SkillDiscoveryResult>
freshnessInventory: () => Promise<SkillFreshnessInventory>
startUpdateRun: (names: string[]) => Promise<SkillUpdateStartResult>
cancelUpdateRun: () => Promise<void>
acknowledgeUpdateRun: () => Promise<void>
getUpdateRun: () => Promise<SkillUpdateRun>
onUpdateRun: (callback: (run: SkillUpdateRun) => void) => () => void
}
+52
View File
@@ -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<AgentStatusIpcPayload[]>
inferInterrupt: (request: AgentInterruptInferenceRequest) => Promise<boolean>
/** 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<boolean>
/** 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<MigrationUnsupportedPtyEntry[]>
/** 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<void>
}
export type AgentAwakeApi = {
getStatus: () => Promise<ComputerAwakeStatus>
onChanged: (callback: (status: ComputerAwakeStatus) => void) => () => void
}
+59
View File
@@ -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<Snapshot extends UsageProviderSnapshot> = Pick<
Snapshot['summary'],
'scope' | 'range'
>
export type UsageProviderApi<Snapshot extends UsageProviderSnapshot, BreakdownKind> = {
getScanState: () => Promise<Snapshot['scanState']>
setEnabled: (args: { enabled: boolean }) => Promise<Snapshot['scanState']>
refresh: (args?: { force?: boolean }) => Promise<Snapshot['scanState']>
getSnapshot: (args: UsageQueryArgs<Snapshot> & { limit?: number }) => Promise<Snapshot>
getSummary: (args: UsageQueryArgs<Snapshot>) => Promise<Snapshot['summary']>
getDaily: (args: UsageQueryArgs<Snapshot>) => Promise<Snapshot['daily']>
getBreakdown: (
args: UsageQueryArgs<Snapshot> & { kind: BreakdownKind }
) => Promise<Snapshot['modelBreakdown']>
getRecentSessions: (
args: UsageQueryArgs<Snapshot> & { limit?: number }
) => Promise<Snapshot['recentSessions']>
}
export type ClaudeUsageApi = UsageProviderApi<ClaudeUsageSnapshot, ClaudeUsageBreakdownKind>
export type CodexUsageApi = UsageProviderApi<CodexUsageSnapshot, CodexUsageBreakdownKind>
export type OpenCodeUsageApi = UsageProviderApi<OpenCodeUsageSnapshot, OpenCodeUsageBreakdownKind>
export type RateLimitsApi = {
get: () => Promise<RateLimitState>
refresh: () => Promise<RateLimitState>
refreshCodexForTarget: (target: RateLimitRuntimeTarget) => Promise<RateLimitState>
consumeCodexResetCredit: () => Promise<CodexRateLimitResetResult>
refreshClaudeForTarget: (target: RateLimitRuntimeTarget) => Promise<RateLimitState>
setPollingInterval: (ms: number) => Promise<void>
fetchInactiveClaudeAccounts: () => Promise<void>
fetchInactiveCodexAccounts: () => Promise<void>
refreshMiniMax: () => Promise<RateLimitState>
refreshGrok: () => Promise<RateLimitState>
onUpdate: (callback: (state: RateLimitState) => void) => () => void
}
+37
View File
@@ -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<AiVaultListResult>
resolveSessionTitles: (args: AiVaultSessionTitlesArgs) => Promise<AiVaultSessionTitlesResult>
cancelListSessions: (args: { requestToken: string }) => Promise<void>
prepareSessionResume: (
args: AiVaultPrepareSessionResumeArgs
) => Promise<AiVaultPrepareSessionResumeResult>
/** Lists the Task subagent transcripts of one session, on demand. */
listSubagentSessions: (args: AiVaultSubagentListArgs) => Promise<AiVaultSubagentListResult>
/** Full first user prompt for copy/reuse (re-parses one transcript). */
getFirstUserPrompt: (args: AiVaultFirstUserPromptArgs) => Promise<AiVaultFirstUserPromptResult>
/** Moves a deletable session's transcript to the OS trash; local sessions only. */
deleteSession: (args: AiVaultDeleteSessionArgs) => Promise<AiVaultDeleteSessionResult>
/** Fires when any app window regains OS focus; returns an unsubscribe. */
onWindowFocused: (callback: () => void) => () => void
}
+82
View File
@@ -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<AppIdentity>
/** 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<string>
/** Relaunches the app (app.relaunch() + app.exit(0)) for settings that need a full restart to apply. */
relaunch: () => Promise<void>
/** Restarts Orca through the normal quit pipeline so daemon-backed terminal
* sessions survive and can reattach after the new process starts. */
restart: () => Promise<void>
/** Reloads the current app renderer through main so expected renderer
* teardown can be classified before Electron emits process-gone events. */
reload: () => Promise<void>
/** Stages the renderer's final state synchronously before unload. */
stageBeforeUnloadSync: (args: {
sessions: { state: WorkspaceSessionState; hostId?: ExecutionHostId }[]
ui: Partial<PersistedUIState>
}) => 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<void>
/** Resolves when the daemon PTY provider and hook receiver have either
* started or failed open for the first BrowserWindow. */
awaitFirstWindowStartupServices: () => Promise<void>
/** Reconciles legacy worker authority around persisted terminal reconnect. */
recoverLegacyWorkerTerminalsForRendererStartup: () => Promise<void>
/** Emits a startup benchmark marker when ORCA_STARTUP_DIAGNOSTICS is enabled. */
startupDiagnostic: (event: string, details?: Record<string, unknown>) => Promise<void>
/** 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<string | null>
/** Updates the macOS Dock unread badge. No-op on Windows/Linux. */
setUnreadDockBadgeCount: (count: number) => Promise<void>
/** Resolves the launch directory for global Floating Terminal tabs. */
getFloatingTerminalCwd: (args?: FloatingTerminalCwdRequest) => Promise<string>
/** Resolves Orca's app-owned directory for auto-created Floating Workspace
* markdown notes. */
getFloatingMarkdownDirectory: () => Promise<string>
/** Opens a native picker for markdown documents, rooted in the floating
* workspace, and authorizes the selected file for editor reads/writes. */
pickFloatingMarkdownDocument: () => Promise<MarkdownDocument | null>
/** Opens a native directory picker and authorizes the selected directory
* for Floating Workspace markdown file creation. */
pickFloatingWorkspaceDirectory: () => Promise<string | null>
/** Persists flag-gated terminal render evidence under app-owned userData. */
writeTerminalRenderDesyncEvidence: (
args: WriteTerminalRenderDesyncEvidenceArgs
) => Promise<WriteTerminalRenderDesyncEvidenceResult>
}
/** 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
}
+37
View File
@@ -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<Automation[]>
listRuns: (args?: { automationId?: string }) => Promise<AutomationRun[]>
listExternalManagers: () => Promise<ExternalAutomationManager[]>
listExternalRuns: (input: ExternalAutomationRunsInput) => Promise<ExternalAutomationRunsPage>
createExternal: (input: ExternalAutomationCreateInput) => Promise<void>
updateExternal: (input: ExternalAutomationUpdateInput) => Promise<void>
runExternalAction: (input: ExternalAutomationActionInput) => Promise<void>
create: (input: AutomationCreateInput) => Promise<Automation>
update: (args: { id: string; updates: AutomationUpdateInput }) => Promise<Automation>
delete: (args: { id: string }) => Promise<void>
runNow: (args: { id: string }) => Promise<AutomationRun>
runPrecheck: (args: {
automationId: string
runId: string
}) => Promise<AutomationPrecheckResult | null>
markDispatchResult: (result: AutomationDispatchResult) => Promise<AutomationRun>
snapshotWorkspaceName: (args: { workspaceId: string; displayName: string }) => Promise<number>
rendererReady: () => Promise<void>
onDispatchRequested: (callback: (request: AutomationDispatchRequest) => void) => () => void
}
+132
View File
@@ -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<boolean>
isGuestRegistered: (args: { browserPageId: string; webContentsId: number }) => Promise<boolean>
repairGuestRegistration: (args: {
browserPageId: string
workspaceId: string
worktreeId: string
sessionProfileId?: string | null
webContentsId: number
}) => Promise<boolean>
unregisterGuest: (args: { browserPageId: string }) => Promise<void>
openDevTools: (args: { browserPageId: string }) => Promise<boolean>
setViewportOverride: (args: {
browserPageId: string
override: BrowserViewportOverride | null
}) => Promise<boolean>
setAnnotationViewportBridge: (args: BrowserSetAnnotationViewportBridgeArgs) => Promise<boolean>
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<BrowserCertificateProceedResult>
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<boolean>
setGrabMode: (args: BrowserSetGrabModeArgs) => Promise<BrowserSetGrabModeResult>
awaitGrabSelection: (args: BrowserAwaitGrabSelectionArgs) => Promise<BrowserGrabResult>
cancelGrab: (args: BrowserCancelGrabArgs) => Promise<boolean>
captureSelectionScreenshot: (
args: BrowserCaptureSelectionScreenshotArgs
) => Promise<BrowserCaptureSelectionScreenshotResult>
extractHoverPayload: (args: BrowserExtractHoverArgs) => Promise<BrowserExtractHoverResult>
onGrabModeToggle: (callback: (browserPageId: string) => void) => () => void
onGrabActionShortcut: (
callback: (args: { browserPageId: string; key: 'c' | 's' }) => void
) => () => void
sessionListProfiles: () => Promise<BrowserSessionProfile[]>
sessionCreateProfile: (
args: {
scope: BrowserSessionProfileScope
label: string
} & BrowserSessionProfileCreateOptions
) => Promise<BrowserSessionProfile | null>
sessionDeleteProfile: (args: { profileId: string }) => Promise<boolean>
sessionImportCookies: (args: { profileId: string }) => Promise<BrowserCookieImportResult>
sessionResolvePartition: (args: { profileId: string | null }) => Promise<string | null>
sessionDetectBrowsers: () => Promise<DetectedBrowserInfo[]>
sessionImportFromBrowser: (args: {
profileId: string
browserFamily: string
browserProfile?: string
}) => Promise<BrowserCookieImportResult>
sessionClearDefaultCookies: () => Promise<boolean>
notifyActiveTabChanged: (args: { browserPageId: string }) => Promise<boolean>
}
export type DetectedBrowserProfileInfo = {
name: string
directory: string
}
export type DetectedBrowserInfo = {
family: BrowserSessionProfileSource['browserFamily']
label: string
profiles: DetectedBrowserProfileInfo[]
selectedProfile: string
}
+10
View File
@@ -0,0 +1,10 @@
import type { CliInstallStatus } from '../../shared/cli-install-types'
export type CliApi = {
getInstallStatus: () => Promise<CliInstallStatus>
install: () => Promise<CliInstallStatus>
remove: () => Promise<CliInstallStatus>
getWslInstallStatus: (args?: { distro?: string | null }) => Promise<CliInstallStatus>
installWsl: (args?: { distro?: string | null }) => Promise<CliInstallStatus>
removeWsl: (args?: { distro?: string | null }) => Promise<CliInstallStatus>
}
+38
View File
@@ -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<CrashReportRecord | null>
getLatestReport: () => Promise<CrashReportRecord | null>
dismiss: (args: { reportId: string }) => Promise<CrashReportRecord | null>
recordRendererError: (
args: ReactErrorBoundaryReportArgs
) => Promise<ReactErrorBoundaryReportResult>
recordBreadcrumb: (args: { name: string; data?: CrashReportBreadcrumbData }) => void
submit: (args: CrashReportSubmitArgs) => Promise<CrashReportSubmitResult>
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 }
>
}
+42
View File
@@ -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<void>
publishSnapshot: (snapshot: DashboardSnapshot) => Promise<void>
getPopoutOpen: () => Promise<boolean>
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<void>
onSnapshot: (callback: (snapshot: DashboardSnapshot) => void) => () => void
onViewRequested: (callback: (view: 'board' | 'map') => void) => () => void
revealAgent: (args: DashboardRevealAgentArgs) => Promise<void>
ackAgent: (paneKey: string) => Promise<void>
spawnAgent: (args: DashboardSpawnAgentArgs) => Promise<void>
sleepWorkspace: (args: DashboardSleepWorkspaceArgs) => Promise<void>
}
export type TerminalPreviewApi = {
connect: (
ptyId: string,
opts?: { scrollbackRows?: number }
) => Promise<TerminalPreviewConnectResult>
input: (ptyId: string, data: string) => Promise<boolean>
/** 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<void>
unsubscribe: (ptyId: string) => Promise<void>
onData: (callback: (payload: TerminalPreviewDataPayload) => void) => () => void
}
+37
View File
@@ -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<void>
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<void>
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
}
+72
View File
@@ -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<OrcaHooks['environmentRecipeDiagnostics']>
message?: string
}>
listRecipeCatalog: () => Promise<
{
repoId: string
repoName: string
repoPath: string
recipes: NonNullable<OrcaHooks['environmentRecipes']>
diagnostics: NonNullable<OrcaHooks['environmentRecipeDiagnostics']>
}[]
>
doctor: (args: { repoId: string; recipeId: string }) => Promise<EphemeralVmRecipeDoctorResult>
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<EphemeralVmRuntimeRecord[]>
attachWorkspace: (args: {
runtimeId: string
workspaceId: string
}) => Promise<EphemeralVmRuntimeRecord>
suspendWorkspace: (args: { workspaceId: string }) => Promise<EphemeralVmRuntimeRecord | null>
resumeWorkspace: (args: { workspaceId: string }) => Promise<EphemeralVmRuntimeRecord | null>
cleanup: (args: { runtimeId: string }) => Promise<EphemeralVmRuntimeRecord>
getCleanupCommand: (args: { runtimeId: string }) => Promise<{
runtimeId: string
command: string | null
payloadJson: string
cleanupDisabled: boolean
message?: string
}>
}
+208
View File
@@ -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<DirEntry[]>
readFile: (args: {
filePath: string
connectionId?: string
includeLocalLogMetadata?: boolean
}) => Promise<{
content: string
isBinary: boolean
isImage?: boolean
mimeType?: string
fileIdentity?: string
}>
readLocalLogTail: (args: LocalLogTailReadArgs) => Promise<LocalLogTailReadResult>
startLocalLogTail: (args: LocalLogTailWatchArgs) => Promise<void>
stopLocalLogTail: (args: { subscriptionId: string }) => Promise<void>
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<MarkdownDocument[]>
writeFile: (
args: {
filePath: string
content: string
connectionId?: string
} & SshMutationExpectation
) => Promise<void>
createFile: (
args: {
filePath: string
connectionId?: string
} & SshMutationExpectation
) => Promise<void>
createDir: (
args: { dirPath: string; connectionId?: string } & SshMutationExpectation
) => Promise<void>
rename: (
args: {
oldPath: string
newPath: string
connectionId?: string
} & SshMutationExpectation
) => Promise<void>
copy: (
args: {
sourcePath: string
destinationPath: string
connectionId?: string
} & SshMutationExpectation
) => Promise<void>
deletePath: (
args: {
targetPath: string
connectionId?: string
recursive?: boolean
} & SshMutationExpectation
) => Promise<void>
authorizeExternalPath: (args: { targetPath: string }) => Promise<void>
stat: (args: {
filePath: string
connectionId?: string
}) => Promise<{ size: number; isDirectory: boolean; mtime: number }>
pathExists: (args: { filePath: string; connectionId?: string }) => Promise<boolean>
listFiles: (args: {
rootPath: string
connectionId?: string
excludePaths?: string[]
requestToken?: string
}) => Promise<string[]>
cancelListFiles: (args: { requestToken: string }) => Promise<void>
search: (args: SearchOptions & { connectionId?: string }) => Promise<SearchResult>
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<void>
unwatchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
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
}
+123
View File
@@ -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<GitStatusResult>
cancelStatus: (args: { requestToken: string }) => Promise<void>
setStatusUpstreamRefWatch: (args: {
worktreeId: string
worktreePath: string
executionHostId: string
connectionId?: string
branch?: string
upstreamName?: string
}) => Promise<void>
submoduleStatus: (args: {
worktreePath: string
submodulePath: string
connectionId?: string
area?: GitStagingArea
}) => Promise<GitStatusResult>
checkIgnored: (args: {
worktreePath: string
paths: string[]
connectionId?: string
}) => Promise<string[]>
findHugeFoldersToIgnore: (args: { worktreePath: string }) => Promise<string[]>
history: (
args: { worktreePath: string; connectionId?: string } & GitHistoryOptions
) => Promise<GitHistoryResult>
conflictOperation: (args: {
worktreePath: string
connectionId?: string
}) => Promise<GitConflictOperation>
diff: (args: {
worktreePath: string
filePath: string
staged: boolean
compareAgainstHead?: boolean
connectionId?: string
}) => Promise<GitDiffResult>
branchCompare: (args: {
worktreePath: string
baseRef: string
connectionId?: string
}) => Promise<GitBranchCompareResult>
commitCompare: (args: {
worktreePath: string
commitId: string
connectionId?: string
}) => Promise<GitCommitCompareResult>
upstreamStatus: (args: {
worktreePath: string
connectionId?: string
pushTarget?: GitPushTarget
}) => Promise<GitUpstreamStatus>
branchDiff: (args: {
worktreePath: string
compare: {
baseRef: string
baseOid: string
headOid: string
mergeBase: string
}
filePath: string
oldPath?: string
connectionId?: string
}) => Promise<GitDiffResult>
commitDiff: (args: {
worktreePath: string
commitOid: string
parentOid?: string | null
filePath: string
oldPath?: string
connectionId?: string
}) => Promise<GitDiffResult>
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<string | null>
remoteCommitUrl: (args: {
worktreePath: string
sha: string
connectionId?: string
}) => Promise<string | null>
}
+128
View File
@@ -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<boolean>
abortMerge: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
abortRebase: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
fetch: (args: {
worktreePath: string
connectionId?: string
pushTarget?: GitPushTarget
}) => Promise<void>
syncFork: (args: {
worktreePath: string
connectionId?: string
expectedUpstream: GitForkSyncExpectedUpstream
}) => Promise<GitForkSyncResult>
push: (args: {
worktreePath: string
publish?: boolean
forceWithLease?: boolean
connectionId?: string
pushTarget?: GitPushTarget
}) => Promise<void>
pull: (args: {
worktreePath: string
connectionId?: string
pushTarget?: GitPushTarget
}) => Promise<void>
fastForward: (args: {
worktreePath: string
connectionId?: string
pushTarget?: GitPushTarget
}) => Promise<void>
rebaseFromBase: (args: {
worktreePath: string
baseRef: string
connectionId?: string
}) => Promise<void>
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<Record<TuiAgent, string>>
}) => Promise<
| { success: true; message: string; agentLabel?: string }
| { success: false; error: string; canceled?: boolean }
>
cancelGenerateCommitMessage: (args: {
worktreePath: string
connectionId?: string
}) => Promise<void>
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<Record<TuiAgent, string>>
}) => 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<void>
stage: (args: { worktreePath: string; filePath: string; connectionId?: string }) => Promise<void>
bulkStage: (args: {
worktreePath: string
filePaths: string[]
connectionId?: string
}) => Promise<void>
unstage: (args: {
worktreePath: string
filePath: string
connectionId?: string
}) => Promise<void>
bulkUnstage: (args: {
worktreePath: string
filePaths: string[]
connectionId?: string
}) => Promise<void>
discard: (args: {
worktreePath: string
filePath: string
connectionId?: string
}) => Promise<void>
bulkDiscard: (args: {
worktreePath: string
filePaths: string[]
connectionId?: string
}) => Promise<void>
}
+196
View File
@@ -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<GitHubViewer | null>
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<PRInfo | null>
refreshPRNow: (args: { candidate: GitHubPRRefreshCandidate }) => Promise<PRRefreshOutcome>
enqueuePRRefresh: (args: {
candidate: GitHubPRRefreshCandidate
reason: GitHubPRRefreshReason
priority?: number
}) => Promise<GitHubPRRefreshEnqueueResult | false>
reportVisiblePRRefreshCandidates: (args: {
candidates: GitHubPRRefreshCandidate[]
generation: number
}) => Promise<boolean>
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<GitHubPRFileContents>
prChecks: (
args: GitHubRepoSelectorArgs & {
prNumber: number
headSha?: string
prRepo?: GitHubOwnerRepo | null
noCache?: boolean
}
) => Promise<PRCheckDetail[]>
prCheckDetails: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
checkRunId?: number
workflowRunId?: number
checkName?: string
url?: string | null
prRepo?: GitHubOwnerRepo | null
}) => Promise<PRCheckRunDetails | null>
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<PRComment[]>
setPRCommentReaction: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
reactionSubjectId: string
content: GitHubReactionContent
reacted: boolean
prRepo?: GitHubOwnerRepo | null
}) => Promise<boolean>
resolveReviewThread: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
threadId: string
resolve: boolean
prRepo?: GitHubOwnerRepo | null
}) => Promise<boolean>
setPRFileViewed: (
args: GitHubRepoSelectorArgs & {
prNumber: number
prRepo?: GitHubOwnerRepo | null
pullRequestId: string
path: string
viewed: boolean
}
) => Promise<boolean>
updatePRTitle: (args: {
repoPath: string
repoId?: string
prNumber: number
title: string
prRepo?: GitHubOwnerRepo | null
}) => Promise<boolean>
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<GitHubCommentResult>
addPRReviewComment: (
args: GitHubPRReviewCommentInput & {
repoId?: string
sourceContext?: TaskSourceContext | null
}
) => Promise<GitHubCommentResult>
checkOrcaStarred: () => Promise<boolean | null>
starOrca: (source: AppStarSource) => Promise<boolean>
/**
* 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<GetRateLimitResult>
/** 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<GhAuthDiagnostic>
}
+167
View File
@@ -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<IssueInfo | null>
workItem: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
number: number
type?: 'issue' | 'pr'
}) => Promise<Omit<GitHubWorkItem, 'repoId'> | null>
workItemByOwnerRepo: (args: {
repoPath: string
repoId?: string
owner: string
repo: string
host?: string
number: number
type: 'issue' | 'pr'
}) => Promise<Omit<GitHubWorkItem, 'repoId'> | null>
workItemDetails: (
args: GitHubRepoSelectorArgs & {
number: number
type?: 'issue' | 'pr'
}
) => Promise<GitHubWorkItemDetails | null>
notifyWorkItemMutated: (args: {
repoPath: string
repoId?: string
type: 'issue' | 'pr'
number: number
}) => Promise<boolean>
listIssues: (args: { repoPath: string; repoId?: string; limit?: number }) => Promise<IssueInfo[]>
createIssue: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
title: string
body: string
labels?: string[]
assignees?: string[]
}) => Promise<GitHubCreateIssueResult>
countWorkItems: (args: { repoPath: string; repoId?: string; query?: string }) => Promise<number>
listWorkItems: (args: {
repoPath: string
repoId?: string
limit?: number
query?: string
page?: number
noCache?: boolean
}) => Promise<ListWorkItemsResult<Omit<GitHubWorkItem, 'repoId'>>>
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<GitHubCommentResult>
listLabels: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
}) => Promise<string[]>
listAssignableUsers: (args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
}) => Promise<GitHubAssignableUser[]>
/** 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<ListAccessibleProjectsResult>
resolveProjectRef: (args: ResolveProjectRefArgs) => Promise<ResolveProjectRefResult>
listProjectViews: (args: ListProjectViewsArgs) => Promise<ListProjectViewsResult>
getProjectViewTable: (args: GetProjectViewTableArgs) => Promise<GetProjectViewTableResult>
projectWorkItemDetailsBySlug: (
args: ProjectWorkItemDetailsBySlugArgs
) => Promise<ProjectWorkItemDetailsBySlugResult>
updateProjectItemField: (args: UpdateProjectItemFieldArgs) => Promise<GitHubProjectMutationResult>
clearProjectItemField: (args: ClearProjectItemFieldArgs) => Promise<GitHubProjectMutationResult>
updateIssueBySlug: (args: UpdateIssueBySlugArgs) => Promise<GitHubProjectMutationResult>
updatePullRequestBySlug: (
args: UpdatePullRequestBySlugArgs
) => Promise<GitHubProjectMutationResult>
addIssueCommentBySlug: (
args: AddIssueCommentBySlugArgs
) => Promise<GitHubProjectCommentMutationResult>
updateIssueCommentBySlug: (
args: UpdateIssueCommentBySlugArgs
) => Promise<GitHubProjectMutationResult>
deleteIssueCommentBySlug: (
args: DeleteIssueCommentBySlugArgs
) => Promise<GitHubProjectMutationResult>
listLabelsBySlug: (args: ListLabelsBySlugArgs) => Promise<ListLabelsBySlugResult>
listAssignableUsersBySlug: (
args: ListAssignableUsersBySlugArgs
) => Promise<ListAssignableUsersBySlugResult>
listIssueTypesBySlug: (args: ListIssueTypesBySlugArgs) => Promise<ListIssueTypesBySlugResult>
updateIssueTypeBySlug: (args: UpdateIssueTypeBySlugArgs) => Promise<GitHubProjectMutationResult>
}
+172
View File
@@ -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<GitLabViewer | null>
diagnoseAuth: () => Promise<GitLabAuthDiagnostic>
rateLimit: (args?: { force?: boolean; host?: string | null }) => Promise<GetGitLabRateLimitResult>
projectSlug: (args: GitLabRepoSelectorArgs) => Promise<GitLabProjectRef | null>
mrForBranch: (
args: GitLabRepoSelectorArgs & {
branch: string
linkedMRIid?: number | null
}
) => Promise<MRInfo | null>
mr: (args: GitLabRepoSelectorArgs & { iid: number }) => Promise<MRInfo | null>
listMRs: (
args: GitLabRepoSelectorArgs & {
state?: MRListState
page?: number
perPage?: number
query?: string
}
) => Promise<ListMergeRequestsResult>
/** 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<ListMergeRequestsResult>
issue: (args: GitLabRepoSelectorArgs & { number: number }) => Promise<GitLabIssueInfo | null>
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<GitLabCommentResult>
listLabels: (args: GitLabRepoSelectorArgs) => Promise<string[]>
listAssignableUsers: (args: GitLabRepoSelectorArgs) => Promise<GitLabAssignableUser[]>
/** Cross-project user-scoped todos (gitlab.com/dashboard/todos). */
todos: (args: GitLabRepoSelectorArgs) => Promise<GitLabTodo[]>
/** Aggregated dialog payload — body + discussions + pipeline jobs. */
workItemDetails: (
args: GitLabRepoSelectorArgs & {
iid: number
type: 'issue' | 'mr'
}
) => Promise<GitLabWorkItemDetails | null>
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<GitLabMRReviewersUpdateResult>
addMRComment: (
args: GitLabRepoSelectorArgs & {
iid: number
body: string
}
) => Promise<GitLabCommentResult>
addMRInlineComment: (
args: GitLabRepoSelectorArgs & {
iid: number
input: GitLabMRInlineCommentInput
projectRef?: GitLabProjectRef | null
}
) => Promise<GitLabCommentResult>
resolveMRDiscussion: (
args: GitLabRepoSelectorArgs & {
iid: number
discussionId: string
resolved: boolean
}
) => Promise<GitLabDiscussionResolveResult>
jobTrace: (
args: GitLabRepoSelectorArgs & {
jobId: number
projectRef?: GitLabProjectRef | null
/** Bound the trace in main to a readable excerpt (see gitLabJobTraceToLogExcerpt). */
logExcerpt?: boolean
}
) => Promise<GitLabJobTraceResult>
retryJob: (
args: GitLabRepoSelectorArgs & {
jobId: number
projectRef?: GitLabProjectRef | null
}
) => Promise<GitLabRetryJobResult>
workItemByPath: (
args: GitLabRepoSelectorArgs & {
host: string
path: string
iid: number
type: 'issue' | 'mr'
}
) => Promise<Omit<GitLabWorkItem, 'repoId'> | null>
}
+31
View File
@@ -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<HostedReviewInfo | null>
getCreationEligibility: (
args: HostedReviewCreationEligibilityArgs
) => Promise<HostedReviewCreationEligibility>
create: (args: CreateHostedReviewArgs) => Promise<CreateHostedReviewResult>
createStacked: (args: CreateStackedHostedReviewArgs) => Promise<CreateStackedHostedReviewResult>
}
export type BitbucketApi = {
connect: (
args: BitbucketConnectArgs
) => Promise<{ ok: true; account: string | null } | { ok: false; error: string }>
disconnect: () => Promise<void>
status: () => Promise<BitbucketConnectionStatus>
}
+84
View File
@@ -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<void>
selectSite: (args: { siteId: JiraSiteSelection }) => Promise<JiraConnectionStatus>
status: () => Promise<JiraConnectionStatus>
readStatus: () => Promise<JiraConnectionStatus>
testConnection: (args?: {
siteId?: string
}) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }>
searchIssues: (args: {
jql: string
limit?: number
siteId?: JiraSiteSelection
requestId?: string
}) => Promise<JiraIssue[]>
cancelSearchIssues: (args: { requestId: string }) => Promise<void>
listIssues: (args?: {
filter?: JiraIssueFilter
limit?: number
siteId?: JiraSiteSelection
}) => Promise<JiraIssue[]>
getIssue: (args: { key: string; siteId?: string }) => Promise<JiraIssue | null>
lookupIssueSummary: (args: {
key: string
siteId: string
requestId?: string
}) => Promise<JiraIssue | null>
cancelIssueSummary: (args: { requestId: string }) => Promise<void>
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<JiraComment[]>
listProjects: (args?: { siteId?: JiraSiteSelection }) => Promise<JiraProject[]>
listIssueTypes: (args: { projectIdOrKey: string; siteId?: string }) => Promise<JiraIssueType[]>
listCreateFields: (args: {
projectIdOrKey: string
issueTypeId: string
siteId?: string
}) => Promise<JiraCreateField[]>
listPriorities: (args?: { siteId?: string }) => Promise<JiraPriority[]>
listAssignableUsers: (args: {
key: string
query?: string
siteId?: string
}) => Promise<JiraUser[]>
listTransitions: (args: { key: string; siteId?: string }) => Promise<JiraTransition[]>
getProjectStatusOrder: (args: {
projectKey: string
siteId?: string
}) => Promise<JiraProjectStatusOrder>
}
+128
View File
@@ -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<void>
selectWorkspace: (args: {
workspaceId: LinearWorkspaceSelection
}) => Promise<LinearConnectionStatus>
status: () => Promise<LinearConnectionStatus>
testConnection: (args?: {
workspaceId?: string
}) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }>
searchIssues: (args: {
query: string
limit?: number
workspaceId?: LinearWorkspaceSelection
}) => Promise<LinearIssue[]>
listIssues: (args?: {
filter?: 'assigned' | 'created' | 'all' | 'completed'
limit?: number
workspaceId?: LinearWorkspaceSelection
attributeFilter?: LinearIssueAttributeFilter
}) => Promise<LinearCollectionResult<LinearIssue>>
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<LinearIssue | null>
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<LinearComment[]>
listTeams: (args?: { workspaceId?: LinearWorkspaceSelection }) => Promise<LinearTeam[]>
listProjects: (args?: {
query?: string
limit?: number
workspaceId?: LinearWorkspaceSelection
force?: boolean
}) => Promise<LinearCollectionResult<LinearProjectSummary>>
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<LinearProjectDetail | null>
listProjectIssues: (args: {
projectId: string
limit?: number
workspaceId: string
force?: boolean
}) => Promise<LinearCollectionResult<LinearIssue>>
listCustomViews: (args: {
model: LinearCustomViewModel
limit?: number
workspaceId?: LinearWorkspaceSelection
force?: boolean
}) => Promise<LinearCollectionResult<LinearCustomViewSummary>>
getCustomView: (args: {
viewId: string
model: LinearCustomViewModel
workspaceId: string
force?: boolean
}) => Promise<LinearCustomViewSummary | null>
listCustomViewIssues: (args: {
viewId: string
limit?: number
workspaceId: string
force?: boolean
}) => Promise<LinearCollectionResult<LinearIssue>>
listCustomViewProjects: (args: {
viewId: string
limit?: number
workspaceId: string
force?: boolean
}) => Promise<LinearCollectionResult<LinearProjectSummary>>
teamStates: (args: { teamId: string; workspaceId?: string }) => Promise<LinearWorkflowState[]>
teamLabels: (args: { teamId: string; workspaceId?: string }) => Promise<LinearLabel[]>
teamMembers: (args: { teamId: string; workspaceId?: string }) => Promise<LinearMember[]>
}
+86
View File
@@ -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<boolean>
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<boolean>
/** Fires (throttled, once per session) when an unpaired phone repeatedly fails direct-transport auth. */
onUnpairedDeviceAuthFailure?: (callback: () => void) => () => void
}
+71
View File
@@ -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<NativeChatReadSessionResult>
/** 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
}
+28
View File
@@ -0,0 +1,28 @@
import type { OnboardingState } from '../../shared/types'
export type OnboardingApi = {
get: () => Promise<OnboardingState>
// Why: main merges the checklist field-by-field, so a partial checklist is fine.
update: (
updates: Partial<Omit<OnboardingState, 'checklist'>> & {
checklist?: Partial<OnboardingState['checklist']>
}
) => Promise<OnboardingState>
}
export type StarNagApi = {
onShow: (
callback: (payload?: { mode?: 'gh' | 'web'; surface?: 'card' | 'toast' }) => void
) => () => void
onHide: (callback: () => void) => () => void
dismiss: () => Promise<void>
later: () => Promise<void>
complete: () => Promise<void>
disable: () => Promise<void>
openWeb: () => Promise<void>
starOrca: () => Promise<boolean>
forceShow: () => Promise<void>
agentValueMoment: () => Promise<{ status: 'ready'; mode: 'gh' | 'web' } | { status: 'skipped' }>
showAgentValueMoment: () => Promise<void>
onboardingCompleted: () => Promise<void>
}
+59
View File
@@ -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<OrcaProfileListResult>
authStatus: () => Promise<OrcaProfileAuthStatus>
createLocal: (args?: CreateLocalOrcaProfileArgs) => Promise<CreateLocalOrcaProfileResult>
createCloudLinked: (
args?: CreateCloudLinkedOrcaProfileArgs
) => Promise<CreateCloudLinkedOrcaProfileResult>
switchProfile: (args: SwitchOrcaProfileArgs) => Promise<SwitchOrcaProfileResult>
transferProject: (
args: TransferOrcaProfileProjectArgs
) => Promise<TransferOrcaProfileProjectResult>
findProjectProfiles: (
args: FindOrcaProfileProjectsByPathArgs
) => Promise<FindOrcaProfileProjectsByPathResult>
connectCurrent: () => Promise<ConnectCurrentOrcaProfileResult>
refreshAuth: () => Promise<RefreshCurrentOrcaProfileAuthResult>
signOutCurrent: () => Promise<SignOutCurrentOrcaProfileResult>
selectOrg: (args: SelectOrcaProfileOrgArgs) => Promise<SelectOrcaProfileOrgResult>
orgMembersList: (args: OrcaProfileOrgMembersListArgs) => Promise<OrcaProfileOrgMembersListResult>
orgMemberInvite: (
args: OrcaProfileOrgMemberInviteArgs
) => Promise<OrcaProfileOrgMemberMutationResult>
orgInviteRevoke: (
args: OrcaProfileOrgInviteRevokeArgs
) => Promise<OrcaProfileOrgMemberMutationResult>
orgMemberChangeRole: (
args: OrcaProfileOrgMemberChangeRoleArgs
) => Promise<OrcaProfileOrgMemberMutationResult>
orgMemberRemove: (
args: OrcaProfileOrgMemberRemoveArgs
) => Promise<OrcaProfileOrgMemberMutationResult>
}
+54
View File
@@ -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<NotificationDispatchResult>
dismiss: (ids: string[]) => Promise<NotificationDismissResult>
openSystemSettings: () => Promise<void>
getPermissionStatus: () => Promise<NotificationPermissionStatusResult>
probeDelivery: (args?: { force?: boolean }) => Promise<NotificationDeliveryProbeResult>
playSound: (options?: { force?: boolean; volume?: number }) => Promise<NotificationSoundResult>
}
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<void>
releasePending: (claimId: number) => Promise<void>
dismiss: () => Promise<void>
}
export type DeveloperPermissionsApi = {
getStatus: () => Promise<DeveloperPermissionState[]>
request: (args: { id: DeveloperPermissionId }) => Promise<DeveloperPermissionRequestResult>
openSettings: (args: { id: DeveloperPermissionId }) => Promise<void>
testLocalNetworkConnection: (args: {
host: string
port: number
}) => Promise<LocalNetworkConnectionTestResult>
}
export type ComputerUsePermissionsApi = {
getStatus: () => Promise<ComputerUsePermissionStatusResult>
openSetup: (args?: { id?: ComputerUsePermissionId }) => Promise<ComputerUsePermissionSetupResult>
reset: () => Promise<ComputerUsePermissionResetResult>
}
+8
View File
@@ -0,0 +1,8 @@
import type { CustomPet } from '../../shared/types'
export type PetApi = {
import: () => Promise<CustomPet | null>
importPetBundle: () => Promise<CustomPet | null>
read: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise<ArrayBuffer | null>
delete: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise<void>
}
+191
View File
@@ -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<PluginHostListEntry[]>
listLanguagePacks: () => Promise<PluginLanguagePackRegistration[]>
/** Records the consent-dialog answer; approval is keyed to the plugin's
* current capability and trusted-worker fingerprint. */
consent: (args: PluginConsentRequest) => Promise<PluginHostListEntry[]>
setEnabled: (args: { pluginKey: string; enabled: boolean }) => Promise<PluginHostListEntry[]>
/** 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<PluginPanelEntry | null>
invokeCommand: (args: {
pluginKey: string
commandId: string
args?: unknown
}) => Promise<unknown>
/** 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<PluginPanelActionOutcome>
install: (source: PluginHostInstallSource) => Promise<PluginHostInstallResult>
listMarketplaces: () => Promise<PluginMarketplaceHostSourceState[]>
addMarketplace: (source: PluginMarketplaceGitSource) => Promise<PluginMarketplaceHostSourceState>
removeMarketplace: (args: { sourceId: string }) => Promise<PluginMarketplaceHostSourceState[]>
refreshMarketplaces: (args?: { sourceId?: string }) => Promise<PluginMarketplaceHostSourceState[]>
listMarketplacePlugins: () => Promise<PluginMarketplaceHostListing[]>
previewMarketplacePlugin: (args: {
marketplaceSourceId: string
pluginKey: string
}) => Promise<PluginMarketplaceHostInstallPreview>
installMarketplacePlugin: (
preview: Pick<
PluginMarketplaceHostInstallPreview,
'marketplaceSourceId' | 'marketplaceCommit' | 'pluginKey' | 'resolvedCommit'
>
) => Promise<PluginHostInstallResult>
previewMarketplaceUpdate: (args: {
pluginKey: string
}) => Promise<PluginMarketplaceHostInstallPreview>
rollbackMarketplacePlugin: (args: { pluginKey: string }) => Promise<PluginHostInstallResult>
remove: (args: { pluginKey: string }) => Promise<PluginHostListEntry[]>
getLogs: (args: { pluginKey: string }) => Promise<PluginHostLogLine[]>
/** Re-discovers after settings edits (feature flag, dev paths). */
refresh: () => Promise<PluginHostListEntry[]>
/** Fires whenever installed plugins, worker states, panels, or content packs change. */
onChanged: (callback: (event: PluginChangeEvent) => void) => () => void
}
+55
View File
@@ -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<PreflightStatus>
detectAgents: (args?: PreflightRuntimeContext) => Promise<string[]>
refreshAgents: (args?: PreflightRuntimeContext) => Promise<RefreshAgentsResult>
detectRemoteAgents: (args: { connectionId: string }) => Promise<string[]>
detectRemoteWindowsTerminalCapabilities: (args: { connectionId: string }) => Promise<{
wslAvailable: boolean
wslDistros: string[]
pwshAvailable: boolean
gitBashAvailable: boolean
hostPlatform: NodeJS.Platform | null
}>
}
+224
View File
@@ -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<string, string>
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<boolean>
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<void>
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<string, number>
}) => 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<PtyRendererDeliveryHealthReply>
/** 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<boolean>
getForegroundProcess: (id: string) => Promise<string | null>
inspectProcess: (id: string) => Promise<{
foregroundProcess: string | null
hasChildProcesses: boolean
unavailable?: true
}>
confirmForegroundProcess: (id: string) => Promise<string | null>
getCwd: (id: string) => Promise<string>
getSize: (id: string) => Promise<{ cols: number; rows: number } | null>
listSessions: () => Promise<PtyListedSession[]>
getAuthoritativeBufferSnapshotCapabilities?: (
ids: string[]
) => Promise<{ id: string; authoritative: boolean | null }[]>
hasPty: (id: string) => Promise<boolean | null>
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<void>
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<TerminalSideEffectBatch | null>
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<number>
settlePaneSerializer: (paneKey: string, gen: number) => Promise<void>
clearPendingPaneSerializer: (paneKey: string, gen: number) => Promise<void>
reportRendererSerializerReady?: (ptyId: string) => Promise<void>
management: PtyManagementApi
}
+30
View File
@@ -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 }>
}
+169
View File
@@ -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<Repo[]>
listForExecutionHost?: (args: ListReposForExecutionHostArgs) => Promise<HostRepoCatalogSnapshot>
// 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<void>
// Forget a project on one execution host only, leaving the same repo id on other hosts intact.
removeForHost: (args: { repoId: string; hostId: string }) => Promise<void>
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<Repo>
pickFolder: () => Promise<string | null>
pickFolders: () => Promise<string[]>
pickDirectory: () => Promise<string | null>
clone: (args: { url: string; destination: string }) => Promise<Repo>
cloneRemote: (args: { connectionId: string; url: string; destination: string }) => Promise<Repo>
createRemote: (args: {
connectionId: string
parentPath: string
name: string
kind: 'git' | 'folder'
}) => Promise<{ repo: Repo } | { error: string }>
cloneAbort: () => Promise<void>
// 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<boolean>
getDefaultCreateProjectParent: () => Promise<string>
onCloneProgress: (callback: (data: { phase: string; percent: number }) => void) => () => void
getGitUsername: (args: { repoId: string }) => Promise<string>
getBaseRefDefault: (args: {
repoId: string
hostId?: ExecutionHostId
}) => Promise<BaseRefDefaultResult>
searchBaseRefs: (args: {
repoId: string
query: string
limit?: number
hostId?: ExecutionHostId
}) => Promise<string[]>
searchBaseRefDetails: (args: {
repoId: string
query: string
limit?: number
hostId?: ExecutionHostId
}) => Promise<BaseRefSearchResult[]>
onChanged: (callback: () => void) => () => void
}
export type ProjectsApi = {
list: () => Promise<Project[]>
update: (args: ProjectUpdateArgs) => Promise<Project | null>
listHostSetups: () => Promise<ProjectHostSetup[]>
createHostSetup: (args: ProjectHostSetupCreateArgs) => Promise<ProjectHostSetupCreateResult>
setupExistingFolder: (args: ProjectHostSetupExistingFolderArgs) => Promise<ProjectHostSetupResult>
updateHostSetup: (args: ProjectHostSetupUpdateArgs) => Promise<ProjectHostSetupUpdateResult>
deleteHostSetup: (args: ProjectHostSetupDeleteArgs) => Promise<ProjectHostSetupDeleteResult>
}
export type ProjectGroupsApi = {
list: () => Promise<ProjectGroup[]>
create: (args: {
name: string
parentPath?: string | null
connectionId?: string | null
parentGroupId?: string | null
createdFrom?: ProjectGroup['createdFrom']
}) => Promise<ProjectGroup>
update: (args: {
groupId: string
updates: Partial<Pick<ProjectGroup, 'name' | 'isCollapsed' | 'tabOrder' | 'color'>>
}) => Promise<ProjectGroup | null>
delete: (args: { groupId: string }) => Promise<boolean>
moveProject: (args: {
projectId: string
groupId: string | null
order?: number
}) => Promise<Repo | null>
scanNested: (args: {
path: string
connectionId?: string
scanId?: string
options?: Record<string, unknown>
}) => Promise<NestedRepoScanResult>
cancelNestedScan: (args: { scanId: string }) => Promise<boolean>
onNestedScanProgress: (
callback: (data: { scanId: string; scan: NestedRepoScanResult }) => void
) => () => void
importNested: (args: {
parentPath: string
groupName: string
projectPaths: string[]
connectionId?: string
scanId?: string
mode: ProjectGroupImportMode
}) => Promise<ProjectGroupImportResult>
}
+116
View File
@@ -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<ArrayBufferLike>) => void
}
export type RuntimeApi = {
runtime: {
syncWindowGraph: (graph: RuntimeSyncWindowGraph) => Promise<RuntimeSyncWindowGraphResult>
getStatus: () => Promise<RuntimeStatus>
call: (args: { method: string; params?: unknown }) => Promise<RuntimeRpcResponse<unknown>>
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<PublicKnownRuntimeEnvironment[]>
addFromPairingCode: (args: {
name: string
pairingCode: string
}) => Promise<{ environment: PublicKnownRuntimeEnvironment }>
verifyAndAddFromPairingCode: (args: {
name: string
pairingCode: string
allowLoopback?: boolean
}) => Promise<VerifyAndAddRuntimeEnvironmentResult>
resolve: (args: { selector: string }) => Promise<PublicKnownRuntimeEnvironment>
remove: (args: { selector: string }) => Promise<{ removed: PublicKnownRuntimeEnvironment }>
disconnect: (args: {
selector: string
}) => Promise<{ disconnected: PublicKnownRuntimeEnvironment }>
connect: (args: {
selector: string
timeoutMs?: number
}) => Promise<RuntimeRpcResponse<RuntimeStatus>>
getStatus: (args: {
selector: string
timeoutMs?: number
}) => Promise<RuntimeRpcResponse<RuntimeStatus>>
// Why: system resume / browser online advance pending shared-control reconnect timers only.
retryConnectionsNow?: () => Promise<void>
call: (args: {
selector: string
method: string
params?: unknown
timeoutMs?: number
expectedEnvironmentPairingRevision?: number
}) => Promise<RuntimeRpcResponse<unknown>>
subscribe: (
args: {
selector: string
method: string
params?: unknown
timeoutMs?: number
expectedEnvironmentPairingRevision?: number
},
callbacks: {
onResponse: (response: RuntimeRpcResponse<unknown>) => void
onBinary?: (bytes: Uint8Array<ArrayBufferLike>) => void
onError?: (error: { code: string; message: string }) => void
onClose?: () => void
}
) => Promise<RuntimeEnvironmentSubscriptionHandle>
}
wsl: {
isAvailable: () => Promise<boolean>
listDistros: () => Promise<string[]>
}
pwsh: {
isAvailable: () => Promise<boolean>
}
gitBash: {
isAvailable: () => Promise<boolean>
}
}
+35
View File
@@ -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<GlobalSettings>
/** Synchronous persisted-settings read for startup decisions that can't wait for async hydration. Blocking IPC — call sparingly. */
getSync: () => GlobalSettings | null
set: (args: Partial<GlobalSettings>) => Promise<GlobalSettings>
setActiveRuntimeEnvironmentPreference: (args: {
environmentId: string | null
}) => Promise<GlobalSettings>
updatePRBotAuthorOverride: (args: { author: string; isBot: boolean }) => Promise<GlobalSettings>
listFonts: () => Promise<string[]>
previewGhosttyImport: () => Promise<GhosttyImportPreview>
previewWarpThemeImport: (source: WarpThemeImportSource) => Promise<WarpThemeImportPreview>
/** Subscribe to out-of-band settings updates (e.g. View > Appearance toggles) to stay in sync with main. */
onChanged: (callback: (updates: Partial<GlobalSettings>) => void) => () => void
}
export type KeybindingsApi = {
get: () => Promise<KeybindingFileSnapshot>
ensureFile: () => Promise<KeybindingFileSnapshot>
setAction: (args: {
actionId: KeybindingActionId
bindings: string[] | null
}) => Promise<KeybindingFileSnapshot>
reload: () => Promise<KeybindingFileSnapshot>
openFile: () => Promise<KeybindingFileSnapshot>
revealFile: () => Promise<KeybindingFileSnapshot>
onChanged: (callback: (snapshot: KeybindingFileSnapshot) => void) => () => void
}
+32
View File
@@ -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<void>
openInFileManager: (path: string) => Promise<ShellOpenLocalPathResult>
openInExternalEditor: (
request: ShellOpenExternalEditorRequest
) => Promise<ShellOpenExternalEditorResult>
openUrl: (url: string) => Promise<void>
openFilePath: (path: string) => Promise<boolean>
openFileUri: (uri: string) => Promise<void>
pathExists: (path: string) => Promise<boolean>
pickAttachment: () => Promise<string | null>
pickImage: () => Promise<string | null>
pickRepoIconImage: () => Promise<{
dataUrl: string
fileName: string
} | null>
pickAudio: () => Promise<string | null>
pickDirectory: (args: { defaultPath?: string }) => Promise<string | null>
copyFile: (args: { srcPath: string; destPath: string }) => Promise<void>
}
+33
View File
@@ -0,0 +1,33 @@
import type {
SpeechErrorEvent,
SpeechLifecycleEvent,
SpeechModelManifest,
SpeechModelState,
SpeechTranscriptEvent
} from '../../shared/speech-types'
export type SpeechApi = {
getCatalog: () => Promise<SpeechModelManifest[]>
getModelStates: () => Promise<SpeechModelState[]>
getOpenAiApiKeyStatus: () => Promise<{ configured: boolean }>
saveOpenAiApiKey: (apiKey: string) => Promise<{ configured: boolean }>
clearOpenAiApiKey: () => Promise<{ configured: boolean }>
downloadModel: (modelId: string) => Promise<void>
cancelDownload: (modelId: string) => Promise<void>
deleteModel: (modelId: string) => Promise<void>
startDictation: (
modelId: string,
hotwords: string[] | undefined,
sessionId: string
) => Promise<void>
feedAudio: (samples: Float32Array, sampleRate: number, sessionId?: string) => Promise<void>
stopDictation: (sessionId?: string) => Promise<void>
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
}
+78
View File
@@ -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<SshTarget[]>
// Removed-target id → last known label, for a friendly host name on workspaces still pinned to a removed target.
listRemovedTargetLabels: () => Promise<Record<string, string>>
addTarget: (args: { target: Omit<SshTarget, 'id'> }) => Promise<SshTargetAddResult>
updateTarget: (args: {
id: string
updates: Partial<Omit<SshTarget, 'id'>>
}) => Promise<SshTarget>
removeTarget: (args: { id: string }) => Promise<void>
importConfig: (args?: { reAdopt?: boolean }) => Promise<SshConfigImportResult>
listConfigHosts: (args?: SshConfigHostListArgs) => Promise<SshConfigHostListResult>
resolveConfigHost: (args: { alias: string }) => Promise<SshConfigHostResolution | null>
connect: (args: { targetId: string }) => Promise<SshConnectionState | null>
disconnect: (args: { targetId: string }) => Promise<void>
terminateSessions: (args: { targetId: string }) => Promise<void>
resetRelay: (args: { targetId: string }) => Promise<void>
getState: (args: { targetId: string }) => Promise<SshConnectionState | null>
needsPassphrasePrompt: (args: { targetId: string }) => Promise<boolean>
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<PortForwardEntry>
updatePortForward: (args: {
id: string
targetId: string
localPort: number
remoteHost: string
remotePort: number
label?: string
}) => Promise<PortForwardEntry>
removePortForward: (args: { id: string }) => Promise<PortForwardEntry | null>
listPortForwards: (args?: { targetId?: string }) => Promise<PortForwardEntry[]>
listDetectedPorts: (args: { targetId: string }) => Promise<EnrichedDetectedPort[]>
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<void>
}
+63
View File
@@ -0,0 +1,63 @@
import type { TelemetryConsentState } from '../../shared/telemetry-consent-types'
import type { MemorySnapshot, StatsSummary } from '../../shared/types'
export type StatsApi = {
getSummary: () => Promise<StatsSummary>
}
// 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<MemorySnapshot>
}
export type DiagnosticsApi = {
getStatus: () => Promise<DiagnosticsStatusPayload>
collectBundle: (lookbackMinutes?: number) => Promise<DiagnosticsBundlePayload>
openBundlePreview: (bundleSubmissionId: string) => Promise<void>
discardBundlePreview: (bundleSubmissionId: string) => Promise<void>
uploadBundle: (bundleSubmissionId: string) => Promise<DiagnosticsUploadPayload>
deleteBundle: (ticketId: string) => Promise<void>
}
export type TelemetryApi = {
/** Fire-and-forget track. Loose IPC typing on purpose — the main-side validator enforces;
* renderer sites should import `track<N>()` from lib/telemetry.ts, not reach here. */
telemetryTrack: (name: string, props: Record<string, unknown>) => Promise<void>
/** 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<void>
/** 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<TelemetryConsentState>
/** 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<void>
stats: StatsApi
memory: MemoryApi
}
+203
View File
@@ -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<PersistedUIState>
set: (args: Partial<PersistedUIState>) => Promise<void>
recordFeatureInteraction: (id: FeatureInteractionId) => Promise<PersistedUIState>
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<boolean>
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<string, string>
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
}
+51
View File
@@ -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<string>
readSelectionClipboardText: (options?: ReadClipboardTextOptions) => Promise<string>
saveClipboardImageAsTempFile: (args?: {
connectionId?: string | null
runtimeEnvironmentId?: string | null
}) => Promise<string | null>
writeClipboardText: (text: string) => Promise<void>
writeTerminalClipboardText: (text: string) => Promise<void>
writeSelectionClipboardText: (text: string) => Promise<void>
writeClipboardImage: (dataUrl: string) => Promise<void>
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<boolean>
onMaximizeChanged: (callback: (isMaximized: boolean) => void) => () => void
requestClose: () => void
popupMenu: () => void
onWindowCloseRequested: (callback: (data: { isQuitting: boolean }) => void) => () => void
confirmWindowClose: () => void
notifyWindowRevealed: () => void
}
+25
View File
@@ -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<string>
getStatus: () => Promise<UpdateStatus>
check: (options?: UpdateCheckOptions) => Promise<void>
download: () => Promise<void>
quitAndInstall: () => Promise<void>
dismissNudge: () => Promise<void>
dismissAvailableUpdate: () => Promise<void>
/** Desktop-only. Rejects unless the current status carries `linux-package-install` recovery. */
getLinuxPackageInstallInstructions: () => Promise<LinuxPackageInstallInstructions>
/** Desktop-only. Reveals the revalidated cached package in the native file manager. */
showLinuxPackage: () => Promise<void>
listBuilds: (channel: ReleaseChannel) => Promise<ReleaseBuildListResult>
onStatus: (callback: (status: UpdateStatus) => void) => () => void
onClearDismissal: (callback: () => void) => () => void
}
+30
View File
@@ -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<WorkspaceCleanupScanResult>
dismiss: (args: WorkspaceCleanupDismissArgs) => Promise<void>
clearDismissals: () => Promise<void>
hasKillableLocalProcesses: (
args: WorkspaceCleanupLocalProcessArgs
) => Promise<WorkspaceCleanupLocalProcessResult>
}
export type WorkspaceSpaceApi = {
analyze: () => Promise<WorkspaceSpaceAnalyzeResult>
cancel: () => Promise<boolean>
onProgress: (callback: (progress: WorkspaceSpaceScanProgress) => void) => () => void
}
+23
View File
@@ -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<WorkspacePortScanResult>
kill: (args: WorkspacePortKillRequest) => Promise<WorkspacePortKillResult>
onAdvertisedUrlChanged: (
callback: (event: WorkspacePortAdvertisedUrlChangedEvent) => void
) => () => void
}
export type LocalhostWorktreeLabelsApi = {
register: (args: LocalhostWorktreeLabelRoute) => Promise<LocalhostWorktreeLabelResult>
}
+50
View File
@@ -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<WorkspaceSessionState>
set: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => Promise<void>
patch: (args: WorkspaceSessionPatch, hostId?: ExecutionHostId) => Promise<void>
flush: () => Promise<void>
readTerminalScrollback: (args: { ref: string }) => string | null
setSync: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => void
}
cache: {
getGitHub: () => Promise<{
pr: Record<string, { data: PRInfo | null; fetchedAt: number }>
issue: Record<string, { data: IssueInfo | null; fetchedAt: number }>
}>
setGitHub: (args: {
cache: {
pr: Record<string, { data: PRInfo | null; fetchedAt: number }>
issue: Record<string, { data: IssueInfo | null; fetchedAt: number }>
}
}) => Promise<void>
}
remoteWorkspace: {
get: (args: { targetId: string }) => Promise<RemoteWorkspaceSnapshot | null>
setForConnectedTargets: (args: {
session?: WorkspaceSessionState
hydratedTargetIds?: string[]
}) => Promise<{ targetId: string; result: RemoteWorkspacePatchResult }[]>
listEnabledConnectedTargets: () => Promise<string[]>
listConnectedClients: (args?: {
targetIds?: string[]
}) => Promise<{ targetId: string; clients: RemoteWorkspaceConnectedClient[] }[]>
clientId: () => Promise<string>
onChanged: (callback: (event: RemoteWorkspaceChangedEvent) => void) => () => void
}
}
+179
View File
@@ -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<Worktree[]>
listDetected: {
(
args: ListDetectedWorktreesArgs
): Promise<HostQualifiedDetectedWorktreeResult | DetectedWorktreeListResult>
(args: LegacyDetectedWorktreeRequest): Promise<DetectedWorktreeListResult>
}
listKnownForExecutionHost?: (
args: ListKnownWorktreesForExecutionHostArgs
) => Promise<HostQualifiedKnownWorktreeResult>
/** Retires the persisted metadata an authoritative scan proved gone, so it stops feeding the read above. */
forgetRemovedForExecutionHost?: (
args: ForgetRemovedWorktreesForExecutionHostArgs
) => Promise<ForgetRemovedWorktreesForExecutionHostResult>
cancelListDetected?: (args: { providerRequestId: ProviderRequestId }) => Promise<void>
listAll: () => Promise<Worktree[]>
create: (args: CreateWorktreeArgs) => Promise<CreateWorktreeResult>
adoptProvisionedRoot: (args: AdoptProvisionedRootArgs) => Promise<CreateWorktreeResult>
/** 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<void>
resolvePrBase: (args: {
repoId: string
prNumber: number
headRefName?: string
baseRefName?: string
isCrossRepository?: boolean
}) => Promise<GitHubPrStartPoint | { error: string }>
/** GitLab parallel of resolvePrBase. For same-project MRs returns
* `<remote>/<source_branch>`; for fork MRs fetches
* refs/merge-requests/<iid>/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<RemoveWorktreeResult>
// 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<RemoveWorktreeResult>
forceDeletePreservedBranch: (args: {
worktreeId: string
branchName: string
expectedHead: string
hostId?: ExecutionHostId
}) => Promise<ForceDeleteWorktreeBranchResult>
updateMeta: (args: { worktreeId: string; updates: Partial<WorktreeMeta> }) => Promise<Worktree>
listLineage: () => Promise<{
lineage: Record<string, WorktreeLineage>
workspaceLineage?: Record<string, WorkspaceLineage>
}>
listLineageForHost?: (args: ListDesktopLineageForHostArgs) => Promise<HostLineageSnapshot>
updateLineage: (args: {
worktreeId: string
parentWorktreeId?: string
noParent?: boolean
}) => Promise<WorktreeLineage | null>
persistSortOrder: (args: { orderedIds: string[] }) => Promise<void>
/** 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<string | null>
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<FolderWorkspace[]>
getPathStatus: (args: FolderWorkspacePathStatusRequest) => Promise<FolderWorkspacePathStatus>
create: (args: {
projectGroupId: string
name?: string
folderPath?: string | null
connectionId?: string | null
linkedTask?: FolderWorkspace['linkedTask']
createdWithAgent?: FolderWorkspace['createdWithAgent']
pendingFirstAgentMessageRename?: boolean
}) => Promise<FolderWorkspace>
update: (args: {
folderWorkspaceId: string
updates: Partial<
Pick<
FolderWorkspace,
| 'name'
| 'folderPath'
| 'linkedTask'
| 'comment'
| 'isArchived'
| 'isUnread'
| 'isPinned'
| 'sortOrder'
| 'manualOrder'
| 'workspaceStatus'
| 'createdWithAgent'
| 'pendingFirstAgentMessageRename'
| 'firstAgentMessageRenameError'
| 'lastActivityAt'
| 'diffComments'
>
>
}) => Promise<FolderWorkspace | null>
delete: (args: { folderWorkspaceId: string }) => Promise<boolean>
}
export type SparsePresetsApi = {
list: (args: { repoId: string }) => Promise<SparsePreset[]>
save: (args: {
repoId: string
id?: string
name: string
directories: string[]
}) => Promise<SparsePreset>
remove: (args: { repoId: string; presetId: string }) => Promise<void>
onChanged: (callback: (data: { repoId: string }) => void) => () => void
}