Files

2015 lines
70 KiB
TypeScript

import { contextBridge, ipcRenderer, webUtils } from 'electron'
import {
type AgentEvent,
type AgentQuestionAnswer,
type AgentRequest,
type AgentRuntimeDetection,
type AgentRuntimeStatus,
type AppInfo,
type BrowserBackRequest,
type BrowserClickRequest,
type BrowserCloseTabRequest,
type BrowserCreateTabRequest,
type BrowserLiveState,
type BrowserListTabsRequest,
type BrowserNavigateRequest,
type BrowserReloadRequest,
type BrowserScreenshotRequest,
type BrowserScreenshotResult,
type BrowserSelectRequest,
type BrowserSetViewportRequest,
type BrowserSnapshotRequest,
type BrowserSnapshotResult,
type BrowserStopLoadingRequest,
type BrowserTabSummary,
type BrowserTypeRequest,
type ContextAttachment,
type ContextFileSelectionProgress,
type DesktopApi,
type KnowledgeLibrary,
type KnowledgeSearchReference,
type KnowledgeSnapshot,
type PastedImageInput,
type RuntimeConversationCompactInput,
type RuntimeConversationCompactResult,
type RuntimeCustomizationSettings,
type RuntimeNativeSnapshot,
type RuntimeNativeSnapshotInput,
type RuntimeSettings,
type RuntimeSettingsInput,
type RuntimeConfigActionInput,
type RuntimeFileSelectionKind,
type RemoteProjectSaveProgress,
type RemoteProjectSaveRequest,
type WindowCaptureOption
} from '../shared/contracts'
import { ipcChannels } from '../shared/ipc-channels'
import type {
BrowserProfileCreateInput,
BrowserProfileRenameInput,
BuiltinMcpServerId,
CapabilityDiagnosticReport,
CapabilitySnapshot,
ComputerCapabilityId,
McpServerTestResult,
ObsidianConnectionTestResult,
WebSearchTestResult
} from '../shared/capability-contracts'
import type {
AssistantProject,
AssistantArtifact,
AssistantMemory,
AssistantSchedule,
AssistantHeartbeatConfig,
AssistantHeartbeatEntry,
AssistantHeartbeatRun,
AssistantExpert,
AssistantTask,
ExecutionStats,
ActivityHistoryPage,
ActivityHistoryPageRequest,
ActivityHistoryReconcileRequest,
ActivityHistorySnapshot,
ActivityHistorySummary,
ActivityHistorySummaryRequest,
ActivityHistoryUpdate,
ActivityHistoryUpdateResult,
ActivityRecord,
TokenUsageSummary,
ConversationBranchInput,
ConversationSnapshot,
LocalConversationSaveBatch,
WorkspaceChanges,
WorkspaceDirectoryListing,
WorkspaceFilePreview,
ProjectCreateInput,
MemoryCreateInput,
ScheduleCreateInput,
HeartbeatCreateInput,
HeartbeatUpdateInput,
ExpertCreateInput,
ExpertUpdateInput
} from '../shared/assistant-contracts'
import type {
ChannelConnectionTestResult,
ChannelRuntimeStatusChange,
ChannelSettingsApply,
ChannelSettingsSnapshot,
CredentialChannel,
DingTalkChannelSettingsInput,
TelegramChannelSettingsInput,
WeComChannelSettingsInput
} from '../shared/channel-settings-contracts'
import type {
ApplicationSettings,
ApplicationSettingsUpdate,
ModelDownloadSource,
VersionCheckResult
} from '../shared/application-settings-contracts'
import type {
LocalToolDiagnoseTarget,
LocalToolEnvironmentProgress,
LocalToolEnvironmentSettings,
LocalToolEnvironmentSnapshot,
LocalToolKind
} from '../shared/local-tool-environment-contracts'
import type { ReleaseNotesSnapshot } from '../shared/release-notes-contracts'
import type {
SpeechModelSnapshot,
SpeechTranscriptionInput,
SpeechTranscriptionResult
} from '../shared/speech-model-contracts'
import type {
EmbeddingDiagnosticResult,
EmbeddingModelProgressSnapshot,
EmbeddingModelSnapshot,
EmbeddingSettingsSnapshot,
KnowledgeEmbeddingIndexSnapshot
} from '../shared/embedding-contracts'
import type {
DocumentOcrAssets,
DocumentOcrFailure,
DocumentOcrModelProgressSnapshot,
DocumentOcrRequest,
DocumentOcrResult,
DocumentParsingDiagnostic,
DocumentParsingSettings,
DocumentParsingSnapshot,
DocumentParsingTestPurpose
} from '../shared/document-parsing-contracts'
import type { AgentRuntimeSelection } from '../shared/runtime-selection-contracts'
import type {
AgentPackageDownloadProgress,
AgentPackageInventory
} from '../shared/agent-package-contracts'
import type {
AgentArchitecture
} from '../shared/agent-installation-contracts'
import type { WeixinBindingSnapshot } from '../shared/weixin-channel-contracts'
import type { RemoteChannelActivity } from '../shared/remote-channel-contracts'
import type {
SshHostDraftInspectionRequest,
SshDirectoryBrowseResult,
SshHostKeyInspection,
RemoteEnvironmentUpdateProgress,
RemoteEnvironmentUpdateRequest,
SshHostRemoteEnvironment,
SshHostRemovalResult,
SshHostAgentConnectionStatus,
SshHostsSnapshot,
SshHostValidationRequest,
SshHostValidationResult
} from '../shared/ssh-host-contracts'
import type {
MagicNoteAnalysisStreamEvent,
MagicNoteDraftAnalysis,
MagicNoteDetail,
MagicNoteCreateResult,
MagicNoteSummary,
MagicNoteEntryCreateResult,
MagicNoteTag,
MagicNoteTagRenameResult,
MagicNotesSnapshot,
MagicTodoItem,
MagicTodoStatus,
MagicTodoUpdateResult,
MagicTodosSnapshot
} from '../shared/magic-notes-contracts'
import type {
KnowledgeChunkDeleteInput,
KnowledgeChunkPage,
KnowledgeChunkUpdateInput,
KnowledgeChunksListInput,
KnowledgeDocumentOpenInput,
KnowledgeDocumentRebuildInput,
KnowledgeLibraryRebuildInput,
KnowledgeReferenceContext,
KnowledgeReferenceContextInput,
KnowledgeReferenceOpenInput,
KnowledgeRetrievalResponse,
KnowledgeRetrieveInput,
KnowledgeSettingsUpdateInput
} from '../shared/knowledge-contracts'
import type {
RuntimeExtensionAction,
RuntimeExtensionMarketplaceSnapshot
} from '../shared/runtime-extension-contracts'
import type {
GlobalShortcutSettings,
GlobalShortcutSettingsSnapshot,
GlobalShortcutSettingsUpdateResult
} from '../shared/shortcut'
import type {
FeedbackSubmitInput,
FeedbackSubmitResult
} from '../shared/feedback-contracts'
import type {
RemoteProjectRecoverySnapshot,
RemoteProjectRecoveryState
} from '../shared/remote-project-recovery-contracts'
import type {
TerminalAckRequest,
TerminalCloseRequest,
TerminalCreateRequest,
TerminalEvent,
TerminalResizeRequest,
TerminalSnapshot,
TerminalSnapshotRequest,
TerminalWriteRequest
} from '../shared/terminal-contracts'
const desktopApi: DesktopApi = {
openRuntimeNativeClient: (input) => ipcRenderer.invoke(ipcChannels.runtimeNativeClientOpen, input),
getRuntimeNativeClient: (input) => ipcRenderer.invoke(ipcChannels.runtimeNativeClientGet, input),
stopRuntimeNativeClient: (input) => ipcRenderer.invoke(ipcChannels.runtimeNativeClientStop, input),
storageUpgrade: {
getProgress: () => ipcRenderer.invoke(ipcChannels.storageUpgradeProgress),
act: async (action) => {
await ipcRenderer.invoke(ipcChannels.storageUpgradeAction, action)
}
},
app: {
getInfo: () => ipcRenderer.invoke(ipcChannels.appInfo) as Promise<AppInfo>,
show: async () => {
await ipcRenderer.invoke(ipcChannels.appShow)
},
hide: async () => {
await ipcRenderer.invoke(ipcChannels.appHide)
},
minimize: async () => {
await ipcRenderer.invoke(ipcChannels.windowMinimize)
},
toggleMaximize: async () => {
await ipcRenderer.invoke(ipcChannels.windowToggleMaximize)
},
close: async () => {
await ipcRenderer.invoke(ipcChannels.windowClose)
},
isMaximized: () =>
ipcRenderer.invoke(ipcChannels.windowIsMaximized) as Promise<boolean>,
onMaximizedChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
maximized: boolean
): void => listener(maximized)
ipcRenderer.on(ipcChannels.windowMaximizedChanged, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.windowMaximizedChanged,
handler
)
},
onPreviewEscape: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.windowPreviewEscape, handler)
return () => ipcRenderer.removeListener(ipcChannels.windowPreviewEscape, handler)
},
onBeforeQuit: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
requestId: string
): void => {
const acknowledge = (): void => {
void ipcRenderer.invoke(
ipcChannels.appRendererPersistenceComplete,
requestId
)
}
void listener().then(acknowledge, acknowledge)
}
ipcRenderer.on(
ipcChannels.appRendererPersistenceRequest,
handler
)
void ipcRenderer.invoke(ipcChannels.appRendererPersistenceReady)
return () =>
ipcRenderer.removeListener(
ipcChannels.appRendererPersistenceRequest,
handler
)
},
clearLocalData: async () => {
await ipcRenderer.invoke(ipcChannels.appClearLocalData)
},
onNewConversation: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.conversationNew, handler)
return () => ipcRenderer.removeListener(ipcChannels.conversationNew, handler)
},
onOpenSettings: (listener) => {
const handler = (_event: Electron.IpcRendererEvent, category?: unknown): void =>
listener(category === 'model' || category === 'document-parsing' ? category : undefined)
ipcRenderer.on(ipcChannels.settingsOpen, handler)
return () => ipcRenderer.removeListener(ipcChannels.settingsOpen, handler)
}
},
clipboard: {
readText: () =>
ipcRenderer.invoke(ipcChannels.clipboardReadText) as Promise<string>,
writeText: async (text: string) => {
await ipcRenderer.invoke(ipcChannels.clipboardWriteText, text)
}
},
agent: {
getStatus: (selection) =>
ipcRenderer.invoke(
ipcChannels.agentStatus,
selection
) as Promise<AgentRuntimeStatus>,
run: async (request: AgentRequest) => {
await ipcRenderer.invoke(ipcChannels.agentRun, request)
},
cancel: async (requestId: string) => {
await ipcRenderer.invoke(ipcChannels.agentCancel, requestId)
},
respondQuestion: async (
questionId: string,
answers?: AgentQuestionAnswer[]
) => {
await ipcRenderer.invoke(ipcChannels.agentQuestionRespond, {
questionId,
answers: answers ?? []
})
},
compactConversation: (input: RuntimeConversationCompactInput) =>
ipcRenderer.invoke(
ipcChannels.agentCompactConversation,
input
) as Promise<RuntimeConversationCompactResult>,
onEvent: (listener) => {
const handler = (_event: Electron.IpcRendererEvent, payload: AgentEvent): void =>
listener(payload)
ipcRenderer.on(ipcChannels.agentEvent, handler)
return () => ipcRenderer.removeListener(ipcChannels.agentEvent, handler)
}
},
browser: {
createTab: (request: BrowserCreateTabRequest) =>
ipcRenderer.invoke(
ipcChannels.browserCreateTab,
request
) as Promise<BrowserTabSummary>,
listTabs: (request: BrowserListTabsRequest) =>
ipcRenderer.invoke(
ipcChannels.browserListTabs,
request
) as Promise<BrowserTabSummary[]>,
closeTab: async (request: BrowserCloseTabRequest) => {
await ipcRenderer.invoke(ipcChannels.browserCloseTab, request)
},
navigate: async (
requestOrConversation: BrowserNavigateRequest | string,
url?: string
) => {
await ipcRenderer.invoke(
ipcChannels.browserNavigate,
typeof requestOrConversation === 'string'
? { conversationId: requestOrConversation, url }
: requestOrConversation
)
},
back: async (requestOrConversation: BrowserBackRequest | string) => {
await ipcRenderer.invoke(
ipcChannels.browserBack,
typeof requestOrConversation === 'string'
? { conversationId: requestOrConversation }
: requestOrConversation
)
},
reload: async (requestOrConversation: BrowserReloadRequest | string) => {
await ipcRenderer.invoke(
ipcChannels.browserReload,
typeof requestOrConversation === 'string'
? { conversationId: requestOrConversation }
: requestOrConversation
)
},
stopLoading: async (
requestOrConversation: BrowserStopLoadingRequest | string
) => {
await ipcRenderer.invoke(
ipcChannels.browserStopLoading,
typeof requestOrConversation === 'string'
? { conversationId: requestOrConversation }
: requestOrConversation
)
},
snapshot: (request: BrowserSnapshotRequest) =>
ipcRenderer.invoke(
ipcChannels.browserSnapshot,
request
) as Promise<BrowserSnapshotResult>,
click: async (request: BrowserClickRequest) => {
await ipcRenderer.invoke(ipcChannels.browserClick, request)
},
type: async (request: BrowserTypeRequest) => {
await ipcRenderer.invoke(ipcChannels.browserType, request)
},
select: async (request: BrowserSelectRequest) => {
await ipcRenderer.invoke(ipcChannels.browserSelect, request)
},
screenshot: (request: BrowserScreenshotRequest) =>
ipcRenderer.invoke(
ipcChannels.browserScreenshot,
request
) as Promise<BrowserScreenshotResult>,
setViewport: async (request: BrowserSetViewportRequest) => {
await ipcRenderer.invoke(ipcChannels.browserSetViewport, request)
},
stop: async (conversationId: string) => {
await ipcRenderer.invoke(
ipcChannels.browserStop,
{ conversationId }
)
},
onState: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
payload: BrowserLiveState
): void => listener(payload)
ipcRenderer.on(ipcChannels.browserState, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.browserState, handler)
}
},
terminal: {
create: (request: TerminalCreateRequest) =>
ipcRenderer.invoke(
ipcChannels.terminalCreate,
request
) as Promise<TerminalSnapshot>,
write: async (request: TerminalWriteRequest) => {
await ipcRenderer.invoke(ipcChannels.terminalWrite, request)
},
resize: async (request: TerminalResizeRequest) => {
await ipcRenderer.invoke(ipcChannels.terminalResize, request)
},
close: (request: TerminalCloseRequest) =>
ipcRenderer.invoke(
ipcChannels.terminalClose,
request
) as Promise<TerminalSnapshot>,
getSnapshot: (request: TerminalSnapshotRequest) =>
ipcRenderer.invoke(
ipcChannels.terminalSnapshot,
request
) as Promise<TerminalSnapshot>,
ack: async (request: TerminalAckRequest) => {
await ipcRenderer.invoke(ipcChannels.terminalAck, request)
},
onEvent: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
payload: TerminalEvent
): void => listener(payload)
ipcRenderer.on(ipcChannels.terminalEvent, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.terminalEvent, handler)
}
},
settings: {
getRuntime: () =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsGet
) as Promise<RuntimeSettings>,
updateRuntime: (input: RuntimeSettingsInput) =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsUpdate,
input
) as Promise<RuntimeSettings>,
selectWorkspace: () =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsSelectWorkspace
) as Promise<string | undefined>,
detectAgentRuntimes: () =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsDetect
) as Promise<AgentRuntimeDetection>,
selectRuntimeFile: (kind: RuntimeFileSelectionKind) =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsSelectFile,
kind
) as Promise<string | undefined>,
openRuntimeConfig: async (input: RuntimeConfigActionInput) => {
await ipcRenderer.invoke(
ipcChannels.runtimeSettingsOpenConfig,
input
)
},
testModelConnection: (profileId: string) =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsTestModel,
profileId
) as Promise<AgentRuntimeStatus>,
testRuntime: (selection: AgentRuntimeSelection) =>
ipcRenderer.invoke(
ipcChannels.runtimeSettingsTest,
selection
) as Promise<AgentRuntimeStatus>
},
sshHosts: {
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.sshHostsGet
) as Promise<SshHostsSnapshot>,
onAgentConnectionStatus: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
status: SshHostAgentConnectionStatus
): void => listener(status)
ipcRenderer.on(
ipcChannels.sshHostsAgentConnectionStatus,
handler
)
return () =>
ipcRenderer.removeListener(
ipcChannels.sshHostsAgentConnectionStatus,
handler
)
},
getAgentPackageInventory: (refresh = false) =>
ipcRenderer.invoke(
ipcChannels.sshHostsAgentPackageInventory,
{ refresh }
) as Promise<AgentPackageInventory>,
downloadAgentPackage: (architecture: AgentArchitecture, platform: 'linux' | 'darwin' = 'linux') =>
ipcRenderer.invoke(
ipcChannels.sshHostsAgentPackageDownload,
{ architecture, platform }
) as Promise<AgentPackageInventory>,
importAgentPackage: () =>
ipcRenderer.invoke(
ipcChannels.sshHostsAgentPackageImport
) as Promise<AgentPackageInventory | undefined>,
exportAgentPackage: async (architecture: AgentArchitecture, platform: 'linux' | 'darwin' = 'linux') => {
await ipcRenderer.invoke(
ipcChannels.sshHostsAgentPackageExport,
{ architecture, platform }
)
},
onAgentPackageProgress: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
progress: AgentPackageDownloadProgress
): void => listener(progress)
ipcRenderer.on(
ipcChannels.sshHostsAgentPackageProgress,
handler
)
return () =>
ipcRenderer.removeListener(
ipcChannels.sshHostsAgentPackageProgress,
handler
)
},
remove: (hostId: string) =>
ipcRenderer.invoke(ipcChannels.sshHostsRemove, {
hostId
}) as Promise<SshHostRemovalResult>,
inspectDraftHostKey: (input: SshHostDraftInspectionRequest) =>
ipcRenderer.invoke(
ipcChannels.sshHostsInspectDraftKey,
input
) as Promise<SshHostKeyInspection>,
discardCandidate: async (candidateId: string) => {
await ipcRenderer.invoke(
ipcChannels.sshHostsDiscardCandidate,
{ candidateId }
)
},
validateAndSave: (input: SshHostValidationRequest) =>
ipcRenderer.invoke(
ipcChannels.sshHostsValidateAndSave,
input
) as Promise<SshHostValidationResult>,
getRemoteEnvironment: (hostId: string) =>
ipcRenderer.invoke(
ipcChannels.sshHostsRemoteEnvironment,
{ hostId }
) as Promise<SshHostRemoteEnvironment>,
updateRemoteEnvironment: async (
input: RemoteEnvironmentUpdateRequest
) => {
await ipcRenderer.invoke(
ipcChannels.sshHostsUpdateRemoteEnvironment,
input
)
},
cancelRemoteEnvironmentUpdate: async (hostId: string) => {
await ipcRenderer.invoke(
ipcChannels.sshHostsCancelRemoteEnvironmentUpdate,
{ hostId }
)
},
onRemoteEnvironmentUpdateProgress: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
progress: RemoteEnvironmentUpdateProgress
): void => listener(progress)
ipcRenderer.on(
ipcChannels.sshHostsRemoteEnvironmentUpdateProgress,
handler
)
return () =>
ipcRenderer.removeListener(
ipcChannels.sshHostsRemoteEnvironmentUpdateProgress,
handler
)
},
browseDirectories: (hostId: string, path?: string) =>
ipcRenderer.invoke(ipcChannels.sshHostsBrowseDirectories, {
hostId,
...(path === undefined ? {} : { path })
}) as Promise<SshDirectoryBrowseResult>,
cancelDirectoryBrowse: async () => {
await ipcRenderer.invoke(
ipcChannels.sshHostsCancelDirectoryBrowse
)
}
},
channels: {
onStatusChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
change: ChannelRuntimeStatusChange
): void => listener(change)
ipcRenderer.on(ipcChannels.channelStatusChanged, handler)
return () => ipcRenderer.removeListener(ipcChannels.channelStatusChanged, handler)
},
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.channelSettingsGet
) as Promise<ChannelSettingsSnapshot>,
apply: (input: ChannelSettingsApply) =>
ipcRenderer.invoke(
ipcChannels.channelSettingsApply,
input
) as Promise<ChannelSettingsSnapshot>,
testConnection: (
channel: CredentialChannel,
settings?: WeComChannelSettingsInput | DingTalkChannelSettingsInput | TelegramChannelSettingsInput
) =>
ipcRenderer.invoke(ipcChannels.channelSettingsTest, {
channel,
settings
}) as Promise<ChannelConnectionTestResult>,
getWeixinBinding: () =>
ipcRenderer.invoke(
ipcChannels.weixinBindingGet
) as Promise<WeixinBindingSnapshot>,
startWeixinBinding: () =>
ipcRenderer.invoke(
ipcChannels.weixinBindingStart
) as Promise<WeixinBindingSnapshot>,
submitWeixinVerification: (code: string) =>
ipcRenderer.invoke(
ipcChannels.weixinBindingVerify,
{ code }
) as Promise<WeixinBindingSnapshot>,
disconnectWeixin: () =>
ipcRenderer.invoke(
ipcChannels.weixinBindingDisconnect
) as Promise<WeixinBindingSnapshot>,
onWeixinBindingChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
snapshot: WeixinBindingSnapshot
): void => listener(snapshot)
ipcRenderer.on(ipcChannels.weixinBindingChanged, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.weixinBindingChanged,
handler
)
},
onRemoteActivity: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
activity: RemoteChannelActivity
): void => listener(activity)
ipcRenderer.on(ipcChannels.remoteChannelActivity, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.remoteChannelActivity,
handler
)
}
},
updates: {
onSettingsChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
settings: ApplicationSettings
): void => listener(settings)
ipcRenderer.on(ipcChannels.applicationSettingsChanged, handler)
return () => ipcRenderer.removeListener(ipcChannels.applicationSettingsChanged, handler)
},
getSettings: () =>
ipcRenderer.invoke(
ipcChannels.applicationSettingsGet
) as Promise<ApplicationSettings>,
updateSettings: (input: ApplicationSettingsUpdate) =>
ipcRenderer.invoke(
ipcChannels.applicationSettingsUpdate,
input
) as Promise<ApplicationSettings>,
check: () =>
ipcRenderer.invoke(
ipcChannels.versionCheck
) as Promise<VersionCheckResult>,
openReleasePage: async () => {
await ipcRenderer.invoke(ipcChannels.versionOpenReleasePage)
},
onResult: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
result: VersionCheckResult
): void => listener(result)
ipcRenderer.on(ipcChannels.versionCheckResult, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.versionCheckResult, handler)
}
},
localToolEnvironment: {
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentGet
) as Promise<LocalToolEnvironmentSnapshot>,
updateSettings: (settings: LocalToolEnvironmentSettings) =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentUpdate,
settings
) as Promise<LocalToolEnvironmentSnapshot>,
refreshCandidates: () =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentRefresh
) as Promise<LocalToolEnvironmentSnapshot>,
selectExecutable: (kind: LocalToolKind) =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentSelectExecutable,
{ kind }
) as Promise<LocalToolEnvironmentSnapshot>,
diagnose: (kind: LocalToolDiagnoseTarget) =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentDiagnose,
{ kind }
) as Promise<LocalToolEnvironmentSnapshot>,
installPython: () =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentInstallPython
) as Promise<LocalToolEnvironmentSnapshot>,
cancelPython: () =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentCancelPython
) as Promise<boolean>,
removePython: () =>
ipcRenderer.invoke(
ipcChannels.localToolEnvironmentRemovePython
) as Promise<LocalToolEnvironmentSnapshot>,
onProgress: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
progress: LocalToolEnvironmentProgress
): void => listener(progress)
ipcRenderer.on(ipcChannels.localToolEnvironmentProgress, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.localToolEnvironmentProgress,
handler
)
}
},
feedback: {
submit: (input: FeedbackSubmitInput) =>
ipcRenderer.invoke(
ipcChannels.feedbackSubmit,
input
) as Promise<FeedbackSubmitResult>
},
shortcuts: {
getSettings: () =>
ipcRenderer.invoke(
ipcChannels.shortcutSettingsGet
) as Promise<GlobalShortcutSettingsSnapshot>,
updateSettings: (input: GlobalShortcutSettings) =>
ipcRenderer.invoke(
ipcChannels.shortcutSettingsUpdate,
input
) as Promise<GlobalShortcutSettingsUpdateResult>
},
releaseNotes: {
getPending: () =>
ipcRenderer.invoke(
ipcChannels.releaseNotesGetPending
) as Promise<ReleaseNotesSnapshot>,
acknowledge: async (version: string) => {
await ipcRenderer.invoke(
ipcChannels.releaseNotesAcknowledge,
{ version }
)
}
},
localInference: {
openSettings: (category) => ipcRenderer.invoke(ipcChannels.localInferenceOpenSettings, category),
getSnapshot: () => ipcRenderer.invoke(ipcChannels.localInferenceGet),
act: (input) => ipcRenderer.invoke(ipcChannels.localInferenceAct, input),
cancel: (taskId) => ipcRenderer.invoke(ipcChannels.localInferenceCancel, { taskId })
},
sharing: {
getSettings: () => ipcRenderer.invoke(ipcChannels.sharingSettingsGet),
saveSettings: input => ipcRenderer.invoke(ipcChannels.sharingSettingsSave, input),
registerDevice: () => ipcRenderer.invoke(ipcChannels.sharingRegister),
getCatalog: () => ipcRenderer.invoke(ipcChannels.sharingCatalog),
publish: input => ipcRenderer.invoke(ipcChannels.sharingPublish, input),
revoke: id => ipcRenderer.invoke(ipcChannels.sharingRevoke, id)
},
speechModels: {
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.speechModelsGet
) as Promise<SpeechModelSnapshot>,
install: (
modelId: string,
expectedDownloadSource: ModelDownloadSource
) =>
ipcRenderer.invoke(
ipcChannels.speechModelsInstall,
{ modelId, expectedDownloadSource }
) as Promise<SpeechModelSnapshot>,
cancel: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.speechModelsCancel,
{ modelId }
) as Promise<boolean>,
remove: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.speechModelsRemove,
{ modelId }
) as Promise<SpeechModelSnapshot>,
select: (modelId: string | null) =>
ipcRenderer.invoke(
ipcChannels.speechModelsSelect,
{ modelId }
) as Promise<SpeechModelSnapshot>,
importArchive: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.speechModelsImportArchive,
{ modelId }
) as Promise<SpeechModelSnapshot | undefined>,
exportArchive: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.speechModelsExportArchive,
{ modelId }
) as Promise<SpeechModelSnapshot | undefined>,
openRepository: async (modelId: string) => {
await ipcRenderer.invoke(
ipcChannels.speechModelsOpenRepository,
{ modelId }
)
},
openModelsDirectory: async () => {
await ipcRenderer.invoke(ipcChannels.speechModelsOpenDirectory)
}
},
speech: {
transcribe: (input: SpeechTranscriptionInput) =>
ipcRenderer.invoke(
ipcChannels.speechTranscribe,
input
) as Promise<SpeechTranscriptionResult>,
cancel: (requestId: string) =>
ipcRenderer.invoke(
ipcChannels.speechTranscriptionCancel,
requestId
) as Promise<boolean>
},
embeddings: {
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.embeddingSettingsGet
) as Promise<EmbeddingSettingsSnapshot>,
getModelProgress: () =>
ipcRenderer.invoke(
ipcChannels.embeddingModelsProgress
) as Promise<EmbeddingModelProgressSnapshot>,
diagnose: (connectionId: string) =>
ipcRenderer.invoke(
ipcChannels.embeddingDiagnose,
{ connectionId }
) as Promise<EmbeddingDiagnosticResult>,
setCurrent: (connectionId: string) =>
ipcRenderer.invoke(
ipcChannels.embeddingSetCurrent,
{ connectionId }
) as Promise<EmbeddingSettingsSnapshot>,
installModel: (
modelId: string,
expectedDownloadSource: ModelDownloadSource
) =>
ipcRenderer.invoke(
ipcChannels.embeddingModelsInstall,
{ modelId, expectedDownloadSource }
) as Promise<EmbeddingModelSnapshot>,
cancelModelOperation: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.embeddingModelsCancel,
{ modelId }
) as Promise<boolean>,
importModelArchive: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.embeddingModelsImportArchive,
{ modelId }
) as Promise<EmbeddingModelSnapshot | undefined>,
removeModel: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.embeddingModelsRemove,
{ modelId }
) as Promise<EmbeddingModelSnapshot>
},
documentParsing: {
getResult: (id) => ipcRenderer.invoke(ipcChannels.documentParsingResult, { id }),
readResultImage: (id, imageId, thumbnail) => ipcRenderer.invoke(ipcChannels.documentParsingImage, { id, imageId, thumbnail }),
openResultOriginal: (id) => ipcRenderer.invoke(ipcChannels.documentParsingOriginal, { id }),
releaseResult: (id) => ipcRenderer.invoke(ipcChannels.documentParsingRelease, { id }),
cancelTest: (operationId) => ipcRenderer.invoke(ipcChannels.documentParsingCancel, { id: operationId }),
checkHttp: () => ipcRenderer.invoke(ipcChannels.documentParsingCheckHttp),
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.documentParsingGet
) as Promise<DocumentParsingSnapshot>,
getOcrModelProgress: () =>
ipcRenderer.invoke(
ipcChannels.documentOcrModelsProgress
) as Promise<DocumentOcrModelProgressSnapshot>,
update: (input: DocumentParsingSettings & { apiKey?: string; clearApiKey?: boolean }) =>
ipcRenderer.invoke(
ipcChannels.documentParsingUpdate,
input
) as Promise<DocumentParsingSnapshot>,
test: (purpose: DocumentParsingTestPurpose, operationId?: string) =>
ipcRenderer.invoke(
ipcChannels.documentParsingTest,
{ purpose, ...(operationId ? { operationId } : {}) }
) as Promise<DocumentParsingDiagnostic | undefined>,
installOcrModel: (
modelId: string,
expectedDownloadSource: ModelDownloadSource
) =>
ipcRenderer.invoke(
ipcChannels.documentOcrModelsInstall,
{ modelId, expectedDownloadSource }
) as Promise<DocumentParsingSnapshot>,
cancelOcrModelOperation: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.documentOcrModelsCancel,
{ modelId }
) as Promise<boolean>,
removeOcrModel: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.documentOcrModelsRemove,
{ modelId }
) as Promise<DocumentParsingSnapshot>,
importOcrModelArchive: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.documentOcrModelsImportArchive,
{ modelId }
) as Promise<DocumentParsingSnapshot | undefined>,
exportOcrModelArchive: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.documentOcrModelsExportArchive,
{ modelId }
) as Promise<DocumentParsingSnapshot | undefined>,
openOcrModelRepository: async (modelId: string) => {
await ipcRenderer.invoke(
ipcChannels.documentOcrModelsOpenRepository,
{ modelId }
)
},
openOcrModelsDirectory: async () => {
await ipcRenderer.invoke(
ipcChannels.documentOcrModelsOpenDirectory
)
},
getOcrAssets: (modelId: string) =>
ipcRenderer.invoke(
ipcChannels.documentParsingOcrAssets,
{ modelId }
) as Promise<DocumentOcrAssets>,
respondOcr: async (
response: DocumentOcrResult | DocumentOcrFailure
) => {
await ipcRenderer.invoke(
ipcChannels.documentParsingOcrRespond,
response
)
},
onOcrRequest: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
request: DocumentOcrRequest
): void => listener(request)
ipcRenderer.on(ipcChannels.documentParsingOcrRequest, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.documentParsingOcrRequest,
handler
)
},
onOcrCancel: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
requestId: string
): void => listener(requestId)
ipcRenderer.on(ipcChannels.documentParsingOcrCancel, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.documentParsingOcrCancel,
handler
)
}
},
projects: {
list: (includeArchived = false) =>
ipcRenderer.invoke(
ipcChannels.projectsList,
includeArchived
) as Promise<AssistantProject[]>,
create: (input: ProjectCreateInput) =>
ipcRenderer.invoke(
ipcChannels.projectsCreate,
input
) as Promise<AssistantProject>,
update: (projectId: string, input: ProjectCreateInput) =>
ipcRenderer.invoke(
ipcChannels.projectsUpdate,
{ projectId, input }
) as Promise<AssistantProject>,
setArchived: async (projectId: string, archived: boolean) => {
await ipcRenderer.invoke(ipcChannels.projectsSetArchived, {
projectId,
archived
})
},
delete: async (projectId: string, confirmation: string) => {
await ipcRenderer.invoke(ipcChannels.projectsDelete, {
projectId,
confirmation
})
},
remote: {
save: (input: RemoteProjectSaveRequest) =>
ipcRenderer.invoke(
ipcChannels.remoteProjectSave,
input
) as Promise<AssistantProject>,
cancelCurrent: async () => {
await ipcRenderer.invoke(ipcChannels.remoteProjectCancelCurrent)
},
onSaveProgress: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
progress: RemoteProjectSaveProgress
): void => listener(progress)
ipcRenderer.on(
ipcChannels.remoteProjectSaveProgress,
handler
)
return () =>
ipcRenderer.removeListener(
ipcChannels.remoteProjectSaveProgress,
handler
)
},
getRecoverySnapshot: () =>
ipcRenderer.invoke(
ipcChannels.remoteProjectRecoveryGet
) as Promise<RemoteProjectRecoverySnapshot>,
retryRecovery: (projectId: string) =>
ipcRenderer.invoke(
ipcChannels.remoteProjectRecoveryRetry,
{ projectId }
) as Promise<RemoteProjectRecoveryState>,
onRecoveryProgress: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
state: RemoteProjectRecoveryState
): void => listener(state)
ipcRenderer.on(
ipcChannels.remoteProjectRecoveryProgress,
handler
)
return () =>
ipcRenderer.removeListener(
ipcChannels.remoteProjectRecoveryProgress,
handler
)
}
}
},
conversations: {
imageOperations: {
cancel: (input) => ipcRenderer.invoke(ipcChannels.imageOperationCancel, input),
regenerate: (input) => ipcRenderer.invoke(ipcChannels.imageOperationRegenerate, input),
onChanged: (listener) => {
const handler = (_event: Electron.IpcRendererEvent, operation: import('../shared/image-generation-contracts').ImageOperation): void => listener(operation)
ipcRenderer.on(ipcChannels.imageOperationChanged, handler)
return () => ipcRenderer.removeListener(ipcChannels.imageOperationChanged, handler)
}
},
list: () =>
ipcRenderer.invoke(
ipcChannels.conversationsList
) as Promise<ConversationSnapshot[]>,
listSummaries: (detailIds = []) =>
ipcRenderer.invoke(ipcChannels.conversationsListSummaries, { detailIds }),
get: (conversationId) =>
ipcRenderer.invoke(ipcChannels.conversationsGet, conversationId),
search: (query, conversationIds = []) =>
ipcRenderer.invoke(ipcChannels.conversationsSearch, { query, conversationIds }),
replace: async (conversations: ConversationSnapshot[]) => {
await ipcRenderer.invoke(
ipcChannels.conversationsReplace,
conversations
)
},
saveLocal: async (batch: LocalConversationSaveBatch) => {
await ipcRenderer.invoke(
ipcChannels.conversationsSaveLocal,
batch
)
},
setPinned: async (input) => {
await ipcRenderer.invoke(ipcChannels.conversationsSetPinned, input)
},
setStoryGraph: async (input) => {
await ipcRenderer.invoke(ipcChannels.conversationsSetStoryGraph, input)
},
branchLocal: (input: ConversationBranchInput) =>
ipcRenderer.invoke(
ipcChannels.conversationsBranchLocal,
input
) as Promise<ConversationSnapshot>,
deleteLocal: (conversationId: string) =>
ipcRenderer.invoke(
ipcChannels.conversationsDeleteLocal,
conversationId
) as Promise<boolean>,
onChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
conversationId?: string
): void => listener(conversationId)
ipcRenderer.on(ipcChannels.conversationsChanged, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.conversationsChanged,
handler
)
}
},
conversationQueue: {
getAttachments: (itemId) => ipcRenderer.invoke(ipcChannels.conversationQueueAttachments, itemId),
restoreToDraft: (itemId, draftText) => ipcRenderer.invoke(ipcChannels.conversationQueueRestoreDraft, { itemId, draftText }),
list: (conversationId?: string) =>
ipcRenderer.invoke(
ipcChannels.conversationQueueList,
conversationId
),
enqueueUser: (input) =>
ipcRenderer.invoke(
ipcChannels.conversationQueueEnqueueUser,
input
),
remove: async (itemId: string) => {
await ipcRenderer.invoke(
ipcChannels.conversationQueueRemove,
itemId
)
},
interruptAndRun: async (itemId: string) => {
await ipcRenderer.invoke(
ipcChannels.conversationQueueInterruptAndRun,
itemId
)
},
releaseUser: async (itemId: string) => {
await ipcRenderer.invoke(
ipcChannels.conversationQueueReleaseUser,
itemId
)
},
ready: async (conversationId: string) => {
await ipcRenderer.invoke(
ipcChannels.conversationQueueReady,
conversationId
)
},
onChanged: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
conversationId?: string
): void => listener(conversationId)
ipcRenderer.on(ipcChannels.conversationQueueChanged, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.conversationQueueChanged,
handler
)
},
onDispatch: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
dispatch: Parameters<typeof listener>[0]
): void => listener(dispatch)
ipcRenderer.on(ipcChannels.conversationQueueDispatch, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.conversationQueueDispatch,
handler
)
}
},
workspace: {
importFiles: (projectId, path) => ipcRenderer.invoke(ipcChannels.workspaceImportFiles, { projectId, path }),
manage: (projectId, action) => ipcRenderer.invoke(ipcChannels.workspaceManage, { projectId, action }),
getFileDiff: (projectId: string, path: string) =>
ipcRenderer.invoke(ipcChannels.workspaceFileDiff, {
projectId,
path
}) as Promise<WorkspaceChanges>,
getChanges: (projectId: string) =>
ipcRenderer.invoke(
ipcChannels.workspaceChangesGet,
projectId
) as Promise<WorkspaceChanges>,
listDirectory: (projectId: string, path: string) =>
ipcRenderer.invoke(ipcChannels.workspaceDirectoryList, {
projectId,
path
}) as Promise<WorkspaceDirectoryListing>,
readFile: (
projectId: string,
path: string,
offsetBytes?: number
) =>
ipcRenderer.invoke(ipcChannels.workspaceFileRead, {
projectId,
path,
offsetBytes
}) as Promise<WorkspaceFilePreview>,
openPath: async (
projectId: string,
path: string,
type: 'file' | 'directory'
) => {
await ipcRenderer.invoke(ipcChannels.workspacePathOpen, {
projectId,
path,
type
})
}
},
tasks: {
list: () =>
ipcRenderer.invoke(ipcChannels.tasksList) as Promise<AssistantTask[]>,
getExecutionStats: (input) =>
ipcRenderer.invoke(ipcChannels.tasksExecutionStats, input) as Promise<ExecutionStats>,
onExecutionStatsChanged: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.tasksExecutionStatsChanged, handler)
return () => { ipcRenderer.removeListener(ipcChannels.tasksExecutionStatsChanged, handler) }
},
setStatus: async (
taskId: string,
status: 'completed' | 'cancelled'
) => {
await ipcRenderer.invoke(ipcChannels.tasksSetStatus, {
taskId,
status
})
}
},
activityHistory: {
get: () =>
ipcRenderer.invoke(
ipcChannels.activityHistoryGet
) as Promise<ActivityHistorySnapshot>,
replace: async (
records: ActivityRecord[],
legacyHistoryMayBeIncomplete: boolean
) => {
await ipcRenderer.invoke(ipcChannels.activityHistoryReplace, {
records,
legacyHistoryMayBeIncomplete
})
},
update: (update: ActivityHistoryUpdate) =>
ipcRenderer.invoke(ipcChannels.activityHistoryUpdate, update) as Promise<ActivityHistoryUpdateResult>,
clear: async () => {
await ipcRenderer.invoke(ipcChannels.activityHistoryClear)
},
page: (request: ActivityHistoryPageRequest) =>
ipcRenderer.invoke(ipcChannels.activityHistoryPage, request) as Promise<ActivityHistoryPage>,
summary: (request: ActivityHistorySummaryRequest) =>
ipcRenderer.invoke(ipcChannels.activityHistorySummary, request) as Promise<ActivityHistorySummary>,
reconcile: (request: ActivityHistoryReconcileRequest) =>
ipcRenderer.invoke(ipcChannels.activityHistoryReconcile, request) as Promise<number>
},
usage: {
getTokenSummary: () =>
ipcRenderer.invoke(
ipcChannels.tokenUsageSummary
) as Promise<TokenUsageSummary>,
onChanged: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.tokenUsageChanged, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.tokenUsageChanged, handler)
}
},
artifacts: {
list: (projectId?: string) =>
ipcRenderer.invoke(
ipcChannels.artifactsList,
projectId
) as Promise<AssistantArtifact[]>,
get: (artifactId: string) =>
ipcRenderer.invoke(
ipcChannels.artifactsGet,
artifactId
) as Promise<AssistantArtifact>,
importFiles: (projectId?: string) =>
ipcRenderer.invoke(
ipcChannels.artifactsImportFiles,
projectId
) as Promise<AssistantArtifact[]>
},
memory: {
list: (scopeId?: string) =>
ipcRenderer.invoke(
ipcChannels.memoryList,
scopeId
) as Promise<AssistantMemory[]>,
create: (input: MemoryCreateInput) =>
ipcRenderer.invoke(
ipcChannels.memoryCreate,
input
) as Promise<AssistantMemory>,
setStatus: async (
memoryId: string,
status: AssistantMemory['status']
) => {
await ipcRenderer.invoke(ipcChannels.memorySetStatus, {
memoryId,
status
})
},
remove: async (memoryId: string) => {
await ipcRenderer.invoke(ipcChannels.memoryRemove, memoryId)
}
},
schedules: {
list: (projectId?: string) =>
ipcRenderer.invoke(
ipcChannels.schedulesList,
projectId
) as Promise<AssistantSchedule[]>,
create: (input: ScheduleCreateInput) =>
ipcRenderer.invoke(
ipcChannels.schedulesCreate,
input
) as Promise<AssistantSchedule>,
setEnabled: async (scheduleId: string, enabled: boolean) => {
await ipcRenderer.invoke(ipcChannels.schedulesSetEnabled, {
scheduleId,
enabled
})
},
remove: async (scheduleId: string) => {
await ipcRenderer.invoke(ipcChannels.schedulesRemove, scheduleId)
},
runNow: async (scheduleId: string) => {
await ipcRenderer.invoke(ipcChannels.schedulesRunNow, scheduleId)
}
},
heartbeats: {
list: (projectId?: string) =>
ipcRenderer.invoke(ipcChannels.heartbeatsList, {
projectId
}) as Promise<AssistantHeartbeatConfig[]>,
create: (input: HeartbeatCreateInput) =>
ipcRenderer.invoke(
ipcChannels.heartbeatsCreate,
input
) as Promise<AssistantHeartbeatConfig>,
update: (heartbeatId: string, input: HeartbeatUpdateInput) =>
ipcRenderer.invoke(ipcChannels.heartbeatsUpdate, {
id: heartbeatId,
config: input
}) as Promise<AssistantHeartbeatConfig>,
setPaused: async (heartbeatId: string, paused: boolean) => {
await ipcRenderer.invoke(ipcChannels.heartbeatsSetPaused, {
id: heartbeatId,
paused
})
},
remove: async (heartbeatId: string) => {
await ipcRenderer.invoke(ipcChannels.heartbeatsRemove, {
id: heartbeatId
})
},
runNow: (heartbeatId: string) =>
ipcRenderer.invoke(ipcChannels.heartbeatsRunNow, {
id: heartbeatId,
idempotencyKey: crypto.randomUUID()
}) as Promise<AssistantHeartbeatRun>,
history: (heartbeatId?: string) =>
ipcRenderer.invoke(ipcChannels.heartbeatsHistory, {
configId: heartbeatId,
limit: 200
}) as Promise<{
runs: AssistantHeartbeatRun[]
entries: AssistantHeartbeatEntry[]
}>
},
supervision: {
activity: (input) => ipcRenderer.invoke(ipcChannels.supervisionActivity, input),
pause: (input) => ipcRenderer.invoke(ipcChannels.supervisionPause, input),
cancel: (input) => ipcRenderer.invoke(ipcChannels.supervisionCancel, input),
execution: () => ipcRenderer.invoke(ipcChannels.supervisionExecution),
resume: (input) => ipcRenderer.invoke(ipcChannels.supervisionResume, input),
batches: (input) => ipcRenderer.invoke(ipcChannels.supervisionBatches, input),
overview: (input) => ipcRenderer.invoke(ipcChannels.supervisionOverview, input),
run: (input: unknown) => ipcRenderer.invoke(ipcChannels.supervisionRun, input),
graph: (input) => ipcRenderer.invoke(ipcChannels.supervisionGraph, input) as Promise<Record<string, unknown>>,
source: (sourceId: string) => ipcRenderer.invoke(ipcChannels.supervisionSource, { sourceId }) as Promise<Record<string, unknown> | undefined>,
sourceContext: (sourceId: string) => ipcRenderer.invoke(ipcChannels.supervisionSourceContext, { sourceId }) as Promise<Record<string, unknown>>,
continueContext: (input) => ipcRenderer.invoke(ipcChannels.supervisionContinueContext, input) as Promise<Record<string, unknown>>,
continue: async (input) => { await ipcRenderer.invoke(ipcChannels.supervisionContinue, input) },
knowledgePreview: (input) => ipcRenderer.invoke(ipcChannels.supervisionKnowledgePreview, input) as Promise<Record<string, unknown>>,
knowledgeCommit: (input) => ipcRenderer.invoke(ipcChannels.supervisionKnowledgeCommit, input) as Promise<Record<string, unknown>>,
entityAction: (input) => ipcRenderer.invoke(ipcChannels.supervisionEntityAction, input) as Promise<void>,
relationAction: (input) => ipcRenderer.invoke(ipcChannels.supervisionRelationAction, input) as Promise<void>,
suggestions: (input) => ipcRenderer.invoke(ipcChannels.supervisionSuggestions, input ?? {}),
suggestionAction: (input) => ipcRenderer.invoke(ipcChannels.supervisionSuggestionAction, input),
retrySuggestions: (input) => ipcRenderer.invoke(ipcChannels.supervisionRetrySuggestions, input),
stories: (input) => ipcRenderer.invoke(ipcChannels.supervisionStories, input),
storyAction: (input) => ipcRenderer.invoke(ipcChannels.supervisionStoryAction, input) as Promise<void>,
retryStories: (input) => ipcRenderer.invoke(ipcChannels.supervisionRetryStories, input)
},
experts: {
list: () =>
ipcRenderer.invoke(
ipcChannels.expertsList
) as Promise<AssistantExpert[]>,
create: (input: ExpertCreateInput) =>
ipcRenderer.invoke(
ipcChannels.expertsCreate,
input
) as Promise<AssistantExpert>,
update: (expertId: string, input: ExpertUpdateInput) =>
ipcRenderer.invoke(ipcChannels.expertsUpdate, {
expertId,
input
}) as Promise<AssistantExpert>,
remove: async (expertId: string) => {
await ipcRenderer.invoke(ipcChannels.expertsRemove, expertId)
}
},
capabilities: {
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.capabilitiesSnapshot
) as Promise<CapabilitySnapshot>,
importSkill: (kind) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesImportSkill,
kind
) as Promise<CapabilitySnapshot>,
removeSkill: (skillId) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesRemoveSkill,
skillId
) as Promise<CapabilitySnapshot>,
setSkillEnabled: (skillId, enabled) =>
ipcRenderer.invoke(ipcChannels.capabilitiesToggleSkill, {
skillId,
enabled
}) as Promise<CapabilitySnapshot>,
setSkillAssignments: (skillId, assignments) =>
ipcRenderer.invoke(ipcChannels.capabilitiesAssignSkill, {
skillId,
assignments
}) as Promise<CapabilitySnapshot>,
setBuiltinMcpServerEnabled: (
serverId: BuiltinMcpServerId,
enabled: boolean
) =>
ipcRenderer.invoke(ipcChannels.capabilitiesToggleBuiltinMcp, {
serverId,
enabled
}) as Promise<CapabilitySnapshot>,
updateObsidianSettings: (input) =>
ipcRenderer.invoke(ipcChannels.capabilitiesUpdateObsidianSettings, input) as Promise<CapabilitySnapshot>,
testObsidianConnection: (input) =>
ipcRenderer.invoke(ipcChannels.capabilitiesTestObsidianConnection, input) as Promise<ObsidianConnectionTestResult>,
selectObsidianVault: () =>
ipcRenderer.invoke(ipcChannels.capabilitiesSelectObsidianVault) as Promise<string | null>,
setBuiltinMcpServerAssignments: (
serverId: BuiltinMcpServerId,
assignments
) =>
ipcRenderer.invoke(ipcChannels.capabilitiesAssignBuiltinMcp, {
serverId,
assignments
}) as Promise<CapabilitySnapshot>,
saveMcpServer: (serverId, input) =>
ipcRenderer.invoke(ipcChannels.capabilitiesSaveMcp, {
serverId,
input
}) as Promise<CapabilitySnapshot>,
removeMcpServer: (serverId) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesRemoveMcp,
serverId
) as Promise<CapabilitySnapshot>,
testMcpServer: (serverId) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesTestMcp,
serverId
) as Promise<McpServerTestResult>,
setWebSearchEnabled: (enabled: boolean) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesToggleWebSearch,
enabled
) as Promise<CapabilitySnapshot>,
testWebSearch: () =>
ipcRenderer.invoke(
ipcChannels.capabilitiesTestWebSearch
) as Promise<WebSearchTestResult>,
setComputerCapabilityEnabled: (
capabilityId: ComputerCapabilityId,
enabled: boolean
) =>
ipcRenderer.invoke(ipcChannels.capabilitiesToggleComputer, {
capabilityId,
enabled
}) as Promise<CapabilitySnapshot>,
setComputerCapabilityBrowserProfile: (
capabilityId: ComputerCapabilityId,
browserProfileId: string | null
) =>
ipcRenderer.invoke(ipcChannels.capabilitiesConfigureComputer, {
capabilityId,
browserProfileId
}) as Promise<CapabilitySnapshot>,
diagnoseComputerCapability: (capabilityId: ComputerCapabilityId) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesDiagnoseComputer,
capabilityId
) as Promise<CapabilityDiagnosticReport>,
createBrowserProfile: (input: BrowserProfileCreateInput) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesCreateBrowserProfile,
input
) as Promise<CapabilitySnapshot>,
renameBrowserProfile: (input: BrowserProfileRenameInput) =>
ipcRenderer.invoke(
ipcChannels.capabilitiesRenameBrowserProfile,
input
) as Promise<CapabilitySnapshot>,
setDefaultBrowserProfile: (profileId: string) =>
ipcRenderer.invoke(ipcChannels.capabilitiesDefaultBrowserProfile, {
profileId
}) as Promise<CapabilitySnapshot>,
removeBrowserProfile: (profileId: string) =>
ipcRenderer.invoke(ipcChannels.capabilitiesRemoveBrowserProfile, {
profileId
}) as Promise<CapabilitySnapshot>
},
runtimeExtensions: {
getSnapshot: () =>
ipcRenderer.invoke(
ipcChannels.runtimeExtensionsSnapshot
) as Promise<RuntimeExtensionMarketplaceSnapshot>,
apply: (action: RuntimeExtensionAction) =>
ipcRenderer.invoke(
ipcChannels.runtimeExtensionsApply,
action
) as Promise<RuntimeExtensionMarketplaceSnapshot>
},
runtimeCustomization: {
getSettings: () =>
ipcRenderer.invoke(
ipcChannels.runtimeCustomizationGet
) as Promise<RuntimeCustomizationSettings>,
updateSettings: (settings: RuntimeCustomizationSettings) =>
ipcRenderer.invoke(
ipcChannels.runtimeCustomizationUpdate,
settings
) as Promise<RuntimeCustomizationSettings>,
getNativeSnapshot: (input: RuntimeNativeSnapshotInput) =>
ipcRenderer.invoke(
ipcChannels.runtimeNativeSnapshot,
input
) as Promise<RuntimeNativeSnapshot>
},
context: {
imageCapability: (conversationId, runtimeSelection) => ipcRenderer.invoke(ipcChannels.contextImageCapability, { conversationId, runtimeSelection }),
copyToDraft: (conversationId, id, operationId) => ipcRenderer.invoke(ipcChannels.contextCopyToDraft, { conversationId, id, operationId }),
pendingParsing: (conversationId) => ipcRenderer.invoke(ipcChannels.contextPendingParsing, conversationId),
retryParsing: (conversationId, id, operationId) => ipcRenderer.invoke(ipcChannels.contextRetryParsing, { conversationId, id, operationId }),
dismissParsing: (conversationId, id) => ipcRenderer.invoke(ipcChannels.contextDismissParsing, { conversationId, id }),
cancelImport: (operationId) => ipcRenderer.invoke(ipcChannels.contextCancelImport, operationId),
reparseDraft: (conversationId, id, operationId) => ipcRenderer.invoke(ipcChannels.contextReparse, { conversationId, id, operationId }),
cancelParsing: (operationId) => ipcRenderer.invoke(ipcChannels.contextCancelParsing, operationId),
sendOriginal: (conversationId, id) => ipcRenderer.invoke(ipcChannels.contextSendOriginal, { conversationId, id }),
openOriginal: (id) => ipcRenderer.invoke(ipcChannels.contextOpenOriginal, id),
readOutput: (input) => ipcRenderer.invoke(ipcChannels.contextReadOutput, input),
addResultImages: (conversationId, resultId, imageIds) => ipcRenderer.invoke(ipcChannels.contextAddResultImages, { conversationId, resultId, imageIds }),
onDraftChanged: (listener) => {
const handler = (_event: Electron.IpcRendererEvent, conversationId: string, attachments: ContextAttachment[]): void => listener(conversationId, attachments)
ipcRenderer.on(ipcChannels.contextDraftChanged, handler)
return () => ipcRenderer.removeListener(ipcChannels.contextDraftChanged, handler)
},
getDraft: (conversationId) => ipcRenderer.invoke(ipcChannels.contextGetDraft, { conversationId }),
saveDraft: (conversationId, ids) => ipcRenderer.invoke(ipcChannels.contextSaveDraft, { conversationId, ids }),
getFilePath: (file) => webUtils.getPathForFile(file),
importFiles: (paths, conversationId) =>
ipcRenderer.invoke(
ipcChannels.contextImportFiles,
{ paths, ...(conversationId ? { conversationId } : {}) }
) as Promise<ContextAttachment[]>,
selectFiles: (conversationId) =>
ipcRenderer.invoke(
ipcChannels.contextSelectFiles,
...(conversationId ? [conversationId] : [])
) as Promise<ContextAttachment[]>,
onFileSelectionProgress: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
progress: ContextFileSelectionProgress
): void => listener(progress)
ipcRenderer.on(ipcChannels.contextFileSelectionProgress, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.contextFileSelectionProgress,
handler
)
},
addPastedImage: (input: PastedImageInput) =>
ipcRenderer.invoke(
ipcChannels.contextAddPastedImage,
input
) as Promise<ContextAttachment>,
captureScreen: () =>
ipcRenderer.invoke(
ipcChannels.contextCaptureScreen
) as Promise<ContextAttachment>,
listWindows: () =>
ipcRenderer.invoke(
ipcChannels.contextListWindows
) as Promise<WindowCaptureOption[]>,
captureWindow: (sourceId) =>
ipcRenderer.invoke(
ipcChannels.contextCaptureWindow,
{ sourceId }
) as Promise<ContextAttachment>,
readClipboard: () =>
ipcRenderer.invoke(
ipcChannels.contextReadClipboard
) as Promise<ContextAttachment>,
remove: async (contextId: string) => {
await ipcRenderer.invoke(ipcChannels.contextRemove, contextId)
}
},
magicNotes: {
search: (input) =>
ipcRenderer.invoke(ipcChannels.magicNotesSearch, input) as Promise<MagicNoteSummary[]>,
list: () =>
ipcRenderer.invoke(
ipcChannels.magicNotesList
) as Promise<MagicNotesSnapshot>,
get: (noteId: string) =>
ipcRenderer.invoke(ipcChannels.magicNotesGet, {
noteId
}) as Promise<MagicNoteDetail>,
create: (input) =>
ipcRenderer.invoke(
ipcChannels.magicNotesCreate,
input
) as Promise<MagicNoteCreateResult>,
update: (input) =>
ipcRenderer.invoke(
ipcChannels.magicNotesUpdate,
input
) as Promise<MagicNoteDetail>,
remove: async (noteId: string) => {
await ipcRenderer.invoke(ipcChannels.magicNotesDelete, { noteId })
},
createTag: (input) =>
ipcRenderer.invoke(
ipcChannels.magicNotesCreateTag,
input
) as Promise<MagicNoteTag>,
renameTag: (input) =>
ipcRenderer.invoke(
ipcChannels.magicNotesRenameTag,
input
) as Promise<MagicNoteTagRenameResult>,
removeTag: async (tagId: string) => {
await ipcRenderer.invoke(ipcChannels.magicNotesDeleteTag, { tagId })
},
createEntry: (input) =>
ipcRenderer.invoke(
ipcChannels.magicNotesCreateEntry,
input
) as Promise<MagicNoteEntryCreateResult>,
updateEntry: (input) =>
ipcRenderer.invoke(
ipcChannels.magicNotesUpdateEntry,
input
) as Promise<MagicNoteDetail>,
removeEntry: (entryId: string) =>
ipcRenderer.invoke(ipcChannels.magicNotesDeleteEntry, {
entryId
}) as Promise<MagicNoteDetail>,
analyze: (entryId, options) =>
ipcRenderer.invoke(ipcChannels.magicNotesAnalyze, {
entryId,
...options
}) as Promise<MagicNoteDetail>,
analyzeDraft: (content, options) =>
ipcRenderer.invoke(
ipcChannels.magicNotesAnalyzeDraft,
{ content, ...options }
) as Promise<MagicNoteDraftAnalysis>,
listTodos: () =>
ipcRenderer.invoke(
ipcChannels.magicTodosList
) as Promise<MagicTodosSnapshot>,
getTodoStatus: () =>
ipcRenderer.invoke(
ipcChannels.magicTodosStatus
) as Promise<MagicTodoStatus>,
updateTodo: (input) =>
ipcRenderer.invoke(
ipcChannels.magicTodosUpdate,
input
) as Promise<MagicTodoUpdateResult>,
analyzeTodo: (todoId, options) =>
ipcRenderer.invoke(ipcChannels.magicTodosAnalyze, {
todoId,
...options
}) as Promise<MagicTodoItem>,
onAnalysisEvent: (listener) => {
const handler = (
_event: Electron.IpcRendererEvent,
payload: MagicNoteAnalysisStreamEvent
): void => listener(payload)
ipcRenderer.on(ipcChannels.magicNotesAnalysisEvent, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.magicNotesAnalysisEvent,
handler
)
},
onChanged: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.magicNotesChanged, handler)
return () =>
ipcRenderer.removeListener(ipcChannels.magicNotesChanged, handler)
},
onTodoStatusChanged: (listener) => {
const handler = (): void => listener()
ipcRenderer.on(ipcChannels.magicTodosStatusChanged, handler)
return () =>
ipcRenderer.removeListener(
ipcChannels.magicTodosStatusChanged,
handler
)
}
},
knowledge: {
externalInstancesList: () => ipcRenderer.invoke(ipcChannels.externalInstancesList),
externalInstancesSave: (input) => ipcRenderer.invoke(ipcChannels.externalInstancesSave,input),
externalInstancesTest: (input) => ipcRenderer.invoke(ipcChannels.externalInstancesTest,input),
externalInstancesSetEnabled: (input) => ipcRenderer.invoke(ipcChannels.externalInstancesSetEnabled,input),
externalInstancesDelete: (input) => ipcRenderer.invoke(ipcChannels.externalInstancesDelete,input),
externalCatalogList: (input) => ipcRenderer.invoke(ipcChannels.externalCatalogList,input),
externalCatalogGet: (input) => ipcRenderer.invoke(ipcChannels.externalCatalogGet,input),
externalBindingsCreate: (input) => ipcRenderer.invoke(ipcChannels.externalBindingsCreate,input),
externalBindingsUpdate: (input) => ipcRenderer.invoke(ipcChannels.externalBindingsUpdate,input),
externalRetrievalTest: (input) => ipcRenderer.invoke(ipcChannels.externalRetrievalTest,input),
getSnapshot: (libraryId?: string) =>
ipcRenderer.invoke(
ipcChannels.knowledgeSnapshot,
libraryId
) as Promise<KnowledgeSnapshot>,
createLibrary: (input) =>
ipcRenderer.invoke(
ipcChannels.knowledgeCreateLibrary,
input
) as Promise<KnowledgeLibrary>,
updateLibrary: async (libraryId, update) => {
await ipcRenderer.invoke(ipcChannels.knowledgeUpdateLibrary, {
libraryId,
...update
})
},
deleteLibrary: async (libraryId) => {
await ipcRenderer.invoke(
ipcChannels.knowledgeDeleteLibrary,
libraryId
)
},
reextractGraph: (libraryId) =>
ipcRenderer.invoke(
ipcChannels.knowledgeReextractGraph,
libraryId
) as Promise<void>,
selectFiles: async (libraryId, graphStrategy) => {
await ipcRenderer.invoke(ipcChannels.knowledgeSelectFiles, {
libraryId,
graphStrategy
})
},
selectDirectory: async (libraryId, graphStrategy) => {
await ipcRenderer.invoke(
ipcChannels.knowledgeSelectDirectory,
{ libraryId, graphStrategy }
)
},
importDroppedFiles: async (libraryId, files, graphStrategy) => {
const paths = files
.map((file) => webUtils.getPathForFile(file))
.filter(Boolean)
await ipcRenderer.invoke(ipcChannels.knowledgeImportPaths, {
libraryId,
paths,
graphStrategy
})
},
importPaths: async (libraryId, paths, graphStrategy) => {
await ipcRenderer.invoke(ipcChannels.knowledgeImportPaths, {
libraryId,
paths,
graphStrategy
})
},
importUrl: async (libraryId, url, graphStrategy) => {
await ipcRenderer.invoke(ipcChannels.knowledgeImportUrl, {
libraryId,
url,
graphStrategy
})
},
syncSource: async (sourceId) => {
await ipcRenderer.invoke(ipcChannels.knowledgeSyncSource, sourceId)
},
pauseSource: async (sourceId) => {
await ipcRenderer.invoke(ipcChannels.knowledgePauseSource, sourceId)
},
retrySource: async (sourceId) => {
await ipcRenderer.invoke(ipcChannels.knowledgeRetrySource, sourceId)
},
removeSource: async (sourceId) => {
await ipcRenderer.invoke(ipcChannels.knowledgeRemoveSource, sourceId)
},
search: (libraryIds, query) =>
ipcRenderer.invoke(ipcChannels.knowledgeSearch, {
libraryIds,
query
}) as Promise<KnowledgeSearchReference[]>,
retrieve: (input: KnowledgeRetrieveInput) =>
ipcRenderer.invoke(
ipcChannels.knowledgeRetrieve,
input
) as Promise<KnowledgeRetrievalResponse>,
updateSettings: (input: KnowledgeSettingsUpdateInput) =>
ipcRenderer.invoke(
ipcChannels.knowledgeUpdateSettings,
input
) as Promise<KnowledgeLibrary>,
listChunks: (input: KnowledgeChunksListInput) =>
ipcRenderer.invoke(
ipcChannels.knowledgeListChunks,
input
) as Promise<KnowledgeChunkPage>,
updateChunk: async (input: KnowledgeChunkUpdateInput) => {
await ipcRenderer.invoke(ipcChannels.knowledgeUpdateChunk, input)
},
deleteChunk: async (input: KnowledgeChunkDeleteInput) => {
await ipcRenderer.invoke(ipcChannels.knowledgeDeleteChunk, input)
},
rebuildDocument: (input: KnowledgeDocumentRebuildInput) =>
ipcRenderer.invoke(
ipcChannels.knowledgeRebuildDocument,
input
) as Promise<KnowledgeSnapshot>,
openDocumentSource: async (input: KnowledgeDocumentOpenInput) => {
await ipcRenderer.invoke(
ipcChannels.knowledgeOpenDocumentSource,
input
)
},
rebuildLibrary: (input: KnowledgeLibraryRebuildInput) =>
ipcRenderer.invoke(
ipcChannels.knowledgeRebuildLibrary,
input
) as Promise<{ rebuilt: number; failed: number }>,
cancelRebuild: (knowledgeBaseId: string) =>
ipcRenderer.invoke(
ipcChannels.knowledgeCancelRebuild,
knowledgeBaseId
) as Promise<boolean>,
getEmbeddingIndex: (knowledgeBaseId: string) =>
ipcRenderer.invoke(ipcChannels.knowledgeEmbeddingIndexGet, {
knowledgeBaseId
}) as Promise<KnowledgeEmbeddingIndexSnapshot>,
rebuildEmbeddingIndex: (knowledgeBaseId: string) =>
ipcRenderer.invoke(
ipcChannels.knowledgeEmbeddingIndexRebuild,
{ knowledgeBaseId }
) as Promise<KnowledgeEmbeddingIndexSnapshot>,
cancelEmbeddingIndex: (
knowledgeBaseId: string,
jobId: string
) =>
ipcRenderer.invoke(
ipcChannels.knowledgeEmbeddingIndexCancel,
{ knowledgeBaseId, jobId }
) as Promise<boolean>,
cancelTask: (taskId: string) =>
ipcRenderer.invoke(
ipcChannels.knowledgeTaskCancel,
{ taskId }
) as Promise<boolean>,
retryTask: async (taskId: string) => {
await ipcRenderer.invoke(
ipcChannels.knowledgeTaskRetry,
{ taskId }
)
},
getReferenceContext: (input: KnowledgeReferenceContextInput) =>
ipcRenderer.invoke(
ipcChannels.knowledgeReferenceContext,
input
) as Promise<KnowledgeReferenceContext>,
openReferenceSource: async (input: KnowledgeReferenceOpenInput) => {
await ipcRenderer.invoke(
ipcChannels.knowledgeOpenReferenceSource,
input
)
},
createEntity: async (libraryId, input) => {
await ipcRenderer.invoke(ipcChannels.knowledgeCreateEntity, {
libraryId,
input
})
},
updateEntity: async (entityId, update) => {
await ipcRenderer.invoke(ipcChannels.knowledgeUpdateEntity, {
entityId,
update
})
},
moveEntity: async (entityId, position) => {
await ipcRenderer.invoke(ipcChannels.knowledgeMoveEntity, {
entityId,
position
})
},
deleteEntity: async (entityId) => {
await ipcRenderer.invoke(ipcChannels.knowledgeDeleteEntity, entityId)
},
mergeEntities: async (sourceEntityId, targetEntityId) => {
await ipcRenderer.invoke(ipcChannels.knowledgeMergeEntities, {
sourceEntityId,
targetEntityId
})
},
createRelation: async (libraryId, input) => {
await ipcRenderer.invoke(ipcChannels.knowledgeCreateRelation, {
libraryId,
input
})
},
updateRelation: async (relationId, input) => {
await ipcRenderer.invoke(ipcChannels.knowledgeUpdateRelation, {
relationId,
input
})
},
deleteRelation: async (relationId) => {
await ipcRenderer.invoke(
ipcChannels.knowledgeDeleteRelation,
relationId
)
}
}
}
contextBridge.exposeInMainWorld('goodbuddy', desktopApi)