@wrongstack/desktop 0.284.1 → 0.285.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -87,4 +87,4 @@ var api = {
87
87
  }
88
88
  };
89
89
  import_electron.contextBridge.exposeInMainWorld("wrongstackDesktop", api);
90
- //# sourceMappingURL=preload.cjs.map
90
+ //# sourceMappingURL=preload.cjs.map
@@ -1 +1,7 @@
1
- {"version":3,"sources":["../../src/main/preload.ts","../../src/main/ipc.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron';\nimport type {\n DesktopConversationSnapshot,\n DesktopStateSnapshot,\n DesktopWebuiStatusSnapshot,\n DesktopWebuiCommand,\n WrongStackDesktopApi,\n} from '../shared/types.js';\nimport { IPC } from './ipc.js';\n\nconst api: WrongStackDesktopApi = {\n getState: () => ipcRenderer.invoke(IPC.getState),\n getConversation: (runtimeId: string) => ipcRenderer.invoke(IPC.getConversation, runtimeId),\n getWebuiStatus: () => ipcRenderer.invoke(IPC.getWebuiStatus),\n openProject: (root?: string) => ipcRenderer.invoke(IPC.openProject, root),\n registerProject: (root?: string) => ipcRenderer.invoke(IPC.registerProject, root),\n unregisterProject: (root: string) => ipcRenderer.invoke(IPC.unregisterProject, root),\n openProjectSession: (runtimeId?: string) => ipcRenderer.invoke(IPC.openProjectSession, runtimeId),\n activateRuntime: (id: string) => ipcRenderer.invoke(IPC.activateRuntime, id),\n closeRuntime: (id: string) => ipcRenderer.invoke(IPC.closeRuntime, id),\n navigateWebui: (command: DesktopWebuiCommand) => ipcRenderer.invoke(IPC.navigateWebui, command),\n reloadWebui: () => ipcRenderer.invoke(IPC.reloadWebui),\n setShellSidebarCollapsed: (collapsed: boolean) =>\n ipcRenderer.invoke(IPC.setShellSidebarCollapsed, collapsed),\n openSettings: () => ipcRenderer.invoke(IPC.openSettings),\n sendMessage: (runtimeId: string, content: string) =>\n ipcRenderer.invoke(IPC.sendMessage, runtimeId, content),\n abortRuntime: (runtimeId: string) => ipcRenderer.invoke(IPC.abortRuntime, runtimeId),\n openRuntimeInBrowser: (id: string) => ipcRenderer.invoke(IPC.openRuntimeInBrowser, id),\n revealRuntimeRoot: (id: string) => ipcRenderer.invoke(IPC.revealRuntimeRoot, id),\n onStateChanged: (cb: (state: DesktopStateSnapshot) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, state: DesktopStateSnapshot) => cb(state);\n ipcRenderer.on(IPC.stateChanged, handler);\n return () => ipcRenderer.off(IPC.stateChanged, handler);\n },\n onConversationChanged: (cb) => {\n const handler = (\n _event: Electron.IpcRendererEvent,\n conversation: DesktopConversationSnapshot,\n ) => cb(conversation);\n ipcRenderer.on(IPC.conversationChanged, handler);\n return () => ipcRenderer.off(IPC.conversationChanged, handler);\n },\n onWebuiStatusChanged: (cb: (status: DesktopWebuiStatusSnapshot) => void) => {\n const handler = (\n _event: Electron.IpcRendererEvent,\n status: DesktopWebuiStatusSnapshot,\n ) => cb(status);\n ipcRenderer.on(IPC.webuiStatusChanged, handler);\n return () => ipcRenderer.off(IPC.webuiStatusChanged, handler);\n },\n onShellSidebarCollapsedChanged: (cb: (collapsed: boolean) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, collapsed: boolean) => cb(collapsed);\n ipcRenderer.on(IPC.shellSidebarCollapsedChanged, handler);\n return () => ipcRenderer.off(IPC.shellSidebarCollapsedChanged, handler);\n },\n setLocale: (locale: string) => {\n ipcRenderer.send(IPC.setLocale, locale);\n },\n onLocaleChanged: (cb: (locale: string) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, locale: string) => cb(locale);\n ipcRenderer.on(IPC.localeChanged, handler);\n return () => ipcRenderer.off(IPC.localeChanged, handler);\n },\n};\n\ncontextBridge.exposeInMainWorld('wrongstackDesktop', api);\n","export const IPC = {\n getState: 'desktop:get-state',\n getConversation: 'desktop:get-conversation',\n getWebuiStatus: 'desktop:get-webui-status',\n openProject: 'desktop:open-project',\n registerProject: 'desktop:register-project',\n unregisterProject: 'desktop:unregister-project',\n openProjectSession: 'desktop:open-project-session',\n activateRuntime: 'desktop:activate-runtime',\n closeRuntime: 'desktop:close-runtime',\n navigateWebui: 'desktop:navigate-webui',\n reloadWebui: 'desktop:reload-webui',\n setShellSidebarCollapsed: 'desktop:set-shell-sidebar-collapsed',\n openSettings: 'desktop:open-settings',\n sendMessage: 'desktop:send-message',\n abortRuntime: 'desktop:abort-runtime',\n openRuntimeInBrowser: 'desktop:open-runtime-in-browser',\n revealRuntimeRoot: 'desktop:reveal-runtime-root',\n stateChanged: 'desktop:state-changed',\n conversationChanged: 'desktop:conversation-changed',\n webuiStatusChanged: 'desktop:webui-status-changed',\n webuiReadyChanged: 'desktop:webui-ready-changed',\n webuiPrefsChanged: 'desktop:webui-prefs-changed',\n webuiCommandAck: 'desktop:webui-command-ack',\n webuiCommand: 'desktop:webui-command',\n shellSidebarCollapsedChanged: 'desktop:shell-sidebar-collapsed-changed',\n setLocale: 'desktop:set-locale',\n localeChanged: 'desktop:locale-changed',\n // Embedded WebUI view side — the desktop shell pushes locale changes here\n // so the React WebUI inside Electron can swap i18n instantly, without waiting\n // for the config-file watcher → WS prefs.updated round-trip.\n webuiLocaleChanged: 'desktop:webui-locale-changed',\n} as const;\n"],"mappings":"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","names":[]}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/main/preload.ts", "../../src/main/ipc.ts"],
4
+ "sourcesContent": ["import { contextBridge, ipcRenderer } from 'electron';\nimport type {\n DesktopConversationSnapshot,\n DesktopStateSnapshot,\n DesktopWebuiStatusSnapshot,\n DesktopWebuiCommand,\n WrongStackDesktopApi,\n} from '../shared/types.js';\nimport { IPC } from './ipc.js';\n\nconst api: WrongStackDesktopApi = {\n getState: () => ipcRenderer.invoke(IPC.getState),\n getConversation: (runtimeId: string) => ipcRenderer.invoke(IPC.getConversation, runtimeId),\n getWebuiStatus: () => ipcRenderer.invoke(IPC.getWebuiStatus),\n openProject: (root?: string) => ipcRenderer.invoke(IPC.openProject, root),\n registerProject: (root?: string) => ipcRenderer.invoke(IPC.registerProject, root),\n unregisterProject: (root: string) => ipcRenderer.invoke(IPC.unregisterProject, root),\n openProjectSession: (runtimeId?: string) => ipcRenderer.invoke(IPC.openProjectSession, runtimeId),\n activateRuntime: (id: string) => ipcRenderer.invoke(IPC.activateRuntime, id),\n closeRuntime: (id: string) => ipcRenderer.invoke(IPC.closeRuntime, id),\n navigateWebui: (command: DesktopWebuiCommand) => ipcRenderer.invoke(IPC.navigateWebui, command),\n reloadWebui: () => ipcRenderer.invoke(IPC.reloadWebui),\n setShellSidebarCollapsed: (collapsed: boolean) =>\n ipcRenderer.invoke(IPC.setShellSidebarCollapsed, collapsed),\n openSettings: () => ipcRenderer.invoke(IPC.openSettings),\n sendMessage: (runtimeId: string, content: string) =>\n ipcRenderer.invoke(IPC.sendMessage, runtimeId, content),\n abortRuntime: (runtimeId: string) => ipcRenderer.invoke(IPC.abortRuntime, runtimeId),\n openRuntimeInBrowser: (id: string) => ipcRenderer.invoke(IPC.openRuntimeInBrowser, id),\n revealRuntimeRoot: (id: string) => ipcRenderer.invoke(IPC.revealRuntimeRoot, id),\n onStateChanged: (cb: (state: DesktopStateSnapshot) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, state: DesktopStateSnapshot) => cb(state);\n ipcRenderer.on(IPC.stateChanged, handler);\n return () => ipcRenderer.off(IPC.stateChanged, handler);\n },\n onConversationChanged: (cb) => {\n const handler = (\n _event: Electron.IpcRendererEvent,\n conversation: DesktopConversationSnapshot,\n ) => cb(conversation);\n ipcRenderer.on(IPC.conversationChanged, handler);\n return () => ipcRenderer.off(IPC.conversationChanged, handler);\n },\n onWebuiStatusChanged: (cb: (status: DesktopWebuiStatusSnapshot) => void) => {\n const handler = (\n _event: Electron.IpcRendererEvent,\n status: DesktopWebuiStatusSnapshot,\n ) => cb(status);\n ipcRenderer.on(IPC.webuiStatusChanged, handler);\n return () => ipcRenderer.off(IPC.webuiStatusChanged, handler);\n },\n onShellSidebarCollapsedChanged: (cb: (collapsed: boolean) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, collapsed: boolean) => cb(collapsed);\n ipcRenderer.on(IPC.shellSidebarCollapsedChanged, handler);\n return () => ipcRenderer.off(IPC.shellSidebarCollapsedChanged, handler);\n },\n setLocale: (locale: string) => {\n ipcRenderer.send(IPC.setLocale, locale);\n },\n onLocaleChanged: (cb: (locale: string) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, locale: string) => cb(locale);\n ipcRenderer.on(IPC.localeChanged, handler);\n return () => ipcRenderer.off(IPC.localeChanged, handler);\n },\n};\n\ncontextBridge.exposeInMainWorld('wrongstackDesktop', api);\n", "export const IPC = {\n getState: 'desktop:get-state',\n getConversation: 'desktop:get-conversation',\n getWebuiStatus: 'desktop:get-webui-status',\n openProject: 'desktop:open-project',\n registerProject: 'desktop:register-project',\n unregisterProject: 'desktop:unregister-project',\n openProjectSession: 'desktop:open-project-session',\n activateRuntime: 'desktop:activate-runtime',\n closeRuntime: 'desktop:close-runtime',\n navigateWebui: 'desktop:navigate-webui',\n reloadWebui: 'desktop:reload-webui',\n setShellSidebarCollapsed: 'desktop:set-shell-sidebar-collapsed',\n openSettings: 'desktop:open-settings',\n sendMessage: 'desktop:send-message',\n abortRuntime: 'desktop:abort-runtime',\n openRuntimeInBrowser: 'desktop:open-runtime-in-browser',\n revealRuntimeRoot: 'desktop:reveal-runtime-root',\n stateChanged: 'desktop:state-changed',\n conversationChanged: 'desktop:conversation-changed',\n webuiStatusChanged: 'desktop:webui-status-changed',\n webuiReadyChanged: 'desktop:webui-ready-changed',\n webuiPrefsChanged: 'desktop:webui-prefs-changed',\n webuiCommandAck: 'desktop:webui-command-ack',\n webuiCommand: 'desktop:webui-command',\n shellSidebarCollapsedChanged: 'desktop:shell-sidebar-collapsed-changed',\n setLocale: 'desktop:set-locale',\n localeChanged: 'desktop:locale-changed',\n // Embedded WebUI view side \u2014 the desktop shell pushes locale changes here\n // so the React WebUI inside Electron can swap i18n instantly, without waiting\n // for the config-file watcher \u2192 WS prefs.updated round-trip.\n webuiLocaleChanged: 'desktop:webui-locale-changed',\n} as const;\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -81,4 +81,4 @@ import_electron.ipcRenderer.on(IPC.webuiCommand, (_event, command) => {
81
81
  });
82
82
  import_electron.contextBridge.exposeInMainWorld("wrongstackDesktopHost", api);
83
83
  import_electron.contextBridge.exposeInMainWorld("wrongstackDesktopCommands", commandApi);
84
- //# sourceMappingURL=webui-preload.cjs.map
84
+ //# sourceMappingURL=webui-preload.cjs.map
@@ -1 +1,7 @@
1
- {"version":3,"sources":["../../src/main/webui-preload.ts","../../src/main/ipc.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron';\nimport type {\n WrongStackDesktopCommandApi,\n DesktopWebuiCommand,\n DesktopWebuiPrefs,\n WrongStackDesktopHostApi,\n} from '../shared/types.js';\nimport { IPC } from './ipc.js';\n\nconst api: WrongStackDesktopHostApi = {\n setReady: (ready: boolean) => {\n ipcRenderer.send(IPC.webuiReadyChanged, ready);\n },\n setPrefs: (prefs: DesktopWebuiPrefs) => {\n ipcRenderer.send(IPC.webuiPrefsChanged, prefs);\n },\n ackCommand: (requestId: string, handled: boolean, message?: string | undefined) => {\n ipcRenderer.send(IPC.webuiCommandAck, requestId, handled, message);\n },\n onLocaleChanged: (cb: (locale: string) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, locale: string) => cb(locale);\n ipcRenderer.on(IPC.webuiLocaleChanged, handler);\n return () => {\n ipcRenderer.off(IPC.webuiLocaleChanged, handler);\n };\n },\n};\n\nconst commandListeners = new Set<(command: DesktopWebuiCommand) => void>();\nconst commandApi: WrongStackDesktopCommandApi = {\n subscribe: (cb: (command: DesktopWebuiCommand) => void) => {\n commandListeners.add(cb);\n return () => {\n commandListeners.delete(cb);\n };\n },\n};\n\nipcRenderer.on(IPC.webuiCommand, (_event, command: DesktopWebuiCommand) => {\n for (const listener of [...commandListeners]) {\n try {\n listener(command);\n } catch {\n /* The WebUI owns command handling errors. Keep the bridge alive. */\n }\n }\n window.dispatchEvent(\n new CustomEvent('wrongstack:desktop-command', {\n detail: command,\n }),\n );\n});\n\ncontextBridge.exposeInMainWorld('wrongstackDesktopHost', api);\ncontextBridge.exposeInMainWorld('wrongstackDesktopCommands', commandApi);\n","export const IPC = {\n getState: 'desktop:get-state',\n getConversation: 'desktop:get-conversation',\n getWebuiStatus: 'desktop:get-webui-status',\n openProject: 'desktop:open-project',\n registerProject: 'desktop:register-project',\n unregisterProject: 'desktop:unregister-project',\n openProjectSession: 'desktop:open-project-session',\n activateRuntime: 'desktop:activate-runtime',\n closeRuntime: 'desktop:close-runtime',\n navigateWebui: 'desktop:navigate-webui',\n reloadWebui: 'desktop:reload-webui',\n setShellSidebarCollapsed: 'desktop:set-shell-sidebar-collapsed',\n openSettings: 'desktop:open-settings',\n sendMessage: 'desktop:send-message',\n abortRuntime: 'desktop:abort-runtime',\n openRuntimeInBrowser: 'desktop:open-runtime-in-browser',\n revealRuntimeRoot: 'desktop:reveal-runtime-root',\n stateChanged: 'desktop:state-changed',\n conversationChanged: 'desktop:conversation-changed',\n webuiStatusChanged: 'desktop:webui-status-changed',\n webuiReadyChanged: 'desktop:webui-ready-changed',\n webuiPrefsChanged: 'desktop:webui-prefs-changed',\n webuiCommandAck: 'desktop:webui-command-ack',\n webuiCommand: 'desktop:webui-command',\n shellSidebarCollapsedChanged: 'desktop:shell-sidebar-collapsed-changed',\n setLocale: 'desktop:set-locale',\n localeChanged: 'desktop:locale-changed',\n // Embedded WebUI view side — the desktop shell pushes locale changes here\n // so the React WebUI inside Electron can swap i18n instantly, without waiting\n // for the config-file watcher → WS prefs.updated round-trip.\n webuiLocaleChanged: 'desktop:webui-locale-changed',\n} as const;\n"],"mappings":";;;AAAA,sBAA2C;;;ACApC,IAAM,MAAM;AAAA,EACjB,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,aAAa;AAAA,EACb,0BAA0B;AAAA,EAC1B,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,8BAA8B;AAAA,EAC9B,WAAW;AAAA,EACX,eAAe;AAAA;AAAA;AAAA;AAAA,EAIf,oBAAoB;AACtB;;;ADvBA,IAAM,MAAgC;AAAA,EACpC,UAAU,CAAC,UAAmB;AAC5B,gCAAY,KAAK,IAAI,mBAAmB,KAAK;AAAA,EAC/C;AAAA,EACA,UAAU,CAAC,UAA6B;AACtC,gCAAY,KAAK,IAAI,mBAAmB,KAAK;AAAA,EAC/C;AAAA,EACA,YAAY,CAAC,WAAmB,SAAkB,YAAiC;AACjF,gCAAY,KAAK,IAAI,iBAAiB,WAAW,SAAS,OAAO;AAAA,EACnE;AAAA,EACA,iBAAiB,CAAC,OAAiC;AACjD,UAAM,UAAU,CAAC,QAAmC,WAAmB,GAAG,MAAM;AAChF,gCAAY,GAAG,IAAI,oBAAoB,OAAO;AAC9C,WAAO,MAAM;AACX,kCAAY,IAAI,IAAI,oBAAoB,OAAO;AAAA,IACjD;AAAA,EACF;AACF;AAEA,IAAM,mBAAmB,oBAAI,IAA4C;AACzE,IAAM,aAA0C;AAAA,EAC9C,WAAW,CAAC,OAA+C;AACzD,qBAAiB,IAAI,EAAE;AACvB,WAAO,MAAM;AACX,uBAAiB,OAAO,EAAE;AAAA,IAC5B;AAAA,EACF;AACF;AAEA,4BAAY,GAAG,IAAI,cAAc,CAAC,QAAQ,YAAiC;AACzE,aAAW,YAAY,CAAC,GAAG,gBAAgB,GAAG;AAC5C,QAAI;AACF,eAAS,OAAO;AAAA,IAClB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,YAAY,8BAA8B;AAAA,MAC5C,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF,CAAC;AAED,8BAAc,kBAAkB,yBAAyB,GAAG;AAC5D,8BAAc,kBAAkB,6BAA6B,UAAU;","names":[]}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/main/webui-preload.ts", "../../src/main/ipc.ts"],
4
+ "sourcesContent": ["import { contextBridge, ipcRenderer } from 'electron';\nimport type {\n WrongStackDesktopCommandApi,\n DesktopWebuiCommand,\n DesktopWebuiPrefs,\n WrongStackDesktopHostApi,\n} from '../shared/types.js';\nimport { IPC } from './ipc.js';\n\nconst api: WrongStackDesktopHostApi = {\n setReady: (ready: boolean) => {\n ipcRenderer.send(IPC.webuiReadyChanged, ready);\n },\n setPrefs: (prefs: DesktopWebuiPrefs) => {\n ipcRenderer.send(IPC.webuiPrefsChanged, prefs);\n },\n ackCommand: (requestId: string, handled: boolean, message?: string | undefined) => {\n ipcRenderer.send(IPC.webuiCommandAck, requestId, handled, message);\n },\n onLocaleChanged: (cb: (locale: string) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, locale: string) => cb(locale);\n ipcRenderer.on(IPC.webuiLocaleChanged, handler);\n return () => {\n ipcRenderer.off(IPC.webuiLocaleChanged, handler);\n };\n },\n};\n\nconst commandListeners = new Set<(command: DesktopWebuiCommand) => void>();\nconst commandApi: WrongStackDesktopCommandApi = {\n subscribe: (cb: (command: DesktopWebuiCommand) => void) => {\n commandListeners.add(cb);\n return () => {\n commandListeners.delete(cb);\n };\n },\n};\n\nipcRenderer.on(IPC.webuiCommand, (_event, command: DesktopWebuiCommand) => {\n for (const listener of [...commandListeners]) {\n try {\n listener(command);\n } catch {\n /* The WebUI owns command handling errors. Keep the bridge alive. */\n }\n }\n window.dispatchEvent(\n new CustomEvent('wrongstack:desktop-command', {\n detail: command,\n }),\n );\n});\n\ncontextBridge.exposeInMainWorld('wrongstackDesktopHost', api);\ncontextBridge.exposeInMainWorld('wrongstackDesktopCommands', commandApi);\n", "export const IPC = {\n getState: 'desktop:get-state',\n getConversation: 'desktop:get-conversation',\n getWebuiStatus: 'desktop:get-webui-status',\n openProject: 'desktop:open-project',\n registerProject: 'desktop:register-project',\n unregisterProject: 'desktop:unregister-project',\n openProjectSession: 'desktop:open-project-session',\n activateRuntime: 'desktop:activate-runtime',\n closeRuntime: 'desktop:close-runtime',\n navigateWebui: 'desktop:navigate-webui',\n reloadWebui: 'desktop:reload-webui',\n setShellSidebarCollapsed: 'desktop:set-shell-sidebar-collapsed',\n openSettings: 'desktop:open-settings',\n sendMessage: 'desktop:send-message',\n abortRuntime: 'desktop:abort-runtime',\n openRuntimeInBrowser: 'desktop:open-runtime-in-browser',\n revealRuntimeRoot: 'desktop:reveal-runtime-root',\n stateChanged: 'desktop:state-changed',\n conversationChanged: 'desktop:conversation-changed',\n webuiStatusChanged: 'desktop:webui-status-changed',\n webuiReadyChanged: 'desktop:webui-ready-changed',\n webuiPrefsChanged: 'desktop:webui-prefs-changed',\n webuiCommandAck: 'desktop:webui-command-ack',\n webuiCommand: 'desktop:webui-command',\n shellSidebarCollapsedChanged: 'desktop:shell-sidebar-collapsed-changed',\n setLocale: 'desktop:set-locale',\n localeChanged: 'desktop:locale-changed',\n // Embedded WebUI view side \u2014 the desktop shell pushes locale changes here\n // so the React WebUI inside Electron can swap i18n instantly, without waiting\n // for the config-file watcher \u2192 WS prefs.updated round-trip.\n webuiLocaleChanged: 'desktop:webui-locale-changed',\n} as const;\n"],
5
+ "mappings": ";;;AAAA,sBAA2C;;;ACApC,IAAM,MAAM;AAAA,EACjB,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,aAAa;AAAA,EACb,0BAA0B;AAAA,EAC1B,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,8BAA8B;AAAA,EAC9B,WAAW;AAAA,EACX,eAAe;AAAA;AAAA;AAAA;AAAA,EAIf,oBAAoB;AACtB;;;ADvBA,IAAM,MAAgC;AAAA,EACpC,UAAU,CAAC,UAAmB;AAC5B,gCAAY,KAAK,IAAI,mBAAmB,KAAK;AAAA,EAC/C;AAAA,EACA,UAAU,CAAC,UAA6B;AACtC,gCAAY,KAAK,IAAI,mBAAmB,KAAK;AAAA,EAC/C;AAAA,EACA,YAAY,CAAC,WAAmB,SAAkB,YAAiC;AACjF,gCAAY,KAAK,IAAI,iBAAiB,WAAW,SAAS,OAAO;AAAA,EACnE;AAAA,EACA,iBAAiB,CAAC,OAAiC;AACjD,UAAM,UAAU,CAAC,QAAmC,WAAmB,GAAG,MAAM;AAChF,gCAAY,GAAG,IAAI,oBAAoB,OAAO;AAC9C,WAAO,MAAM;AACX,kCAAY,IAAI,IAAI,oBAAoB,OAAO;AAAA,IACjD;AAAA,EACF;AACF;AAEA,IAAM,mBAAmB,oBAAI,IAA4C;AACzE,IAAM,aAA0C;AAAA,EAC9C,WAAW,CAAC,OAA+C;AACzD,qBAAiB,IAAI,EAAE;AACvB,WAAO,MAAM;AACX,uBAAiB,OAAO,EAAE;AAAA,IAC5B;AAAA,EACF;AACF;AAEA,4BAAY,GAAG,IAAI,cAAc,CAAC,QAAQ,YAAiC;AACzE,aAAW,YAAY,CAAC,GAAG,gBAAgB,GAAG;AAC5C,QAAI;AACF,eAAS,OAAO;AAAA,IAClB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,YAAY,8BAA8B;AAAA,MAC5C,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF,CAAC;AAED,8BAAc,kBAAkB,yBAAyB,GAAG;AAC5D,8BAAc,kBAAkB,6BAA6B,UAAU;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/desktop",
3
- "version": "0.284.1",
3
+ "version": "0.285.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack Desktop - Electron shell for managing multiple local WrongStack runtimes.",
6
6
  "keywords": [
@@ -33,22 +33,21 @@
33
33
  "electron": "^43.0.0",
34
34
  "ws": "^8.21.0",
35
35
  "zod": "^4.4.3",
36
- "@wrongstack/core": "0.284.1",
37
- "@wrongstack/webui": "0.284.1",
38
- "@wrongstack/webui-server": "0.284.1"
36
+ "@wrongstack/core": "0.285.0",
37
+ "@wrongstack/webui": "0.285.0",
38
+ "@wrongstack/webui-server": "0.285.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^26.0.1",
42
42
  "@types/ws": "^8.18.1",
43
- "tsup": "^8.5.1",
44
- "typescript": "^6.0.3",
43
+ "typescript": "^7.0.2",
45
44
  "vite": "^8.1.0"
46
45
  },
47
46
  "publishConfig": {
48
47
  "access": "public"
49
48
  },
50
49
  "scripts": {
51
- "build": "node ../../scripts/build.mjs --target @wrongstack/webui --skip-if-workspace-build && tsup && vite build",
50
+ "build": "node ../../scripts/build.mjs --target @wrongstack/webui --skip-if-workspace-build && node ../../scripts/build-package.mjs && vite build",
52
51
  "dev": "pnpm run build && electron .",
53
52
  "start": "electron .",
54
53
  "test": "vitest run --config vitest.config.ts",
@@ -1,357 +0,0 @@
1
- // src/main/agent-bridge.ts
2
- import { randomUUID } from "crypto";
3
- import { EventEmitter } from "events";
4
- import WebSocket from "ws";
5
- var MAX_MESSAGES = 300;
6
- var RECONNECT_CONFIG = {
7
- /** Maximum number of reconnection attempts (0 = disabled) */
8
- maxAttempts: 5,
9
- /** Initial delay in ms before first reconnection */
10
- initialDelayMs: 1e3,
11
- /** Maximum delay in ms between reconnection attempts */
12
- maxDelayMs: 3e4,
13
- /** Multiplier for exponential backoff */
14
- backoffMultiplier: 2,
15
- /** Jitter factor (0-1) to add randomness to delays */
16
- jitterFactor: 0.1
17
- };
18
- var DesktopAgentBridge = class extends EventEmitter {
19
- conversations = /* @__PURE__ */ new Map();
20
- snapshot(runtimeId) {
21
- return publicConversation(this.getOrCreate(runtimeId));
22
- }
23
- /**
24
- * Get reconnection status for a runtime.
25
- */
26
- getReconnectStatus(runtimeId) {
27
- const conversation = this.conversations.get(runtimeId);
28
- if (!conversation) return null;
29
- return {
30
- attempt: conversation.reconnectAttempt,
31
- maxAttempts: RECONNECT_CONFIG.maxAttempts
32
- };
33
- }
34
- /**
35
- * Force reconnection for a runtime (resets reconnection state).
36
- */
37
- forceReconnect(runtimeId, wsUrl) {
38
- const conversation = this.getOrCreate(runtimeId);
39
- this.cancelReconnect(conversation);
40
- conversation.reconnectAttempt = 0;
41
- void this.ensureConnected(runtimeId, wsUrl);
42
- }
43
- async ensureConnected(runtimeId, wsUrl) {
44
- const conversation = this.getOrCreate(runtimeId);
45
- if (conversation.ws?.readyState === WebSocket.OPEN) return publicConversation(conversation);
46
- if (conversation.connectPromise) {
47
- await conversation.connectPromise;
48
- return publicConversation(conversation);
49
- }
50
- if (conversation.reconnectTimer) {
51
- return publicConversation(conversation);
52
- }
53
- conversation.reconnectUrl = wsUrl;
54
- conversation.reconnectAttempt = 0;
55
- await this.connect(runtimeId, wsUrl);
56
- return publicConversation(conversation);
57
- }
58
- /**
59
- * Internal connect method that actually establishes the WebSocket.
60
- */
61
- async connect(runtimeId, wsUrl) {
62
- const conversation = this.getOrCreate(runtimeId);
63
- this.cancelReconnect(conversation);
64
- conversation.status = "connecting";
65
- conversation.error = void 0;
66
- this.emitChanged(conversation);
67
- this.emitReconnectEvent(conversation, "connecting");
68
- return new Promise((resolve, reject) => {
69
- const ws = new WebSocket(wsUrl);
70
- conversation.ws = ws;
71
- const timeout = setTimeout(() => {
72
- if (conversation.ws === ws) {
73
- ws.close();
74
- reject(new Error("Connection timeout"));
75
- }
76
- }, 1e4);
77
- ws.once("open", () => {
78
- clearTimeout(timeout);
79
- conversation.status = "connected";
80
- conversation.error = void 0;
81
- conversation.connectPromise = null;
82
- conversation.reconnectAttempt = 0;
83
- conversation.reconnectUrl = null;
84
- this.emitChanged(conversation);
85
- this.emitReconnectEvent(conversation, "connected");
86
- resolve();
87
- });
88
- ws.on("message", (data) => {
89
- this.handleServerMessage(conversation, data.toString());
90
- });
91
- ws.once("error", (err) => {
92
- clearTimeout(timeout);
93
- conversation.status = "error";
94
- conversation.error = err instanceof Error ? err.message : String(err);
95
- conversation.connectPromise = null;
96
- this.appendMessage(conversation, {
97
- role: "system",
98
- text: `Connection error: ${conversation.error}`
99
- });
100
- this.emitChanged(conversation);
101
- this.emitReconnectEvent(conversation, "error");
102
- reject(err);
103
- });
104
- ws.once("close", () => {
105
- clearTimeout(timeout);
106
- if (conversation.ws === ws) conversation.ws = null;
107
- conversation.connectPromise = null;
108
- if (conversation.status !== "error") {
109
- conversation.status = "disconnected";
110
- }
111
- conversation.activeAssistantMessageId = null;
112
- this.emitChanged(conversation);
113
- if (conversation.reconnectUrl && conversation.status === "disconnected") {
114
- this.scheduleReconnect(conversation);
115
- }
116
- });
117
- });
118
- }
119
- /**
120
- * Schedule a reconnection attempt with exponential backoff.
121
- */
122
- scheduleReconnect(conversation) {
123
- if (RECONNECT_CONFIG.maxAttempts === 0) return;
124
- if (conversation.reconnectAttempt >= RECONNECT_CONFIG.maxAttempts) {
125
- this.emitReconnectEvent(conversation, "exhausted");
126
- return;
127
- }
128
- const baseDelay = Math.min(
129
- RECONNECT_CONFIG.initialDelayMs * Math.pow(RECONNECT_CONFIG.backoffMultiplier, conversation.reconnectAttempt),
130
- RECONNECT_CONFIG.maxDelayMs
131
- );
132
- const jitter = baseDelay * RECONNECT_CONFIG.jitterFactor * Math.random();
133
- const delay = Math.floor(baseDelay + jitter);
134
- conversation.reconnectAttempt++;
135
- this.emitReconnectEvent(conversation, "scheduled", { delay, attempt: conversation.reconnectAttempt });
136
- conversation.reconnectTimer = setTimeout(() => {
137
- conversation.reconnectTimer = null;
138
- if (!conversation.reconnectUrl) return;
139
- if (conversation.ws?.readyState !== WebSocket.OPEN) {
140
- void this.connect(conversation.runtimeId, conversation.reconnectUrl);
141
- }
142
- }, delay);
143
- }
144
- /**
145
- * Cancel pending reconnection.
146
- */
147
- cancelReconnect(conversation) {
148
- if (conversation.reconnectTimer) {
149
- clearTimeout(conversation.reconnectTimer);
150
- conversation.reconnectTimer = null;
151
- }
152
- }
153
- /**
154
- * Emit reconnection event for UI feedback.
155
- */
156
- emitReconnectEvent(conversation, status, data) {
157
- this.emit("reconnect", {
158
- runtimeId: conversation.runtimeId,
159
- status,
160
- attempt: conversation.reconnectAttempt,
161
- maxAttempts: RECONNECT_CONFIG.maxAttempts,
162
- ...data
163
- });
164
- }
165
- async sendMessage(runtimeId, wsUrl, content) {
166
- const trimmed = content.trim();
167
- if (!trimmed) return this.snapshot(runtimeId);
168
- const conversation = this.getOrCreate(runtimeId);
169
- conversation.reconnectAttempt = 0;
170
- await this.ensureConnected(runtimeId, wsUrl);
171
- const conv = this.getOrCreate(runtimeId);
172
- this.appendMessage(conv, {
173
- id: `user_${randomUUID()}`,
174
- role: "user",
175
- text: trimmed
176
- });
177
- conv.status = "running";
178
- conv.activeAssistantMessageId = null;
179
- this.emitChanged(conv);
180
- this.send(conv, {
181
- type: "user_message",
182
- payload: {
183
- id: `msg_${Date.now()}_${randomUUID().slice(0, 8)}`,
184
- content: trimmed,
185
- timestamp: Date.now(),
186
- ...conv.sessionId ? { sessionId: conv.sessionId } : {}
187
- }
188
- });
189
- return publicConversation(conv);
190
- }
191
- async abort(runtimeId, wsUrl) {
192
- const conversation = this.getOrCreate(runtimeId);
193
- conversation.reconnectAttempt = 0;
194
- await this.ensureConnected(runtimeId, wsUrl);
195
- const conv = this.getOrCreate(runtimeId);
196
- this.send(conv, {
197
- type: "abort",
198
- payload: conv.sessionId ? { sessionId: conv.sessionId } : {}
199
- });
200
- conv.status = "connected";
201
- this.appendMessage(conv, { role: "system", text: "Abort requested." });
202
- return publicConversation(conv);
203
- }
204
- close(runtimeId) {
205
- const conversation = this.conversations.get(runtimeId);
206
- if (!conversation) return;
207
- this.cancelReconnect(conversation);
208
- conversation.reconnectAttempt = 0;
209
- conversation.reconnectUrl = null;
210
- conversation.ws?.close();
211
- conversation.ws = null;
212
- conversation.connectPromise = null;
213
- conversation.status = "disconnected";
214
- conversation.activeAssistantMessageId = null;
215
- this.emitChanged(conversation);
216
- }
217
- closeAll() {
218
- for (const runtimeId of this.conversations.keys()) {
219
- this.close(runtimeId);
220
- }
221
- }
222
- handleServerMessage(conversation, raw) {
223
- let message;
224
- try {
225
- message = JSON.parse(raw);
226
- } catch {
227
- return;
228
- }
229
- const payload = message.payload ?? {};
230
- switch (message.type) {
231
- case "session.start": {
232
- const sessionId = stringValue(payload["sessionId"]);
233
- if (sessionId) conversation.sessionId = sessionId;
234
- conversation.status = conversation.status === "running" ? "running" : "connected";
235
- this.emitChanged(conversation);
236
- break;
237
- }
238
- case "provider.text_delta": {
239
- conversation.status = "running";
240
- this.appendAssistantDelta(conversation, stringValue(payload["text"]) ?? "");
241
- break;
242
- }
243
- case "tool.started": {
244
- conversation.status = "running";
245
- const name = stringValue(payload["name"]) ?? "tool";
246
- this.appendMessage(conversation, { role: "tool", text: `Started ${name}` });
247
- break;
248
- }
249
- case "tool.executed": {
250
- const name = stringValue(payload["name"]) ?? "tool";
251
- const ok = payload["ok"] === true;
252
- this.appendMessage(conversation, {
253
- role: "tool",
254
- text: `${name} ${ok ? "completed" : "failed"}`
255
- });
256
- break;
257
- }
258
- case "provider.error":
259
- case "provider.stream_error":
260
- case "error": {
261
- const text = stringValue(payload["message"]) ?? stringValue(payload["description"]) ?? `${message.type} received`;
262
- conversation.status = "error";
263
- conversation.error = text;
264
- this.appendMessage(conversation, { role: "system", text });
265
- break;
266
- }
267
- case "run.result": {
268
- const finalText = stringValue(payload["finalText"]);
269
- if (finalText && !this.lastAssistantHasText(conversation)) {
270
- this.appendMessage(conversation, { role: "assistant", text: finalText });
271
- }
272
- conversation.status = payload["status"] === "failed" ? "error" : "connected";
273
- conversation.activeAssistantMessageId = null;
274
- this.emitChanged(conversation);
275
- break;
276
- }
277
- }
278
- }
279
- send(conversation, message) {
280
- if (conversation.ws?.readyState !== WebSocket.OPEN) {
281
- throw new Error("Runtime socket is not connected");
282
- }
283
- conversation.ws.send(JSON.stringify(message));
284
- }
285
- appendAssistantDelta(conversation, text) {
286
- if (!text) return;
287
- let message = conversation.messages.find((m) => m.id === conversation.activeAssistantMessageId);
288
- if (!message) {
289
- message = {
290
- id: `assistant_${randomUUID()}`,
291
- role: "assistant",
292
- text: "",
293
- timestamp: Date.now()
294
- };
295
- conversation.activeAssistantMessageId = message.id;
296
- conversation.messages.push(message);
297
- }
298
- message.text += text;
299
- this.trimMessages(conversation);
300
- this.emitChanged(conversation);
301
- }
302
- appendMessage(conversation, input) {
303
- conversation.messages.push({
304
- id: input.id ?? `${input.role}_${randomUUID()}`,
305
- role: input.role,
306
- text: input.text,
307
- timestamp: input.timestamp ?? Date.now()
308
- });
309
- this.trimMessages(conversation);
310
- this.emitChanged(conversation);
311
- }
312
- lastAssistantHasText(conversation) {
313
- const lastAssistant = [...conversation.messages].reverse().find((m) => m.role === "assistant");
314
- return Boolean(lastAssistant?.text.trim());
315
- }
316
- trimMessages(conversation) {
317
- if (conversation.messages.length <= MAX_MESSAGES) return;
318
- conversation.messages.splice(0, conversation.messages.length - MAX_MESSAGES);
319
- }
320
- getOrCreate(runtimeId) {
321
- let conversation = this.conversations.get(runtimeId);
322
- if (conversation) return conversation;
323
- conversation = {
324
- runtimeId,
325
- status: "disconnected",
326
- messages: [],
327
- ws: null,
328
- connectPromise: null,
329
- activeAssistantMessageId: null,
330
- reconnectAttempt: 0,
331
- reconnectTimer: null,
332
- reconnectUrl: null
333
- };
334
- this.conversations.set(runtimeId, conversation);
335
- return conversation;
336
- }
337
- emitChanged(conversation) {
338
- this.emit("changed", publicConversation(conversation));
339
- }
340
- };
341
- function publicConversation(conversation) {
342
- return {
343
- runtimeId: conversation.runtimeId,
344
- status: conversation.status,
345
- sessionId: conversation.sessionId,
346
- error: conversation.error,
347
- messages: conversation.messages.map((message) => ({ ...message }))
348
- };
349
- }
350
- function stringValue(value) {
351
- return typeof value === "string" ? value : void 0;
352
- }
353
-
354
- export {
355
- DesktopAgentBridge
356
- };
357
- //# sourceMappingURL=chunk-AMPSW3AD.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/main/agent-bridge.ts"],"sourcesContent":["/**\n * Desktop Agent Bridge - WebSocket communication with runtimes.\n * Handles conversation management and WebSocket connections with automatic reconnection.\n */\nimport { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport type {\n DesktopConversationMessage,\n DesktopConversationSnapshot,\n DesktopConversationStatus,\n} from '../shared/types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface ConversationInternal {\n runtimeId: string;\n status: DesktopConversationStatus;\n sessionId?: string | undefined;\n error?: string | undefined;\n messages: DesktopConversationMessage[];\n ws: WebSocket | null;\n connectPromise: Promise<void> | null;\n activeAssistantMessageId: string | null;\n /** Reconnection state */\n reconnectAttempt: number;\n reconnectTimer: ReturnType<typeof setTimeout> | null;\n reconnectUrl: string | null;\n}\n\ninterface ServerMessage {\n type: string;\n payload?: Record<string, unknown> | undefined;\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst MAX_MESSAGES = 300;\n\n/** Reconnection configuration */\nconst RECONNECT_CONFIG = {\n /** Maximum number of reconnection attempts (0 = disabled) */\n maxAttempts: 5,\n /** Initial delay in ms before first reconnection */\n initialDelayMs: 1000,\n /** Maximum delay in ms between reconnection attempts */\n maxDelayMs: 30000,\n /** Multiplier for exponential backoff */\n backoffMultiplier: 2,\n /** Jitter factor (0-1) to add randomness to delays */\n jitterFactor: 0.1,\n};\n\n// ============================================================================\n// Bridge Implementation\n// ============================================================================\n\nexport class DesktopAgentBridge extends EventEmitter {\n private readonly conversations = new Map<string, ConversationInternal>();\n\n snapshot(runtimeId: string): DesktopConversationSnapshot {\n return publicConversation(this.getOrCreate(runtimeId));\n }\n\n /**\n * Get reconnection status for a runtime.\n */\n getReconnectStatus(runtimeId: string): { attempt: number; maxAttempts: number } | null {\n const conversation = this.conversations.get(runtimeId);\n if (!conversation) return null;\n return {\n attempt: conversation.reconnectAttempt,\n maxAttempts: RECONNECT_CONFIG.maxAttempts,\n };\n }\n\n /**\n * Force reconnection for a runtime (resets reconnection state).\n */\n forceReconnect(runtimeId: string, wsUrl: string): void {\n const conversation = this.getOrCreate(runtimeId);\n this.cancelReconnect(conversation);\n conversation.reconnectAttempt = 0;\n void this.ensureConnected(runtimeId, wsUrl);\n }\n\n async ensureConnected(runtimeId: string, wsUrl: string): Promise<DesktopConversationSnapshot> {\n const conversation = this.getOrCreate(runtimeId);\n \n // If already connected, return immediately\n if (conversation.ws?.readyState === WebSocket.OPEN) return publicConversation(conversation);\n \n // If currently connecting, wait for it\n if (conversation.connectPromise) {\n await conversation.connectPromise;\n return publicConversation(conversation);\n }\n\n // If reconnecting in background, return current state\n if (conversation.reconnectTimer) {\n return publicConversation(conversation);\n }\n\n // Start fresh connection\n conversation.reconnectUrl = wsUrl;\n conversation.reconnectAttempt = 0;\n await this.connect(runtimeId, wsUrl);\n return publicConversation(conversation);\n }\n\n /**\n * Internal connect method that actually establishes the WebSocket.\n */\n private async connect(runtimeId: string, wsUrl: string): Promise<void> {\n const conversation = this.getOrCreate(runtimeId);\n \n // Cancel any pending reconnect timer\n this.cancelReconnect(conversation);\n\n conversation.status = 'connecting';\n conversation.error = undefined;\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'connecting');\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n conversation.ws = ws;\n\n // Set connection timeout\n const timeout = setTimeout(() => {\n if (conversation.ws === ws) {\n ws.close();\n reject(new Error('Connection timeout'));\n }\n }, 10000);\n\n ws.once('open', () => {\n clearTimeout(timeout);\n conversation.status = 'connected';\n conversation.error = undefined;\n conversation.connectPromise = null;\n conversation.reconnectAttempt = 0;\n conversation.reconnectUrl = null;\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'connected');\n resolve();\n });\n\n ws.on('message', (data) => {\n this.handleServerMessage(conversation, data.toString());\n });\n\n ws.once('error', (err) => {\n clearTimeout(timeout);\n conversation.status = 'error';\n conversation.error = err instanceof Error ? err.message : String(err);\n conversation.connectPromise = null;\n this.appendMessage(conversation, {\n role: 'system',\n text: `Connection error: ${conversation.error}`,\n });\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'error');\n reject(err);\n });\n\n ws.once('close', () => {\n clearTimeout(timeout);\n if (conversation.ws === ws) conversation.ws = null;\n conversation.connectPromise = null;\n \n if (conversation.status !== 'error') {\n conversation.status = 'disconnected';\n }\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n \n // Schedule reconnection if applicable\n if (conversation.reconnectUrl && conversation.status === 'disconnected') {\n this.scheduleReconnect(conversation);\n }\n });\n });\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(conversation: ConversationInternal): void {\n // Check if reconnection is enabled and within limits\n if (RECONNECT_CONFIG.maxAttempts === 0) return;\n if (conversation.reconnectAttempt >= RECONNECT_CONFIG.maxAttempts) {\n this.emitReconnectEvent(conversation, 'exhausted');\n return;\n }\n\n // Calculate delay with exponential backoff and jitter\n const baseDelay = Math.min(\n RECONNECT_CONFIG.initialDelayMs *\n Math.pow(RECONNECT_CONFIG.backoffMultiplier, conversation.reconnectAttempt),\n RECONNECT_CONFIG.maxDelayMs,\n );\n \n // Add jitter\n const jitter = baseDelay * RECONNECT_CONFIG.jitterFactor * Math.random();\n const delay = Math.floor(baseDelay + jitter);\n\n conversation.reconnectAttempt++;\n this.emitReconnectEvent(conversation, 'scheduled', { delay, attempt: conversation.reconnectAttempt });\n\n conversation.reconnectTimer = setTimeout(() => {\n conversation.reconnectTimer = null;\n if (!conversation.reconnectUrl) return;\n \n // Check if still disconnected\n if (conversation.ws?.readyState !== WebSocket.OPEN) {\n void this.connect(conversation.runtimeId, conversation.reconnectUrl);\n }\n }, delay);\n }\n\n /**\n * Cancel pending reconnection.\n */\n private cancelReconnect(conversation: ConversationInternal): void {\n if (conversation.reconnectTimer) {\n clearTimeout(conversation.reconnectTimer);\n conversation.reconnectTimer = null;\n }\n }\n\n /**\n * Emit reconnection event for UI feedback.\n */\n private emitReconnectEvent(\n conversation: ConversationInternal,\n status: 'connecting' | 'connected' | 'error' | 'scheduled' | 'exhausted',\n data?: { delay?: number; attempt?: number },\n ): void {\n this.emit('reconnect', {\n runtimeId: conversation.runtimeId,\n status,\n attempt: conversation.reconnectAttempt,\n maxAttempts: RECONNECT_CONFIG.maxAttempts,\n ...data,\n });\n }\n\n async sendMessage(\n runtimeId: string,\n wsUrl: string,\n content: string,\n ): Promise<DesktopConversationSnapshot> {\n const trimmed = content.trim();\n if (!trimmed) return this.snapshot(runtimeId);\n \n // Reset reconnection state on manual action\n const conversation = this.getOrCreate(runtimeId);\n conversation.reconnectAttempt = 0;\n \n await this.ensureConnected(runtimeId, wsUrl);\n \n // Refresh conversation after connect\n const conv = this.getOrCreate(runtimeId);\n this.appendMessage(conv, {\n id: `user_${randomUUID()}`,\n role: 'user',\n text: trimmed,\n });\n conv.status = 'running';\n conv.activeAssistantMessageId = null;\n this.emitChanged(conv);\n this.send(conv, {\n type: 'user_message',\n payload: {\n id: `msg_${Date.now()}_${randomUUID().slice(0, 8)}`,\n content: trimmed,\n timestamp: Date.now(),\n ...(conv.sessionId ? { sessionId: conv.sessionId } : {}),\n },\n });\n return publicConversation(conv);\n }\n\n async abort(runtimeId: string, wsUrl: string): Promise<DesktopConversationSnapshot> {\n // Reset reconnection state on manual action\n const conversation = this.getOrCreate(runtimeId);\n conversation.reconnectAttempt = 0;\n \n await this.ensureConnected(runtimeId, wsUrl);\n const conv = this.getOrCreate(runtimeId);\n this.send(conv, {\n type: 'abort',\n payload: conv.sessionId ? { sessionId: conv.sessionId } : {},\n });\n conv.status = 'connected';\n this.appendMessage(conv, { role: 'system', text: 'Abort requested.' });\n return publicConversation(conv);\n }\n\n close(runtimeId: string): void {\n const conversation = this.conversations.get(runtimeId);\n if (!conversation) return;\n \n // Cancel reconnection and clear state\n this.cancelReconnect(conversation);\n conversation.reconnectAttempt = 0;\n conversation.reconnectUrl = null;\n \n conversation.ws?.close();\n conversation.ws = null;\n conversation.connectPromise = null;\n conversation.status = 'disconnected';\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n }\n\n closeAll(): void {\n for (const runtimeId of this.conversations.keys()) {\n this.close(runtimeId);\n }\n }\n\n private handleServerMessage(conversation: ConversationInternal, raw: string): void {\n let message: ServerMessage;\n try {\n message = JSON.parse(raw) as ServerMessage;\n } catch {\n return;\n }\n const payload = message.payload ?? {};\n switch (message.type) {\n case 'session.start': {\n const sessionId = stringValue(payload['sessionId']);\n if (sessionId) conversation.sessionId = sessionId;\n conversation.status = conversation.status === 'running' ? 'running' : 'connected';\n this.emitChanged(conversation);\n break;\n }\n case 'provider.text_delta': {\n conversation.status = 'running';\n this.appendAssistantDelta(conversation, stringValue(payload['text']) ?? '');\n break;\n }\n case 'tool.started': {\n conversation.status = 'running';\n const name = stringValue(payload['name']) ?? 'tool';\n this.appendMessage(conversation, { role: 'tool', text: `Started ${name}` });\n break;\n }\n case 'tool.executed': {\n const name = stringValue(payload['name']) ?? 'tool';\n const ok = payload['ok'] === true;\n this.appendMessage(conversation, {\n role: 'tool',\n text: `${name} ${ok ? 'completed' : 'failed'}`,\n });\n break;\n }\n case 'provider.error':\n case 'provider.stream_error':\n case 'error': {\n const text =\n stringValue(payload['message']) ??\n stringValue(payload['description']) ??\n `${message.type} received`;\n conversation.status = 'error';\n conversation.error = text;\n this.appendMessage(conversation, { role: 'system', text });\n break;\n }\n case 'run.result': {\n const finalText = stringValue(payload['finalText']);\n if (finalText && !this.lastAssistantHasText(conversation)) {\n this.appendMessage(conversation, { role: 'assistant', text: finalText });\n }\n conversation.status = payload['status'] === 'failed' ? 'error' : 'connected';\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n break;\n }\n }\n }\n\n private send(conversation: ConversationInternal, message: Record<string, unknown>): void {\n if (conversation.ws?.readyState !== WebSocket.OPEN) {\n throw new Error('Runtime socket is not connected');\n }\n conversation.ws.send(JSON.stringify(message));\n }\n\n private appendAssistantDelta(conversation: ConversationInternal, text: string): void {\n if (!text) return;\n let message = conversation.messages.find((m) => m.id === conversation.activeAssistantMessageId);\n if (!message) {\n message = {\n id: `assistant_${randomUUID()}`,\n role: 'assistant',\n text: '',\n timestamp: Date.now(),\n };\n conversation.activeAssistantMessageId = message.id;\n conversation.messages.push(message);\n }\n message.text += text;\n this.trimMessages(conversation);\n this.emitChanged(conversation);\n }\n\n private appendMessage(\n conversation: ConversationInternal,\n input: Partial<DesktopConversationMessage> & Pick<DesktopConversationMessage, 'role' | 'text'>,\n ): void {\n conversation.messages.push({\n id: input.id ?? `${input.role}_${randomUUID()}`,\n role: input.role,\n text: input.text,\n timestamp: input.timestamp ?? Date.now(),\n });\n this.trimMessages(conversation);\n this.emitChanged(conversation);\n }\n\n private lastAssistantHasText(conversation: ConversationInternal): boolean {\n const lastAssistant = [...conversation.messages].reverse().find((m) => m.role === 'assistant');\n return Boolean(lastAssistant?.text.trim());\n }\n\n private trimMessages(conversation: ConversationInternal): void {\n if (conversation.messages.length <= MAX_MESSAGES) return;\n conversation.messages.splice(0, conversation.messages.length - MAX_MESSAGES);\n }\n\n private getOrCreate(runtimeId: string): ConversationInternal {\n let conversation = this.conversations.get(runtimeId);\n if (conversation) return conversation;\n conversation = {\n runtimeId,\n status: 'disconnected',\n messages: [],\n ws: null,\n connectPromise: null,\n activeAssistantMessageId: null,\n reconnectAttempt: 0,\n reconnectTimer: null,\n reconnectUrl: null,\n };\n this.conversations.set(runtimeId, conversation);\n return conversation;\n }\n\n private emitChanged(conversation: ConversationInternal): void {\n this.emit('changed', publicConversation(conversation));\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction publicConversation(conversation: ConversationInternal): DesktopConversationSnapshot {\n return {\n runtimeId: conversation.runtimeId,\n status: conversation.status,\n sessionId: conversation.sessionId,\n error: conversation.error,\n messages: conversation.messages.map((message) => ({ ...message })),\n };\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n// ============================================================================\n// Reconnect Event Type (for external consumers)\n// ============================================================================\n\nexport interface DesktopBridgeReconnectEvent {\n runtimeId: string;\n status: 'connecting' | 'connected' | 'error' | 'scheduled' | 'exhausted';\n attempt: number;\n maxAttempts: number;\n delay?: number;\n}\n"],"mappings":"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","names":[]}