@abot-ai/runtime 1.6.0 → 1.7.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.
Files changed (380) hide show
  1. package/README.md +2 -0
  2. package/dist/src/bridge/request-steering.js +60 -15
  3. package/dist/src/capabilities/normal-invocation/contracts.d.ts +3 -0
  4. package/dist/src/capabilities/normal-invocation/output-channels.d.ts +10 -0
  5. package/dist/src/capabilities/normal-invocation/output-channels.js +29 -0
  6. package/dist/src/capabilities/normal-invocation/validator.js +4 -2
  7. package/dist/src/capabilities/tool-types.d.ts +1 -1
  8. package/dist/src/cli/host-companion-bundle.mjs +7570 -6781
  9. package/dist/src/cli/host-companion-entry.js +5 -22
  10. package/dist/src/cli/host-companion-input.d.ts +3 -0
  11. package/dist/src/cli/host-companion-input.js +21 -0
  12. package/dist/src/cli/host-companion-lifecycle.d.ts +11 -0
  13. package/dist/src/cli/host-companion-lifecycle.js +32 -0
  14. package/dist/src/cli/host-companion-setup-target.d.ts +4 -0
  15. package/dist/src/cli/host-companion-setup-target.js +17 -0
  16. package/dist/src/cli/host-companion-startup.d.ts +2 -0
  17. package/dist/src/cli/host-companion-startup.js +7 -2
  18. package/dist/src/cli/host-companion.d.ts +6 -0
  19. package/dist/src/cli/host-companion.js +30 -27
  20. package/dist/src/cli/windows-companion-store-entry.d.ts +9 -0
  21. package/dist/src/cli/windows-companion-store-entry.js +67 -0
  22. package/dist/src/computer-access/command-output-capture.d.ts +37 -0
  23. package/dist/src/computer-access/command-output-capture.js +136 -0
  24. package/dist/src/computer-access/command-output-retention.d.ts +5 -0
  25. package/dist/src/computer-access/command-output-retention.js +90 -0
  26. package/dist/src/computer-access/command-output.d.ts +7 -0
  27. package/dist/src/computer-access/command-output.js +1 -0
  28. package/dist/src/computer-access/commands.d.ts +1 -0
  29. package/dist/src/computer-access/commands.js +1 -1
  30. package/dist/src/computer-access/companion/command-output-protocol.d.ts +12 -0
  31. package/dist/src/computer-access/companion/command-output-protocol.js +28 -0
  32. package/dist/src/computer-access/companion/command-output.d.ts +13 -0
  33. package/dist/src/computer-access/companion/command-output.js +110 -0
  34. package/dist/src/computer-access/companion/install-link-macos.d.ts +7 -0
  35. package/dist/src/computer-access/companion/install-link-macos.js +48 -0
  36. package/dist/src/computer-access/companion/install-link-registration.d.ts +4 -0
  37. package/dist/src/computer-access/companion/install-link-registration.js +184 -0
  38. package/dist/src/computer-access/companion/install-link-windows.d.ts +4 -0
  39. package/dist/src/computer-access/companion/install-link-windows.js +65 -0
  40. package/dist/src/computer-access/companion/install-link.d.ts +9 -0
  41. package/dist/src/computer-access/companion/install-link.js +54 -0
  42. package/dist/src/computer-access/companion/native-messages.js +3 -0
  43. package/dist/src/computer-access/companion/native-session.d.ts +1 -0
  44. package/dist/src/computer-access/companion/native-session.js +14 -2
  45. package/dist/src/computer-access/companion/native-supervisor.js +1 -0
  46. package/dist/src/computer-access/companion/notification-windows.d.ts +1 -1
  47. package/dist/src/computer-access/companion/notification-windows.js +8 -3
  48. package/dist/src/computer-access/companion/protocol.d.ts +1 -1
  49. package/dist/src/computer-access/companion/protocol.js +6 -0
  50. package/dist/src/computer-access/companion/release-version.d.ts +1 -1
  51. package/dist/src/computer-access/companion/release-version.js +1 -1
  52. package/dist/src/computer-access/companion/windows-store/connection-error.d.ts +7 -0
  53. package/dist/src/computer-access/companion/windows-store/connection-error.js +19 -0
  54. package/dist/src/computer-access/companion/windows-store/connection-link.d.ts +13 -0
  55. package/dist/src/computer-access/companion/windows-store/connection-link.js +109 -0
  56. package/dist/src/computer-access/companion/windows-store/identity.d.ts +13 -0
  57. package/dist/src/computer-access/companion/windows-store/identity.js +13 -0
  58. package/dist/src/computer-access/companion/windows-store/lifecycle.d.ts +5 -0
  59. package/dist/src/computer-access/companion/windows-store/lifecycle.js +90 -0
  60. package/dist/src/computer-access/contracts.d.ts +1 -1
  61. package/dist/src/computer-access/handlers.d.ts +2 -1
  62. package/dist/src/computer-access/handlers.js +5 -1
  63. package/dist/src/computer-access/process-capture.d.ts +6 -0
  64. package/dist/src/computer-access/process-capture.js +1 -0
  65. package/dist/src/computer-access/process-result.d.ts +2 -0
  66. package/dist/src/computer-access/process-result.js +14 -6
  67. package/dist/src/computer-access/process-runner.d.ts +2 -1
  68. package/dist/src/computer-access/process-runner.js +21 -13
  69. package/dist/src/model-gateway/server/configuration.d.ts +2 -0
  70. package/dist/src/model-gateway/server/configuration.js +73 -21
  71. package/dist/src/plugin-contract/install-source.d.ts +13 -0
  72. package/dist/src/plugin-contract/install-source.js +99 -0
  73. package/dist/src/plugin-contract/manifest.d.ts +2 -1
  74. package/dist/src/plugin-sdk/computer-access.d.ts +8 -0
  75. package/dist/src/plugin-sdk/computer-access.js +5 -0
  76. package/dist/src/runtime/adapters/in-memory-session-store.js +6 -16
  77. package/dist/src/runtime/admin/config-control.js +2 -1
  78. package/dist/src/runtime/automations/catalog.d.ts +3 -0
  79. package/dist/src/runtime/automations/catalog.js +1 -0
  80. package/dist/src/runtime/automations/model-dependency-projection.d.ts +2 -0
  81. package/dist/src/runtime/automations/service.d.ts +2 -0
  82. package/dist/src/runtime/capabilities/automation-builder/authoring-reference.d.ts +10 -5
  83. package/dist/src/runtime/capabilities/automation-builder/authoring-reference.js +8 -7
  84. package/dist/src/runtime/capabilities/automation-builder/payload-instructions.d.ts +2 -2
  85. package/dist/src/runtime/capabilities/automation-builder/payload-instructions.js +2 -2
  86. package/dist/src/runtime/capabilities/automation-builder/tool-contract.d.ts +2 -2
  87. package/dist/src/runtime/capabilities/automations/authoring-catalog.js +1 -1
  88. package/dist/src/runtime/capabilities/automations/authoring-preparation.d.ts +11 -5
  89. package/dist/src/runtime/capabilities/automations/tool-module.js +1 -1
  90. package/dist/src/runtime/capabilities/request-permission-catalog.d.ts +3 -1
  91. package/dist/src/runtime/capabilities/request-permission-catalog.js +21 -2
  92. package/dist/src/runtime/capabilities/request-permission-tool-registry.d.ts +2 -1
  93. package/dist/src/runtime/capabilities/request-permission-tool-registry.js +18 -10
  94. package/dist/src/runtime/capabilities/request-runtime-tool-registry.d.ts +2 -0
  95. package/dist/src/runtime/capabilities/request-runtime-tool-registry.js +1 -1
  96. package/dist/src/runtime/capabilities/scheduling/create-operations.d.ts +6 -0
  97. package/dist/src/runtime/config/builders.js +6 -5
  98. package/dist/src/runtime/config/effective-model-config-validation.js +4 -35
  99. package/dist/src/runtime/config/fields.js +15 -0
  100. package/dist/src/runtime/config/model-context-capacity.d.ts +20 -0
  101. package/dist/src/runtime/config/model-context-capacity.js +55 -0
  102. package/dist/src/runtime/config/model-execution-policy.d.ts +5 -1
  103. package/dist/src/runtime/config/model-execution-policy.js +16 -0
  104. package/dist/src/runtime/config/model-plugin-config.d.ts +8 -0
  105. package/dist/src/runtime/config/model-plugin-config.js +40 -0
  106. package/dist/src/runtime/config/schema.js +5 -1
  107. package/dist/src/runtime/config/validation/model-profile-validation.js +2 -0
  108. package/dist/src/runtime/config/validation/runtime-config-validation-contract.d.ts +3 -1
  109. package/dist/src/runtime/config/validation/runtime-config-validation-contract.js +3 -1
  110. package/dist/src/runtime/config/validation/runtime-root-section-validation.js +11 -4
  111. package/dist/src/runtime/config/validation/web-ui-config-validation.js +3 -2
  112. package/dist/src/runtime/config/web-ui.d.ts +5 -0
  113. package/dist/src/runtime/config/web-ui.js +5 -0
  114. package/dist/src/runtime/config.js +2 -0
  115. package/dist/src/runtime/local-application.d.ts +2 -0
  116. package/dist/src/runtime/local-host/app-owner.js +50 -6
  117. package/dist/src/runtime/local-host/app-request-control.d.ts +3 -1
  118. package/dist/src/runtime/local-host/app-request-control.js +91 -11
  119. package/dist/src/runtime/local-host/client-requests.js +32 -5
  120. package/dist/src/runtime/local-host/client-services.d.ts +2 -0
  121. package/dist/src/runtime/local-host/local-request-diagnostics.d.ts +9 -0
  122. package/dist/src/runtime/local-host/local-request-diagnostics.js +47 -0
  123. package/dist/src/runtime/local-host/saved-approvals.js +23 -2
  124. package/dist/src/runtime/model/model-selection.js +4 -1
  125. package/dist/src/runtime/observability/request-control-diagnostics.d.ts +6 -0
  126. package/dist/src/runtime/observability/request-control-diagnostics.js +52 -0
  127. package/dist/src/runtime/plugins/configuration-context.d.ts +9 -0
  128. package/dist/src/runtime/plugins/configuration-context.js +42 -0
  129. package/dist/src/runtime/plugins/configuration-settings.d.ts +2 -0
  130. package/dist/src/runtime/plugins/configuration-settings.js +26 -0
  131. package/dist/src/runtime/plugins/configuration-store.d.ts +23 -0
  132. package/dist/src/runtime/plugins/configuration-store.js +100 -0
  133. package/dist/src/runtime/plugins/configuration-write.d.ts +5 -0
  134. package/dist/src/runtime/plugins/configuration-write.js +58 -0
  135. package/dist/src/runtime/plugins/installation/archive.d.ts +2 -0
  136. package/dist/src/runtime/plugins/installation/archive.js +107 -0
  137. package/dist/src/runtime/plugins/installation/github-download.d.ts +9 -0
  138. package/dist/src/runtime/plugins/installation/github-download.js +72 -0
  139. package/dist/src/runtime/plugins/installation/installed-package.d.ts +9 -0
  140. package/dist/src/runtime/plugins/installation/installed-package.js +66 -0
  141. package/dist/src/runtime/plugins/installation/package.d.ts +45 -0
  142. package/dist/src/runtime/plugins/installation/package.js +79 -0
  143. package/dist/src/runtime/plugins/installation/portable-path.d.ts +2 -0
  144. package/dist/src/runtime/plugins/installation/portable-path.js +10 -0
  145. package/dist/src/runtime/plugins/installation/publication.d.ts +10 -0
  146. package/dist/src/runtime/plugins/installation/publication.js +17 -0
  147. package/dist/src/runtime/plugins/installation/service.d.ts +41 -0
  148. package/dist/src/runtime/plugins/installation/service.js +80 -0
  149. package/dist/src/runtime/plugins/loader.js +15 -29
  150. package/dist/src/runtime/plugins/manifest-projection.js +1 -0
  151. package/dist/src/runtime/plugins/manifest-validator.js +3 -0
  152. package/dist/src/runtime/plugins/module-cache.d.ts +2 -0
  153. package/dist/src/runtime/plugins/module-cache.js +19 -0
  154. package/dist/src/runtime/plugins/selection.d.ts +2 -0
  155. package/dist/src/runtime/plugins/selection.js +16 -11
  156. package/dist/src/runtime/ports.d.ts +6 -4
  157. package/dist/src/runtime/request/dependencies.d.ts +2 -1
  158. package/dist/src/runtime/request/execution-payload-compacted-evidence.d.ts +1 -1
  159. package/dist/src/runtime/request/handler.js +8 -0
  160. package/dist/src/runtime/request/prepared-execution.js +6 -1
  161. package/dist/src/runtime/request/request-steering.d.ts +4 -0
  162. package/dist/src/runtime/request/request-steering.js +37 -12
  163. package/dist/src/runtime/request/worker-capability-composition.d.ts +2 -0
  164. package/dist/src/runtime/request/worker-capability-composition.js +4 -1
  165. package/dist/src/runtime/scheduler/journal-index-state.d.ts +1 -1
  166. package/dist/src/sessions/assistant-initiative-message.d.ts +3 -0
  167. package/dist/src/sessions/assistant-initiative-message.js +12 -0
  168. package/dist/src/sessions/assistant-initiative.d.ts +2 -2
  169. package/dist/src/sessions/assistant-initiative.js +1 -7
  170. package/dist/src/sessions/durable-session-commit.d.ts +2 -1
  171. package/dist/src/sessions/durable-session-commit.js +6 -1
  172. package/dist/src/sessions/metadata/projection.d.ts +15 -0
  173. package/dist/src/sessions/metadata/projection.js +55 -0
  174. package/dist/src/sessions/metadata/publication.d.ts +12 -0
  175. package/dist/src/sessions/metadata/publication.js +78 -0
  176. package/dist/src/sessions/metadata/source-receipt.d.ts +5 -0
  177. package/dist/src/sessions/metadata/source-receipt.js +42 -0
  178. package/dist/src/sessions/metadata/store.d.ts +7 -0
  179. package/dist/src/sessions/metadata/store.js +119 -0
  180. package/dist/src/sessions/record/projection.d.ts +3 -1
  181. package/dist/src/sessions/record/projection.js +26 -18
  182. package/dist/src/sessions/request-lifecycle/service.js +3 -3
  183. package/dist/src/sessions/session-service.d.ts +2 -4
  184. package/dist/src/sessions/session-service.js +4 -16
  185. package/dist/src/sessions/session-store.d.ts +2 -1
  186. package/dist/src/sessions/session-store.js +9 -3
  187. package/dist/src/sessions/snapshot.d.ts +17 -0
  188. package/dist/src/sessions/snapshot.js +105 -0
  189. package/dist/src/sessions/types.d.ts +2 -0
  190. package/dist/src/shared/tool-selection.d.ts +2 -0
  191. package/dist/src/shared/tool-selection.js +7 -0
  192. package/dist/src/web-ui/app/ARCHITECTURE.md +14 -0
  193. package/dist/src/web-ui/app/app.js +6 -3
  194. package/dist/src/web-ui/app/components/config-workspace/config-model.js +2 -0
  195. package/dist/src/web-ui/app/components/config-workspace/model-plugin-permissions.js +130 -0
  196. package/dist/src/web-ui/app/components/config-workspace/model-plugin-selection.js +180 -0
  197. package/dist/src/web-ui/app/components/config-workspace/model-plugin-view-state.js +114 -0
  198. package/dist/src/web-ui/app/components/config-workspace/model-rendering.js +3 -0
  199. package/dist/src/web-ui/app/components/config-workspace/model-summary.js +39 -0
  200. package/dist/src/web-ui/app/components/config-workspace/summary-rendering.js +30 -0
  201. package/dist/src/web-ui/app/components/config-workspace/workspace-events.js +37 -0
  202. package/dist/src/web-ui/app/components/config-workspace/workspace-mutations.js +2 -0
  203. package/dist/src/web-ui/app/components/config-workspace/workspace-persistence.js +9 -1
  204. package/dist/src/web-ui/app/components/config-workspace/workspace-view.js +23 -67
  205. package/dist/src/web-ui/app/components/config-workspace.js +11 -2
  206. package/dist/src/web-ui/app/components/conversation-view.js +36 -5
  207. package/dist/src/web-ui/app/components/dashboard/recent-conversations.js +2 -1
  208. package/dist/src/web-ui/app/components/dashboard/workspace.js +2 -1
  209. package/dist/src/web-ui/app/components/operations-summary.js +105 -0
  210. package/dist/src/web-ui/app/components/passive-learning/home.js +1 -9
  211. package/dist/src/web-ui/app/components/plugins/configuration-draft.js +34 -0
  212. package/dist/src/web-ui/app/components/plugins/configuration-editor.js +147 -0
  213. package/dist/src/web-ui/app/components/plugins/configuration-fields.js +103 -0
  214. package/dist/src/web-ui/app/components/plugins/configuration-retained-secrets.js +20 -0
  215. package/dist/src/web-ui/app/components/plugins/configuration-settings.js +108 -0
  216. package/dist/src/web-ui/app/components/plugins/discovery-rendering.js +37 -0
  217. package/dist/src/web-ui/app/components/plugins/installation.js +212 -0
  218. package/dist/src/web-ui/app/components/plugins/manager.js +160 -6
  219. package/dist/src/web-ui/app/components/plugins/removal.js +130 -0
  220. package/dist/src/web-ui/app/components/plugins/rendering.js +14 -2
  221. package/dist/src/web-ui/app/components/plugins/summary.js +54 -0
  222. package/dist/src/web-ui/app/components/plugins/tabs.js +31 -0
  223. package/dist/src/web-ui/app/components/plugins/view-state.js +3 -1
  224. package/dist/src/web-ui/app/components/runtime-setup/connection-form.js +5 -2
  225. package/dist/src/web-ui/app/components/runtime-setup/connection-state.js +9 -1
  226. package/dist/src/web-ui/app/components/runtime-setup/context-window-feedback.js +71 -0
  227. package/dist/src/web-ui/app/components/runtime-setup/optional-steps.js +2 -0
  228. package/dist/src/web-ui/app/components/runtime-setup-guide.js +15 -3
  229. package/dist/src/web-ui/app/components/spark-chat/empty-state.js +1 -0
  230. package/dist/src/web-ui/app/components/spark-chat/panel.js +1 -0
  231. package/dist/src/web-ui/app/components/system-host/connection-card.js +35 -0
  232. package/dist/src/web-ui/app/components/system-host/macos-setup.js +1 -1
  233. package/dist/src/web-ui/app/components/system-host/manager.js +45 -12
  234. package/dist/src/web-ui/app/components/system-host/rendering.js +111 -106
  235. package/dist/src/web-ui/app/components/system-host/windows-store-setup.js +293 -0
  236. package/dist/src/web-ui/app/components/workspace-shell.js +6 -0
  237. package/dist/src/web-ui/app/computer-access-feature.js +22 -5
  238. package/dist/src/web-ui/app/configuration-feature.js +35 -2
  239. package/dist/src/web-ui/app/controllers/app-event-bindings.js +4 -1
  240. package/dist/src/web-ui/app/controllers/conversation-read-state-controller.d.ts +1 -0
  241. package/dist/src/web-ui/app/controllers/conversation-read-state-controller.js +7 -3
  242. package/dist/src/web-ui/app/controllers/conversation-request-hydration.js +88 -0
  243. package/dist/src/web-ui/app/controllers/conversation-session-controller.d.ts +9 -3
  244. package/dist/src/web-ui/app/controllers/conversation-session-controller.js +52 -144
  245. package/dist/src/web-ui/app/controllers/history-pagination.js +111 -0
  246. package/dist/src/web-ui/app/controllers/operations-controller.d.ts +12 -0
  247. package/dist/src/web-ui/app/controllers/operations-controller.js +28 -21
  248. package/dist/src/web-ui/app/controllers/runtime-onboarding-controller.d.ts +1 -0
  249. package/dist/src/web-ui/app/controllers/runtime-onboarding-controller.js +11 -0
  250. package/dist/src/web-ui/app/controllers/session-controller.js +10 -2
  251. package/dist/src/web-ui/app/controllers/session-list-pagination.js +71 -0
  252. package/dist/src/web-ui/app/controllers/session-sidebar-controller.js +6 -4
  253. package/dist/src/web-ui/app/controllers/spark-chat-reading.js +1 -0
  254. package/dist/src/web-ui/app/controllers/spark-chat-sync.js +24 -5
  255. package/dist/src/web-ui/app/controllers/workspace-route-controller.js +21 -19
  256. package/dist/src/web-ui/app/dashboard-feature.js +2 -2
  257. package/dist/src/web-ui/app/index.html +6 -2
  258. package/dist/src/web-ui/app/lib/history-page-messages.js +49 -0
  259. package/dist/src/web-ui/app/lib/request-lifecycle-view.js +8 -0
  260. package/dist/src/web-ui/app/lib/workspace-route.d.ts +3 -0
  261. package/dist/src/web-ui/app/lib/workspace-route.js +4 -0
  262. package/dist/src/web-ui/app/passive-learning-feature.js +0 -1
  263. package/dist/src/web-ui/app/runtime-onboarding-feature.js +3 -0
  264. package/dist/src/web-ui/app/schedules-feature.js +1 -1
  265. package/dist/src/web-ui/app/services/runtime-web-client/configuration.js +20 -0
  266. package/dist/src/web-ui/app/services/runtime-web-client/plugin-configuration.js +19 -0
  267. package/dist/src/web-ui/app/services/runtime-web-client/spark-chat.js +4 -4
  268. package/dist/src/web-ui/app/services/runtime-web-client/system-host.js +2 -0
  269. package/dist/src/web-ui/app/services/runtime-web-client.d.ts +6 -1
  270. package/dist/src/web-ui/app/services/runtime-web-client.js +12 -3
  271. package/dist/src/web-ui/app/spark-chat-feature.js +76 -19
  272. package/dist/src/web-ui/app/styles/11-product-links.css +30 -0
  273. package/dist/src/web-ui/app/styles/41-plugin-configuration.css +79 -0
  274. package/dist/src/web-ui/app/styles/41-plugin-discovery.css +132 -0
  275. package/dist/src/web-ui/app/styles/41-plugin-management.css +6 -0
  276. package/dist/src/web-ui/app/styles/42-model-plugin-permissions.css +173 -0
  277. package/dist/src/web-ui/app/styles/46-system-host.css +61 -107
  278. package/dist/src/web-ui/app/styles/47-config-controls.css +13 -1
  279. package/dist/src/web-ui/app/styles/54-spark-chat.css +33 -1
  280. package/dist/src/web-ui/app/styles.css +4 -0
  281. package/dist/src/web-ui/local-runtime/api-router.js +11 -6
  282. package/dist/src/web-ui/local-runtime/approval-request-attachment.js +14 -5
  283. package/dist/src/web-ui/local-runtime/configuration-activation-source.d.ts +1 -1
  284. package/dist/src/web-ui/local-runtime/configuration-activation-source.js +9 -3
  285. package/dist/src/web-ui/local-runtime/contracts.d.ts +4 -3
  286. package/dist/src/web-ui/local-runtime/environment-registry.d.ts +5 -1
  287. package/dist/src/web-ui/local-runtime/environment-registry.js +53 -11
  288. package/dist/src/web-ui/local-runtime/history-pagination.d.ts +16 -0
  289. package/dist/src/web-ui/local-runtime/history-pagination.js +20 -0
  290. package/dist/src/web-ui/local-runtime/plugin-configuration-routes.d.ts +16 -0
  291. package/dist/src/web-ui/local-runtime/plugin-configuration-routes.js +68 -0
  292. package/dist/src/web-ui/local-runtime/plugin-installation-routes.d.ts +16 -0
  293. package/dist/src/web-ui/local-runtime/plugin-installation-routes.js +96 -0
  294. package/dist/src/web-ui/local-runtime/plugin-management-routes.d.ts +8 -1
  295. package/dist/src/web-ui/local-runtime/plugin-management-routes.js +17 -0
  296. package/dist/src/web-ui/local-runtime/plugin-mutation-admission.d.ts +7 -0
  297. package/dist/src/web-ui/local-runtime/plugin-mutation-admission.js +17 -0
  298. package/dist/src/web-ui/local-runtime/plugin-removal-routes.d.ts +18 -0
  299. package/dist/src/web-ui/local-runtime/plugin-removal-routes.js +60 -0
  300. package/dist/src/web-ui/local-runtime/realtime-controller.js +41 -2
  301. package/dist/src/web-ui/local-runtime/request-cancellation-route.js +28 -1
  302. package/dist/src/web-ui/local-runtime/request-control-observation.d.ts +19 -0
  303. package/dist/src/web-ui/local-runtime/request-control-observation.js +80 -0
  304. package/dist/src/web-ui/local-runtime/request-execution.js +69 -37
  305. package/dist/src/web-ui/local-runtime/runtime-setup-context-window.d.ts +20 -0
  306. package/dist/src/web-ui/local-runtime/runtime-setup-context-window.js +44 -0
  307. package/dist/src/web-ui/local-runtime/runtime-setup-routes.js +2 -0
  308. package/dist/src/web-ui/local-runtime/runtime-setup-validation.js +9 -2
  309. package/dist/src/web-ui/local-runtime/session-routes.d.ts +1 -0
  310. package/dist/src/web-ui/local-runtime/session-routes.js +16 -4
  311. package/dist/src/web-ui/local-runtime/spark-chat-routes.js +4 -3
  312. package/dist/src/web-ui/plugin-capability-selection.js +4 -1
  313. package/dist/src/web-ui/plugin-configuration-input.d.ts +12 -0
  314. package/dist/src/web-ui/plugin-configuration-input.js +68 -0
  315. package/dist/src/web-ui/plugin-configuration-retained-secrets.d.ts +9 -0
  316. package/dist/src/web-ui/plugin-configuration-retained-secrets.js +48 -0
  317. package/dist/src/web-ui/plugin-configuration-service.d.ts +52 -0
  318. package/dist/src/web-ui/plugin-configuration-service.js +66 -0
  319. package/dist/src/web-ui/plugin-configuration-view.d.ts +25 -0
  320. package/dist/src/web-ui/plugin-configuration-view.js +45 -0
  321. package/dist/src/web-ui/plugin-disabled-selection.d.ts +7 -0
  322. package/dist/src/web-ui/plugin-disabled-selection.js +17 -0
  323. package/dist/src/web-ui/plugin-management-catalog.d.ts +3 -1
  324. package/dist/src/web-ui/plugin-management-catalog.js +11 -1
  325. package/dist/src/web-ui/plugin-management-selection.js +22 -9
  326. package/dist/src/web-ui/plugin-management-service.d.ts +5 -1
  327. package/dist/src/web-ui/plugin-management-service.js +11 -7
  328. package/dist/src/web-ui/plugin-removal-error.d.ts +7 -0
  329. package/dist/src/web-ui/plugin-removal-error.js +16 -0
  330. package/dist/src/web-ui/plugin-removal-service.d.ts +30 -0
  331. package/dist/src/web-ui/plugin-removal-service.js +109 -0
  332. package/dist/src/web-ui/runtime-setup-gateway.d.ts +6 -2
  333. package/dist/src/web-ui/runtime-setup-gateway.js +30 -10
  334. package/dist/src/web-ui/server.js +6 -2
  335. package/dist/src/web-ui/session-read-state/overview.d.ts +42 -0
  336. package/dist/src/web-ui/session-read-state/overview.js +16 -0
  337. package/dist/src/web-ui/session-read-state/pagination.d.ts +15 -0
  338. package/dist/src/web-ui/session-read-state/pagination.js +42 -0
  339. package/dist/src/web-ui/session-read-state/projection.d.ts +4 -3
  340. package/dist/src/web-ui/session-read-state/service.d.ts +213 -20
  341. package/dist/src/web-ui/session-read-state/service.js +25 -23
  342. package/dist/src/web-ui/session-read-state/visible-messages.d.ts +3 -3
  343. package/dist/src/web-ui/system-host-setup/companion-linux.js +1 -1
  344. package/dist/src/web-ui/system-host-setup/installer-contract.d.ts +1 -1
  345. package/dist/src/web-ui/system-host-setup/installer-contract.js +2 -2
  346. package/dist/src/web-ui/system-host-setup/installers.js +0 -7
  347. package/dist/src/web-ui/system-host-setup/local-connection.d.ts +4 -1
  348. package/dist/src/web-ui/system-host-setup/local-connection.js +10 -17
  349. package/dist/src/web-ui/system-host-setup/routes.d.ts +2 -0
  350. package/dist/src/web-ui/system-host-setup/routes.js +32 -9
  351. package/dist/src/web-ui/system-host-setup/setup-mutation.d.ts +9 -0
  352. package/dist/src/web-ui/system-host-setup/setup-mutation.js +27 -0
  353. package/dist/src/web-ui/system-host-setup/windows-store-route.d.ts +13 -0
  354. package/dist/src/web-ui/system-host-setup/windows-store-route.js +68 -0
  355. package/docs/configuration.md +46 -0
  356. package/docs/installation.md +45 -24
  357. package/docs/plugins.md +165 -2
  358. package/docs/projects-and-full-plus.md +2 -6
  359. package/docs/runtime-library.md +0 -1
  360. package/docs/windows-companion-store.md +127 -0
  361. package/package.json +3 -3
  362. package/plugins/document-reader/plugin.json +1 -0
  363. package/plugins/exec/plugin.json +3 -0
  364. package/plugins/filesystem/plugin.json +2 -0
  365. package/plugins/local-search/plugin.json +1 -0
  366. package/plugins/system/README.md +43 -3
  367. package/plugins/system/plugin.json +5 -0
  368. package/plugins/system/source/companion-command-output.ts +82 -0
  369. package/plugins/system/source/host-dispatch.ts +4 -0
  370. package/plugins/system/source/index.ts +11 -5
  371. package/plugins/system/source/local-command-output.ts +61 -0
  372. package/plugins/system/source/request-system-modules.ts +2 -0
  373. package/plugins/system/src/index.cjs +474 -39
  374. package/plugins/web/README.md +2 -2
  375. package/plugins/web/plugin.json +4 -0
  376. package/runtime.config.schema.json +25 -1
  377. package/src/runtime/README.md +12 -2
  378. package/dist/src/web-ui/system-host-setup/companion-windows.d.ts +0 -2
  379. package/dist/src/web-ui/system-host-setup/companion-windows.js +0 -108
  380. package/docs/publishing.md +0 -167
@@ -47,10 +47,11 @@ export interface ConversationSessionClient {
47
47
  readThroughMessageId: number | string | null;
48
48
  readThroughRequestId?: string;
49
49
  }): Promise<Record<string, unknown>>;
50
- listSessions(environmentId: string): Promise<Record<string, unknown>>;
50
+ listSessions(environmentId: string, page?: Record<string, unknown>): Promise<Record<string, unknown>>;
51
51
  loadSession(
52
52
  sessionId: string,
53
53
  environmentId: string,
54
+ page?: Record<string, unknown>,
54
55
  ): Promise<Record<string, unknown>>;
55
56
  fetchRequestEvents(options: {
56
57
  requestId: string;
@@ -125,6 +126,7 @@ export declare function normalizeConversationMessage(
125
126
  export declare function createConversationSessionController(
126
127
  options: ConversationSessionControllerOptions,
127
128
  ): {
129
+ history: { load(direction?: "older" | "newer"): Promise<void>; latest(): Promise<void>; hasNewer(): boolean };
128
130
  activateRequestForScope(
129
131
  scope: { environmentId: string; sessionId: string },
130
132
  requestId: string,
@@ -150,12 +152,16 @@ export declare function createConversationSessionController(
150
152
  invalidateEnvironmentLoads(): void;
151
153
  loadSessions(): Promise<ConversationSession[]>;
152
154
  markCurrentSessionReadSoon(): void;
155
+ rehydrateRequests(requests: Record<string, unknown>[]): Promise<boolean>;
153
156
  normalizeMessage(raw: unknown): ConversationMessage;
154
157
  openSession(
155
158
  sessionId: string,
156
159
  options?: {
157
- isRouteCurrent(): boolean;
158
- commitRouteNavigation(): boolean;
160
+ isRouteCurrent?(): boolean;
161
+ commitRouteNavigation?(): boolean;
162
+ snapshot?: Record<string, unknown>;
163
+ page?: Record<string, unknown>;
164
+ preserveCurrentWindow?: boolean;
159
165
  },
160
166
  ): Promise<void>;
161
167
  requestBelongsToCurrentView(requestId: string): boolean;
@@ -4,25 +4,24 @@ import {
4
4
  isContextReplayCurrent,
5
5
  } from "../lib/context-window-replay.js";
6
6
  import { synchronizeContextRequestLifecycle } from "../lib/context-window-state.js";
7
- import { mergeSessionListReadState } from "../lib/session-read-state.js";
7
+ import { createSessionListPagination } from "./session-list-pagination.js";
8
+ import { createHistoryPagination, canRestoreSnapshotQueue } from "./history-pagination.js";
8
9
  import { createConversationReadStateController } from "./conversation-read-state-controller.js";
10
+ import { createConversationRequestHydration } from "./conversation-request-hydration.js";
9
11
  import { normalizeScheduleReference } from "../lib/schedule-message.js";
10
- import { createWebSessionId, escapeHtml, textOf } from "../lib/text-format.js";
12
+ import { createWebSessionId, textOf } from "../lib/text-format.js";
11
13
  import { insertRequestUserMessageBeforeAssistant } from "../ui-behavior.js";
12
14
  import { normalizeRealtimeMessage } from "../lib/realtime-message.js";
13
15
  import {
14
16
  sparkProposalId,
15
17
  sparkHandoffAvailable,
16
18
  } from "../lib/spark-proposal-message.js";
17
- import {
18
- isStoppedRequest,
19
- restoreStoppedResponses,
20
- } from "../lib/request-stop-state.js";
21
19
  import {
22
20
  isApprovalPresentation,
23
21
  requestLifecycles,
24
22
  rememberRequestLifecycle,
25
23
  sameConversationMessage,
24
+ mergeConversationMessage,
26
25
  } from "../lib/request-lifecycle-view.js";
27
26
 
28
27
  export function normalizeConversationMessage(raw, fallbackIndex = 0) {
@@ -82,12 +81,19 @@ export function createConversationSessionController({
82
81
  onSessionCreated = () => {},
83
82
  }) {
84
83
  let environmentLoadRevision = 0;
85
- let sessionListRevision = 0;
84
+ const listPagination = createSessionListPagination({ state, dom, client, sessions, selectedEnvironmentId, renderSessions, onSessionListState });
85
+ const history = createHistoryPagination({ state, dom, client, selectedEnvironmentId,
86
+ normalizeMessage: normalizeConversationMessage, renderMessages, conversationView, applyReadState: sessions.applyReadState,
87
+ onRequests: (requests) => restoreSessionEventState(requests, new Set(), true),
88
+ loadLatest: () => openSession(state.currentSessionId, { preserveCurrentWindow: true }),
89
+ onNewerPage: (messages) => markDisplayedMessagesReadSoon(messages),
90
+ });
86
91
  let sessionRestoreRevision = 0;
87
92
 
88
93
  function invalidateEnvironmentLoads() {
89
94
  environmentLoadRevision += 1;
90
- sessionListRevision += 1;
95
+ listPagination.invalidate();
96
+ history.reset();
91
97
  sessionRestoreRevision += 1;
92
98
  state.sessions = [];
93
99
  onSessionListState({ status: "loading", sessions: [] });
@@ -98,7 +104,7 @@ export function createConversationSessionController({
98
104
  return normalizeConversationMessage(raw, state.messages.length);
99
105
  }
100
106
 
101
- const { markCurrentSessionReadSoon } = createConversationReadStateController({
107
+ const { markCurrentSessionReadSoon, markDisplayedMessagesReadSoon } = createConversationReadStateController({
102
108
  state,
103
109
  client,
104
110
  selectedEnvironmentId,
@@ -107,6 +113,11 @@ export function createConversationSessionController({
107
113
  isConversationVisible,
108
114
  scheduleTask,
109
115
  });
116
+ const requestHydration = createConversationRequestHydration({
117
+ state, client, selectedEnvironmentId, sessionQueue, applyConversationChrome, renderSessions,
118
+ updateComposerSendState, reportQueueFailure, activeAssistantForRequest, restoreSessionEventState,
119
+ subscribeSession, subscribeRequest, replayRequestEvents, resumeRequest, recoverBlockedQueue, drainQueuedMessage, renderMessages,
120
+ });
110
121
 
111
122
  function addOrMergeMessage(message) {
112
123
  const id = textOf(message.id);
@@ -115,13 +126,7 @@ export function createConversationSessionController({
115
126
  sameConversationMessage(item, message),
116
127
  );
117
128
  if (existingIndex >= 0) {
118
- state.messages[existingIndex] = {
119
- ...state.messages[existingIndex],
120
- ...message,
121
- text: message.text || state.messages[existingIndex].text,
122
- thinkingText:
123
- message.thinkingText || state.messages[existingIndex].thinkingText,
124
- };
129
+ state.messages[existingIndex] = mergeConversationMessage(state.messages[existingIndex], message);
125
130
  } else {
126
131
  state.messages.push(message);
127
132
  }
@@ -156,6 +161,7 @@ export function createConversationSessionController({
156
161
  }
157
162
 
158
163
  function resetLiveRequestView() {
164
+ history.reset();
159
165
  state.events = [];
160
166
  state.taskProgressByRequest.clear();
161
167
  state.contextWindowByRequest.clear();
@@ -253,12 +259,16 @@ export function createConversationSessionController({
253
259
  function restoreSessionEventState(
254
260
  requests = [],
255
261
  hydrateRequestIds = new Set(),
262
+ append = false,
256
263
  ) {
257
- state.events = [];
258
- state.taskProgressByRequest.clear();
259
- state.contextWindowByRequest.clear();
264
+ if (!append) {
265
+ state.events = [];
266
+ state.taskProgressByRequest.clear();
267
+ state.contextWindowByRequest.clear();
268
+ }
260
269
  const requestEvents = [];
261
270
  for (const request of requests) {
271
+ rememberRequestLifecycle(state, request.lifecycle);
262
272
  if (!Array.isArray(request.events)) continue;
263
273
  const replayScope = captureContextReplayScope(
264
274
  state,
@@ -372,46 +382,7 @@ export function createConversationSessionController({
372
382
  handleRealtimeMessage(event);
373
383
  }
374
384
 
375
- async function loadSessions() {
376
- const environmentRevision = environmentLoadRevision;
377
- const requestRevision = ++sessionListRevision;
378
- const environmentId = selectedEnvironmentId();
379
- onSessionListState({ status: "loading", sessions: state.sessions });
380
- try {
381
- const payload = await client.listSessions(environmentId);
382
- if (
383
- environmentRevision !== environmentLoadRevision ||
384
- requestRevision !== sessionListRevision ||
385
- environmentId !== selectedEnvironmentId()
386
- ) {
387
- return [];
388
- }
389
- state.sessions = mergeSessionListReadState(
390
- Array.isArray(payload.sessions) ? payload.sessions : [],
391
- state.sessions,
392
- );
393
- onSessionListState({ status: "ready", sessions: state.sessions });
394
- renderSessions();
395
- return state.sessions;
396
- } catch (error) {
397
- if (
398
- environmentRevision !== environmentLoadRevision ||
399
- requestRevision !== sessionListRevision ||
400
- environmentId !== selectedEnvironmentId()
401
- ) {
402
- return [];
403
- }
404
- onSessionListState({
405
- status: "error",
406
- sessions: state.sessions,
407
- error: error instanceof Error ? error.message : String(error),
408
- });
409
- dom.sessionsList.innerHTML = `<div class="empty-state error-text">${escapeHtml(
410
- error instanceof Error ? error.message : String(error),
411
- )}</div>`;
412
- return [];
413
- }
414
- }
385
+ const loadSessions = () => listPagination.load();
415
386
 
416
387
  async function restoreLastSession() {
417
388
  const environmentRevision = environmentLoadRevision;
@@ -422,33 +393,38 @@ export function createConversationSessionController({
422
393
  environmentRevision !== environmentLoadRevision ||
423
394
  restoreRevision !== sessionRestoreRevision ||
424
395
  environmentId !== selectedEnvironmentId() ||
425
- availableSessions.length === 0 ||
426
396
  state.currentSessionId
427
397
  ) {
428
398
  return;
429
399
  }
430
400
  const savedSessionId = preferences.sessionIdForEnvironment(environmentId);
431
- const targetSession =
432
- availableSessions.find((session) => session.id === savedSessionId) ||
433
- availableSessions[0];
434
- if (targetSession?.id) await openSession(targetSession.id);
401
+ const targetId = savedSessionId || availableSessions[0]?.id;
402
+ if (!targetId) return;
403
+ try { await openSession(targetId); }
404
+ catch (error) {
405
+ if (error.code !== "session_not_found") throw error;
406
+ if (availableSessions[0]?.id && availableSessions[0].id !== targetId) await openSession(availableSessions[0].id);
407
+ }
435
408
  }
436
409
 
437
410
  async function openSession(
438
411
  sessionId,
439
- { isRouteCurrent, commitRouteNavigation } = {},
412
+ { isRouteCurrent, commitRouteNavigation, snapshot, page, preserveCurrentWindow = false } = {},
440
413
  ) {
441
414
  const routeOwned = typeof commitRouteNavigation === "function";
415
+ const loadBeforeReset = routeOwned || preserveCurrentWindow;
442
416
  let payload;
443
- if (routeOwned) {
417
+ const pageOptions = { ...page, waitingRequestId: sessionQueue.peek({ environmentId: selectedEnvironmentId(), sessionId })?.waitForRequestId };
418
+ const prefetched = canRestoreSnapshotQueue(snapshot, pageOptions.waitingRequestId) ? snapshot : undefined;
419
+ if (loadBeforeReset) {
444
420
  const environmentId = selectedEnvironmentId();
445
421
  const viewVersion = state.sessionViewVersion;
446
422
  const currentSessionId = state.currentSessionId;
447
- payload = await client.loadSession(sessionId, environmentId);
423
+ payload = prefetched ?? await client.loadSession(sessionId, environmentId, pageOptions);
448
424
  if (environmentId !== selectedEnvironmentId()) return;
449
425
  if (viewVersion !== state.sessionViewVersion) return;
450
426
  if (currentSessionId !== state.currentSessionId) return;
451
- if (isRouteCurrent?.() !== true) return;
427
+ if (routeOwned && isRouteCurrent?.() !== true) return;
452
428
  }
453
429
  if (
454
430
  state.currentSessionId &&
@@ -474,8 +450,8 @@ export function createConversationSessionController({
474
450
  renderMessages();
475
451
  subscribeSession(sessionId);
476
452
 
477
- if (!routeOwned) {
478
- payload = await client.loadSession(sessionId, selectedEnvironmentId());
453
+ if (!loadBeforeReset) {
454
+ payload = prefetched ?? await client.loadSession(sessionId, selectedEnvironmentId(), pageOptions);
479
455
  }
480
456
  if (
481
457
  state.currentSessionId !== sessionId ||
@@ -483,87 +459,17 @@ export function createConversationSessionController({
483
459
  ) {
484
460
  return;
485
461
  }
462
+ history.adopt(payload);
486
463
  const rawMessages = Array.isArray(payload.messages) ? payload.messages : [];
487
- const scope = { environmentId: selectedEnvironmentId(), sessionId };
488
464
  state.messages = rawMessages.map((message, index) =>
489
465
  normalizeConversationMessage(message, index),
490
466
  );
467
+ const displayedPage = [...state.messages];
491
468
  sessions.applyTitle(sessionId, payload.title);
492
469
  sessions.applyReadState(sessionId, payload.readState);
493
- for (const message of state.messages) {
494
- if (
495
- message.role === "assistant" &&
496
- message.requestId &&
497
- !isApprovalPresentation(message)
498
- ) {
499
- state.requestMessages.set(message.requestId, message.id);
500
- }
501
- }
502
470
  const requests = Array.isArray(payload.requests) ? payload.requests : [];
503
- for (const request of requests)
504
- rememberRequestLifecycle(state, request.lifecycle);
505
- applyConversationChrome();
506
- renderSessions();
507
- const streamingRequestIds = new Set(
508
- requests
509
- .filter(
510
- (request) => request.status === "streaming" && request.requestId,
511
- )
512
- .map((request) => textOf(request.requestId)),
513
- );
514
- if (streamingRequestIds.size === 1) {
515
- const [streamingRequestId] = streamingRequestIds;
516
- try {
517
- sessionQueue.rebindBlocked(scope, streamingRequestId);
518
- } catch (error) {
519
- reportQueueFailure(
520
- scope,
521
- `The active request was restored, but the remaining Send next queue is paused: ${
522
- error instanceof Error ? error.message : String(error)
523
- }`,
524
- );
525
- }
526
- }
527
- state.activeRequestId = [...streamingRequestIds][0] || "";
528
- updateComposerSendState();
529
- for (const requestId of streamingRequestIds) {
530
- activeAssistantForRequest(requestId);
531
- }
532
- restoreStoppedResponses(state.messages, requests);
533
- restoreSessionEventState(requests, streamingRequestIds);
534
- for (const request of requests) {
535
- if (request.status === "streaming" && request.requestId) {
536
- subscribeRequest(request.requestId);
537
- await replayRequestEvents(request.requestId, viewVersion);
538
- if (
539
- state.currentSessionId !== sessionId ||
540
- state.sessionViewVersion !== viewVersion
541
- ) {
542
- return;
543
- }
544
- resumeRequest(request.requestId);
545
- }
546
- }
547
- if (!state.activeRequestId) {
548
- recoverBlockedQueue(scope);
549
- const queued = sessionQueue.peek(scope);
550
- const terminal = queued
551
- ? requests.find(
552
- (request) =>
553
- textOf(request.requestId) === queued.waitForRequestId &&
554
- (request.status === "completed" || request.status === "failed"),
555
- )
556
- : null;
557
- if (terminal) {
558
- await drainQueuedMessage({
559
- ...scope,
560
- terminalRequestId: terminal.requestId,
561
- cancelled: isStoppedRequest(terminal),
562
- });
563
- }
564
- }
565
- renderMessages();
566
- markCurrentSessionReadSoon();
471
+ if (!await requestHydration.restore(requests)) return;
472
+ markDisplayedMessagesReadSoon(displayedPage);
567
473
  }
568
474
 
569
475
  function ensureSession() {
@@ -628,6 +534,7 @@ export function createConversationSessionController({
628
534
  }
629
535
 
630
536
  return {
537
+ history,
631
538
  activateRequestForScope,
632
539
  activeAssistantForRequest,
633
540
  addOrMergeMessage,
@@ -642,6 +549,7 @@ export function createConversationSessionController({
642
549
  invalidateEnvironmentLoads,
643
550
  loadSessions,
644
551
  markCurrentSessionReadSoon,
552
+ rehydrateRequests: requestHydration.rehydrate,
645
553
  normalizeMessage,
646
554
  openSession,
647
555
  requestBelongsToCurrentView,
@@ -0,0 +1,111 @@
1
+ import { mergeHistoryPage } from "../lib/history-page-messages.js";
2
+
3
+ export function canRestoreSnapshotQueue(snapshot, waitingRequestId) {
4
+ if (!snapshot) return false;
5
+ if (!waitingRequestId) return true;
6
+ return (snapshot.requests || []).some((request) => request.requestId === waitingRequestId);
7
+ }
8
+
9
+ export function createHistoryPagination({ state, dom, client, selectedEnvironmentId, normalizeMessage, onRequests, renderMessages, conversationView, applyReadState, loadLatest, onNewerPage = () => {} }) {
10
+ let revision = 0;
11
+ let pending = false;
12
+ let older = null;
13
+ let newer = null;
14
+ let error = "";
15
+ let scrollRequested = false;
16
+ let windowEnd;
17
+ const requestIds = new Set();
18
+ function reset() {
19
+ revision += 1; pending = false; older = null; newer = null; error = ""; requestIds.clear();
20
+ scrollRequested = false;
21
+ windowEnd = undefined;
22
+ }
23
+ function adopt(snapshot) {
24
+ scrollRequested = false;
25
+ older = snapshot.nextCursor || null; newer = snapshot.newerCursor || null;
26
+ windowEnd = snapshot.messages?.length ? normalizeMessage(snapshot.messages.at(-1)) : undefined;
27
+ for (const request of snapshot.requests || []) requestIds.add(request.requestId);
28
+ }
29
+ function refresh(snapshot) {
30
+ if (newer) return;
31
+ const first = snapshot.messages?.[0];
32
+ if (!first) { adopt(snapshot); return; }
33
+ const overlaps = state.messages.some((message) => String(message.persistedMessageId ?? message.id) === String(first.id) && message.createdAt === first.createdAt);
34
+ if (!overlaps) { reset(); adopt(snapshot); }
35
+ }
36
+ function isCurrent(environmentId, sessionId, version, viewVersion) {
37
+ if (version !== revision || viewVersion !== state.sessionViewVersion) return false;
38
+ if (environmentId !== selectedEnvironmentId()) return false;
39
+ return sessionId === state.currentSessionId;
40
+ }
41
+ async function load(direction = "older") {
42
+ const cursor = direction === "older" ? older : newer;
43
+ if (pending || !cursor) return;
44
+ scrollRequested = false;
45
+ const environmentId = selectedEnvironmentId();
46
+ const sessionId = state.currentSessionId;
47
+ const version = revision;
48
+ const viewVersion = state.sessionViewVersion;
49
+ pending = true; error = "";
50
+ if (conversationView.preserveScroll) conversationView.preserveScroll(renderMessages);
51
+ else renderMessages();
52
+ try {
53
+ const snapshot = await client.loadSession(sessionId, environmentId, { cursor });
54
+ if (!isCurrent(environmentId, sessionId, version, viewVersion)) return;
55
+ const incoming = (snapshot.messages || []).map(normalizeMessage);
56
+ const requests = (snapshot.requests || []).filter((request) => !requestIds.has(request.requestId));
57
+ for (const request of requests) requestIds.add(request.requestId);
58
+ const merge = () => {
59
+ mergeHistoryPage(state, incoming, direction, windowEnd);
60
+ if (direction === "newer" && incoming.length) windowEnd = incoming.at(-1);
61
+ if (direction === "older") older = snapshot.nextCursor || null;
62
+ else newer = snapshot.newerCursor || null;
63
+ onRequests(requests);
64
+ applyReadState(sessionId, snapshot.readState);
65
+ pending = false;
66
+ renderMessages();
67
+ };
68
+ if (conversationView.preserveScroll) conversationView.preserveScroll(merge);
69
+ else merge();
70
+ if (direction === "newer" && incoming.length) onNewerPage(incoming);
71
+ } catch (failure) {
72
+ if (!isCurrent(environmentId, sessionId, version, viewVersion)) return;
73
+ error = failure.code === "history_cursor_stale" ? "History changed. Reopen this conversation." : "Could not load history. Retry.";
74
+ if (failure.code === "history_cursor_stale") { older = null; newer = null; }
75
+ } finally {
76
+ if (isCurrent(environmentId, sessionId, version, viewVersion)) { pending = false; renderMessages(); }
77
+ }
78
+ }
79
+ function renderControls(root) {
80
+ if (!root?.ownerDocument) return;
81
+ for (const [direction, cursor, label] of [["older", older, "Load older messages"], ["newer", newer, "Load newer messages"]]) {
82
+ if (!cursor) continue;
83
+ const button = root.ownerDocument.createElement("button");
84
+ button.type = "button"; button.className = "history-page-button";
85
+ button.disabled = pending;
86
+ button.textContent = pending ? "Loading…" : error || label;
87
+ button.addEventListener("click", () => void load(direction));
88
+ if (direction === "older") root.prepend(button);
89
+ else root.append(button);
90
+ }
91
+ if (error && !older && !newer) {
92
+ const note = root.ownerDocument.createElement("p"); note.textContent = error; root.prepend(note);
93
+ }
94
+ }
95
+ // Rendering and target positioning also emit scroll events; only user input can request another page.
96
+ for (const name of ["wheel", "touchmove", "pointerdown"]) {
97
+ dom.messagesList?.addEventListener?.(name, () => { scrollRequested = true; }, { passive: true });
98
+ }
99
+ dom.messagesList?.addEventListener?.("keydown", (event) => {
100
+ if (["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "].includes(event.key)) scrollRequested = true;
101
+ });
102
+ dom.messagesList?.addEventListener?.("scroll", () => {
103
+ if (error || pending || !scrollRequested) return;
104
+ const root = dom.messagesList;
105
+ if (root.scrollTop < 60 && older) { scrollRequested = false; void load("older"); return; }
106
+ if (root.scrollHeight - root.scrollTop - root.clientHeight < 60 && newer) { scrollRequested = false; void load("newer"); }
107
+ }, { passive: true });
108
+ const controller = { reset, adopt, refresh, load, renderControls, latest: loadLatest, hasNewer: () => Boolean(newer) };
109
+ conversationView.setHistoryPager?.(controller);
110
+ return controller;
111
+ }
@@ -3,8 +3,19 @@ export declare function latestHealthEvent(
3
3
  details: readonly Record<string, unknown>[],
4
4
  ): Record<string, unknown> | undefined;
5
5
 
6
+ export interface OperationsSummary {
7
+ readonly label: string;
8
+ readonly items: readonly {
9
+ readonly label: string;
10
+ readonly value: string;
11
+ readonly dirty?: boolean;
12
+ readonly status?: boolean;
13
+ }[];
14
+ }
15
+
6
16
  export interface OperationsControllerDependencies {
7
17
  readonly getEnvironmentId?: () => string | undefined;
18
+ readonly onSummaryChange?: () => void;
8
19
  readonly dom: Readonly<{
9
20
  runtimeStatus: Pick<HTMLElement, "innerHTML" | "textContent">;
10
21
  runtimeLogs: Pick<HTMLElement, "textContent">;
@@ -20,6 +31,7 @@ export interface OperationsControllerDependencies {
20
31
  export declare function createOperationsController(
21
32
  options: OperationsControllerDependencies,
22
33
  ): {
34
+ summary(tab: string): OperationsSummary;
23
35
  environmentChanged(): void;
24
36
  loadRuntimeStatus(): Promise<void>;
25
37
  loadRuntimeLogs(): Promise<void>;
@@ -1,5 +1,7 @@
1
1
  import { escapeHtml, formatTime, shortId, textOf } from "../lib/text-format.js";
2
- import { getNumber } from "../lib/event-presentation.js";
2
+ import { formatDurationMs, latestHealthEvent, projectOperationsSummary } from "../components/operations-summary.js";
3
+
4
+ export { formatDurationMs, latestHealthEvent } from "../components/operations-summary.js";
3
5
 
4
6
  function renderRuntimeRows(rows) {
5
7
  return `
@@ -18,34 +20,27 @@ function renderRuntimeRows(rows) {
18
20
  `;
19
21
  }
20
22
 
21
- export function formatDurationMs(value) {
22
- const ms = typeof value === "number" ? value : Number(value);
23
- if (!Number.isFinite(ms) || ms < 0) return "-";
24
- const seconds = Math.round(ms / 1000);
25
- if (seconds < 60) return `${seconds}s`;
26
- const minutes = Math.round(seconds / 60);
27
- if (minutes < 90) return `${minutes}m`;
28
- const hours = Math.round(minutes / 60);
29
- if (hours < 48) return `${hours}h`;
30
- return `${Math.round(hours / 24)}d`;
31
- }
32
-
33
- export function latestHealthEvent(details) {
34
- return [...details].sort(
35
- (left, right) =>
36
- getNumber(right.lastEventAt, 0) - getNumber(left.lastEventAt, 0),
37
- )[0];
38
- }
39
-
40
- export function createOperationsController({ dom, client, getEnvironmentId = () => undefined }) {
23
+ export function createOperationsController({ dom, client, getEnvironmentId = () => undefined, onSummaryChange = () => {} }) {
41
24
  let statusRevision = 0;
42
25
  let logsRevision = 0;
26
+ let healthRevision = 0;
43
27
  let displayedEnvironment = getEnvironmentId();
28
+ const reads = { runtime: { phase: "idle" }, logs: { phase: "idle" }, health: { phase: "idle" } };
29
+ function updateSummaryRead(tab, read) {
30
+ reads[tab] = read;
31
+ onSummaryChange();
32
+ }
44
33
  function isCurrentOperationsRead(environmentId, revision, currentRevision) {
45
34
  if (environmentId !== getEnvironmentId()) return false;
46
35
  return revision === currentRevision;
47
36
  }
37
+ function isCurrentHealthRead(revision) {
38
+ return revision === healthRevision;
39
+ }
48
40
  const controller = {
41
+ summary(tab) {
42
+ return projectOperationsSummary(tab, reads, getEnvironmentId());
43
+ },
49
44
  environmentChanged() {
50
45
  const environmentId = getEnvironmentId();
51
46
  if (displayedEnvironment === environmentId) return;
@@ -57,6 +52,7 @@ export function createOperationsController({ dom, client, getEnvironmentId = ()
57
52
  const environmentId = getEnvironmentId();
58
53
  const revision = ++statusRevision;
59
54
  dom.runtimeStatus.textContent = "Loading...";
55
+ updateSummaryRead("runtime", { phase: "loading", environmentId });
60
56
  try {
61
57
  const payload = await client.getRuntimeStatus(environmentId);
62
58
  if (!isCurrentOperationsRead(environmentId, revision, statusRevision)) return;
@@ -70,11 +66,13 @@ export function createOperationsController({ dom, client, getEnvironmentId = ()
70
66
  <div class="runtime-row"><span>Trace</span><strong>${escapeHtml(status.config?.traceFile || "-")}</strong></div>
71
67
  </div>
72
68
  `;
69
+ updateSummaryRead("runtime", { phase: "ready", environmentId, payload });
73
70
  } catch (error) {
74
71
  if (!isCurrentOperationsRead(environmentId, revision, statusRevision)) return;
75
72
  dom.runtimeStatus.innerHTML = `<span class="error-text">${escapeHtml(
76
73
  error instanceof Error ? error.message : String(error),
77
74
  )}</span>`;
75
+ updateSummaryRead("runtime", { phase: "error", environmentId });
78
76
  }
79
77
  },
80
78
 
@@ -82,6 +80,7 @@ export function createOperationsController({ dom, client, getEnvironmentId = ()
82
80
  const environmentId = getEnvironmentId();
83
81
  const revision = ++logsRevision;
84
82
  dom.runtimeLogs.textContent = "Loading...";
83
+ updateSummaryRead("logs", { phase: "loading", environmentId });
85
84
  try {
86
85
  const payload = await client.getRuntimeLogs(100, environmentId);
87
86
  if (!isCurrentOperationsRead(environmentId, revision, logsRevision)) return;
@@ -91,17 +90,22 @@ export function createOperationsController({ dom, client, getEnvironmentId = ()
91
90
  dom.runtimeLogs.textContent = lines.length
92
91
  ? lines.join("\n")
93
92
  : "No runtime log lines returned.";
93
+ updateSummaryRead("logs", { phase: "ready", environmentId, payload, refreshedAt: Date.now() });
94
94
  } catch (error) {
95
95
  if (!isCurrentOperationsRead(environmentId, revision, logsRevision)) return;
96
96
  dom.runtimeLogs.textContent =
97
97
  error instanceof Error ? error.message : String(error);
98
+ updateSummaryRead("logs", { phase: "error", environmentId });
98
99
  }
99
100
  },
100
101
 
101
102
  async loadSystemHealth() {
103
+ const revision = ++healthRevision;
102
104
  dom.healthStatus.textContent = "Loading...";
105
+ updateSummaryRead("health", { phase: "loading" });
103
106
  try {
104
107
  const payload = await client.getSystemHealth();
108
+ if (!isCurrentHealthRead(revision)) return;
105
109
  const details = Array.isArray(payload.activeStreamingRequestDetails)
106
110
  ? payload.activeStreamingRequestDetails
107
111
  : [];
@@ -149,10 +153,13 @@ export function createOperationsController({ dom, client, getEnvironmentId = ()
149
153
  : '<div class="empty-state compact">No active streaming requests</div>'
150
154
  }
151
155
  `;
156
+ updateSummaryRead("health", { phase: "ready", payload });
152
157
  } catch (error) {
158
+ if (!isCurrentHealthRead(revision)) return;
153
159
  dom.healthStatus.innerHTML = `<span class="error-text">${escapeHtml(
154
160
  error instanceof Error ? error.message : String(error),
155
161
  )}</span>`;
162
+ updateSummaryRead("health", { phase: "error" });
156
163
  }
157
164
  },
158
165
  };
@@ -17,6 +17,7 @@ export interface RuntimeOnboardingControllerDependencies {
17
17
  render(setup: RuntimeAvailability): void;
18
18
  }>;
19
19
  readonly reloadModels: () => void | Promise<void>;
20
+ readonly getEnvironmentId?: () => string;
20
21
  readonly setMessageStatus: (message: string) => void;
21
22
  readonly onStateChange?: () => void;
22
23
  }
@@ -40,11 +40,13 @@ export function createRuntimeOnboardingController({
40
40
  state,
41
41
  guide,
42
42
  reloadModels,
43
+ getEnvironmentId = () => "",
43
44
  setMessageStatus,
44
45
  onStateChange = () => {},
45
46
  }) {
46
47
  let checkPromise = null;
47
48
  let checkRevision = 0;
49
+ let catalogEnvironmentId = getEnvironmentId();
48
50
 
49
51
  function bind() {
50
52
  guide.bind({ onCheckAgain: checkAgain, onApplied: startCatalogCheck });
@@ -55,7 +57,16 @@ export function createRuntimeOnboardingController({
55
57
  guide.render(state.runtimeAvailability);
56
58
  }
57
59
 
60
+ function resetCatalogEnvironment() {
61
+ const environmentId = getEnvironmentId();
62
+ if (catalogEnvironmentId === environmentId) return;
63
+ catalogEnvironmentId = environmentId;
64
+ state.runtimeAvailability = { status: "loading" };
65
+ render();
66
+ }
67
+
58
68
  function beginCatalogLoad() {
69
+ resetCatalogEnvironment();
59
70
  state.runtimeAvailability = {
60
71
  status: "checking",
61
72
  showGuide: guideWasVisible(state.runtimeAvailability),