@salsita-ai/sdk 0.50.0-develop.da8d27f5d7 → 0.50.0-develop.db8514eccf

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 (298) hide show
  1. package/README.md +19 -11
  2. package/dist/application-spec/errors/diagnostic-codes.d.ts +6 -0
  3. package/dist/application-spec/errors/diagnostic-codes.js +1 -1
  4. package/dist/application-spec/errors/diagnostic-messages.d.ts +6 -0
  5. package/dist/application-spec/errors/diagnostic-messages.js +1 -1
  6. package/dist/application-spec/generate-application/code/domain-runtime-files.js +1 -1
  7. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  8. package/dist/application-spec/generate-application/code/templates/application-types.js +6 -6
  9. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.d.ts +1 -0
  10. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.js +11 -8
  11. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.d.ts +3 -1
  12. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.js +70 -66
  13. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-phx-globals.js +3 -0
  14. package/dist/application-spec/generate-application/code/templates/domain-spec.js +5 -5
  15. package/dist/application-spec/generate-application/code/templates/domain.js +2 -2
  16. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.d.ts +11 -0
  17. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.js +8 -0
  18. package/dist/application-spec/generate-application/code/templates/global-definition/index.d.ts +1 -0
  19. package/dist/application-spec/generate-application/code/templates/global-definition/index.js +1 -1
  20. package/dist/application-spec/generate-application/code/templates/globals.js +1 -0
  21. package/dist/application-spec/generate-application/code/templates/index.d.ts +7 -6
  22. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  23. package/dist/application-spec/generate-application/code/templates/skills/agents-md.js +2 -0
  24. package/dist/application-spec/generate-application/code/templates/skills/phoenix-cli-skill.js +1 -0
  25. package/dist/application-spec/generate-application/code/templates/{phoenix-create-configurator-skill.d.ts → skills/phoenix-create-configurator-skill.d.ts} +1 -1
  26. package/dist/application-spec/generate-application/code/templates/{phoenix-create-configurator-skill.js → skills/phoenix-create-configurator-skill.js} +43 -13
  27. package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js +54 -0
  28. package/dist/application-spec/generate-application/code/templates/{phoenix-review-skill.js → skills/phoenix-review-skill.js} +7 -5
  29. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -0
  30. package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.d.ts +1 -0
  31. package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.js +6 -0
  32. package/dist/application-spec/generate-application/code/templates/{common → skills}/sdk-content.d.ts +52 -3
  33. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +82 -0
  34. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +2 -0
  35. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  36. package/dist/application-spec/generate-application/runtime/sections/actions-runtime.d.ts +14 -0
  37. package/dist/application-spec/generate-application/runtime/sections/actions-runtime.js +1 -0
  38. package/dist/application-spec/generate-application/runtime/sections/index.d.ts +1 -0
  39. package/dist/application-spec/generate-application/runtime/sections/index.js +1 -1
  40. package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +3 -1
  41. package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
  42. package/dist/application-spec/parse-application-files/merge/index.d.ts +1 -0
  43. package/dist/application-spec/parse-application-files/merge/index.js +1 -1
  44. package/dist/application-spec/parse-application-files/merge/merged-actions.d.ts +11 -0
  45. package/dist/application-spec/parse-application-files/merge/merged-actions.js +1 -0
  46. package/dist/application-spec/parse-application-files/merge/merged-spec.d.ts +2 -0
  47. package/dist/application-spec/parse-application-files/merge/merged-spec.js +1 -1
  48. package/dist/application-spec/parse-application-files/raw-spec/index.d.ts +1 -0
  49. package/dist/application-spec/parse-application-files/raw-spec/index.js +1 -1
  50. package/dist/application-spec/parse-application-files/raw-spec/raw-actions.d.ts +18 -0
  51. package/dist/application-spec/parse-application-files/raw-spec/raw-actions.js +1 -0
  52. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +14 -2
  53. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.js +1 -1
  54. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.d.ts +2 -0
  55. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.js +1 -1
  56. package/dist/application-spec/parse-application-files/resolve/resolve-import-modules.js +1 -1
  57. package/dist/application-spec/parse-application-files/spec/application/application-spec.js +1 -1
  58. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.d.ts +4 -2
  59. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.js +1 -1
  60. package/dist/application-spec/parse-application-files/spec/common/index.d.ts +1 -0
  61. package/dist/application-spec/parse-application-files/spec/common/index.js +1 -1
  62. package/dist/application-spec/parse-application-files/spec/common/schema-ref-resolver.d.ts +23 -0
  63. package/dist/application-spec/parse-application-files/spec/common/schema-ref-resolver.js +1 -0
  64. package/dist/application-spec/parse-application-files/spec/model/names/action-name.d.ts +10 -0
  65. package/dist/application-spec/parse-application-files/spec/model/names/action-name.js +1 -0
  66. package/dist/application-spec/parse-application-files/spec/model/names/index.d.ts +1 -0
  67. package/dist/application-spec/parse-application-files/spec/model/names/index.js +1 -1
  68. package/dist/application-spec/parse-application-files/spec/sections/actions.d.ts +35 -0
  69. package/dist/application-spec/parse-application-files/spec/sections/actions.js +1 -0
  70. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.d.ts +3 -14
  71. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.js +1 -1
  72. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.d.ts +12 -3
  73. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
  74. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant.d.ts +5 -3
  75. package/dist/application-spec/parse-application-files/spec/sections/index.d.ts +1 -0
  76. package/dist/application-spec/parse-application-files/spec/sections/index.js +1 -1
  77. package/dist/client/phoenix-api.d.ts +5 -1
  78. package/dist/configurator/application-runtime/extensions/configurator-extension-systems.d.ts +2 -1
  79. package/dist/configurator/application-runtime/extensions/configurator-extension-systems.js +1 -1
  80. package/dist/configurator/application-runtime/extensions/selectors.d.ts +2 -1
  81. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  82. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +168 -0
  83. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -0
  84. package/dist/configurator/{assistant → application-runtime/extensions/systems/assistant-system}/browser-details.d.ts +1 -1
  85. package/dist/configurator/application-runtime/extensions/systems/assistant-system/browser-details.js +1 -0
  86. package/dist/configurator/application-runtime/extensions/systems/assistant-system/change-recorder.d.ts +85 -0
  87. package/dist/configurator/application-runtime/extensions/systems/assistant-system/change-recorder.js +1 -0
  88. package/dist/configurator/{assistant → application-runtime/extensions/systems/assistant-system}/graphql-tools.d.ts +20 -16
  89. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -0
  90. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.d.ts +4 -0
  91. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.js +1 -1
  92. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +16 -1
  93. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  94. package/dist/configurator/application-runtime/extensions/systems/graphql-system/side-effects.d.ts +9 -0
  95. package/dist/configurator/application-runtime/extensions/systems/graphql-system/side-effects.js +1 -0
  96. package/dist/configurator/application-runtime/extensions/systems/index.d.ts +1 -0
  97. package/dist/configurator/application-runtime/extensions/systems/index.js +1 -1
  98. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -0
  99. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js +1 -1
  100. package/dist/configurator/common/frontend-analytics.d.ts +21 -21
  101. package/dist/configurator/index.d.ts +0 -1
  102. package/dist/configurator/index.js +1 -1
  103. package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
  104. package/dist/configurator/scene/scene-analytics/scene-analytics.d.ts +2 -2
  105. package/dist/configurator/ui/assistant/AssistantChips.d.ts +11 -0
  106. package/dist/configurator/ui/assistant/AssistantChips.js +1 -0
  107. package/dist/configurator/ui/assistant/AssistantComposer.d.ts +6 -0
  108. package/dist/configurator/ui/assistant/AssistantComposer.js +1 -0
  109. package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +7 -0
  110. package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -0
  111. package/dist/configurator/ui/assistant/AssistantPanel.d.ts +11 -0
  112. package/dist/configurator/ui/assistant/AssistantPanel.js +1 -0
  113. package/dist/configurator/ui/assistant/AssistantThread.d.ts +6 -0
  114. package/dist/configurator/ui/assistant/AssistantThread.js +1 -0
  115. package/dist/configurator/ui/assistant/assistant-failure.d.ts +18 -0
  116. package/dist/configurator/ui/assistant/assistant-failure.js +1 -0
  117. package/dist/configurator/ui/assistant/index.d.ts +2 -0
  118. package/dist/configurator/ui/assistant/index.js +1 -0
  119. package/dist/configurator/ui/assistant/streamed-markdown.d.ts +6 -0
  120. package/dist/configurator/ui/assistant/streamed-markdown.js +2 -0
  121. package/dist/configurator/ui/assistant/theme.d.ts +2 -0
  122. package/dist/configurator/ui/assistant/theme.js +1 -0
  123. package/dist/configurator/ui/assistant/thread-items.d.ts +37 -0
  124. package/dist/configurator/ui/assistant/thread-items.js +1 -0
  125. package/dist/configurator/ui/assistant/tool-chip.d.ts +37 -0
  126. package/dist/configurator/ui/assistant/tool-chip.js +1 -0
  127. package/dist/configurator/ui/assistant/use-follow-stream.d.ts +17 -0
  128. package/dist/configurator/ui/assistant/use-follow-stream.js +1 -0
  129. package/dist/configurator/ui/assistant/use-is-assistant-panel-open.d.ts +5 -0
  130. package/dist/configurator/ui/assistant/use-is-assistant-panel-open.js +1 -0
  131. package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.js +5 -5
  132. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.d.ts +2 -0
  133. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.js +4 -4
  134. package/dist/configurator/ui/legacy/mobile-controls/MobileControls.d.ts +1 -1
  135. package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.d.ts +10 -0
  136. package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.js +1 -0
  137. package/dist/configurator/ui/phoenix-layout-elements/index.d.ts +1 -0
  138. package/dist/configurator/ui/phoenix-layout-elements/index.js +1 -1
  139. package/dist/docs/index.json +12 -2
  140. package/dist/docs/layouts/toolbar.mdx +6 -0
  141. package/dist/docs/other-features/domain-actions.mdx +418 -0
  142. package/dist/docs/other-features/side-effects.mdx +26 -0
  143. package/dist/docs/phoenix/admin.mdx +2 -2
  144. package/dist/docs/phoenix/best-practices.mdx +1 -0
  145. package/dist/docs/phoenix/finalization.mdx +1 -1
  146. package/dist/docs/phoenix/integration/headless-mode-external.mdx +5 -5
  147. package/dist/docs/phoenix/integration/widget-mode-external.mdx +5 -5
  148. package/dist/docs/phoenix/migrations.mdx +45 -5
  149. package/dist/docs/phoenix/phoenix-cli.mdx +139 -20
  150. package/dist/docs/phoenix/project-structure.mdx +7 -3
  151. package/dist/docs/phoenix/sdk-builder-workflow.mdx +12 -8
  152. package/dist/domain/actions/action-factory.d.ts +10 -0
  153. package/dist/domain/actions/action-factory.js +1 -0
  154. package/dist/domain/actions/prepare-action-implementations.d.ts +15 -0
  155. package/dist/domain/actions/prepare-action-implementations.js +5 -0
  156. package/dist/domain/application/application-config.d.ts +3 -1
  157. package/dist/domain/application/assistant-mutations.d.ts +15 -14
  158. package/dist/domain/application/assistant-mutations.js +1 -1
  159. package/dist/domain/components/updates.d.ts +16 -0
  160. package/dist/domain/core/action.d.ts +67 -0
  161. package/dist/domain/core/action.js +1 -0
  162. package/dist/domain/core/domain-factory.d.ts +4 -0
  163. package/dist/domain/core/index.d.ts +1 -0
  164. package/dist/domain/domain-builder.d.ts +7 -0
  165. package/dist/domain/domain-builder.js +1 -1
  166. package/dist/domain/domain-context/action-system/action-system.d.ts +40 -0
  167. package/dist/domain/domain-context/action-system/action-system.js +1 -0
  168. package/dist/domain/domain-context/domain-context-systems.d.ts +5 -2
  169. package/dist/domain/domain-context/index.d.ts +2 -1
  170. package/dist/domain/domain-context/index.js +1 -1
  171. package/dist/domain/domain-context/store-system/store-system.js +1 -1
  172. package/dist/domain/domain-runtime/domain-context/domain-context.js +1 -1
  173. package/dist/domain/domain-runtime/domain-context/selectors.d.ts +4 -0
  174. package/dist/domain/domain-runtime/domain-context/selectors.js +1 -1
  175. package/dist/domain/domain-runtime/domain-runtime.d.ts +12 -0
  176. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  177. package/dist/domain/domain-runtime/spec/types.d.ts +15 -1
  178. package/dist/domain/index.d.ts +2 -0
  179. package/dist/domain/index.js +1 -1
  180. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +6 -0
  181. package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.d.ts +44 -0
  182. package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.js +1 -0
  183. package/dist/domain/system-components/phoenix-layout-elements/index.d.ts +1 -0
  184. package/dist/domain/system-components/phoenix-layout-elements/index.js +1 -1
  185. package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.d.ts +2 -1
  186. package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.js +1 -1
  187. package/dist/editor/Editor.js +1 -1
  188. package/dist/editor/domain-editors/DomainSpecContext.js +1 -1
  189. package/dist/editor/domain-editors/apply-spec-update.js +1 -1
  190. package/dist/editor/domain-editors/use-implementation-files.d.ts +7 -3
  191. package/dist/editor/domain-editors/use-implementation-files.js +1 -1
  192. package/dist/editor/graphql/GraphQLPanel.js +1 -1
  193. package/dist/editor/variants/VariantAnswerFooter.d.ts +17 -0
  194. package/dist/editor/variants/VariantAnswerFooter.js +1 -0
  195. package/dist/editor/variants/VariantPreviewHandler.d.ts +6 -0
  196. package/dist/editor/variants/VariantPreviewHandler.js +1 -0
  197. package/dist/editor/variants/variant-preview-styles.d.ts +49 -0
  198. package/dist/editor/variants/variant-preview-styles.js +1 -0
  199. package/dist/i18n/locales/cs/messages.js +1 -1
  200. package/dist/i18n/locales/da/messages.js +1 -1
  201. package/dist/i18n/locales/de/messages.js +1 -1
  202. package/dist/i18n/locales/de-AT/messages.js +1 -1
  203. package/dist/i18n/locales/en/messages.js +1 -1
  204. package/dist/i18n/locales/en-AU/messages.js +1 -1
  205. package/dist/i18n/locales/en-GB/messages.js +1 -1
  206. package/dist/i18n/locales/es/messages.js +1 -1
  207. package/dist/i18n/locales/fr/messages.js +1 -1
  208. package/dist/i18n/locales/hu/messages.js +1 -1
  209. package/dist/i18n/locales/it/messages.js +1 -1
  210. package/dist/i18n/locales/nl/messages.js +1 -1
  211. package/dist/i18n/locales/pl/messages.js +1 -1
  212. package/dist/i18n/locales/pt/messages.js +1 -1
  213. package/dist/i18n/locales/ro/messages.js +1 -1
  214. package/dist/i18n/locales/sk/messages.js +1 -1
  215. package/dist/i18n/locales/tr/messages.js +1 -1
  216. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +6 -0
  217. package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
  218. package/dist/phoenix/application-spec/generate-assistant-prompt.d.ts +4 -3
  219. package/dist/phoenix/application-spec/generate-assistant-prompt.js +1 -1
  220. package/dist/phoenix/cli.js +1 -1
  221. package/dist/phoenix/commands/build.d.ts +5 -0
  222. package/dist/phoenix/commands/build.js +1 -1
  223. package/dist/phoenix/commands/deploy.js +2 -2
  224. package/dist/phoenix/commands/dev.js +5 -5
  225. package/dist/phoenix/commands/feedback.d.ts +2 -1
  226. package/dist/phoenix/commands/feedback.js +1 -1
  227. package/dist/phoenix/commands/index.d.ts +1 -0
  228. package/dist/phoenix/commands/index.js +1 -1
  229. package/dist/phoenix/commands/login.d.ts +24 -6
  230. package/dist/phoenix/commands/login.js +2 -2
  231. package/dist/phoenix/commands/story-dev.js +2 -2
  232. package/dist/phoenix/commands/variants.d.ts +35 -0
  233. package/dist/phoenix/commands/variants.js +1 -0
  234. package/dist/phoenix/editor-server/commands-router.d.ts +68 -1
  235. package/dist/phoenix/editor-server/commands-router.js +1 -1
  236. package/dist/phoenix/editor-server/editor-router.d.ts +65 -0
  237. package/dist/phoenix/editor-server/editor-server.d.ts +3 -3
  238. package/dist/phoenix/editor-server/editor-server.js +1 -1
  239. package/dist/phoenix/editor-server/protocol.d.ts +1 -0
  240. package/dist/phoenix/editor-server/spec-router.js +1 -1
  241. package/dist/phoenix/editor-server/trpc.d.ts +3 -0
  242. package/dist/phoenix/services/application-spec-project-watcher.d.ts +13 -2
  243. package/dist/phoenix/services/application-spec-project-watcher.js +3 -3
  244. package/dist/phoenix/services/index.d.ts +5 -0
  245. package/dist/phoenix/services/index.js +1 -1
  246. package/dist/phoenix/services/phoenix-application.d.ts +21 -0
  247. package/dist/phoenix/services/phoenix-application.js +1 -0
  248. package/dist/phoenix/services/phoenix-store.d.ts +48 -0
  249. package/dist/phoenix/services/phoenix-store.js +1 -0
  250. package/dist/phoenix/services/phx-banner-plugin.d.ts +32 -0
  251. package/dist/phoenix/services/phx-banner-plugin.js +1 -0
  252. package/dist/phoenix/services/phx-licenses-plugin.d.ts +55 -0
  253. package/dist/phoenix/services/phx-licenses-plugin.js +8 -0
  254. package/dist/phoenix/services/phx-variant-previews-plugin.d.ts +9 -0
  255. package/dist/phoenix/services/phx-variant-previews-plugin.js +1 -0
  256. package/dist/phoenix/services/variant-file-session.d.ts +36 -0
  257. package/dist/phoenix/services/variant-file-session.js +1 -0
  258. package/dist/phoenix/services/variant-preview-service.d.ts +80 -0
  259. package/dist/phoenix/services/variant-preview-service.js +1 -0
  260. package/dist/phoenix/services/vite-config.d.ts +2 -0
  261. package/dist/phoenix/services/vite-config.js +1 -1
  262. package/dist/phoenix/utils/index.d.ts +1 -0
  263. package/dist/phoenix/utils/index.js +1 -1
  264. package/dist/phoenix/utils/read-git-commit.d.ts +14 -0
  265. package/dist/phoenix/utils/read-git-commit.js +1 -0
  266. package/dist/react-sdk/application-runtime/common-services/analytics/analytics-service.d.ts +2 -2
  267. package/dist/react-sdk/application-runtime/common-services/analytics/domain-controls-analytics.d.ts +21 -21
  268. package/dist/react-sdk/application-runtime/hooks/data/use-published-data.d.ts +1 -1
  269. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +12 -0
  270. package/dist/react-sdk/domain-context/common-hooks/use-domain-context.d.ts +3 -0
  271. package/dist/ui-sdk/common/use-viewport-size.d.ts +4 -1
  272. package/dist/ui-sdk/common/use-viewport-size.js +1 -1
  273. package/dist/ui-sdk/components/mobile-controls/index.d.ts +1 -1
  274. package/dist/ui-sdk/theme/types.d.ts +4 -0
  275. package/package.json +5 -8
  276. package/schema/domain-spec.json +24 -0
  277. package/dist/application-spec/generate-application/code/templates/agents-md.js +0 -2
  278. package/dist/application-spec/generate-application/code/templates/common/sdk-content.js +0 -67
  279. package/dist/application-spec/generate-application/code/templates/phoenix-cli-skill.js +0 -1
  280. package/dist/application-spec/generate-application/code/templates/phoenix-feedback-skill.js +0 -31
  281. package/dist/application-spec/generate-application/code/templates/phoenix-skill.js +0 -1
  282. package/dist/configurator/assistant/browser-details.js +0 -1
  283. package/dist/configurator/assistant/graphql-tools.js +0 -1
  284. package/dist/configurator/assistant/index.d.ts +0 -1
  285. package/dist/configurator/assistant/index.js +0 -1
  286. package/dist/configurator/assistant/use-assistant-chat.d.ts +0 -20
  287. package/dist/configurator/assistant/use-assistant-chat.js +0 -1
  288. package/dist/extras/assistant/AssistantChat.d.ts +0 -6
  289. package/dist/extras/assistant/AssistantChat.js +0 -1
  290. package/dist/extras/assistant/index.d.ts +0 -1
  291. package/dist/extras/assistant/index.js +0 -1
  292. /package/dist/application-spec/generate-application/code/templates/{common → skills}/agent-skill.d.ts +0 -0
  293. /package/dist/application-spec/generate-application/code/templates/{common → skills}/agent-skill.js +0 -0
  294. /package/dist/application-spec/generate-application/code/templates/{agents-md.d.ts → skills/agents-md.d.ts} +0 -0
  295. /package/dist/application-spec/generate-application/code/templates/{phoenix-cli-skill.d.ts → skills/phoenix-cli-skill.d.ts} +0 -0
  296. /package/dist/application-spec/generate-application/code/templates/{phoenix-feedback-skill.d.ts → skills/phoenix-feedback-skill.d.ts} +0 -0
  297. /package/dist/application-spec/generate-application/code/templates/{phoenix-review-skill.d.ts → skills/phoenix-review-skill.d.ts} +0 -0
  298. /package/dist/application-spec/generate-application/code/templates/{phoenix-skill.d.ts → skills/phoenix-skill.d.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import type { AccessInfoSelectors, DomainContextSelectors, DomainServicesSelectors, SpecSelectors } from '#package/domain';
2
2
  import type { CommonServicesSelectors } from '#package/react-sdk';
3
3
  import type { AnyConfiguratorExtensionSystems } from './configurator-extension-systems.js';
4
- import { AnimationSystem, type AnyCameraSystem, type AnyCanvasSystem, type AnyDragAndDropSystem, type AnyExportSystem, type AnyImageRenderingSystem, type AnyLayoutSystem, type AnySketchingSystem, CameraControlSystem, GraphQLSystem } from './systems/index.js';
4
+ import { AnimationSystem, type AnyCameraSystem, type AnyCanvasSystem, type AnyDragAndDropSystem, type AnyExportSystem, type AnyImageRenderingSystem, type AnyLayoutSystem, type AnySketchingSystem, AssistantSystem, CameraControlSystem, GraphQLSystem } from './systems/index.js';
5
5
  interface Context {
6
6
  readonly domainContextSelectors: DomainContextSelectors;
7
7
  readonly specSelectors: SpecSelectors;
@@ -31,6 +31,7 @@ export declare const createConfiguratorExtensionSelectors: (context: Context) =>
31
31
  getExportSystem: () => AnyExportSystem;
32
32
  getCameraSystem: () => AnyCameraSystem;
33
33
  getGraphQLSystem: () => GraphQLSystem;
34
+ getAssistantSystem: () => AssistantSystem;
34
35
  };
35
36
  export type ConfiguratorExtensionSelectors = ReturnType<typeof createConfiguratorExtensionSelectors>;
36
37
  export {};
@@ -1 +1 @@
1
- import{AnimationSystem as e,CameraControlSystem as t,CameraSystem as n,CanvasSystem as o,DragAndDropSystem as r,ExportSystem as g,GraphQLSystem as s,ImageRenderingSystem as i,LayoutSystem as a,SketchingSystem as m}from"./systems/index.js";export const createConfiguratorExtensionSelectors=S=>{let{domainContextSelectors:y,specSelectors:u,domainServicesSelectors:C,accessSelectors:w,commonServicesSelectors:c}=S,f={getConfigurationId:()=>w.getConfiguratorConfigurationId(),getAuthorizedClient:()=>c.getConfiguratorAuthorizedClient(),domainContextSelectors:y,domainServicesSelectors:C,specSelectors:u},d={getSystems:()=>k,getCanvasSystem:()=>x,getCameraControlSystem:()=>p,getAnimationSystem:()=>A,getDragAndDropSystem:()=>h,getImageRenderingSystem:()=>j,getLayoutSystem:()=>z,getSketchingSystem:()=>D,getExportSystem:()=>E,getCameraSystem:()=>L,getGraphQLSystem:()=>I},l={...f,configuratorExtensionSelectors:d},x=new o(f),p=new t({configuratorExtensionSelectors:d}),h=new r(l),A=new e(f),E=new g(l),I=new s(f),z=new a(f),D=new m(l),L=new n(l),j=new i(l),k={cameraControlSystem:p,layoutSystem:z,sketchingSystem:D,dragAndDropSystem:h,canvasSystem:x,cameraSystem:L,exportSystem:E,imageRenderingSystem:j,animationSystem:A,graphQLSystem:I};return d};
1
+ import{AnimationSystem as e,AssistantSystem as t,CameraControlSystem as n,CameraSystem as o,CanvasSystem as r,DragAndDropSystem as s,ExportSystem as g,GraphQLSystem as i,ImageRenderingSystem as a,LayoutSystem as m,SketchingSystem as S}from"./systems/index.js";export const createConfiguratorExtensionSelectors=y=>{let{domainContextSelectors:u,specSelectors:w,domainServicesSelectors:C,accessSelectors:c,commonServicesSelectors:f}=y,d={getConfigurationId:()=>c.getConfiguratorConfigurationId(),getAuthorizedClient:()=>f.getConfiguratorAuthorizedClient(),domainContextSelectors:u,domainServicesSelectors:C,specSelectors:w},l={getSystems:()=>G,getCanvasSystem:()=>p,getCameraControlSystem:()=>A,getAnimationSystem:()=>E,getDragAndDropSystem:()=>h,getImageRenderingSystem:()=>v,getLayoutSystem:()=>L,getSketchingSystem:()=>j,getExportSystem:()=>I,getCameraSystem:()=>k,getGraphQLSystem:()=>z,getAssistantSystem:()=>D},x={...d,configuratorExtensionSelectors:l},p=new r(d),A=new n({configuratorExtensionSelectors:l}),h=new s(x),E=new e(d),I=new g(x),z=new i(d),D=new t(x),L=new m(d),j=new S(x),k=new o(x),v=new a(x),G={cameraControlSystem:A,layoutSystem:L,sketchingSystem:j,dragAndDropSystem:h,canvasSystem:p,cameraSystem:k,exportSystem:I,imageRenderingSystem:v,animationSystem:E,graphQLSystem:z,assistantSystem:D};return l};
@@ -0,0 +1,168 @@
1
+ import type { AnyClientTool } from '@tanstack/ai';
2
+ import { type UIMessage } from '@tanstack/ai-client';
3
+ import { type ObservableWithLastValue } from '@salsita-ai/common/utils';
4
+ import type { AuthorizedPhoenixApiClient } from '#package/client';
5
+ import type { DomainContextSelectors, DomainServicesSelectors, InitializableSystem, SpecSelectors } from '#package/domain';
6
+ import type { ConfiguratorExtensionSelectors } from '../../selectors.js';
7
+ /** A tool of the assistant that the model calls and the browser runs, as the chat client accepts it. */
8
+ export type AssistantClientTool = AnyClientTool;
9
+ /**
10
+ * Constructor context of an `AssistantSystem`: whole selector objects, like its peers take them, so
11
+ * the tools reach the store, the session mode and the `GraphQLSystem` when they are called, and the
12
+ * chat reaches the server of the current session when it sends a request.
13
+ */
14
+ export interface AssistantSystemContext {
15
+ readonly configuratorExtensionSelectors: ConfiguratorExtensionSelectors;
16
+ readonly domainContextSelectors: DomainContextSelectors;
17
+ readonly domainServicesSelectors: DomainServicesSelectors;
18
+ readonly specSelectors: SpecSelectors;
19
+ readonly getConfigurationId: () => string;
20
+ readonly getAuthorizedClient: () => AuthorizedPhoenixApiClient<any, any, any>;
21
+ }
22
+ /**
23
+ * The conversation with the assistant as the panel paints it. One value is published per change, so
24
+ * it changes with every chunk of a streaming reply.
25
+ */
26
+ export interface AssistantChatSnapshot {
27
+ /** The thread as painted: hydrated from the server, then streamed. */
28
+ readonly messages: ReadonlyArray<UIMessage>;
29
+ /** True while a round of a reply streams or its browser tools run; false in the pause before the next round. */
30
+ readonly isLoading: boolean;
31
+ readonly error: Error | undefined;
32
+ /**
33
+ * HTTP status of the latest request the server answered with a failure, for telling a refusal from
34
+ * a dropped connection when `error` is set. Nothing when the latest answered request succeeded or
35
+ * the server was never reached.
36
+ */
37
+ readonly failedStatus: number | undefined;
38
+ /** False when the server said this session may read the thread but not add to it. */
39
+ readonly isContinuable: boolean;
40
+ /** False until the first request of the chat, the thread read, was answered or failed. */
41
+ readonly isLoaded: boolean;
42
+ }
43
+ /**
44
+ * The built-in assistant panel as a layout and the toolbar button see it: whether it is open, and
45
+ * the few facts about it they render. It changes only when one of those does.
46
+ */
47
+ export interface AssistantPanelSnapshot {
48
+ readonly open: boolean;
49
+ /** True when a reply finished while the panel was closed and the panel was not opened since. */
50
+ readonly hasUnreadReply: boolean;
51
+ /** True while the message input has focus; a phone layout gives the panel the whole screen then. */
52
+ readonly isInputFocused: boolean;
53
+ }
54
+ /**
55
+ * Owns the conversational assistant of a configurator: its client tools (`getBrowserDetails`,
56
+ * `query` and `apply`), the chat with the server, the state of the built-in panel, and the record
57
+ * of what changed in the configuration since the agent last acted, which every user message carries.
58
+ *
59
+ * The server owns the thread, so the chat keeps nothing in browser storage: its client hydrates
60
+ * from the server and rejoins a reply that is still streaming.
61
+ *
62
+ * WORKAROUND: the runtime cannot tell a system that its configuration was left, and a system cannot
63
+ * observe the application spec. Until it can do both, the chat is started and ended from outside,
64
+ * through {@link AssistantSystem.activate}; see there for what that costs.
65
+ */
66
+ export declare class AssistantSystem implements InitializableSystem {
67
+ private readonly _context;
68
+ private readonly _tools;
69
+ private readonly _chat;
70
+ private readonly _panel;
71
+ /** Identity of the current `activate()` call; nothing while the chat is not active. */
72
+ private _activation;
73
+ /** The chat of the current activation; nothing while no chat is active. */
74
+ private _current;
75
+ /**
76
+ * Chats that have ended while their reply was still running and are finishing it in the
77
+ * background. Each leaves the set when its reply ends or when a new chat takes its thread over.
78
+ */
79
+ private readonly _finishing;
80
+ private readonly _recorder;
81
+ /** The recorder's subscription to the committed emissions of the store; made by the first `initialize()`. */
82
+ private _recording;
83
+ constructor(_context: AssistantSystemContext);
84
+ /** The conversation. It never completes, and holds the empty chat whenever no chat is active. */
85
+ readonly chat$: ObservableWithLastValue<AssistantChatSnapshot>;
86
+ /** The state of the built-in panel. It never completes. */
87
+ readonly panel$: ObservableWithLastValue<AssistantPanelSnapshot>;
88
+ /**
89
+ * A new configuration load: the chat of the previous one is deactivated and both observables go
90
+ * back to their empty values. An activation that is still held outlives it and gets the chat of
91
+ * the new load, because the page that holds it can be mounted before this runs: a configuration
92
+ * that is entered again renders its page at once and initializes from an effect after the page's.
93
+ *
94
+ * Accepted cost: on such a re-entry the thread is read twice. The page's `activate()` has already
95
+ * created a client and started its read when this runs, and that client is replaced here, because
96
+ * the system cannot tell that it was created for the load that is only now being initialized.
97
+ *
98
+ * The change recorder starts over from the loaded state, so the first message after a load says
99
+ * that the changes are unavailable. The store is retained across loads and has already emitted the
100
+ * loaded state when this runs, so one subscription serves every load.
101
+ */
102
+ initialize: () => void;
103
+ /**
104
+ * Starts the chat of the loaded configuration: creates the client, which reads the thread from the
105
+ * server and rejoins a reply that is still running. The returned function ends it.
106
+ *
107
+ * WORKAROUND, to be replaced once the runtime has teardown and spec observation for systems. Two
108
+ * things a system cannot learn by itself are decided by the caller, the loaded configurator page:
109
+ *
110
+ * - Whether the application spec has an `assistant` section, also after editor hot reload added or
111
+ * removed it. The page calls this only while the section is there; the system does not check.
112
+ * - That the user left the configuration. No `initialize()` follows a route change, so without the
113
+ * returned function the chat would go on working on a configuration nobody looks at.
114
+ *
115
+ * Ending the chat does not cut a running reply off: it finishes in the background, where every
116
+ * tool call it still makes is answered with a refusal that tells the model to stop, and it no
117
+ * longer reaches `chat$` or `panel$`. A tool call that is already running when the chat ends is
118
+ * not interrupted, so an `apply` that has started may still commit. The background reply keeps
119
+ * the configuration and the authorized client it started with.
120
+ *
121
+ * The background reply ends early when a new chat starts for the same configuration, which is
122
+ * what opening the configuration again does: see `_takeOverThread`.
123
+ */
124
+ activate: () => (() => void);
125
+ /**
126
+ * Sends a user message with the record of what changed in the configuration since the agent last
127
+ * acted, which the server puts in front of the message for the model. The record is taken only
128
+ * when there is a chat to send it on, because taking it moves its baseline.
129
+ */
130
+ send: (content: string) => Promise<void>;
131
+ /**
132
+ * Stops the reply on both sides. Closing the stream alone keeps the server run going, because a
133
+ * closed connection is also how a reload looks and a reload must be able to rejoin the run.
134
+ */
135
+ stop: () => Promise<void>;
136
+ /**
137
+ * Loads the thread from the server again, rejoining a reply that is still running there. It never
138
+ * sends a message again, because the server may already hold it; a message that did not reach the
139
+ * server is therefore gone from the thread and has to be written again.
140
+ */
141
+ reloadThread: () => void;
142
+ setOpen: (open: boolean) => void;
143
+ setInputFocused: (isInputFocused: boolean) => void;
144
+ private _committedState;
145
+ private _patchPanel;
146
+ /**
147
+ * Disconnects every chat that is still finishing a reply on the thread of the given configuration,
148
+ * so the chat that is about to start is the only one on it.
149
+ *
150
+ * Two clients on one thread would both follow the same server run: the new one rejoins it when it
151
+ * reads the thread. Both would then handle the run's next tool call, the old one with a refusal
152
+ * and the new one for real, and both would send the result back, of which the server accepts one.
153
+ * The model could be told that nothing was run about a change that was made.
154
+ *
155
+ * Stopping a client only closes its stream in the browser. Nothing is cancelled on the server, so
156
+ * the run goes on there and the new chat rejoins it. A chat that finishes a reply for another
157
+ * configuration is on another thread and is left alone.
158
+ */
159
+ private _takeOverThread;
160
+ private _startClient;
161
+ /**
162
+ * Lets go of the current chat and puts the empty chat in its place. The client itself is left as
163
+ * it is: whether it finishes its reply or is cut off is the caller's decision.
164
+ */
165
+ private _retireChat;
166
+ /** Ends the current chat without cutting its reply off. */
167
+ private _endChat;
168
+ }
@@ -0,0 +1 @@
1
+ import{ChatClient as t,fetchServerSentEvents as e}from"@tanstack/ai-client";import{BehaviorSubject as i}from"rxjs";import{skip as s}from"rxjs/operators";import{isShallowEqual as a}from"@salsita-ai/common/utils";import{createBrowserDetailsClientTool as n}from"./browser-details.js";import{ChangeRecorder as r}from"./change-recorder.js";import{createApplyClientTool as o,createQueryClientTool as c}from"./graphql-tools.js";let l=Object.freeze({messages:Object.freeze([]),isLoading:!1,error:void 0,failedStatus:void 0,isContinuable:!0,isLoaded:!1}),h=Object.freeze({open:!1,hasUnreadReply:!1,isInputFocused:!1}),d=Object.freeze({}),u=Object.freeze({results:Object.freeze([]),committed:!1,sideEffects:Object.freeze([]),message:"Nothing was run, because the user closed the configurator while this reply was being written. Do not call any more tools in this reply and end it now. The tools work again as soon as the user writes the next message."}),_=t=>{if(t.getError())return!0;let e=t.getMessages().at(-1)?.parts.at(-1);return e?.type!=="tool-call"&&e?.type!=="tool-result"};export class AssistantSystem{_context;_tools;_chat=new i(l);_panel=new i(h);_activation=null;_current=null;_finishing=new Set;_recorder;_recording=null;constructor(t){this._context=t;let{configuratorExtensionSelectors:e,domainContextSelectors:i,domainServicesSelectors:s,specSelectors:a}=t;this._recorder=new r({maxEntities:30},{readers:{selectedEntityId:t=>i.getNavigationSystem().getActiveSelectionState(t),navigationState:t=>i.getNavigationSystem().getActiveNavigationState(t),viewState:t=>i.getNavigationSystem().getActiveViewState(t),toolState:t=>i.getNavigationSystem().getActiveToolState(t)},getExposedFields:()=>a.getAssistant()?.fields??d,resolveLocalizable:t=>s.getResolveLocalizable()(t)});let l={getGraphQLSystem:()=>e.getGraphQLSystem(),getStoreSystem:()=>i.getStoreSystem(),getActiveMode:()=>i.getActiveMode(),clearChanges:()=>this._recorder.clear(this._committedState())};this._tools=[n(),c(l),o(l)]}chat$=this._chat;panel$=this._panel;initialize=()=>{let{committed:t}=this._context.domainContextSelectors.getStoreSystem().stateSlices;this._recording??=t.pipe(s(1)).subscribe(this._recorder.record),this._recorder.reset(t.value.state),this._endChat(),this._patchPanel(h),this._activation&&this._startClient()};activate=()=>{this._endChat();let t={};return this._activation=t,this._startClient(),()=>{this._activation===t&&(this._activation=null,this._endChat())}};send=async t=>{let e=this._current;if(!e)return;let i={changes:this._recorder.take(this._committedState())};await e.client.sendMessage({content:t,metadata:i})};stop=async()=>{let t=this._current;if(!t)return;let e=t.client.getCurrentRunId();if(t.client.stop(),null!=e)try{let i=t.getConnection();await fetch(`${i.url}/runs/${encodeURIComponent(e)}/cancel`,{method:"POST",headers:await i.headers()})}catch(t){this._context.domainServicesSelectors.getLogger().warn("Could not cancel the assistant run on the server",{error:t})}};reloadThread=()=>{if(!this._activation)return;let t=this._retireChat();t?.client.stop(),t?.dispose(),this._recorder.reset(this._committedState()),this._startClient()};setOpen=t=>{this._patchPanel(t?{open:t,hasUnreadReply:!1}:{open:t,isInputFocused:!1})};setInputFocused=t=>{this._patchPanel({isInputFocused:t})};_committedState=()=>this._context.domainContextSelectors.getStoreSystem().stateSlices.committed.value.state;_patchPanel=t=>{let e={...this._panel.value,...t};a(e,this._panel.value)||this._panel.next(e)};_takeOverThread=t=>{for(let e of this._finishing)e.configurationId===t&&(e.client.stop(),e.dispose())};_startClient=()=>{let i={active:!0},s=this._context.getConfigurationId();this._takeOverThread(s);let n=this._context.getAuthorizedClient(),r=()=>n.getAssistantChatConnection(s),o=t=>{if(!i.active)return;let e={...this._chat.value,...t};a(e,this._chat.value)||this._chat.next(e)},c=async(t,e)=>{let i=await fetch(t,e).finally(()=>o({isLoaded:!0})),s=i.headers.get("x-assistant-thread-continuable");return o({failedStatus:i.ok?void 0:i.status,...null!=s&&{isContinuable:"false"!==s}}),i},l=async()=>({headers:await r().headers(),fetchClient:c}),h=e(()=>r().url,l),d=e(()=>`${r().url}/runs`,l),g=this._tools.map(t=>({...t,execute:(e,s)=>i.active?t.execute?.(e,s):u})),p=new t({threadId:`assistant:${s}`,connection:{...h,joinRun:d.joinRun},persistence:!0,history:{pageSize:50},tools:g,onMessagesChange:t=>o({messages:t}),onLoadingChange:t=>{let e=i.active&&this._chat.value.isLoading;o({isLoading:t}),!t&&_(p)&&(i.active?e&&!this._panel.value.open&&this._patchPanel({hasUnreadReply:!0}):v())},onErrorChange:t=>{i.active&&t!==this._chat.value.error&&(o({error:t}),t&&this._context.domainServicesSelectors.getLogger().error("The assistant request failed",{status:this._chat.value.failedStatus},t))}}),m=!1,v=()=>{m||(m=!0,this._finishing.delete(f),p.dispose())},f={client:p,configurationId:s,generation:i,getConnection:r,dispose:v};this._current=f,p.attach()};_retireChat=()=>{let t=this._current;return this._current=null,t&&(t.generation.active=!1),this._chat.value!==l&&this._chat.next(l),t};_endChat=()=>{let t=this._retireChat();if(t){if(!t.client.getIsLoading()&&_(t.client))return void t.dispose();this._finishing.add(t)}}}
@@ -2,7 +2,7 @@
2
2
  * Browser implementation of the assistant's `getBrowserDetails` tool: draws the browser details onto
3
3
  * a canvas and returns the capture as a PNG image part, so the model reads them from an image.
4
4
  */
5
- export declare const browserDetailsClientTool: import("@tanstack/ai").ClientTool<{
5
+ export declare const createBrowserDetailsClientTool: () => import("@tanstack/ai").ClientTool<{
6
6
  type: string;
7
7
  properties: {};
8
8
  required: never[];
@@ -0,0 +1 @@
1
+ import{browserDetailsTool as e}from"@salsita-ai/contracts/assistant";let t=e=>{let t=[];for(let i=0;i<e.length;i+=64)t.push(e.slice(i,i+64));return t};export const createBrowserDetailsClientTool=()=>e.client(()=>{let e=(()=>{let e=[`User agent: ${navigator.userAgent}`,`Viewport: ${window.innerWidth} x ${window.innerHeight} px`,`Screen: ${window.screen.width} x ${window.screen.height} px`,`Device pixel ratio: ${window.devicePixelRatio}`,`Language: ${navigator.language}`,`Time zone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`].flatMap(t),i=document.createElement("canvas");i.width=720,i.height=48+26*e.length;let n=i.getContext("2d");if(!n)throw Error("The browser details capture failed because the canvas has no 2D context. Retry in another browser.");return n.fillStyle="#ffffff",n.fillRect(0,0,i.width,i.height),n.fillStyle="#111111",n.font="16px monospace",n.textBaseline="top",e.forEach((e,t)=>n.fillText(e,24,24+26*t)),i.toDataURL("image/png")})();return[{type:"image",source:{type:"data",value:e.slice(e.indexOf(",")+1),mimeType:"image/png"}}]});
@@ -0,0 +1,85 @@
1
+ import type { Localizable } from '@salsita-ai/common/utils';
2
+ import { type $DomainEntity, type AssistantManifest, type DomainEntity, type DomainState, type StateEmission } from '#package/domain';
3
+ type AnyDomainEntity = DomainEntity<string>;
4
+ type AnyDomainState = DomainState<AnyDomainEntity>;
5
+ type AnyStateEmission = StateEmission<AnyDomainEntity, Record<string, $DomainEntity<AnyDomainEntity>>>;
6
+ /**
7
+ * What the change record compares besides the entities: the values the `Query` root of the
8
+ * assistant's schema exposes as `selectedEntity` and `navigation`, read off a state.
9
+ */
10
+ export interface ChangeRecorderStateReaders {
11
+ readonly selectedEntityId: (state: AnyDomainState) => string | undefined;
12
+ readonly navigationState: (state: AnyDomainState) => string | undefined;
13
+ readonly viewState: (state: AnyDomainState) => string | undefined;
14
+ readonly toolState: (state: AnyDomainState) => string | undefined;
15
+ }
16
+ /** What a {@link ChangeRecorder} reads from the running configurator. */
17
+ export interface ChangeRecorderContext {
18
+ readonly readers: ChangeRecorderStateReaders;
19
+ /**
20
+ * The field table of the assistant manifest: per entity type of the schema, each exposed field
21
+ * name with the stored field or expression it reads. The record lists nothing the table does not
22
+ * hold, so the agent is shown only what it could also query. Read at every take, because editor
23
+ * hot reload replaces the manifest.
24
+ */
25
+ readonly getExposedFields: () => AssistantManifest['fields'];
26
+ readonly resolveLocalizable: (message: Localizable) => string;
27
+ }
28
+ export interface ChangeRecorderProps {
29
+ /** More differing entities than this are counted and not listed. */
30
+ readonly maxEntities: number;
31
+ }
32
+ /**
33
+ * Records what changed in the committed configuration since the assistant agent last acted, and
34
+ * prints it as the change record a user message carries to the agent.
35
+ *
36
+ * The record is a net diff: the committed state at the last take or clear is kept as the baseline
37
+ * and compared with the current one, so a value that was changed and changed back is not listed.
38
+ * Of the emissions in between, only the last update that touched each entity and each entity field
39
+ * is kept, to say why a line is there. Selection and navigation are read off the two states and
40
+ * never from their updates.
41
+ *
42
+ * Only what the assistant manifest exposes is listed: entities of the types its schema has, and of
43
+ * their stored fields the exposed ones, under their exposed names and localized like the schema
44
+ * returns them. A change of anything else is not in the record.
45
+ */
46
+ export declare class ChangeRecorder {
47
+ private readonly _props;
48
+ private readonly _context;
49
+ private _baseline;
50
+ /** True from a reset until the first take: what happened before the baseline is not known. */
51
+ private _isUnavailable;
52
+ private readonly _touches;
53
+ private _lastReset;
54
+ private _wasUndoneOrRedone;
55
+ constructor(_props: ChangeRecorderProps, _context: ChangeRecorderContext);
56
+ /** Starts over from this committed state; the next take reports `unavailable`. */
57
+ reset: (state: AnyDomainState) => void;
58
+ /** Records one committed emission: the last update that touched each entity and field, or the reset. */
59
+ record: (emission: AnyStateEmission) => void;
60
+ /** The change record since the last take or clear, as the prompt describes it; moves the baseline. */
61
+ take: (state: AnyDomainState) => string;
62
+ /** Forgets what was recorded and makes this committed state the baseline. */
63
+ clear: (state: AnyDomainState) => void;
64
+ private _touchesOf;
65
+ private _touchFields;
66
+ private _print;
67
+ private _diffStates;
68
+ /** Added and modified entities in the order of the current state, then the deleted ones in the order they had. */
69
+ private _diffEntities;
70
+ /**
71
+ * The lines of the exposed stored fields that differ, in manifest order. A field no update
72
+ * touched since its entity was created or replaced under an id that existed before is explained
73
+ * by that creation.
74
+ */
75
+ private _diffFields;
76
+ /** Why a line is in the record; a line no recorded update explains was left by a reset. */
77
+ private _comment;
78
+ /**
79
+ * What the agent is told about an update, else what the user was told, else the update tag. As
80
+ * for `sideEffects`, an `agentHint` replaces the `hint` whatever it holds: `null` or empty text
81
+ * hides the wording from the agent, not the change, so the tag stands in.
82
+ */
83
+ private _explain;
84
+ }
85
+ export {};
@@ -0,0 +1 @@
1
+ import{match as e}from"ts-pattern";import{isUndoRedoReset as t}from"#package/domain";import{localize as i}from"../graphql-system/graphql-system.js";let s=[["selected","selectedEntityId"],["navigationState","navigationState"],["viewState","viewState"],["toolState","toolState"]],n=e=>e.length>80?`${e.slice(0,80)}…`:e,a=e=>void 0===e||""===e?"none":e,r=e=>new Map(Object.entries(e)),l=["id","type"];export class ChangeRecorder{_props;_context;_baseline;_isUnavailable=!0;_touches=new Map;_lastReset;_wasUndoneOrRedone=!1;constructor(e,t){this._props=e,this._context=t}reset=e=>{this.clear(e),this._isUnavailable=!0};record=i=>{if("reset"===i.kind){this._touches.clear(),this._lastReset=t(i)?"undo/redo":"reset",this._wasUndoneOrRedone||="undo/redo"===this._lastReset;return}for(let t of i.updates)e(t).with({kind:"createDomainEntity"},e=>{this._touchesOf(e.entityId).created=e}).with({kind:"deleteEntity"},e=>{let t=this._touchesOf(e.entityId);t.fields.clear(),t.deleted=e}).with({kind:"modifyDomainEntity"},e=>{this._touchFields(e.entityId,Object.keys(e.stateDelta),e)}).with({kind:"replaceDomainEntity"},e=>{let{replacementEntity:t}=e;this._touchesOf(e.entityId).fields.clear(),this._touchesOf(e.entityId).deleted=e,this._touchesOf(t.id).created=e}).with({kind:"createEphemeralRecord"},{kind:"modifyEphemeralRecord"},{kind:"deleteEphemeralRecord"},()=>{}).exhaustive()};take=e=>{let t=this._print(e);return this.clear(e),this._isUnavailable=!1,t};clear=e=>{this._baseline=e,this._touches.clear(),this._lastReset=void 0,this._wasUndoneOrRedone=!1};_touchesOf=e=>{let t=this._touches.get(e);if(t)return t;let i={created:void 0,deleted:void 0,fields:new Map};return this._touches.set(e,i),i};_touchFields=(e,t,i)=>{let s=this._touchesOf(e);for(let e of t)s.fields.set(e,i)};_print=e=>{let t=this._baseline;if(this._isUnavailable||!t)return"[changes: unavailable]";let{lines:i,count:s}=this._diffEntities(t,e);if(s>this._props.maxEntities)return`[changes: too many (${s})]`;let n=[...this._diffStates(t,e),...i];return 0===n.length?"[changes: none]":["[changes]",...this._wasUndoneOrRedone?["reset: undo/redo"]:[],...n].join("\n")};_diffStates=(e,t)=>s.flatMap(([i,s])=>{let n=a(this._context.readers[s](e)),r=a(this._context.readers[s](t));return n===r?[]:[`${i}: ${n} -> ${r}`]});_diffEntities=(e,t)=>{let i=this._context.getExposedFields(),s=new Map(e.entities.map(e=>[e.id,e])),n=new Set(t.entities.map(e=>e.id)),a=t.entities.flatMap(e=>{let t=i[e.type],n=s.get(e.id);if(!t)return[];if(!n)return[[`A ${e.id}${this._comment(this._touches.get(e.id)?.created)}`]];let a=n===e?[]:this._diffFields(n,e,t);return a.length>0?[[`M ${e.id}`,...a]]:[]}),r=e.entities.filter(e=>i[e.type]&&!n.has(e.id)).map(e=>[`D ${e.id}${this._comment(this._touches.get(e.id)?.deleted)}`]);return{lines:[...a,...r].flat(),count:a.length+r.length}};_diffFields=(e,t,s)=>{let a=r(e),d=r(t),o=this._touches.get(t.id);return Object.entries(s).flatMap(([e,{source:t,localized:s}])=>{if(!(!t.startsWith("$")&&!l.includes(t)))return[];let r=e=>{let t;return void 0===(t=s?i(e,this._context.resolveLocalizable):e)?"undefined":JSON.stringify(t)},h=r(a.get(t)),c=r(d.get(t));return h===c?[]:[` ${e}: ${n(h)} -> ${n(c)}${this._comment(o?.fields.get(t)??o?.created)}`]})};_comment=e=>e?` # ${this._explain(e).replace(/\s*\n\s*/g," ")}`:this._lastReset?` # ${this._lastReset}`:"";_explain=({agentHint:e,hint:t,updateTag:i})=>void 0!==e?null===e||""===e?i:e:t?this._context.resolveLocalizable(t):i}
@@ -1,14 +1,13 @@
1
1
  import type { DomainEntity, DomainMode, DomainState } from '#package/domain';
2
- import type { GraphQLSystem } from '../application-runtime/index.js';
2
+ import type { GraphQLSystem } from '../graphql-system/graphql-system.js';
3
3
  /**
4
- * What the assistant's client tools need from the running configurator. The chat hook passes it as
5
- * the client context of `useChat`, which captures it when a request starts: a tool call reads the
6
- * systems as they were when its reply began streaming. A runtime replaced in between (dev hot
7
- * reload) fails that call, which is accepted.
4
+ * What the assistant's client tools need from the running configurator, as accessors they call when
5
+ * the model calls them: a tool call always runs against the systems and the session mode current at
6
+ * that moment, never against ones captured when its reply began streaming.
8
7
  */
9
- export interface AssistantToolContext {
10
- readonly graphQLSystem: Pick<GraphQLSystem, 'prepare' | 'execute'>;
11
- readonly storeSystem: {
8
+ export interface AssistantToolDependencies {
9
+ readonly getGraphQLSystem: () => Pick<GraphQLSystem, 'prepare' | 'execute' | 'readSideEffects'>;
10
+ readonly getStoreSystem: () => {
12
11
  readonly isInitialized: boolean;
13
12
  readonly stateSlices: {
14
13
  readonly committed: {
@@ -19,13 +18,16 @@ export interface AssistantToolContext {
19
18
  };
20
19
  };
21
20
  readonly getActiveMode: () => DomainMode;
21
+ /** Forgets the changes recorded for the next change record, so it never lists what the agent itself changed. */
22
+ readonly clearChanges: () => void;
22
23
  }
23
24
  /**
24
25
  * Browser implementation of the assistant's `query` tool: prepares the operations from the
25
26
  * committed state and drops their emissions, so mutations in the list show a hypothetical state and
26
- * the configuration, the undo stack and the saved revision stay as they were.
27
+ * the configuration, the undo stack and the saved revision stay as they were. The side effects of
28
+ * the dropped emissions are still reported, as what the changes would do.
27
29
  */
28
- export declare const queryClientTool: import("@tanstack/ai").ClientTool<{
30
+ export declare const createQueryClientTool: (dependencies: AssistantToolDependencies) => import("@tanstack/ai").ClientTool<{
29
31
  type: string;
30
32
  properties: {
31
33
  operations: {
@@ -53,7 +55,7 @@ export declare const queryClientTool: import("@tanstack/ai").ClientTool<{
53
55
  };
54
56
  };
55
57
  required: string[];
56
- }, undefined, "query", AssistantToolContext, false, undefined> & {
58
+ }, undefined, "query", unknown, false, undefined> & {
57
59
  inputSchema: {
58
60
  type: string;
59
61
  properties: {
@@ -89,11 +91,13 @@ export declare const queryClientTool: import("@tanstack/ai").ClientTool<{
89
91
  /**
90
92
  * Browser implementation of the assistant's `apply` tool: hands the operations to
91
93
  * `GraphQLSystem.execute`, which commits their changes as one undo step, and returns its result,
92
- * including why nothing was committed when a commit was held or lost to a conflict. Only an owner
93
- * session may change the configuration; the check lives here and not in the system, whose other
94
- * caller, the editor panel, has its own rules.
94
+ * including why nothing was committed when a commit was held or lost to a conflict. A commit clears
95
+ * the recorded changes; a held one does not, so it shows in the next change record once the user
96
+ * approves it. Only an owner
97
+ * session may change the configuration; the check lives here and not in the `GraphQLSystem`, whose
98
+ * other caller, the editor panel, has its own rules.
95
99
  */
96
- export declare const applyClientTool: import("@tanstack/ai").ClientTool<{
100
+ export declare const createApplyClientTool: (dependencies: AssistantToolDependencies) => import("@tanstack/ai").ClientTool<{
97
101
  type: string;
98
102
  properties: {
99
103
  operations: {
@@ -121,7 +125,7 @@ export declare const applyClientTool: import("@tanstack/ai").ClientTool<{
121
125
  };
122
126
  };
123
127
  required: string[];
124
- }, undefined, "apply", AssistantToolContext, false, undefined> & {
128
+ }, undefined, "apply", unknown, false, undefined> & {
125
129
  inputSchema: {
126
130
  type: string;
127
131
  properties: {
@@ -0,0 +1 @@
1
+ import{AssistantGraphQLToolInput as e,applyTool as t,queryTool as o}from"@salsita-ai/contracts/assistant";let a=e=>({results:[],committed:!1,message:e,sideEffects:[]}),i=e=>JSON.parse(JSON.stringify(e)),s=t=>{let o=e.safeParse(t);if(!o.success){let e=o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ");return{ok:!1,refusal:a(`Nothing was run, because the tool input is not a list of operations (${e}). Send { "operations": [{ "query": "<one GraphQL operation>" }] } with at least one item.`)}}return{ok:!0,operations:o.data.operations.map(({query:e,variables:t,operationName:o})=>({document:e,variables:t??void 0,operationName:o??void 0}))}};export const createQueryClientTool=e=>o.client(async t=>{let o=s(t);if(!o.ok)return o.refusal;let n=e.getStoreSystem();if(!n.isInitialized)return a("Nothing was run, because the configuration is not loaded yet, so there is no state to query. Call the tool again once the configurator has finished loading.");let r=e.getGraphQLSystem(),l=await r.prepare(n.stateSlices.committed.value.state,o.operations);return i({results:l.map(({result:e})=>e),committed:!1,sideEffects:r.readSideEffects(l)})});export const createApplyClientTool=e=>t.client(async t=>{if("owner"!==e.getActiveMode())return a("Nothing was run, because changes cannot be applied in this session: the user may view this configuration but not change it. The query tool still works here, including mutations, to show what a change would do without keeping it.");let o=s(t);if(!o.ok)return o.refusal;let n=await e.getGraphQLSystem().execute(o.operations);return n.committed&&e.clearChanges(),i(n)});
@@ -47,12 +47,14 @@ export declare const createEntityMeta: (source: EntityMetaSource, context: Entit
47
47
  root: null;
48
48
  changedEntities: never[];
49
49
  deletedEntityIds: never[];
50
+ sideEffects: never[];
50
51
  errors: readonly DryRunError[];
51
52
  } | {
52
53
  ok: boolean;
53
54
  root: unknown;
54
55
  changedEntities: {}[];
55
56
  deletedEntityIds: DomainEntityId<string>[];
57
+ sideEffects: readonly string[];
56
58
  errors: never[];
57
59
  } | null;
58
60
  }[];
@@ -71,12 +73,14 @@ export declare const createEntityMeta: (source: EntityMetaSource, context: Entit
71
73
  root: null;
72
74
  changedEntities: never[];
73
75
  deletedEntityIds: never[];
76
+ sideEffects: never[];
74
77
  errors: readonly DryRunError[];
75
78
  } | {
76
79
  ok: boolean;
77
80
  root: unknown;
78
81
  changedEntities: {}[];
79
82
  deletedEntityIds: DomainEntityId<string>[];
83
+ sideEffects: readonly string[];
80
84
  errors: never[];
81
85
  } | null;
82
86
  }[];
@@ -1 +1 @@
1
- import{findAssistantFieldOptions as e,findAssistantRecipeGroups as t,modifyEntityUpdateTag as i,prepareAssistantRecipeUpdates as r,readAssistantFieldOptions as n,readAssistantRecipeGroup as s,systemUpdateFactory as a}from"#package/domain";let o=e=>({ok:!1,root:null,changedEntities:[],deletedEntityIds:[],errors:e}),d=(e,{state:t},{recipes:i,prepareStateEmission:r,find$Entity:n})=>{let{validity:s,state:a}=r(e,t,"committed");if(s.valid){let e=new Map(t.entities.map(e=>[e.id,e])),i=new Set(a.entities.map(e=>e.id));return{ok:!0,root:n(a.rootEntityId,a),changedEntities:a.entities.filter(t=>e.get(t.id)!==t).flatMap(e=>n(e.id,a)??[]),deletedEntityIds:t.entities.map(e=>e.id).filter(e=>!i.has(e)),errors:[]}}return o("uncaught"===s.severity?[{type:"uncaught",entityId:null,message:s.error.message}]:s.entityViolations.filter(({violation:e})=>e.severity===s.severity).map(({entityId:e,violation:t})=>({type:t.type,entityId:e,message:void 0===t.message?null:i.resolveLocalizable(t.message)})))};export const createEntityMeta=(l,c)=>{let p=e=>()=>t(c.manifest,e,l.entity.type).flatMap(t=>{let i=s(e,t,l.entity.id,l.state,c.recipes);return i?[((e,t,i)=>{let{group:n,label:s,description:a,recipes:l}=e,{resolveLocalizable:c}=i.recipes;return{groupId:n.groupId,name:void 0===s?n.instanceName:c(s),description:n.description??(void 0===a?null:c(a)),recipes:l.map(n=>((e,t,i,n)=>{let{key:s,item:a}=e,{resolveLocalizable:l}=n.recipes;return{key:s,name:l(a.title),description:void 0===a.description?null:l(a.description),imageUrl:void 0===a.thumbnailUrl?null:l(a.thumbnailUrl),dryRun:()=>t.group.dryRuns?((e,t,i,n)=>{let{updates:s,error:a}=((e,t,{entity:i,state:n},{recipes:s})=>{try{return{updates:r(e,t,i.id,n,s),error:void 0}}catch(e){if("child"===t.target.kind)throw e;return{updates:void 0,error:e instanceof Error?e.message:String(e)}}})(e,t,i,n);return void 0===s?o([{type:"uncaught",entityId:null,message:a}]):d(s,i,n)})(e,t,i,n):null}})(n,e,t,i))}})(i,l,c)]:[]});return{...Object.fromEntries(e(c.manifest,l.entity.type).map(e=>[e.edgeName,()=>n(e,l.entity.id,l.state,c.recipes)?.map(t=>({...t,dryRun:()=>e.dryRuns?(({field:e},{value:t},r,n)=>{let{entity:s}=r,l=Object.entries(Object.entries(n.manifest.writableFields??{}).find(([e])=>e===s.type)?.[1]??{}).find(([t])=>t===e)?.[1];if(void 0===l)throw Error(`The options of "${e}" on "${s.id}" cannot be dry-run: the schema lists them, but the configurator has no writable field "${e}" on ${s.type}. Regenerate the application so the schema matches the configurator.`);let c=r.state.entities.find(e=>e.id===s.id),p={[l]:t},u=c?n.validateEntity({...c,...p}):{success:!0};return u.success?d([a.DomainEntityModification({entityId:s.id,stateDelta:p,updateTag:i})],r,n):o([{type:"uncaught",entityId:s.id,message:`Writing ${JSON.stringify(t)} to "${e}" would make "${s.id}" an invalid ${s.type}: ${u.errors.join("; ")}.${e===l?"":` The message names the stored property: ${e} is stored as ${l}.`} The option comes from the configurator and cannot be corrected from here, so do not write it.`}])})(e,t,l,c):null}))??null])),childRecipes:p("child"),replaceRecipes:p("replace")}};
1
+ import{findAssistantFieldOptions as e,findAssistantRecipeGroups as t,modifyEntityUpdateTag as i,prepareAssistantRecipeUpdates as r,readAssistantFieldOptions as n,readAssistantRecipeGroup as s,systemUpdateFactory as a}from"#package/domain";import{readSideEffects as o}from"./side-effects.js";let d=e=>({ok:!1,root:null,changedEntities:[],deletedEntityIds:[],sideEffects:[],errors:e}),l=(e,{state:t},{recipes:i,prepareStateEmission:r,find$Entity:n})=>{let{validity:s,state:a,updates:l}=r(e,t,"committed");if(s.valid){let e=new Map(t.entities.map(e=>[e.id,e])),r=new Set(a.entities.map(e=>e.id));return{ok:!0,root:n(a.rootEntityId,a),changedEntities:a.entities.filter(t=>e.get(t.id)!==t).flatMap(e=>n(e.id,a)??[]),deletedEntityIds:t.entities.map(e=>e.id).filter(e=>!r.has(e)),sideEffects:o(l,i.resolveLocalizable),errors:[]}}return d("uncaught"===s.severity?[{type:"uncaught",entityId:null,message:s.error.message}]:s.entityViolations.filter(({violation:e})=>e.severity===s.severity).map(({entityId:e,violation:t})=>({type:t.type,entityId:e,message:void 0===t.message?null:i.resolveLocalizable(t.message)})))};export const createEntityMeta=(o,c)=>{let p=e=>()=>t(c.manifest,e,o.entity.type).flatMap(t=>{let i=s(e,t,o.entity.id,o.state,c.recipes);return i?[((e,t,i)=>{let{group:n,label:s,description:a,recipes:o}=e,{resolveLocalizable:c}=i.recipes;return{groupId:n.groupId,name:void 0===s?n.instanceName:c(s),description:n.description??(void 0===a?null:c(a)),recipes:o.map(n=>((e,t,i,n)=>{let{key:s,item:a}=e,{resolveLocalizable:o}=n.recipes;return{key:s,name:o(a.title),description:void 0===a.description?null:o(a.description),imageUrl:void 0===a.thumbnailUrl?null:o(a.thumbnailUrl),dryRun:()=>t.group.dryRuns?((e,t,i,n)=>{let{updates:s,error:a}=((e,t,{entity:i,state:n},{recipes:s})=>{try{return{updates:r(e,t,i.id,n,s),error:void 0}}catch(e){if("child"===t.target.kind)throw e;return{updates:void 0,error:e instanceof Error?e.message:String(e)}}})(e,t,i,n);return void 0===s?d([{type:"uncaught",entityId:null,message:a}]):l(s,i,n)})(e,t,i,n):null}})(n,e,t,i))}})(i,o,c)]:[]});return{...Object.fromEntries(e(c.manifest,o.entity.type).map(e=>[e.edgeName,()=>n(e,o.entity.id,o.state,c.recipes)?.map(t=>({...t,dryRun:()=>e.dryRuns?(({field:e},{value:t},r,n)=>{let{entity:s}=r,o=Object.entries(Object.entries(n.manifest.writableFields??{}).find(([e])=>e===s.type)?.[1]??{}).find(([t])=>t===e)?.[1];if(void 0===o)throw Error(`The options of "${e}" on "${s.id}" cannot be dry-run: the schema lists them, but the configurator has no writable field "${e}" on ${s.type}. Regenerate the application so the schema matches the configurator.`);let c=r.state.entities.find(e=>e.id===s.id),p={[o]:t},u=c?n.validateEntity({...c,...p}):{success:!0};return u.success?l([a.DomainEntityModification({entityId:s.id,stateDelta:p,updateTag:i})],r,n):d([{type:"uncaught",entityId:s.id,message:`Writing ${JSON.stringify(t)} to "${e}" would make "${s.id}" an invalid ${s.type}: ${u.errors.join("; ")}.${e===o?"":` The message names the stored property: ${e} is stored as ${o}.`} The option comes from the configurator and cannot be corrected from here, so do not write it.`}])})(e,t,o,c):null}))??null])),childRecipes:p("child"),replaceRecipes:p("replace")}};
@@ -1,4 +1,5 @@
1
1
  import { type ExecutionResult } from 'graphql';
2
+ import { type Localizable } from '@salsita-ai/common/utils';
2
3
  import { type $DomainEntity, type DomainContextSelectors, type DomainEntity, type DomainServicesSelectors, type DomainState, type SpecSelectors, type StateUpdateEmission } from '#package/domain';
3
4
  /** One single-operation GraphQL document with its own variables, as executed by {@link GraphQLSystem}. */
4
5
  export interface GraphQLOperation {
@@ -26,13 +27,22 @@ export interface PreparedOperation {
26
27
  /**
27
28
  * What {@link GraphQLSystem.execute} returns: the results of the operations as prepared from the
28
29
  * committed state, whether their changes were committed as one undo step, and, when the operations
29
- * succeeded but their changes were not committed, the reason addressed to the caller.
30
+ * succeeded but their changes were not committed, the reason addressed to the caller. `sideEffects`
31
+ * describes the prepared changes whether or not they were committed, so a caller can weigh a held
32
+ * commit before the user approves it.
30
33
  */
31
34
  export interface GraphQLExecution {
32
35
  readonly results: readonly ExecutionResult[];
33
36
  readonly committed: boolean;
34
37
  readonly message?: string;
38
+ /** What the configurator did, or would do, beyond what the operations asked for, in applied order. */
39
+ readonly sideEffects: readonly string[];
35
40
  }
41
+ /**
42
+ * Resolves a localizable value, or each item of a list of them, to a string in the current locale.
43
+ * `null` and values that are not localizable pass through, so the schema's nullability decides.
44
+ */
45
+ export declare const localize: (value: unknown, resolveLocalizable: (message: Localizable) => string) => unknown;
36
46
  /**
37
47
  * Executes GraphQL operations against the schema generated from the application spec `assistant`
38
48
  * manifest, resolving it against the committed domain state. Agent-independent: the assistant's
@@ -75,6 +85,11 @@ export declare class GraphQLSystem {
75
85
  * "not run" error instead.
76
86
  */
77
87
  prepare: (originalState: AnyDomainState, operations: readonly GraphQLOperation[]) => Promise<readonly PreparedOperation[]>;
88
+ /**
89
+ * The side effects of prepared operations: the hints of every update of their emissions, as the
90
+ * agent is told them, in applied order.
91
+ */
92
+ readSideEffects: (prepared: readonly PreparedOperation[]) => readonly string[];
78
93
  private readonly _prepareAndCommit;
79
94
  /**
80
95
  * Prepares the operations from the committed state and commits their changes as one emission,
@@ -1 +1 @@
1
- import{GraphQLError as e,buildSchema as t,defaultFieldResolver as i,getNamedType as o,graphql as a,validateSchema as n}from"graphql";import{assertExistence as r,createSelector as s,isMessageDescriptor as l,isMultilingualString as c}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as h,findAssistantMutation as d}from"#package/domain";import{createEntityMeta as m}from"./entity-meta.js";let p={errors:[new e('No assistant manifest in the application spec. The GraphQL schema is generated from the "assistant" section. Add an "assistant" section to a spec file to expose a schema.')]},u={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},f=new RegExp(h.pattern),y=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,g=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,v=e=>e instanceof Error?e.message:String(e),S=e=>/[.!?]$/.test(e)?e:`${e}.`,_=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,x=(e,t)=>{let i=_(e)?e._raw:void 0;if(y(i))return i;if(!Array.isArray(e)&&!_(e))return e;if(t.has(e))throw Error("The value contains itself. Return ids or copies of the values instead of objects that reference each other.");let o=new Set([...t,e]);return Array.isArray(e)?e.map(e=>x(e,o)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,x(t,o)]))},w=(e,t)=>Array.isArray(e)?e.map(e=>w(e,t)):"string"==typeof e||l(e)||c(e)?t(e):e,$=t=>new e(`The GraphQL schema generated from the "assistant" manifest is invalid: ${S(t)} Regenerate the application so the schema is rebuilt from the current spec.`),E=e=>(e.errors?.length??0)>0,b=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=s([s.leaf(e=>e)],e=>{try{let i=t(e),o=n(i);return o.length>0?$(o.map(e=>e.message).join(" ")):i}catch(e){return $(v(e))}});_selectFieldResolver=s([s.leaf(e=>e)],t=>(a,n,r,s)=>{if(s.parentType===s.schema.getQueryType())return i(a,n,r,s);if(s.parentType===s.schema.getMutationType())return this._resolveMutation(s.fieldName,String(s.path.key),n,r);if(!y(a))return i(a,n,r,s);if("meta"===s.fieldName){let e;return m({entity:a,state:("object"==typeof(e=g(a,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??r.state},{manifest:r.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e)})}let{source:l,localized:c}=t[s.parentType.name]?.[s.fieldName]??{source:s.fieldName},h=((t,i)=>{try{return g(t,i)}catch(o){throw new e(`Expression "${i}" failed on ${y(t)?t.id:"an entity"}: ${S(v(o))} The expression threw while the query read it; fix the expression or the configuration state it reads.`,{originalError:o instanceof Error?o:void 0})}})(a,l);return c?w(h,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===o(s.returnType).name?((t,i,o)=>{try{return x(o,new Set)}catch(o){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${y(t)?t.id:"an entity"}: ${S(v(o))} A JSON field can only return data that serializes to JSON.`,{originalError:o instanceof Error?o:void 0})}})(a,l,h):h});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var o=i.rootEntityId;let a=t.filter(e=>!f.test(e));if(a.length>0)throw new e(`${a.map(e=>`"${e}"`).join(", ")} ${1===a.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${o}". Use ids as returned by the "id" field.`);return t.map(e=>f.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>y(e)?e.type:void 0;initialize=()=>{};_resolveMutation=(t,i,o,a)=>{if(void 0!==a.failedMutationField)throw new e(`Not run: the earlier field "${a.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,o,a)}catch(e){throw a.failedMutationField=i,e}};_runMutation=(t,i,o)=>{let a=d(t,o.manifest);if(!a)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. Regenerate the application so the schema matches the configurator.`);let n=o.state,r=a.implementation(i,n,{manifest:o.manifest,domain:this._context.domainContextSelectors.getDomain(),...this._recipeContext()});if(r.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(r.updates,n,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${S(v(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);o.emissions.push(i),o.state=i.state}return r.readResult({previousState:n,state:o.state})};_setup=()=>{let t=this._context.specSelectors.getAssistant();if(!t)return{ready:!1,result:p};let i=this._selectSchema(t.sdl);return i instanceof e?{ready:!1,result:{errors:[i]}}:{ready:!0,assistant:t,schema:i,fieldResolver:this._selectFieldResolver(t.fields)}};prepare=async(e,t)=>{let i,o=this._setup();if(!o.ready)return t.map(()=>({result:o.result,emissions:[]}));let n={manifest:o.assistant,state:e,emissions:[],failedMutationField:void 0},r=[];for(let[e,s]of t.entries())if(void 0===i){let t=n.emissions.length;n.failedMutationField=void 0;let l=await a({schema:o.schema,source:s.document,rootValue:this._rootValue,contextValue:n,variableValues:s.variables,operationName:s.operationName,fieldResolver:o.fieldResolver,typeResolver:this._resolveType});r.push({result:l,emissions:n.emissions.slice(t)}),E(l)&&(i=e)}else r.push({result:b(i),emissions:[]});return r};_prepareAndCommit=async e=>{let t,i,o,a=this._context.domainContextSelectors.getStoreSystem(),n=await this.prepare(a.stateSlices.committed.value.state,e),s=n.map(({result:e})=>e),l=n.flatMap(e=>e.emissions);return s.some(E)||0===l.length?{results:s,outcome:"none"}:{results:s,outcome:a.commitPrepared((t=r(l[0]),i=r(l[l.length-1]),o=l.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:t.previousState,state:i.state,updates:l.flatMap(e=>e.updates),validity:i.validity,transformationLog:o?l.flatMap(e=>e.transformationLog??[]):void 0}))}};execute=async e=>{if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>u),committed:!1};let t=await this._prepareAndCommit(e),{results:i,outcome:o}="conflict"===t.outcome?await this._prepareAndCommit(e):t;return"committed"===o?{results:i,committed:!0}:"held"===o?{results:i,committed:!1,message:"The changes were not committed yet: the application held them, usually to ask the user for approval. They may still be applied once the user approves, and the next turn shows whether they were. Do not send the operations again."}:"conflict"===o?{results:i,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense."}:{results:i,committed:!1}}}
1
+ import{GraphQLError as e,buildSchema as t,defaultFieldResolver as i,getNamedType as o,graphql as a,validateSchema as n}from"graphql";import{assertExistence as r,createSelector as s,isMessageDescriptor as l,isMultilingualString as c}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as h,findAssistantMutation as d}from"#package/domain";import{createEntityMeta as m}from"./entity-meta.js";import{readSideEffects as p}from"./side-effects.js";let f={errors:[new e('No assistant manifest in the application spec. The GraphQL schema is generated from the "assistant" section. Add an "assistant" section to a spec file to expose a schema.')]},u={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},y=new RegExp(h.pattern),g=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,v=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,S=e=>e instanceof Error?e.message:String(e),x=e=>/[.!?]$/.test(e)?e:`${e}.`,_=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,w=(e,t)=>{let i=_(e)?e._raw:void 0;if(g(i))return i;if(!Array.isArray(e)&&!_(e))return e;if(t.has(e))throw Error("The value contains itself. Return ids or copies of the values instead of objects that reference each other.");let o=new Set([...t,e]);return Array.isArray(e)?e.map(e=>w(e,o)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,w(t,o)]))};export const localize=(e,t)=>Array.isArray(e)?e.map(e=>localize(e,t)):"string"==typeof e||l(e)||c(e)?t(e):e;let E=t=>new e(`The GraphQL schema generated from the "assistant" manifest is invalid: ${x(t)} Regenerate the application so the schema is rebuilt from the current spec.`),$=e=>(e.errors?.length??0)>0,b=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=s([s.leaf(e=>e)],e=>{try{let i=t(e),o=n(i);return o.length>0?E(o.map(e=>e.message).join(" ")):i}catch(e){return E(S(e))}});_selectFieldResolver=s([s.leaf(e=>e)],t=>(a,n,r,s)=>{if(s.parentType===s.schema.getQueryType())return i(a,n,r,s);if(s.parentType===s.schema.getMutationType())return this._resolveMutation(s.fieldName,String(s.path.key),n,r);if(!g(a))return i(a,n,r,s);if("meta"===s.fieldName){let e;return m({entity:a,state:("object"==typeof(e=v(a,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??r.state},{manifest:r.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e)})}let{source:l,localized:c}=t[s.parentType.name]?.[s.fieldName]??{source:s.fieldName},h=((t,i)=>{try{return v(t,i)}catch(o){throw new e(`Expression "${i}" failed on ${g(t)?t.id:"an entity"}: ${x(S(o))} The expression threw while the query read it; fix the expression or the configuration state it reads.`,{originalError:o instanceof Error?o:void 0})}})(a,l);return c?localize(h,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===o(s.returnType).name?((t,i,o)=>{try{return w(o,new Set)}catch(o){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${g(t)?t.id:"an entity"}: ${x(S(o))} A JSON field can only return data that serializes to JSON.`,{originalError:o instanceof Error?o:void 0})}})(a,l,h):h});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var o=i.rootEntityId;let a=t.filter(e=>!y.test(e));if(a.length>0)throw new e(`${a.map(e=>`"${e}"`).join(", ")} ${1===a.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${o}". Use ids as returned by the "id" field.`);return t.map(e=>y.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>g(e)?e.type:void 0;initialize=()=>{};_resolveMutation=(t,i,o,a)=>{if(void 0!==a.failedMutationField)throw new e(`Not run: the earlier field "${a.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,o,a)}catch(e){throw a.failedMutationField=i,e}};_runMutation=(t,i,o)=>{let a=d(t,o.manifest);if(!a)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. Regenerate the application so the schema matches the configurator.`);let n=o.state,r=a.implementation(i,n,{manifest:o.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(n),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(r.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(r.updates,n,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${x(S(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);o.emissions.push(i),o.state=i.state}return r.readResult({previousState:n,state:o.state})};_setup=()=>{let t=this._context.specSelectors.getAssistant();if(!t)return{ready:!1,result:f};let i=this._selectSchema(t.sdl);return i instanceof e?{ready:!1,result:{errors:[i]}}:{ready:!0,assistant:t,schema:i,fieldResolver:this._selectFieldResolver(t.fields)}};prepare=async(e,t)=>{let i,o=this._setup();if(!o.ready)return t.map(()=>({result:o.result,emissions:[]}));let n={manifest:o.assistant,state:e,emissions:[],failedMutationField:void 0},r=[];for(let[e,s]of t.entries())if(void 0===i){let t=n.emissions.length;n.failedMutationField=void 0;let l=await a({schema:o.schema,source:s.document,rootValue:this._rootValue,contextValue:n,variableValues:s.variables,operationName:s.operationName,fieldResolver:o.fieldResolver,typeResolver:this._resolveType});r.push({result:l,emissions:n.emissions.slice(t)}),$(l)&&(i=e)}else r.push({result:b(i),emissions:[]});return r};readSideEffects=e=>{let t=this._context.domainServicesSelectors.getResolveLocalizable();return e.flatMap(({emissions:e})=>e.flatMap(e=>p(e.updates,t)))};_prepareAndCommit=async e=>{let t,i,o,a=this._context.domainContextSelectors.getStoreSystem(),n=await this.prepare(a.stateSlices.committed.value.state,e),s=n.map(({result:e})=>e),l=this.readSideEffects(n),c=n.flatMap(e=>e.emissions);return s.some($)||0===c.length?{results:s,sideEffects:l,outcome:"none"}:{results:s,sideEffects:l,outcome:a.commitPrepared((t=r(c[0]),i=r(c[c.length-1]),o=c.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:t.previousState,state:i.state,updates:c.flatMap(e=>e.updates),validity:i.validity,transformationLog:o?c.flatMap(e=>e.transformationLog??[]):void 0}))}};execute=async e=>{if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>u),committed:!1,sideEffects:[]};let t=await this._prepareAndCommit(e),{results:i,sideEffects:o,outcome:a}="conflict"===t.outcome?await this._prepareAndCommit(e):t;return"committed"===a?{results:i,committed:!0,sideEffects:o}:"held"===a?{results:i,committed:!1,message:"The changes were not committed yet: the application held them, usually to ask the user for approval. They may still be applied once the user approves, and the next turn shows whether they were. Do not send the operations again.",sideEffects:o}:"conflict"===a?{results:i,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense.",sideEffects:o}:{results:i,committed:!1,sideEffects:o}}}
@@ -0,0 +1,9 @@
1
+ import type { Localizable } from '@salsita-ai/common/utils';
2
+ import type { DataUpdate, DomainEntity } from '#package/domain';
3
+ /**
4
+ * What the updates of an emission tell the assistant agent the configurator did beyond what was
5
+ * asked, in applied order with duplicates kept, the way the user gets them as snackbars. An update's
6
+ * `agentHint` replaces its user-facing `hint` for the agent, `null` keeps the update from the agent,
7
+ * and an update with neither says nothing. Empty text counts as none, as it does for the snackbar.
8
+ */
9
+ export declare const readSideEffects: (updates: ReadonlyArray<DataUpdate<DomainEntity<string>, DomainEntity<string>>>, resolveLocalizable: (message: Localizable) => string) => readonly string[];
@@ -0,0 +1 @@
1
+ export const readSideEffects=(e,t)=>e.flatMap(({hint:e,agentHint:a})=>null===a?[]:void 0!==a?a?[a]:[]:e?[t(e)]:[]);
@@ -1,4 +1,5 @@
1
1
  export { AnimationSystem, type AnimationSystemContext } from './animation-system.js';
2
+ export { type AssistantChatSnapshot, type AssistantClientTool, type AssistantPanelSnapshot, AssistantSystem, type AssistantSystemContext, } from './assistant-system/assistant-system.js';
2
3
  export { CameraControlSystem, CameraControlType } from './camera-control-system/camera-control-system.js';
3
4
  export { type AnyCameraSystem, CameraSystem, type CameraSystemContext } from './camera-control-system/camera-system.js';
4
5
  export { createCameraControls } from './camera-control-system/create-camera-controls.js';
@@ -1 +1 @@
1
- export{AnimationSystem}from"./animation-system.js";export{CameraControlSystem,CameraControlType}from"./camera-control-system/camera-control-system.js";export{CameraSystem}from"./camera-control-system/camera-system.js";export{createCameraControls}from"./camera-control-system/create-camera-controls.js";export{CanvasSystem}from"./canvas-system.js";export{DragAndDropSystem}from"./drag-and-drop-system/drag-and-drop-system.js";export{ExportSystem}from"./export-system/export-system.js";export{GraphQLSystem}from"./graphql-system/graphql-system.js";export{ImageRenderingSystem}from"./image-rendering-system/image-rendering-system.js";export{LayoutSystem}from"./layout-system/layout-system.js";export{toolbarHelpElementName}from"./layout-system/toolbar-help-element.js";export{SketchingSystem}from"./sketching-system/sketching-system.js";export{LineEndPointUpdateTag,LineLengthUpdateTag,LineLineConnectionUpdateTag,LineRectangleBottomLeftConnectionUpdateTag,LineRectangleBottomRightConnectionUpdateTag,LineRectangleTopLeftConnectionUpdateTag,LineRectangleTopRightConnectionUpdateTag,LineSketchingPointUpdateTag,LineStartPointUpdateTag,RectangleBottomLeftCornerUpdateTag,RectangleBottomRightCornerUpdateTag,RectangleDepthUpdateTag,RectangleSketchingPointUpdateTag,RectangleTopLeftCornerUpdateTag,RectangleTopRightCornerUpdateTag,RectangleWidthUpdateTag,SetInitialSketchingPointPositionTag,SketchingFixTransformationTag,isAnySketchingPointUpdate,isLineEndPointUpdate,isLineLengthUpdate,isLineLineConnectionUpdate,isLineRectangleBottomLeftConnectionUpdate,isLineRectangleBottomRightConnectionUpdate,isLineRectangleTopLeftConnectionUpdate,isLineRectangleTopRightConnectionUpdate,isLineSketchingPointUpdate,isLineStartPointUpdate,isRectangleBottomLeftCornerUpdate,isRectangleBottomRightCornerUpdate,isRectangleDepthUpdate,isRectangleSketchingPointUpdate,isRectangleTopLeftCornerUpdate,isRectangleTopRightCornerUpdate,isRectangleWidthUpdate,isSetInitialLinePositionUpdate,isSetInitialRectanglePositionUpdate,isSketchingFixTransformationUpdate}from"./sketching-system/sketching-updates.js";
1
+ export{AnimationSystem}from"./animation-system.js";export{AssistantSystem}from"./assistant-system/assistant-system.js";export{CameraControlSystem,CameraControlType}from"./camera-control-system/camera-control-system.js";export{CameraSystem}from"./camera-control-system/camera-system.js";export{createCameraControls}from"./camera-control-system/create-camera-controls.js";export{CanvasSystem}from"./canvas-system.js";export{DragAndDropSystem}from"./drag-and-drop-system/drag-and-drop-system.js";export{ExportSystem}from"./export-system/export-system.js";export{GraphQLSystem}from"./graphql-system/graphql-system.js";export{ImageRenderingSystem}from"./image-rendering-system/image-rendering-system.js";export{LayoutSystem}from"./layout-system/layout-system.js";export{toolbarHelpElementName}from"./layout-system/toolbar-help-element.js";export{SketchingSystem}from"./sketching-system/sketching-system.js";export{LineEndPointUpdateTag,LineLengthUpdateTag,LineLineConnectionUpdateTag,LineRectangleBottomLeftConnectionUpdateTag,LineRectangleBottomRightConnectionUpdateTag,LineRectangleTopLeftConnectionUpdateTag,LineRectangleTopRightConnectionUpdateTag,LineSketchingPointUpdateTag,LineStartPointUpdateTag,RectangleBottomLeftCornerUpdateTag,RectangleBottomRightCornerUpdateTag,RectangleDepthUpdateTag,RectangleSketchingPointUpdateTag,RectangleTopLeftCornerUpdateTag,RectangleTopRightCornerUpdateTag,RectangleWidthUpdateTag,SetInitialSketchingPointPositionTag,SketchingFixTransformationTag,isAnySketchingPointUpdate,isLineEndPointUpdate,isLineLengthUpdate,isLineLineConnectionUpdate,isLineRectangleBottomLeftConnectionUpdate,isLineRectangleBottomRightConnectionUpdate,isLineRectangleTopLeftConnectionUpdate,isLineRectangleTopRightConnectionUpdate,isLineSketchingPointUpdate,isLineStartPointUpdate,isRectangleBottomLeftCornerUpdate,isRectangleBottomRightCornerUpdate,isRectangleDepthUpdate,isRectangleSketchingPointUpdate,isRectangleTopLeftCornerUpdate,isRectangleTopRightCornerUpdate,isRectangleWidthUpdate,isSetInitialLinePositionUpdate,isSetInitialRectanglePositionUpdate,isSketchingFixTransformationUpdate}from"./sketching-system/sketching-updates.js";
@@ -25,6 +25,8 @@ export declare class LayoutSystem<ENTITIES extends DomainEntity<string>> {
25
25
  * whatever was true when it was created.
26
26
  */
27
27
  private get _toolbarHelpExists();
28
+ /** Whether the application spec has an `assistant` section; read on demand for the same reason. */
29
+ private get _hasAssistant();
28
30
  initialize: () => void;
29
31
  getCleanUpPropertyPageStateUpdates: (entityId: DomainEntityId<string>, state: DomainState<ENTITIES>) => import("#package/domain").ComponentModification<import("#package/domain").SystemComponentType<"layoutStateComponent", import("./layout-state.js").LayoutStateComponentState, "propertyPageStates">>[];
30
32
  getLayoutConfig: (state: DomainState<ENTITIES>) => {