zidane-ui 9.1.13 → 9.1.16

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.
Files changed (290) hide show
  1. package/dist/adapters/index.d.ts +2 -2
  2. package/dist/adapters/index.js +2 -2
  3. package/dist/adapters/live.d.ts +29 -3
  4. package/dist/adapters/live.js +9 -1
  5. package/dist/adapters/server-composer-host.js +17 -2
  6. package/dist/adapters/server-extensions-host.d.ts +0 -1
  7. package/dist/adapters/server-extensions-host.js +4 -15
  8. package/dist/adapters/server-git-host.js +7 -16
  9. package/dist/adapters/server-host.js +181 -43
  10. package/dist/adapters/server-machines-host.d.ts +2 -0
  11. package/dist/adapters/server-machines-host.js +22 -23
  12. package/dist/adapters/server-onboarding-host.d.ts +1 -1
  13. package/dist/adapters/server-onboarding-host.js +3 -4
  14. package/dist/adapters/server-session-host.js +22 -3
  15. package/dist/adapters/server-settings-accounts.js +2 -2
  16. package/dist/adapters/server-settings-host.js +2 -2
  17. package/dist/adapters/server-settings-performance.js +7 -9
  18. package/dist/adapters/server-settings-sections.js +13 -14
  19. package/dist/adapters/server-sidebar-host.js +2 -2
  20. package/dist/adapters/sse-host.js +68 -3
  21. package/dist/adapters/sse-protocol.d.ts +12 -2
  22. package/dist/adapters/sse-protocol.js +4 -2
  23. package/dist/app/AppHostProvider.js +4 -3
  24. package/dist/app/ModalProvider.d.ts +13 -3
  25. package/dist/app/ModalProvider.js +103 -24
  26. package/dist/app/NoticeStack.d.ts +33 -0
  27. package/dist/app/NoticeStack.js +90 -0
  28. package/dist/app/index.d.ts +3 -2
  29. package/dist/app/index.js +3 -2
  30. package/dist/chat/SessionFilterMenu.d.ts +1 -1
  31. package/dist/chat/SessionFilterMenu.js +2 -1
  32. package/dist/chat/composer/Composer.js +10 -8
  33. package/dist/chat/composer/ModelChainList.js +17 -8
  34. package/dist/chat/composer/ModelChainTooltip.js +5 -2
  35. package/dist/chat/composer/use-draft-target.d.ts +20 -1
  36. package/dist/chat/composer/use-draft-target.js +121 -5
  37. package/dist/chat/composer/use-new-session-defaults.d.ts +5 -1
  38. package/dist/chat/composer/use-new-session-defaults.js +17 -4
  39. package/dist/chat/headless/chip-labels.d.ts +1 -0
  40. package/dist/chat/headless/chip-labels.js +1 -0
  41. package/dist/chat/headless/composer/labels-editor.d.ts +0 -2
  42. package/dist/chat/headless/composer/labels-editor.js +0 -2
  43. package/dist/chat/headless/draft-target.d.ts +21 -1
  44. package/dist/chat/headless/fixture-host.js +17 -2
  45. package/dist/chat/headless/host.d.ts +3 -3
  46. package/dist/chat/headless/index.d.ts +9 -9
  47. package/dist/chat/headless/index.js +2 -2
  48. package/dist/chat/headless/labels.d.ts +13 -6
  49. package/dist/chat/headless/labels.js +10 -9
  50. package/dist/chat/headless/session-list.d.ts +20 -8
  51. package/dist/chat/headless/session-list.js +1 -0
  52. package/dist/chat/headless/sidebar/directory.d.ts +56 -14
  53. package/dist/chat/headless/sidebar/directory.js +149 -70
  54. package/dist/chat/headless/sidebar/index.d.ts +3 -3
  55. package/dist/chat/headless/sidebar/index.js +2 -2
  56. package/dist/chat/headless/sidebar/labels.d.ts +15 -8
  57. package/dist/chat/headless/sidebar/types.d.ts +3 -1
  58. package/dist/chat/headless/sidebar/view.d.ts +13 -5
  59. package/dist/chat/headless/sidebar/view.js +31 -11
  60. package/dist/chat/headless/streams/actions.d.ts +5 -3
  61. package/dist/chat/headless/streams/actions.js +3 -2
  62. package/dist/chat/headless/streams/index.d.ts +3 -3
  63. package/dist/chat/headless/streams/merge.d.ts +6 -10
  64. package/dist/chat/headless/streams/merge.js +7 -21
  65. package/dist/chat/headless/streams/session-streams.d.ts +29 -4
  66. package/dist/chat/headless/streams/session-streams.js +86 -0
  67. package/dist/chat/headless/streams/store.d.ts +162 -16
  68. package/dist/chat/headless/streams/store.js +334 -38
  69. package/dist/chat/headless/streams/types.d.ts +10 -7
  70. package/dist/chat/headless/transcript-labels.d.ts +1 -0
  71. package/dist/chat/interactions/PlanCard.js +10 -3
  72. package/dist/chat/interactions/QuestionForm.js +7 -3
  73. package/dist/chat/interactions/card-ui.d.ts +5 -3
  74. package/dist/chat/interactions/card-ui.js +9 -7
  75. package/dist/chat/rich-composer/RichComposer.js +1 -7
  76. package/dist/chat/rich-composer/composer-extensions.d.ts +0 -2
  77. package/dist/chat/rich-composer/composer-extensions.js +2 -2
  78. package/dist/chat/rich-composer/use-composer-editor.d.ts +0 -2
  79. package/dist/chat/rich-composer/use-composer-editor.js +1 -2
  80. package/dist/chat/sidebar/SessionSidebar.js +5 -5
  81. package/dist/chat/sidebar/SidebarFilterMenu.js +3 -1
  82. package/dist/chat/sidebar/SidebarFrame.d.ts +4 -0
  83. package/dist/chat/sidebar/SidebarFrame.js +27 -9
  84. package/dist/chat/sidebar/SidebarGroupHeader.js +1 -1
  85. package/dist/chat/sidebar/SidebarHostProvider.d.ts +8 -5
  86. package/dist/chat/sidebar/SidebarHostProvider.js +13 -9
  87. package/dist/chat/sidebar/SidebarToggleButton.js +7 -3
  88. package/dist/chat/sidebar/index.d.ts +2 -2
  89. package/dist/chat/sidebar/session-menu.js +4 -4
  90. package/dist/chat/sidebar/useSaveViewAsSpace.js +2 -2
  91. package/dist/chat/testing/contract.js +2 -2
  92. package/dist/chat/transcript/AuthFailureCard.js +11 -8
  93. package/dist/chat/transcript/BadgeMenu.js +13 -2
  94. package/dist/chat/transcript/MermaidDiagram.js +7 -3
  95. package/dist/chat/transcript/fold.d.ts +4 -0
  96. package/dist/chat/transcript/fold.js +53 -22
  97. package/dist/chat/transcript/script-input.d.ts +1 -1
  98. package/dist/chat/transcript/shell-block.js +7 -3
  99. package/dist/chat/transcript/useStickToBottom.js +12 -0
  100. package/dist/components/BrowserSignIn.js +23 -21
  101. package/dist/components/Form.d.ts +6 -4
  102. package/dist/components/Form.js +16 -8
  103. package/dist/components/LoadError.d.ts +4 -2
  104. package/dist/components/LoadError.js +13 -9
  105. package/dist/components/Notice.d.ts +29 -0
  106. package/dist/components/Notice.js +63 -0
  107. package/dist/components/PanelEmpty.d.ts +1 -1
  108. package/dist/components/PanelEmpty.js +4 -2
  109. package/dist/components/Restricted.d.ts +1 -1
  110. package/dist/components/palette/PaletteList.js +8 -1
  111. package/dist/components/palette/PalettePanel.js +14 -6
  112. package/dist/core.css +24 -5
  113. package/dist/dist/{index-Bq87OcGy.d.ts → index-BZM8tu4L.d.ts} +28 -3
  114. package/dist/dist/index.d.ts +2 -2
  115. package/dist/dist/{timeout-B5pO5I5a.d.ts → timeout-Cyx3VGPz.d.ts} +1 -1
  116. package/dist/extensions/ExtensionSettingsPage.js +1 -4
  117. package/dist/extensions/ExtensionSettingsSection.js +4 -3
  118. package/dist/extensions/ExtensionSettingsSidebar.js +2 -6
  119. package/dist/extensions/SettingsWireForm.d.ts +1 -7
  120. package/dist/extensions/SettingsWireForm.js +7 -5
  121. package/dist/extensions/headless/fixture-host.d.ts +1 -5
  122. package/dist/extensions/headless/fixture-host.js +1 -4
  123. package/dist/extensions/headless/host.d.ts +1 -11
  124. package/dist/extensions/headless/index.d.ts +3 -3
  125. package/dist/extensions/headless/index.js +2 -2
  126. package/dist/extensions/headless/select.d.ts +1 -11
  127. package/dist/extensions/headless/select.js +1 -18
  128. package/dist/files/EditorColumn.js +12 -1
  129. package/dist/files/TreeColumn.js +13 -20
  130. package/dist/files/useOpenFiles.js +2 -0
  131. package/dist/git/BranchMenu.js +5 -3
  132. package/dist/git/ImportRepoDialog.js +18 -9
  133. package/dist/git/MoveSessionDialog.js +15 -7
  134. package/dist/git/RemoteFolderPicker.js +34 -16
  135. package/dist/git/SyncChip.js +17 -17
  136. package/dist/git/WherePicker.d.ts +16 -1
  137. package/dist/git/WherePicker.js +80 -36
  138. package/dist/git/headless/environment.d.ts +33 -3
  139. package/dist/git/headless/environment.js +33 -5
  140. package/dist/git/headless/index.d.ts +2 -2
  141. package/dist/git/headless/index.js +2 -2
  142. package/dist/git/headless/labels.d.ts +15 -2
  143. package/dist/git/headless/labels.js +17 -4
  144. package/dist/git/useEnvironmentChoices.d.ts +3 -1
  145. package/dist/git/useEnvironmentChoices.js +6 -3
  146. package/dist/index.d.ts +2 -1
  147. package/dist/index.js +2 -1
  148. package/dist/labels.d.ts +5 -0
  149. package/dist/labels.js +5 -0
  150. package/dist/machines/AddMachineDialog.d.ts +34 -0
  151. package/dist/machines/AddMachineDialog.js +464 -0
  152. package/dist/machines/Banners.d.ts +33 -4
  153. package/dist/machines/Banners.js +64 -3
  154. package/dist/machines/DaemonMenu.js +27 -4
  155. package/dist/machines/HostCapabilities.d.ts +1 -3
  156. package/dist/machines/HostCapabilities.js +24 -45
  157. package/dist/machines/HostSyncPanel.js +123 -129
  158. package/dist/machines/HostsSection.d.ts +4 -1
  159. package/dist/machines/HostsSection.js +30 -7
  160. package/dist/machines/MachinesExplainer.d.ts +17 -0
  161. package/dist/machines/MachinesExplainer.js +71 -0
  162. package/dist/machines/MachinesProvider.d.ts +14 -2
  163. package/dist/machines/MachinesProvider.js +35 -1
  164. package/dist/machines/ServerAddedCard.d.ts +31 -0
  165. package/dist/machines/ServerAddedCard.js +72 -0
  166. package/dist/machines/ServerCard.js +91 -215
  167. package/dist/machines/ServerMachinesPanel.d.ts +2 -2
  168. package/dist/machines/ServerMachinesPanel.js +184 -323
  169. package/dist/machines/ServerUpdatePanel.js +51 -40
  170. package/dist/machines/ShareThisComputerForm.d.ts +19 -0
  171. package/dist/machines/ShareThisComputerForm.js +133 -0
  172. package/dist/machines/TokensPanel.d.ts +5 -3
  173. package/dist/machines/TokensPanel.js +98 -78
  174. package/dist/machines/headless/fixture-host.d.ts +19 -3
  175. package/dist/machines/headless/fixture-host.js +42 -5
  176. package/dist/machines/headless/fixtures.js +4 -6
  177. package/dist/machines/headless/host.d.ts +40 -19
  178. package/dist/machines/headless/index.d.ts +3 -3
  179. package/dist/machines/headless/index.js +2 -2
  180. package/dist/machines/headless/labels.d.ts +63 -20
  181. package/dist/machines/headless/labels.js +88 -25
  182. package/dist/machines/headless/messages.js +8 -0
  183. package/dist/machines/headless/stores.d.ts +9 -2
  184. package/dist/machines/headless/stores.js +28 -1
  185. package/dist/machines/index.d.ts +7 -3
  186. package/dist/machines/index.js +8 -4
  187. package/dist/machines/testing/contract.d.ts +15 -3
  188. package/dist/machines/testing/contract.js +68 -14
  189. package/dist/motion/grow.d.ts +8 -1
  190. package/dist/motion/grow.js +10 -4
  191. package/dist/onboarding/demos/index.js +1 -1
  192. package/dist/onboarding/headless/labels.d.ts +13 -0
  193. package/dist/onboarding/headless/labels.js +8 -0
  194. package/dist/onboarding/index.js +3 -3
  195. package/dist/onboarding/sources/SourceBrowser.js +11 -7
  196. package/dist/onboarding/steps/ConnectorsStep.js +1 -1
  197. package/dist/onboarding/steps/ImportStep.js +4 -1
  198. package/dist/onboarding/steps/ReadyStep.js +9 -2
  199. package/dist/onboarding/steps/SkillsStep.js +1 -1
  200. package/dist/onboarding/steps/connect/KeyForm.js +6 -2
  201. package/dist/onboarding/steps/connect/ProviderPanel.js +6 -2
  202. package/dist/onboarding/steps/connect/ProviderReady.js +25 -20
  203. package/dist/onboarding/steps/tasks.js +17 -14
  204. package/dist/server/sse-handler.js +141 -19
  205. package/dist/session/ArtifactsPanel.js +39 -17
  206. package/dist/session/PlanTracker.js +12 -7
  207. package/dist/session/ProjectHome.js +13 -11
  208. package/dist/session/RemindersPanel.js +34 -16
  209. package/dist/session/RunningWorkDialog.js +6 -2
  210. package/dist/session/SecretsPanel.js +29 -10
  211. package/dist/session/SessionDetailsDialog.d.ts +1 -1
  212. package/dist/session/SessionDetailsDialog.js +18 -10
  213. package/dist/session/SideChat.js +10 -7
  214. package/dist/session/TurnDetailsDialog.js +19 -5
  215. package/dist/session/UsagePanel.js +10 -4
  216. package/dist/session/headless/host.d.ts +2 -0
  217. package/dist/session/headless/labels.d.ts +19 -1
  218. package/dist/session/headless/labels.js +15 -1
  219. package/dist/session/headless/queue.js +2 -1
  220. package/dist/session/headless/reminder-labels.d.ts +4 -0
  221. package/dist/session/headless/reminder-labels.js +4 -0
  222. package/dist/settings/SettingsRestricted.d.ts +1 -1
  223. package/dist/settings/SettingsRestricted.js +4 -4
  224. package/dist/settings/SettingsShell.d.ts +7 -0
  225. package/dist/settings/SettingsShell.js +6 -1
  226. package/dist/settings/headless/fixture-performance.d.ts +1 -1
  227. package/dist/settings/headless/host.d.ts +2 -2
  228. package/dist/settings/headless/labels.d.ts +3 -3
  229. package/dist/settings/headless/labels.js +3 -3
  230. package/dist/settings/headless/performance.d.ts +3 -4
  231. package/dist/settings/headless/search.d.ts +1 -1
  232. package/dist/settings/index.d.ts +2 -1
  233. package/dist/settings/index.js +2 -1
  234. package/dist/settings/sections/editor/EditorSection.js +7 -4
  235. package/dist/settings/sections/editor/labels.d.ts +1 -0
  236. package/dist/settings/sections/editor/labels.js +1 -0
  237. package/dist/settings/sections/environments/EnvironmentsSection.js +19 -12
  238. package/dist/settings/sections/environments/labels.d.ts +1 -0
  239. package/dist/settings/sections/environments/labels.js +2 -1
  240. package/dist/settings/sections/extensions/ExtensionConfigRows.d.ts +1 -3
  241. package/dist/settings/sections/extensions/ExtensionConfigRows.js +20 -19
  242. package/dist/settings/sections/extensions/ExtensionOptionsSection.js +2 -1
  243. package/dist/settings/sections/extensions/options-labels.d.ts +2 -1
  244. package/dist/settings/sections/extensions/options-labels.js +2 -1
  245. package/dist/settings/sections/index.d.ts +2 -1
  246. package/dist/settings/sections/index.js +2 -1
  247. package/dist/settings/sections/logs/LogsSection.d.ts +2 -2
  248. package/dist/settings/sections/logs/LogsSection.js +2 -2
  249. package/dist/settings/sections/mcp/McpSection.d.ts +1 -1
  250. package/dist/settings/sections/mcp/McpSection.js +12 -6
  251. package/dist/settings/sections/mcp/McpServerRow.js +20 -13
  252. package/dist/settings/sections/mcp/labels.d.ts +8 -2
  253. package/dist/settings/sections/mcp/labels.js +8 -2
  254. package/dist/settings/sections/models/ModelRows.js +1 -1
  255. package/dist/settings/sections/models/NewChatPlaceRow.d.ts +26 -0
  256. package/dist/settings/sections/models/NewChatPlaceRow.js +78 -0
  257. package/dist/settings/sections/models/SpacesDefaultsSection.js +15 -3
  258. package/dist/settings/sections/models/labels.d.ts +7 -0
  259. package/dist/settings/sections/models/labels.js +7 -0
  260. package/dist/settings/sections/models/model-test.js +7 -4
  261. package/dist/settings/sections/performance/PerformanceSection.js +16 -6
  262. package/dist/settings/sections/performance/labels.d.ts +1 -1
  263. package/dist/settings/sections/performance/labels.js +1 -1
  264. package/dist/settings/sections/providers/ProvidersSection.d.ts +1 -1
  265. package/dist/settings/sections/providers/ProvidersSection.js +14 -7
  266. package/dist/settings/sections/providers/ServerProvidersSection.d.ts +1 -1
  267. package/dist/settings/sections/providers/ServerProvidersSection.js +11 -5
  268. package/dist/settings/sections/providers/labels.d.ts +2 -2
  269. package/dist/settings/sections/providers/labels.js +2 -2
  270. package/dist/settings/sections/skills/SkillList.js +1 -1
  271. package/dist/settings/sections/skills/SkillsSection.js +1 -1
  272. package/dist/settings/testing/performance.contract.js +8 -12
  273. package/dist/styles.css +32 -3
  274. package/dist/terminal/HostTerminalSurface.js +15 -5
  275. package/dist/terminal/TerminalPanel.js +30 -17
  276. package/dist/terminal/TerminalSurface.js +11 -4
  277. package/dist/terminal/headless/labels.d.ts +5 -0
  278. package/dist/terminal/headless/labels.js +5 -0
  279. package/dist/terminal/headless/store.d.ts +1 -1
  280. package/dist/terminal/headless/store.js +6 -3
  281. package/dist/workspace/LayoutBudget.d.ts +5 -1
  282. package/dist/workspace/LayoutBudget.js +14 -1
  283. package/dist/workspace/PanelContentView.d.ts +1 -1
  284. package/dist/workspace/PanelContentView.js +17 -6
  285. package/dist/workspace/WorkspaceProvider.js +3 -2
  286. package/dist/workspace/headless/labels.d.ts +1 -0
  287. package/dist/workspace/headless/labels.js +1 -0
  288. package/package.json +3 -3
  289. package/src/styles/motion.css +8 -0
  290. package/src/styles/primitives.css +7 -0
@@ -7,7 +7,7 @@ import { FromWorker, MessageEndpoint, ToWorker, WORKER_PROTOCOL_VERSION, WorkerR
7
7
  import { WorkerHostOptions, createWorkerHost, exposeRunHost } from "./worker-host.js";
8
8
  import { SseHost, SseHostOptions, createSseHost } from "./sse-host.js";
9
9
  import { SseParser, SseRecord, createSseParser } from "./sse-parser.js";
10
- import { AnswerBody, HydrateResponse, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, SendBody, StreamRecord, attachmentsFromPreviews, encodeSseRecord, formatCursor, parseCursor } from "./sse-protocol.js";
10
+ import { AnswerBody, HydrateResponse, SSE_HISTORY_RECORDS_VERSION, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, SendBody, StreamRecord, attachmentsFromPreviews, encodeSseRecord, formatCursor, parseCursor } from "./sse-protocol.js";
11
11
  import { SERVER_AUTH_CHECK_PATH, ServerCallError, ServerClient, ServerClientOptions, ServerClientState, ServerConnectionError, ServerRefusal, createServerClient } from "./server-client.js";
12
12
  import { ServerHost, ServerHostOptions, createServerHost, toChatModelProviders, toChatSession, toSidebarListSession } from "./server-host.js";
13
13
  import { ServerApprovalsHostOptions, createServerApprovalsHost } from "./server-approvals-host.js";
@@ -26,4 +26,4 @@ import { applyLiveEvent, closeThinkingSegment, finishLive } from "../chat/headle
26
26
  import { LiveState, LiveStateOptions, LiveTranscript, createLiveState } from "./live.js";
27
27
  import "./live-events.js";
28
28
  import { StreamPacer, StreamPacerOptions, createStreamPacer } from "./stream-pacer.js";
29
- export { AnswerBody, BrowserAppearanceHostOptions, BrowserKeybindingsHostOptions, BrowserWorkspaceHostOptions, EXTENSION_HOST_MODULES_GLOBAL, ExtensionFlowStepHandler, ExtensionHostModule, ExtensionHostModuleSources, ExtensionHostModules, ExtensionModuleLinkError, ExtensionModuleLoad, ExtensionModuleLoadError, ExtensionsClientAnnounce, FromWorker, HydrateResponse, LinkedExtensionModule, LiveState, LiveStateOptions, LiveTranscript, MessageEndpoint, RunContext, RunFn, RunHost, RunHostOptions, SERVER_AUTH_CHECK_PATH, SERVER_MACHINES_HOST_ID, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, SendBody, ServerApprovalsHostOptions, ServerCallError, type ServerClient, type ServerClientOptions, type ServerClientState, ServerComposerHost, ServerComposerHostOptions, ServerConnectionError, ServerExtensionAssetsAccess, ServerExtensionBundle, ServerExtensionBundleLookup, ServerExtensionCommandWire, ServerExtensionCommands, ServerExtensionDialogs, ServerExtensionModuleLoader, ServerExtensionModuleLoaderOptions, ServerExtensionsHost, ServerExtensionsHostOptions, ServerFilesHostOptions, ServerGitHostOptions, ServerHost, ServerHostOptions, ServerMachinesHost, ServerMachinesHostOptions, ServerOnboardingHost, ServerOnboardingHostOptions, ServerPaletteHost, ServerPaletteHostOptions, type ServerRefusal, ServerSessionHost, ServerSessionHostOptions, ServerSettingsHost, ServerSettingsHostOptions, ServerSidebarHostOptions, ServerTerminalHostOptions, ServerWebviewSources, ServerWebviewSourcesOptions, SseHost, SseHostOptions, SseParser, SseRecord, StreamPacer, StreamPacerOptions, StreamRecord, ToWorker, WORKER_PROTOCOL_VERSION, WorkerHostOptions, WorkerRunInput, applyLiveEvent, attachmentsFromPreviews, closeThinkingSegment, createBrowserAppearanceHost, createBrowserKeybindingsHost, createBrowserWorkspaceHost, createLiveState, createRunHost, createServerApprovalsHost, createServerClient, createServerComposerHost, createServerExtensionModuleLoader, createServerExtensionsHost, createServerFilesHost, createServerGitHost, createServerHost, createServerMachinesHost, createServerOnboardingHost, createServerPaletteHost, createServerSessionHost, createServerSettingsHost, createServerSidebarHost, createServerTerminalHost, createServerWebviewSources, createSseHost, createSseParser, createStreamPacer, createWorkerHost, decodeFromWorker, decodeToWorker, encodeSseRecord, exposeRunHost, finishLive, formatCursor, installExtensionHostModules, linkExtensionModule, parseCursor, toChatModelProviders, toChatSession, toSidebarListSession };
29
+ export { AnswerBody, BrowserAppearanceHostOptions, BrowserKeybindingsHostOptions, BrowserWorkspaceHostOptions, EXTENSION_HOST_MODULES_GLOBAL, ExtensionFlowStepHandler, ExtensionHostModule, ExtensionHostModuleSources, ExtensionHostModules, ExtensionModuleLinkError, ExtensionModuleLoad, ExtensionModuleLoadError, ExtensionsClientAnnounce, FromWorker, HydrateResponse, LinkedExtensionModule, LiveState, LiveStateOptions, LiveTranscript, MessageEndpoint, RunContext, RunFn, RunHost, RunHostOptions, SERVER_AUTH_CHECK_PATH, SERVER_MACHINES_HOST_ID, SSE_HISTORY_RECORDS_VERSION, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, SendBody, ServerApprovalsHostOptions, ServerCallError, type ServerClient, type ServerClientOptions, type ServerClientState, ServerComposerHost, ServerComposerHostOptions, ServerConnectionError, ServerExtensionAssetsAccess, ServerExtensionBundle, ServerExtensionBundleLookup, ServerExtensionCommandWire, ServerExtensionCommands, ServerExtensionDialogs, ServerExtensionModuleLoader, ServerExtensionModuleLoaderOptions, ServerExtensionsHost, ServerExtensionsHostOptions, ServerFilesHostOptions, ServerGitHostOptions, ServerHost, ServerHostOptions, ServerMachinesHost, ServerMachinesHostOptions, ServerOnboardingHost, ServerOnboardingHostOptions, ServerPaletteHost, ServerPaletteHostOptions, type ServerRefusal, ServerSessionHost, ServerSessionHostOptions, ServerSettingsHost, ServerSettingsHostOptions, ServerSidebarHostOptions, ServerTerminalHostOptions, ServerWebviewSources, ServerWebviewSourcesOptions, SseHost, SseHostOptions, SseParser, SseRecord, StreamPacer, StreamPacerOptions, StreamRecord, ToWorker, WORKER_PROTOCOL_VERSION, WorkerHostOptions, WorkerRunInput, applyLiveEvent, attachmentsFromPreviews, closeThinkingSegment, createBrowserAppearanceHost, createBrowserKeybindingsHost, createBrowserWorkspaceHost, createLiveState, createRunHost, createServerApprovalsHost, createServerClient, createServerComposerHost, createServerExtensionModuleLoader, createServerExtensionsHost, createServerFilesHost, createServerGitHost, createServerHost, createServerMachinesHost, createServerOnboardingHost, createServerPaletteHost, createServerSessionHost, createServerSettingsHost, createServerSidebarHost, createServerTerminalHost, createServerWebviewSources, createSseHost, createSseParser, createStreamPacer, createWorkerHost, decodeFromWorker, decodeToWorker, encodeSseRecord, exposeRunHost, finishLive, formatCursor, installExtensionHostModules, linkExtensionModule, parseCursor, toChatModelProviders, toChatSession, toSidebarListSession };
@@ -8,7 +8,7 @@ import { createRunHost } from "./run-host.js";
8
8
  import { WORKER_PROTOCOL_VERSION, decodeFromWorker, decodeToWorker } from "./worker-protocol.js";
9
9
  import { createWorkerHost, exposeRunHost } from "./worker-host.js";
10
10
  import { createSseParser } from "./sse-parser.js";
11
- import { SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, attachmentsFromPreviews, encodeSseRecord, formatCursor, parseCursor } from "./sse-protocol.js";
11
+ import { SSE_HISTORY_RECORDS_VERSION, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, attachmentsFromPreviews, encodeSseRecord, formatCursor, parseCursor } from "./sse-protocol.js";
12
12
  import { createSseHost } from "./sse-host.js";
13
13
  import { SERVER_AUTH_CHECK_PATH, ServerCallError, ServerConnectionError, createServerClient } from "./server-client.js";
14
14
  import { createServerHost, toChatModelProviders, toChatSession, toSidebarListSession } from "./server-host.js";
@@ -25,4 +25,4 @@ import { createServerSettingsHost } from "./server-settings-host.js";
25
25
  import { createServerSidebarHost } from "./server-sidebar-host.js";
26
26
  import { createServerExtensionsHost } from "./server-extensions-host.js";
27
27
  import { createStreamPacer } from "./stream-pacer.js";
28
- export { EXTENSION_HOST_MODULES_GLOBAL, ExtensionModuleLinkError, ExtensionModuleLoadError, SERVER_AUTH_CHECK_PATH, SERVER_MACHINES_HOST_ID, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, ServerCallError, ServerConnectionError, WORKER_PROTOCOL_VERSION, applyLiveEvent, attachmentsFromPreviews, closeThinkingSegment, createBrowserAppearanceHost, createBrowserKeybindingsHost, createBrowserWorkspaceHost, createLiveState, createRunHost, createServerApprovalsHost, createServerClient, createServerComposerHost, createServerExtensionModuleLoader, createServerExtensionsHost, createServerFilesHost, createServerGitHost, createServerHost, createServerMachinesHost, createServerOnboardingHost, createServerPaletteHost, createServerSessionHost, createServerSettingsHost, createServerSidebarHost, createServerTerminalHost, createServerWebviewSources, createSseHost, createSseParser, createStreamPacer, createWorkerHost, decodeFromWorker, decodeToWorker, encodeSseRecord, exposeRunHost, finishLive, formatCursor, installExtensionHostModules, linkExtensionModule, parseCursor, toChatModelProviders, toChatSession, toSidebarListSession };
28
+ export { EXTENSION_HOST_MODULES_GLOBAL, ExtensionModuleLinkError, ExtensionModuleLoadError, SERVER_AUTH_CHECK_PATH, SERVER_MACHINES_HOST_ID, SSE_HISTORY_RECORDS_VERSION, SSE_PROTOCOL_HEADER, SSE_PROTOCOL_VERSION, ServerCallError, ServerConnectionError, WORKER_PROTOCOL_VERSION, applyLiveEvent, attachmentsFromPreviews, closeThinkingSegment, createBrowserAppearanceHost, createBrowserKeybindingsHost, createBrowserWorkspaceHost, createLiveState, createRunHost, createServerApprovalsHost, createServerClient, createServerComposerHost, createServerExtensionModuleLoader, createServerExtensionsHost, createServerFilesHost, createServerGitHost, createServerHost, createServerMachinesHost, createServerOnboardingHost, createServerPaletteHost, createServerSessionHost, createServerSettingsHost, createServerSidebarHost, createServerTerminalHost, createServerWebviewSources, createSseHost, createSseParser, createStreamPacer, createWorkerHost, decodeFromWorker, decodeToWorker, encodeSseRecord, exposeRunHost, finishLive, formatCursor, installExtensionHostModules, linkExtensionModule, parseCursor, toChatModelProviders, toChatSession, toSidebarListSession };
@@ -1,6 +1,8 @@
1
1
  import { Readable, Writable } from "../chat/headless/store.js";
2
2
  import { ChatAttachment, ChatEvent, ChatInteraction, ChatSession, ChatSessionStatus, Transcript, TranscriptState } from "../chat/headless/host.js";
3
3
  import { applyLiveEvent, finishLive } from "../chat/headless/streams/live-events.js";
4
+ import { PendingEcho } from "../chat/headless/streams/store.js";
5
+ import { HistoryChange, HistorySince } from "zidane/chat/pure";
4
6
  import { InteractionRequest } from "zidane/chat";
5
7
  //#region src/adapters/live.d.ts
6
8
  /** One session's live transcript: its state store and the echo bookkeeping. */
@@ -8,7 +10,7 @@ interface LiveTranscript {
8
10
  /** The session's transcript state, updated as events arrive. */
9
11
  state: Writable<TranscriptState>;
10
12
  /** Prompts this client echoed itself, waiting for the host's echo to drop (the store's pending-echo list). */
11
- echoes: string[];
13
+ echoes: PendingEcho[];
12
14
  }
13
15
  /** The client half of the live transcript: the stores a {@link ChatHost} exposes, fed by live events. Shared by every adapter. */
14
16
  interface LiveState {
@@ -18,8 +20,32 @@ interface LiveState {
18
20
  transcript(sessionId: string): LiveTranscript;
19
21
  /** The `Transcript` a host hands the components, with `onFirstSubscribe` / `onLastUnsubscribe` hooks. */
20
22
  readable(sessionId: string, extra?: Pick<Transcript, 'loadEarlier'>): Transcript;
21
- /** Replaces a session's events with a loaded history and marks it hydrated. `patch` sets other transcript state fields, such as `running`. */
22
- hydrate(sessionId: string, events: readonly ChatEvent[], patch?: Partial<TranscriptState>): void;
23
+ /**
24
+ * Replaces a session's events with a loaded history and marks it hydrated. `patch` sets other transcript state
25
+ * fields, such as `running`. `pos` is the history position the history was read at, and `historyId` is its
26
+ * numbering. Returns `false` and replaces nothing when the history is older than a write the window applied
27
+ * since. The adapter then reads it again.
28
+ */
29
+ hydrate(sessionId: string, events: readonly ChatEvent[], patch?: Partial<TranscriptState>, pos?: number, historyId?: string): boolean;
30
+ /**
31
+ * Applies one history write (`history:changed`) to a session's window with the shared rule
32
+ * (`applyHistoryOp`). Returns `true` when the adapter must hydrate the session again now (a rewrite, a
33
+ * missed write). While its run streams, that read waits for the run's end (`takeRefetchDue`).
34
+ */
35
+ applyHistory(sessionId: string, change: HistoryChange): boolean;
36
+ /** Whether the session must be hydrated again now that its run ended (see `applyHistory`). Clears the flag. */
37
+ takeRefetchDue(sessionId: string): boolean;
38
+ /** Applies the reconnect ladder's second step (`applyHistorySince`). Returns `true` when the adapter must hydrate the session again. */
39
+ applyHistorySince(sessionId: string, since: HistorySince<ChatEvent>): boolean;
40
+ /** The history position the session's window is at (its reconnect bookmark), when known. */
41
+ historyPos(sessionId: string): number | undefined;
42
+ /** The numbering of {@link LiveState.historyPos}, when known. */
43
+ historyId(sessionId: string): string | undefined;
44
+ /**
45
+ * Whether the session's rows are behind its history position. This is so when a tail read that a write asked for
46
+ * has not landed (it failed, or it waits for the run's end). A reconnect then reads the tail.
47
+ */
48
+ tailDue(sessionId: string): boolean;
23
49
  /** A live event from the agent; `echo` marks the host repeating a prompt (dropped when this client echoed it). */
24
50
  apply(sessionId: string, event: ChatEvent, echo?: boolean): void;
25
51
  /** Show the user's prompt at once (with its attachment previews); the host's echo of it is then dropped. */
@@ -103,7 +103,8 @@ function createLiveState(options = {}) {
103
103
  readables.set(sessionId, r);
104
104
  return r;
105
105
  },
106
- hydrate(sessionId, events, patch) {
106
+ hydrate(sessionId, events, patch, pos, historyId) {
107
+ if (store.adoptHistoryPos(sessionId, pos, historyId) === "stale") return false;
107
108
  const t = transcript(sessionId);
108
109
  t.echoes = [];
109
110
  t.state.set((s) => ({
@@ -112,7 +113,14 @@ function createLiveState(options = {}) {
112
113
  events: [...events],
113
114
  hydrated: true
114
115
  }));
116
+ return true;
115
117
  },
118
+ applyHistory: (sessionId, change) => store.applyHistory(sessionId, change),
119
+ takeRefetchDue: (sessionId) => store.takeRefetchDue(sessionId),
120
+ applyHistorySince: (sessionId, since) => store.applyHistorySince(sessionId, since),
121
+ historyPos: (sessionId) => store.getHistoryPosSnapshot(sessionId),
122
+ historyId: (sessionId) => store.getHistoryIdSnapshot(sessionId),
123
+ tailDue: (sessionId) => store.isTailDue(sessionId),
116
124
  apply(sessionId, event, echo) {
117
125
  transcript(sessionId);
118
126
  store.applySignal({
@@ -146,6 +146,20 @@ function createServerComposerHost(options) {
146
146
  return () => void listeners.delete(filtered);
147
147
  };
148
148
  let seq = 0;
149
+ /** Each session's fields a `#` reference shows: a row change that moves none of them (a write to its history, a run) reads nothing again. */
150
+ const chatKeys = /* @__PURE__ */ new Map();
151
+ const chatsMoved = (signal) => {
152
+ if (signal.op === "delete") return chatKeys.delete(signal.sessionId) || true;
153
+ const key = JSON.stringify([
154
+ signal.row.title,
155
+ signal.row.archived,
156
+ signal.row.projectRoot,
157
+ signal.row.cwd
158
+ ]);
159
+ const moved = chatKeys.get(signal.sessionId) !== key;
160
+ chatKeys.set(signal.sessionId, key);
161
+ return moved;
162
+ };
149
163
  const resources = createResourceStore({
150
164
  invalidation: () => ({ onInvalidate: (listener) => on([
151
165
  "settings:changed",
@@ -156,8 +170,9 @@ function createServerComposerHost(options) {
156
170
  "sessions:changed"
157
171
  ], (signal) => {
158
172
  if (signal.kind === "settings:changed" || signal.kind === "catalog:changed") listener({ topic: "models" });
159
- else if (signal.kind === "sessions:changed") listener({ topic: "chats" });
160
- else if (signal.kind === "event") {
173
+ else if (signal.kind === "sessions:changed") {
174
+ if (chatsMoved(signal)) listener({ topic: "chats" });
175
+ } else if (signal.kind === "event") {
161
176
  if (signal.event.kind === "tool-result") listener({ topic: "files" });
162
177
  } else listener({
163
178
  topic: "session:context",
@@ -79,7 +79,6 @@ interface ServerExtensionsHost extends ExtensionsHost {
79
79
  commands: ServerExtensionCommands;
80
80
  panelChanged: NonNullable<ExtensionsHost['panelChanged']>;
81
81
  openLink: NonNullable<ExtensionsHost['openLink']>;
82
- scope: NonNullable<ExtensionsHost['scope']>;
83
82
  /** With `assets`: the webview URLs the server mints. */
84
83
  webviews?: ExtensionWebviews & ServerWebviewSources;
85
84
  /** Settles once the first listing of the folder's items, settings, commands and integrations is in. */
@@ -4,7 +4,7 @@ import { createDialogQueue } from "../extensions/headless/dialog-queue.js";
4
4
  import { createUiEffectChannel } from "../extensions/headless/effect-channel.js";
5
5
  import { ExtensionsHostError } from "../extensions/headless/host.js";
6
6
  import { EXTENSION_TOKENS } from "../extensions/headless/wire.js";
7
- import { IpcErrorCode, ServerCallError, ServerConnectionError, createExtensionsClient } from "zidane/ipc/browser";
7
+ import { ServerCallError, ServerConnectionError, createExtensionsClient } from "zidane/ipc/browser";
8
8
  //#region src/adapters/server-extensions-host.ts
9
9
  /**
10
10
  * An `ExtensionsHost` on a running zidane-server, over the same client as the chat host
@@ -22,9 +22,7 @@ import { IpcErrorCode, ServerCallError, ServerConnectionError, createExtensionsC
22
22
  * - `ctx.ui` panels, navigation, composer edits, notifications and `ctx.keymap.dispatch` chords into a
23
23
  * `createUiEffectChannel`, answered with `ui.client.reply`
24
24
  * - links: `openLink` offers a link to the folder's link handlers (`extensions.links.open`)
25
- * - the token's scope (`scope`, from the handshake): action settings above it show disabled
26
- * - RPC: `rpc.call` (aborts send `rpc.cancel`); the server checks each method's `scope` against
27
- * this window's token (`rpc.forbidden` below it)
25
+ * - RPC: `rpc.call` (aborts send `rpc.cancel`)
28
26
  * - stores: `extensions.storage.load` / `.set`, kept current by `extensions:storage`; a write shows at
29
27
  * once and settles to what the server holds (a refused one is put back)
30
28
  * - settings (`extensions.settings`, the catalog provenance), integrations, commands (with their
@@ -56,11 +54,6 @@ const CATALOG_KINDS = [
56
54
  function hostError(err) {
57
55
  if (err instanceof ExtensionsHostError) return err;
58
56
  if (err instanceof ServerConnectionError) return new ExtensionsHostError({ code: "unavailable" });
59
- if (err instanceof ServerCallError && err.code === IpcErrorCode.Forbidden) return new ExtensionsHostError({
60
- code: "refused",
61
- reason: "not-allowed",
62
- message: err.message
63
- });
64
57
  if (err instanceof Error && err.name === "RpcCallError" && typeof err.type === "string") {
65
58
  const { type, data } = err;
66
59
  return new ExtensionsHostError({
@@ -447,11 +440,8 @@ function createServerExtensionsHost(options) {
447
440
  if (reconnected) reset();
448
441
  }));
449
442
  if (client.hello()) sendAnnounce().catch(onError);
450
- const ready = refreshAll();
451
- const scope = writable(client.hello()?.scope ?? null);
452
- offs.push(client.onOpen((hello) => scope.set(hello.scope ?? null)));
453
443
  return {
454
- ready,
444
+ ready: refreshAll(),
455
445
  items,
456
446
  invoke,
457
447
  dialogs,
@@ -467,7 +457,6 @@ function createServerExtensionsHost(options) {
467
457
  const { handled } = await ext.links.open(cwd, link).catch(rethrow);
468
458
  return handled;
469
459
  },
470
- scope,
471
460
  storage: storage.storage,
472
461
  settings: settings.settings,
473
462
  integrations: integrations.integrations,
@@ -726,7 +715,7 @@ function serverIntegrations(client, folder, onError, onStep) {
726
715
  key: key ?? "",
727
716
  methodId
728
717
  }).catch((err) => {
729
- if (err instanceof ServerCallError && err.code !== IpcErrorCode.Forbidden) throw new ExtensionsHostError({
718
+ if (err instanceof ServerCallError) throw new ExtensionsHostError({
730
719
  code: "refused",
731
720
  reason: "invalid",
732
721
  message: err.message
@@ -5,8 +5,8 @@ import { parseRepoInput } from "zidane/chat/pure";
5
5
  * host (`createServerClient`, or `createServerHost(...).client`).
6
6
  *
7
7
  * What it offers: the server's folders for a session's folder (`fs.listDirs`,
8
- * inside the server's workspace roots, with what the hello's `clone` and
9
- * `createFolders` allow there), creating and searching them (`fs.mkdir`,
8
+ * inside the server's workspace roots, with what the server offers there),
9
+ * creating and searching them (`fs.mkdir`,
10
10
  * `fs.searchDirs`), a shared machine's folders (`machines.listDirs`), the
11
11
  * environment kinds a new session can run in
12
12
  * (`environments.list`, read again on `settings:changed`, where environment
@@ -21,9 +21,7 @@ import { parseRepoInput } from "zidane/chat/pure";
21
21
  * the first workspace root by default). A session's tool results refresh the
22
22
  * git bar (`event` signals), and so do its refs moving (`subscribeRefs`:
23
23
  * `git.watch`, `git:refs`, watched again after a reconnect). Each git member
24
- * is offered when the connection may call its method (`client.can`): reads,
25
- * cloning and new folders need a `chat` token, the other writes an `admin`
26
- * one.
24
+ * is offered when the server's hello lists its method (`client.can`).
27
25
  *
28
26
  * Not offered: a native folder dialog, and clone on send (the browser has no
29
27
  * project on this computer to start from: `originUrl`, `cloneOnServer`).
@@ -60,22 +58,15 @@ function readSync(result) {
60
58
  };
61
59
  }
62
60
  /**
63
- * What the server lets this connection do in its folders: the hello's flags, and the methods its token reaches. An
64
- * older server sends no flags: the methods its hello lists say it then. `undefined` when neither says.
61
+ * What the server offers in its folders, from the methods its hello lists. `undefined` when it lists none (an older
62
+ * server).
65
63
  */
66
64
  function folderAllows(client) {
67
- const hello = client.hello();
68
- const searchFolders = client.can("fs.searchDirs");
69
- if (hello?.clone !== void 0 || hello?.createFolders !== void 0) return {
70
- createFolders: hello.createFolders === true && client.can("fs.mkdir"),
71
- clone: hello.clone === true && client.can("git.clone"),
72
- searchFolders
73
- };
74
- if (!hello?.methods) return void 0;
65
+ if (!client.hello()?.methods) return void 0;
75
66
  return {
76
67
  createFolders: client.can("fs.mkdir"),
77
68
  clone: client.can("git.clone"),
78
- searchFolders
69
+ searchFolders: client.can("fs.searchDirs")
79
70
  };
80
71
  }
81
72
  /** The repository import over `git.clone`: one clone per connection, landing in the first workspace root by default. */
@@ -3,6 +3,7 @@ import { createLiveState } from "./live.js";
3
3
  import { createServerClient } from "./server-client.js";
4
4
  import { wireOriginal } from "../chat/headless/send-flow.js";
5
5
  import { createServerTranscriptMembers } from "./server-transcript-members.js";
6
+ import { climbReconnectLadder, createRecentChanges, cursorValid } from "zidane/chat/pure";
6
7
  //#region src/adapters/server-host.ts
7
8
  /** Transcript turns per page. */
8
9
  const PAGE_TURNS = 200;
@@ -38,6 +39,7 @@ function toSidebarListSession(row) {
38
39
  return {
39
40
  ...toChatSession(row),
40
41
  cwd: row.cwd ?? row.projectRoot ?? "",
42
+ ...row.lastUserMessageAt !== void 0 ? { lastUserMessageAt: row.lastUserMessageAt } : {},
41
43
  ...row.projectRoot ? { projectRoot: row.projectRoot } : {},
42
44
  ...row.spaceId ? { spaceId: row.spaceId } : {},
43
45
  ...row.isMain ? { isMain: true } : {},
@@ -80,12 +82,15 @@ function createServerHost(options) {
80
82
  const client = options.client ?? createServerClient(options);
81
83
  const connection = writable(client.state());
82
84
  const watched = /* @__PURE__ */ new Set();
85
+ /** Per transcript, the scroll-back cursor and the history position it was taken at. */
83
86
  const cursors = /* @__PURE__ */ new Map();
84
87
  /** Last signal number seen per session, and the host process that numbered it. */
85
88
  const seqs = /* @__PURE__ */ new Map();
86
89
  let epoch;
87
90
  /** Signals that arrive between a hydrate's snapshot and its replace, replayed after it. */
88
91
  const hydrating = /* @__PURE__ */ new Map();
92
+ /** A session's signals that arrive while its reconnect ladder runs (`resume`), applied after what it brings. */
93
+ const resuming = /* @__PURE__ */ new Map();
89
94
  const models = writable([]);
90
95
  const state = createLiveState({
91
96
  onOpen: (sessionId) => {
@@ -99,14 +104,21 @@ function createServerHost(options) {
99
104
  const facetListeners = /* @__PURE__ */ new Set();
100
105
  const onSignal = (signal, meta) => {
101
106
  const sessionId = "sessionId" in signal && typeof signal.sessionId === "string" ? signal.sessionId : void 0;
102
- if (sessionId && meta) seqs.set(sessionId, meta.seq);
103
- if ((signal.kind === "event" || signal.kind === "run:start" || signal.kind === "run:end") && hydrating.has(signal.sessionId)) {
107
+ if (sessionId && resuming.has(sessionId)) {
108
+ resuming.get(sessionId).push({
109
+ signal,
110
+ ...meta ? { meta } : {}
111
+ });
112
+ return;
113
+ }
114
+ if ((signal.kind === "event" || signal.kind === "run:start" || signal.kind === "run:end" || signal.kind === "history:changed") && hydrating.has(signal.sessionId)) {
104
115
  hydrating.get(signal.sessionId).push({
105
116
  signal,
106
117
  ...meta ? { meta } : {}
107
118
  });
108
119
  return;
109
120
  }
121
+ if (sessionId && meta) seqs.set(sessionId, meta.seq);
110
122
  switch (signal.kind) {
111
123
  case "event":
112
124
  state.apply(signal.sessionId, signal.event, signal.echo);
@@ -114,8 +126,10 @@ function createServerHost(options) {
114
126
  case "run:start":
115
127
  case "run:end":
116
128
  if (signal.kind === "run:start") state.setRunning(signal.sessionId, true);
117
- else state.setRunning(signal.sessionId, false, signal.ok ? "completed" : "error");
118
- if (state.sessions.get().some((s) => s.id === signal.sessionId && s.title === UNTITLED)) refreshTitles().catch(onError);
129
+ else {
130
+ state.setRunning(signal.sessionId, false, signal.ok ? "completed" : "error");
131
+ if (state.takeRefetchDue(signal.sessionId) && watched.has(signal.sessionId)) reread(signal.sessionId);
132
+ }
119
133
  return;
120
134
  case "interaction:request":
121
135
  state.addInteraction(signal.sessionId, signal.request);
@@ -126,58 +140,152 @@ function createServerHost(options) {
126
140
  case "sessions:facets":
127
141
  for (const listener of [...facetListeners]) listener(signal.sessionId, signal.facets);
128
142
  return;
129
- case "sessions:changed":
130
- for (const listener of [...changed]) listener(signal.sessionId);
131
- refresh().catch(onError);
132
- if (signal.op === "update" && watched.has(signal.sessionId)) hydrate(signal.sessionId).catch(onError);
143
+ case "sessions:changed": {
144
+ const listed = signal.op === "delete" || signal.row.sideOf ? void 0 : signal.row;
145
+ const change = signal.op === "delete" ? {
146
+ op: "delete",
147
+ sessionId: signal.sessionId
148
+ } : {
149
+ op: signal.op,
150
+ sessionId: signal.sessionId,
151
+ ...listed ? { row: toSidebarListSession(listed) } : {}
152
+ };
153
+ if (signal.op === "delete") {
154
+ state.sessions.set((all) => withoutSession(all, signal.sessionId));
155
+ listChanges.note(signal.sessionId, null);
156
+ } else if (listed) {
157
+ const session = toChatSession(listed);
158
+ upsertSession(session);
159
+ listChanges.note(session.id, session);
160
+ }
161
+ for (const listener of [...changed]) listener(change);
162
+ return;
163
+ }
164
+ case "history:changed":
165
+ if (watched.has(signal.sessionId) && state.applyHistory(signal.sessionId, signal)) reread(signal.sessionId);
166
+ return;
133
167
  }
134
168
  };
135
169
  const loadPage = async (sessionId, before) => {
136
- const params = {
170
+ return client.call("sessions.loadEvents", {
137
171
  sessionId,
138
172
  limit: PAGE_TURNS,
139
173
  ...before !== void 0 ? { before } : {}
140
- };
141
- const result = await client.call("sessions.loadEvents", params);
142
- return Array.isArray(result) ? { events: result } : result;
174
+ });
143
175
  };
144
- const hydrate = async (sessionId) => {
176
+ const hydrate = async (sessionId, attempts = 3) => {
145
177
  const buffered = [];
146
178
  hydrating.set(sessionId, buffered);
179
+ let stale = false;
147
180
  try {
148
181
  const page = await loadPage(sessionId);
149
182
  const running = await client.call("chat.isRunning", { sessionId });
150
- cursors.set(sessionId, page.cursor);
151
- state.hydrate(sessionId, page.events, {
183
+ stale = !state.hydrate(sessionId, page.events, {
152
184
  running,
153
185
  hasEarlier: page.cursor !== void 0
186
+ }, page.pos, page.historyId);
187
+ if (!stale) cursors.set(sessionId, {
188
+ before: page.cursor,
189
+ ...page.pos !== void 0 ? { pos: page.pos } : {},
190
+ ...page.pos !== void 0 && page.historyId !== void 0 ? { historyId: page.historyId } : {}
154
191
  });
155
192
  } finally {
156
193
  if (hydrating.get(sessionId) === buffered) hydrating.delete(sessionId);
157
194
  for (const { signal, meta } of buffered) onSignal(signal, meta);
158
195
  }
196
+ if (stale && attempts > 1) return hydrate(sessionId, attempts - 1);
159
197
  const interactions = await client.call("interactions.list", { sessionId });
160
198
  for (const i of state.pending(sessionId).get()) state.resolveInteraction(i.requestId);
161
199
  for (const i of interactions) state.addInteraction(sessionId, i.request);
162
200
  };
163
- /** After a reconnect: replay what the transcript missed, or reload it when the server can't. */
164
- const resume = async (sessionId) => {
165
- const afterSeq = seqs.get(sessionId);
166
- if (epoch && afterSeq !== void 0 && client.can("sessions.resume")) {
167
- const result = await client.call("sessions.resume", {
168
- sessionId,
169
- epoch,
170
- afterSeq
171
- });
172
- if (result.ok) {
173
- for (const { seq, signal } of result.signals) onSignal(signal, { seq });
174
- return;
201
+ /**
202
+ * Reads a transcript again for a signal (a rewrite, a missed write, a run's end). One such read per session is in
203
+ * flight. A read asked for while another runs is made when the first ends, if the first did not bring the window up
204
+ * to date (it failed, or a write asked for rows it lacks).
205
+ */
206
+ const rereads = /* @__PURE__ */ new Map();
207
+ const reread = (sessionId) => {
208
+ const inFlight = rereads.get(sessionId);
209
+ if (inFlight) {
210
+ inFlight.again = true;
211
+ return;
212
+ }
213
+ const entry = { again: false };
214
+ rereads.set(sessionId, entry);
215
+ hydrate(sessionId).catch(onError).finally(() => {
216
+ rereads.delete(sessionId);
217
+ if (entry.again && state.tailDue(sessionId) && watched.has(sessionId)) reread(sessionId);
218
+ });
219
+ };
220
+ /**
221
+ * Climbs the reconnect ladder from the transcript's bookmark after a
222
+ * reconnect. The session's live signals wait until the ladder's rows are in
223
+ * (the signals it missed, or the rows appended since), and apply after them.
224
+ */
225
+ const resume = async (sessionId, bookmark) => {
226
+ const held = [];
227
+ resuming.set(sessionId, held);
228
+ /**
229
+ * Applies `missed` (the ladder's rows), then what arrived live meanwhile. It
230
+ * skips the signals that the replay also carried (numbered up to `replayedTo`).
231
+ */
232
+ const release = (missed = () => void 0, replayedTo = -1) => {
233
+ if (resuming.get(sessionId) === held) resuming.delete(sessionId);
234
+ try {
235
+ missed();
236
+ } finally {
237
+ for (const { signal, meta } of held) if (meta === void 0 || meta.seq > replayedTo) onSignal(signal, meta);
238
+ }
239
+ };
240
+ const outcome = await climbReconnectLadder(bookmark, {
241
+ resume: async (from, afterSeq) => {
242
+ const result = await client.call("sessions.resume", {
243
+ sessionId,
244
+ epoch: from,
245
+ afterSeq
246
+ });
247
+ return result.ok ? result.signals : null;
248
+ },
249
+ since: async (afterPos, historyId) => {
250
+ const result = await client.call("sessions.historySince", {
251
+ sessionId,
252
+ afterPos,
253
+ historyId
254
+ }).catch(() => null);
255
+ return result?.ok ? {
256
+ events: result.events,
257
+ pos: result.pos,
258
+ floor: result.floor,
259
+ historyId: result.historyId,
260
+ ...result.after !== void 0 ? { after: result.after } : {}
261
+ } : null;
175
262
  }
263
+ }).catch((error) => {
264
+ release();
265
+ throw error;
266
+ });
267
+ if (outcome.via === "resume") {
268
+ const { signals } = outcome;
269
+ release(() => {
270
+ for (const { seq, signal } of signals) onSignal(signal, { seq });
271
+ }, signals.at(-1)?.seq ?? -1);
272
+ return;
273
+ }
274
+ const since = outcome.via === "since" ? outcome.since : void 0;
275
+ let read = since === void 0;
276
+ release(() => {
277
+ if (since) read = state.applyHistorySince(sessionId, since);
278
+ });
279
+ if (!read) {
280
+ const running = await client.call("chat.isRunning", { sessionId });
281
+ state.setRunning(sessionId, running);
282
+ if (running || !state.takeRefetchDue(sessionId)) return;
176
283
  }
177
284
  await hydrate(sessionId);
178
285
  };
179
286
  const loadEarlier = async (sessionId) => {
180
- const before = cursors.get(sessionId);
287
+ const cursor = cursors.get(sessionId);
288
+ const before = cursor?.before;
181
289
  const t = state.transcript(sessionId).state;
182
290
  if (before === void 0 || t.get().loadingEarlier) return;
183
291
  t.set((s) => ({
@@ -186,7 +294,19 @@ function createServerHost(options) {
186
294
  }));
187
295
  try {
188
296
  const page = await loadPage(sessionId, before);
189
- cursors.set(sessionId, page.cursor);
297
+ if (!cursorValid(cursor?.pos, page, cursor?.historyId)) {
298
+ t.set((s) => ({
299
+ ...s,
300
+ loadingEarlier: false
301
+ }));
302
+ await hydrate(sessionId);
303
+ return;
304
+ }
305
+ cursors.set(sessionId, {
306
+ before: page.cursor,
307
+ ...cursor?.pos !== void 0 ? { pos: cursor.pos } : {},
308
+ ...cursor?.historyId !== void 0 ? { historyId: cursor.historyId } : {}
309
+ });
190
310
  t.set((s) => ({
191
311
  ...s,
192
312
  events: [...page.events, ...s.events],
@@ -201,19 +321,19 @@ function createServerHost(options) {
201
321
  onError(error);
202
322
  }
203
323
  };
324
+ /** The changes applied while the list is read again. They are newer than its answer (`createRecentChanges`). */
325
+ const listChanges = createRecentChanges();
204
326
  const refresh = async () => {
205
- const rows = await client.call("sessions.list", { archive: "all" });
206
- state.sessions.set(rows.map(toChatSession));
207
- };
208
- /** Takes the listed titles only: a run's status, set from its signals, stays. */
209
- const refreshTitles = async () => {
210
- const rows = await client.call("sessions.list", { archive: "all" });
211
- const titles = new Map(rows.map((r) => [r.id, r.title || UNTITLED]));
212
- state.sessions.set((all) => all.some((s) => titles.has(s.id) && titles.get(s.id) !== s.title) ? all.map((s) => titles.has(s.id) && titles.get(s.id) !== s.title ? {
213
- ...s,
214
- title: titles.get(s.id)
215
- } : s) : all);
327
+ const reading = listChanges.begin();
328
+ try {
329
+ const rows = await client.call("sessions.list", { archive: "all" });
330
+ state.sessions.set(listChanges.end(reading, rows.map(toChatSession), (list, session, id) => session ? withSession(list, session) : withoutSession(list, id)));
331
+ } finally {
332
+ listChanges.drop(reading);
333
+ }
216
334
  };
335
+ /** A session's row from a change signal, in place (a new one first). */
336
+ const upsertSession = (session) => state.sessions.set((all) => withSession(all, session));
217
337
  const loadModels = async () => {
218
338
  if (!client.can("models.list")) return;
219
339
  const { providers } = await client.call("models.list", options.cwd ? { cwd: options.cwd } : {});
@@ -223,13 +343,23 @@ function createServerHost(options) {
223
343
  client.onState((s) => connection.set(s)),
224
344
  client.onSignal(onSignal),
225
345
  client.onOpen((hello, reconnected) => {
226
- const sameProcess = epoch !== void 0 && hello.signalEpoch === epoch;
346
+ const bookmarks = [...watched].map((sessionId) => state.tailDue(sessionId) ? { sessionId } : {
347
+ sessionId,
348
+ ...state.historyPos(sessionId) !== void 0 && state.historyId(sessionId) !== void 0 ? {
349
+ pos: state.historyPos(sessionId),
350
+ historyId: state.historyId(sessionId)
351
+ } : {},
352
+ ...epoch !== void 0 && seqs.has(sessionId) ? {
353
+ epoch,
354
+ seq: seqs.get(sessionId)
355
+ } : {}
356
+ });
357
+ if (epoch === void 0 || hello.signalEpoch !== epoch) seqs.clear();
227
358
  epoch = hello.signalEpoch;
228
- if (!sameProcess) seqs.clear();
229
359
  loadModels().catch(onError);
230
360
  if (!reconnected) return;
231
361
  refresh().catch(onError);
232
- for (const id of watched) resume(id).catch(onError);
362
+ for (const bookmark of bookmarks) resume(bookmark.sessionId, bookmark).catch(onError);
233
363
  })
234
364
  ];
235
365
  const ready = client.ready.then(() => refresh());
@@ -439,5 +569,13 @@ function createServerHost(options) {
439
569
  }
440
570
  };
441
571
  }
572
+ /** `list` with `session`'s row in place, or first when it is new. */
573
+ function withSession(list, session) {
574
+ return list.some((s) => s.id === session.id) ? list.map((s) => s.id === session.id ? session : s) : [session, ...list];
575
+ }
576
+ /** `list` without the session's row. */
577
+ function withoutSession(list, sessionId) {
578
+ return list.some((s) => s.id === sessionId) ? list.filter((s) => s.id !== sessionId) : list;
579
+ }
442
580
  //#endregion
443
581
  export { createServerHost, toChatModelProviders, toChatSession, toSidebarListSession };
@@ -12,6 +12,8 @@ interface ServerMachinesHostOptions {
12
12
  * Absent: no `machines.invite`.
13
13
  */
14
14
  url?: string;
15
+ /** Opens a new chat on the server, or on one of its machines (the app's own navigation). Absent: no `newChat`. */
16
+ newChat?: (hostId: string, machine?: string) => void;
15
17
  }
16
18
  /** The {@link MachinesHost} {@link createServerMachinesHost} returns. */
17
19
  interface ServerMachinesHost extends MachinesHost {