import type { GitCheckoutInput, GitCheckoutResult, GitCreateBranchInput, GitPreparePullRequestThreadInput, GitPreparePullRequestThreadResult, GitPullRequestRefInput, GitCreateWorktreeInput, GitCreateWorktreeResult, GitInitInput, GitListBranchesInput, GitListBranchesResult, GitPullInput, GitPullResult, GitRemoveWorktreeInput, GitResolvePullRequestResult, GitStatusInput, GitStatusResult, GitCreateBranchResult,} from "./git";import type { ProjectSearchEntriesInput, ProjectSearchEntriesResult, ProjectWriteFileInput, ProjectWriteFileResult,} from "./project";import type { ServerConfig, ServerProviderUpdatedPayload, ServerUpsertKeybindingResult,} from "./server";import type { TerminalClearInput, TerminalCloseInput, TerminalEvent, TerminalOpenInput, TerminalResizeInput, TerminalRestartInput, TerminalSessionSnapshot, TerminalWriteInput,} from "./terminal";import type { ServerUpsertKeybindingInput } from "./server";import type { ClientOrchestrationCommand, OrchestrationGetFullThreadDiffInput, OrchestrationGetFullThreadDiffResult, OrchestrationGetTurnDiffInput, OrchestrationGetTurnDiffResult, OrchestrationEvent, OrchestrationReadModel,} from "./orchestration";import type { EnvironmentId } from "./baseSchemas";import { EditorId } from "./editor";import { ClientSettings, ServerSettings, ServerSettingsPatch } from "./settings"; export interface ContextMenuItem<T extends string = string> { id: T; label: string; destructive?: boolean; disabled?: boolean;} export type DesktopUpdateStatus = | "disabled" | "idle" | "checking" | "up-to-date" | "available" | "downloading" | "downloaded" | "error"; export type DesktopRuntimeArch = "arm64" | "x64" | "other";export type DesktopTheme = "light" | "dark" | "system"; export interface DesktopRuntimeInfo { hostArch: DesktopRuntimeArch; appArch: DesktopRuntimeArch; runningUnderArm64Translation: boolean;} export interface DesktopUpdateState { enabled: boolean; status: DesktopUpdateStatus; currentVersion: string; hostArch: DesktopRuntimeArch; appArch: DesktopRuntimeArch; runningUnderArm64Translation: boolean; availableVersion: string | null; downloadedVersion: string | null; downloadPercent: number | null; checkedAt: string | null; message: string | null; errorContext: "check" | "download" | "install" | null; canRetry: boolean;} export interface DesktopUpdateActionResult { accepted: boolean; completed: boolean; state: DesktopUpdateState;} export interface DesktopUpdateCheckResult { checked: boolean; state: DesktopUpdateState;} export interface DesktopEnvironmentBootstrap { label: string; httpBaseUrl: string | null; wsBaseUrl: string | null; bootstrapToken?: string;} export interface PersistedSavedEnvironmentRecord { environmentId: EnvironmentId; label: string; wsBaseUrl: string; httpBaseUrl: string; createdAt: string; lastConnectedAt: string | null;} export type DesktopServerExposureMode = "local-only" | "network-accessible"; export interface DesktopServerExposureState { mode: DesktopServerExposureMode; endpointUrl: string | null; advertisedHost: string | null;} export interface DesktopBridge { getLocalEnvironmentBootstrap: () => DesktopEnvironmentBootstrap | null; getClientSettings: () => Promise<ClientSettings | null>; setClientSettings: (settings: ClientSettings) => Promise<void>; getSavedEnvironmentRegistry: () => Promise<readonly PersistedSavedEnvironmentRecord[]>; setSavedEnvironmentRegistry: ( records: readonly PersistedSavedEnvironmentRecord[], ) => Promise<void>; getSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<string | null>; setSavedEnvironmentSecret: (environmentId: EnvironmentId, secret: string) => Promise<boolean>; removeSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<void>; getServerExposureState: () => Promise<DesktopServerExposureState>; setServerExposureMode: (mode: DesktopServerExposureMode) => Promise<DesktopServerExposureState>; pickFolder: () => Promise<string | null>; confirm: (message: string) => Promise<boolean>; setTheme: (theme: DesktopTheme) => Promise<void>; showContextMenu: <T extends string>( items: readonly ContextMenuItem<T>[], position?: { x: number; y: number }, ) => Promise<T | null>; openExternal: (url: string) => Promise<boolean>; onMenuAction: (listener: (action: string) => void) => () => void; getUpdateState: () => Promise<DesktopUpdateState>; checkForUpdate: () => Promise<DesktopUpdateCheckResult>; downloadUpdate: () => Promise<DesktopUpdateActionResult>; installUpdate: () => Promise<DesktopUpdateActionResult>; onUpdateState: (listener: (state: DesktopUpdateState) => void) => () => void;} /** * APIs bound to the local app shell, not to any particular backend environment. * * These capabilities describe the desktop/browser host that the user is * currently running: dialogs, editor/external-link opening, context menus, and * app-level settings/config access. They must not be used as a proxy for * "whatever environment the user is targeting", because in a multi-environment * world the local shell and a selected backend environment are distinct * concepts. */export interface LocalApi { dialogs: { pickFolder: () => Promise<string | null>; confirm: (message: string) => Promise<boolean>; }; shell: { openInEditor: (cwd: string, editor: EditorId) => Promise<void>; openExternal: (url: string) => Promise<void>; }; contextMenu: { show: <T extends string>( items: readonly ContextMenuItem<T>[], position?: { x: number; y: number }, ) => Promise<T | null>; }; persistence: { getClientSettings: () => Promise<ClientSettings | null>; setClientSettings: (settings: ClientSettings) => Promise<void>; getSavedEnvironmentRegistry: () => Promise<readonly PersistedSavedEnvironmentRecord[]>; setSavedEnvironmentRegistry: ( records: readonly PersistedSavedEnvironmentRecord[], ) => Promise<void>; getSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<string | null>; setSavedEnvironmentSecret: (environmentId: EnvironmentId, secret: string) => Promise<boolean>; removeSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<void>; }; server: { getConfig: () => Promise<ServerConfig>; refreshProviders: () => Promise<ServerProviderUpdatedPayload>; upsertKeybinding: (input: ServerUpsertKeybindingInput) => Promise<ServerUpsertKeybindingResult>; getSettings: () => Promise<ServerSettings>; updateSettings: (patch: ServerSettingsPatch) => Promise<ServerSettings>; };} /** * APIs bound to a specific backend environment connection. * * These operations must always be routed with explicit environment context. * They represent remote stateful capabilities such as orchestration, terminal, * project, and git operations. In multi-environment mode, each environment gets * its own instance of this surface, and callers should resolve it by * `environmentId` rather than reaching through the local desktop bridge. */export interface EnvironmentApi { terminal: { open: (input: typeof TerminalOpenInput.Encoded) => Promise<TerminalSessionSnapshot>; write: (input: typeof TerminalWriteInput.Encoded) => Promise<void>; resize: (input: typeof TerminalResizeInput.Encoded) => Promise<void>; clear: (input: typeof TerminalClearInput.Encoded) => Promise<void>; restart: (input: typeof TerminalRestartInput.Encoded) => Promise<TerminalSessionSnapshot>; close: (input: typeof TerminalCloseInput.Encoded) => Promise<void>; onEvent: (callback: (event: TerminalEvent) => void) => () => void; }; projects: { searchEntries: (input: ProjectSearchEntriesInput) => Promise<ProjectSearchEntriesResult>; writeFile: (input: ProjectWriteFileInput) => Promise<ProjectWriteFileResult>; }; git: { listBranches: (input: GitListBranchesInput) => Promise<GitListBranchesResult>; createWorktree: (input: GitCreateWorktreeInput) => Promise<GitCreateWorktreeResult>; removeWorktree: (input: GitRemoveWorktreeInput) => Promise<void>; createBranch: (input: GitCreateBranchInput) => Promise<GitCreateBranchResult>; checkout: (input: GitCheckoutInput) => Promise<GitCheckoutResult>; init: (input: GitInitInput) => Promise<void>; resolvePullRequest: (input: GitPullRequestRefInput) => Promise<GitResolvePullRequestResult>; preparePullRequestThread: ( input: GitPreparePullRequestThreadInput, ) => Promise<GitPreparePullRequestThreadResult>; pull: (input: GitPullInput) => Promise<GitPullResult>; refreshStatus: (input: GitStatusInput) => Promise<GitStatusResult>; onStatus: ( input: GitStatusInput, callback: (status: GitStatusResult) => void, options?: { onResubscribe?: () => void; }, ) => () => void; }; orchestration: { getSnapshot: () => Promise<OrchestrationReadModel>; dispatchCommand: (command: ClientOrchestrationCommand) => Promise<{ sequence: number }>; getTurnDiff: (input: OrchestrationGetTurnDiffInput) => Promise<OrchestrationGetTurnDiffResult>; getFullThreadDiff: ( input: OrchestrationGetFullThreadDiffInput, ) => Promise<OrchestrationGetFullThreadDiffResult>; replayEvents: (fromSequenceExclusive: number) => Promise<OrchestrationEvent[]>; onDomainEvent: ( callback: (event: OrchestrationEvent) => void, options?: { onResubscribe?: () => void; }, ) => () => void; };}
import type {
GitCheckoutInput,
GitCheckoutResult,
GitCreateBranchInput,
GitPreparePullRequestThreadInput,
GitPreparePullRequestThreadResult,
GitPullRequestRefInput,
GitCreateWorktreeInput,
GitCreateWorktreeResult,
GitInitInput,
GitListBranchesInput,
GitListBranchesResult,
GitPullInput,
GitPullResult,
GitRemoveWorktreeInput,
GitResolvePullRequestResult,
GitStatusInput,
GitStatusResult,
GitCreateBranchResult,
} from "./git";
import type {
ProjectSearchEntriesInput,
ProjectSearchEntriesResult,
ProjectWriteFileInput,
ProjectWriteFileResult,
} from "./project";
import type {
ServerConfig,
ServerProviderUpdatedPayload,
ServerUpsertKeybindingResult,
} from "./server";
import type {
TerminalClearInput,
TerminalCloseInput,
TerminalEvent,
TerminalOpenInput,
TerminalResizeInput,
TerminalRestartInput,
TerminalSessionSnapshot,
TerminalWriteInput,
} from "./terminal";
import type { ServerUpsertKeybindingInput } from "./server";
import type {
ClientOrchestrationCommand,
OrchestrationGetFullThreadDiffInput,
OrchestrationGetFullThreadDiffResult,
OrchestrationGetTurnDiffInput,
OrchestrationGetTurnDiffResult,
OrchestrationEvent,
OrchestrationReadModel,
} from "./orchestration";
import type { EnvironmentId } from "./baseSchemas";
import { EditorId } from "./editor";
import { ClientSettings, ServerSettings, ServerSettingsPatch } from "./settings";
export interface ContextMenuItem<T extends string = string> {
id: T;
label: string;
destructive?: boolean;
disabled?: boolean;
}
export type DesktopUpdateStatus =
| "disabled"
| "idle"
| "checking"
| "up-to-date"
| "available"
| "downloading"
| "downloaded"
| "error";
export type DesktopRuntimeArch = "arm64" | "x64" | "other";
export type DesktopTheme = "light" | "dark" | "system";
export interface DesktopRuntimeInfo {
hostArch: DesktopRuntimeArch;
appArch: DesktopRuntimeArch;
runningUnderArm64Translation: boolean;
}
export interface DesktopUpdateState {
enabled: boolean;
status: DesktopUpdateStatus;
currentVersion: string;
hostArch: DesktopRuntimeArch;
appArch: DesktopRuntimeArch;
runningUnderArm64Translation: boolean;
availableVersion: string | null;
downloadedVersion: string | null;
downloadPercent: number | null;
checkedAt: string | null;
message: string | null;
errorContext: "check" | "download" | "install" | null;
canRetry: boolean;
}
export interface DesktopUpdateActionResult {
accepted: boolean;
completed: boolean;
state: DesktopUpdateState;
}
export interface DesktopUpdateCheckResult {
checked: boolean;
state: DesktopUpdateState;
}
export interface DesktopEnvironmentBootstrap {
label: string;
httpBaseUrl: string | null;
wsBaseUrl: string | null;
bootstrapToken?: string;
}
export interface PersistedSavedEnvironmentRecord {
environmentId: EnvironmentId;
label: string;
wsBaseUrl: string;
httpBaseUrl: string;
createdAt: string;
lastConnectedAt: string | null;
}
export type DesktopServerExposureMode = "local-only" | "network-accessible";
export interface DesktopServerExposureState {
mode: DesktopServerExposureMode;
endpointUrl: string | null;
advertisedHost: string | null;
}
export interface DesktopBridge {
getLocalEnvironmentBootstrap: () => DesktopEnvironmentBootstrap | null;
getClientSettings: () => Promise<ClientSettings | null>;
setClientSettings: (settings: ClientSettings) => Promise<void>;
getSavedEnvironmentRegistry: () => Promise<readonly PersistedSavedEnvironmentRecord[]>;
setSavedEnvironmentRegistry: (
records: readonly PersistedSavedEnvironmentRecord[],
) => Promise<void>;
getSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<string | null>;
setSavedEnvironmentSecret: (environmentId: EnvironmentId, secret: string) => Promise<boolean>;
removeSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<void>;
getServerExposureState: () => Promise<DesktopServerExposureState>;
setServerExposureMode: (mode: DesktopServerExposureMode) => Promise<DesktopServerExposureState>;
pickFolder: () => Promise<string | null>;
confirm: (message: string) => Promise<boolean>;
setTheme: (theme: DesktopTheme) => Promise<void>;
showContextMenu: <T extends string>(
items: readonly ContextMenuItem<T>[],
position?: { x: number; y: number },
) => Promise<T | null>;
openExternal: (url: string) => Promise<boolean>;
onMenuAction: (listener: (action: string) => void) => () => void;
getUpdateState: () => Promise<DesktopUpdateState>;
checkForUpdate: () => Promise<DesktopUpdateCheckResult>;
downloadUpdate: () => Promise<DesktopUpdateActionResult>;
installUpdate: () => Promise<DesktopUpdateActionResult>;
onUpdateState: (listener: (state: DesktopUpdateState) => void) => () => void;
}
/**
* APIs bound to the local app shell, not to any particular backend environment.
*
* These capabilities describe the desktop/browser host that the user is
* currently running: dialogs, editor/external-link opening, context menus, and
* app-level settings/config access. They must not be used as a proxy for
* "whatever environment the user is targeting", because in a multi-environment
* world the local shell and a selected backend environment are distinct
* concepts.
*/
export interface LocalApi {
dialogs: {
pickFolder: () => Promise<string | null>;
confirm: (message: string) => Promise<boolean>;
};
shell: {
openInEditor: (cwd: string, editor: EditorId) => Promise<void>;
openExternal: (url: string) => Promise<void>;
};
contextMenu: {
show: <T extends string>(
items: readonly ContextMenuItem<T>[],
position?: { x: number; y: number },
) => Promise<T | null>;
};
persistence: {
getClientSettings: () => Promise<ClientSettings | null>;
setClientSettings: (settings: ClientSettings) => Promise<void>;
getSavedEnvironmentRegistry: () => Promise<readonly PersistedSavedEnvironmentRecord[]>;
setSavedEnvironmentRegistry: (
records: readonly PersistedSavedEnvironmentRecord[],
) => Promise<void>;
getSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<string | null>;
setSavedEnvironmentSecret: (environmentId: EnvironmentId, secret: string) => Promise<boolean>;
removeSavedEnvironmentSecret: (environmentId: EnvironmentId) => Promise<void>;
};
server: {
getConfig: () => Promise<ServerConfig>;
refreshProviders: () => Promise<ServerProviderUpdatedPayload>;
upsertKeybinding: (input: ServerUpsertKeybindingInput) => Promise<ServerUpsertKeybindingResult>;
getSettings: () => Promise<ServerSettings>;
updateSettings: (patch: ServerSettingsPatch) => Promise<ServerSettings>;
};
}
/**
* APIs bound to a specific backend environment connection.
*
* These operations must always be routed with explicit environment context.
* They represent remote stateful capabilities such as orchestration, terminal,
* project, and git operations. In multi-environment mode, each environment gets
* its own instance of this surface, and callers should resolve it by
* `environmentId` rather than reaching through the local desktop bridge.
*/
export interface EnvironmentApi {
terminal: {
open: (input: typeof TerminalOpenInput.Encoded) => Promise<TerminalSessionSnapshot>;
write: (input: typeof TerminalWriteInput.Encoded) => Promise<void>;
resize: (input: typeof TerminalResizeInput.Encoded) => Promise<void>;
clear: (input: typeof TerminalClearInput.Encoded) => Promise<void>;
restart: (input: typeof TerminalRestartInput.Encoded) => Promise<TerminalSessionSnapshot>;
close: (input: typeof TerminalCloseInput.Encoded) => Promise<void>;
onEvent: (callback: (event: TerminalEvent) => void) => () => void;
};
projects: {
searchEntries: (input: ProjectSearchEntriesInput) => Promise<ProjectSearchEntriesResult>;
writeFile: (input: ProjectWriteFileInput) => Promise<ProjectWriteFileResult>;
};
git: {
listBranches: (input: GitListBranchesInput) => Promise<GitListBranchesResult>;
createWorktree: (input: GitCreateWorktreeInput) => Promise<GitCreateWorktreeResult>;
removeWorktree: (input: GitRemoveWorktreeInput) => Promise<void>;
createBranch: (input: GitCreateBranchInput) => Promise<GitCreateBranchResult>;
checkout: (input: GitCheckoutInput) => Promise<GitCheckoutResult>;
init: (input: GitInitInput) => Promise<void>;
resolvePullRequest: (input: GitPullRequestRefInput) => Promise<GitResolvePullRequestResult>;
preparePullRequestThread: (
input: GitPreparePullRequestThreadInput,
) => Promise<GitPreparePullRequestThreadResult>;
pull: (input: GitPullInput) => Promise<GitPullResult>;
refreshStatus: (input: GitStatusInput) => Promise<GitStatusResult>;
onStatus: (
input: GitStatusInput,
callback: (status: GitStatusResult) => void,
options?: {
onResubscribe?: () => void;
},
) => () => void;
};
orchestration: {
getSnapshot: () => Promise<OrchestrationReadModel>;
dispatchCommand: (command: ClientOrchestrationCommand) => Promise<{ sequence: number }>;
getTurnDiff: (input: OrchestrationGetTurnDiffInput) => Promise<OrchestrationGetTurnDiffResult>;
getFullThreadDiff: (
input: OrchestrationGetFullThreadDiffInput,
) => Promise<OrchestrationGetFullThreadDiffResult>;
replayEvents: (fromSequenceExclusive: number) => Promise<OrchestrationEvent[]>;
onDomainEvent: (
callback: (event: OrchestrationEvent) => void,
options?: {
onResubscribe?: () => void;
},
) => () => void;
};
}