@salsita-ai/sdk 0.50.0-develop.78a839cc7b → 0.50.0-develop.7c472a27fe

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 (371) hide show
  1. package/dist/application-spec/errors/diagnostic-codes.d.ts +9 -0
  2. package/dist/application-spec/errors/diagnostic-codes.js +1 -1
  3. package/dist/application-spec/errors/diagnostic-messages.d.ts +10 -1
  4. package/dist/application-spec/errors/diagnostic-messages.js +1 -1
  5. package/dist/application-spec/generate-application/code/domain-runtime-files.js +1 -1
  6. package/dist/application-spec/generate-application/code/templates/application-types.js +6 -6
  7. package/dist/application-spec/generate-application/code/templates/common/doc-comment.d.ts +2 -0
  8. package/dist/application-spec/generate-application/code/templates/common/doc-comment.js +1 -0
  9. package/dist/application-spec/generate-application/code/templates/configurator-globals.d.ts +2 -1
  10. package/dist/application-spec/generate-application/code/templates/configurator-globals.js +19 -6
  11. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.js +7 -7
  12. package/dist/application-spec/generate-application/code/templates/global-definition/generate-assistant-form-element-types.d.ts +12 -0
  13. package/dist/application-spec/generate-application/code/templates/global-definition/generate-assistant-form-element-types.js +7 -0
  14. package/dist/application-spec/generate-application/code/templates/global-definition/generate-phx-types.js +24 -22
  15. package/dist/application-spec/generate-application/code/templates/global-definition/index.d.ts +1 -0
  16. package/dist/application-spec/generate-application/code/templates/global-definition/index.js +1 -1
  17. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +13 -6
  18. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +2 -2
  19. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.d.ts +3 -2
  20. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.js +1 -1
  21. package/dist/application-spec/generate-application/runtime/application-runtime.d.ts +12 -4
  22. package/dist/application-spec/generate-application/runtime/application-runtime.js +1 -1
  23. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +3 -3
  24. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  25. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.d.ts +9 -3
  26. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.js +1 -1
  27. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +33 -3
  28. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.js +1 -1
  29. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-form-elements.d.ts +9 -0
  30. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-form-elements.js +1 -0
  31. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.d.ts +4 -1
  32. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
  33. package/dist/configurator/ConfiguratorApp.js +1 -1
  34. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  35. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +4 -3
  36. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  37. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +33 -7
  38. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  39. package/dist/configurator/application-runtime/extensions/systems/canvas-system.d.ts +8 -0
  40. package/dist/configurator/application-runtime/extensions/systems/canvas-system.js +1 -1
  41. package/dist/configurator/application-runtime/extensions/systems/export-system/export-busy-error.d.ts +8 -0
  42. package/dist/configurator/application-runtime/extensions/systems/export-system/export-busy-error.js +1 -0
  43. package/dist/configurator/application-runtime/extensions/systems/export-system/export-system.d.ts +23 -1
  44. package/dist/configurator/application-runtime/extensions/systems/export-system/export-system.js +1 -1
  45. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.d.ts +14 -1
  46. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.js +1 -1
  47. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +45 -4
  48. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  49. package/dist/configurator/application-runtime/extensions/systems/image-rendering-system/image-rendering-system.d.ts +14 -5
  50. package/dist/configurator/application-runtime/extensions/systems/image-rendering-system/image-rendering-system.js +1 -1
  51. package/dist/configurator/application-runtime/extensions/systems/index.d.ts +1 -1
  52. package/dist/configurator/common/configurator-interface.d.ts +12 -2
  53. package/dist/configurator/common/configurator-interface.js +1 -1
  54. package/dist/configurator/configurator-context/ConfiguratorResponsiveProvider.d.ts +3 -3
  55. package/dist/configurator/configurator-context/ConfiguratorResponsiveProvider.js +1 -1
  56. package/dist/configurator/configurator-context/PreviewContextProvider.js +1 -1
  57. package/dist/configurator/configurator-context/configurator-hooks/layout-hooks.d.ts +4 -0
  58. package/dist/configurator/configurator-context/configurator-routing/ConfiguratorRoutingProvider.js +1 -1
  59. package/dist/configurator/pages/error-boundaries/LoadingErrorBoundary.js +1 -1
  60. package/dist/configurator/toolbar-actions/index.d.ts +1 -0
  61. package/dist/configurator/toolbar-actions/index.js +1 -1
  62. package/dist/configurator/toolbar-actions/use-toolbar-actions.d.ts +100 -0
  63. package/dist/configurator/toolbar-actions/use-toolbar-actions.js +1 -0
  64. package/dist/configurator/toolbar-actions/use-toolbar-help.d.ts +7 -0
  65. package/dist/configurator/toolbar-actions/use-toolbar-help.js +1 -0
  66. package/dist/configurator/ui/assistant/AssistantChips.d.ts +3 -2
  67. package/dist/configurator/ui/assistant/AssistantChips.js +1 -1
  68. package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +1 -1
  69. package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -1
  70. package/dist/configurator/ui/assistant/alert/AssistantAlert.d.ts +8 -0
  71. package/dist/configurator/ui/assistant/alert/AssistantAlert.js +1 -0
  72. package/dist/configurator/ui/assistant/alert/index.d.ts +2 -0
  73. package/dist/configurator/ui/assistant/alert/index.js +1 -0
  74. package/dist/configurator/ui/assistant/alert/remark-alerts.d.ts +10 -0
  75. package/dist/configurator/ui/assistant/alert/remark-alerts.js +1 -0
  76. package/dist/configurator/ui/assistant/form/AssistantForm.js +1 -1
  77. package/dist/configurator/ui/assistant/form/compose-answer.d.ts +1 -1
  78. package/dist/configurator/ui/assistant/form/compose-answer.js +1 -1
  79. package/dist/configurator/ui/assistant/form/form-model.d.ts +3 -4
  80. package/dist/configurator/ui/assistant/form/form-state.d.ts +1 -1
  81. package/dist/configurator/ui/assistant/form/index.d.ts +2 -0
  82. package/dist/configurator/ui/assistant/form/index.js +1 -1
  83. package/dist/configurator/ui/assistant/form/parse-form-block.d.ts +3 -2
  84. package/dist/configurator/ui/assistant/form/parse-form-block.js +1 -1
  85. package/dist/configurator/ui/assistant/form/read-form-attributes.d.ts +1 -1
  86. package/dist/configurator/ui/assistant/form/read-form-attributes.js +1 -1
  87. package/dist/configurator/ui/assistant/form/registry/built-in-form-elements.d.ts +3 -0
  88. package/dist/configurator/ui/assistant/form/registry/built-in-form-elements.js +1 -0
  89. package/dist/configurator/ui/assistant/form/registry/create-assistant-form-element.d.ts +31 -0
  90. package/dist/configurator/ui/assistant/form/registry/create-assistant-form-element.js +1 -0
  91. package/dist/configurator/ui/assistant/form/{form-element-definition.d.ts → registry/form-element-definition.d.ts} +3 -1
  92. package/dist/configurator/ui/assistant/form/registry/form-element-definition.js +1 -0
  93. package/dist/configurator/ui/assistant/form/{form-registry.d.ts → registry/form-registry.d.ts} +2 -2
  94. package/dist/configurator/ui/assistant/form/registry/form-registry.js +1 -0
  95. package/dist/configurator/ui/assistant/form/registry/index.d.ts +6 -0
  96. package/dist/configurator/ui/assistant/form/registry/index.js +1 -0
  97. package/dist/configurator/ui/assistant/form/registry/use-form-registry.d.ts +8 -0
  98. package/dist/configurator/ui/assistant/form/registry/use-form-registry.js +1 -0
  99. package/dist/configurator/ui/assistant/form-elements/FormCard.d.ts +9 -0
  100. package/dist/configurator/ui/assistant/form-elements/FormCard.js +1 -0
  101. package/dist/configurator/ui/assistant/form-elements/FormConfirm.d.ts +9 -0
  102. package/dist/configurator/ui/assistant/form-elements/FormConfirm.js +1 -0
  103. package/dist/configurator/ui/assistant/form-elements/FormGroup.d.ts +4 -1
  104. package/dist/configurator/ui/assistant/form-elements/FormNumber.d.ts +9 -0
  105. package/dist/configurator/ui/assistant/form-elements/FormNumber.js +1 -0
  106. package/dist/configurator/ui/assistant/form-elements/FormOptions.d.ts +4 -1
  107. package/dist/configurator/ui/assistant/form-elements/FormReply.d.ts +4 -1
  108. package/dist/configurator/ui/assistant/form-elements/FormReply.js +1 -1
  109. package/dist/configurator/ui/assistant/form-elements/FormSwatch.d.ts +10 -0
  110. package/dist/configurator/ui/assistant/form-elements/FormSwatch.js +1 -0
  111. package/dist/configurator/ui/assistant/form-elements/FormText.d.ts +4 -1
  112. package/dist/configurator/ui/assistant/form-elements/FormText.js +1 -1
  113. package/dist/configurator/ui/assistant/form-elements/clamp-form-number.d.ts +10 -0
  114. package/dist/configurator/ui/assistant/form-elements/clamp-form-number.js +1 -0
  115. package/dist/configurator/ui/assistant/form-elements/form-element-props.d.ts +20 -16
  116. package/dist/configurator/ui/assistant/form-elements/index.d.ts +4 -0
  117. package/dist/configurator/ui/assistant/form-elements/index.js +1 -1
  118. package/dist/configurator/ui/assistant/index.d.ts +3 -0
  119. package/dist/configurator/ui/assistant/index.js +1 -1
  120. package/dist/configurator/ui/assistant/resource-image/index.d.ts +1 -1
  121. package/dist/configurator/ui/assistant/resource-image/index.js +1 -1
  122. package/dist/configurator/ui/assistant/resource-image/resource-path.d.ts +0 -9
  123. package/dist/configurator/ui/assistant/resource-image/resource-path.js +1 -1
  124. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.d.ts +9 -0
  125. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.js +1 -0
  126. package/dist/configurator/ui/assistant/screenshots/index.d.ts +2 -0
  127. package/dist/configurator/ui/assistant/screenshots/index.js +1 -0
  128. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.d.ts +14 -0
  129. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.js +1 -0
  130. package/dist/configurator/ui/assistant/streamed-markdown.d.ts +2 -2
  131. package/dist/configurator/ui/assistant/streamed-markdown.js +2 -2
  132. package/dist/configurator/ui/assistant/tool-chip.js +1 -1
  133. package/dist/configurator/ui/configurator-layout/slots/canvas/LayoutCanvas.js +1 -1
  134. package/dist/configurator/ui/configurator-layout/slots/canvas/SharedCanvas.js +1 -1
  135. package/dist/configurator/ui/configurator-layout/universal/ExportingOverlay.d.ts +5 -0
  136. package/dist/configurator/ui/configurator-layout/universal/ExportingOverlay.js +1 -0
  137. package/dist/configurator/ui/configurator-layout/universal/Layout.js +1 -1
  138. package/dist/configurator/ui/configurator-layout/universal/use-layout-scene-safe-area.js +1 -1
  139. package/dist/configurator/ui/index.d.ts +1 -0
  140. package/dist/configurator/ui/index.js +1 -1
  141. package/dist/configurator/ui/legacy/layout/common/ReactiveInterfaceLayout.js +1 -1
  142. package/dist/configurator/ui/phoenix-layout-elements/AppLoader.js +2 -2
  143. package/dist/configurator/ui/save-message-snackbars/SaveMessageSnackbars.js +1 -1
  144. package/dist/docs/index.json +62 -1
  145. package/dist/docs/integrations/webcomponent-api/basics.mdx +2 -0
  146. package/dist/docs/layouts/custom-layout.mdx +318 -158
  147. package/dist/docs/layouts/gallery/ardent-compressor.mdx +3516 -0
  148. package/dist/docs/layouts/gallery/fenwick-window.mdx +2803 -0
  149. package/dist/docs/layouts/gallery/ferrule-bar-station.mdx +6567 -0
  150. package/dist/docs/layouts/gallery/kestrel-kettle.mdx +1858 -0
  151. package/dist/docs/layouts/gallery/sunfold-solar.mdx +2683 -0
  152. package/dist/docs/other-features/client-side-rendering/declarative.mdx +1 -1
  153. package/dist/docs/phoenix/configurator-interface.mdx +12 -10
  154. package/dist/docs/phoenix/sdk-builder-workflow.mdx +1 -1
  155. package/dist/docs/phoenix/sessions-and-access.mdx +2 -0
  156. package/dist/domain/application/assistant/assistant-manifest.d.ts +16 -1
  157. package/dist/domain/application/assistant/assistant-resolved-manifest.d.ts +8 -1
  158. package/dist/domain/application/assistant/build-assistant-manifest.js +1 -1
  159. package/dist/domain/application/assistant/form/assistant-form-element-name.d.ts +11 -0
  160. package/dist/domain/application/assistant/form/assistant-form-element-name.js +8 -0
  161. package/dist/domain/application/assistant/form/assistant-form-elements.d.ts +174 -0
  162. package/dist/domain/application/assistant/form/assistant-form-elements.js +1 -0
  163. package/dist/domain/application/assistant/form/assistant-resource-path.d.ts +10 -0
  164. package/dist/domain/application/assistant/form/assistant-resource-path.js +1 -0
  165. package/dist/domain/application/assistant/form/index.d.ts +4 -0
  166. package/dist/domain/application/assistant/form/index.js +1 -0
  167. package/dist/domain/application/assistant/generate-assistant-prompt.d.ts +3 -0
  168. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
  169. package/dist/domain/application/assistant/index.d.ts +2 -3
  170. package/dist/domain/application/assistant/index.js +1 -1
  171. package/dist/domain/application/assistant/session-setup/apply-assistant-setup.js +1 -1
  172. package/dist/domain/application/assistant/session-setup/assistant-setup-manifest.d.ts +2 -0
  173. package/dist/domain/application/assistant/session-setup/assistant-setup-manifest.js +1 -1
  174. package/dist/domain/application/web-component-property.d.ts +3 -1
  175. package/dist/domain/application/web-component-property.js +1 -1
  176. package/dist/domain/available-system-components.d.ts +1 -1
  177. package/dist/domain/available-system-components.js +1 -1
  178. package/dist/domain/domain-builder.d.ts +9 -3
  179. package/dist/domain/domain-builder.js +1 -1
  180. package/dist/domain/domain-context/store-system/store-system.d.ts +5 -0
  181. package/dist/domain/domain-context/store-system/store-system.js +1 -1
  182. package/dist/domain/domain-runtime/domain-context/selectors.d.ts +1 -0
  183. package/dist/domain/domain-runtime/domain-context/selectors.js +1 -1
  184. package/dist/domain/domain-runtime/domain-runtime.d.ts +8 -0
  185. package/dist/domain/domain-runtime/environment/types.d.ts +1 -1
  186. package/dist/domain/domain-runtime/errors/implementation-error-collector.d.ts +2 -0
  187. package/dist/domain/domain-runtime/errors/implementation-error-collector.js +1 -1
  188. package/dist/domain/domain-runtime/index.d.ts +1 -1
  189. package/dist/domain/domain-runtime/index.js +1 -1
  190. package/dist/domain/domain-runtime/spec/index.d.ts +1 -1
  191. package/dist/domain/domain-runtime/spec/index.js +1 -1
  192. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  193. package/dist/domain/domain-runtime/spec/module-checks/element-registration.d.ts +15 -1
  194. package/dist/domain/domain-runtime/spec/module-checks/element-registration.js +1 -1
  195. package/dist/domain/domain-runtime/spec/module-checks/find-assistant-form-element-errors.d.ts +12 -0
  196. package/dist/domain/domain-runtime/spec/module-checks/find-assistant-form-element-errors.js +1 -0
  197. package/dist/domain/domain-runtime/spec/module-checks/index.d.ts +3 -1
  198. package/dist/domain/domain-runtime/spec/module-checks/index.js +1 -1
  199. package/dist/domain/domain-runtime/spec/module-checks/merge-expression-modules.d.ts +23 -0
  200. package/dist/domain/domain-runtime/spec/module-checks/merge-expression-modules.js +1 -0
  201. package/dist/domain/domain-runtime/spec/module-checks/resolve-expression-modules.d.ts +3 -4
  202. package/dist/domain/domain-runtime/spec/module-checks/resolve-expression-modules.js +1 -1
  203. package/dist/domain/domain-runtime/spec/selectors.d.ts +3 -1
  204. package/dist/domain/domain-runtime/spec/selectors.js +1 -1
  205. package/dist/domain/extensions/export-images/agent-screenshot.d.ts +24 -0
  206. package/dist/domain/extensions/export-images/agent-screenshot.js +1 -0
  207. package/dist/domain/extensions/{client-side-image.d.ts → export-images/client-side-image.d.ts} +2 -2
  208. package/dist/domain/extensions/export-images/client-side-image.js +1 -0
  209. package/dist/domain/extensions/export-images/index.d.ts +3 -0
  210. package/dist/domain/extensions/export-images/index.js +1 -0
  211. package/dist/domain/extensions/{save-template-thumbnail.d.ts → export-images/save-template-thumbnail.d.ts} +2 -2
  212. package/dist/domain/extensions/export-images/save-template-thumbnail.js +1 -0
  213. package/dist/domain/extensions/index.d.ts +1 -2
  214. package/dist/domain/extensions/index.js +1 -1
  215. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +4 -0
  216. package/dist/editor/domain-editors/DomainSpecContext.js +1 -1
  217. package/dist/editor/domain-editors/apply-spec-update.d.ts +8 -1
  218. package/dist/editor/domain-editors/apply-spec-update.js +1 -1
  219. package/dist/editor/domain-editors/use-implementation-files.d.ts +6 -11
  220. package/dist/editor/domain-editors/use-implementation-files.js +1 -1
  221. package/dist/editor/screenshot/ScreenshotHandler.js +1 -1
  222. package/dist/i18n/locales/cs/messages.js +1 -1
  223. package/dist/i18n/locales/da/messages.js +1 -1
  224. package/dist/i18n/locales/de/messages.js +1 -1
  225. package/dist/i18n/locales/de-AT/messages.js +1 -1
  226. package/dist/i18n/locales/en/messages.js +1 -1
  227. package/dist/i18n/locales/en-AU/messages.js +1 -1
  228. package/dist/i18n/locales/en-GB/messages.js +1 -1
  229. package/dist/i18n/locales/es/messages.js +1 -1
  230. package/dist/i18n/locales/fr/messages.js +1 -1
  231. package/dist/i18n/locales/hu/messages.js +1 -1
  232. package/dist/i18n/locales/it/messages.js +1 -1
  233. package/dist/i18n/locales/nl/messages.js +1 -1
  234. package/dist/i18n/locales/pl/messages.js +1 -1
  235. package/dist/i18n/locales/pt/messages.js +1 -1
  236. package/dist/i18n/locales/ro/messages.js +1 -1
  237. package/dist/i18n/locales/sk/messages.js +1 -1
  238. package/dist/i18n/locales/tr/messages.js +1 -1
  239. package/dist/legacy/domain-cli/utils/cache.d.ts +1 -1
  240. package/dist/legacy/domain-cli/utils/cache.js +1 -1
  241. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +16 -0
  242. package/dist/legacy/domain-spec/spec/domain-spec.js +1 -1
  243. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/claude-md.d.ts +1 -1
  244. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/cursor-rules.d.ts +1 -1
  245. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.d.ts +1 -1
  246. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.js +11 -5
  247. package/dist/react-sdk/application-runtime/hooks/index.d.ts +2 -0
  248. package/dist/react-sdk/application-runtime/hooks/index.js +1 -1
  249. package/dist/react-sdk/application-runtime/hooks/loading/index.d.ts +5 -0
  250. package/dist/react-sdk/application-runtime/hooks/loading/index.js +1 -0
  251. package/dist/react-sdk/application-runtime/hooks/loading/loader-host-context.d.ts +11 -0
  252. package/dist/react-sdk/application-runtime/hooks/loading/loader-host-context.js +1 -0
  253. package/dist/react-sdk/application-runtime/hooks/loading/use-is-awaiting-loading-phase.d.ts +8 -0
  254. package/dist/react-sdk/application-runtime/hooks/loading/use-is-awaiting-loading-phase.js +1 -0
  255. package/dist/react-sdk/application-runtime/hooks/loading/use-is-loader-hosted.d.ts +7 -0
  256. package/dist/react-sdk/application-runtime/hooks/loading/use-is-loader-hosted.js +1 -0
  257. package/dist/react-sdk/application-runtime/hooks/loading/use-loading-actions.d.ts +9 -0
  258. package/dist/react-sdk/application-runtime/hooks/loading/use-loading-actions.js +1 -0
  259. package/dist/react-sdk/application-runtime/hooks/loading/use-loading-application.d.ts +24 -0
  260. package/dist/react-sdk/application-runtime/hooks/loading/use-loading-application.js +1 -0
  261. package/dist/react-sdk/application-runtime/hooks/responsive/index.d.ts +3 -0
  262. package/dist/react-sdk/application-runtime/hooks/responsive/index.js +1 -0
  263. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-actions.d.ts +9 -0
  264. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-actions.js +1 -0
  265. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-application.d.ts +26 -0
  266. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-application.js +1 -0
  267. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-state.d.ts +10 -0
  268. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-state.js +1 -0
  269. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-store.d.ts +24 -0
  270. package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-store.js +1 -0
  271. package/dist/react-sdk/application-runtime/hooks/use-access-service.d.ts +5 -0
  272. package/dist/react-sdk/application-runtime/hooks/use-access-service.js +1 -1
  273. package/dist/react-sdk/application-runtime/hooks/use-phoenix-element-slots.d.ts +2 -1
  274. package/dist/react-sdk/application-runtime/index.d.ts +2 -0
  275. package/dist/react-sdk/application-runtime/integration/initialize.d.ts +7 -6
  276. package/dist/react-sdk/application-runtime/integration/initialize.js +1 -1
  277. package/dist/react-sdk/application-runtime/integration/selectors.d.ts +11 -2
  278. package/dist/react-sdk/application-runtime/integration/selectors.js +1 -1
  279. package/dist/react-sdk/application-runtime/integration/types.d.ts +5 -2
  280. package/dist/react-sdk/application-runtime/loading/actions.d.ts +45 -0
  281. package/dist/react-sdk/application-runtime/loading/actions.js +1 -0
  282. package/dist/react-sdk/application-runtime/loading/index.d.ts +4 -0
  283. package/dist/react-sdk/application-runtime/loading/index.js +1 -0
  284. package/dist/react-sdk/application-runtime/loading/initialize.d.ts +3 -0
  285. package/dist/react-sdk/application-runtime/loading/initialize.js +1 -0
  286. package/dist/react-sdk/application-runtime/loading/selectors.d.ts +18 -0
  287. package/dist/react-sdk/application-runtime/loading/selectors.js +1 -0
  288. package/dist/react-sdk/application-runtime/loading/types.d.ts +59 -0
  289. package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.js +1 -1
  290. package/dist/react-sdk/application-runtime/provider/ApplicationRuntime.d.ts +6 -0
  291. package/dist/react-sdk/application-runtime/provider/ApplicationRuntime.js +1 -1
  292. package/dist/react-sdk/application-runtime/provider/ApplicationType.d.ts +7 -0
  293. package/dist/react-sdk/application-runtime/provider/ApplicationType.js +1 -1
  294. package/dist/react-sdk/application-runtime/responsive/actions.d.ts +47 -0
  295. package/dist/react-sdk/application-runtime/responsive/actions.js +1 -0
  296. package/dist/react-sdk/application-runtime/responsive/index.d.ts +4 -0
  297. package/dist/react-sdk/application-runtime/responsive/index.js +1 -0
  298. package/dist/react-sdk/application-runtime/responsive/initialize.d.ts +3 -0
  299. package/dist/react-sdk/application-runtime/responsive/initialize.js +1 -0
  300. package/dist/react-sdk/application-runtime/responsive/selectors.d.ts +20 -0
  301. package/dist/react-sdk/application-runtime/responsive/selectors.js +1 -0
  302. package/dist/react-sdk/application-runtime/responsive/types.d.ts +104 -0
  303. package/dist/react-sdk/application-runtime/routing/routing-service.d.ts +34 -2
  304. package/dist/react-sdk/application-runtime/routing/routing-service.js +1 -1
  305. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +164 -3
  306. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.js +1 -1
  307. package/dist/react-sdk/domain-application-context/BackendDataProvider.js +1 -1
  308. package/dist/react-sdk/domain-application-context/FrontendDataProvider.js +1 -1
  309. package/dist/react-sdk/domain-application-context/PublishedDataProvider.js +1 -1
  310. package/dist/react-sdk/domain-context/common-hooks/index.d.ts +1 -0
  311. package/dist/react-sdk/domain-context/common-hooks/index.js +1 -1
  312. package/dist/react-sdk/domain-context/common-hooks/use-configuration-save-state.d.ts +23 -0
  313. package/dist/react-sdk/domain-context/common-hooks/use-configuration-save-state.js +1 -0
  314. package/dist/react-sdk/domain-context/common-hooks/use-domain-context.d.ts +1 -0
  315. package/dist/react-sdk/domain-context/index.d.ts +1 -0
  316. package/dist/react-sdk/domain-context/index.js +1 -1
  317. package/dist/react-sdk/domain-context/system-hooks/undo-redo-hooks.js +1 -1
  318. package/dist/react-sdk/index.d.ts +0 -1
  319. package/dist/react-sdk/index.js +1 -1
  320. package/dist/react-sdk/loading-state/LoaderHost.d.ts +20 -0
  321. package/dist/react-sdk/loading-state/LoaderHost.js +1 -0
  322. package/dist/react-sdk/loading-state/built-in-loading-phase.d.ts +9 -0
  323. package/dist/react-sdk/loading-state/built-in-loading-phase.js +1 -0
  324. package/dist/react-sdk/loading-state/index.d.ts +3 -2
  325. package/dist/react-sdk/loading-state/index.js +1 -1
  326. package/dist/react-sdk/loading-state/{LoadingStateProvider.d.ts → loading-state-hooks.d.ts} +3 -12
  327. package/dist/react-sdk/loading-state/loading-state-hooks.js +1 -0
  328. package/dist/react-sdk/session/SessionServiceProvider.js +1 -1
  329. package/dist/react-sdk/web-component/create-web-component.js +1 -1
  330. package/dist/react-sdk/web-component/god-mode/god-mode-registry.d.ts +30 -0
  331. package/dist/react-sdk/web-component/god-mode/god-mode-registry.js +1 -0
  332. package/dist/react-sdk/web-component/god-mode/index.d.ts +1 -0
  333. package/dist/react-sdk/web-component/god-mode/index.js +1 -0
  334. package/dist/ui-sdk/icons/__generated/Lightbulb.d.ts +3 -0
  335. package/dist/ui-sdk/icons/__generated/Lightbulb.js +1 -0
  336. package/dist/ui-sdk/icons/__generated/XCircle.d.ts +3 -0
  337. package/dist/ui-sdk/icons/__generated/XCircle.js +1 -0
  338. package/dist/ui-sdk/icons/__generated/index.d.ts +4 -0
  339. package/dist/ui-sdk/icons/__generated/index.js +1 -1
  340. package/dist/ui-sdk/icons/icons-registry.js +1 -1
  341. package/dist/ui-sdk/responsive-application/ResponsiveApplicationProvider.d.ts +2 -2
  342. package/dist/ui-sdk/responsive-application/responsive-state-store.d.ts +11 -9
  343. package/dist/ui-sdk/responsive-application/use-store-responsive-info.js +1 -1
  344. package/dist/ui-sdk/theme/types.d.ts +5 -1
  345. package/package.json +13 -3
  346. package/schema/domain-spec.json +140 -0
  347. package/dist/configurator/ui/assistant/form/built-in-form-registry.d.ts +0 -10
  348. package/dist/configurator/ui/assistant/form/built-in-form-registry.js +0 -1
  349. package/dist/configurator/ui/assistant/form/form-registry.js +0 -8
  350. package/dist/domain/application/assistant/assistant-form-elements.d.ts +0 -28
  351. package/dist/domain/application/assistant/assistant-form-elements.js +0 -1
  352. package/dist/domain/extensions/client-side-image.js +0 -1
  353. package/dist/domain/extensions/save-template-thumbnail.js +0 -1
  354. package/dist/react-sdk/application-runtime/runtime/god-mode.d.ts +0 -19
  355. package/dist/react-sdk/application-runtime/runtime/god-mode.js +0 -1
  356. package/dist/react-sdk/loading-state/LoadingStateProvider.js +0 -1
  357. package/dist/react-sdk/loading-state/loading-state-manager.d.ts +0 -37
  358. package/dist/react-sdk/loading-state/loading-state-manager.js +0 -1
  359. package/dist/react-sdk/responsive-state/ResponsiveStateProvider.d.ts +0 -25
  360. package/dist/react-sdk/responsive-state/ResponsiveStateProvider.js +0 -1
  361. package/dist/react-sdk/responsive-state/canvas-padding.d.ts +0 -14
  362. package/dist/react-sdk/responsive-state/index.d.ts +0 -5
  363. package/dist/react-sdk/responsive-state/index.js +0 -1
  364. package/dist/react-sdk/responsive-state/responsive-state-manager.d.ts +0 -111
  365. package/dist/react-sdk/responsive-state/responsive-state-manager.js +0 -1
  366. package/dist/react-sdk/responsive-state/use-responsive-state-manager.d.ts +0 -10
  367. package/dist/react-sdk/responsive-state/use-responsive-state-manager.js +0 -1
  368. package/dist/react-sdk/responsive-state/use-responsive-state.d.ts +0 -11
  369. package/dist/react-sdk/responsive-state/use-responsive-state.js +0 -1
  370. /package/dist/{configurator/ui/assistant/form/form-element-definition.js → react-sdk/application-runtime/loading/types.js} +0 -0
  371. /package/dist/react-sdk/{responsive-state/canvas-padding.js → application-runtime/responsive/types.js} +0 -0
@@ -40,12 +40,19 @@ Without a lockfile, use the package manager the \`packageManager\` field of \`pa
40
40
  - \`src/components/\` — ECS system components (\`phx.domain.attachComponent.withEntity()\`)
41
41
  - \`src/elements/\` — Page-level React components (\`phx.frontend.createCustomPhoenixLayoutElement()\`)
42
42
  - \`ConfiguratorLayout\`: a layout element with this name replaces everything the built-in layout renders for a loaded configuration, in owner and read mode, finalization included. Prefer arranging the spec's layout components; write one for shell-level changes, such as moving the sidebar or adding an area the built-in layout has no place for
43
- - Mount \`LayoutSlots.SharedCanvas\` (from \`@salsita-ai/sdk/configurator\`) in a positioned root covering the page, or the app loader stays on screen; put the UI above it, positioned and after it in the markup. Call \`useSceneSafeArea()\` (same import) and put \`containerRef\` on the element covering the whole canvas and \`safeAreaRef\` on the region the UI leaves free, with \`pointerEvents: 'none'\`, so the camera frames the scene there
43
+ - Mount \`LayoutSlots.SharedCanvas\` (from \`@salsita-ai/sdk/configurator\`) in a positioned root covering the page; put the UI above it, positioned and after it in the markup. With the canvas mounted the app loader waits until the scene has loaded; a layout without a canvas is not held by the loader. Call \`phx.frontend.useSceneSafeArea()\` and put \`containerRef\` on the element covering the whole canvas and \`safeAreaRef\` on the region the UI leaves free, with \`pointerEvents: 'none'\`, so the camera frames the scene there
44
44
  - Sidebar moved, contents as the spec configures them: mount \`LayoutSlots.DesktopSidebar\` in a positioned container that sets its size, with \`side="start"\` on the left; \`docs/layouts/sidebar/in-a-custom-layout.mdx\` is the whole layout
45
- - Your own UI: "Hooks for a Project Layout" in \`docs/phoenix/configurator-interface.mdx\` lists the hooks with what each returns (\`pnpm phoenix docs -r docs/phoenix/configurator-interface.mdx\`), \`docs/phoenix/layout-slots.mdx\` the built-in pieces you can still mount, and \`docs/layouts/custom-layout.mdx\` is a worked example
46
- - Messages: mount \`LayoutSlots.Services\` for the built-in snackbars, or show violations, side-effect hints and errors yourself with \`phx.frontend.useConfiguratorMessages(onMessage)\`, not both. Either way mount a snackbar root (\`LayoutSlots.DesktopSnackbarsRoot\` or \`LayoutSlots.MobileSnackbarsRoot\`): save and built-in action errors are always snackbars
47
- - Finalize over the spec's \`finalizeAction\` components, each naming a handler registered with \`phx.frontend.createPhoenixHandler\`: run each with \`phx.frontend.useFinalizeAction(handler)\` in a component of its own wrapped in \`PhoenixHandlerBoundary\` (from \`@salsita-ai/sdk/react\`), or mount \`FinalizeActions\` (from \`@salsita-ai/sdk/configurator\`) for the built-in buttons. \`finalizePage\` and \`finalizeButton\` are legacy
48
- - Read mode and fullscreen are the layout's job, no hook enforces them: hide what a viewer must not change while \`phx.frontend.useAccessService().accessInfo.viewMode\` is \`true\`, and remove panels while \`phx.frontend.useIsFullscreenActive()\` is set; \`phx.frontend.useSetFullscreen()\` returns its setter
45
+ - Your own UI: "Hooks for a Project Layout" in \`docs/phoenix/configurator-interface.mdx\` lists the hooks with what each returns (\`pnpm phoenix docs -r docs/phoenix/configurator-interface.mdx\`), \`docs/phoenix/layout-slots.mdx\` the built-in pieces you can still mount, and \`docs/layouts/custom-layout.mdx\` is a worked example. Call the hooks as \`phx.frontend.useX()\`; besides those below, these are often needed: \`usePreview()\` (\`isPreview\` while the preview start screen shows), \`useCloseAction()\` (the host's close action or \`null\`), \`useActiveViewports()\` (the container's breakpoints, \`null\` until measured, \`desktop\` above 768 px: the desktop/phone switch), \`useResolveLocalizable()\` and \`useLocalizable(text)\` (spec texts to strings), \`useIsCloningAllowed()\` (a read-mode "edit a copy" action with \`LayoutSlots.CloneToEditDialog\`)
46
+ - Undo and redo: \`phx.frontend.useUndoRedo()\` returns \`{ canUndo, canRedo, undo, redo }\`; disable each button from \`canUndo\`/\`canRedo\`
47
+ - Tabs: \`phx.frontend.useTabNavigation()\` returns \`{ source, tabs, activeTab, select }\` for the spec's \`sidebarTabs\` (of the selected entity) or \`horizontalTabs\`; render \`tabs\` (each \`{ index, label, icon, state, isActive }\`, \`icon\` a PhxIcon name) and call \`select(tab.index)\` on click. Pass \`{ source: 'sidebarTabs' }\` or \`{ source: 'horizontalTabs' }\` when the layout shows one kind only. Not \`LayoutSlots.useSidebarTabs()\`: it serves the built-in sidebar and returns no tabs while its summary shows
48
+ - Steps: \`phx.frontend.useStepNavigation()\` returns \`{ steps, activeStep, activeIndex, isFirst, isLast, canGoNext, canGoPrevious, next, previous, goTo }\` for the spec's \`navigationSteps\`; render \`steps\` (each \`{ index, label, state, status }\`, \`status\` \`'complete' | 'active' | 'upcoming'\`), call \`next()\`/\`previous()\` for Back and Next and \`goTo(step.index)\` from a stepper. It enforces no order and no validity: enable only \`'complete'\` steps for an "earlier steps only" stepper, and to block moving on while the configuration has a problem, require \`phx.frontend.useStateValidity().severity !== 'warning'\` besides \`canGoNext\`: a change with a \`fixable\` or \`fatal\` violation is rejected, so the configuration is never invalid, and a \`warning\` is the problem that stays (it blocks finalizing too). \`useSteps\` and \`useHasSteps\` from \`@salsita-ai/sdk/configurator\` are deprecated
49
+ - Messages: mount \`LayoutSlots.Services\` for the built-in snackbars, or show violations, side-effect hints, errors and failed saves yourself with \`phx.frontend.useConfiguratorMessages(onMessage)\`, not both. A failed save arrives as \`saveError\`, the next successful save as \`saveRecovery\`; a layout that mounts neither shows no sign of a failed save. Either way mount a snackbar root (\`LayoutSlots.DesktopSnackbarsRoot\` or \`LayoutSlots.MobileSnackbarsRoot\`): errors of the built-in actions are always snackbars
50
+ - Finalize over the spec's \`finalizeAction\` components, each naming a handler registered with \`phx.frontend.createPhoenixHandler\`: \`phx.frontend.useFinalizeActions()\` returns \`{ actions, primary, secondary, disabled, hints }\`; render \`primary\` (and \`secondary\` in a menu or list) in a loop, each action \`{ key, label, title, icon, run, isPending, error, disabled }\`, and call \`action.run()\` on click (its promise never rejects; show \`action.error\` on failure). The configurator runs the handlers itself, outside the layout, so a handler must not rely on a provider the layout adds. For one known handler, \`phx.frontend.useFinalizeAction(handler)\` in a component wrapped in \`PhoenixHandlerBoundary\` (from \`@salsita-ai/sdk/react\`) works too; \`FinalizeActions\` (from \`@salsita-ai/sdk/configurator\`) mounts the built-in buttons. \`finalizePage\` and \`finalizeButton\` are legacy
51
+ - Read mode and fullscreen are the layout's job, no hook enforces them (\`useUndoRedo\`, \`useFinalizeActions\` and \`useStepNavigation\` included): hide undo, redo, finalization and whatever else a viewer must not change while \`phx.frontend.useAccessService().accessInfo.viewMode\` is \`true\` (tabs are navigation, so a viewer may switch them), and remove panels while \`phx.frontend.useIsFullscreenActive()\` is set; \`phx.frontend.useSetFullscreen()\` returns its setter
52
+ - \`ConfigurationNotFoundPage\`: \`phx.frontend.createConfigurationNotFoundPage(({ onBack }) => …)\` replaces the "configuration not accessible" page; show a way back only when the optional \`onBack\` is set. The not-authenticated page's default renders it too; only the admin's not-authorized page keeps the built-in page
53
+ - \`ConfiguratorErrorPage\`: \`phx.frontend.createConfiguratorErrorPage(({ reason, onRetry }) => …)\` replaces the production error page; \`reason\` is \`uncaughtError\` or \`presetCreationFailed\`. A page that throws falls back to the built-in one, and development keeps the detailed error pages, so the project page shows only in a production build. Register both pages with these helpers: \`createCustomPhoenixLayoutElement\` with the name works too, but types the component as taking only \`children\`, so the typed props do not compile there in a strict project
54
+ - \`AppLoaderContent\`: \`phx.frontend.createAppLoader(({ phases }) => …)\` replaces what the loading overlay shows (each phase \`{ id, label?, progress? }\`, the list possibly empty); Phoenix keeps the overlay itself
55
+ - Assistant form elements: an element declared under \`assistant.formElements\` (\`description\`, \`role\`, scalar \`attributes\` beside the built-in \`label\`, a \`value\` included, \`required\`) is the default export \`phx.frontend.createAssistantFormElement('name', Component)\` of one file; the name types the component's props by its role (\`AssistantFormElementProps<'name'>\`): an option reports its answer with \`onSelect(answer)\`, an input with \`onChange(state, answer)\`; and the application does not start while a declared element has no registration or a registration no declaration
49
56
  - \`src/handlers/\` — Event & lifecycle handlers
50
57
  - \`src/webhooks/\` — Server-side webhook handlers (\`phx.webhooks.*Builder\`); the webhook is the default export of \`src/webhooks/index.ts\`, which you create when the project needs its own handlers — without it the build uses a built-in inert webhook that answers only anonymous auth, so create the file before setting \`useAuthHook\` or \`useClerk\`
51
58
  - \`src/migrations/\` — Schema version migrations (v*.ts)
@@ -87,7 +94,7 @@ The default export must be what the folder's phx factory returns (for example \`
87
94
  readonly webhookMembership: `## Webhook Membership
88
95
  - User-authored components, admin components, and handlers are frontend-only by default.
89
96
  - A handler that registers \`phx.domain.createAssistantSetupHandler\`, \`createMapPublishedRevisionDataHandler\`, \`createMapPublishedChunkedDataHandler\`, \`createDataRowValidatorHandler\`, or \`createSheetValidatorHandler\` is included in the webhook automatically.
90
- - An assistant setup handler receives the declaration-shaped whitelist and { role, sessionData, access }. Keep the exact entity/alias split from YAML; inherited fields and expanded member entries are absent. Narrow original declarations, then the SDK expands them. Use project-defined role/session data for per-user customization. Keep the handler synchronous, pure and deterministic. The handler runs in the browser and webhook. Return null to disable assistant execution, or omit whitelist entries to restrict them. Button and panel visibility use spec presence; project developers own dynamic UI filtering through normal layout mechanics such as $filter. Leaves expose optional descriptions; writable options allow only validate: true and allowDryRuns: false; recipe groups allow descriptions and allowDryRuns: false. Deletable entries are {}. Names, component bindings, hints and types are internal. Omitted overrides retain baseline values. Existing entity, enhanced-entity and action types constrain the handler; no separate generated manifest types. Browser GraphQL, tools and change recording use the session-derived manifest; rejected setup fails closed. UI reads the pure spec selector. Omitted server instructions or AI fields preserve the baseline. An empty server instructions string clears it.
97
+ - An assistant setup handler receives the declaration-shaped whitelist and { role, sessionData, access }. Keep the exact entity/alias split from YAML; inherited fields and expanded member entries are absent. Narrow original declarations, then the SDK expands them. Use project-defined role/session data for per-user customization. Keep the handler synchronous, pure and deterministic. The handler runs in the browser and webhook. Return null to disable assistant execution, or omit whitelist entries to restrict them. Button and panel visibility use spec presence; project developers own dynamic UI filtering through normal layout mechanics such as $filter. Leaves expose optional descriptions; writable options allow only validate: true and allowDryRuns: false; recipe groups allow descriptions and allowDryRuns: false; screenshot views allow descriptions. Deletable entries are {}. Names, component bindings, hints and types are internal. Omitted overrides retain baseline values. Existing entity, enhanced-entity and action types constrain the handler; no separate generated manifest types. Browser GraphQL, tools and change recording use the session-derived manifest; rejected setup fails closed. UI reads the pure spec selector. Omitted server instructions or AI fields preserve the baseline. An empty server instructions string clears it.
91
98
  - To include another component or handler in the webhook, put \`'use webhook'\` as its first statement (leading comments are allowed).
92
99
  - A webhook-included file and its runtime local-import closure must not import browser SDK entrypoints (\`application\`, \`admin\`, \`configurator\`, \`editor\`, \`react\`, \`ui\`) or style/font/image assets. Spec parsing reports the unsafe import and inclusion reason as a diagnostic.
93
100
  - Keep browser rendering in an unpinned file. Move shared server-safe logic into a separate module when both frontend and webhook code need it.`;
@@ -1,10 +1,10 @@
1
- export const sdkGuidanceSections={packageManager:"## Package Manager\nThe project installs with its own package manager, and the commands in this skill are written for pnpm. Run them through the package manager the project uses, which the lockfile in the project root names:\n- `pnpm-lock.yaml`: `pnpm phoenix <command>`, `pnpm install`, `pnpm add <package>`\n- `package-lock.json`: `npx phoenix <command>`, `npm install`, `npm install <package>`\n- `yarn.lock`: `yarn phoenix <command>`, `yarn install`, `yarn add <package>`\n- `bun.lock` or `bun.lockb`: `bunx phoenix <command>`, `bun install`, `bun add <package>`\n\nWithout a lockfile, use the package manager the `packageManager` field of `package.json` names, or ask the user.",buildCommands:"## Build Commands\n- `pnpm phoenix build` — Full production build\n- `pnpm phoenix dev` — Development mode with hot reload\n- `pnpm phoenix sync` — Sync generated files without full build\n- `pnpm phoenix deps` — Add the SDK's missing peer dependencies to `package.json` and move out-of-range ones into the SDK's range; it installs nothing, so install after it",devLogs:"## Dev Logs\n- `pnpm phoenix dev` writes all logs to `.phoenix/phoenix-dev.log`\n- Contains: spec parse errors, build errors, browser runtime errors, file watcher events\n- After making changes, always check the log for errors: `tail -50 .phoenix/phoenix-dev.log` or `grep ERROR .phoenix/phoenix-dev.log`\n- Rotated automatically at 10MB (`.log.1`, `.log.2` are older rotations)",projectStructure:"## Project Structure\n- `spec/` — Domain YAML specs (entities, relations, expressions, invariants, components, application config)\n- `src/expressions/` — Computed/derived entity properties (`phx.domain.implementCustomExpressions()`)\n- `src/actions/` — Domain operations declared under `actions:` in the spec (`phx.domain.implementActions()`), run by controls and exposed to the assistant\n- `src/invariants/` — Business rule constraints (`phx.domain.implementCustomInvariants()`)\n- `src/transformers/` — Automatic state fixers (`phx.frontend.transformerBuilder`)\n- `src/components/` — ECS system components (`phx.domain.attachComponent.withEntity()`)\n- `src/elements/` — Page-level React components (`phx.frontend.createCustomPhoenixLayoutElement()`)\n - `ConfiguratorLayout`: a layout element with this name replaces everything the built-in layout renders for a loaded configuration, in owner and read mode, finalization included. Prefer arranging the spec's layout components; write one for shell-level changes, such as moving the sidebar or adding an area the built-in layout has no place for\n - Mount `LayoutSlots.SharedCanvas` (from `@salsita-ai/sdk/configurator`) in a positioned root covering the page, or the app loader stays on screen; put the UI above it, positioned and after it in the markup. Call `useSceneSafeArea()` (same import) and put `containerRef` on the element covering the whole canvas and `safeAreaRef` on the region the UI leaves free, with `pointerEvents: 'none'`, so the camera frames the scene there\n - Sidebar moved, contents as the spec configures them: mount `LayoutSlots.DesktopSidebar` in a positioned container that sets its size, with `side=\"start\"` on the left; `docs/layouts/sidebar/in-a-custom-layout.mdx` is the whole layout\n - Your own UI: \"Hooks for a Project Layout\" in `docs/phoenix/configurator-interface.mdx` lists the hooks with what each returns (`pnpm phoenix docs -r docs/phoenix/configurator-interface.mdx`), `docs/phoenix/layout-slots.mdx` the built-in pieces you can still mount, and `docs/layouts/custom-layout.mdx` is a worked example\n - Messages: mount `LayoutSlots.Services` for the built-in snackbars, or show violations, side-effect hints and errors yourself with `phx.frontend.useConfiguratorMessages(onMessage)`, not both. Either way mount a snackbar root (`LayoutSlots.DesktopSnackbarsRoot` or `LayoutSlots.MobileSnackbarsRoot`): save and built-in action errors are always snackbars\n - Finalize over the spec's `finalizeAction` components, each naming a handler registered with `phx.frontend.createPhoenixHandler`: run each with `phx.frontend.useFinalizeAction(handler)` in a component of its own wrapped in `PhoenixHandlerBoundary` (from `@salsita-ai/sdk/react`), or mount `FinalizeActions` (from `@salsita-ai/sdk/configurator`) for the built-in buttons. `finalizePage` and `finalizeButton` are legacy\n - Read mode and fullscreen are the layout's job, no hook enforces them: hide what a viewer must not change while `phx.frontend.useAccessService().accessInfo.viewMode` is `true`, and remove panels while `phx.frontend.useIsFullscreenActive()` is set; `phx.frontend.useSetFullscreen()` returns its setter\n- `src/handlers/` — Event & lifecycle handlers\n- `src/webhooks/` — Server-side webhook handlers (`phx.webhooks.*Builder`); the webhook is the default export of `src/webhooks/index.ts`, which you create when the project needs its own handlers — without it the build uses a built-in inert webhook that answers only anonymous auth, so create the file before setting `useAuthHook` or `useClerk`\n- `src/migrations/` — Schema version migrations (v*.ts)\n- `src/locales/` — Locale catalogs (authored .po sources; compiled .ts, do not edit)\n- `src/admin/` — Admin interface (mirrors main src/ structure)\n- `src/_shared/` — Shared utilities (NOT auto-imported, requires explicit imports)\n- `.phoenix/` — Auto-generated files (DO NOT EDIT, except the option files in `.phoenix/variants/`)\n- `public/` — Static assets (3D models, textures, images, HDRI maps, fonts)\n- `assets/` — Uploadable resources (PDF templates, email assets)",architecturePrinciples:"## Architecture Principles\n- YAML-first: 99% of configuration in spec/*.yaml, custom code is last resort\n- ECS pattern: entities (domain objects) + components (behavior/UI) + systems (SDK runtime)\n- Reactive state: immutable domain state flows through RxJS pipelines\n- Data binding: `$read` (read-only), `$write` (write-only), `$bind` (bidirectional) connect components to domain\n- memoEval: all component implementations must use memoEval for stable object references",keyPatterns:"## Key Patterns\n- Expressions = computed properties on entities (prefix: `$`), implemented per-entity in src/expressions/\n- Actions = named domain operations (`actions:` in YAML with `params` and `result` schemas), implemented in src/actions/, prepared through `actionSystem.prepareAction()` and exposed as assistant mutations through `assistant.actions`\n- Invariants = validation rules returning violations, static or dynamic\n- Transformers = auto-fixers maintaining consistency after state changes\n- Components defined in YAML with `$read`/`$bind` bindings, or custom in src/components/ using attachComponent builder\n- Webhooks = server-side finalization tasks (PDF, email, CSV, image rendering)\n- Filters = contextual component activation via navigationStates, viewStates, modes, selectedEntityIds",fileConventions:`## File Conventions
1
+ export const sdkGuidanceSections={packageManager:"## Package Manager\nThe project installs with its own package manager, and the commands in this skill are written for pnpm. Run them through the package manager the project uses, which the lockfile in the project root names:\n- `pnpm-lock.yaml`: `pnpm phoenix <command>`, `pnpm install`, `pnpm add <package>`\n- `package-lock.json`: `npx phoenix <command>`, `npm install`, `npm install <package>`\n- `yarn.lock`: `yarn phoenix <command>`, `yarn install`, `yarn add <package>`\n- `bun.lock` or `bun.lockb`: `bunx phoenix <command>`, `bun install`, `bun add <package>`\n\nWithout a lockfile, use the package manager the `packageManager` field of `package.json` names, or ask the user.",buildCommands:"## Build Commands\n- `pnpm phoenix build` — Full production build\n- `pnpm phoenix dev` — Development mode with hot reload\n- `pnpm phoenix sync` — Sync generated files without full build\n- `pnpm phoenix deps` — Add the SDK's missing peer dependencies to `package.json` and move out-of-range ones into the SDK's range; it installs nothing, so install after it",devLogs:"## Dev Logs\n- `pnpm phoenix dev` writes all logs to `.phoenix/phoenix-dev.log`\n- Contains: spec parse errors, build errors, browser runtime errors, file watcher events\n- After making changes, always check the log for errors: `tail -50 .phoenix/phoenix-dev.log` or `grep ERROR .phoenix/phoenix-dev.log`\n- Rotated automatically at 10MB (`.log.1`, `.log.2` are older rotations)",projectStructure:"## Project Structure\n- `spec/` — Domain YAML specs (entities, relations, expressions, invariants, components, application config)\n- `src/expressions/` — Computed/derived entity properties (`phx.domain.implementCustomExpressions()`)\n- `src/actions/` — Domain operations declared under `actions:` in the spec (`phx.domain.implementActions()`), run by controls and exposed to the assistant\n- `src/invariants/` — Business rule constraints (`phx.domain.implementCustomInvariants()`)\n- `src/transformers/` — Automatic state fixers (`phx.frontend.transformerBuilder`)\n- `src/components/` — ECS system components (`phx.domain.attachComponent.withEntity()`)\n- `src/elements/` — Page-level React components (`phx.frontend.createCustomPhoenixLayoutElement()`)\n - `ConfiguratorLayout`: a layout element with this name replaces everything the built-in layout renders for a loaded configuration, in owner and read mode, finalization included. Prefer arranging the spec's layout components; write one for shell-level changes, such as moving the sidebar or adding an area the built-in layout has no place for\n - Mount `LayoutSlots.SharedCanvas` (from `@salsita-ai/sdk/configurator`) in a positioned root covering the page; put the UI above it, positioned and after it in the markup. With the canvas mounted the app loader waits until the scene has loaded; a layout without a canvas is not held by the loader. Call `phx.frontend.useSceneSafeArea()` and put `containerRef` on the element covering the whole canvas and `safeAreaRef` on the region the UI leaves free, with `pointerEvents: 'none'`, so the camera frames the scene there\n - Sidebar moved, contents as the spec configures them: mount `LayoutSlots.DesktopSidebar` in a positioned container that sets its size, with `side=\"start\"` on the left; `docs/layouts/sidebar/in-a-custom-layout.mdx` is the whole layout\n - Your own UI: \"Hooks for a Project Layout\" in `docs/phoenix/configurator-interface.mdx` lists the hooks with what each returns (`pnpm phoenix docs -r docs/phoenix/configurator-interface.mdx`), `docs/phoenix/layout-slots.mdx` the built-in pieces you can still mount, and `docs/layouts/custom-layout.mdx` is a worked example. Call the hooks as `phx.frontend.useX()`; besides those below, these are often needed: `usePreview()` (`isPreview` while the preview start screen shows), `useCloseAction()` (the host's close action or `null`), `useActiveViewports()` (the container's breakpoints, `null` until measured, `desktop` above 768 px: the desktop/phone switch), `useResolveLocalizable()` and `useLocalizable(text)` (spec texts to strings), `useIsCloningAllowed()` (a read-mode \"edit a copy\" action with `LayoutSlots.CloneToEditDialog`)\n - Undo and redo: `phx.frontend.useUndoRedo()` returns `{ canUndo, canRedo, undo, redo }`; disable each button from `canUndo`/`canRedo`\n - Tabs: `phx.frontend.useTabNavigation()` returns `{ source, tabs, activeTab, select }` for the spec's `sidebarTabs` (of the selected entity) or `horizontalTabs`; render `tabs` (each `{ index, label, icon, state, isActive }`, `icon` a PhxIcon name) and call `select(tab.index)` on click. Pass `{ source: 'sidebarTabs' }` or `{ source: 'horizontalTabs' }` when the layout shows one kind only. Not `LayoutSlots.useSidebarTabs()`: it serves the built-in sidebar and returns no tabs while its summary shows\n - Steps: `phx.frontend.useStepNavigation()` returns `{ steps, activeStep, activeIndex, isFirst, isLast, canGoNext, canGoPrevious, next, previous, goTo }` for the spec's `navigationSteps`; render `steps` (each `{ index, label, state, status }`, `status` `'complete' | 'active' | 'upcoming'`), call `next()`/`previous()` for Back and Next and `goTo(step.index)` from a stepper. It enforces no order and no validity: enable only `'complete'` steps for an \"earlier steps only\" stepper, and to block moving on while the configuration has a problem, require `phx.frontend.useStateValidity().severity !== 'warning'` besides `canGoNext`: a change with a `fixable` or `fatal` violation is rejected, so the configuration is never invalid, and a `warning` is the problem that stays (it blocks finalizing too). `useSteps` and `useHasSteps` from `@salsita-ai/sdk/configurator` are deprecated\n - Messages: mount `LayoutSlots.Services` for the built-in snackbars, or show violations, side-effect hints, errors and failed saves yourself with `phx.frontend.useConfiguratorMessages(onMessage)`, not both. A failed save arrives as `saveError`, the next successful save as `saveRecovery`; a layout that mounts neither shows no sign of a failed save. Either way mount a snackbar root (`LayoutSlots.DesktopSnackbarsRoot` or `LayoutSlots.MobileSnackbarsRoot`): errors of the built-in actions are always snackbars\n - Finalize over the spec's `finalizeAction` components, each naming a handler registered with `phx.frontend.createPhoenixHandler`: `phx.frontend.useFinalizeActions()` returns `{ actions, primary, secondary, disabled, hints }`; render `primary` (and `secondary` in a menu or list) in a loop, each action `{ key, label, title, icon, run, isPending, error, disabled }`, and call `action.run()` on click (its promise never rejects; show `action.error` on failure). The configurator runs the handlers itself, outside the layout, so a handler must not rely on a provider the layout adds. For one known handler, `phx.frontend.useFinalizeAction(handler)` in a component wrapped in `PhoenixHandlerBoundary` (from `@salsita-ai/sdk/react`) works too; `FinalizeActions` (from `@salsita-ai/sdk/configurator`) mounts the built-in buttons. `finalizePage` and `finalizeButton` are legacy\n - Read mode and fullscreen are the layout's job, no hook enforces them (`useUndoRedo`, `useFinalizeActions` and `useStepNavigation` included): hide undo, redo, finalization and whatever else a viewer must not change while `phx.frontend.useAccessService().accessInfo.viewMode` is `true` (tabs are navigation, so a viewer may switch them), and remove panels while `phx.frontend.useIsFullscreenActive()` is set; `phx.frontend.useSetFullscreen()` returns its setter\n - `ConfigurationNotFoundPage`: `phx.frontend.createConfigurationNotFoundPage(({ onBack }) => …)` replaces the \"configuration not accessible\" page; show a way back only when the optional `onBack` is set. The not-authenticated page's default renders it too; only the admin's not-authorized page keeps the built-in page\n - `ConfiguratorErrorPage`: `phx.frontend.createConfiguratorErrorPage(({ reason, onRetry }) => …)` replaces the production error page; `reason` is `uncaughtError` or `presetCreationFailed`. A page that throws falls back to the built-in one, and development keeps the detailed error pages, so the project page shows only in a production build. Register both pages with these helpers: `createCustomPhoenixLayoutElement` with the name works too, but types the component as taking only `children`, so the typed props do not compile there in a strict project\n - `AppLoaderContent`: `phx.frontend.createAppLoader(({ phases }) => …)` replaces what the loading overlay shows (each phase `{ id, label?, progress? }`, the list possibly empty); Phoenix keeps the overlay itself\n - Assistant form elements: an element declared under `assistant.formElements` (`description`, `role`, scalar `attributes` beside the built-in `label`, a `value` included, `required`) is the default export `phx.frontend.createAssistantFormElement('name', Component)` of one file; the name types the component's props by its role (`AssistantFormElementProps<'name'>`): an option reports its answer with `onSelect(answer)`, an input with `onChange(state, answer)`; and the application does not start while a declared element has no registration or a registration no declaration\n- `src/handlers/` — Event & lifecycle handlers\n- `src/webhooks/` — Server-side webhook handlers (`phx.webhooks.*Builder`); the webhook is the default export of `src/webhooks/index.ts`, which you create when the project needs its own handlers — without it the build uses a built-in inert webhook that answers only anonymous auth, so create the file before setting `useAuthHook` or `useClerk`\n- `src/migrations/` — Schema version migrations (v*.ts)\n- `src/locales/` — Locale catalogs (authored .po sources; compiled .ts, do not edit)\n- `src/admin/` — Admin interface (mirrors main src/ structure)\n- `src/_shared/` — Shared utilities (NOT auto-imported, requires explicit imports)\n- `.phoenix/` — Auto-generated files (DO NOT EDIT, except the option files in `.phoenix/variants/`)\n- `public/` — Static assets (3D models, textures, images, HDRI maps, fonts)\n- `assets/` — Uploadable resources (PDF templates, email assets)",architecturePrinciples:"## Architecture Principles\n- YAML-first: 99% of configuration in spec/*.yaml, custom code is last resort\n- ECS pattern: entities (domain objects) + components (behavior/UI) + systems (SDK runtime)\n- Reactive state: immutable domain state flows through RxJS pipelines\n- Data binding: `$read` (read-only), `$write` (write-only), `$bind` (bidirectional) connect components to domain\n- memoEval: all component implementations must use memoEval for stable object references",keyPatterns:"## Key Patterns\n- Expressions = computed properties on entities (prefix: `$`), implemented per-entity in src/expressions/\n- Actions = named domain operations (`actions:` in YAML with `params` and `result` schemas), implemented in src/actions/, prepared through `actionSystem.prepareAction()` and exposed as assistant mutations through `assistant.actions`\n- Invariants = validation rules returning violations, static or dynamic\n- Transformers = auto-fixers maintaining consistency after state changes\n- Components defined in YAML with `$read`/`$bind` bindings, or custom in src/components/ using attachComponent builder\n- Webhooks = server-side finalization tasks (PDF, email, CSV, image rendering)\n- Filters = contextual component activation via navigationStates, viewStates, modes, selectedEntityIds",fileConventions:`## File Conventions
2
2
  - Components: PascalCase (.tsx)
3
3
  - Utilities: kebab-case (.ts)
4
4
  - Expressions: entity-name.ts
5
5
  - Migrations: v*.ts (sequential)
6
6
  - Specs: kebab-case.yaml
7
- - Underscore folders (_shared/, _utils/) are NOT auto-imported`,exportRules:'## Export Rules\nAll files in src/expressions/, src/actions/, src/invariants/, src/transformers/, src/components/, src/handlers/, src/elements/, src/webhooks/ MUST use default exports. Can be a single item or an array. Named exports are ignored by the SDK auto-import system.\nThe default export must be what the folder\'s phx factory returns (for example `phx.frontend.createCustomPhoenixLayoutElement()` in src/elements/). Any other value, such as a plain React component, stops the application at start with an "Invalid default export in <file>" error. Put plain components and helpers in an underscore folder.',webhookMembership:"## Webhook Membership\n- User-authored components, admin components, and handlers are frontend-only by default.\n- A handler that registers `phx.domain.createAssistantSetupHandler`, `createMapPublishedRevisionDataHandler`, `createMapPublishedChunkedDataHandler`, `createDataRowValidatorHandler`, or `createSheetValidatorHandler` is included in the webhook automatically.\n- An assistant setup handler receives the declaration-shaped whitelist and { role, sessionData, access }. Keep the exact entity/alias split from YAML; inherited fields and expanded member entries are absent. Narrow original declarations, then the SDK expands them. Use project-defined role/session data for per-user customization. Keep the handler synchronous, pure and deterministic. The handler runs in the browser and webhook. Return null to disable assistant execution, or omit whitelist entries to restrict them. Button and panel visibility use spec presence; project developers own dynamic UI filtering through normal layout mechanics such as $filter. Leaves expose optional descriptions; writable options allow only validate: true and allowDryRuns: false; recipe groups allow descriptions and allowDryRuns: false. Deletable entries are {}. Names, component bindings, hints and types are internal. Omitted overrides retain baseline values. Existing entity, enhanced-entity and action types constrain the handler; no separate generated manifest types. Browser GraphQL, tools and change recording use the session-derived manifest; rejected setup fails closed. UI reads the pure spec selector. Omitted server instructions or AI fields preserve the baseline. An empty server instructions string clears it.\n- To include another component or handler in the webhook, put `'use webhook'` as its first statement (leading comments are allowed).\n- A webhook-included file and its runtime local-import closure must not import browser SDK entrypoints (`application`, `admin`, `configurator`, `editor`, `react`, `ui`) or style/font/image assets. Spec parsing reports the unsafe import and inclusion reason as a diagnostic.\n- Keep browser rendering in an unpinned file. Move shared server-safe logic into a separate module when both frontend and webhook code need it.",doNotEdit:"## Do Not Edit\n- `.phoenix/` directory — completely regenerated on each build. The one exception is `.phoenix/variants/`, where the `phoenix-variants` skill has you write option files; a rebuild keeps it. The project's TypeScript settings come from `.phoenix/tsconfig.json`; the root `tsconfig.json` is the project's and extends it. `phoenix dev`, `phoenix build` and the webhook bundle do not resolve `paths` aliases, so an alias added to the root file passes `phoenix check` and fails the build; use relative imports\n- `src/locales/` .ts files — compiled from .po files\n- `phoenix` and `phoenix-*` folders under `.claude/skills/` and `.agents/skills/` — Phoenix rewrites its skills on every sync, removes anything else inside their folders and removes any other `phoenix-*` skill folder. To customise a skill, copy it to a folder whose name does not start with `phoenix`",modelVersioning:"## Version the Domain Model Once It Is Deployed\nA deployed model version cannot change, because saved configurations depend on it. `pnpm phoenix deploy` is refused, before anything is uploaded, when the model differs from the one already deployed under the same version. The version is not written anywhere: `model.latest` is always the highest numbered version plus one, so a spec with only `latest` is v1, and `applicationVersion` plays no part. `pnpm phoenix dev` accepts every model change, so a working preview does not show that the deploy will pass.\n\nTo change a model that has been deployed as v<N>:\n1. Keep the deployed model, unchanged, as `model.v<N>` in the spec. A file of its own, such as `spec/model-v<N>.yaml`, keeps `latest` readable.\n2. Make the change in `model.latest`, which is now v<N+1>.\n3. Add `src/migrations/v<N+1>.ts` exporting `migrateToV<N+1>`. It receives the saved entities of v<N> and returns entities that are valid in v<N+1>; `ModelV<N>` and `ModelV<N+1>` are generated global namespaces.\n\nThe admin model is versioned on its own, under `src/admin/migrations/`, with its types in `AdminV<N>`.\n\n**Replacing the starter.** When the first deployment of the workspace was the starter, its `Box` model is v1 in production. When the real model replaces it, keep the starter's model as `model.v1` and define the real model in `latest`. Configurations saved from the starter are not worth converting, so the migration returns the starting state of the new model, the `initialDomainEntities` of its preset, and drops the boxes:\n```typescript\n// src/migrations/v2.ts\nimport type { Migration } from '@salsita-ai/sdk/domain'\n\nexport const migrateToV2: Migration<ModelV1.AnyDomainEntity, ModelV2.AnyDomainEntity> = () => [\n // the entities of the new model's preset\n]\n```\nAn entity of a type the new version no longer has must not stay in the returned state. An entity id starts with its type (`Box-1`), so an entity that keeps its data under a new type name needs a new id and every relation pointing at it rewritten. Read `docs/phoenix/migrations.mdx` before writing any other migration.",sdkDocumentation:'## SDK Documentation\n\nAlways search SDK docs for relevant examples before implementing Phoenix features.\n\n### Commands\n- `pnpm phoenix docs` — Full documentation index with descriptions\n- `pnpm phoenix docs -s <term>` — Search all doc content and metadata (case-insensitive)\n- `pnpm phoenix docs --dir` — Print the absolute docs directory for grep or ripgrep\n- `pnpm phoenix docs -r <path>` — Read specific doc (raw text to stdout)\n- `pnpm phoenix docs -c <category>` — List docs in a category\n- `pnpm phoenix docs -l` — List all doc paths with titles\n\n### Key Docs\n- `docs/phoenix/getting-started.mdx` — Setup, prerequisites, first project\n- `docs/phoenix/quickstart.mdx` — End-to-end tutorial building a configurator\n- `docs/phoenix/domain-model.mdx` — Entities, attributes, relations, specs\n- `docs/phoenix/components.mdx` — ECS pattern, YAML components, custom components\n- `docs/phoenix/project-structure.mdx` — Files, folders, conventions\n- `docs/phoenix/best-practices.mdx` — YAML-first, code patterns, common pitfalls\n- `docs/phoenix/state-management.mdx` — Reactive state, RxJS pipelines, data flow\n- `docs/phoenix/expression-context.mdx` — Expression API, context object, helpers\n- `docs/phoenix/webhooks.mdx` — Server-side finalization workflows\n- `docs/phoenix/finalization.mdx` — Finalization system, PDF/email/CSV generation\n- `docs/phoenix/migrations.mdx` — Schema versioning, migration files\n\n### Agent Workflow\n1. Find relevant docs by content: `pnpm phoenix docs -s \'camera\'`\n2. Read matching doc: `pnpm phoenix docs -r docs/3d-scene/cameras/transitions.mdx`\n3. Search within a doc: `pnpm phoenix docs -r <path> | grep "example"`\n4. Search titles only as a fallback: `pnpm phoenix docs -l | grep "camera"`\n5. Search generated files directly when needed: `grep -rn "camera" "$(pnpm phoenix docs --dir)"`\n6. Browse a topic area: `pnpm phoenix docs -c "Layouts"`',assetInspection:"## 3D Asset Inspection\n\nRun `pnpm phoenix inspect:assets` to discover all glTF/GLB files in `public/` and get a summary table. Use `--file <path>` for detailed single-file inspection.\n\n**When to use:** before writing YAML/code that references objectName, materialsToOverride, morphTargetInfluences, or object positioning.\n\n**What it shows per node:**\n- Name, type (Mesh/Light/Camera/Empty), children\n- Transform: `pos=`, `rot=`, `scale=` (only non-default values shown)\n- Per-node morph targets: `morphs=Width,Height`\n- Bounding box: `bounds=WxHxD` (width x height x depth in meters)\n- Origin placement: `origin=center`, `bottom-center`, `top-center`, `bottom-corner`, etc. with numerical offset\n\n**Use cases:**\n- Find exact node names for `objectName` in scene entity components\n- Find material names for `materialsToOverride`\n- Find morph target names for `morphTargetInfluences`\n- Check model dimensions (bounds) for positioning, dimension indicators, or invariants\n- Check origin placement to understand how models anchor (center vs bottom-center)\n- Use `--json` for programmatic access to all data\n\n**Flags:** `--objects`, `--materials`, `--morphs`, `--animations`, `--json`",screenshotCli:"## Screenshot CLI\n\nCapture 3D canvas screenshots from a running dev server via `pnpm phoenix screenshot`.\n\n**Prerequisites:**\n1. `phoenix dev` must be running (starts the WebSocket server on port 3020)\n2. Configurator must be open in a browser (the browser does the actual rendering)\n\n**Options:**\n- `--output, -o` — Output file path (default: `./screenshot.png`)\n- `--width, -w` — Width in pixels (default: 1920)\n- `--height` — Height in pixels (default: 1080)\n- `--format, -f` — Image format: PNG or JPEG (default: PNG)\n- `--port` — WebSocket server port (default: 3020)\n\n**Example workflow:**\n1. Start dev server: `pnpm phoenix dev`\n2. Open configurator in browser (or via Playwright MCP)\n3. Take screenshot: `pnpm phoenix screenshot -o ./render.png -w 2560 --height 1440`\n\n**Use cases:** visual regression testing, generating product images, debugging 3D renders",inspectState:"## Inspect: State\n\nGet the current editor runtime state as JSON via `pnpm phoenix inspect:state`.\n\n**Prerequisites:** same as screenshot (dev server running + browser open)\n\n**Options:**\n- `--output, -o` — Output file path (prints to stdout if omitted)\n- `--sections, -s` — Comma-separated: entities, navigation, validation (default: all)\n- `--pretty` — Pretty-print JSON (default: true)\n- `--port` — WebSocket server port (default: 3020)\n\n**Example:** `pnpm phoenix inspect:state -o ./state.json -s entities,navigation`\n\n**Lifecycle status:** Always included in response. Shows which systems are ready:\n- `domainLoaded` — domain data loaded\n- `domainContextReady` — domain context initialized (entities available)\n- `configuratorReady` — 3D configurator initialized\n- `sessionAuthenticated` — session service active\n- `editorMode` — editor mode enabled\n\n**Tips:** Use `--sections` to limit payload size. Pipe through `head`/`tail` for partial output (e.g., `pnpm phoenix inspect:state | head -50`).\n\n**Use cases:** AI agent context, debugging entity state, inspecting navigation/validation",inspectData:`## Inspect: Data
7
+ - Underscore folders (_shared/, _utils/) are NOT auto-imported`,exportRules:'## Export Rules\nAll files in src/expressions/, src/actions/, src/invariants/, src/transformers/, src/components/, src/handlers/, src/elements/, src/webhooks/ MUST use default exports. Can be a single item or an array. Named exports are ignored by the SDK auto-import system.\nThe default export must be what the folder\'s phx factory returns (for example `phx.frontend.createCustomPhoenixLayoutElement()` in src/elements/). Any other value, such as a plain React component, stops the application at start with an "Invalid default export in <file>" error. Put plain components and helpers in an underscore folder.',webhookMembership:"## Webhook Membership\n- User-authored components, admin components, and handlers are frontend-only by default.\n- A handler that registers `phx.domain.createAssistantSetupHandler`, `createMapPublishedRevisionDataHandler`, `createMapPublishedChunkedDataHandler`, `createDataRowValidatorHandler`, or `createSheetValidatorHandler` is included in the webhook automatically.\n- An assistant setup handler receives the declaration-shaped whitelist and { role, sessionData, access }. Keep the exact entity/alias split from YAML; inherited fields and expanded member entries are absent. Narrow original declarations, then the SDK expands them. Use project-defined role/session data for per-user customization. Keep the handler synchronous, pure and deterministic. The handler runs in the browser and webhook. Return null to disable assistant execution, or omit whitelist entries to restrict them. Button and panel visibility use spec presence; project developers own dynamic UI filtering through normal layout mechanics such as $filter. Leaves expose optional descriptions; writable options allow only validate: true and allowDryRuns: false; recipe groups allow descriptions and allowDryRuns: false; screenshot views allow descriptions. Deletable entries are {}. Names, component bindings, hints and types are internal. Omitted overrides retain baseline values. Existing entity, enhanced-entity and action types constrain the handler; no separate generated manifest types. Browser GraphQL, tools and change recording use the session-derived manifest; rejected setup fails closed. UI reads the pure spec selector. Omitted server instructions or AI fields preserve the baseline. An empty server instructions string clears it.\n- To include another component or handler in the webhook, put `'use webhook'` as its first statement (leading comments are allowed).\n- A webhook-included file and its runtime local-import closure must not import browser SDK entrypoints (`application`, `admin`, `configurator`, `editor`, `react`, `ui`) or style/font/image assets. Spec parsing reports the unsafe import and inclusion reason as a diagnostic.\n- Keep browser rendering in an unpinned file. Move shared server-safe logic into a separate module when both frontend and webhook code need it.",doNotEdit:"## Do Not Edit\n- `.phoenix/` directory — completely regenerated on each build. The one exception is `.phoenix/variants/`, where the `phoenix-variants` skill has you write option files; a rebuild keeps it. The project's TypeScript settings come from `.phoenix/tsconfig.json`; the root `tsconfig.json` is the project's and extends it. `phoenix dev`, `phoenix build` and the webhook bundle do not resolve `paths` aliases, so an alias added to the root file passes `phoenix check` and fails the build; use relative imports\n- `src/locales/` .ts files — compiled from .po files\n- `phoenix` and `phoenix-*` folders under `.claude/skills/` and `.agents/skills/` — Phoenix rewrites its skills on every sync, removes anything else inside their folders and removes any other `phoenix-*` skill folder. To customise a skill, copy it to a folder whose name does not start with `phoenix`",modelVersioning:"## Version the Domain Model Once It Is Deployed\nA deployed model version cannot change, because saved configurations depend on it. `pnpm phoenix deploy` is refused, before anything is uploaded, when the model differs from the one already deployed under the same version. The version is not written anywhere: `model.latest` is always the highest numbered version plus one, so a spec with only `latest` is v1, and `applicationVersion` plays no part. `pnpm phoenix dev` accepts every model change, so a working preview does not show that the deploy will pass.\n\nTo change a model that has been deployed as v<N>:\n1. Keep the deployed model, unchanged, as `model.v<N>` in the spec. A file of its own, such as `spec/model-v<N>.yaml`, keeps `latest` readable.\n2. Make the change in `model.latest`, which is now v<N+1>.\n3. Add `src/migrations/v<N+1>.ts` exporting `migrateToV<N+1>`. It receives the saved entities of v<N> and returns entities that are valid in v<N+1>; `ModelV<N>` and `ModelV<N+1>` are generated global namespaces.\n\nThe admin model is versioned on its own, under `src/admin/migrations/`, with its types in `AdminV<N>`.\n\n**Replacing the starter.** When the first deployment of the workspace was the starter, its `Box` model is v1 in production. When the real model replaces it, keep the starter's model as `model.v1` and define the real model in `latest`. Configurations saved from the starter are not worth converting, so the migration returns the starting state of the new model, the `initialDomainEntities` of its preset, and drops the boxes:\n```typescript\n// src/migrations/v2.ts\nimport type { Migration } from '@salsita-ai/sdk/domain'\n\nexport const migrateToV2: Migration<ModelV1.AnyDomainEntity, ModelV2.AnyDomainEntity> = () => [\n // the entities of the new model's preset\n]\n```\nAn entity of a type the new version no longer has must not stay in the returned state. An entity id starts with its type (`Box-1`), so an entity that keeps its data under a new type name needs a new id and every relation pointing at it rewritten. Read `docs/phoenix/migrations.mdx` before writing any other migration.",sdkDocumentation:'## SDK Documentation\n\nAlways search SDK docs for relevant examples before implementing Phoenix features.\n\n### Commands\n- `pnpm phoenix docs` — Full documentation index with descriptions\n- `pnpm phoenix docs -s <term>` — Search all doc content and metadata (case-insensitive)\n- `pnpm phoenix docs --dir` — Print the absolute docs directory for grep or ripgrep\n- `pnpm phoenix docs -r <path>` — Read specific doc (raw text to stdout)\n- `pnpm phoenix docs -c <category>` — List docs in a category\n- `pnpm phoenix docs -l` — List all doc paths with titles\n\n### Key Docs\n- `docs/phoenix/getting-started.mdx` — Setup, prerequisites, first project\n- `docs/phoenix/quickstart.mdx` — End-to-end tutorial building a configurator\n- `docs/phoenix/domain-model.mdx` — Entities, attributes, relations, specs\n- `docs/phoenix/components.mdx` — ECS pattern, YAML components, custom components\n- `docs/phoenix/project-structure.mdx` — Files, folders, conventions\n- `docs/phoenix/best-practices.mdx` — YAML-first, code patterns, common pitfalls\n- `docs/phoenix/state-management.mdx` — Reactive state, RxJS pipelines, data flow\n- `docs/phoenix/expression-context.mdx` — Expression API, context object, helpers\n- `docs/phoenix/webhooks.mdx` — Server-side finalization workflows\n- `docs/phoenix/finalization.mdx` — Finalization system, PDF/email/CSV generation\n- `docs/phoenix/migrations.mdx` — Schema versioning, migration files\n\n### Agent Workflow\n1. Find relevant docs by content: `pnpm phoenix docs -s \'camera\'`\n2. Read matching doc: `pnpm phoenix docs -r docs/3d-scene/cameras/transitions.mdx`\n3. Search within a doc: `pnpm phoenix docs -r <path> | grep "example"`\n4. Search titles only as a fallback: `pnpm phoenix docs -l | grep "camera"`\n5. Search generated files directly when needed: `grep -rn "camera" "$(pnpm phoenix docs --dir)"`\n6. Browse a topic area: `pnpm phoenix docs -c "Layouts"`',assetInspection:"## 3D Asset Inspection\n\nRun `pnpm phoenix inspect:assets` to discover all glTF/GLB files in `public/` and get a summary table. Use `--file <path>` for detailed single-file inspection.\n\n**When to use:** before writing YAML/code that references objectName, materialsToOverride, morphTargetInfluences, or object positioning.\n\n**What it shows per node:**\n- Name, type (Mesh/Light/Camera/Empty), children\n- Transform: `pos=`, `rot=`, `scale=` (only non-default values shown)\n- Per-node morph targets: `morphs=Width,Height`\n- Bounding box: `bounds=WxHxD` (width x height x depth in meters)\n- Origin placement: `origin=center`, `bottom-center`, `top-center`, `bottom-corner`, etc. with numerical offset\n\n**Use cases:**\n- Find exact node names for `objectName` in scene entity components\n- Find material names for `materialsToOverride`\n- Find morph target names for `morphTargetInfluences`\n- Check model dimensions (bounds) for positioning, dimension indicators, or invariants\n- Check origin placement to understand how models anchor (center vs bottom-center)\n- Use `--json` for programmatic access to all data\n\n**Flags:** `--objects`, `--materials`, `--morphs`, `--animations`, `--json`",screenshotCli:"## Screenshot CLI\n\nCapture 3D canvas screenshots from a running dev server via `pnpm phoenix screenshot`.\n\n**Prerequisites:**\n1. `phoenix dev` must be running (starts the WebSocket server on port 3020)\n2. Configurator must be open in a browser (the browser does the actual rendering)\n\n**Options:**\n- `--output, -o` — Output file path (default: `./screenshot.png`)\n- `--width, -w` — Width in pixels (default: 1920)\n- `--height` — Height in pixels (default: 1080)\n- `--format, -f` — Image format: PNG or JPEG (default: PNG)\n- `--port` — WebSocket server port (default: 3020)\n\n**Example workflow:**\n1. Start dev server: `pnpm phoenix dev`\n2. Open configurator in browser (or via Playwright MCP)\n3. Take screenshot: `pnpm phoenix screenshot -o ./render.png -w 2560 --height 1440`\n\n**Use cases:** visual regression testing, generating product images, debugging 3D renders",inspectState:"## Inspect: State\n\nGet the current editor runtime state as JSON via `pnpm phoenix inspect:state`.\n\n**Prerequisites:** same as screenshot (dev server running + browser open)\n\n**Options:**\n- `--output, -o` — Output file path (prints to stdout if omitted)\n- `--sections, -s` — Comma-separated: entities, navigation, validation (default: all)\n- `--pretty` — Pretty-print JSON (default: true)\n- `--port` — WebSocket server port (default: 3020)\n\n**Example:** `pnpm phoenix inspect:state -o ./state.json -s entities,navigation`\n\n**Lifecycle status:** Always included in response. Shows which systems are ready:\n- `domainLoaded` — domain data loaded\n- `domainContextReady` — domain context initialized (entities available)\n- `configuratorReady` — 3D configurator initialized\n- `sessionAuthenticated` — session service active\n- `editorMode` — editor mode enabled\n\n**Tips:** Use `--sections` to limit payload size. Pipe through `head`/`tail` for partial output (e.g., `pnpm phoenix inspect:state | head -50`).\n\n**Use cases:** AI agent context, debugging entity state, inspecting navigation/validation",inspectData:`## Inspect: Data
8
8
 
9
9
  Get the current editor context data as JSON via \`pnpm phoenix inspect:data\`.
10
10
 
@@ -1,10 +1,11 @@
1
1
  import { Branded } from '@salsita-ai/common/utils';
2
+ import { type ImplementationErrorCollector, type ProjectModule } from '#package/domain';
2
3
  import type { RuntimeReferenceStore } from '../../../caching/index.js';
3
4
  import type { RuntimeSpec } from '../../runtime-spec/index.js';
4
5
  import { FactoryDataRuntime } from '../factory-data/index.js';
5
6
  import { type MigrationModules, MigrationRuntime } from '../migrations/index.js';
6
7
  import { ComponentOptics } from '../sections/component-optic.js';
7
- import { type ExpressionImplementations, ExpressionRuntime, type InvariantImplementations, type MissingExpression } from '../sections/expression-runtime.js';
8
+ import { ExpressionRuntime, type InvariantImplementations, type MissingExpression } from '../sections/expression-runtime.js';
8
9
  import { PresetRuntime } from '../sections/preset-runtime.js';
9
10
  import type { SpecCodeMap } from '../spec-code/index.js';
10
11
  import { AdminModelRuntime } from './admin-model-runtime.js';
@@ -25,4 +26,4 @@ export type AdminRuntimeResult = {
25
26
  readonly ok: false;
26
27
  readonly missingExpressions: readonly MissingExpression[];
27
28
  };
28
- export declare const AdminRuntime: (spec: RuntimeSpec, referenceStore: RuntimeReferenceStore, implementations: ExpressionImplementations, invariantImplementations: InvariantImplementations, migrationModules: MigrationModules, code: SpecCodeMap | undefined) => AdminRuntimeResult;
29
+ export declare const AdminRuntime: (spec: RuntimeSpec, referenceStore: RuntimeReferenceStore, expressionModules: readonly ProjectModule[], invariantImplementations: InvariantImplementations, migrationModules: MigrationModules, code: SpecCodeMap | undefined, errors: ImplementationErrorCollector) => AdminRuntimeResult;
@@ -1 +1 @@
1
- import{Branded as e}from"@salsita-ai/common/utils";import{Preset as t,PresetCategory as i,commonAdminCustomExpressions as n,commonAdminViolationExpressions as r}from"#package/domain";import{FactoryDataRuntime as s}from"../factory-data/index.js";import{MigrationRuntime as o}from"../migrations/index.js";import{ComponentOptics as m}from"../sections/component-optic.js";import{ExpressionRuntime as a}from"../sections/expression-runtime.js";import{PresetRuntime as p}from"../sections/preset-runtime.js";import{AdminModelRuntime as f}from"./admin-model-runtime.js";import{AdminStatesRuntime as u}from"./states-runtime.js";let c={Preset:[{evaluate:(e,i,n)=>{let s=n.domain.get$Entity(e,i);return t.assert(s),r.Preset.nameNotEmpty(s)}}]},l={PresetCategory:{$isDraft:{origin:{name:"$isDraft",typeOrAlias:"PresetCategory"},evaluate:(e,t,r)=>{let s=r.domain.get$Entity(e,t);return i.assert(s),n.PresetCategory.$isDraft(s)},meta:{}}}},d=new WeakMap;export const AdminRuntime=(t,i,n,r,g,R)=>{let y={...l};for(let[e,t]of Object.entries(n))y[e]={...y[e],...t};let j=f(t.admin,t.configurator.dataSchemas,i,R),k=a(t.admin,i,y,"adminExpressionRuntime",((e,t)=>{if(0===Object.keys(e).length)return c;let i=d.get(t)??new WeakMap;d.set(t,i);let n=i.get(e);if(n)return n;let r={};for(let i of[c,e])for(let[e,n]of Object.entries(i))for(let i of t[e]??[e])r[i]=[...r[i]??[],...n];return i.set(e,r),r})(r,j.aliases),R);return k.ok?{ok:!0,runtime:e({modelRuntime:j,componentOptics:m(i,{components:t.admin.components,referenceScope:"adminRuntime/componentOptics",modelRuntime:j,code:R}),expressionRuntime:k.runtime,presetRuntime:p(i,{referenceScope:"adminRuntime/presetRuntime",presets:t.admin.presets.presets}),statesRuntime:u(t,i),migrationRuntime:o(i,{referenceScope:"adminRuntime/migrationRuntime",resolvedVersions:t.admin.resolvedVersions,latestEntities:j.entities,migrationModules:g}),factoryDataRuntime:s(i,{referenceScope:"adminRuntime/factoryDataRuntime",invariants:t.admin.invariants,defaults:t.admin.defaults,aliases:j.aliases,entities:j.entities})})}:{ok:!1,missingExpressions:k.missing}};
1
+ import{Branded as e}from"@salsita-ai/common/utils";import{Preset as t,PresetCategory as i,commonAdminCustomExpressions as n,commonAdminViolationExpressions as r}from"#package/domain";import{FactoryDataRuntime as s}from"../factory-data/index.js";import{MigrationRuntime as o}from"../migrations/index.js";import{ComponentOptics as m}from"../sections/component-optic.js";import{ExpressionRuntime as a}from"../sections/expression-runtime.js";import{PresetRuntime as p}from"../sections/preset-runtime.js";import{AdminModelRuntime as u}from"./admin-model-runtime.js";import{AdminStatesRuntime as f}from"./states-runtime.js";let c={Preset:[{evaluate:(e,i,n)=>{let s=n.domain.get$Entity(e,i);return t.assert(s),r.Preset.nameNotEmpty(s)}}]},d={PresetCategory:{$isDraft:{origin:{name:"$isDraft",typeOrAlias:"PresetCategory"},evaluate:(e,t,r)=>{let s=r.domain.get$Entity(e,t);return i.assert(s),n.PresetCategory.$isDraft(s)},meta:{}}}},l=new WeakMap;export const AdminRuntime=(t,i,n,r,g,R,y)=>{let j=u(t.admin,t.configurator.dataSchemas,i,R),k=a(t.admin,i,n,"adminExpressionRuntime",((e,t)=>{if(0===Object.keys(e).length)return c;let i=l.get(t)??new WeakMap;l.set(t,i);let n=i.get(e);if(n)return n;let r={};for(let i of[c,e])for(let[e,n]of Object.entries(i))for(let i of t[e]??[e])r[i]=[...r[i]??[],...n];return i.set(e,r),r})(r,j.aliases),R,y,d);return k.ok?{ok:!0,runtime:e({modelRuntime:j,componentOptics:m(i,{components:t.admin.components,referenceScope:"adminRuntime/componentOptics",modelRuntime:j,code:R}),expressionRuntime:k.runtime,presetRuntime:p(i,{referenceScope:"adminRuntime/presetRuntime",presets:t.admin.presets.presets}),statesRuntime:f(t,i),migrationRuntime:o(i,{referenceScope:"adminRuntime/migrationRuntime",resolvedVersions:t.admin.resolvedVersions,latestEntities:j.entities,migrationModules:g}),factoryDataRuntime:s(i,{referenceScope:"adminRuntime/factoryDataRuntime",invariants:t.admin.invariants,defaults:t.admin.defaults,aliases:j.aliases,entities:j.entities})})}:{ok:!1,missingExpressions:k.missing}};
@@ -1,12 +1,12 @@
1
1
  import { Branded } from '@salsita-ai/common/utils';
2
- import type { ImportedTransformer } from '#package/domain';
2
+ import { type ImplementationErrorCollector, type ImportedTransformer, type ProjectModule } from '#package/domain';
3
3
  import type { RuntimeReferenceStore } from '../../caching/index.js';
4
4
  import type { RuntimeSpec } from '../runtime-spec/index.js';
5
5
  import { AdminRuntime } from './admin-runtime/admin-runtime.js';
6
6
  import { ConfiguratorRuntime } from './configurator-runtime/configurator-runtime.js';
7
7
  import type { MigrationModules } from './migrations/index.js';
8
8
  import { DataRuntime } from './sections/data-runtime.js';
9
- import type { ExpressionImplementations, InvariantImplementations, MissingExpression } from './sections/expression-runtime.js';
9
+ import type { InvariantImplementations, MissingExpression } from './sections/expression-runtime.js';
10
10
  import type { SpecCodeModule } from './spec-code/index.js';
11
11
  /**
12
12
  * Runtime objects built from a RuntimeSpec.
@@ -42,6 +42,14 @@ export type ApplicationRuntimeResult = {
42
42
  * same generation as `spec`; an id carries a hash of its code, so a module from another generation
43
43
  * misses the ids of changed code instead of running an old closure.
44
44
  *
45
- * Returns an error result when declared expressions are missing implementations.
45
+ * `expressionModules` and `adminExpressionModules` are the project's expression files
46
+ * (`src/expressions/`, `src/admin/expressions/`), as the generated barrels or the editor's reload
47
+ * hand them over; each domain merges them with the aliases of its spec (`mergeExpressionModules`).
48
+ *
49
+ * The mistakes of the project files are recorded in `errors` as `ImplementationError`s: a file with
50
+ * the wrong export, an expression two files define, or that a file defines although the spec or the
51
+ * SDK gives it a value (`DUPLICATE_DEFINITION`), and a declared expression no file implements
52
+ * (`MISSING_IMPLEMENTATION`), which also makes the result an error. Without `errors`, everything
53
+ * recorded is thrown together as one `ImplementationErrors` before the result is returned.
46
54
  */
47
- export declare const ApplicationRuntime: (spec: RuntimeSpec, referenceStore: RuntimeReferenceStore, implementations: ExpressionImplementations, importedTransformers: readonly ImportedTransformer[], invariantImplementations?: InvariantImplementations, adminImplementations?: ExpressionImplementations, adminInvariantImplementations?: InvariantImplementations, migrationModules?: MigrationModules, specCode?: SpecCodeModule) => ApplicationRuntimeResult;
55
+ export declare const ApplicationRuntime: (spec: RuntimeSpec, referenceStore: RuntimeReferenceStore, expressionModules: readonly ProjectModule[], importedTransformers: readonly ImportedTransformer[], invariantImplementations?: InvariantImplementations, adminExpressionModules?: readonly ProjectModule[], adminInvariantImplementations?: InvariantImplementations, migrationModules?: MigrationModules, specCode?: SpecCodeModule, errors?: ImplementationErrorCollector) => ApplicationRuntimeResult;
@@ -1 +1 @@
1
- import{Branded as i}from"@salsita-ai/common/utils";import{AdminRuntime as r}from"./admin-runtime/admin-runtime.js";import{ConfiguratorRuntime as n}from"./configurator-runtime/configurator-runtime.js";import{DataRuntime as o}from"./sections/data-runtime.js";export const ApplicationRuntime=(s,t,m,e,a,u={},p={},f={},c)=>{let g=n(s,t,m,e,a,f,c?.configurator);if(!g.ok)return{ok:!1,missingExpressions:g.missingExpressions};let d=r(s,t,u,p,f,c?.admin);return d.ok?{ok:!0,runtime:i({configurator:g.runtime,admin:d.runtime,data:o(s,t)})}:{ok:!1,missingExpressions:d.missingExpressions}};
1
+ import{Branded as i}from"@salsita-ai/common/utils";import{createImplementationErrorCollector as o}from"#package/domain";import{AdminRuntime as r}from"./admin-runtime/admin-runtime.js";import{ConfiguratorRuntime as n}from"./configurator-runtime/configurator-runtime.js";import{DataRuntime as m}from"./sections/data-runtime.js";export const ApplicationRuntime=(t,s,a,e,u,p=[],f={},c={},d,g)=>{let k=g??o(),l=n(t,s,a,e,u,c,d?.configurator,k),x=r(t,s,p,f,c,d?.admin,k);return(void 0===g&&k.throwIfAny(),l.ok&&x.ok)?{ok:!0,runtime:i({configurator:l.runtime,admin:x.runtime,data:m(t,s)})}:{ok:!1,missingExpressions:[...l.ok?[]:l.missingExpressions,...x.ok?[]:x.missingExpressions]}};
@@ -1,5 +1,5 @@
1
1
  import { Branded } from '@salsita-ai/common/utils';
2
- import type { ImportedTransformer } from '#package/domain';
2
+ import type { ImplementationErrorCollector, ImportedTransformer, ProjectModule } from '#package/domain';
3
3
  import type { RuntimeReferenceStore } from '../../../caching/index.js';
4
4
  import type { RuntimeSpec } from '../../runtime-spec/index.js';
5
5
  import { FactoryDataRuntime } from '../factory-data/index.js';
@@ -7,7 +7,7 @@ import { type MigrationModules, MigrationRuntime } from '../migrations/index.js'
7
7
  import { ActionsRuntime } from '../sections/actions-runtime.js';
8
8
  import { AssistantRuntime } from '../sections/assistant-runtime.js';
9
9
  import { ComponentOptics } from '../sections/component-optic.js';
10
- import { type ExpressionImplementations, ExpressionRuntime, type InvariantImplementations, type MissingExpression } from '../sections/expression-runtime.js';
10
+ import { ExpressionRuntime, type InvariantImplementations, type MissingExpression } from '../sections/expression-runtime.js';
11
11
  import { PresetRuntime } from '../sections/preset-runtime.js';
12
12
  import { ThemeRuntime } from '../sections/theme-runtime.js';
13
13
  import { TransformersRuntime } from '../sections/transformers-runtime.js';
@@ -34,4 +34,4 @@ export type ConfiguratorRuntimeResult = {
34
34
  readonly ok: false;
35
35
  readonly missingExpressions: readonly MissingExpression[];
36
36
  };
37
- export declare const ConfiguratorRuntime: (spec: RuntimeSpec, referenceStore: RuntimeReferenceStore, implementations: ExpressionImplementations, importedTransformers: readonly ImportedTransformer[], invariantImplementations: InvariantImplementations | undefined, migrationModules: MigrationModules, code: SpecCodeMap | undefined) => ConfiguratorRuntimeResult;
37
+ export declare const ConfiguratorRuntime: (spec: RuntimeSpec, referenceStore: RuntimeReferenceStore, expressionModules: readonly ProjectModule[], importedTransformers: readonly ImportedTransformer[], invariantImplementations: InvariantImplementations | undefined, migrationModules: MigrationModules, code: SpecCodeMap | undefined, errors: ImplementationErrorCollector) => ConfiguratorRuntimeResult;
@@ -1 +1 @@
1
- import{Branded as t}from"@salsita-ai/common/utils";import{FactoryDataRuntime as e}from"../factory-data/index.js";import{MigrationRuntime as i}from"../migrations/index.js";import{ActionsRuntime as o}from"../sections/actions-runtime.js";import{AssistantRuntime as r}from"../sections/assistant-runtime.js";import{ComponentOptics as n}from"../sections/component-optic.js";import{ExpressionRuntime as s}from"../sections/expression-runtime.js";import{PresetRuntime as m}from"../sections/preset-runtime.js";import{ThemeRuntime as a}from"../sections/theme-runtime.js";import{TransformersRuntime as u}from"../sections/transformers-runtime.js";import{ConfiguratorModelRuntime as c}from"./configurator-model-runtime.js";import{ConfiguratorStatesRuntime as f}from"./states-runtime.js";export const ConfiguratorRuntime=(p,g,R,j,l,d,x)=>{let v=c(p.configurator,g,x),S=s(p.configurator,g,R,"configuratorExpressionRuntime",l,x);return S.ok?{ok:!0,runtime:t({modelRuntime:v,componentOptics:n(g,{components:p.configurator.components,referenceScope:"configuratorRuntime/componentOptics",modelRuntime:v,code:x}),presetRuntime:m(g,{referenceScope:"configuratorRuntime/presetRuntime",presets:p.configurator.presets.presets}),statesRuntime:f(p,g),expressionRuntime:S.runtime,themeRuntime:a(p,g),transformersRuntime:u(p,g,j),assistantRuntime:r(p,g),actionsRuntime:o(p.configurator.actions,g),migrationRuntime:i(g,{referenceScope:"configuratorRuntime/migrationRuntime",resolvedVersions:p.configurator.resolvedVersions,latestEntities:v.entities,migrationModules:d}),factoryDataRuntime:e(g,{referenceScope:"configuratorRuntime/factoryDataRuntime",invariants:p.configurator.invariants,defaults:p.configurator.defaults,aliases:v.aliases,entities:v.entities})})}:{ok:!1,missingExpressions:S.missing}};
1
+ import{Branded as t}from"@salsita-ai/common/utils";import{FactoryDataRuntime as e}from"../factory-data/index.js";import{MigrationRuntime as i}from"../migrations/index.js";import{ActionsRuntime as o}from"../sections/actions-runtime.js";import{AssistantRuntime as r}from"../sections/assistant-runtime.js";import{ComponentOptics as n}from"../sections/component-optic.js";import{ExpressionRuntime as s}from"../sections/expression-runtime.js";import{PresetRuntime as m}from"../sections/preset-runtime.js";import{ThemeRuntime as a}from"../sections/theme-runtime.js";import{TransformersRuntime as u}from"../sections/transformers-runtime.js";import{ConfiguratorModelRuntime as c}from"./configurator-model-runtime.js";import{ConfiguratorStatesRuntime as f}from"./states-runtime.js";export const ConfiguratorRuntime=(p,g,R,j,l,d,x,v)=>{let S=c(p.configurator,g,x),k=s(p.configurator,g,R,"configuratorExpressionRuntime",l,x,v);return k.ok?{ok:!0,runtime:t({modelRuntime:S,componentOptics:n(g,{components:p.configurator.components,referenceScope:"configuratorRuntime/componentOptics",modelRuntime:S,code:x}),presetRuntime:m(g,{referenceScope:"configuratorRuntime/presetRuntime",presets:p.configurator.presets.presets}),statesRuntime:f(p,g),expressionRuntime:k.runtime,themeRuntime:a(p,g),transformersRuntime:u(p,g,j),assistantRuntime:r(p,g),actionsRuntime:o(p.configurator.actions,g),migrationRuntime:i(g,{referenceScope:"configuratorRuntime/migrationRuntime",resolvedVersions:p.configurator.resolvedVersions,latestEntities:S.entities,migrationModules:d}),factoryDataRuntime:e(g,{referenceScope:"configuratorRuntime/factoryDataRuntime",invariants:p.configurator.invariants,defaults:p.configurator.defaults,aliases:S.aliases,entities:S.entities})})}:{ok:!1,missingExpressions:k.missing}};
@@ -1,5 +1,5 @@
1
1
  import { Branded } from '@salsita-ai/common/utils';
2
- import type { DomainEntity, Expression as DomainExpression, Invariant as DomainInvariant } from '#package/domain';
2
+ import type { DomainEntity, Expression as DomainExpression, Invariant as DomainInvariant, ImplementationErrorCollector, ProjectModule } from '#package/domain';
3
3
  import type { RuntimeReferenceStore } from '../../../caching/index.js';
4
4
  import type { Expressions } from '../../../parse-application-files/index.js';
5
5
  import { type RuntimeModelVersion } from '../../runtime-spec/index.js';
@@ -39,11 +39,17 @@ interface ExpressionRuntimeSpec {
39
39
  *
40
40
  * Dispatches on `expression.implementation.kind`:
41
41
  * - `relation` / `violations` — calls the matching SDK factory with params + runtime context
42
- * - `user` — uses the dynamically imported impl from `src/expressions/*.ts`
42
+ * - `user` — uses the implementation of the project files `expressionModules` (`src/expressions/`,
43
+ * or `src/admin/expressions/`), merged with the spec's aliases by `mergeExpressionModules`, or the
44
+ * one `builtIn` holds
43
45
  * - `value` — evaluates the parsed {@link MemoEvalPlan}; stabilized per
44
46
  * {@link canonicalEnhancedJsonValueKey} so identical values reuse the same
45
47
  * evaluator across rebuilds. Its `$eval` code comes from `code`, the domain's map of the generated
46
48
  * code module, when that holds it.
49
+ *
50
+ * A file that is wrong, or that defines an expression another file, the spec or `builtIn` already
51
+ * defines, is recorded in `errors` as an `ImplementationError` and left out. So is every declared
52
+ * expression without an implementation, which also makes the result an error.
47
53
  */
48
- export declare const ExpressionRuntime: (spec: ExpressionRuntimeSpec, referenceStore: RuntimeReferenceStore, implementations: ExpressionImplementations, referenceScopePrefix: string, invariantImplementations: InvariantImplementations | undefined, code: SpecCodeMap | undefined) => ExpressionRuntimeResult;
54
+ export declare const ExpressionRuntime: (spec: ExpressionRuntimeSpec, referenceStore: RuntimeReferenceStore, expressionModules: readonly ProjectModule[], referenceScopePrefix: string, invariantImplementations: InvariantImplementations | undefined, code: SpecCodeMap | undefined, errors: ImplementationErrorCollector, builtIn?: ExpressionImplementations) => ExpressionRuntimeResult;
49
55
  export {};
@@ -1 +1 @@
1
- import e from"lodash-es/isEqual.js";import{Branded as t}from"@salsita-ai/common/utils";import{createRelationFactory as i}from"#package/domain";import{canonicalEnhancedJsonValueKey as n,isSdkExpressionImplementation as a}from"../../../core/index.js";import{specCodeExpressionKey as r}from"../../runtime-spec/index.js";import{generateMemoEvalPlanRuntime as l}from"./generate-memo-eval-plan-runtime.js";let o={},s=["0-1","1-1","0-N","1-N"],p={relation:(e,t)=>{let n=t[0];if(!("object"==typeof n&&null!==n&&"kind"in n&&"target"in n&&"string"==typeof n.target&&"cardinality"in n&&s.includes(n.cardinality)&&("allOfType"===n.kind||("direct"===n.kind||"indirect"===n.kind)&&"field"in n&&"string"==typeof n.field)))throw Error("Cannot create the relation expression because its parameter is not a relation declaration. The application spec is inconsistent; rebuild it from a valid relations declaration.");return i(e.aliasMap)(n)},violations:(e,t)=>{let i=t[0];if("object"!=typeof i||null===i||!("entityType"in i)||"string"!=typeof i.entityType)throw Error("Cannot create the violations expression because its entityType parameter is missing. The application spec is inconsistent; rebuild it from a valid invariants declaration.");let n=i.entityType,a=Object.entries(e.invariantImplementations).flatMap(([t,i])=>{let a=e.aliasMap[t];return t===n||a?.includes(n)?i:[]});return{origin:{name:"$violations",typeOrAlias:n},evaluate:(e,t,i)=>a.flatMap(n=>n.evaluate(e,t,i)),meta:{}}}},m=e=>{let t=l(e.plan,r(e.entityOrAlias,e.name),e.code);return{origin:{name:e.name,typeOrAlias:e.entityOrAlias},evaluate:(e,i,n)=>t(n.domain.get$Entity(e,i),i,n),meta:{}}},d=e=>({...(0,p[e.kind])({aliasMap:e.aliasMap,invariantImplementations:e.invariantImplementations},e.params),origin:{name:e.name,typeOrAlias:e.entityOrAlias}});export const ExpressionRuntime=(i,r,l,s,p=o,u)=>{let c={};for(let[e,t]of i.latestModel.aliases)c[String(e)]=t.map(String);let f=r.createBuilder(`${s}/sdk`,{build:d,getKey:e=>`sdk::${e.entityOrAlias}::${e.name}`,compare:(t,i)=>t.kind===i.kind&&e(t.params,i.params)&&e(t.aliasMap,i.aliasMap)&&("violations"!==i.kind||t.invariantImplementations===i.invariantImplementations)}),y=r.createBuilder(`${s}/user`,{build:e=>e.impl,getKey:e=>`user::${e.entityType}::${e.name}`,compare:(e,t)=>e.impl===t.impl}),g=r.createBuilder(`${s}/value`,{build:m,getKey:e=>`value::${e.entityOrAlias}::${e.name}`,compare:(e,t)=>e.valueKey===t.valueKey&&e.code===t.code}),v={},S=!1,k=new Set,b=new Set,O=new Set;for(let[e,t]of i.expressions.expressions)for(let[,i]of t){let t=c[String(e)]??[String(e)];if(a(i.implementation)){let n={entityOrAlias:String(e),name:String(i.name),kind:i.implementation.kind,params:i.implementation.params,aliasMap:c,invariantImplementations:p},a=f.build(n);for(let e of(a.changed&&(S=!0),k.add(f.getKey(n)),t))v[e]||(v[e]={}),v[e][String(i.name)]=a.output;continue}if("value"===i.implementation.kind){let a=i.implementation.value,r={entityOrAlias:String(e),name:String(i.name),valueKey:n(a),plan:i.implementation.plan,code:u},l=g.build(r);for(let e of(l.changed&&(S=!0),O.add(g.getKey(r)),t))v[e]||(v[e]={}),v[e][String(i.name)]=l.output;continue}}for(let[e,t]of Object.entries(l))for(let[i,n]of Object.entries(t)){let t={entityType:e,name:i,impl:n},a=y.build(t);a.changed&&(S=!0),b.add(y.getKey(t)),v[e]||(v[e]={}),v[e][i]=a.output}f.cleanup(k)&&(S=!0),y.cleanup(b)&&(S=!0),g.cleanup(O)&&(S=!0);let h=[];for(let[e,t]of i.expressions.entityExpressions)for(let n of t.shapeGroups){let t=i.expressions.expressions.get(n);if(t)for(let[i]of t)v[String(e)]?.[String(i)]||h.push({entityType:String(e),exprName:String(i)})}return h.length>0?{ok:!1,missing:h}:{ok:!0,runtime:t({merged:v,changed:S})}};
1
+ import e from"lodash-es/isEqual.js";import{Branded as t}from"@salsita-ai/common/utils";import{ImplementationError as i,createRelationFactory as n,mergeExpressionModules as a}from"#package/domain";import{canonicalEnhancedJsonValueKey as r,isSdkExpressionImplementation as o}from"../../../core/index.js";import{specCodeExpressionKey as l}from"../../runtime-spec/index.js";import{generateMemoEvalPlanRuntime as s}from"./generate-memo-eval-plan-runtime.js";let p={},m=["0-1","1-1","0-N","1-N"],d={relation:(e,t)=>{let i=t[0];if(!("object"==typeof i&&null!==i&&"kind"in i&&"target"in i&&"string"==typeof i.target&&"cardinality"in i&&m.includes(i.cardinality)&&("allOfType"===i.kind||("direct"===i.kind||"indirect"===i.kind)&&"field"in i&&"string"==typeof i.field)))throw Error("Cannot create the relation expression because its parameter is not a relation declaration. The application spec is inconsistent; rebuild it from a valid relations declaration.");return n(e.aliasMap)(i)},violations:(e,t)=>{let i=t[0];if("object"!=typeof i||null===i||!("entityType"in i)||"string"!=typeof i.entityType)throw Error("Cannot create the violations expression because its entityType parameter is missing. The application spec is inconsistent; rebuild it from a valid invariants declaration.");let n=i.entityType,a=Object.entries(e.invariantImplementations).flatMap(([t,i])=>{let a=e.aliasMap[t];return t===n||a?.includes(n)?i:[]});return{origin:{name:"$violations",typeOrAlias:n},evaluate:(e,t,i)=>a.flatMap(n=>n.evaluate(e,t,i)),meta:{}}}},f=e=>{let t=s(e.plan,l(e.entityOrAlias,e.name),e.code);return{origin:{name:e.name,typeOrAlias:e.entityOrAlias},evaluate:(e,i,n)=>t(n.domain.get$Entity(e,i),i,n),meta:{}}},u=e=>({...(0,d[e.kind])({aliasMap:e.aliasMap,invariantImplementations:e.invariantImplementations},e.params),origin:{name:e.name,typeOrAlias:e.entityOrAlias}});export const ExpressionRuntime=(n,l,s,m,d=p,c,g,y={})=>{let v={};for(let[e,t]of n.latestModel.aliases)v[String(e)]=t.map(String);let x={...y};for(let[e,t]of Object.entries(a(s,v,g,((e,t,i)=>{let n=new Map,a=(e,t)=>{n.set(e,(n.get(e)??new Set).add(t))};for(let[i,n]of e.expressions.expressions)for(let[e,r]of n)if("user"!==r.implementation.kind)for(let n of t[String(i)]??[String(i)])a(n,String(e));for(let[e,t]of Object.entries(i))for(let i of Object.keys(t))a(e,i);return n})(n,v,y))))x[e]={...x[e],...t};let S=l.createBuilder(`${m}/sdk`,{build:u,getKey:e=>`sdk::${e.entityOrAlias}::${e.name}`,compare:(t,i)=>t.kind===i.kind&&e(t.params,i.params)&&e(t.aliasMap,i.aliasMap)&&("violations"!==i.kind||t.invariantImplementations===i.invariantImplementations)}),h=l.createBuilder(`${m}/user`,{build:e=>e.impl,getKey:e=>`user::${e.entityType}::${e.name}`,compare:(e,t)=>{let i,n;return(i=e.impl)===(n=t.impl)||i.evaluate===n.evaluate&&i.meta===n.meta&&i.origin.name===n.origin.name&&i.origin.typeOrAlias===n.origin.typeOrAlias}}),O=l.createBuilder(`${m}/value`,{build:f,getKey:e=>`value::${e.entityOrAlias}::${e.name}`,compare:(e,t)=>e.valueKey===t.valueKey&&e.code===t.code}),b={},k=!1,$=new Set,M=new Set,j=new Set;for(let[e,t]of n.expressions.expressions)for(let[,i]of t){let t=v[String(e)]??[String(e)];if(o(i.implementation)){let n={entityOrAlias:String(e),name:String(i.name),kind:i.implementation.kind,params:i.implementation.params,aliasMap:v,invariantImplementations:d},a=S.build(n);for(let e of(a.changed&&(k=!0),$.add(S.getKey(n)),t))b[e]||(b[e]={}),b[e][String(i.name)]=a.output;continue}if("value"===i.implementation.kind){let n=i.implementation.value,a={entityOrAlias:String(e),name:String(i.name),valueKey:r(n),plan:i.implementation.plan,code:c},o=O.build(a);for(let e of(o.changed&&(k=!0),j.add(O.getKey(a)),t))b[e]||(b[e]={}),b[e][String(i.name)]=o.output;continue}}for(let[e,t]of Object.entries(x))for(let[i,n]of Object.entries(t)){let t={entityType:e,name:i,impl:n},a=h.build(t);a.changed&&(k=!0),M.add(h.getKey(t)),b[e]||(b[e]={}),b[e][i]=a.output}S.cleanup($)&&(k=!0),h.cleanup(M)&&(k=!0),O.cleanup(j)&&(k=!0);let A=[];for(let[e,t]of n.expressions.entityExpressions)for(let i of t.shapeGroups){let t=n.expressions.expressions.get(i);if(t)for(let[i]of t)b[String(e)]?.[String(i)]||A.push({entityType:String(e),exprName:String(i)})}return A.length>0?(A.forEach(e=>g.add(new i({code:"MISSING_IMPLEMENTATION",origin:null,problem:`Missing implementation of expression ${e.entityType}.${e.exprName}`,fix:"The spec declares it without a value of its own ($eval, $msg or a read), so a project file implements it: a default export of phx.domain.implementCustomExpressions({ ... }) in src/expressions/, or of phx.domain.admin.implementCustomExpressions({ ... }) in src/admin/expressions/ for an admin entity type. Implement it, or remove the declaration from the spec."}))),{ok:!1,missing:A}):{ok:!0,runtime:t({merged:b,changed:k})}};
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { type AssistantAiSettings } from '@salsita-ai/contracts/assistant';
3
- import { ASSISTANT_HINTS } from '#package/domain';
3
+ import { ASSISTANT_HINTS, type AssistantFormAttribute, type AssistantFormRole } from '#package/domain';
4
4
  /**
5
5
  * One whitelisted field in the assistant manifest. `description` overrides the JSON Schema
6
6
  * description of the shape property; `as` exposes the field under a different GraphQL name; `lenses`
@@ -12,6 +12,21 @@ export interface RawAssistantField {
12
12
  readonly as?: string;
13
13
  readonly lenses?: readonly string[];
14
14
  }
15
+ /**
16
+ * One of the project's own elements of an assistant form, keyed under `assistant.formElements` by
17
+ * its tag, and implemented by the component a file in `src/elements/` registers under the same name
18
+ * with `phx.frontend.createAssistantFormElement`. `role` decides the props the component gets, how
19
+ * the form collects it into the answer and where the prompt lists it, with `description` after its
20
+ * signature. `attributes` are flat scalars beside the built-in `label`, each a JSON Schema of the
21
+ * keywords `AssistantFormAttribute` allows, and `required` names those the model must write. An
22
+ * option or an input that takes a `value` declares it like any other attribute.
23
+ */
24
+ export interface RawAssistantFormElement {
25
+ readonly description: string;
26
+ readonly role: AssistantFormRole;
27
+ readonly attributes?: Readonly<Record<string, AssistantFormAttribute>>;
28
+ readonly required?: readonly string[];
29
+ }
15
30
  /**
16
31
  * One lens of the assistant `lenses` map, keyed by the name the agent spreads it by: a named
17
32
  * selection made of the `entities` entries tagged with that name. `description` tells the agent when
@@ -90,6 +105,16 @@ export interface RawAssistantAction {
90
105
  readonly description?: string;
91
106
  readonly as?: string;
92
107
  }
108
+ /**
109
+ * One view of the assistant `screenshots` map, keyed by the instance name of an `agentScreenshot`
110
+ * component: it lets the agent take a screenshot of the entity from that view under `meta`.
111
+ * `description` tells the agent what the view shows; `as` is the name the agent asks for the view
112
+ * by, the instance name by default.
113
+ */
114
+ export interface RawAssistantScreenshot {
115
+ readonly description: string;
116
+ readonly as?: string;
117
+ }
93
118
  /**
94
119
  * Experimental assistant manifest authored under the `assistant` root key. `entities` whitelists the
95
120
  * stored fields the generated GraphQL schema exposes, keyed by entity type or alias name and then by
@@ -100,8 +125,11 @@ export interface RawAssistantAction {
100
125
  * entity type or alias, the `childEntityRecipes` groups whose recipes the agent may list and add
101
126
  * with `addChild`. `replaceRecipes` names, per entity type or alias, the `replaceEntityRecipes`
102
127
  * groups whose recipes the agent may list and replace the entity by with `replaceEntity`. `actions`
103
- * names the declared actions the agent may run as custom mutations. Nothing is exposed without an
104
- * entry. `lenses` declares the named selections that `entities` entries are tagged with.
128
+ * names the declared actions the agent may run as custom mutations. `screenshots` names, per entity
129
+ * type or alias, the `agentScreenshot` views the agent may take a screenshot of the entity from.
130
+ * Nothing is exposed without an entry. `lenses` declares the named selections that `entities`
131
+ * entries are tagged with, and `formElements` the project's own elements of the forms the agent
132
+ * asks the user with.
105
133
  *
106
134
  * `ai` and `instructions` are for the server only: the provider, model tier and reasoning level the
107
135
  * deployment's assistant runs with, and the integrator's own guidance for it, appended to the system
@@ -114,7 +142,9 @@ export interface RawAssistant {
114
142
  readonly childRecipes?: Readonly<Record<string, RawAssistantRecipes>>;
115
143
  readonly replaceRecipes?: Readonly<Record<string, RawAssistantRecipes>>;
116
144
  readonly actions?: Readonly<Record<string, RawAssistantAction | null>>;
145
+ readonly screenshots?: Readonly<Record<string, Readonly<Record<string, RawAssistantScreenshot>>>>;
117
146
  readonly lenses?: Readonly<Record<string, RawAssistantLens>>;
147
+ readonly formElements?: Readonly<Record<string, RawAssistantFormElement>>;
118
148
  readonly instructions?: string;
119
149
  readonly ai: AssistantAiSettings;
120
150
  }
@@ -1 +1 @@
1
- import{z as e}from"zod";import{ASSISTANT_AI_PROVIDERS as t,ASSISTANT_MODEL_TIERS as o,ASSISTANT_REASONING_LEVELS as i}from"@salsita-ai/contracts/assistant";import{ASSISTANT_HINTS as n}from"#package/domain";export const ASSISTANT_OPTION_COMPONENT_TYPES=["materialSwatch","attributeSelection","distanceDimensionSelect"];let r=e.string().regex(/^(?!__)[_A-Za-z][_0-9A-Za-z]*$/,'must be a GraphQL name: letters, digits and "_", not starting with a digit or "__"'),s=t=>e.enum(t,{errorMap:()=>({message:`must be one of: ${t.join(", ")}`})}),a=e.object({description:e.string().optional(),as:r.optional(),lenses:e.array(e.string()).optional()}).strict(),l="must be a whole number of at least 1; remove the key to nest tagged relations one level",c=e.object({description:e.string().trim().min(1,"must not be empty; say when the agent should use the lens"),maxDepth:e.number({invalid_type_error:l}).int(l).min(1,l).optional()}).strict(),p=r.refine(e=>!e.includes("__"),'must not contain "__", which names the generated copies of a lens'),m=e.array(s(n)),d=e.object({options:e.object({componentType:s(ASSISTANT_OPTION_COMPONENT_TYPES),instanceName:e.string().min(1,"must not be empty; name the component instance that holds the choices"),as:r.optional(),validate:e.boolean().optional(),allowDryRuns:e.boolean().optional(),hints:m.optional()}).strict().optional()}).strict(),u=e.object({as:e.string().trim().min(1,"must not be empty; remove the key to use the instance name").optional(),description:e.string().optional(),allowDryRuns:e.boolean().optional()}).strict(),h=t=>e.object({hints:m.optional(),groups:e.record(e.string(),u.nullable(),{required_error:`is required: the recipe groups of the type go under "groups", keyed by the instance name of a "${t}" component`})}).strict(),b=e.object({description:e.string().optional(),as:r.optional()}).strict(),g=`is required: declare provider (${t.join(", ")}), model (${o.join(", ")}) and reasoning (${i.join(", ")})`,_="must be a positive whole number of input tokens; remove the key to use the default of the server",y=e.object({provider:s(t),model:s(o),reasoning:s(i),maxThreadSize:e.number({invalid_type_error:_}).int(_).positive(_).optional()},{required_error:g,invalid_type_error:g}).strict();export const rawAssistantSchema=e.object({entities:e.record(e.string(),e.record(e.string(),a.nullable())).optional(),deletable:e.record(e.string(),e.object({}).strict().nullable()).optional(),writable:e.record(e.string(),e.record(e.string(),d.nullable())).optional(),childRecipes:e.record(e.string(),h("childEntityRecipes")).optional(),replaceRecipes:e.record(e.string(),h("replaceEntityRecipes")).optional(),actions:e.record(e.string(),b.nullable()).optional(),lenses:e.record(p,c).optional(),instructions:e.string().trim().min(1,"must not be empty; remove the key when there are no instructions").optional(),ai:y}).strict();
1
+ import{z as e}from"zod";import{ASSISTANT_AI_PROVIDERS as t,ASSISTANT_MODEL_TIERS as n,ASSISTANT_REASONING_LEVELS as o}from"@salsita-ai/contracts/assistant";import{ASSISTANT_FORM_NAME_PATTERN as i,ASSISTANT_HINTS as r}from"#package/domain";export const ASSISTANT_OPTION_COMPONENT_TYPES=["materialSwatch","attributeSelection","distanceDimensionSelect"];let s=e.string().regex(/^(?!__)[_A-Za-z][_0-9A-Za-z]*$/,'must be a GraphQL name: letters, digits and "_", not starting with a digit or "__"'),a=t=>e.enum(t,{errorMap:()=>({message:`must be one of: ${t.join(", ")}`})}),m=e.object({description:e.string().optional(),as:s.optional(),lenses:e.array(e.string()).optional()}).strict(),l="must be a whole number of at least 1; remove the key to nest tagged relations one level",p=e.object({description:e.string().trim().min(1,"must not be empty; say when the agent should use the lens"),maxDepth:e.number({invalid_type_error:l}).int(l).min(1,l).optional()}).strict(),c=s.refine(e=>!e.includes("__"),'must not contain "__", which names the generated copies of a lens'),u=e.array(a(r)),d=e.object({options:e.object({componentType:a(ASSISTANT_OPTION_COMPONENT_TYPES),instanceName:e.string().min(1,"must not be empty; name the component instance that holds the choices"),as:s.optional(),validate:e.boolean().optional(),allowDryRuns:e.boolean().optional(),hints:u.optional()}).strict().optional()}).strict(),g=e.object({as:e.string().trim().min(1,"must not be empty; remove the key to use the instance name").optional(),description:e.string().optional(),allowDryRuns:e.boolean().optional()}).strict(),h=t=>e.object({hints:u.optional(),groups:e.record(e.string(),g.nullable(),{required_error:`is required: the recipe groups of the type go under "groups", keyed by the instance name of a "${t}" component`})}).strict(),b=e.object({description:e.string().optional(),as:s.optional()}).strict(),y=["enum","pattern","minimum","maximum","minLength","maxLength"],f=["enum","pattern","minLength","maxLength"],_=["enum","minimum","maximum"],v=e.object({type:a(["string","number","integer","boolean"]),enum:e.array(e.union([e.string(),e.number()])).min(1,"must list at least one value").optional(),pattern:e.string().refine(e=>{try{return RegExp(e,"u"),!0}catch{return!1}},"must be a regular expression, as in JSON Schema").optional(),minimum:e.number().optional(),maximum:e.number().optional(),minLength:e.number().int().nonnegative().optional(),maxLength:e.number().int().nonnegative().optional()}).strict().superRefine((t,n)=>{let o="string"===t.type?f:"boolean"===t.type?[]:_;for(let i of y)void 0===t[i]||o.includes(i)||n.addIssue({code:e.ZodIssueCode.custom,path:[i],message:`does not apply to an attribute of type "${t.type}"; remove it or change the type`});let i="string"===t.type?"string":"number";for(let[o,r]of("boolean"!==t.type&&t.enum?.some(e=>typeof e!==i||"integer"===t.type&&!Number.isInteger(e))&&n.addIssue({code:e.ZodIssueCode.custom,path:["enum"],message:`must list only ${"integer"===t.type?"whole number":i}s, since the attribute is of type "${t.type}"`}),[["minimum","maximum"],["minLength","maxLength"]])){let[i,s]=[t[o],t[r]];void 0!==i&&void 0!==s&&i>s&&n.addIssue({code:e.ZodIssueCode.custom,path:[o],message:`must not be greater than "${r}", or no value could satisfy the attribute`})}}),w=e.string().regex(i,'must start with a lowercase letter and hold only lowercase letters, digits and "-", since the panel reads form markup in lowercase'),j=e.object({description:e.string().trim().min(1,"must not be empty; say what the element is for and where the agent may use it"),role:a(["group","choice","choices","option","input"]),attributes:e.record(w,v).optional(),required:e.array(e.string()).optional()}).strict(),x=e.object({description:e.string().trim().min(1,"must not be empty; say what the view shows"),as:s.optional()}).strict(),S=`is required: declare provider (${t.join(", ")}), model (${n.join(", ")}) and reasoning (${o.join(", ")})`,T="must be a positive whole number of input tokens; remove the key to use the default of the server",I=e.object({provider:a(t),model:a(n),reasoning:a(o),maxThreadSize:e.number({invalid_type_error:T}).int(T).positive(T).optional()},{required_error:S,invalid_type_error:S}).strict();export const rawAssistantSchema=e.object({entities:e.record(e.string(),e.record(e.string(),m.nullable())).optional(),deletable:e.record(e.string(),e.object({}).strict().nullable()).optional(),writable:e.record(e.string(),e.record(e.string(),d.nullable())).optional(),childRecipes:e.record(e.string(),h("childEntityRecipes")).optional(),replaceRecipes:e.record(e.string(),h("replaceEntityRecipes")).optional(),actions:e.record(e.string(),b.nullable()).optional(),screenshots:e.record(e.string(),e.record(e.string(),x)).optional(),lenses:e.record(c,p).optional(),formElements:e.record(w,j).optional(),instructions:e.string().trim().min(1,"must not be empty; remove the key when there are no instructions").optional(),ai:I}).strict();
@@ -0,0 +1,9 @@
1
+ import { type AssistantFormElementDescriptor } from '#package/domain';
2
+ import type { ParseContext } from '../../../../core/index.js';
3
+ import type { RawAssistantFormElement } from '../../../raw-spec/index.js';
4
+ /**
5
+ * Checks the project's own form elements and turns them into the descriptors the built-ins use, in
6
+ * YAML order. Whether each has an implementation is not known here, since module analysis does not
7
+ * see the name a file registers; the application checks that pairing when it starts.
8
+ */
9
+ export declare const resolveFormElements: (formElements: Readonly<Record<string, RawAssistantFormElement>>, ctx: ParseContext) => readonly AssistantFormElementDescriptor[];
@@ -0,0 +1 @@
1
+ import{ASSISTANT_BUILT_IN_FORM_ELEMENTS as e,isHtmlElementName as r}from"#package/domain";import{DiagnosticCode as E,diagnosticMessages as _}from"../../../../errors/index.js";let T=e.map(({name:e})=>e),t=new Set(["label"]);export const resolveFormElements=(e,N)=>Object.entries(e).map(([e,S])=>{let A=N.at(e);return T.includes(e)?A.error(E.ASSISTANT_FORM_ELEMENT_BUILT_IN_NAME,_.ASSISTANT_FORM_ELEMENT_BUILT_IN_NAME(e,T)):r(e)&&A.error(E.ASSISTANT_FORM_ELEMENT_HTML_NAME,_.ASSISTANT_FORM_ELEMENT_HTML_NAME(e)),((e,{attributes:r={},required:T=[]},N)=>{let S=Object.keys(r);for(let r of S)t.has(r)&&N.at("attributes",r).error(E.ASSISTANT_FORM_ATTRIBUTE_RESERVED_NAME,_.ASSISTANT_FORM_ATTRIBUTE_RESERVED_NAME(e,r));for(let t of T)Object.hasOwn(r,t)||N.at("required").error(E.ASSISTANT_FORM_ELEMENT_UNKNOWN_REQUIRED,_.ASSISTANT_FORM_ELEMENT_UNKNOWN_REQUIRED(e,t,S))})(e,S,A),{name:e,role:S.role,description:S.description,attributes:S.attributes??{},...void 0===S.required?{}:{required:S.required}}});
@@ -29,7 +29,10 @@ export type AssistantSchema = Required<AssistantManifest> & {
29
29
  /** What the assistant schema is built from, besides the manifest. */
30
30
  export interface AssistantSchemaInput {
31
31
  readonly model: ModelVersion;
32
- readonly states: AssistantStates;
32
+ /** The declared UI states; the export states only check the screenshot views, the schema never shows them. */
33
+ readonly states: AssistantStates & {
34
+ readonly exportStates: readonly string[];
35
+ };
33
36
  /** Resolves `phx:` refs, so a field's nullability is read through phoenix definitions. */
34
37
  readonly phxDefinitions: PhoenixDefinitions;
35
38
  /** The parsed expressions, which `$` manifest entries whitelist by their declared schema. */
@@ -1 +1 @@
1
- import{isArray as e}from"@salsita-ai/common/utils";import{ASSISTANT_HINTS as t,AssistantFieldClashError as i,buildAssistantManifest as r,describeAssistantValues as n,mergeAssistantFields as s,resolveAssistantField as o}from"#package/domain";import{DiagnosticCode as a,diagnosticMessages as l}from"../../../../errors/index.js";import{createSchemaRefResolver as S,isNullableSchema as p,phxDefinitionRefName as N}from"../../common/index.js";import{resolveAssistantFieldType as T}from"./assistant-field-type.js";let c="AnyDomainEntity",d="RecipeGroup",f="Recipe",A="DryRun",E="FieldOption",m={section:"childRecipes",component:"childEntityRecipes",mutation:"addChild"},_={section:"replaceRecipes",component:"replaceEntityRecipes",mutation:"replaceEntity"},u=new Set(["id","type","meta"]),O=new Set(["Query","Mutation","Navigation",d,f,A,"DryRunError",E,"JSON","String","Int","Float","Boolean","ID"]),I=new Set([d,f,E,A]),y=new Set(["id","type"]),b="A string, a message descriptor or a multilingual object; passed without translation.",h=(e,t)=>Object.entries(e).find(([e])=>e===t)?.[1],g=(e,{property:t,writableKey:i},r,{resolveRef:s,exposed:o,sources:S},p)=>{let N=Object.entries(h(S,e)??{}),c=o.get(e)?.find(({source:e})=>e===t)?.name??N.find(([,{source:e}])=>e===t)?.[0],d=void 0===c?N.find(([e])=>e===t)?.[1]:void 0;if(d)return void p.at(i,t).error(a.ASSISTANT_FIELD_NAME_CONFLICT,l.ASSISTANT_FIELD_NAME_CONFLICT(e,t,[d.source,t],`${i}.${t}`));let{type:f,enumValues:A,localized:E,description:m}=T(r,s),_={...f,nonNull:!1},u=c??t,O=void 0===c?n(m,A):void 0;return E?{name:u,property:t,type:{kind:"scalar",name:"JSON",nonNull:!1},description:O?`${O} ${b}`:b}:{name:u,property:t,type:_,...void 0===O?{}:{description:O}}},v=(e,t,{model:i,components:r})=>{let n=new Set([e,...[...i.aliases].filter(([,t])=>t.includes(e)).map(([e])=>e)]);return r.filter(({componentName:e,typeOrAlias:i})=>e===t&&n.has(i)).map(({instanceName:e})=>String(e))},R=(...e)=>t.filter(t=>e.some(e=>e?.includes(t))),j=(e,t,i,r)=>{let{model:n}=i,s=new Map,o=new Map;for(let[S,{groups:p,hints:N}]of Object.entries(e)){let e=n.entityTypes.find(e=>e===S),T=e?[e]:[...n.aliases].find(([e])=>e===S)?.[1];if(!T){r.at(S).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE(t.section,S));continue}for(let[e,n]of(T.forEach(e=>o.set(e,R(o.get(e),N))),Object.entries(p))){let o=r.at(S,"groups",e),p=`${S}.groups.${e}`,N={groupId:n?.as??e,instanceName:e,...n?.description===void 0?{}:{description:n.description},dryRuns:n?.allowDryRuns??!0};for(let r of T){let n=v(r,t.component,i),T=s.get(r)??[],c=T.find(e=>e.group.groupId===N.groupId);n.includes(e)?c?o.error(a.ASSISTANT_DUPLICATE_RECIPE_GROUP,l.ASSISTANT_DUPLICATE_RECIPE_GROUP(t,r,N.groupId,[c.entry,p])):s.set(r,[...T,{group:N,entry:p,entryCtx:o}]):o.error(a.ASSISTANT_UNKNOWN_RECIPE_GROUP,l.ASSISTANT_UNKNOWN_RECIPE_GROUP(t,S,e,r,n))}}}let S=n.entityTypes.flatMap(e=>{let t=s.get(e);return t?[{entityType:e,groups:t}]:[]});return{groups:Object.fromEntries(S.map(({entityType:e,groups:t})=>[e,t.map(({group:e})=>e)])),hints:o,metaEntries:new Map(S.flatMap(({entityType:e,groups:[t]})=>t?[[e,t.entryCtx]]:[]))}},M=e=>`${e}Meta`,D=e=>`${e.charAt(0).toUpperCase()+e.slice(1)}Result`,C=e=>{let t=(({anyOf:e,oneOf:t})=>[...e??[],...t??[]].filter(e=>"null"!==e.type))(e);return e.$ref||1!==t.length?e:t[0]},P=(e,t)=>{let{type:i,enumValues:r,localized:s,description:o}=T(e,t),a={...i,nonNull:!1},l=s?`${n(o,r)??""} ${b}`.trim():n(o,r);return{type:{...s?{kind:"scalar",name:"JSON",nonNull:!1}:a,nonNull:i.nonNull},...void 0===l?{}:{description:l}}},L=(t,i)=>{let r=(t=>{let i=C(t),r=i?.type==="array"&&i.items&&!e(i.items)?C(i.items):void 0,n=r??i;if(n?.$ref!==void 0&&"$DomainEntity"===N(n.$ref))return{target:n.references??c,list:void 0!==r}})(t);if(r)return{kind:"edge",...r,nullable:p(t,i),...void 0===t.description?{}:{description:t.description}};let{type:s,enumValues:o,localized:a,description:l}=T(t,i),S=n(l,o);return a?{kind:"value",type:{kind:"scalar",name:"JSON",nonNull:s.nonNull},description:`${l??""} ${b}`.trim()}:{kind:"value",type:s,...void 0===S?{}:{description:S}}},U=(e,t)=>{let{type:i,enumValues:r,localized:n,description:s}=T(e,t);return{type:{kind:"value",type:i},localized:!0===n,description:s,enumValues:r}},w=(e,t,i,r,s)=>{let o=t.startsWith("$")?((e,t,i,r)=>{let n=((e,t,{expressions:i,entityTypes:r})=>{let n=r.find(t=>t===e);for(let r of void 0===n?[e]:i.entityExpressions.get(n)?.shapeGroups??[]){let e=[...i.expressions.get(r)?.values()??[]].find(e=>e.name===t);if(e?.declaredSchema)return{implementation:e.implementation,declaredSchema:e.declaredSchema}}})(e.name,t,i);if(!n)return void r.error(a.ASSISTANT_UNKNOWN_FIELD,l.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t));let{implementation:s}=n;if("violations"===s.kind)return void r.error(a.ASSISTANT_UNSUPPORTED_EXPRESSION,l.ASSISTANT_UNSUPPORTED_EXPRESSION(e.name,t));if("relation"===s.kind){let[{target:e,cardinality:t}]=s.params;return{type:{kind:"edge",target:e,cardinality:t},localized:!1,description:void 0}}return U(n.declaredSchema,i.resolveRef)})(e,t,r,s):((e,t,{resolveRef:i},r)=>{let n=Object.entries(e.shape.properties).find(([e])=>e===t)?.[1];return n?U(n,i):void r.error(a.ASSISTANT_UNKNOWN_FIELD,l.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t))})(e,t,r,s);if(!o)return;let S=i?.as??(t.startsWith("$")?`_${t.slice(1)}`:t);return u.has(S)?void s.error(a.ASSISTANT_RESERVED_FIELD_NAME,l.ASSISTANT_RESERVED_FIELD_NAME(e.name,t,S)):{name:S,source:t,type:o.type,localized:o.localized,description:n(i?.description??o.description,o.enumValues),describedByManifest:i?.description!==void 0,enumValues:o.enumValues,valueDescription:i?.description??o.description,...i?.lenses?.length?{lenses:[...new Set(i.lenses)]}:{}}},$=(e,t,i,r,n)=>s(t,i,r,(t,i,r)=>{n.error(a.ASSISTANT_FIELD_NAME_CONFLICT,l.ASSISTANT_FIELD_NAME_CONFLICT(e,t,[i,r]))});export const resolveAssistantManifest=(e,{model:t,states:i,phxDefinitions:r,expressions:n,components:s,actions:p},N)=>{let d=N.errors.length,f=N.at("entities"),A=S(t.commonTypes,r);for(let e of t.resolvedShapes.keys())O.has(e)&&N.error(a.ASSISTANT_RESERVED_TYPE_NAME,l.ASSISTANT_RESERVED_TYPE_NAME(e,[...O]));let E=((e,t,i)=>{let r=new Map;for(let n of e){let e=[];for(let[r,s]of Object.entries(n.entries)){let o=w(n,r,s,t,i.at(n.name,r));o&&e.push(o)}r.set(n.name,e)}return r})(((e,t,i)=>{let r=[];for(let[i,n]of t.resolvedShapes){let t=e[i];t&&r.push({name:i,shape:n,entries:t})}let n=new Set(t.resolvedShapes.keys());for(let t of Object.keys(e))n.has(t)||i.at(t).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("entities",t));return r})(e.entities??{},t,f),{resolveRef:A,expressions:n,entityTypes:t.entityTypes},f),u=$(c,[],E.get(c)??[],()=>void 0,N),C=new Map([[c,u]]);for(let e of t.aliases.keys())e!==c&&C.set(e,$(e,[u],E.get(e)??[],()=>void 0,N));for(let e of t.entityTypes){let i=[c,...[...t.aliases].filter(([t,i])=>t!==c&&i.includes(e)).map(([e])=>e)],r=[...t.resolvedShapes].find(([t])=>t===e)?.[1];C.set(e,$(e,i.map(e=>C.get(e)??[]),E.get(e)??[],e=>{let t=Object.entries(r?.properties??{}).find(([t])=>t===e)?.[1];return t&&T(t,A).enumValues},N))}let U=Object.fromEntries(t.entityTypes.map(e=>[e,Object.fromEntries((C.get(e)??[]).map(({name:e,source:t,localized:i})=>[e,i?{source:t,localized:!0}:{source:t}]))])),F=j(e.childRecipes??{},m,{model:t,components:s},N.at("childRecipes")),W=j(e.replaceRecipes??{},_,{model:t,components:s},N.at("replaceRecipes")),k=((e,t,i)=>{let r=new Set;for(let n of Object.keys(e)){let e=t.entityTypes.find(e=>e===n),s=e?[e]:[...t.aliases].find(([e])=>e===n)?.[1];s?s.forEach(e=>r.add(e)):i.at(n).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("deletable",n))}return t.entityTypes.filter(e=>r.has(e))})(e.deletable??{},t,N.at("deletable")),K=((e,t,i)=>{let{model:r}=t,n=((e,t,i)=>{let r=new Map;for(let[n,s]of Object.entries(e)){let e=t.entityTypes.find(e=>e===n),o=e?[e]:[...t.aliases].find(([e])=>e===n)?.[1],S=[...t.resolvedShapes].find(([e])=>e===n)?.[1];if(!o||!S){i.at(n).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("writable",n));continue}for(let e of Object.keys(s)){let t=i.at(n,e);e.startsWith("$")||y.has(e)?t.error(a.ASSISTANT_NOT_WRITABLE,l.ASSISTANT_NOT_WRITABLE(n,e)):h(S.properties,e)?o.forEach(t=>{let i=r.get(t)??new Map;r.set(t,i.has(e)?i:i.set(e,n))}):t.error(a.ASSISTANT_UNKNOWN_FIELD,l.ASSISTANT_UNKNOWN_FIELD("writable",n,e))}}return r})(e,r,i);return r.entityTypes.flatMap(e=>{let s=[...r.resolvedShapes].find(([t])=>t===e)?.[1],o=[...n.get(e)??[]].flatMap(([r,n])=>{let o=s&&h(s.properties,r);return(o&&g(e,{property:r,writableKey:n},o,t,i))??[]});return o.length>0?[{entityType:e,fields:o}]:[]})})(e.writable??{},{model:t,resolveRef:A,exposed:E,sources:U},N.at("writable")),V=((e,t,i,r)=>{let{model:n}=i,s=new Map;for(let[o,S]of Object.entries(e)){let e=n.entityTypes.find(e=>e===o),p=e?[e]:[...n.aliases].find(([e])=>e===o)?.[1]??[];for(let[e,n]of Object.entries(S)){let S=n?.options;if(!S)continue;let N=r.at(o,e,"options"),T=`${o}.${e}`;for(let r of p){let n=t.find(e=>e.entityType===r)?.fields.find(t=>t.property===e);if(!n)continue;let o=S.as??`${n.name}Options`,p=v(r,S.componentType,i),c=s.get(r)??[],d=c.find(e=>e.options.edgeName===o||e.options.field===n.name);if(p.includes(S.instanceName))if(o===m.section||o===_.section)N.error(a.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME,l.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME(T,o,n.name));else if(d)N.error(a.ASSISTANT_DUPLICATE_OPTIONS_EDGE,l.ASSISTANT_DUPLICATE_OPTIONS_EDGE(r,d.options.edgeName===o?{edgeName:o}:{field:n.name},[d.entry,T]));else{let e={field:n.name,edgeName:o,componentType:S.componentType,instanceName:S.instanceName,validate:S.validate??!0,dryRuns:S.allowDryRuns??!0};s.set(r,[...c,{options:e,hints:R(S.hints),entry:T,entryCtx:N}])}else N.error(a.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT,l.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT(T,S,r,p))}}}return s})(e.writable??{},K,{model:t,components:s},N.at("writable"));var Y=[F.metaEntries,W.metaEntries,new Map([...V].flatMap(([e,[t]])=>t?[[e,t.entryCtx]]:[]))];for(let e of t.entityTypes){let i=Y.flatMap(t=>t.get(e)??[])[0],r=M(e);i&&[...t.resolvedShapes.keys()].some(e=>e===r)&&i.error(a.ASSISTANT_META_TYPE_NAME_CONFLICT,l.ASSISTANT_META_TYPE_NAME_CONFLICT(e,r))}let x=t.entityTypes.filter(e=>(F.groups[e]?.length??0)>0||(W.groups[e]?.length??0)>0||(V.get(e)?.length??0)>0).map(M),G=((e,{actions:t,resolveRef:i,builtInMutations:r,typeNames:n},s)=>{let o=[],S=[...t.values()].map(e=>String(e.name));for(let[p,N]of Object.entries(e)){let e=s.at(p),T=[...t.values()].find(e=>e.name===p);if(!T){e.error(a.ASSISTANT_UNKNOWN_ACTION,l.ASSISTANT_UNKNOWN_ACTION(p,S));continue}let c=N?.as??p,d=o.find(e=>e.fieldName===c),f=D(c);if(r.has(c)||d){e.error(a.ASSISTANT_MUTATION_NAME_CONFLICT,l.ASSISTANT_MUTATION_NAME_CONFLICT(p,c,d?`"assistant.actions.${d.actionName}" already exposes`:"the built-in mutation of that name already takes"));continue}if(n.has(f)||o.some(e=>D(e.fieldName)===f)){e.error(a.ASSISTANT_ACTION_RESULT_TYPE_CONFLICT,l.ASSISTANT_ACTION_RESULT_TYPE_CONFLICT(p,c,f));continue}let A=N?.description??T.description;o.push({fieldName:c,actionName:p,...void 0===A?{}:{description:A},args:Object.fromEntries(Object.entries(T.params).map(([e,{schema:t}])=>[e,P(t,i)])),result:Object.fromEntries(Object.entries(T.result).map(([e,t])=>[e,L(t,i)]))})}return o})(e.actions??{},{actions:p,resolveRef:A,builtInMutations:new Set(["deleteEntity",m.mutation,_.mutation,...K.map(({entityType:e})=>`modify${e}`)]),typeNames:new Set([...O,...t.resolvedShapes.keys(),...x])},N.at("actions")),z=((e,t)=>{let i=e.lenses??{},r=new Set;for(let[n,s]of Object.entries(e.entities??{}))for(let[e,o]of Object.entries(s))for(let s of o?.lenses??[])Object.hasOwn(i,s)?r.add(s):t.at("entities",n,e,"lenses").error(a.ASSISTANT_UNKNOWN_LENS,l.ASSISTANT_UNKNOWN_LENS(`${n}.${e}`,s,Object.keys(i)));for(let e of Object.keys(i))I.has(e)?t.at("lenses",e).error(a.ASSISTANT_RESERVED_LENS_NAME,l.ASSISTANT_RESERVED_LENS_NAME(e,[...I])):r.has(e)||t.at("lenses",e).error(a.ASSISTANT_EMPTY_LENS,l.ASSISTANT_EMPTY_LENS(e));return Object.fromEntries(Object.entries(i).map(([e,{description:t,maxDepth:i}])=>[e,{description:t,maxDepth:i??1}]))})(e,N);if(N.errors.length>d)return;let B=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,{hints:R(t.hints),groups:Object.entries(t.groups).map(([e,t])=>({instanceName:e,groupId:t?.as??e,...t?.description===void 0?{}:{description:t.description},dryRuns:t?.allowDryRuns??!0}))}]));return{declarations:{entities:Object.fromEntries(Object.keys(e.entities??{}).map(e=>[e,(E.get(e)??[]).map(e=>({name:e.name,source:e.source,type:e.type,localized:e.localized,describedByManifest:e.describedByManifest,...void 0===e.description?{}:{description:e.description},...void 0===e.valueDescription?{}:{valueDescription:e.valueDescription},...void 0===e.enumValues?{}:{enumValues:e.enumValues},...void 0===e.lenses?{}:{lenses:e.lenses}}))])),enumValues:Object.fromEntries(t.entityTypes.map(e=>[e,Object.fromEntries(Object.entries(t.resolvedShapes.get(e)?.properties??{}).flatMap(([e,t])=>{let i=T(t,A).enumValues;return i?[[e,i]]:[]}))])),deletable:Object.keys(e.deletable??{}),writable:Object.fromEntries(Object.entries(e.writable??{}).map(([e,i])=>[e,Object.fromEntries(Object.entries(i).map(([i,r])=>[i,{defaultOptionsName:r?.options?.as===void 0,members:Object.fromEntries(t.entityTypes.filter(i=>i===e||[...t.aliases].find(([t])=>t===e)?.[1].includes(i)).flatMap(r=>{let n=h(t.resolvedShapes.get(r)?.properties??{},i),s=n&&g(r,{property:i,writableKey:e},n,{model:t,resolveRef:A,exposed:new Map,sources:{}},N.at("writable")),o=V.get(r)?.find(t=>t.entry===`${e}.${i}`);return s?[[r,{field:s,...T(n,A).localized?{exposedDescription:b}:{},...o?{options:{options:o.options,hints:o.hints}}:{}}]]:[]}))}]))])),childRecipes:B(e.childRecipes??{}),replaceRecipes:B(e.replaceRecipes??{})},entityTypes:t.entityTypes,aliases:Object.fromEntries(t.aliases),exampleType:t.rootEntityTypes[0]??t.entityTypes[0],states:{navigationStates:i.navigationStates,viewStates:i.viewStates,toolStates:i.toolStates},deletableTypes:k,writableTypes:K,actionFields:G,childRecipes:Object.fromEntries(Object.entries(F.groups).map(([e,t])=>[e,{groups:t,hints:F.hints.get(e)??[]}])),replaceRecipes:Object.fromEntries(Object.entries(W.groups).map(([e,t])=>[e,{groups:t,hints:W.hints.get(e)??[]}])),optionsEdges:Object.fromEntries([...V].map(([e,t])=>[e,t.map(({options:e,hints:t})=>({options:e,hints:t}))])),fields:Object.fromEntries([...C].map(([e,t])=>[e,t.map(t=>o(t,E.get(e)??[]))])),lenses:z}};export const createAssistantSchema=(e,t,n)=>{let s=resolveAssistantManifest(e,t,n);if(s)try{return{...r(s),resolved:s}}catch(e){if(e instanceof i)return void n.at("entities").error(a.ASSISTANT_FIELD_SHAPE_CONFLICT,l.ASSISTANT_FIELD_SHAPE_CONFLICT(e.fieldName,e.exposures));n.error(a.ASSISTANT_INVALID_SCHEMA,l.ASSISTANT_INVALID_SCHEMA([e instanceof Error?e.message:String(e)]));return}};
1
+ import{isArray as e}from"@salsita-ai/common/utils";import{ASSISTANT_HINTS as t,AssistantFieldClashError as r,buildAssistantManifest as i,describeAssistantValues as n,mergeAssistantFields as s,resolveAssistantField as o}from"#package/domain";import{DiagnosticCode as a,diagnosticMessages as l}from"../../../../errors/index.js";import{createSchemaRefResolver as S,isNullableSchema as p,phxDefinitionRefName as N}from"../../common/index.js";import{resolveAssistantFieldType as T}from"./assistant-field-type.js";import{resolveFormElements as c}from"./assistant-form-elements.js";let d="AnyDomainEntity",E="RecipeGroup",f="Recipe",A="DryRun",_="FieldOption",m="agentScreenshot",u=/^(?!(true|false|null)$)[_A-Za-z][_0-9A-Za-z]*$/,O={section:"childRecipes",component:"childEntityRecipes",mutation:"addChild"},I={section:"replaceRecipes",component:"replaceEntityRecipes",mutation:"replaceEntity"},y=new Set(["id","type","meta"]),h=new Set(["Query","Mutation","Navigation",E,f,A,"DryRunError",_,"Screenshot","JSON","String","Int","Float","Boolean","ID"]),v=new Set([E,f,_,A]),b=new Set(["id","type"]),g="A string, a message descriptor or a multilingual object; passed without translation.",R=(e,t)=>Object.entries(e).find(([e])=>e===t)?.[1],M=(e,{property:t,writableKey:r},i,{resolveRef:s,exposed:o,sources:S},p)=>{let N=Object.entries(R(S,e)??{}),c=o.get(e)?.find(({source:e})=>e===t)?.name??N.find(([,{source:e}])=>e===t)?.[0],d=void 0===c?N.find(([e])=>e===t)?.[1]:void 0;if(d)return void p.at(r,t).error(a.ASSISTANT_FIELD_NAME_CONFLICT,l.ASSISTANT_FIELD_NAME_CONFLICT(e,t,[d.source,t],`${r}.${t}`));let{type:E,enumValues:f,localized:A,description:_}=T(i,s),m={...E,nonNull:!1},u=c??t,O=void 0===c?n(_,f):void 0;return A?{name:u,property:t,type:{kind:"scalar",name:"JSON",nonNull:!1},description:O?`${O} ${g}`:g}:{name:u,property:t,type:m,...void 0===O?{}:{description:O}}},C=(e,t,{model:r,components:i})=>{let n=new Set([e,...[...r.aliases].filter(([,t])=>t.includes(e)).map(([e])=>e)]);return i.filter(({componentName:e,typeOrAlias:r})=>e===t&&n.has(r)).map(({instanceName:e})=>String(e))},j=(...e)=>t.filter(t=>e.some(e=>e?.includes(t))),w=(e,t,r,i)=>{let{model:n}=r,s=new Map,o=new Map;for(let[S,{groups:p,hints:N}]of Object.entries(e)){let e=n.entityTypes.find(e=>e===S),T=e?[e]:[...n.aliases].find(([e])=>e===S)?.[1];if(!T){i.at(S).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE(t.section,S));continue}for(let[e,n]of(T.forEach(e=>o.set(e,j(o.get(e),N))),Object.entries(p))){let o=i.at(S,"groups",e),p=`${S}.groups.${e}`,N={groupId:n?.as??e,instanceName:e,...n?.description===void 0?{}:{description:n.description},dryRuns:n?.allowDryRuns??!0};for(let i of T){let n=C(i,t.component,r),T=s.get(i)??[],c=T.find(e=>e.group.groupId===N.groupId);n.includes(e)?c?o.error(a.ASSISTANT_DUPLICATE_RECIPE_GROUP,l.ASSISTANT_DUPLICATE_RECIPE_GROUP(t,i,N.groupId,[c.entry,p])):s.set(i,[...T,{group:N,entry:p,entryCtx:o}]):o.error(a.ASSISTANT_UNKNOWN_RECIPE_GROUP,l.ASSISTANT_UNKNOWN_RECIPE_GROUP(t,S,e,i,n))}}}let S=n.entityTypes.flatMap(e=>{let t=s.get(e);return t?[{entityType:e,groups:t}]:[]});return{groups:Object.fromEntries(S.map(({entityType:e,groups:t})=>[e,t.map(({group:e})=>e)])),hints:o,metaEntries:new Map(S.flatMap(({entityType:e,groups:[t]})=>t?[[e,t.entryCtx]]:[]))}},P=(e,t,{model:r,components:i})=>{let n=new Set([e,...[...r.aliases].filter(([,t])=>t.includes(e)).map(([e])=>e)]),s=i.find(e=>e.componentName===m&&String(e.instanceName)===t&&n.has(e.typeOrAlias));if(s?.implementation.kind!=="userBound"||"composite"!==s.implementation.implementation.kind)return;let o=s.implementation.implementation.resolvedProps.exportState;return"string"==typeof o?o:void 0},D=e=>`${e}Meta`,L=e=>`${e}ScreenshotView`,U=e=>`${e.charAt(0).toUpperCase()+e.slice(1)}Result`,W=e=>{let t=(({anyOf:e,oneOf:t})=>[...e??[],...t??[]].filter(e=>"null"!==e.type))(e);return e.$ref||1!==t.length?e:t[0]},$=(e,t)=>{let{type:r,enumValues:i,localized:s,description:o}=T(e,t),a={...r,nonNull:!1},l=s?`${n(o,i)??""} ${g}`.trim():n(o,i);return{type:{...s?{kind:"scalar",name:"JSON",nonNull:!1}:a,nonNull:r.nonNull},...void 0===l?{}:{description:l}}},F=(t,r)=>{let i=(t=>{let r=W(t),i=r?.type==="array"&&r.items&&!e(r.items)?W(r.items):void 0,n=i??r;if(n?.$ref!==void 0&&"$DomainEntity"===N(n.$ref))return{target:n.references??d,list:void 0!==i}})(t);if(i)return{kind:"edge",...i,nullable:p(t,r),...void 0===t.description?{}:{description:t.description}};let{type:s,enumValues:o,localized:a,description:l}=T(t,r),S=n(l,o);return a?{kind:"value",type:{kind:"scalar",name:"JSON",nonNull:s.nonNull},description:`${l??""} ${g}`.trim()}:{kind:"value",type:s,...void 0===S?{}:{description:S}}},V=(e,t)=>{let{type:r,enumValues:i,localized:n,description:s}=T(e,t);return{type:{kind:"value",type:r},localized:!0===n,description:s,enumValues:i}},K=(e,t,r,i,s)=>{let o=t.startsWith("$")?((e,t,r,i)=>{let n=((e,t,{expressions:r,entityTypes:i})=>{let n=i.find(t=>t===e);for(let i of void 0===n?[e]:r.entityExpressions.get(n)?.shapeGroups??[]){let e=[...r.expressions.get(i)?.values()??[]].find(e=>e.name===t);if(e?.declaredSchema)return{implementation:e.implementation,declaredSchema:e.declaredSchema}}})(e.name,t,r);if(!n)return void i.error(a.ASSISTANT_UNKNOWN_FIELD,l.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t));let{implementation:s}=n;if("violations"===s.kind)return void i.error(a.ASSISTANT_UNSUPPORTED_EXPRESSION,l.ASSISTANT_UNSUPPORTED_EXPRESSION(e.name,t));if("relation"===s.kind){let[{target:e,cardinality:t}]=s.params;return{type:{kind:"edge",target:e,cardinality:t},localized:!1,description:void 0}}return V(n.declaredSchema,r.resolveRef)})(e,t,i,s):((e,t,{resolveRef:r},i)=>{let n=Object.entries(e.shape.properties).find(([e])=>e===t)?.[1];return n?V(n,r):void i.error(a.ASSISTANT_UNKNOWN_FIELD,l.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t))})(e,t,i,s);if(!o)return;let S=r?.as??(t.startsWith("$")?`_${t.slice(1)}`:t);return y.has(S)?void s.error(a.ASSISTANT_RESERVED_FIELD_NAME,l.ASSISTANT_RESERVED_FIELD_NAME(e.name,t,S)):{name:S,source:t,type:o.type,localized:o.localized,description:n(r?.description??o.description,o.enumValues),describedByManifest:r?.description!==void 0,enumValues:o.enumValues,valueDescription:r?.description??o.description,...r?.lenses?.length?{lenses:[...new Set(r.lenses)]}:{}}},k=(e,t,r,i,n)=>s(t,r,i,(t,r,i)=>{n.error(a.ASSISTANT_FIELD_NAME_CONFLICT,l.ASSISTANT_FIELD_NAME_CONFLICT(e,t,[r,i]))});export const resolveAssistantManifest=(e,{model:t,states:r,phxDefinitions:i,expressions:n,components:s,actions:p},N)=>{let E=N.errors.length,f=N.at("entities"),A=S(t.commonTypes,i);for(let e of t.resolvedShapes.keys())h.has(e)&&N.error(a.ASSISTANT_RESERVED_TYPE_NAME,l.ASSISTANT_RESERVED_TYPE_NAME(e,[...h]));let _=((e,t,r)=>{let i=new Map;for(let n of e){let e=[];for(let[i,s]of Object.entries(n.entries)){let o=K(n,i,s,t,r.at(n.name,i));o&&e.push(o)}i.set(n.name,e)}return i})(((e,t,r)=>{let i=[];for(let[r,n]of t.resolvedShapes){let t=e[r];t&&i.push({name:r,shape:n,entries:t})}let n=new Set(t.resolvedShapes.keys());for(let t of Object.keys(e))n.has(t)||r.at(t).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("entities",t));return i})(e.entities??{},t,f),{resolveRef:A,expressions:n,entityTypes:t.entityTypes},f),y=k(d,[],_.get(d)??[],()=>void 0,N),W=new Map([[d,y]]);for(let e of t.aliases.keys())e!==d&&W.set(e,k(e,[y],_.get(e)??[],()=>void 0,N));for(let e of t.entityTypes){let r=[d,...[...t.aliases].filter(([t,r])=>t!==d&&r.includes(e)).map(([e])=>e)],i=[...t.resolvedShapes].find(([t])=>t===e)?.[1];W.set(e,k(e,r.map(e=>W.get(e)??[]),_.get(e)??[],e=>{let t=Object.entries(i?.properties??{}).find(([t])=>t===e)?.[1];return t&&T(t,A).enumValues},N))}let V=Object.fromEntries(t.entityTypes.map(e=>[e,Object.fromEntries((W.get(e)??[]).map(({name:e,source:t,localized:r})=>[e,r?{source:t,localized:!0}:{source:t}]))])),Y=w(e.childRecipes??{},O,{model:t,components:s},N.at("childRecipes")),x=w(e.replaceRecipes??{},I,{model:t,components:s},N.at("replaceRecipes")),H=((e,t,r)=>{let i=new Set;for(let n of Object.keys(e)){let e=t.entityTypes.find(e=>e===n),s=e?[e]:[...t.aliases].find(([e])=>e===n)?.[1];s?s.forEach(e=>i.add(e)):r.at(n).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("deletable",n))}return t.entityTypes.filter(e=>i.has(e))})(e.deletable??{},t,N.at("deletable")),z=((e,t,r)=>{let{model:i}=t,n=((e,t,r)=>{let i=new Map;for(let[n,s]of Object.entries(e)){let e=t.entityTypes.find(e=>e===n),o=e?[e]:[...t.aliases].find(([e])=>e===n)?.[1],S=[...t.resolvedShapes].find(([e])=>e===n)?.[1];if(!o||!S){r.at(n).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("writable",n));continue}for(let e of Object.keys(s)){let t=r.at(n,e);e.startsWith("$")||b.has(e)?t.error(a.ASSISTANT_NOT_WRITABLE,l.ASSISTANT_NOT_WRITABLE(n,e)):R(S.properties,e)?o.forEach(t=>{let r=i.get(t)??new Map;i.set(t,r.has(e)?r:r.set(e,n))}):t.error(a.ASSISTANT_UNKNOWN_FIELD,l.ASSISTANT_UNKNOWN_FIELD("writable",n,e))}}return i})(e,i,r);return i.entityTypes.flatMap(e=>{let s=[...i.resolvedShapes].find(([t])=>t===e)?.[1],o=[...n.get(e)??[]].flatMap(([i,n])=>{let o=s&&R(s.properties,i);return(o&&M(e,{property:i,writableKey:n},o,t,r))??[]});return o.length>0?[{entityType:e,fields:o}]:[]})})(e.writable??{},{model:t,resolveRef:A,exposed:_,sources:V},N.at("writable")),G=((e,t,r,i)=>{let{model:n}=r,s=new Map;for(let[o,S]of Object.entries(e)){let e=n.entityTypes.find(e=>e===o),p=e?[e]:[...n.aliases].find(([e])=>e===o)?.[1]??[];for(let[e,n]of Object.entries(S)){let S=n?.options;if(!S)continue;let N=i.at(o,e,"options"),T=`${o}.${e}`;for(let i of p){let n=t.find(e=>e.entityType===i)?.fields.find(t=>t.property===e);if(!n)continue;let o=S.as??`${n.name}Options`,p=C(i,S.componentType,r),c=s.get(i)??[],d=c.find(e=>e.options.edgeName===o||e.options.field===n.name);if(p.includes(S.instanceName))if(o===O.section||o===I.section||"screenshot"===o)N.error(a.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME,l.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME(T,o,n.name));else if(d)N.error(a.ASSISTANT_DUPLICATE_OPTIONS_EDGE,l.ASSISTANT_DUPLICATE_OPTIONS_EDGE(i,d.options.edgeName===o?{edgeName:o}:{field:n.name},[d.entry,T]));else{let e={field:n.name,edgeName:o,componentType:S.componentType,instanceName:S.instanceName,validate:S.validate??!0,dryRuns:S.allowDryRuns??!0};s.set(i,[...c,{options:e,hints:j(S.hints),entry:T,entryCtx:N}])}else N.error(a.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT,l.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT(T,S,i,p))}}}return s})(e.writable??{},z,{model:t,components:s},N.at("writable")),B=((e,t,r)=>{let{model:i,states:n}=t,s=new Map;for(let[o,S]of Object.entries(e)){let e=i.entityTypes.find(e=>e===o),p=e?[e]:[...i.aliases].find(([e])=>e===o)?.[1];if(!p){r.at(o).error(a.ASSISTANT_UNKNOWN_TYPE,l.ASSISTANT_UNKNOWN_TYPE("screenshots",o));continue}for(let[e,{description:i,as:N}]of Object.entries(S)){let S=r.at(o,e),T=`${o}.${e}`,c={view:N??e,instanceName:e,description:i};if(!u.test(c.view)){S.error(a.ASSISTANT_INVALID_SCREENSHOT_VIEW_NAME,l.ASSISTANT_INVALID_SCREENSHOT_VIEW_NAME(o,e,c.view));continue}for(let r of p){let i=C(r,m,t),p=P(r,e,t),N=s.get(r)??[],d=N.find(e=>e.view.view===c.view);i.includes(e)?void 0===p||n.exportStates.includes(p)?d?S.error(a.ASSISTANT_DUPLICATE_SCREENSHOT_VIEW,l.ASSISTANT_DUPLICATE_SCREENSHOT_VIEW(r,c.view,[d.entry,T])):s.set(r,[...N,{view:c,entry:T,entryCtx:S}]):S.error(a.ASSISTANT_UNKNOWN_SCREENSHOT_EXPORT_STATE,l.ASSISTANT_UNKNOWN_SCREENSHOT_EXPORT_STATE(o,e,p,n.exportStates)):S.error(a.ASSISTANT_UNKNOWN_SCREENSHOT_VIEW,l.ASSISTANT_UNKNOWN_SCREENSHOT_VIEW(o,e,r,i))}}}let o=i.entityTypes.flatMap(e=>{let t=s.get(e);return t?[{entityType:e,views:t}]:[]});return{views:new Map(o.map(({entityType:e,views:t})=>[e,t.map(({view:e})=>e)])),metaEntries:new Map(o.flatMap(({entityType:e,views:[t]})=>t?[[e,t.entryCtx]]:[]))}})(e.screenshots??{},{model:t,components:s,states:r},N.at("screenshots"));var J=[Y.metaEntries,x.metaEntries,new Map([...G].flatMap(([e,[t]])=>t?[[e,t.entryCtx]]:[])),B.metaEntries];for(let e of t.entityTypes){let r=J.flatMap(t=>t.get(e)??[])[0],i=D(e);r&&[...t.resolvedShapes.keys()].some(e=>e===i)&&r.error(a.ASSISTANT_META_TYPE_NAME_CONFLICT,l.ASSISTANT_META_TYPE_NAME_CONFLICT(e,i))}for(let[e,r]of B.metaEntries){let i=L(e);[...t.resolvedShapes.keys()].some(e=>e===i)&&r.error(a.ASSISTANT_SCREENSHOT_VIEW_TYPE_NAME_CONFLICT,l.ASSISTANT_SCREENSHOT_VIEW_TYPE_NAME_CONFLICT(e,i))}let X=t.entityTypes.filter(e=>(Y.groups[e]?.length??0)>0||(x.groups[e]?.length??0)>0||(G.get(e)?.length??0)>0||B.views.has(e)).flatMap(e=>[D(e),...B.views.has(e)?[L(e)]:[]]),Z=((e,{actions:t,resolveRef:r,builtInMutations:i,typeNames:n},s)=>{let o=[],S=[...t.values()].map(e=>String(e.name));for(let[p,N]of Object.entries(e)){let e=s.at(p),T=[...t.values()].find(e=>e.name===p);if(!T){e.error(a.ASSISTANT_UNKNOWN_ACTION,l.ASSISTANT_UNKNOWN_ACTION(p,S));continue}let c=N?.as??p,d=o.find(e=>e.fieldName===c),E=U(c);if(i.has(c)||d){e.error(a.ASSISTANT_MUTATION_NAME_CONFLICT,l.ASSISTANT_MUTATION_NAME_CONFLICT(p,c,d?`"assistant.actions.${d.actionName}" already exposes`:"the built-in mutation of that name already takes"));continue}if(n.has(E)||o.some(e=>U(e.fieldName)===E)){e.error(a.ASSISTANT_ACTION_RESULT_TYPE_CONFLICT,l.ASSISTANT_ACTION_RESULT_TYPE_CONFLICT(p,c,E));continue}let f=N?.description??T.description;o.push({fieldName:c,actionName:p,...void 0===f?{}:{description:f},args:Object.fromEntries(Object.entries(T.params).map(([e,{schema:t}])=>[e,$(t,r)])),result:Object.fromEntries(Object.entries(T.result).map(([e,t])=>[e,F(t,r)]))})}return o})(e.actions??{},{actions:p,resolveRef:A,builtInMutations:new Set(["deleteEntity",O.mutation,I.mutation,...z.map(({entityType:e})=>`modify${e}`)]),typeNames:new Set([...h,...t.resolvedShapes.keys(),...X])},N.at("actions")),Q=((e,t)=>{let r=e.lenses??{},i=new Set;for(let[n,s]of Object.entries(e.entities??{}))for(let[e,o]of Object.entries(s))for(let s of o?.lenses??[])Object.hasOwn(r,s)?i.add(s):t.at("entities",n,e,"lenses").error(a.ASSISTANT_UNKNOWN_LENS,l.ASSISTANT_UNKNOWN_LENS(`${n}.${e}`,s,Object.keys(r)));for(let e of Object.keys(r))v.has(e)?t.at("lenses",e).error(a.ASSISTANT_RESERVED_LENS_NAME,l.ASSISTANT_RESERVED_LENS_NAME(e,[...v])):i.has(e)||t.at("lenses",e).error(a.ASSISTANT_EMPTY_LENS,l.ASSISTANT_EMPTY_LENS(e));return Object.fromEntries(Object.entries(r).map(([e,{description:t,maxDepth:r}])=>[e,{description:t,maxDepth:r??1}]))})(e,N),q=c(e.formElements??{},N.at("formElements"));if(N.errors.length>E)return;let ee=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,{hints:j(t.hints),groups:Object.entries(t.groups).map(([e,t])=>({instanceName:e,groupId:t?.as??e,...t?.description===void 0?{}:{description:t.description},dryRuns:t?.allowDryRuns??!0}))}]));return{declarations:{entities:Object.fromEntries(Object.keys(e.entities??{}).map(e=>[e,(_.get(e)??[]).map(e=>({name:e.name,source:e.source,type:e.type,localized:e.localized,describedByManifest:e.describedByManifest,...void 0===e.description?{}:{description:e.description},...void 0===e.valueDescription?{}:{valueDescription:e.valueDescription},...void 0===e.enumValues?{}:{enumValues:e.enumValues},...void 0===e.lenses?{}:{lenses:e.lenses}}))])),enumValues:Object.fromEntries(t.entityTypes.map(e=>[e,Object.fromEntries(Object.entries(t.resolvedShapes.get(e)?.properties??{}).flatMap(([e,t])=>{let r=T(t,A).enumValues;return r?[[e,r]]:[]}))])),deletable:Object.keys(e.deletable??{}),writable:Object.fromEntries(Object.entries(e.writable??{}).map(([e,r])=>[e,Object.fromEntries(Object.entries(r).map(([r,i])=>[r,{defaultOptionsName:i?.options?.as===void 0,members:Object.fromEntries(t.entityTypes.filter(r=>r===e||[...t.aliases].find(([t])=>t===e)?.[1].includes(r)).flatMap(i=>{let n=R(t.resolvedShapes.get(i)?.properties??{},r),s=n&&M(i,{property:r,writableKey:e},n,{model:t,resolveRef:A,exposed:new Map,sources:{}},N.at("writable")),o=G.get(i)?.find(t=>t.entry===`${e}.${r}`);return s?[[i,{field:s,...T(n,A).localized?{exposedDescription:g}:{},...o?{options:{options:o.options,hints:o.hints}}:{}}]]:[]}))}]))])),childRecipes:ee(e.childRecipes??{}),replaceRecipes:ee(e.replaceRecipes??{}),screenshots:Object.fromEntries(Object.entries(e.screenshots??{}).map(([e,t])=>[e,Object.entries(t).map(([e,{description:t,as:r}])=>({view:r??e,instanceName:e,description:t}))]))},entityTypes:t.entityTypes,aliases:Object.fromEntries(t.aliases),exampleType:t.rootEntityTypes[0]??t.entityTypes[0],states:{navigationStates:r.navigationStates,viewStates:r.viewStates,toolStates:r.toolStates},deletableTypes:H,writableTypes:z,actionFields:Z,childRecipes:Object.fromEntries(Object.entries(Y.groups).map(([e,t])=>[e,{groups:t,hints:Y.hints.get(e)??[]}])),replaceRecipes:Object.fromEntries(Object.entries(x.groups).map(([e,t])=>[e,{groups:t,hints:x.hints.get(e)??[]}])),optionsEdges:Object.fromEntries([...G].map(([e,t])=>[e,t.map(({options:e,hints:t})=>({options:e,hints:t}))])),screenshots:Object.fromEntries(B.views),fields:Object.fromEntries([...W].map(([e,t])=>[e,t.map(t=>o(t,_.get(e)??[]))])),lenses:Q,formElements:q}};export const createAssistantSchema=(e,t,n)=>{let s=resolveAssistantManifest(e,t,n);if(s)try{return{...i(s),resolved:s}}catch(e){if(e instanceof r)return void n.at("entities").error(a.ASSISTANT_FIELD_SHAPE_CONFLICT,l.ASSISTANT_FIELD_SHAPE_CONFLICT(e.fieldName,e.exposures));n.error(a.ASSISTANT_INVALID_SCHEMA,l.ASSISTANT_INVALID_SCHEMA([e instanceof Error?e.message:String(e)]));return}};
@@ -1 +1 @@
1
- import{jsx as r,jsxs as e}from"react/jsx-runtime";import{I18nProvider as o}from"@lingui/react";import{QueryClientProvider as i}from"@tanstack/react-query";import n from"mui-modal-provider";import{useEffect as t}from"react";import{standardWebComponentProperties as c}from"#package/domain";import{getAvailableLocales as a,resolveLocale as m}from"#package/i18n";import{ApplicationType as l,ApplicationTypeProvider as d,InvalidDeploymentReload as p,LoadingStateProvider as s,SessionServiceProvider as f,useApplicationQueryClient as u,useI18n as h,useSpecAlwaysExpand as g,useSpecAppLoader as x,useSpecPrefetchTextureUrls as j,useSpecPreview as k,useWebComponentProperty$ as v,useWebComponentPropertySetter as b,useWebComponentPropertyValue as y,useWebComponentReadonlySync as C}from"#package/react-sdk";import{SnackbarProvider as w}from"#package/ui-sdk";import{ConfiguratorDataProvider as E}from"./configurator-context/ConfiguratorDataProvider.js";import{ConfiguratorResponsiveProvider as I}from"./configurator-context/ConfiguratorResponsiveProvider.js";import{usePowerRoutingLifecycle as L}from"./configurator-context/configurator-routing/use-power-routing-lifecycle.js";import{ConfiguratorRoutingProvider as P,OnApplicationStartProvider as Z,OnRoutingReadyProvider as q,OnSessionReadyProvider as A,PresetSelectionProvider as B,PreviewApplicationProvider as D}from"./configurator-context/index.js";import{LoadingErrorBoundary as F}from"./pages/error-boundaries/LoadingErrorBoundary.js";import{GeneralErrorFallback as R,RootPage as S}from"./pages/index.js";import{SceneResourcesProvider as T}from"./scene/index.js";import{EncapsulatedConfiguratorTheme as U}from"./ui/index.js";import{AppLoader as z}from"./ui/phoenix-layout-elements/index.js";export const ConfiguratorApp=()=>{let G=u(),H=h(),J=x(),K=k(),M=g(),N=j(),O=b(c.locale),Q=v(c.locale);C(),t(()=>{let r=Q.subscribe(r=>{let e=m(r,a(H));H.activate(e)}),e=()=>{O(H.locale)};return H.on("change",e),()=>{r.unsubscribe(),H.removeListener("change",e)}},[Q,H,O]);let V=y(c.containerZIndex),W=y(c.power);return(L(),"off"===W)?null:r(d,{type:l.Configurator,children:r(i,{client:G,children:r(U,{children:r(s,{children:r(I,{containerZIndex:V,children:r(z,{mode:J,children:r(D,{alwaysExpand:M,preview:K,children:r(o,{i18n:H,children:r(w,{maxSnack:5,children:r(F,{FallbackComponent:R,children:r(T,{prefetchTextureUrls:N,children:r(Z,{children:r(f,{children:r(A,{children:"on"===W&&r(P,{children:r(E,{children:r(B,{children:r(q,{children:e(n,{children:[r(p,{}),r(S,{})]})})})})})})})})})})})})})})})})})})})};
1
+ import{jsx as r,jsxs as e}from"react/jsx-runtime";import{I18nProvider as o}from"@lingui/react";import{QueryClientProvider as i}from"@tanstack/react-query";import n from"mui-modal-provider";import{useEffect as t}from"react";import{standardWebComponentProperties as c}from"#package/domain";import{getAvailableLocales as a,resolveLocale as m}from"#package/i18n";import{ApplicationType as l,ApplicationTypeProvider as d,InvalidDeploymentReload as p,LoaderHost as s,SessionServiceProvider as f,useApplicationQueryClient as u,useI18n as h,useSpecAlwaysExpand as g,useSpecAppLoader as x,useSpecPrefetchTextureUrls as j,useSpecPreview as k,useWebComponentProperty$ as v,useWebComponentPropertySetter as b,useWebComponentPropertyValue as y,useWebComponentReadonlySync as C}from"#package/react-sdk";import{SnackbarProvider as w}from"#package/ui-sdk";import{ConfiguratorDataProvider as E}from"./configurator-context/ConfiguratorDataProvider.js";import{ConfiguratorResponsiveProvider as I}from"./configurator-context/ConfiguratorResponsiveProvider.js";import{usePowerRoutingLifecycle as L}from"./configurator-context/configurator-routing/use-power-routing-lifecycle.js";import{ConfiguratorRoutingProvider as P,OnApplicationStartProvider as Z,OnRoutingReadyProvider as q,OnSessionReadyProvider as A,PresetSelectionProvider as B,PreviewApplicationProvider as D}from"./configurator-context/index.js";import{LoadingErrorBoundary as F}from"./pages/error-boundaries/LoadingErrorBoundary.js";import{GeneralErrorFallback as R,RootPage as S}from"./pages/index.js";import{SceneResourcesProvider as T}from"./scene/index.js";import{EncapsulatedConfiguratorTheme as U}from"./ui/index.js";import{AppLoader as z}from"./ui/phoenix-layout-elements/index.js";export const ConfiguratorApp=()=>{let G=u(),H=h(),J=x(),K=k(),M=g(),N=j(),O=b(c.locale),Q=v(c.locale);C(),t(()=>{let r=Q.subscribe(r=>{let e=m(r,a(H));H.activate(e)}),e=()=>{O(H.locale)};return H.on("change",e),()=>{r.unsubscribe(),H.removeListener("change",e)}},[Q,H,O]);let V=y(c.containerZIndex),W=y(c.power);return(L(),"off"===W)?null:r(d,{type:l.Configurator,children:r(i,{client:G,children:r(U,{children:r(s,{children:r(I,{containerZIndex:V,children:r(z,{mode:J,children:r(D,{alwaysExpand:M,preview:K,children:r(o,{i18n:H,children:r(w,{maxSnack:5,children:r(F,{FallbackComponent:R,children:r(T,{prefetchTextureUrls:N,children:r(Z,{children:r(f,{children:r(A,{children:"on"===W&&r(P,{children:r(E,{children:r(B,{children:r(q,{children:e(n,{children:[r(p,{}),r(S,{})]})})})})})})})})})})})})})})})})})})})};
@@ -1 +1 @@
1
- import{createSelector as e}from"@salsita-ai/common/utils";import{createSessionAssistant as t,isAssistantSetupHandler as n}from"#package/domain";import{AnimationSystem as s,AssistantSystem as o,CameraControlSystem as r,CameraSystem as a,CanvasSystem as i,DragAndDropSystem as g,ExportSystem as m,GraphQLSystem as c,ImageRenderingSystem as l,LayoutSystem as S,SketchingSystem as u}from"./systems/index.js";export const createConfiguratorExtensionSelectors=y=>{let{getState:C,domainContextSelectors:d,specSelectors:f,domainServicesSelectors:p,accessSelectors:w,commonServicesSelectors:h}=y,x=e.withTypes()([f.selectConfiguratorSpec,d.selectInitializationContext],(e,s)=>t({resolved:e.assistant,handler:e.handlers.find(n),inputs:s&&s.access?{role:s.role,sessionData:s.sessionData,access:s.access}:null})),A={getConfigurationId:()=>w.getConfiguratorConfigurationId(),getAuthorizedClient:()=>h.getConfiguratorAuthorizedClient(),getAssistantSetup:()=>x(C()),domainContextSelectors:d,domainServicesSelectors:p,specSelectors:f},D={getSystems:()=>q,getCanvasSystem:()=>I,getCameraControlSystem:()=>z,getAnimationSystem:()=>v,getDragAndDropSystem:()=>k,getImageRenderingSystem:()=>b,getLayoutSystem:()=>Q,getSketchingSystem:()=>R,getExportSystem:()=>L,getCameraSystem:()=>T,getGraphQLSystem:()=>j,getAssistantSystem:()=>G},E={...A,configuratorExtensionSelectors:D},I=new i(A),z=new r({configuratorExtensionSelectors:D}),k=new g(E),v=new s(A),L=new m(E),j=new c(A),G=new o(E),Q=new S(A),R=new u(E),T=new a(E),b=new l(E),q={cameraControlSystem:z,layoutSystem:Q,sketchingSystem:R,dragAndDropSystem:k,canvasSystem:I,cameraSystem:T,exportSystem:L,imageRenderingSystem:b,animationSystem:v,graphQLSystem:j,assistantSystem:G};return D};
1
+ import{createSelector as e}from"@salsita-ai/common/utils";import{createSessionAssistant as t,isAssistantSetupHandler as n}from"#package/domain";import{AnimationSystem as s,AssistantSystem as o,CameraControlSystem as r,CameraSystem as a,CanvasSystem as i,DragAndDropSystem as g,ExportSystem as m,GraphQLSystem as c,ImageRenderingSystem as l,LayoutSystem as S,SketchingSystem as d}from"./systems/index.js";export const createConfiguratorExtensionSelectors=u=>{let{getState:y,domainContextSelectors:w,specSelectors:C,domainServicesSelectors:f,accessSelectors:p,commonServicesSelectors:h}=u,x=e.withTypes()([C.selectConfiguratorSpec,w.selectInitializationContext],(e,s)=>t({resolved:e.assistant,handler:e.handlers.find(n),inputs:s&&s.access?{role:s.role,sessionData:s.sessionData,access:s.access}:null})),A={getConfigurationId:()=>p.getConfiguratorConfigurationId(),getAuthorizedClient:()=>h.getConfiguratorAuthorizedClient(),getAssistantSetup:()=>x(y()),domainContextSelectors:w,domainServicesSelectors:f,specSelectors:C},I={getSystems:()=>Q,getCanvasSystem:()=>D,getCameraControlSystem:()=>v,getAnimationSystem:()=>b,getDragAndDropSystem:()=>z,getImageRenderingSystem:()=>G,getLayoutSystem:()=>T,getSketchingSystem:()=>j,getExportSystem:()=>k,getCameraSystem:()=>F,getGraphQLSystem:()=>L,getAssistantSystem:()=>R},E={...A,configuratorExtensionSelectors:I},D=new i(A),v=new r({configuratorExtensionSelectors:I}),z=new g(E),b=new s(A),k=new m(E),L=new c({...A,renderScreenshot:async(e,t)=>{let[n]=await I.getImageRenderingSystem().generateImagesFromComponents([e],t,{},{awaitSettledState:!0});if(!n)throw Error("The canvas rendered no image.");return n.blob}}),R=new o(E),T=new S(A),j=new d(E),F=new a(E),G=new l(E),Q={cameraControlSystem:v,layoutSystem:T,sketchingSystem:j,dragAndDropSystem:z,canvasSystem:D,cameraSystem:F,exportSystem:k,imageRenderingSystem:G,animationSystem:b,graphQLSystem:L,assistantSystem:R};return I};