@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
@@ -1,3 +1,4 @@
1
+ import { createModelPluginViewState } from "./config-workspace/model-plugin-view-state.js";
1
2
  import { createConfigCalibrationDisclosure } from "./config-workspace/calibration-disclosure.js";
2
3
  import { createConfigDashboardRendering } from "./config-workspace/dashboard-rendering.js";
3
4
  import { createConfigFieldRendering } from "./config-workspace/field-rendering.js";
@@ -22,10 +23,12 @@ export {
22
23
  export function createConfigWorkspace({
23
24
  dom,
24
25
  loadDashboard,
26
+ loadModelPlugins,
25
27
  saveFile,
26
28
  memorySetup,
27
29
  memoryManagement,
28
30
  pluginManagement,
31
+ getOperationsSummary,
29
32
  onAddModel,
30
33
  onRemoveModel,
31
34
  onNavigationChange = () => {},
@@ -35,6 +38,7 @@ export function createConfigWorkspace({
35
38
  }) {
36
39
  const workspaceModel = createConfigWorkspaceModel();
37
40
  const { state } = workspaceModel;
41
+ const modelPluginViewState = createModelPluginViewState(dom.configDashboard);
38
42
  const calibrationDisclosure = createConfigCalibrationDisclosure({
39
43
  state,
40
44
  root: dom.configDashboard,
@@ -61,6 +65,7 @@ export function createConfigWorkspace({
61
65
  });
62
66
 
63
67
  const modelRendering = createConfigModelRendering({
68
+ modelPluginViewState,
64
69
  state,
65
70
  calibrationProfileLabel: fieldRendering.calibrationProfileLabel,
66
71
  configFileKey: workspaceModel.configFileKey,
@@ -87,6 +92,7 @@ export function createConfigWorkspace({
87
92
  });
88
93
 
89
94
  const workspaceView = createConfigWorkspaceView({
95
+ modelPluginViewState,
90
96
  onNavigationChange: () => {
91
97
  navigationRevision += 1;
92
98
  onNavigationChange();
@@ -95,11 +101,10 @@ export function createConfigWorkspace({
95
101
  dom,
96
102
  eventTarget,
97
103
  pluginManagement,
104
+ getOperationsSummary,
98
105
  configFileEntries: workspaceModel.configFileEntries,
99
106
  configFileKey: workspaceModel.configFileKey,
100
- countObjectKeys: fieldRendering.countObjectKeys,
101
107
  dashboardIsBusy: workspaceModel.dashboardIsBusy,
102
- dirtyFiles: workspaceModel.dirtyFiles,
103
108
  ensureRawSelection: workspaceModel.ensureRawSelection,
104
109
  fileStatusText: fieldRendering.fileStatusText,
105
110
  hasPendingFileChanges: workspaceModel.hasPendingFileChanges,
@@ -115,6 +120,7 @@ export function createConfigWorkspace({
115
120
  });
116
121
 
117
122
  const workspaceMutations = createConfigWorkspaceMutations({
123
+ modelPluginViewState,
118
124
  state,
119
125
  dom,
120
126
  confirmDiscard,
@@ -137,6 +143,7 @@ export function createConfigWorkspace({
137
143
  state,
138
144
  dom,
139
145
  loadDashboard,
146
+ loadModelPlugins,
140
147
  saveFile,
141
148
  memorySetup,
142
149
  memoryManagement,
@@ -160,6 +167,7 @@ export function createConfigWorkspace({
160
167
  });
161
168
 
162
169
  const workspaceEvents = createConfigWorkspaceEvents({
170
+ modelPluginViewState,
163
171
  onAddModel,
164
172
  onRemoveModel,
165
173
  state,
@@ -190,6 +198,7 @@ export function createConfigWorkspace({
190
198
 
191
199
  return {
192
200
  activeCategory: () => state.activeCategory,
201
+ syncSummary: workspaceView.syncDashboardSummary,
193
202
  setActive: calibrationDisclosure.setActive,
194
203
  setWorkspace: (workspace) => {
195
204
  const category = configurationCategoryForWorkspace(workspace);
@@ -81,6 +81,8 @@ export function createConversationView({
81
81
  if (viewState.followMessages) scrollToEnd();
82
82
  },
83
83
  });
84
+ let historyPager;
85
+ let holdingPosition = false;
84
86
  const viewState = {
85
87
  followMessages: true,
86
88
  messageRenderTimer: 0,
@@ -92,7 +94,7 @@ export function createConversationView({
92
94
  function updateJumpToLatest() {
93
95
  const hasOverflow =
94
96
  dom.messagesList.scrollHeight > dom.messagesList.clientHeight + 24;
95
- dom.jumpToLatestButton.hidden = viewState.followMessages || !hasOverflow;
97
+ dom.jumpToLatestButton.hidden = !historyPager?.hasNewer() && (viewState.followMessages || !hasOverflow);
96
98
  }
97
99
 
98
100
  function scrollToEnd() {
@@ -246,7 +248,7 @@ export function createConversationView({
246
248
  );
247
249
  const previousScrollTop = dom.messagesList.scrollTop;
248
250
  const shouldFollow =
249
- viewState.followMessages || isNearScrollEnd(dom.messagesList);
251
+ !holdingPosition && !historyPager?.hasNewer() && (viewState.followMessages || isNearScrollEnd(dom.messagesList));
250
252
  const messages = preserveMessageHistory ? getMessages() : projectApprovalContinuations(getMessages());
251
253
  conversationReasoning.beginRender(messages);
252
254
  messageLinkPreviews.reset();
@@ -265,7 +267,12 @@ export function createConversationView({
265
267
  for (const pendingApproval of getPendingApprovals()) {
266
268
  dom.messagesList.appendChild(createApprovalCard(pendingApproval));
267
269
  }
268
- if (scrollBehavior) return scrollBehavior.afterRender();
270
+ historyPager?.renderControls(dom.messagesList);
271
+ if (scrollBehavior) {
272
+ scrollBehavior.afterRender();
273
+ if (historyPager?.hasNewer()) dom.jumpToLatestButton.hidden = false;
274
+ return;
275
+ }
269
276
  if (shouldFollow) {
270
277
  scrollToEnd();
271
278
  } else {
@@ -303,7 +310,23 @@ export function createConversationView({
303
310
  viewState.thinkingRenderTimer = 0;
304
311
  }
305
312
 
313
+ function preserveScroll(change) {
314
+ const root = dom.messagesList;
315
+ const bounds = root.getBoundingClientRect();
316
+ const anchor = [...root.querySelectorAll("[data-message-id]")].find((node) => node.getBoundingClientRect().bottom > bounds.top);
317
+ const id = anchor?.dataset.messageId;
318
+ const offset = anchor?.getBoundingClientRect().top ?? 0;
319
+ const previousTop = root.scrollTop;
320
+ holdingPosition = true; viewState.followMessages = false; viewState.scrollSettleVersion += 1;
321
+ change();
322
+ const restored = [...root.querySelectorAll("[data-message-id]")].find((node) => node.dataset.messageId === id);
323
+ root.scrollTop = restored ? root.scrollTop + restored.getBoundingClientRect().top - offset : previousTop;
324
+ scrollBehavior?.preservePosition?.();
325
+ updateJumpToLatest();
326
+ }
327
+
306
328
  function reset() {
329
+ holdingPosition = false;
307
330
  filePreview.reset();
308
331
  viewState.followMessages = true;
309
332
  viewState.scrollSettleVersion += 1;
@@ -318,19 +341,27 @@ export function createConversationView({
318
341
  messageLinkPreviews.reset();
319
342
  }
320
343
 
344
+ function jumpToLatest() {
345
+ if (!historyPager?.hasNewer()) { scrollToEnd(); return; }
346
+ void historyPager.latest().then(scrollToEnd).catch(() => notify("Could not load latest messages", "failed"));
347
+ }
348
+
321
349
  function bind() {
322
350
  if (viewState.bound) return;
323
351
  viewState.bound = true;
324
- dom.jumpToLatestButton.addEventListener("click", scrollToEnd);
352
+ dom.jumpToLatestButton.addEventListener("click", jumpToLatest);
325
353
  dom.messagesList.addEventListener("scroll", () => {
354
+ holdingPosition = false;
326
355
  if (scrollBehavior) return;
327
- viewState.followMessages = isNearScrollEnd(dom.messagesList);
356
+ viewState.followMessages = !historyPager?.hasNewer() && isNearScrollEnd(dom.messagesList);
328
357
  if (!viewState.followMessages) viewState.scrollSettleVersion += 1;
329
358
  updateJumpToLatest();
330
359
  });
331
360
  }
332
361
 
333
362
  return {
363
+ preserveScroll,
364
+ setHistoryPager: (pager) => { historyPager = pager; },
334
365
  setWorkspace: filePreview.setWorkspace,
335
366
  closeFilePreview: filePreview.reset,
336
367
  bind,
@@ -35,6 +35,7 @@ function unreadAvailabilityNotice({ conversations, hasUnavailableReadState }) {
35
35
 
36
36
  export function renderDashboardConversations({
37
37
  sessions = [],
38
+ overview,
38
39
  loading = false,
39
40
  error = "",
40
41
  }) {
@@ -43,7 +44,7 @@ export function renderDashboardConversations({
43
44
  recentConversations: conversations,
44
45
  unreadCount,
45
46
  hasUnavailableReadState,
46
- } = projection;
47
+ } = { ...projection, ...(overview ? { unreadCount: overview.unreadCount, hasUnavailableReadState: overview.hasUnavailableReadState } : {}) };
47
48
  const badge =
48
49
  unreadCount > 0 && !hasUnavailableReadState
49
50
  ? `<span class="home-unread-count" aria-label="${unreadCount} unread messages">${unreadCount}</span>`
@@ -104,7 +104,8 @@ export function createDashboardWorkspace({ root, actions, passiveLearning }) {
104
104
  updateLiveRegion(
105
105
  conversations,
106
106
  renderDashboardConversations({
107
- sessions: snapshot.sessions,
107
+ sessions: snapshot.sessionOverview?.sessions ?? snapshot.sessions,
108
+ overview: snapshot.sessionOverview,
108
109
  loading: snapshot.loadingSessions,
109
110
  error: snapshot.sessionsError,
110
111
  }),
@@ -0,0 +1,105 @@
1
+ import { formatTime, textOf } from "../lib/text-format.js";
2
+ import { getNumber } from "../lib/event-presentation.js";
3
+
4
+ export function formatDurationMs(value) {
5
+ const ms = typeof value === "number" ? value : Number(value);
6
+ if (!Number.isFinite(ms) || ms < 0) return "-";
7
+ const seconds = Math.round(ms / 1000);
8
+ if (seconds < 60) return `${seconds}s`;
9
+ const minutes = Math.round(seconds / 60);
10
+ if (minutes < 90) return `${minutes}m`;
11
+ const hours = Math.round(minutes / 60);
12
+ if (hours < 48) return `${hours}h`;
13
+ return `${Math.round(hours / 24)}d`;
14
+ }
15
+
16
+ export function latestHealthEvent(details) {
17
+ return [...details].sort(
18
+ (left, right) => getNumber(right.lastEventAt, 0) - getNumber(left.lastEventAt, 0),
19
+ )[0];
20
+ }
21
+
22
+ function unavailableReadValue(read, environmentId, environmentScoped) {
23
+ if (environmentScoped && read.environmentId !== environmentId) return "Not loaded";
24
+ if (read.phase === "loading") return "Loading…";
25
+ if (read.phase === "error") return "Unavailable";
26
+ if (read.phase !== "ready") return "Not loaded";
27
+ return null;
28
+ }
29
+
30
+ function environmentValue(environmentId, source) {
31
+ const environment = textOf(environmentId) || "Current environment";
32
+ if (typeof source !== "string" || !source) return environment;
33
+ return `${environment} · ${source}`;
34
+ }
35
+
36
+ function runtimeSummary(read, environmentId) {
37
+ const unavailable = unavailableReadValue(read, environmentId, true);
38
+ const payload = unavailable ? {} : read.payload;
39
+ const status = payload.status || {};
40
+ const uptime = typeof status.uptimeMs === "number" ? formatDurationMs(status.uptimeMs) : "-";
41
+ return {
42
+ label: "Runtime · selected environment",
43
+ items: [
44
+ { label: "Environment", value: environmentValue(environmentId, payload.source) },
45
+ { label: "Uptime", value: unavailable || uptime, status: Boolean(unavailable) },
46
+ { label: "PID", value: unavailable || textOf(status.pid, "-") || "-" },
47
+ { label: "Node", value: unavailable || textOf(status.node?.version, "-") || "-" },
48
+ ],
49
+ };
50
+ }
51
+
52
+ function logFileAvailability(exists) {
53
+ if (exists === true) return "Available";
54
+ if (exists === false) return "Missing";
55
+ return "-";
56
+ }
57
+
58
+ function logsSummary(read, environmentId) {
59
+ const unavailable = unavailableReadValue(read, environmentId, true);
60
+ const log = unavailable ? {} : read.payload.log || {};
61
+ return {
62
+ label: "Logs · selected environment",
63
+ items: [
64
+ { label: "Environment", value: environmentValue(environmentId) },
65
+ { label: "Lines shown", value: unavailable || (Array.isArray(log.lines) ? String(log.lines.length) : "-"), status: Boolean(unavailable) },
66
+ { label: "Last refreshed", value: unavailable || formatTime(read.refreshedAt) || "-" },
67
+ { label: "Log file", value: unavailable || logFileAvailability(log.exists) },
68
+ ],
69
+ };
70
+ }
71
+
72
+ function healthValue(ok) {
73
+ if (ok === true) return "Healthy";
74
+ if (ok === false) return "Unhealthy";
75
+ return "-";
76
+ }
77
+
78
+ function activeStreamCount(payload) {
79
+ const count = payload.activeStreamingRequests;
80
+ if (typeof count === "number" && Number.isFinite(count) && count >= 0) return String(count);
81
+ if (Array.isArray(payload.activeStreamingRequestDetails)) return String(payload.activeStreamingRequestDetails.length);
82
+ return "-";
83
+ }
84
+
85
+ function healthSummary(read) {
86
+ const unavailable = unavailableReadValue(read, undefined, false);
87
+ const payload = unavailable ? {} : read.payload;
88
+ const details = Array.isArray(payload.activeStreamingRequestDetails) ? payload.activeStreamingRequestDetails : [];
89
+ const latest = latestHealthEvent(details);
90
+ return {
91
+ label: "System health · all environments",
92
+ items: [
93
+ { label: "Health · all environments", value: unavailable || healthValue(payload.ok), status: true },
94
+ { label: "Runtime mode", value: unavailable || textOf(payload.agentMode, "-") || "-" },
95
+ { label: "Active streams", value: unavailable || activeStreamCount(payload) },
96
+ { label: "Latest event", value: unavailable || textOf(latest?.lastEventName || latest?.lastEventType, "-") || "-" },
97
+ ],
98
+ };
99
+ }
100
+
101
+ export function projectOperationsSummary(tab, reads, environmentId) {
102
+ if (tab === "health") return healthSummary(reads.health);
103
+ if (tab === "logs") return logsSummary(reads.logs, environmentId);
104
+ return runtimeSummary(reads.runtime, environmentId);
105
+ }
@@ -3,7 +3,6 @@ import { agentMarkup, renderAgent } from "./agent-view.js";
3
3
  import { createAgentMotion } from "./agent-motion.js";
4
4
  import { learningFailurePresentation } from "./presentation.js";
5
5
  import { homeActivityStartPatch, readActivityPermissions } from "./activity-controls.js";
6
- import { computerUpdateNoticeMarkup, renderComputerUpdateNotice } from "./computer-update-notice.js";
7
6
  import { macCollectionRestartNoticeMarkup, renderMacCollectionRestartNotice, handleMacCollectionRestartClick } from "./macos-collection-restart-notice.js";
8
7
 
9
8
  function canStartHomeLearning(snapshot) {
@@ -33,7 +32,6 @@ export function createPassiveLearningHome({
33
32
  actions,
34
33
  openLearning,
35
34
  openSetup = openLearning,
36
- openComputerAccess = openSetup,
37
35
  }) {
38
36
  let root = null;
39
37
  let renderedSnapshot = {};
@@ -45,7 +43,7 @@ export function createPassiveLearningHome({
45
43
  root?.removeEventListener("click", handleClick);
46
44
  root = nextRoot;
47
45
  if (!root) return;
48
- root.innerHTML = `${computerUpdateNoticeMarkup()}${macCollectionRestartNoticeMarkup()}
46
+ root.innerHTML = `${macCollectionRestartNoticeMarkup()}
49
47
  <div class="home-learning-agent">${agentMarkup({
50
48
  compact: true,
51
49
  actionMarkup: '<button type="button" class="home-text-button" data-learning-open>Open ABot Spark</button>',
@@ -57,11 +55,6 @@ export function createPassiveLearningHome({
57
55
 
58
56
  function handleClick(event) {
59
57
  if (handleMacCollectionRestartClick(root, event, actions)) return;
60
- const update = event.target.closest("[data-learning-open-update]");
61
- if (update && root.contains(update)) {
62
- openComputerAccess();
63
- return;
64
- }
65
58
  const button = event.target.closest("[data-learning-open]");
66
59
  if (!button || !root.contains(button) || button.disabled) return;
67
60
  const snapshot = actions?.snapshot?.() ?? renderedSnapshot;
@@ -80,7 +73,6 @@ export function createPassiveLearningHome({
80
73
  function render(snapshot) {
81
74
  renderedSnapshot = snapshot;
82
75
  if (!root) return;
83
- renderComputerUpdateNotice(root, snapshot);
84
76
  renderMacCollectionRestartNotice(root, snapshot);
85
77
  renderAgent(root, snapshot);
86
78
  motion?.update(snapshot);
@@ -0,0 +1,34 @@
1
+ import { clearPluginSecretInputs } from "./configuration-fields.js";
2
+
3
+ /** Keep the actual controls mounted; never copy entered passwords into state. */
4
+ export function createPluginConfigurationDraft() {
5
+ let fields = null;
6
+ let owner = null;
7
+ function clear() {
8
+ clearPluginSecretInputs(fields);
9
+ fields = null;
10
+ owner = null;
11
+ }
12
+ function capture(root, review, busy) {
13
+ if (busy || !review || owner !== review) {
14
+ clear();
15
+ return;
16
+ }
17
+ fields =
18
+ root?.querySelector("[data-plugin-configuration-fields]") || fields;
19
+ }
20
+ function restore(root, review, busy) {
21
+ if (busy || !review) {
22
+ clear();
23
+ return;
24
+ }
25
+ const replacement = root.querySelector(
26
+ "[data-plugin-configuration-fields]",
27
+ );
28
+ if (owner === review && fields && replacement)
29
+ replacement.replaceWith(fields);
30
+ fields = root.querySelector("[data-plugin-configuration-fields]");
31
+ owner = review;
32
+ }
33
+ return { capture, restore, clear };
34
+ }
@@ -0,0 +1,147 @@
1
+ import { escapeHtml } from "../../lib/text-format.js";
2
+ import {
3
+ bindPluginConfigurationFields,
4
+ clearPluginSecretInputs,
5
+ readPluginConfigurationFields,
6
+ renderPluginConfigurationFields,
7
+ } from "./configuration-fields.js";
8
+
9
+ /** A mounted dialog preserves password controls across dashboard rerenders. */
10
+ export function createPluginConfigurationEditor({
11
+ loadConfiguration,
12
+ saveConfiguration,
13
+ getEnvironmentId,
14
+ beginRuntimeMutation,
15
+ endRuntimeMutation,
16
+ onConfigurationSaved,
17
+ onClosed = () => {},
18
+ }) {
19
+ let dialog = null;
20
+ let busy = false;
21
+ let configuration = null;
22
+ let environment = "";
23
+ let generation = 0;
24
+
25
+ function close() {
26
+ if (busy || !dialog) return false;
27
+ generation += 1;
28
+ clearPluginSecretInputs(dialog);
29
+ dialog.close?.();
30
+ dialog.remove();
31
+ dialog = null;
32
+ configuration = null;
33
+ endRuntimeMutation();
34
+ onClosed();
35
+ return true;
36
+ }
37
+
38
+ function message(text, failed = false) {
39
+ const status = dialog?.querySelector("[data-plugin-configuration-status]");
40
+ if (!status) return;
41
+ status.textContent = text;
42
+ status.className = failed ? "error-text" : "";
43
+ }
44
+
45
+ function setBusy(value) {
46
+ busy = value;
47
+ const fields = dialog?.querySelector("fieldset");
48
+ if (fields) fields.disabled = value;
49
+ for (const button of dialog?.querySelectorAll("button") || [])
50
+ button.disabled = value;
51
+ }
52
+
53
+ async function save() {
54
+ if (busy || !configuration) return false;
55
+ if (environment !== getEnvironmentId()) return false;
56
+ let input;
57
+ try {
58
+ input = readPluginConfigurationFields(dialog, configuration);
59
+ } catch (error) {
60
+ message(error.message, true);
61
+ return false;
62
+ }
63
+ setBusy(true);
64
+ message("Saving…");
65
+ try {
66
+ const result = await saveConfiguration(
67
+ configuration.pluginId,
68
+ input,
69
+ environment,
70
+ );
71
+ configuration = result.configuration;
72
+ onConfigurationSaved();
73
+ message(
74
+ "Saved. Close this form, then Apply changes to use these values.",
75
+ );
76
+ clearPluginSecretInputs(dialog);
77
+ const fields = dialog.querySelector("[data-plugin-configuration-body]");
78
+ fields.innerHTML = renderPluginConfigurationFields(configuration);
79
+ bindPluginConfigurationFields(fields.querySelector("fieldset"), () =>
80
+ message("Unsaved changes. Save to keep them."),
81
+ );
82
+ return true;
83
+ } catch (error) {
84
+ // Do not project transport payloads or an entered password into HTML or logs.
85
+ clearPluginSecretInputs(dialog);
86
+ message(
87
+ error.message || "Plugin configuration could not be saved.",
88
+ true,
89
+ );
90
+ return false;
91
+ } finally {
92
+ input = undefined;
93
+ setBusy(false);
94
+ }
95
+ }
96
+
97
+ async function open(pluginId, root) {
98
+ if (dialog || !root?.ownerDocument || !loadConfiguration) return false;
99
+ if (!beginRuntimeMutation("plugin configuration")) return false;
100
+ environment = getEnvironmentId();
101
+ const request = ++generation;
102
+ dialog = root.ownerDocument.createElement("dialog");
103
+ dialog.className = "plugin-configuration-dialog";
104
+ dialog.setAttribute("aria-label", `Configure ${pluginId}`);
105
+ dialog.innerHTML = `<form><h2 tabindex="-1" autofocus>Configure ${escapeHtml(pluginId)}</h2><div data-plugin-configuration-body></div><p data-plugin-configuration-status role="status" aria-live="polite">Loading…</p><div class="plugin-configuration-actions"><button type="button" data-plugin-configuration-close>Close</button><button type="submit" class="primary-button">Save changes</button></div></form>`;
106
+ root.ownerDocument.body.append(dialog);
107
+ dialog.querySelector("form").addEventListener("submit", (event) => {
108
+ event.preventDefault();
109
+ void save();
110
+ });
111
+ dialog
112
+ .querySelector("[data-plugin-configuration-close]")
113
+ .addEventListener("click", close);
114
+ dialog.addEventListener("cancel", (event) => {
115
+ event.preventDefault();
116
+ close();
117
+ });
118
+ dialog.showModal();
119
+ setBusy(true);
120
+ try {
121
+ const result = await loadConfiguration(pluginId, environment);
122
+ if (request !== generation || environment !== getEnvironmentId())
123
+ return false;
124
+ configuration = result.configuration;
125
+ const fields = dialog.querySelector("[data-plugin-configuration-body]");
126
+ fields.innerHTML = renderPluginConfigurationFields(configuration);
127
+ bindPluginConfigurationFields(fields.querySelector("fieldset"), () =>
128
+ message("Unsaved changes. Save to keep them."),
129
+ );
130
+ message(
131
+ "Changes take effect after Apply. Clearing a required key prevents the plugin from loading until it is configured again.",
132
+ );
133
+ return true;
134
+ } catch (error) {
135
+ message(
136
+ error.message || "Plugin configuration could not be loaded.",
137
+ true,
138
+ );
139
+ return false;
140
+ } finally {
141
+ setBusy(false);
142
+ if (environment !== getEnvironmentId()) close();
143
+ }
144
+ }
145
+
146
+ return { open, close, save, isOpen: () => Boolean(dialog) };
147
+ }
@@ -0,0 +1,103 @@
1
+ import {
2
+ renderPluginSettings,
3
+ bindPluginSettingFields,
4
+ readPluginSettingOverrides,
5
+ } from "./configuration-settings.js";
6
+ import {
7
+ renderRetainedPluginSecrets,
8
+ readRetainedPluginSecretClears,
9
+ } from "./configuration-retained-secrets.js";
10
+ import { escapeAttribute, escapeHtml } from "../../lib/text-format.js";
11
+
12
+ function renderSecret(secret, index) {
13
+ return `<div class="plugin-secret-field">
14
+ <label><span>${escapeHtml(secret.name)}${secret.required ? " (required)" : ""}</span>
15
+ <small>${escapeHtml(secret.env)} · ${secret.configured ? "Configured" : "Not configured"}</small>
16
+ <select data-plugin-secret-action="${index}" aria-label="${escapeAttribute(`Action for ${secret.name}`)}"><option value="keep">Keep current value</option><option value="set">Replace value</option><option value="clear">Clear value</option></select>
17
+ </label>
18
+ <input type="password" data-plugin-secret-value="${index}" aria-label="${escapeAttribute(`New value for ${secret.name}`)}" autocomplete="new-password" disabled hidden>
19
+ </div>`;
20
+ }
21
+
22
+ export function renderPluginConfigurationFields(configuration, busy = false) {
23
+ if (!configuration) return "";
24
+ const entries = Object.entries(configuration.defaults || {});
25
+ const secrets = configuration.secrets || [];
26
+ const retained = configuration.retainedSecrets || [];
27
+ if (!entries.length && !secrets.length && !retained.length) return "";
28
+ return `<fieldset class="plugin-configuration-fields" data-plugin-configuration-fields ${busy ? "disabled" : ""}>
29
+ <legend>Configuration</legend>
30
+ <p>Shared across environments in this ABot installation. Saved values stay local and are retained when the plugin is removed.</p>
31
+ ${renderPluginSettings(configuration)}
32
+ ${secrets.map(renderSecret).join("")}
33
+ ${renderRetainedPluginSecrets(retained)}
34
+ </fieldset>`;
35
+ }
36
+
37
+ const boundFields = new WeakSet();
38
+
39
+ export function bindPluginConfigurationFields(root, onChange = () => {}) {
40
+ if (!root || boundFields.has(root)) return;
41
+ boundFields.add(root);
42
+ bindPluginSettingFields(root, onChange);
43
+ root?.addEventListener("input", onChange);
44
+ for (const select of root?.querySelectorAll("[data-plugin-secret-action]") ||
45
+ []) {
46
+ select.addEventListener("change", () => {
47
+ const input = root.querySelector(
48
+ `[data-plugin-secret-value="${select.dataset.pluginSecretAction}"]`,
49
+ );
50
+ input.hidden = select.value !== "set";
51
+ input.disabled = select.value !== "set";
52
+ input.required = select.value === "set";
53
+ if (select.value !== "set") input.value = "";
54
+ onChange();
55
+ });
56
+ }
57
+ }
58
+
59
+ /** Secrets exist only in password controls and the one outbound save payload. */
60
+ export function readPluginConfigurationFields(
61
+ root,
62
+ configuration,
63
+ requireSecrets = false,
64
+ ) {
65
+ const settings = readPluginSettingOverrides(root, configuration);
66
+ const secrets = Object.create(null);
67
+ for (const [index, secret] of (configuration.secrets || []).entries()) {
68
+ const action =
69
+ root?.querySelector(`[data-plugin-secret-action="${index}"]`)?.value ||
70
+ "keep";
71
+ if (action === "set") {
72
+ const value = root.querySelector(
73
+ `[data-plugin-secret-value="${index}"]`,
74
+ ).value;
75
+ if (!value.trim()) throw new Error(`Enter a value for ${secret.name}.`);
76
+ secrets[secret.name] = { action, value };
77
+ continue;
78
+ }
79
+ if (
80
+ requireSecrets &&
81
+ secret.required &&
82
+ (action === "clear" || !secret.configured)
83
+ )
84
+ throw new Error(`Configure ${secret.name} before installing.`);
85
+ if (action === "clear") secrets[secret.name] = { action };
86
+ }
87
+ const clearRetainedSecrets = readRetainedPluginSecretClears(
88
+ root,
89
+ configuration.retainedSecrets,
90
+ );
91
+ return {
92
+ revision: configuration.revision,
93
+ settings,
94
+ secrets,
95
+ ...(clearRetainedSecrets.length ? { clearRetainedSecrets } : {}),
96
+ };
97
+ }
98
+
99
+ export function clearPluginSecretInputs(root) {
100
+ for (const input of root?.querySelectorAll("[data-plugin-secret-value]") ||
101
+ [])
102
+ input.value = "";
103
+ }
@@ -0,0 +1,20 @@
1
+ import { escapeHtml } from "../../lib/text-format.js";
2
+
3
+ export function renderRetainedPluginSecrets(secrets = []) {
4
+ if (!secrets.length) return "";
5
+ return `<section class="plugin-retained-secrets">
6
+ <h3>Keys no longer used by this plugin</h3>
7
+ <p>These saved keys stay on this device until you choose to delete them. Delete an old key before configuring a new key with the same name.</p>
8
+ ${secrets.map((secret, index) => `<label class="plugin-setting-checkbox"><input type="checkbox" data-plugin-retained-secret="${index}"><span>Delete saved key <strong>${escapeHtml(secret.name)}</strong> <small>${escapeHtml(secret.env)}</small></span></label>`).join("")}
9
+ </section>`;
10
+ }
11
+
12
+ export function readRetainedPluginSecretClears(root, secrets = []) {
13
+ return secrets.flatMap((secret, index) => {
14
+ const input = root?.querySelector(
15
+ `[data-plugin-retained-secret="${index}"]`,
16
+ );
17
+ if (!input?.checked) return [];
18
+ return [{ name: secret.name, env: secret.env }];
19
+ });
20
+ }