@salsita-ai/sdk 0.50.0-develop.91d65bd507 → 0.50.0-develop.9239ced550

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 (212) hide show
  1. package/README.md +1 -1
  2. package/dist/application/index.d.ts +1 -0
  3. package/dist/application/index.js +1 -1
  4. package/dist/application/mount-application.d.ts +1 -1
  5. package/dist/application/mount-application.js +1 -1
  6. package/dist/application/mount-configurator.d.ts +19 -0
  7. package/dist/application/mount-configurator.js +1 -0
  8. package/dist/application/mounted-application-runtime.d.ts +4 -0
  9. package/dist/application/mounted-application-runtime.js +1 -0
  10. package/dist/application/register-web-component.d.ts +2 -0
  11. package/dist/application/register-web-component.js +1 -0
  12. package/dist/application-spec/core/enhanced-json-value.d.ts +2 -31
  13. package/dist/application-spec/core/enhanced-json-value.js +1 -1
  14. package/dist/application-spec/core/index.d.ts +1 -3
  15. package/dist/application-spec/core/index.js +1 -1
  16. package/dist/application-spec/core/markers/index.d.ts +1 -1
  17. package/dist/application-spec/core/markers/index.js +1 -1
  18. package/dist/application-spec/core/markers/msg-marker.d.ts +2 -28
  19. package/dist/application-spec/core/markers/msg-marker.js +1 -1
  20. package/dist/application-spec/core/markers/raw-eval-write-marker.d.ts +1 -1
  21. package/dist/application-spec/core/markers/raw-eval-write-marker.js +1 -1
  22. package/dist/application-spec/core/markers/raw-read-write-marker.js +1 -1
  23. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  24. package/dist/application-spec/generate-application/code/templates/index.d.ts +2 -6
  25. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  26. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill-bundle.d.ts +10 -0
  27. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill-bundle.js +7 -0
  28. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
  29. package/dist/application-spec/generate-application/implementations/phx-application-types.d.ts +25 -0
  30. package/dist/application-spec/generate-application/implementations/phx-application-types.js +1 -0
  31. package/dist/application-spec/generate-application/implementations/phx-domain.d.ts +36 -0
  32. package/dist/application-spec/generate-application/implementations/phx-domain.js +1 -0
  33. package/dist/application-spec/generate-application/implementations/phx-model.d.ts +24 -0
  34. package/dist/application-spec/generate-application/implementations/phx-model.js +1 -0
  35. package/dist/application-spec/generate-application/implementations/phx.d.ts +22 -0
  36. package/dist/application-spec/generate-application/implementations/phx.js +1 -0
  37. package/dist/application-spec/generate-application/index.d.ts +2 -0
  38. package/dist/application-spec/generate-application/index.js +1 -1
  39. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-model-runtime.d.ts +3 -3
  40. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.d.ts +4 -2
  41. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.js +1 -1
  42. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-spec-runtime.d.ts +16 -0
  43. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-spec-runtime.js +1 -0
  44. package/dist/application-spec/generate-application/runtime/application-runtime.d.ts +5 -0
  45. package/dist/application-spec/generate-application/runtime/application-runtime.js +1 -1
  46. package/dist/application-spec/generate-application/runtime/application-spec-slice.d.ts +71 -0
  47. package/dist/application-spec/generate-application/runtime/application-spec-slice.js +1 -0
  48. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-model-runtime.d.ts +3 -3
  49. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +4 -2
  50. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  51. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-spec-runtime.d.ts +16 -0
  52. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-spec-runtime.js +1 -0
  53. package/dist/application-spec/generate-application/runtime/create-application.d.ts +57 -0
  54. package/dist/application-spec/generate-application/runtime/create-application.js +1 -0
  55. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.js +1 -1
  56. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.d.ts +1 -1
  57. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.js +1 -1
  58. package/dist/application-spec/generate-application/runtime/generate-runtime-instance.d.ts +1 -1
  59. package/dist/application-spec/generate-application/runtime/generate-runtime-instance.js +1 -1
  60. package/dist/application-spec/generate-application/runtime/index.d.ts +8 -0
  61. package/dist/application-spec/generate-application/runtime/index.js +1 -0
  62. package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.js +1 -1
  63. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.d.ts +1 -1
  64. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.js +1 -1
  65. package/dist/application-spec/generate-application/runtime/sections/generate-memo-eval-plan-runtime.d.ts +1 -1
  66. package/dist/application-spec/generate-application/runtime/sections/generate-memo-eval-plan-runtime.js +1 -1
  67. package/dist/application-spec/generate-application/runtime/spec-code/resolve-spec-code.d.ts +1 -1
  68. package/dist/application-spec/generate-application/runtime/spec-runtime.d.ts +21 -0
  69. package/dist/application-spec/generate-application/runtime/spec-runtime.js +1 -0
  70. package/dist/application-spec/generate-application/runtime-spec/encoded-runtime-spec.d.ts +3 -2
  71. package/dist/application-spec/generate-application/runtime-spec/runtime-spec.d.ts +44 -9
  72. package/dist/application-spec/generate-application/runtime-spec/runtime-spec.js +1 -1
  73. package/dist/application-spec/parse-application-files/raw-spec/raw-expressions.d.ts +1 -1
  74. package/dist/application-spec/parse-application-files/raw-spec/raw-expressions.js +1 -1
  75. package/dist/client/authorized-client.d.ts +3 -1
  76. package/dist/client/authorized-client.js +1 -1
  77. package/dist/client/contract.d.ts +17 -0
  78. package/dist/client/index.d.ts +1 -1
  79. package/dist/client/local-authorized-client.d.ts +3 -1
  80. package/dist/client/local-authorized-client.js +1 -1
  81. package/dist/client/memory-authorized-client.d.ts +4 -1
  82. package/dist/client/memory-authorized-client.js +1 -1
  83. package/dist/client/phoenix-api.d.ts +228 -0
  84. package/dist/configurator/ConfiguratorApp.js +1 -1
  85. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  86. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +13 -0
  87. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  88. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +2 -1
  89. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  90. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +15 -9
  91. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  92. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.d.ts +1 -1
  93. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.js +1 -1
  94. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.d.ts +12 -4
  95. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.js +1 -1
  96. package/dist/configurator/ui/assistant/screenshots/use-screenshot-src.d.ts +8 -0
  97. package/dist/configurator/ui/assistant/screenshots/use-screenshot-src.js +1 -0
  98. package/dist/docs/index.json +1 -1
  99. package/dist/docs/layouts/gallery/nightjar-guitar.mdx +31 -16
  100. package/dist/docs/layouts/gallery/tidepool-aquascape.mdx +205 -33
  101. package/dist/docs/phoenix/domain-model.mdx +4 -1
  102. package/dist/docs/phoenix/phoenix-cli.mdx +14 -6
  103. package/dist/docs/phoenix/project-structure.mdx +21 -17
  104. package/dist/docs/phoenix/sdk-builder-workflow.mdx +6 -4
  105. package/dist/domain/application/assistant/build-assistant-manifest.js +1 -1
  106. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
  107. package/dist/domain/application/configurator-builder.d.ts +7 -1
  108. package/dist/domain/application/configurator-builder.js +1 -1
  109. package/dist/domain/application/index.d.ts +1 -1
  110. package/dist/domain/application/index.js +1 -1
  111. package/dist/domain/application/standard-properties.js +1 -1
  112. package/dist/domain/domain-runtime/application.d.ts +39 -0
  113. package/dist/domain/domain-runtime/application.js +1 -0
  114. package/dist/domain/domain-runtime/domain-runtime.d.ts +36 -4
  115. package/dist/domain/domain-runtime/domain-runtime.js +1 -1
  116. package/dist/domain/domain-runtime/environment/initialize.d.ts +1 -1
  117. package/dist/domain/domain-runtime/index.d.ts +1 -0
  118. package/dist/domain/domain-runtime/index.js +1 -1
  119. package/dist/domain/domain-runtime/web-component/actions.d.ts +3 -0
  120. package/dist/domain/domain-runtime/web-component/actions.js +1 -1
  121. package/dist/domain/domain-runtime/web-component/initialize.d.ts +1 -0
  122. package/dist/domain/domain-runtime/web-component/initialize.js +1 -1
  123. package/dist/domain/domain-runtime/web-component/selectors.d.ts +2 -0
  124. package/dist/domain/domain-runtime/web-component/selectors.js +1 -1
  125. package/dist/domain/domain-runtime/web-component/types.d.ts +6 -3
  126. package/dist/domain/index.d.ts +1 -0
  127. package/dist/domain/index.js +1 -1
  128. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +5 -0
  129. package/dist/domain/spec/application/index.d.ts +1 -0
  130. package/dist/domain/spec/application/index.js +1 -0
  131. package/dist/{application-spec/core → domain/spec}/components/component-filter.d.ts +3 -2
  132. package/dist/domain/spec/enhanced-json-value.d.ts +30 -0
  133. package/dist/domain/spec/enhanced-json-value.js +1 -0
  134. package/dist/domain/spec/index.d.ts +5 -0
  135. package/dist/domain/spec/index.js +1 -0
  136. package/dist/domain/spec/markers/index.d.ts +2 -0
  137. package/dist/domain/spec/markers/index.js +1 -0
  138. package/dist/domain/spec/markers/msg-marker.d.ts +28 -0
  139. package/dist/domain/spec/markers/msg-marker.js +1 -0
  140. package/dist/editor/domain-editors/DomainSpecContext.js +1 -1
  141. package/dist/editor/domain-editors/apply-spec-update.d.ts +2 -1
  142. package/dist/editor/domain-editors/apply-spec-update.js +1 -1
  143. package/dist/editor/domain-editors/use-implementation-files.d.ts +0 -3
  144. package/dist/editor/domain-editors/use-implementation-files.js +1 -1
  145. package/dist/phoenix/application-spec/generate-application-spec-project.js +2 -2
  146. package/dist/phoenix/application-spec/resolve-deployment-data.d.ts +11 -0
  147. package/dist/phoenix/application-spec/resolve-deployment-data.js +2 -2
  148. package/dist/phoenix/bin.js +1 -1
  149. package/dist/phoenix/cli.js +1 -1
  150. package/dist/phoenix/commands/deploy.js +1 -1
  151. package/dist/phoenix/commands/dev.js +5 -5
  152. package/dist/phoenix/commands/generated-env-markers.d.ts +1 -0
  153. package/dist/phoenix/commands/generated-env-markers.js +1 -1
  154. package/dist/phoenix/commands/index.d.ts +1 -1
  155. package/dist/phoenix/commands/index.js +1 -1
  156. package/dist/phoenix/lingui/index.d.ts +1 -0
  157. package/dist/phoenix/lingui/index.js +1 -0
  158. package/dist/phoenix/{services → lingui}/lingui.d.ts +5 -2
  159. package/dist/phoenix/lingui/lingui.js +1 -0
  160. package/dist/phoenix/services/application-spec-project-watcher.d.ts +3 -1
  161. package/dist/phoenix/services/application-spec-project-watcher.js +2 -2
  162. package/dist/phoenix/services/index.d.ts +1 -0
  163. package/dist/phoenix/services/index.js +1 -1
  164. package/dist/phoenix/services/lingui-watcher.js +1 -1
  165. package/dist/phoenix/services/prepare-development-environment.js +1 -1
  166. package/dist/phoenix/services/vite-config.js +1 -1
  167. package/dist/react-sdk/application-runtime/common-services/analytics/analytics-service.d.ts +1 -1
  168. package/dist/react-sdk/application-runtime/common-services/initialize.d.ts +2 -2
  169. package/dist/react-sdk/application-runtime/hooks/use-access-service.js +1 -1
  170. package/dist/react-sdk/application-runtime/hooks/use-web-component-readonly-sync.d.ts +4 -11
  171. package/dist/react-sdk/application-runtime/hooks/use-web-component-readonly-sync.js +1 -1
  172. package/dist/react-sdk/application-runtime/hooks/use-web-component.d.ts +5 -0
  173. package/dist/react-sdk/application-runtime/hooks/use-web-component.js +1 -1
  174. package/dist/react-sdk/application-runtime/host-properties/actions.d.ts +15 -0
  175. package/dist/react-sdk/application-runtime/host-properties/actions.js +1 -0
  176. package/dist/react-sdk/application-runtime/host-properties/connect-host-properties.d.ts +30 -0
  177. package/dist/react-sdk/application-runtime/host-properties/connect-host-properties.js +1 -0
  178. package/dist/react-sdk/application-runtime/host-properties/domain-host-properties.d.ts +7 -0
  179. package/dist/react-sdk/application-runtime/host-properties/domain-host-properties.js +1 -0
  180. package/dist/react-sdk/application-runtime/host-properties/host-property-descriptors.d.ts +21 -0
  181. package/dist/react-sdk/application-runtime/host-properties/host-property-descriptors.js +1 -0
  182. package/dist/react-sdk/application-runtime/host-properties/host-property-scopes.d.ts +42 -0
  183. package/dist/react-sdk/application-runtime/host-properties/host-property-scopes.js +1 -0
  184. package/dist/react-sdk/application-runtime/host-properties/index.d.ts +8 -0
  185. package/dist/react-sdk/application-runtime/host-properties/index.js +1 -0
  186. package/dist/react-sdk/application-runtime/host-properties/initialize.d.ts +7 -0
  187. package/dist/react-sdk/application-runtime/host-properties/initialize.js +1 -0
  188. package/dist/react-sdk/application-runtime/host-properties/selectors.d.ts +54 -0
  189. package/dist/react-sdk/application-runtime/host-properties/selectors.js +1 -0
  190. package/dist/react-sdk/application-runtime/host-properties/types.d.ts +18 -0
  191. package/dist/react-sdk/application-runtime/host-properties/types.js +1 -0
  192. package/dist/react-sdk/application-runtime/index.d.ts +1 -0
  193. package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.js +1 -1
  194. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +151 -9
  195. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.js +1 -1
  196. package/package.json +3 -3
  197. package/dist/phoenix/services/lingui.js +0 -1
  198. /package/dist/{application-spec/core → domain/spec/application}/application-instance.d.ts +0 -0
  199. /package/dist/{application-spec/core → domain/spec/application}/application-instance.js +0 -0
  200. /package/dist/{application-spec/core → domain/spec}/components/component-filter.js +0 -0
  201. /package/dist/{application-spec/core → domain/spec}/components/index.d.ts +0 -0
  202. /package/dist/{application-spec/core → domain/spec}/components/index.js +0 -0
  203. /package/dist/{application-spec/core → domain/spec}/components/sdk-code-ref.d.ts +0 -0
  204. /package/dist/{application-spec/core → domain/spec}/components/sdk-code-ref.js +0 -0
  205. /package/dist/{application-spec/core → domain/spec}/expressions/index.d.ts +0 -0
  206. /package/dist/{application-spec/core → domain/spec}/expressions/index.js +0 -0
  207. /package/dist/{application-spec/core → domain/spec}/expressions/relation-expression-params.d.ts +0 -0
  208. /package/dist/{application-spec/core → domain/spec}/expressions/relation-expression-params.js +0 -0
  209. /package/dist/{application-spec/core → domain/spec}/expressions/sdk-expression-implementation.d.ts +0 -0
  210. /package/dist/{application-spec/core → domain/spec}/expressions/sdk-expression-implementation.js +0 -0
  211. /package/dist/{application-spec/core → domain/spec}/markers/eval-marker.d.ts +0 -0
  212. /package/dist/{application-spec/core → domain/spec}/markers/eval-marker.js +0 -0
@@ -1 +1 @@
1
- import{GraphQLError as e,NoSchemaIntrospectionCustomRule as t,defaultFieldResolver as i,execute as n,getNamedType as r,parse as o,specifiedRules as s,validate as a,validateSchema as l}from"graphql";import{assertExistence as c,createSelector as h,isMessageDescriptor as d,isMultilingualString as u}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as f,agentScreenshotComponent as m,createAssistantExecutionSchema as p,findAssistantMutation as g}from"#package/domain";import{ExportBusyError as y}from"../export-system/export-busy-error.js";import{closeUnclosedBraces as v}from"./close-unclosed-braces.js";import{createEntityMeta as S}from"./entity-meta.js";import{injectAssistantLenses as w}from"./inject-assistant-lenses.js";import{readSideEffects as _}from"./side-effects.js";let x={errors:[new e("The configurator does not provide this request. This is a fault in the configurator, not in your request. Tell the user this is not available.")]},$={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},b="Not taken: another configuration was loaded while this request ran. Query the current configuration before asking for screenshots again.",E="Not taken: the screenshots of this tool call have used up its image size budget. Ask for this screenshot in another call, and for fewer in one call.",T=async e=>{let t=new Uint8Array(await e.arrayBuffer()),i="";for(let e=0;e<t.length;e+=32768)i+=String.fromCharCode(...t.subarray(e,e+32768));return btoa(i)},C=new RegExp(f.pattern),M=[...s,t],k=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,j=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,R=e=>e instanceof Error?e.message:String(e),A=e=>/[.!?]$/.test(e)?e:`${e}.`,N=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,z=(e,t)=>{let i=N(e)?e._raw:void 0;if(k(i))return i;if(!Array.isArray(e)&&!N(e))return e;if(t.has(e))throw Error("The value contains itself.");let n=new Set([...t,e]);return Array.isArray(e)?e.map(e=>z(e,n)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,z(t,n)]))};export const localize=(e,t)=>Array.isArray(e)?e.map(e=>localize(e,t)):"string"==typeof e||d(e)||u(e)?t(e):e;let q=t=>new e(`The configurator cannot handle this request: ${A(t)} This is a fault in the configurator, not in your request. Tell the user this is not available.`),L="SCREENSHOT_NOT_TAKEN",F=(t,i)=>new e(t,{originalError:i,extensions:{code:L}}),O=e=>(e.errors??[]).some(e=>e.extensions.code!==L),V=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=h([h.leaf(e=>e)],e=>{try{let t=p(e),i=l(t);return i.length>0?q(i.map(e=>e.message).join(" ")):t}catch(e){return q(R(e))}});_selectLenses=h([h.leaf(e=>e)],e=>o(e));_selectFieldResolver=h([h.leaf(e=>e)],t=>(n,o,s,a)=>{if(a.parentType===a.schema.getQueryType())return i(n,o,s,a);if(a.parentType===a.schema.getMutationType())return this._resolveMutation(a.fieldName,String(a.path.key),o,s);if(!k(n))return i(n,o,s,a);if("meta"===a.fieldName){let e;return S({entity:n,state:("object"==typeof(e=j(n,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??s.state},{manifest:s.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e),takeScreenshot:(e,t)=>this._takeScreenshot(e,t,s)})}let{source:l,localized:c}=t[a.parentType.name]?.[a.fieldName]??{source:a.fieldName},h=((t,i)=>{try{return j(t,i)}catch(n){throw new e(`Expression "${i}" failed on ${k(t)?t.id:"an entity"}: ${A(R(n))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:n instanceof Error?n:void 0})}})(n,l);return c?localize(h,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===r(a.returnType).name?((t,i,n)=>{try{return z(n,new Set)}catch(n){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${k(t)?t.id:"an entity"}: ${A(R(n))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:n instanceof Error?n:void 0})}})(n,l,h):h});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var n=i.rootEntityId;let r=t.filter(e=>!C.test(e));if(r.length>0)throw new e(`${r.map(e=>`"${e}"`).join(", ")} ${1===r.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${n}". Use ids as returned by the "id" field.`);return t.map(e=>C.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>k(e)?e.type:void 0;_generation={active:!0};_renderScreenshot=async(e,t,i)=>{let n=this._context.domainContextSelectors.getComponentReader().readOneComponent({entityId:e,componentType:m,instanceName:t},i);if(!n)return null;let r=await this._context.renderScreenshot(n,i);return null===r?null:{data:await T(r),mimeType:"JPEG"===n.state.fileFormat?"image/jpeg":"image/png"}};initialize=()=>{this._generation.active=!1,this._generation={active:!0}};_resolveMutation=(t,i,n,r)=>{if(void 0!==r.failedMutationField)throw new e(`Not run: the earlier field "${r.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,n,r)}catch(e){throw r.failedMutationField=i,e}};_runMutation=(t,i,n)=>{let r=g(t,n.manifest);if(!r)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. This is a fault in the configurator, not in your request. Tell the user this is not available.`);let o=n.state,s=r.implementation(i,o,{manifest:n.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(o),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(s.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(s.updates,o,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${A(R(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);n.emissions.push(i),n.state=i.state}return s.readResult({previousState:o,state:n.state})};_takeScreenshot=({entity:t,state:i},n,r)=>{let o=r.manifest.screenshots?.[t.type]?.find(e=>e.view===n);if(!o)return Promise.reject(new e(`The schema declares the screenshot view "${n}" on ${t.type}, but the configurator has no view of that name. This is a fault in the configurator, not in your request. Tell the user this is not available.`));let s=r.lastScreenshot.then(async()=>{if(!r.generation.active)throw F(b);if(r.screenshotBudget<=0)throw F(E);let e=await this._renderScreenshot(t.id,o.instanceName,i).catch(e=>{throw F(e instanceof y?"User made concurrent actions that prevented the screenshot from being taken, try again":`The screenshot of ${t.id} from "${n}" could not be taken: ${A(R(e))} Try again; if it fails again, tell the user the screenshot is not available.`,e instanceof Error?e:void 0)});if(!r.generation.active)throw F(b);if(null===e)return null;if(e.data.length>r.screenshotBudget)throw r.screenshotBudget=0,F(E);r.screenshotBudget-=e.data.length;let s=`${t.id}/${n}`,a=[...r.screenshots.values()].filter(e=>e.entityId===t.id&&e.view===n).length,l=0===a?s:`${s}#${a+1}`;return r.screenshots.set(l,{entityId:t.id,view:n,...e}),{image:l}});return r.lastScreenshot=s.catch(()=>void 0),s};_setup=()=>{let{manifest:t,error:i}=this._context.getAssistantSetup();if(i)return{ready:!1,result:{errors:[new e(i.message,{originalError:i})]}};if(!t)return{ready:!1,result:x};let n=this._selectSchema(t.sdl);return n instanceof e?{ready:!1,result:{errors:[n]}}:{ready:!0,assistant:t,schema:n,lenses:t.lenses?.definitions?this._selectLenses(t.lenses.definitions):void 0,fieldResolver:this._selectFieldResolver(t.fields)}};_run=(t,i,r,s)=>{let l;try{l=o(t)}catch(t){return{errors:[t instanceof e?t:new e(R(t))]}}let c=a(r.schema,l,M);return c.length>0?{errors:c}:n({schema:r.schema,document:l,rootValue:this._rootValue,contextValue:s,variableValues:i.variables,operationName:i.operationName,fieldResolver:r.fieldResolver,typeResolver:this._resolveType})};prepare=async(t,i,n=this._generation)=>{let r,o=this._setup();if(!o.ready)return i.map(()=>({result:o.result,emissions:[],screenshots:new Map}));let s={manifest:o.assistant,state:t,emissions:[],failedMutationField:void 0,screenshots:new Map,screenshotBudget:14e5,lastScreenshot:Promise.resolve(),generation:n},a=[];for(let[t,n]of i.entries())if(void 0===r){let i=s.emissions.length,l=s.screenshots.size;s.failedMutationField=void 0;let c=v(n.document),h=o.lenses?w(c,o.lenses):c,d=h instanceof e?{errors:[h]}:await this._run(h,n,o,s);await s.lastScreenshot,a.push({result:d,emissions:s.emissions.slice(i),screenshots:new Map([...s.screenshots].slice(l))}),O(d)&&(r=t)}else a.push({result:V(r),emissions:[],screenshots:new Map});return a};readSideEffects=e=>{let t=this._context.domainServicesSelectors.getResolveLocalizable();return e.flatMap(({emissions:e})=>e.flatMap(e=>_(e.updates,t)))};readScreenshots=e=>new Map(e.flatMap(({screenshots:e})=>[...e]));_prepareAndCommit=async(e,t)=>{let i,n,r,o=this._context.domainContextSelectors.getStoreSystem(),s=await this.prepare(o.stateSlices.committed.value.state,e,t),a=s.map(({result:e})=>e),l=this.readSideEffects(s),h=this.readScreenshots(s),d=s.flatMap(e=>e.emissions);if(!t.active)return{results:a,sideEffects:l,screenshots:h,outcome:"conflict"};if(a.some(O)||0===d.length)return{results:a,sideEffects:l,screenshots:h,outcome:"none"};let u=(i=c(d[0]),n=c(d[d.length-1]),r=d.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:i.previousState,state:n.state,updates:d.flatMap(e=>e.updates),validity:n.validity,transformationLog:r?d.flatMap(e=>e.transformationLog??[]):void 0});return{results:a,sideEffects:l,screenshots:h,outcome:o.commitPrepared(u),preparedState:u.state}};execute=async e=>{let t=this._generation;if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>$),committed:!1,sideEffects:[],screenshots:new Map};let i=await this._prepareAndCommit(e,t),{results:n,sideEffects:r,screenshots:o,outcome:s,preparedState:a}="conflict"===i.outcome&&t.active?await this._prepareAndCommit(e,t):i;return"committed"===s?{results:n,committed:!0,sideEffects:r,screenshots:o,preparedState:a}:"held"===s?{results:n,committed:!1,message:"The changes wait for the user's approval and are not applied yet. Do not send them again; the next `[changes]` block lists them once approved.",sideEffects:r,screenshots:o,preparedState:a}:"conflict"===s?{results:n,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense.",sideEffects:r,screenshots:o}:{results:n,committed:!1,sideEffects:r,screenshots:o}}}
1
+ import{GraphQLError as e,NoSchemaIntrospectionCustomRule as t,defaultFieldResolver as i,execute as n,getNamedType as o,parse as r,specifiedRules as s,validate as a,validateSchema as l}from"graphql";import{assertExistence as c,createSelector as h,isMessageDescriptor as d,isMultilingualString as u}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as f,agentScreenshotComponent as m,createAssistantExecutionSchema as p,findAssistantMutation as g}from"#package/domain";import{ExportBusyError as y}from"../export-system/export-busy-error.js";import{closeUnclosedBraces as v}from"./close-unclosed-braces.js";import{createEntityMeta as S}from"./entity-meta.js";import{injectAssistantLenses as _}from"./inject-assistant-lenses.js";import{readSideEffects as w}from"./side-effects.js";let x={errors:[new e("The configurator does not provide this request. This is a fault in the configurator, not in your request. Tell the user this is not available.")]},$={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},b="Not taken: another configuration was loaded while this request ran. Query the current configuration before asking for screenshots again.",E=new RegExp(f.pattern),T=[...s,t],C=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,M=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,j=e=>e instanceof Error?e.message:String(e),k=e=>/[.!?]$/.test(e)?e:`${e}.`,R=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,N=(e,t)=>{let i=R(e)?e._raw:void 0;if(C(i))return i;if(!Array.isArray(e)&&!R(e))return e;if(t.has(e))throw Error("The value contains itself.");let n=new Set([...t,e]);return Array.isArray(e)?e.map(e=>N(e,n)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,N(t,n)]))};export const localize=(e,t)=>Array.isArray(e)?e.map(e=>localize(e,t)):"string"==typeof e||d(e)||u(e)?t(e):e;let A=t=>new e(`The configurator cannot handle this request: ${k(t)} This is a fault in the configurator, not in your request. Tell the user this is not available.`),q="SCREENSHOT_NOT_TAKEN",z=(t,i)=>new e(t,{originalError:i,extensions:{code:q}}),L=e=>(e.errors??[]).some(e=>e.extensions.code!==q),F=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=h([h.leaf(e=>e)],e=>{try{let t=p(e),i=l(t);return i.length>0?A(i.map(e=>e.message).join(" ")):t}catch(e){return A(j(e))}});_selectLenses=h([h.leaf(e=>e)],e=>r(e));_selectFieldResolver=h([h.leaf(e=>e)],t=>(n,r,s,a)=>{if(a.parentType===a.schema.getQueryType())return i(n,r,s,a);if(a.parentType===a.schema.getMutationType())return this._resolveMutation(a.fieldName,String(a.path.key),r,s);if(!C(n))return i(n,r,s,a);if("meta"===a.fieldName){let e;return S({entity:n,state:("object"==typeof(e=M(n,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??s.state},{manifest:s.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e),takeScreenshot:(e,t)=>this._takeScreenshot(e,t,s)})}let{source:l,localized:c}=t[a.parentType.name]?.[a.fieldName]??{source:a.fieldName},h=((t,i)=>{try{return M(t,i)}catch(n){throw new e(`Expression "${i}" failed on ${C(t)?t.id:"an entity"}: ${k(j(n))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:n instanceof Error?n:void 0})}})(n,l);return c?localize(h,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===o(a.returnType).name?((t,i,n)=>{try{return N(n,new Set)}catch(n){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${C(t)?t.id:"an entity"}: ${k(j(n))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:n instanceof Error?n:void 0})}})(n,l,h):h});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var n=i.rootEntityId;let o=t.filter(e=>!E.test(e));if(o.length>0)throw new e(`${o.map(e=>`"${e}"`).join(", ")} ${1===o.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${n}". Use ids as returned by the "id" field.`);return t.map(e=>E.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>C(e)?e.type:void 0;_generation={active:!0};_renderScreenshot=async(e,t,i)=>{let n=this._context.domainContextSelectors.getComponentReader().readOneComponent({entityId:e,componentType:m,instanceName:t},i);if(!n)return null;let o=await this._context.renderScreenshot(n,i);return null===o?null:new Blob([o],{type:"JPEG"===n.state.fileFormat?"image/jpeg":"image/png"})};initialize=()=>{this._generation.active=!1,this._generation={active:!0}};_resolveMutation=(t,i,n,o)=>{if(void 0!==o.failedMutationField)throw new e(`Not run: the earlier field "${o.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,n,o)}catch(e){throw o.failedMutationField=i,e}};_runMutation=(t,i,n)=>{let o=g(t,n.manifest);if(!o)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. This is a fault in the configurator, not in your request. Tell the user this is not available.`);let r=n.state,s=o.implementation(i,r,{manifest:n.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(r),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(s.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(s.updates,r,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${k(j(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);n.emissions.push(i),n.state=i.state}return s.readResult({previousState:r,state:n.state})};_takeScreenshot=({entity:t,state:i},n,o)=>{let r=o.manifest.screenshots?.[t.type]?.find(e=>e.view===n);if(!r)return Promise.reject(new e(`The schema declares the screenshot view "${n}" on ${t.type}, but the configurator has no view of that name. This is a fault in the configurator, not in your request. Tell the user this is not available.`));let s=o.lastScreenshot.then(async()=>{if(!o.generation.active)throw z(b);let e=await this._renderScreenshot(t.id,r.instanceName,i).catch(e=>{throw z(e instanceof y?"User made concurrent actions that prevented the screenshot from being taken, try again":`The screenshot of ${t.id} from "${n}" could not be taken: ${k(j(e))} Try again; if it fails again, tell the user the screenshot is not available.`,e instanceof Error?e:void 0)});if(!o.generation.active)throw z(b);if(null===e)return null;let{imageId:s}=await this._context.uploadScreenshot(e).catch(e=>{throw z(`The screenshot of ${t.id} from "${n}" was taken but could not be stored: ${k(j(e))} Try again; if it fails again, tell the user the screenshot is not available.`,e instanceof Error?e:void 0)}),a=`${t.id}/${n}`,l=[...o.screenshots.values()].filter(e=>e.entityId===t.id&&e.view===n).length,c=0===l?a:`${a}#${l+1}`;return o.screenshots.set(c,{entityId:t.id,view:n,imageId:s}),{image:c}});return o.lastScreenshot=s.catch(()=>void 0),s};_setup=()=>{let{manifest:t,error:i}=this._context.getAssistantSetup();if(i)return{ready:!1,result:{errors:[new e(i.message,{originalError:i})]}};if(!t)return{ready:!1,result:x};let n=this._selectSchema(t.sdl);return n instanceof e?{ready:!1,result:{errors:[n]}}:{ready:!0,assistant:t,schema:n,lenses:t.lenses?.definitions?this._selectLenses(t.lenses.definitions):void 0,fieldResolver:this._selectFieldResolver(t.fields)}};_run=(t,i,o,s)=>{let l;try{l=r(t)}catch(t){return{errors:[t instanceof e?t:new e(j(t))]}}let c=a(o.schema,l,T);return c.length>0?{errors:c}:n({schema:o.schema,document:l,rootValue:this._rootValue,contextValue:s,variableValues:i.variables,operationName:i.operationName,fieldResolver:o.fieldResolver,typeResolver:this._resolveType})};prepare=async(t,i,n=this._generation)=>{let o,r=this._setup();if(!r.ready)return i.map(()=>({result:r.result,emissions:[],screenshots:new Map}));let s={manifest:r.assistant,state:t,emissions:[],failedMutationField:void 0,screenshots:new Map,lastScreenshot:Promise.resolve(),generation:n},a=[];for(let[t,n]of i.entries())if(void 0===o){let i=s.emissions.length,l=s.screenshots.size;s.failedMutationField=void 0;let c=v(n.document),h=r.lenses?_(c,r.lenses):c,d=h instanceof e?{errors:[h]}:await this._run(h,n,r,s);await s.lastScreenshot,a.push({result:d,emissions:s.emissions.slice(i),screenshots:new Map([...s.screenshots].slice(l))}),L(d)&&(o=t)}else a.push({result:F(o),emissions:[],screenshots:new Map});return a};readSideEffects=e=>{let t=this._context.domainServicesSelectors.getResolveLocalizable();return e.flatMap(({emissions:e})=>e.flatMap(e=>w(e.updates,t)))};readScreenshots=e=>new Map(e.flatMap(({screenshots:e})=>[...e]));_prepareAndCommit=async(e,t)=>{let i,n,o,r=this._context.domainContextSelectors.getStoreSystem(),s=await this.prepare(r.stateSlices.committed.value.state,e,t),a=s.map(({result:e})=>e),l=this.readSideEffects(s),h=this.readScreenshots(s),d=s.flatMap(e=>e.emissions);if(!t.active)return{results:a,sideEffects:l,screenshots:h,outcome:"conflict"};if(a.some(L)||0===d.length)return{results:a,sideEffects:l,screenshots:h,outcome:"none"};let u=(i=c(d[0]),n=c(d[d.length-1]),o=d.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:i.previousState,state:n.state,updates:d.flatMap(e=>e.updates),validity:n.validity,transformationLog:o?d.flatMap(e=>e.transformationLog??[]):void 0});return{results:a,sideEffects:l,screenshots:h,outcome:r.commitPrepared(u),preparedState:u.state}};execute=async e=>{let t=this._generation;if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>$),committed:!1,sideEffects:[],screenshots:new Map};let i=await this._prepareAndCommit(e,t),{results:n,sideEffects:o,screenshots:r,outcome:s,preparedState:a}="conflict"===i.outcome&&t.active?await this._prepareAndCommit(e,t):i;return"committed"===s?{results:n,committed:!0,sideEffects:o,screenshots:r,preparedState:a}:"held"===s?{results:n,committed:!1,message:"The changes wait for the user's approval and are not applied yet. Do not send them again; the next `[changes]` block lists them once approved.",sideEffects:o,screenshots:r,preparedState:a}:"conflict"===s?{results:n,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense.",sideEffects:o,screenshots:r}:{results:n,committed:!1,sideEffects:o,screenshots:r}}}
@@ -4,6 +4,6 @@ interface ScreenshotThumbnailsProps {
4
4
  /** Called once a picture has loaded, which changes the size of the popover. */
5
5
  readonly onImageLoad: () => void;
6
6
  }
7
- /** The pictures of a tool call's screenshots, each captioned by its view and entity id. */
7
+ /** The pictures of a tool call's screenshots. */
8
8
  export declare const ScreenshotThumbnails: ({ screenshots, onImageLoad }: ScreenshotThumbnailsProps) => import("react/jsx-runtime").JSX.Element;
9
9
  export {};
@@ -1 +1 @@
1
- import{jsx as e,jsxs as i}from"react/jsx-runtime";import{styled as r}from"@mui/material";import{AssistantPanelName as o}from"../theme.js";let a=r("span",{name:o,slot:"screenshots"})(({theme:{palette:e,shape:i,shadows:r,spacing:o,typography:a}})=>({...a.caption,background:e.background.paper,borderRadius:i.borderRadius,boxShadow:r[4],color:e.text.secondary,display:"grid",gap:o(1.5),gridTemplateColumns:"repeat(auto-fill, minmax(140px, 1fr))",inlineSize:"min(80vw, 480px)",padding:o(1.5)})),n=r("span")(({theme:{spacing:e}})=>({display:"flex",flexDirection:"column",gap:e(.5),minInlineSize:0})),t=r("img")(({theme:{palette:e,shape:i}})=>({blockSize:"auto",borderRadius:i.borderRadius/2,boxShadow:`inset 0 0 0 1px ${e.divider}`,inlineSize:"100%"})),s=r("span")({overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"});export const ScreenshotThumbnails=({screenshots:r,onImageLoad:o})=>e(a,{children:r.map(({label:r,entityId:a,view:d,src:l})=>i(n,{children:[e(t,{src:l,alt:"",onLoad:o}),i(s,{title:r,children:[d," \xb7 ",a]})]},r))});
1
+ import{jsx as e}from"react/jsx-runtime";import{styled as o}from"@mui/material";import{AssistantPanelName as r}from"../theme.js";import{useScreenshotSrc as a}from"./use-screenshot-src.js";let i=o("span",{name:r,slot:"screenshots",shouldForwardProp:e=>"columns"!==e})(({columns:e,theme:{palette:o,shape:r,shadows:a,spacing:i}})=>({background:o.background.paper,borderRadius:r.borderRadius,boxShadow:a[4],display:"grid",gap:i(1.5),gridTemplateColumns:`repeat(${e}, minmax(0, 200px))`,maxInlineSize:"80vw",padding:i(1.5)})),s=o("img")(({theme:{palette:e,shape:o}})=>({blockSize:"auto",borderRadius:o.borderRadius/2,boxShadow:`inset 0 0 0 1px ${e.divider}`,display:"block",inlineSize:"100%"})),d=o("span")(({theme:{palette:e,shape:o}})=>({aspectRatio:"4 / 3",background:e.action.hover,borderRadius:o.borderRadius/2,display:"block"})),t=({picture:o,onImageLoad:r})=>{let i=a(o);return void 0===i?e(d,{}):e(s,{src:i,alt:"",onLoad:r})};export const ScreenshotThumbnails=({screenshots:o,onImageLoad:r})=>e(i,{columns:Math.min(o.length,2),children:o.map(({label:o,picture:a})=>e(t,{picture:a,onImageLoad:r},o))});
@@ -1,11 +1,19 @@
1
+ /**
2
+ * Where the panel gets a screenshot's picture from: the server, by the id the browser uploaded it
3
+ * under, or the data URL of a picture a thread from before the upload carries inline.
4
+ */
5
+ export type AssistantScreenshotPicture = {
6
+ readonly kind: 'stored';
7
+ readonly imageId: string;
8
+ } | {
9
+ readonly kind: 'inline';
10
+ readonly src: string;
11
+ };
1
12
  /** A screenshot a `query` or `apply` call took, as the panel shows it. */
2
13
  export interface AssistantToolScreenshot {
3
14
  /** The label the call's `image` field returned, such as `Cabinet-2/doorLayout#2`. */
4
15
  readonly label: string;
5
- readonly entityId: string;
6
- readonly view: string;
7
- /** The picture as a data URL. */
8
- readonly src: string;
16
+ readonly picture: AssistantScreenshotPicture;
9
17
  }
10
18
  /**
11
19
  * The screenshots of a tool result: the image parts that follow the result's JSON, each after the
@@ -1 +1 @@
1
- import{z as e}from"zod";let t=e.object({type:e.literal("text"),content:e.string()}),a=e.object({type:e.literal("image"),source:e.object({type:e.literal("data"),value:e.string(),mimeType:e.string()})}),s=/^([^/]+)\/([^#]+)(?:#\d+)?$/;export const readToolScreenshots=e=>Array.isArray(e)?e.slice(1).flatMap((e,r,c)=>{let l=a.safeParse(e),o=t.safeParse(c[r-1]),n=o.success?s.exec(o.data.content):null;if(!l.success||!o.success||!n?.[1]||!n[2])return[];let{value:i,mimeType:d}=l.data.source;return[{label:o.data.content,entityId:n[1],view:n[2],src:`data:${d};base64,${i}`}]}):[];
1
+ import{z as t}from"zod";import{readAssistantImageReference as e}from"@salsita-ai/contracts/assistant";let a=t.object({type:t.literal("text"),content:t.string()}),r=t.object({type:t.literal("image"),source:t.discriminatedUnion("type",[t.object({type:t.literal("url"),value:t.string()}),t.object({type:t.literal("data"),value:t.string(),mimeType:t.string()})])}),s=/^[^/]+\/[^#]+(?:#\d+)?$/;export const readToolScreenshots=t=>Array.isArray(t)?t.slice(1).flatMap((t,l,i)=>{let n=r.safeParse(t),o=a.safeParse(i[l-1]),c=n.success?(({source:t})=>{if("data"===t.type)return{kind:"inline",src:`data:${t.mimeType};base64,${t.value}`};let a=e(t.value);return null===a?null:{kind:"stored",imageId:a}})(n.data):null;return c&&o.success&&s.test(o.data.content)?[{label:o.data.content,picture:c}]:[]}):[];
@@ -0,0 +1,8 @@
1
+ import type { AssistantScreenshotPicture } from './tool-screenshots.js';
2
+ /**
3
+ * The `src` of a screenshot's picture: the data URL of an inline one at once, and for an uploaded
4
+ * one the object URL the assistant system reads it into, once it has been read; `undefined` until
5
+ * then and when the read fails. Only the open popover of a call's screenshots calls it, so nothing
6
+ * is read while the popover is closed.
7
+ */
8
+ export declare const useScreenshotSrc: (picture: AssistantScreenshotPicture) => string | undefined;
@@ -0,0 +1 @@
1
+ import{useEffect as r,useState as e}from"react";import{useConfigurator as n}from"../../../application-runtime/index.js";export const useScreenshotSrc=t=>{let{assistantSystem:i}=n(),o="stored"===t.kind?t.imageId:null,[l,d]=e();return(r(()=>{if(null===o)return;let r=!0;return i.loadScreenshotUrl(o).then(e=>{r&&d({imageId:o,src:e})},()=>void 0),()=>{r=!1}},[i,o]),"inline"===t.kind)?t.src:l?.imageId===o?l.src:void 0};
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-10-17T12:35:03.296Z",
2
+ "generatedAt": "2026-10-19T07:12:48.010Z",
3
3
  "categories": [
4
4
  {
5
5
  "name": "Phoenix",
@@ -80,7 +80,7 @@ components:
80
80
 
81
81
  `phx.frontend.useStepNavigation()` reads them and returns every step with its label resolved and its `status`, `'complete'`, `'active'` or `'upcoming'`, with `next()`, `previous()` and `goTo(index)` to move. `StepHeader` draws the stepper from `steps`, and `OrderBar` the buttons from `canGoPrevious`, `isLast` and the label of the step after the active one:
82
82
 
83
- - **A wizard that remembers.** The hook enforces no order: `goTo` enters any step. `StepHeader` lets the customer go back to any step they have reached and never ahead of it, so after changing the body from the summary they return to the summary in one click. The furthest step is the layout's own state.
83
+ - **A wizard that remembers.** The hook enforces no order: `goTo` enters any step. `StepHeader` lets the customer go back to any step they have reached and never ahead of it, so after changing the body from the summary they return to the summary in one click. The furthest step is the layout's own state, kept in `ConfiguratorLayout` with `useReachedStep()` so it survives the switch between the desktop and the phone page.
84
84
  - **Analytics come with the moves.** `next()` and `previous()` record the built-in step buttons' events and `goTo()` the header stepper's, so the stepper and the bar need no analytics code. The button that opens the summary is the exception: like the built-in **Summary** button, it records `FrontendAnalyticsFactory.SummaryButtonClicked()` from `@salsita-ai/sdk/configurator` through `phx.frontend.useAnalytics().push()` and enters the state with `phx.frontend.useNavigate()`, because `next()` would record the next-step button's event instead.
85
85
  - **Locked once ordered.** The stepper takes no clicks while the order is being placed or after it was placed; **Design another** in the bar returns to the first step.
86
86
 
@@ -494,7 +494,7 @@ A viewer of a shared configuration sees no **Place order**: neither hook checks
494
494
 
495
495
  ## Pages that slide in
496
496
 
497
- `ConfiguratorLayout` keeps the scene mounted and lays the desktop or the phone page over it, by the width of the configurator's container from `phx.frontend.useActiveViewports()`. It also holds the contact form's state, so the form survives the switch between the two and the move to the confirmation.
497
+ `ConfiguratorLayout` keeps the scene mounted and lays the desktop or the phone page over it, by the width of the configurator's container from `phx.frontend.useActiveViewports()`. It also holds the contact form's state and the furthest step reached, so both survive the switch between the two, and the form survives the move to the confirmation.
498
498
 
499
499
  ```tsx
500
500
  import { Box } from '@mui/material'
@@ -506,19 +506,24 @@ import { NIGHTJAR } from '../_fields/index.js'
506
506
  import { DesktopPage } from '../_shared/DesktopPage.js'
507
507
  import { PhonePage } from '../_shared/PhonePage.js'
508
508
  import { useOrderForm } from '../_shared/order-form.js'
509
+ import { useReachedStep } from '../_shared/use-reached-step.js'
509
510
 
510
511
  // A wizard over the scene. The canvas stays mounted the whole time; the desktop or the phone page lies over it. The
511
- // contact form's state lives here, so it survives the switch between the two and the move to the confirmation.
512
+ // contact form's state and the furthest step reached live here, so they survive the switch between the two; the form
513
+ // also survives the move to the confirmation.
512
514
  const ConfiguratorLayout = () => {
513
515
  // `null` until the container has been measured; only the scene shows until then
514
516
  const viewports = phx.frontend.useActiveViewports()
517
+ const { activeIndex } = phx.frontend.useStepNavigation()
515
518
  const form = useOrderForm()
519
+ const reached = useReachedStep(activeIndex)
516
520
  const isDesktop = viewports?.includes('desktop') ?? false
517
521
 
518
522
  return (
519
523
  <Box component="div" sx={{ position: 'absolute', inset: 0, overflow: 'clip', bgcolor: NIGHTJAR.ink }}>
520
524
  <LayoutSlots.SharedCanvas />
521
- {viewports !== null && (isDesktop ? <DesktopPage form={form} /> : <PhonePage form={form} />)}
525
+ {viewports !== null &&
526
+ (isDesktop ? <DesktopPage form={form} reached={reached} /> : <PhonePage form={form} reached={reached} />)}
522
527
  </Box>
523
528
  )
524
529
  }
@@ -569,6 +574,7 @@ const Brand = () => {
569
574
 
570
575
  interface DesktopPageProps {
571
576
  readonly form: OrderForm
577
+ readonly reached: number
572
578
  }
573
579
 
574
580
  // The desktop wizard: the stepper on top, the guitar in the free area on the left, the panel of the step on the
@@ -579,7 +585,7 @@ interface DesktopPageProps {
579
585
  // measurement, taken when the page mounts, sees it shifted, and no resize follows when the slide ends. `update()`
580
586
  // from `useSceneSafeArea` measures again once the slide has settled, so the camera frames the guitar in the place the
581
587
  // page leaves for it.
582
- export const DesktopPage = ({ form }: DesktopPageProps) => {
588
+ export const DesktopPage = ({ form, reached }: DesktopPageProps) => {
583
589
  const { containerRef, safeAreaRef, update } = phx.frontend.useSceneSafeArea()
584
590
  const navigationState = phx.frontend.useActiveNavigationState()
585
591
  const page = pageOf(navigationState)
@@ -620,7 +626,7 @@ export const DesktopPage = ({ form }: DesktopPageProps) => {
620
626
  }}
621
627
  >
622
628
  <Brand />
623
- <StepHeader compact={false} />
629
+ <StepHeader compact={false} reached={reached} />
624
630
  </Box>
625
631
 
626
632
  <Box
@@ -786,6 +792,7 @@ The guitar is `public/models/gallery/guitar.glb`, generated by `scripts/models/g
786
792
  - Typing past 16 characters turns the counter and the field red and shows "The headstock fits 16 characters. Shorten the signature."; leaving the field puts back the signature committed before, and the headstock with it.
787
793
  - **Review order** opens the summary: a wider panel lists every choice, with the signature in script. With the humbucker conflict, the summary lists it with **Go to Electronics**, the bar shows "Settle the choices above first", and **Place order** reveals "Enter the name the order is for." and "Enter an e-mail address for the build updates." under the empty fields.
788
794
  - **Go to Electronics** opens that step; after **P-90s**, the stepper's **Summary** returns to the summary in one click.
795
+ - After going back from the summary to an earlier step, narrowing the window until the phone page shows and widening it again leaves the stepper's **Summary** enabled.
789
796
  - With a name and an e-mail, **Place order** shows "Placing order…" with a spinner for under two seconds. With gold hardware it then shows "The order did not go through" and the service's reason at the top of the summary; with chrome or black it opens the confirmation: "Thank you, …", the order number, the e-mail and the build. The stepper no longer takes clicks, and **Design another** returns to **Body**.
790
797
  - Each step, the summary and the confirmation leave the guitar centred in the area beside the panel once their slide has settled.
791
798
  - With the browser console's Verbose level, **Next** and **Back** log the step buttons' `nextStepButton` and `previousStepButton` events, the stepper the `topNavigation` `step` event, **Review order** the `summaryButton` event and **Place order** the `finalizeButton` event: without a Google Tag Manager id, Storybook's analytics log their events with `console.debug`.
@@ -2367,6 +2374,7 @@ const Brand = () => {
2367
2374
 
2368
2375
  interface DesktopPageProps {
2369
2376
  readonly form: OrderForm
2377
+ readonly reached: number
2370
2378
  }
2371
2379
 
2372
2380
  // The desktop wizard: the stepper on top, the guitar in the free area on the left, the panel of the step on the
@@ -2377,7 +2385,7 @@ interface DesktopPageProps {
2377
2385
  // measurement, taken when the page mounts, sees it shifted, and no resize follows when the slide ends. `update()`
2378
2386
  // from `useSceneSafeArea` measures again once the slide has settled, so the camera frames the guitar in the place the
2379
2387
  // page leaves for it.
2380
- export const DesktopPage = ({ form }: DesktopPageProps) => {
2388
+ export const DesktopPage = ({ form, reached }: DesktopPageProps) => {
2381
2389
  const { containerRef, safeAreaRef, update } = phx.frontend.useSceneSafeArea()
2382
2390
  const navigationState = phx.frontend.useActiveNavigationState()
2383
2391
  const page = pageOf(navigationState)
@@ -2418,7 +2426,7 @@ export const DesktopPage = ({ form }: DesktopPageProps) => {
2418
2426
  }}
2419
2427
  >
2420
2428
  <Brand />
2421
- <StepHeader compact={false} />
2429
+ <StepHeader compact={false} reached={reached} />
2422
2430
  </Box>
2423
2431
 
2424
2432
  <Box
@@ -2741,12 +2749,13 @@ const pageOf = (navigationState: string): Page => {
2741
2749
 
2742
2750
  interface PhonePageProps {
2743
2751
  readonly form: OrderForm
2752
+ readonly reached: number
2744
2753
  }
2745
2754
 
2746
2755
  // The phone wizard: a compact progress line, the guitar at the top, the step's choices in a sheet below it and the
2747
2756
  // bar fixed at the bottom. Like the desktop pages, each page rises in with its own safe area and measures it again
2748
2757
  // with `update()` once the slide has settled.
2749
- export const PhonePage = ({ form }: PhonePageProps) => {
2758
+ export const PhonePage = ({ form, reached }: PhonePageProps) => {
2750
2759
  const { containerRef, safeAreaRef, update } = phx.frontend.useSceneSafeArea()
2751
2760
  const navigationState = phx.frontend.useActiveNavigationState()
2752
2761
  const page = pageOf(navigationState)
@@ -2787,7 +2796,7 @@ export const PhonePage = ({ form }: PhonePageProps) => {
2787
2796
  <Box component="span" aria-hidden sx={{ inlineSize: 3, blockSize: 16, bgcolor: NIGHTJAR.amber }} />
2788
2797
  <Typography sx={{ ...HEADING_SX, fontSize: 13, letterSpacing: '0.32em' }}>Northfret</Typography>
2789
2798
  </Box>
2790
- <StepHeader compact />
2799
+ <StepHeader compact reached={reached} />
2791
2800
  </Box>
2792
2801
 
2793
2802
  <Box
@@ -2842,24 +2851,24 @@ import { Box, ButtonBase, Typography } from '@mui/material'
2842
2851
  import { phx } from '../../.phoenix/phx.js'
2843
2852
  import { HEADING_SX, NIGHTJAR, useWarnings } from '../_fields/index.js'
2844
2853
  import { STEP_WARNINGS } from './step-warnings.js'
2845
- import { useReachedStep } from './use-reached-step.js'
2846
2854
 
2847
2855
  const CHECK_ICON = 'M5 12.5 10 17.5 19 7'
2848
2856
 
2849
2857
  interface StepHeaderProps {
2850
2858
  /** One line with a progress bar, for a phone; the full stepper otherwise */
2851
2859
  readonly compact: boolean
2860
+ /** The furthest step the customer has reached; the layout keeps it, so it survives the switch between the pages */
2861
+ readonly reached: number
2852
2862
  }
2853
2863
 
2854
2864
  // The spec's `navigationSteps` as a stepper. Labels and order come from the spec; `useStepNavigation` gives each step
2855
2865
  // its status, and `goTo` enters a step and records the stepper's analytics event. The customer can go back to any step
2856
2866
  // they have reached, never ahead of it, and nowhere while the order is being placed or after it was placed.
2857
- export const StepHeader = ({ compact }: StepHeaderProps) => {
2867
+ export const StepHeader = ({ compact, reached }: StepHeaderProps) => {
2858
2868
  const { steps, activeIndex, activeStep, goTo } = phx.frontend.useStepNavigation()
2859
2869
  const navigationState = phx.frontend.useActiveNavigationState()
2860
2870
  const { primary } = phx.frontend.useFinalizeActions()
2861
2871
  const warnings = useWarnings()
2862
- const reached = useReachedStep(activeIndex)
2863
2872
  const isLocked = navigationState === 'ordered' || (primary?.isPending ?? false)
2864
2873
 
2865
2874
  const hasWarning = (state: string) => {
@@ -3377,7 +3386,8 @@ export const STEP_WARNINGS: Readonly<Record<string, readonly string[]>> = {
3377
3386
  import { useState } from 'react'
3378
3387
 
3379
3388
  // The furthest step the customer has reached. The stepper offers every step up to it, so after changing an earlier
3380
- // step the customer returns to where they were without stepping through the ones between.
3389
+ // step the customer returns to where they were without stepping through the ones between. The layout calls this, where
3390
+ // it stays mounted, so the furthest step survives the switch between the desktop and the phone page.
3381
3391
  export const useReachedStep = (activeIndex: number): number => {
3382
3392
  const [reached, setReached] = useState(activeIndex)
3383
3393
 
@@ -3401,19 +3411,24 @@ import { NIGHTJAR } from '../_fields/index.js'
3401
3411
  import { DesktopPage } from '../_shared/DesktopPage.js'
3402
3412
  import { PhonePage } from '../_shared/PhonePage.js'
3403
3413
  import { useOrderForm } from '../_shared/order-form.js'
3414
+ import { useReachedStep } from '../_shared/use-reached-step.js'
3404
3415
 
3405
3416
  // A wizard over the scene. The canvas stays mounted the whole time; the desktop or the phone page lies over it. The
3406
- // contact form's state lives here, so it survives the switch between the two and the move to the confirmation.
3417
+ // contact form's state and the furthest step reached live here, so they survive the switch between the two; the form
3418
+ // also survives the move to the confirmation.
3407
3419
  const ConfiguratorLayout = () => {
3408
3420
  // `null` until the container has been measured; only the scene shows until then
3409
3421
  const viewports = phx.frontend.useActiveViewports()
3422
+ const { activeIndex } = phx.frontend.useStepNavigation()
3410
3423
  const form = useOrderForm()
3424
+ const reached = useReachedStep(activeIndex)
3411
3425
  const isDesktop = viewports?.includes('desktop') ?? false
3412
3426
 
3413
3427
  return (
3414
3428
  <Box component="div" sx={{ position: 'absolute', inset: 0, overflow: 'clip', bgcolor: NIGHTJAR.ink }}>
3415
3429
  <LayoutSlots.SharedCanvas />
3416
- {viewports !== null && (isDesktop ? <DesktopPage form={form} /> : <PhonePage form={form} />)}
3430
+ {viewports !== null &&
3431
+ (isDesktop ? <DesktopPage form={form} reached={reached} /> : <PhonePage form={form} reached={reached} />)}
3417
3432
  </Box>
3418
3433
  )
3419
3434
  }