@salsita-ai/sdk 0.50.0-develop.699f439183 → 0.50.0-develop.6e9e536d77

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 (59) hide show
  1. package/README.md +1 -1
  2. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  3. package/dist/application-spec/generate-application/code/templates/index.d.ts +2 -6
  4. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  5. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill-bundle.d.ts +10 -0
  6. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill-bundle.js +7 -0
  7. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
  8. package/dist/client/authorized-client.d.ts +3 -1
  9. package/dist/client/authorized-client.js +1 -1
  10. package/dist/client/contract.d.ts +17 -0
  11. package/dist/client/index.d.ts +1 -1
  12. package/dist/client/local-authorized-client.d.ts +3 -1
  13. package/dist/client/local-authorized-client.js +1 -1
  14. package/dist/client/memory-authorized-client.d.ts +4 -1
  15. package/dist/client/memory-authorized-client.js +1 -1
  16. package/dist/client/phoenix-api.d.ts +228 -0
  17. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  18. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +13 -0
  19. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  20. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +2 -1
  21. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  22. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +15 -9
  23. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  24. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.d.ts +1 -1
  25. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.js +1 -1
  26. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.d.ts +12 -4
  27. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.js +1 -1
  28. package/dist/configurator/ui/assistant/screenshots/use-screenshot-src.d.ts +8 -0
  29. package/dist/configurator/ui/assistant/screenshots/use-screenshot-src.js +1 -0
  30. package/dist/docs/index.json +1 -1
  31. package/dist/docs/phoenix/domain-model.mdx +4 -1
  32. package/dist/docs/phoenix/phoenix-cli.mdx +14 -6
  33. package/dist/docs/phoenix/project-structure.mdx +21 -17
  34. package/dist/docs/phoenix/sdk-builder-workflow.mdx +6 -4
  35. package/dist/domain/application/assistant/build-assistant-manifest.js +1 -1
  36. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
  37. package/dist/phoenix/application-spec/generate-application-spec-project.js +2 -2
  38. package/dist/phoenix/application-spec/resolve-deployment-data.d.ts +11 -0
  39. package/dist/phoenix/application-spec/resolve-deployment-data.js +2 -2
  40. package/dist/phoenix/cli.js +1 -1
  41. package/dist/phoenix/commands/deploy.js +1 -1
  42. package/dist/phoenix/commands/dev.js +5 -5
  43. package/dist/phoenix/commands/generated-env-markers.d.ts +1 -0
  44. package/dist/phoenix/commands/generated-env-markers.js +1 -1
  45. package/dist/phoenix/commands/index.d.ts +1 -1
  46. package/dist/phoenix/commands/index.js +1 -1
  47. package/dist/phoenix/lingui/index.d.ts +1 -0
  48. package/dist/phoenix/lingui/index.js +1 -0
  49. package/dist/phoenix/{services → lingui}/lingui.d.ts +5 -2
  50. package/dist/phoenix/lingui/lingui.js +1 -0
  51. package/dist/phoenix/services/application-spec-project-watcher.d.ts +3 -1
  52. package/dist/phoenix/services/application-spec-project-watcher.js +2 -2
  53. package/dist/phoenix/services/index.d.ts +1 -0
  54. package/dist/phoenix/services/index.js +1 -1
  55. package/dist/phoenix/services/lingui-watcher.js +1 -1
  56. package/dist/phoenix/services/prepare-development-environment.js +1 -1
  57. package/dist/phoenix/services/vite-config.js +1 -1
  58. package/package.json +3 -3
  59. package/dist/phoenix/services/lingui.js +0 -1
package/README.md CHANGED
@@ -103,7 +103,7 @@ The `package.json` `exports` map is the machine-readable list, and the TypeScrip
103
103
  ## Documentation and support
104
104
 
105
105
  - `pnpm phoenix docs -s "<topic>"` searches the bundled documentation; `phoenix docs -r <path>` reads a page.
106
- - `phoenix sync` generates agent skills into `.claude/skills/` and `.agents/skills/`, so coding agents know the project conventions and the CLI.
106
+ - `phoenix sync` generates one `phoenix` skill under `.claude/skills/` and `.agents/skills/`, with task-specific project, CLI, workflow, review, variants and feedback guides linked from its entry point.
107
107
  - The builder docs at [docs.salsita.ai](https://docs.salsita.ai) have the guides for getting started and the CLI reference; Studio is where you sign in and manage projects.
108
108
  - `pnpm phoenix feedback "<message>"` reaches the team directly.
109
109
 
@@ -1,6 +1,6 @@
1
- import{GeneratedApplicationFile as e}from"../../core/index.js";import{followsCanaryBuilds as r,generateApplicationSpecEslintConfig as t,generateApplicationSpecFavicon as i,generateApplicationSpecGitignore as n,generateApplicationSpecPrettierConfig as a,generateApplicationSpecRobotsTxt as o,generateApplicationSpecRootTsconfig as l,generateApplicationSpecTsconfig as s,generateLinguiRcFile as c,generatePhoenixCliSkill as f,generatePhoenixCreateConfiguratorSkill as p,generatePhoenixCreateConfiguratorSkillReferences as d,generatePhoenixFeedbackSkill as m,generatePhoenixReviewSkill as g,generatePhoenixSkill as h,generatePhoenixVariantsSkill as u,stripSdkMarkerContent as j}from"./templates/index.js";export const agentSkillRoots=[".claude/skills",".agents/skills"];let x=[{path:"CLAUDE.md",generatedPrefix:""},{path:".cursor/rules/phoenix.mdc",generatedPrefix:`---
1
+ import{GeneratedApplicationFile as e}from"../../core/index.js";import{followsCanaryBuilds as i,generateApplicationSpecEslintConfig as r,generateApplicationSpecFavicon as t,generateApplicationSpecGitignore as a,generateApplicationSpecPrettierConfig as o,generateApplicationSpecRobotsTxt as n,generateApplicationSpecRootTsconfig as l,generateApplicationSpecTsconfig as s,generateLinguiRcFile as c,generatePhoenixSkillBundle as d,stripSdkMarkerContent as f}from"./templates/index.js";export const agentSkillRoots=[".claude/skills",".agents/skills"];let p=[{path:"CLAUDE.md",generatedPrefix:""},{path:".cursor/rules/phoenix.mdc",generatedPrefix:`---
2
2
  description: Phoenix SDK project rules
3
3
  globs:
4
4
  alwaysApply: true
5
5
  ---
6
- `}],P=[".claude/commands/review.md",".cursor/commands/review.md"],v=(e,r)=>e.projectFiles.find(e=>e.file.name===r)?.file.content;export const generateProjectScaffolding=F=>{let E,G,{applicationSpec:b,categorizedFiles:k,sdkVersion:w,projectInGitRepository:L=!0,cliUseWorkspace:S}=F,$=v(k,".gitignore"),y=void 0!==S?S:b.appMetadata.workspace,C=(r=>{let t=[],i=[...P];for(let{path:n,generatedPrefix:a}of x){let o=v(r,n);if(void 0===o)continue;let l=j(o,a);void 0===l?i.push(n):l!==o&&t.push(e(n,l,{format:!1}))}return{strippedFiles:t,removedFiles:i}})(k),M={generatedProjectFiles:(E=[{file:e("tsconfig.json",l()),hasEarlierGenerated:k.hasEarlierGeneratedTsconfig},{file:e("eslint.config.js",t()),hasEarlierGenerated:k.hasEarlierGeneratedEslintConfig},{file:e("prettier.config.js",a()),hasEarlierGenerated:k.hasEarlierGeneratedPrettierConfig}]).filter(e=>e.hasEarlierGenerated).map(e=>e.file),initialProjectFiles:E.filter(e=>!e.hasEarlierGenerated).map(e=>e.file)};return{phoenixFiles:[e("tsconfig.json",s()),e(".linguirc",c(b.configurator.ui.availableLocales),{format:!1})],generatedProjectFiles:[...M.generatedProjectFiles,...L?[e(".gitignore",n($))]:[],...(G=[{name:"phoenix",content:h({checkLatestCanary:r({useWorkspace:y,sdkVersion:w})}),references:[]},{name:"phoenix-cli",content:f(),references:[]},{name:"phoenix-create-configurator",content:p(),references:d()},{name:"phoenix-feedback",content:m(),references:[]},{name:"phoenix-review",content:g(),references:[]},{name:"phoenix-variants",content:u(),references:[]}],agentSkillRoots.flatMap(r=>G.flatMap(t=>[e(`${r}/${t.name}/SKILL.md`,t.content),...t.references.map(i=>e(`${r}/${t.name}/${i.path}`,i.content))]))),...C.strippedFiles],initialProjectFiles:[...M.initialProjectFiles,e("public/favicon.ico",i(),{binary:!0}),e("public/robots.txt",o(),{format:!1})],removedProjectFiles:C.removedFiles}};
6
+ `}],g=[".claude/commands/review.md",".cursor/commands/review.md"],m=(e,i)=>e.projectFiles.find(e=>e.file.name===i)?.file.content;export const generateProjectScaffolding=h=>{let u,j,{applicationSpec:x,categorizedFiles:P,sdkVersion:F,projectInGitRepository:E=!0,cliUseWorkspace:v}=h,G=m(P,".gitignore"),b=void 0!==v?v:x.appMetadata.workspace,k=(i=>{let r=[],t=[...g];for(let{path:a,generatedPrefix:o}of p){let n=m(i,a);if(void 0===n)continue;let l=f(n,o);void 0===l?t.push(a):l!==n&&r.push(e(a,l,{format:!1}))}return{strippedFiles:r,removedFiles:t}})(P),L={generatedProjectFiles:(u=[{file:e("tsconfig.json",l()),hasEarlierGenerated:P.hasEarlierGeneratedTsconfig},{file:e("eslint.config.js",r()),hasEarlierGenerated:P.hasEarlierGeneratedEslintConfig},{file:e("prettier.config.js",o()),hasEarlierGenerated:P.hasEarlierGeneratedPrettierConfig}]).filter(e=>e.hasEarlierGenerated).map(e=>e.file),initialProjectFiles:u.filter(e=>!e.hasEarlierGenerated).map(e=>e.file)};return{phoenixFiles:[e("tsconfig.json",s()),e(".linguirc",c(x.configurator.ui.availableLocales),{format:!1})],generatedProjectFiles:[...L.generatedProjectFiles,...E?[e(".gitignore",a(G))]:[],...(j=d({checkLatestCanary:i({useWorkspace:b,sdkVersion:F})}),agentSkillRoots.flatMap(i=>[e(`${i}/phoenix/SKILL.md`,j.content),...j.references.map(r=>e(`${i}/phoenix/${r.path}`,r.content))])),...k.strippedFiles],initialProjectFiles:[...L.initialProjectFiles,e("public/favicon.ico",t(),{binary:!0}),e("public/robots.txt",n(),{format:!1})],removedProjectFiles:k.removedFiles}};
@@ -37,12 +37,8 @@ export { generateApplicationSpecGlobals } from './globals.js';
37
37
  export { generateApplicationSpecHtmlAdminIndex } from './html-admin-index.js';
38
38
  export { generateApplicationSpecHtmlIndex } from './html-index.js';
39
39
  export { generateLinguiRcFile } from './lingui-rc.js';
40
- export { generatePhoenixCliSkill } from './skills/phoenix-cli-skill.js';
41
- export { generatePhoenixCreateConfiguratorSkill, generatePhoenixCreateConfiguratorSkillReferences, } from './skills/phoenix-create-configurator-skill.js';
42
- export { generatePhoenixFeedbackSkill } from './skills/phoenix-feedback-skill.js';
43
- export { generatePhoenixReviewSkill } from './skills/phoenix-review-skill.js';
44
- export { type PhoenixSkillOptions, followsCanaryBuilds, generatePhoenixSkill } from './skills/phoenix-skill.js';
45
- export { generatePhoenixVariantsSkill } from './skills/phoenix-variants-skill.js';
40
+ export { generatePhoenixSkillBundle } from './skills/phoenix-skill-bundle.js';
41
+ export { type PhoenixSkillOptions, followsCanaryBuilds } from './skills/phoenix-skill.js';
46
42
  export { generateApplicationSpecPrettierConfig } from './prettier-config.js';
47
43
  export { generateApplicationSpecRobotsTxt } from './robots-txt.js';
48
44
  export { generateApplicationSpecRootTsconfig } from './root-tsconfig.js';
@@ -1 +1 @@
1
- export{stripSdkMarkerContent}from"./common/strip-sdk-marker-content.js";export{generateApplicationSpecAdminConfig}from"./admin-config.js";export{generateApplicationSpecAdminCode}from"./admin-domain-code.js";export{generateApplicationSpecAdminFrontendCode}from"./admin-frontend-code.js";export{generateAdminGlobalBindingBlock,generateApplicationSpecAdminGlobals}from"./admin-globals.js";export{generateApplicationSpecTypesModule}from"./application-types.js";export{generateApplicationSpecAdminFile}from"./admin.js";export{generateApplicationSpecConfiguratorConfig}from"./configurator-config.js";export{generateApplicationSpecConfiguratorCode}from"./configurator-domain-code.js";export{generateApplicationSpecConfiguratorFrontendCode}from"./configurator-frontend-code.js";export{generateApplicationSpecConfiguratorGlobals,generateConfiguratorGlobalBindingBlock}from"./configurator-globals.js";export{generateApplicationSpecConfiguratorFile}from"./configurator.js";export{generateApplicationSpecDomainSpecFile}from"./domain-spec.js";export{generateImportedStateConstants,generatePhxExportsBlock,generatePhxGlobalsBlock,generateStateConstants}from"./domain-spec/index.js";export{generateApplicationSpecDomainFile}from"./domain.js";export{generateApplicationSpecEditorFile}from"./editor.js";export{generateApplicationSpecEmbeddedAdmin}from"./embedded-admin.js";export{generateApplicationSpecEmbeddedConfigurator}from"./embedded-configurator.js";export{generateApplicationSpecEslintConfig}from"./eslint-config.js";export{generateApplicationSpecFavicon}from"./favicon.js";export{generateApplicationSpecFrontendConfig}from"./frontend-config.js";export{generateApplicationSpecGitignore}from"./gitignore.js";export{generateApplicationSpecGlobals}from"./globals.js";export{generateApplicationSpecHtmlAdminIndex}from"./html-admin-index.js";export{generateApplicationSpecHtmlIndex}from"./html-index.js";export{generateLinguiRcFile}from"./lingui-rc.js";export{generatePhoenixCliSkill}from"./skills/phoenix-cli-skill.js";export{generatePhoenixCreateConfiguratorSkill,generatePhoenixCreateConfiguratorSkillReferences}from"./skills/phoenix-create-configurator-skill.js";export{generatePhoenixFeedbackSkill}from"./skills/phoenix-feedback-skill.js";export{generatePhoenixReviewSkill}from"./skills/phoenix-review-skill.js";export{followsCanaryBuilds,generatePhoenixSkill}from"./skills/phoenix-skill.js";export{generatePhoenixVariantsSkill}from"./skills/phoenix-variants-skill.js";export{generateApplicationSpecPrettierConfig}from"./prettier-config.js";export{generateApplicationSpecRobotsTxt}from"./robots-txt.js";export{generateApplicationSpecRootTsconfig}from"./root-tsconfig.js";export{generateApplicationSpecStoryDevDeclaration,generateApplicationSpecStoryPhxModule,generateApplicationSpecStoryRawProjectFiles,generateApplicationSpecStoryWrapper}from"./story/index.js";export{generateApplicationSpecCodeFile}from"./spec-code/index.js";export{generateApplicationSpecMessagesFile}from"./spec-messages/index.js";export{generateApplicationSpecTsconfig}from"./tsconfig.js";export{generateApplicationSpecWebhookBuilders}from"./webhook-builders.js";export{generateApplicationSpecWebhookIndex}from"./webhook-index.js";export{generateApplicationSpecWebhooksConfig}from"./webhooks-config.js";
1
+ export{stripSdkMarkerContent}from"./common/strip-sdk-marker-content.js";export{generateApplicationSpecAdminConfig}from"./admin-config.js";export{generateApplicationSpecAdminCode}from"./admin-domain-code.js";export{generateApplicationSpecAdminFrontendCode}from"./admin-frontend-code.js";export{generateAdminGlobalBindingBlock,generateApplicationSpecAdminGlobals}from"./admin-globals.js";export{generateApplicationSpecTypesModule}from"./application-types.js";export{generateApplicationSpecAdminFile}from"./admin.js";export{generateApplicationSpecConfiguratorConfig}from"./configurator-config.js";export{generateApplicationSpecConfiguratorCode}from"./configurator-domain-code.js";export{generateApplicationSpecConfiguratorFrontendCode}from"./configurator-frontend-code.js";export{generateApplicationSpecConfiguratorGlobals,generateConfiguratorGlobalBindingBlock}from"./configurator-globals.js";export{generateApplicationSpecConfiguratorFile}from"./configurator.js";export{generateApplicationSpecDomainSpecFile}from"./domain-spec.js";export{generateImportedStateConstants,generatePhxExportsBlock,generatePhxGlobalsBlock,generateStateConstants}from"./domain-spec/index.js";export{generateApplicationSpecDomainFile}from"./domain.js";export{generateApplicationSpecEditorFile}from"./editor.js";export{generateApplicationSpecEmbeddedAdmin}from"./embedded-admin.js";export{generateApplicationSpecEmbeddedConfigurator}from"./embedded-configurator.js";export{generateApplicationSpecEslintConfig}from"./eslint-config.js";export{generateApplicationSpecFavicon}from"./favicon.js";export{generateApplicationSpecFrontendConfig}from"./frontend-config.js";export{generateApplicationSpecGitignore}from"./gitignore.js";export{generateApplicationSpecGlobals}from"./globals.js";export{generateApplicationSpecHtmlAdminIndex}from"./html-admin-index.js";export{generateApplicationSpecHtmlIndex}from"./html-index.js";export{generateLinguiRcFile}from"./lingui-rc.js";export{generatePhoenixSkillBundle}from"./skills/phoenix-skill-bundle.js";export{followsCanaryBuilds}from"./skills/phoenix-skill.js";export{generateApplicationSpecPrettierConfig}from"./prettier-config.js";export{generateApplicationSpecRobotsTxt}from"./robots-txt.js";export{generateApplicationSpecRootTsconfig}from"./root-tsconfig.js";export{generateApplicationSpecStoryDevDeclaration,generateApplicationSpecStoryPhxModule,generateApplicationSpecStoryRawProjectFiles,generateApplicationSpecStoryWrapper}from"./story/index.js";export{generateApplicationSpecCodeFile}from"./spec-code/index.js";export{generateApplicationSpecMessagesFile}from"./spec-messages/index.js";export{generateApplicationSpecTsconfig}from"./tsconfig.js";export{generateApplicationSpecWebhookBuilders}from"./webhook-builders.js";export{generateApplicationSpecWebhookIndex}from"./webhook-index.js";export{generateApplicationSpecWebhooksConfig}from"./webhooks-config.js";
@@ -0,0 +1,10 @@
1
+ import { type AgentSkillReference } from './agent-skill.js';
2
+ import { type PhoenixSkillOptions } from './phoenix-skill.js';
3
+ /** The discoverable entry point and the documents it ships beside it. */
4
+ interface PhoenixSkillBundle {
5
+ readonly content: string;
6
+ readonly references: readonly AgentSkillReference[];
7
+ }
8
+ /** Keeps the existing generators as content owners while changing only discovery and relative routing. */
9
+ export declare const generatePhoenixSkillBundle: (options: PhoenixSkillOptions) => PhoenixSkillBundle;
10
+ export {};
@@ -0,0 +1,7 @@
1
+ import{renderAgentSkill as e}from"./agent-skill.js";import{generatePhoenixCliSkill as t}from"./phoenix-cli-skill.js";import{generatePhoenixCreateConfiguratorSkill as n,generatePhoenixCreateConfiguratorSkillReferences as r}from"./phoenix-create-configurator-skill.js";import{generatePhoenixFeedbackSkill as i}from"./phoenix-feedback-skill.js";import{generatePhoenixReviewSkill as o}from"./phoenix-review-skill.js";import{generatePhoenixSkill as a}from"./phoenix-skill.js";import{generatePhoenixVariantsSkill as s}from"./phoenix-variants-skill.js";let l=(e,t,n)=>n.reduce((e,n)=>{let r="../".repeat(t.split("/").length-2),i=`[${n.title}](${r}${n.path.slice(11)})`;return e.replaceAll(`\`${n.name}\` skill`,i).replaceAll(`${n.name} skill`,i)},e),c=e=>{let t="\n---\n\n",n=e.content.indexOf(t),r=e.content.slice(0,n).split("\n").find(e=>e.startsWith("description: "));if(-1===n||void 0===r)throw Error(`Cannot emit the ${e.name} guide: its skill frontmatter is missing. Render it with renderAgentSkill.`);return{description:r.slice(13),body:e.content.slice(n+t.length)}};export const generatePhoenixSkillBundle=h=>{let p=[{name:"phoenix",title:"Project guidance",path:"references/project.md",content:a(h),references:[]},{name:"phoenix-create-configurator",title:"Configurator workflow",path:"references/create-configurator/guide.md",content:n(),references:r()},{name:"phoenix-cli",title:"CLI guide",path:"references/cli.md",content:t(),references:[]},{name:"phoenix-review",title:"Code review guide",path:"references/review.md",content:o(),references:[]},{name:"phoenix-variants",title:"Variants guide",path:"references/variants.md",content:s(),references:[]},{name:"phoenix-feedback",title:"Feedback guide",path:"references/feedback.md",content:i(),references:[]}];return{content:e({name:"phoenix",description:"Build, change, inspect, deploy or review a Phoenix configurator project. Use for Phoenix project work, visual alternatives, and platform feedback.",sections:[`# Phoenix
2
+
3
+ Load the guides for the work in hand before doing that work. Several may apply. Follow their existing instructions; this entry point only routes to them. Reuse guidance already read in the conversation unless sync or an SDK update changed it.`,`## Choose the relevant guidance
4
+
5
+ | When | Read |
6
+ | --- | --- |
7
+ ${p.map(e=>`| ${c(e).description} | [${e.title}](${e.path}) |`).join("\n")}`,"The workflow guide links to product discovery, asset conversion, UX guidance and appearance review at the stages that use them. An isolated code fix needs project guidance and the applicable CLI/review guides; it does not restart the configurator workflow.\n\nEarlier Phoenix skill names refer to these guides: ask for the same task through `phoenix`. Read linked documents when their task applies, not the whole reference tree."]}),references:p.flatMap(e=>[{path:e.path,content:l(c(e).body,e.path,p)},...e.references.map(t=>{let n=`${e.path.slice(0,e.path.lastIndexOf("/")+1)}${t.path}`;return{path:n,content:l(t.content,n,p)}})])}};
@@ -1 +1 @@
1
- import{renderAgentSkill as e}from"./agent-skill.js";import{sdkGuidanceSections as t}from"./sdk-content.js";export const followsCanaryBuilds=e=>!e.useWorkspace&&e.sdkVersion.includes("-develop.");export const generatePhoenixSkill=({checkLatestCanary:n})=>e({name:"phoenix",description:`Phoenix SDK project conventions, covering project structure, architecture principles, key patterns, file and export rules, webhook membership, versioning the domain model once it is deployed, showing the user the dev preview and the build of a deploy, the run evaluation that follows a live deploy, inspecting the rendered result, and how to search the SDK documentation${n?", and the internal-beta check for the latest canary SDK build":""}. Use before writing or changing specs or source code, or deploying this Phoenix configurator project.`,sections:[t.packageManager,...n?["## Stay on the Latest Canary\nDuring the internal beta this project uses the `canary` build of the SDK, which is published from every change to the platform. Once per conversation, before you change anything, compare the installed version, the `version` in `node_modules/@salsita-ai/sdk/package.json`, with the newest canary:\n```bash\nnpm view @salsita-ai/sdk@canary version\n```\nWhen they differ, tell the user which version you are moving to, then update the SDK, bring the packages it needs next to it in line, and regenerate the project's files:\n```bash\npnpm add @salsita-ai/sdk@canary && pnpm phoenix deps && pnpm install && pnpm phoenix sync\n```\n`sync` rewrites the Phoenix skills, so read the ones you use again after it. When the user runs a globally installed `phoenix`, update that too with `npm install -g @salsita-ai/sdk@canary`. If the project no longer builds after the update, stop and tell the user; the `phoenix-feedback` skill covers reporting it."]:[],t.projectStructure,t.architecturePrinciples,t.keyPatterns,t.fileConventions,t.exportRules,t.webhookMembership,t.doNotEdit,t.modelVersioning,"## Show the Preview While You Work\nThe user should see the configurator change as you build it. Before your first change in a conversation, make sure `pnpm phoenix dev` is running (start it in the background when it is not) and open the address it prints as `Preview: <url>` (also written to `.phoenix/phoenix-dev.log`) in the user's browser: `open '<url>'` on macOS, `xdg-open '<url>'` on Linux, `Start-Process '<url>'` in PowerShell, `start \"\" \"<url>\"` in cmd. Keep the quotes, the shell would otherwise read a `?` in the address as a pattern. The log keeps the lines of earlier runs, so when you have just started dev, wait for the `Preview:` line written after your start and use the last one. Open it once; the preview reloads on every change, so tell the user to keep that tab open instead of opening it again.",t.visualInspection,"## Show the Build After a Deploy\nImmediately before invoking `pnpm phoenix deploy`, refresh `conversation.log` in the project root with the current conversation. Prefer the agent application's native session export when available; do not invent an export command or scan unrelated sessions. Otherwise write the messages still available in context and label the log `reconstructed; incomplete`. Never present a compaction summary as verbatim earlier messages.\n\nUse UTF-8 text with a header naming the agent, export time, source (native export or reconstructed), covered session and known omissions. Preserve user and assistant messages in chronological order, plus tool names, arguments and results when available. Keep each tool result to at most 16 KiB of text, marking truncation explicitly; describe image attachments by reference rather than embedding binary/base64 data, and mark references whose files are not uploaded. Exclude internal instructions, hidden reasoning, credentials and unrelated sensitive content; mark redactions without repeating the removed content. Refresh the current session's log rather than appending duplicate copies or silently carrying over a previous session.\n\nShow the user the exact file to review or redact and obtain approval to include it before deploying; do not ask again when they have already approved that file. If they decline, move it out of the project root before deployment so it cannot be collected. Deploy includes the file in the build's private source archive, downloadable by those authorized to download that build's sources, and leaves it out when extracting build inputs. A missing log warns but does not block deployment. The CLI does not export, redact or validate the transcript's completeness for you.\n\n`pnpm phoenix deploy` starts a build on the server and waits for it to go live. Right after `Build <id> started` it prints `Follow the build in Studio: <url>`. Run the deploy so that you can read its output while it runs, and open that link in the user's browser, quoted as above, as soon as it appears, so they can follow the build they just triggered. When the line is missing, the sign-in is older than the link: tell the user the build is listed under Deployments in Studio, and that `pnpm phoenix login <workspace>` brings the link back.\n\nWhen the deploy reports the configurator live, load the `phoenix-feedback` skill before you write your final message and follow its \"Evaluate the Run\" section, so the user is asked about the run while they are still there. It is asked once per conversation, and only once the user has nothing more to build.","## Code Review\nUse the `phoenix-review` skill to review code changes before committing.",t.sdkDocumentation]});
1
+ import{renderAgentSkill as e}from"./agent-skill.js";import{sdkGuidanceSections as t}from"./sdk-content.js";export const followsCanaryBuilds=e=>!e.useWorkspace&&e.sdkVersion.includes("-develop.");export const generatePhoenixSkill=({checkLatestCanary:n})=>e({name:"phoenix",description:`Phoenix SDK project conventions, covering project structure, architecture principles, key patterns, file and export rules, webhook membership, versioning the domain model once it is deployed, showing the user the dev preview and the build of a deploy, the run evaluation that follows a live deploy, inspecting the rendered result, and how to search the SDK documentation${n?", and the internal-beta check for the latest canary SDK build":""}. Use before writing or changing specs or source code, or deploying this Phoenix configurator project.`,sections:[t.packageManager,...n?["## Stay on the Latest Canary\nDuring the internal beta this project uses the `canary` build of the SDK, which is published from every change to the platform. Once per conversation, before you change anything, compare the installed version, the `version` in `node_modules/@salsita-ai/sdk/package.json`, with the newest canary:\n```bash\nnpm view @salsita-ai/sdk@canary version\n```\nWhen they differ, tell the user which version you are moving to, then update the SDK, bring the packages it needs next to it in line, and regenerate the project's files:\n```bash\npnpm add @salsita-ai/sdk@canary && pnpm phoenix deps && pnpm install && pnpm phoenix sync\n```\n`sync` rewrites the Phoenix skills, so read the ones you use again after it. When the user runs a globally installed `phoenix`, update that too with `npm install -g @salsita-ai/sdk@canary`. If the project no longer builds after the update, stop and tell the user; the `phoenix-feedback` skill covers reporting it."]:[],t.projectStructure,t.architecturePrinciples,t.keyPatterns,t.fileConventions,t.exportRules,t.webhookMembership,t.doNotEdit,t.modelVersioning,"## Show the Preview While You Work\nThe user should see the configurator change as you build it. Before your first change in a conversation, make sure `pnpm phoenix dev` is running (start it in the background when it is not) and open the address it prints as `Preview: <url>` (also written to `.phoenix/phoenix-dev.log`) in the user's browser: `open '<url>'` on macOS, `xdg-open '<url>'` on Linux, `Start-Process '<url>'` in PowerShell, `start \"\" \"<url>\"` in cmd. Keep the quotes, the shell would otherwise read a `?` in the address as a pattern. The log keeps the lines of earlier runs, so when you have just started dev, wait for the `Preview:` line written after your start and use the last one. Open it once; the preview reloads on every change, so tell the user to keep that tab open instead of opening it again. A domain model change is the exception: `pnpm phoenix dev` registers the model with the server only when it starts, and after the change it logs an error that asks for a restart. Restart it, wait for the new `Preview:` line and open that address in place of the old tab, whose address carries the `?cid=` of a configuration saved with the earlier model, which the new model rejects.",t.visualInspection,"## Show the Build After a Deploy\nImmediately before invoking `pnpm phoenix deploy`, refresh `conversation.log` in the project root with the current conversation. Prefer the agent application's native session export when available; do not invent an export command or scan unrelated sessions. Otherwise write the messages still available in context and label the log `reconstructed; incomplete`. Never present a compaction summary as verbatim earlier messages.\n\nUse UTF-8 text with a header naming the agent, export time, source (native export or reconstructed), covered session and known omissions. Preserve user and assistant messages in chronological order, plus tool names, arguments and results when available. Keep each tool result to at most 16 KiB of text, marking truncation explicitly; describe image attachments by reference rather than embedding binary/base64 data, and mark references whose files are not uploaded. Exclude internal instructions, hidden reasoning, credentials and unrelated sensitive content; mark redactions without repeating the removed content. Refresh the current session's log rather than appending duplicate copies or silently carrying over a previous session.\n\nShow the user the exact file to review or redact and obtain approval to include it before deploying; do not ask again when they have already approved that file. If they decline, move it out of the project root before deployment so it cannot be collected. Deploy includes the file in the build's private source archive, downloadable by those authorized to download that build's sources, and leaves it out when extracting build inputs. A missing log warns but does not block deployment. The CLI does not export, redact or validate the transcript's completeness for you.\n\n`pnpm phoenix deploy` starts a build on the server and waits for it to go live. Right after `Build <id> started` it prints `Follow the build in Studio: <url>`. Run the deploy so that you can read its output while it runs, and open that link in the user's browser, quoted as above, as soon as it appears, so they can follow the build they just triggered. When the line is missing, the sign-in is older than the link: tell the user the build is listed under Deployments in Studio, and that `pnpm phoenix login <workspace>` brings the link back.\n\nWhen the deploy reports the configurator live, load the `phoenix-feedback` skill before you write your final message and follow its \"Evaluate the Run\" section, so the user is asked about the run while they are still there. It is asked once per conversation, and only once the user has nothing more to build.","## Code Review\nUse the `phoenix-review` skill to review code changes before committing.",t.sdkDocumentation]});
@@ -1,6 +1,6 @@
1
1
  import type { AccessLevel } from '@salsita-ai/contracts/api';
2
2
  import { type ChatStreamChunk } from '@salsita-ai/contracts/chat';
3
- import { type AssistantChatConnection, type AssistantEarlierThread, type AssistantThreadCompaction, type AuthorizedPhoenixApiClient, type ChatToolCall, type ChatToolCallResult, type ConfigurationPayload, type ConfigurationSchema, type waitForFinalizationResultSettings } from './contract.js';
3
+ import { type AssistantChatConnection, type AssistantEarlierThread, type AssistantImageUpload, type AssistantThreadCompaction, type AuthorizedPhoenixApiClient, type ChatToolCall, type ChatToolCallResult, type ConfigurationPayload, type ConfigurationSchema, type waitForFinalizationResultSettings } from './contract.js';
4
4
  import type { components } from './phoenix-api.js';
5
5
  export declare class AuthorizedClient<FINALIZE_DATA extends Record<string, unknown>, FINALIZE_RESULT extends Record<string, unknown>, FRONTEND_DATA extends Record<string, unknown>> implements AuthorizedPhoenixApiClient<FINALIZE_DATA, FINALIZE_RESULT, FRONTEND_DATA> {
6
6
  private readonly _baseUrl;
@@ -28,6 +28,8 @@ export declare class AuthorizedClient<FINALIZE_DATA extends Record<string, unkno
28
28
  getAssistantChatConnection: (configurationId: string) => AssistantChatConnection;
29
29
  compactAssistantThread: (configurationId: string) => Promise<AssistantThreadCompaction>;
30
30
  getAssistantEarlierThread: (configurationId: string, threadId: string, limit: number) => Promise<AssistantEarlierThread>;
31
+ uploadAssistantImage: (configurationId: string, image: Blob) => Promise<AssistantImageUpload>;
32
+ getAssistantImage: (configurationId: string, imageId: string) => Promise<Blob>;
31
33
  sendMessage: (configurationId: string, content: string, context?: Record<string, string>) => Promise<ReadableStream<ChatStreamChunk>>;
32
34
  sendToolCallResults: (configurationId: string, calls: ChatToolCall[], results: ChatToolCallResult[], agent?: string) => Promise<ReadableStream<ChatStreamChunk>>;
33
35
  generateShareLink: (configurationId: string, revisionId: string, expiration: number, access: AccessLevel, baseUrl: string | undefined) => Promise<{
@@ -1 +1 @@
1
- import{match as t}from"ts-pattern";import{InfoError as a}from"@salsita-ai/common/utils";import{isChatError as e}from"@salsita-ai/contracts/chat";import{createApiBaseClient as r}from"./base-client.js";import{AssistantRequestError as i,InvalidAccessKeyError as o}from"./contract.js";let n=()=>new TransformStream({transform(t,a){a.enqueue(t)}}),s=async(t,a)=>{for(;;){let{done:r,value:i}=await t.read();if(r){await a.close();break}for(let t of i.split("\n").filter(t=>""!==t.trim()).map(t=>JSON.parse(t))){if(e(t))return void await a.abort(Error(t.error));await a.write(t)}}},h=(e,r)=>{let i=`${r} API request failed: ${JSON.stringify(e)}`;return t(e).with({statusCode:403,invalidAccessKey:!0},()=>new o).with({statusCode:403},()=>new a(i)).otherwise(()=>Error(i))};export class AuthorizedClient{_baseUrl;_getAccessToken;_getSessionToken;_client;constructor(t,a,e){this._baseUrl=t,this._getAccessToken=a,this._getSessionToken=e,this._client=r(this._baseUrl)}_authHeaders=async()=>{let t=this._getAccessToken(),a=await this._getSessionToken();return{"X-Access-Key":t,Authorization:`Bearer ${a}`}};getConfiguration=async t=>{let a=await this._authHeaders(),e=await this._client.GET("/configurations/{configurationId}",{params:{path:{configurationId:t}},headers:a});if(e.error)throw h(e.error,"getConfiguration");return e.data};updateConfiguration=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.PUT("/configurations/{configurationId}",{params:{path:{configurationId:t}},body:a,headers:e});if(r.error)throw h(r.error,"updateConfiguration");return r.data};finalize=async(t,a,e,r,i)=>{let o=await this._authHeaders(),n=await this._client.POST("/configurations/{configurationId}/{finalizationUuid}/finalize",{params:{path:{configurationId:t,finalizationUuid:a},query:{locale:r}},body:{finalizeData:e,frontendData:i},headers:o});if(n.error)throw h(n.error,"finalize")};waitForFinalizationToComplete=async(t,a,e)=>{let r=e?.pollingFrequency??500,i=Math.ceil((e?.pollingTimeout??3e4)/r);for(let e=0;e<i;e++){let e=await this._authHeaders(),i=await this._client.GET("/configurations/{configurationId}/{finalizationUuid}/state",{params:{path:{configurationId:t,finalizationUuid:a}},headers:e});if(i.error)throw h(i.error,"getFinalizeJobState");let o=i.data;if("completed"===o.state)return o.result[0];if("failed"===o.state)throw Error("finalization failed");if("unknown"===o.state)throw Error("finalization state is unknown");await new Promise(t=>setTimeout(t,r))}throw Error("finalization did not complete within the specified timeout")};getConfigurationRevision=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/revisions/{revisionId}",{params:{path:{configurationId:t,revisionId:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationRevision");return r.data};getChatConversation=async t=>{let a=await this._authHeaders(),e=await this._client.GET("/configurations/{configurationId}/chat",{params:{path:{configurationId:t}},headers:a});if(e.error)throw h(e.error,"getChatConversation");return e.data};getSpeechToText=async(t,a)=>{let e=await this._authHeaders(),r=new FormData;r.append("voiceFile",a,"voice.wav");let i=await this._client.POST("/configurations/{configurationId}/chat/stt",{params:{path:{configurationId:t}},headers:e,body:r,bodySerializer:()=>r});if(i.error)throw h(i.error,"getSpeechToText");return i.data.text};getTextToSpeech=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.POST("/configurations/{configurationId}/chat/tts",{params:{path:{configurationId:t}},body:{content:a},headers:e,parseAs:"arrayBuffer"});if(r.error)throw h(r.error,"getTextToSpeech");return new Blob([r.data],{type:"audio/mp3"})};getAssistantChatConnection=t=>({url:new URL(`configurations/${encodeURIComponent(t)}/assistant/chat`,`${this._baseUrl}/`).toString(),headers:this._authHeaders});compactAssistantThread=async t=>{let a=await this._authHeaders(),e=await this._client.POST("/configurations/{configurationId}/assistant/chat/compact",{params:{path:{configurationId:t}},headers:{...a,"Content-Type":null}});if(e.error)throw new i(`compactAssistantThread API request failed: ${JSON.stringify(e.error)}`,e.response.status);return e.data};getAssistantEarlierThread=async(t,a,e)=>{let r=await this._authHeaders(),o=await this._client.GET("/configurations/{configurationId}/assistant/chat/threads/{threadId}",{params:{path:{configurationId:t,threadId:a},query:{limit:e}},headers:r});if(o.error)throw new i(`getAssistantEarlierThread API request failed: ${JSON.stringify(o.error)}`,o.response.status);let{messages:n}=o.data;return{threadId:a,messages:Array.isArray(n)?n:[],previousThreadId:o.response.headers.get("x-assistant-previous-thread")}};sendMessage=async(t,a,e)=>{let r=await this._authHeaders(),i=await this._client.POST("/configurations/{configurationId}/chat/message",{params:{path:{configurationId:t}},body:{content:a,context:e},headers:r,parseAs:"stream"});if(i.error)throw h(i.error,"sendMessage");let o=i.data?.pipeThrough(new TextDecoderStream).getReader();if(!o)throw Error("sendMessage API response did not contain a readable stream");let c=n();return s(o,c.writable.getWriter()),c.readable};sendToolCallResults=async(t,a,e,r)=>{let i=await this._authHeaders(),o=await this._client.POST("/configurations/{configurationId}/chat/tool-calls",{params:{path:{configurationId:t}},body:{agent:r,calls:a,results:e},headers:i,parseAs:"stream"});if(o.error)throw h(o.error,"sendToolCallResults");let c=o.data?.pipeThrough(new TextDecoderStream).getReader();if(!c)throw Error("sendMessage API response did not contain a readable stream");let u=n();return s(c,u.writable.getWriter()),u.readable};generateShareLink=async(t,a,e,r,i)=>{let o=await this._authHeaders(),n=await this._client.POST("/configurations/{configurationId}/share-link",{params:{path:{configurationId:t}},body:{revisionId:a,expiration:e,access:r,baseUrl:i},headers:o});if(n.error)throw h(n.error,"generateShareLink");return n.data};generateUploadLinks=async(t,a,e)=>{let r=await this._authHeaders(),i=await this._client.POST("/configurations/{configurationId}/{finalizationUuid}/image-upload-links",{params:{path:{configurationId:t,finalizationUuid:a}},body:{names:e},headers:r});if(i.error)throw h(i.error,"generateUploadLinks");return i.data.links};getConfigurationPublicUploadLink=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/resources/{key}/public/upload",{params:{path:{configurationId:t,key:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationPublicUploadLink");return r.data};getConfigurationPrivateUploadLink=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/resources/{key}/private/upload",{params:{path:{configurationId:t,key:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationPrivateUploadLink");return r.data};getConfigurationPrivateResourceLink=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/resources/{key}/private",{params:{path:{configurationId:t,key:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationPrivateUploadLink");return r.data};cloneConfiguration=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.POST("/configurations/{configurationId}/revisions/{revisionId}/clone",{params:{path:{configurationId:t,revisionId:a}},headers:{...e,"Content-Type":null}});if(r.error)throw h(r.error,"cloneConfiguration");return r.data}}
1
+ import{match as t}from"ts-pattern";import{InfoError as a}from"@salsita-ai/common/utils";import{isChatError as e}from"@salsita-ai/contracts/chat";import{createApiBaseClient as r}from"./base-client.js";import{AssistantRequestError as i,InvalidAccessKeyError as s}from"./contract.js";let o=()=>new TransformStream({transform(t,a){a.enqueue(t)}}),n=async(t,a)=>{for(;;){let{done:r,value:i}=await t.read();if(r){await a.close();break}for(let t of i.split("\n").filter(t=>""!==t.trim()).map(t=>JSON.parse(t))){if(e(t))return void await a.abort(Error(t.error));await a.write(t)}}},h=(e,r)=>{let i=`${r} API request failed: ${JSON.stringify(e)}`;return t(e).with({statusCode:403,invalidAccessKey:!0},()=>new s).with({statusCode:403},()=>new a(i)).otherwise(()=>Error(i))};export class AuthorizedClient{_baseUrl;_getAccessToken;_getSessionToken;_client;constructor(t,a,e){this._baseUrl=t,this._getAccessToken=a,this._getSessionToken=e,this._client=r(this._baseUrl)}_authHeaders=async()=>{let t=this._getAccessToken(),a=await this._getSessionToken();return{"X-Access-Key":t,Authorization:`Bearer ${a}`}};getConfiguration=async t=>{let a=await this._authHeaders(),e=await this._client.GET("/configurations/{configurationId}",{params:{path:{configurationId:t}},headers:a});if(e.error)throw h(e.error,"getConfiguration");return e.data};updateConfiguration=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.PUT("/configurations/{configurationId}",{params:{path:{configurationId:t}},body:a,headers:e});if(r.error)throw h(r.error,"updateConfiguration");return r.data};finalize=async(t,a,e,r,i)=>{let s=await this._authHeaders(),o=await this._client.POST("/configurations/{configurationId}/{finalizationUuid}/finalize",{params:{path:{configurationId:t,finalizationUuid:a},query:{locale:r}},body:{finalizeData:e,frontendData:i},headers:s});if(o.error)throw h(o.error,"finalize")};waitForFinalizationToComplete=async(t,a,e)=>{let r=e?.pollingFrequency??500,i=Math.ceil((e?.pollingTimeout??3e4)/r);for(let e=0;e<i;e++){let e=await this._authHeaders(),i=await this._client.GET("/configurations/{configurationId}/{finalizationUuid}/state",{params:{path:{configurationId:t,finalizationUuid:a}},headers:e});if(i.error)throw h(i.error,"getFinalizeJobState");let s=i.data;if("completed"===s.state)return s.result[0];if("failed"===s.state)throw Error("finalization failed");if("unknown"===s.state)throw Error("finalization state is unknown");await new Promise(t=>setTimeout(t,r))}throw Error("finalization did not complete within the specified timeout")};getConfigurationRevision=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/revisions/{revisionId}",{params:{path:{configurationId:t,revisionId:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationRevision");return r.data};getChatConversation=async t=>{let a=await this._authHeaders(),e=await this._client.GET("/configurations/{configurationId}/chat",{params:{path:{configurationId:t}},headers:a});if(e.error)throw h(e.error,"getChatConversation");return e.data};getSpeechToText=async(t,a)=>{let e=await this._authHeaders(),r=new FormData;r.append("voiceFile",a,"voice.wav");let i=await this._client.POST("/configurations/{configurationId}/chat/stt",{params:{path:{configurationId:t}},headers:e,body:r,bodySerializer:()=>r});if(i.error)throw h(i.error,"getSpeechToText");return i.data.text};getTextToSpeech=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.POST("/configurations/{configurationId}/chat/tts",{params:{path:{configurationId:t}},body:{content:a},headers:e,parseAs:"arrayBuffer"});if(r.error)throw h(r.error,"getTextToSpeech");return new Blob([r.data],{type:"audio/mp3"})};getAssistantChatConnection=t=>({url:new URL(`configurations/${encodeURIComponent(t)}/assistant/chat`,`${this._baseUrl}/`).toString(),headers:this._authHeaders});compactAssistantThread=async t=>{let a=await this._authHeaders(),e=await this._client.POST("/configurations/{configurationId}/assistant/chat/compact",{params:{path:{configurationId:t}},headers:{...a,"Content-Type":null}});if(e.error)throw new i(`compactAssistantThread API request failed: ${JSON.stringify(e.error)}`,e.response.status);return e.data};getAssistantEarlierThread=async(t,a,e)=>{let r=await this._authHeaders(),s=await this._client.GET("/configurations/{configurationId}/assistant/chat/threads/{threadId}",{params:{path:{configurationId:t,threadId:a},query:{limit:e}},headers:r});if(s.error)throw new i(`getAssistantEarlierThread API request failed: ${JSON.stringify(s.error)}`,s.response.status);let{messages:o}=s.data;return{threadId:a,messages:Array.isArray(o)?o:[],previousThreadId:s.response.headers.get("x-assistant-previous-thread")}};uploadAssistantImage=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.POST("/configurations/{configurationId}/assistant/images",{params:{path:{configurationId:t}},headers:{...e,"Content-Type":a.type},body:a.type,bodySerializer:()=>a});if(r.error)throw new i(`uploadAssistantImage API request failed: ${JSON.stringify(r.error)}`,r.response.status);return{imageId:r.data.imageId}};getAssistantImage=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/assistant/images/{imageId}",{params:{path:{configurationId:t,imageId:a}},headers:e,parseAs:"blob"});if(r.error)throw new i(`getAssistantImage API request failed: ${JSON.stringify(r.error)}`,r.response.status);return r.data};sendMessage=async(t,a,e)=>{let r=await this._authHeaders(),i=await this._client.POST("/configurations/{configurationId}/chat/message",{params:{path:{configurationId:t}},body:{content:a,context:e},headers:r,parseAs:"stream"});if(i.error)throw h(i.error,"sendMessage");let s=i.data?.pipeThrough(new TextDecoderStream).getReader();if(!s)throw Error("sendMessage API response did not contain a readable stream");let u=o();return n(s,u.writable.getWriter()),u.readable};sendToolCallResults=async(t,a,e,r)=>{let i=await this._authHeaders(),s=await this._client.POST("/configurations/{configurationId}/chat/tool-calls",{params:{path:{configurationId:t}},body:{agent:r,calls:a,results:e},headers:i,parseAs:"stream"});if(s.error)throw h(s.error,"sendToolCallResults");let u=s.data?.pipeThrough(new TextDecoderStream).getReader();if(!u)throw Error("sendMessage API response did not contain a readable stream");let c=o();return n(u,c.writable.getWriter()),c.readable};generateShareLink=async(t,a,e,r,i)=>{let s=await this._authHeaders(),o=await this._client.POST("/configurations/{configurationId}/share-link",{params:{path:{configurationId:t}},body:{revisionId:a,expiration:e,access:r,baseUrl:i},headers:s});if(o.error)throw h(o.error,"generateShareLink");return o.data};generateUploadLinks=async(t,a,e)=>{let r=await this._authHeaders(),i=await this._client.POST("/configurations/{configurationId}/{finalizationUuid}/image-upload-links",{params:{path:{configurationId:t,finalizationUuid:a}},body:{names:e},headers:r});if(i.error)throw h(i.error,"generateUploadLinks");return i.data.links};getConfigurationPublicUploadLink=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/resources/{key}/public/upload",{params:{path:{configurationId:t,key:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationPublicUploadLink");return r.data};getConfigurationPrivateUploadLink=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/resources/{key}/private/upload",{params:{path:{configurationId:t,key:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationPrivateUploadLink");return r.data};getConfigurationPrivateResourceLink=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.GET("/configurations/{configurationId}/resources/{key}/private",{params:{path:{configurationId:t,key:a}},headers:e});if(r.error)throw h(r.error,"getConfigurationPrivateUploadLink");return r.data};cloneConfiguration=async(t,a)=>{let e=await this._authHeaders(),r=await this._client.POST("/configurations/{configurationId}/revisions/{revisionId}/clone",{params:{path:{configurationId:t,revisionId:a}},headers:{...e,"Content-Type":null}});if(r.error)throw h(r.error,"cloneConfiguration");return r.data}}
@@ -100,6 +100,18 @@ export interface AuthorizedPhoenixApiClient<FINALIZE_DATA extends Record<string,
100
100
  * `limit` of its newest messages. Rejects with an {@link AssistantRequestError} when the server refuses.
101
101
  */
102
102
  getAssistantEarlierThread: (configurationId: string, threadId: string, limit: number) => Promise<AssistantEarlierThread>;
103
+ /**
104
+ * Stores a screenshot the assistant took in the open assistant thread of the configuration, while a
105
+ * reply runs there, and resolves with the id its tool result references it by. The image is the
106
+ * rendered JPEG or PNG blob, sent with its own type. Rejects with an {@link AssistantRequestError}
107
+ * when the server refuses.
108
+ */
109
+ uploadAssistantImage: (configurationId: string, image: Blob) => Promise<AssistantImageUpload>;
110
+ /**
111
+ * Reads a screenshot of an assistant thread of the configuration by the id its tool result
112
+ * references it by. Rejects with an {@link AssistantRequestError} when the server refuses.
113
+ */
114
+ getAssistantImage: (configurationId: string, imageId: string) => Promise<Blob>;
103
115
  getSpeechToText: (configurationId: string, blob: Blob) => Promise<string>;
104
116
  getTextToSpeech: (configurationId: string, text: string) => Promise<Blob>;
105
117
  sendMessage: (configurationId: string, message: string, state?: Record<string, string>) => Promise<ReadableStream<ChatStreamChunk>>;
@@ -137,6 +149,11 @@ export interface AssistantThreadCompaction {
137
149
  /** The closed thread the new one continues. */
138
150
  readonly previousThreadId: string;
139
151
  }
152
+ /** A screenshot stored in an assistant thread. */
153
+ export interface AssistantImageUpload {
154
+ /** The id a tool result references the image by, as `assistant-image:<imageId>`. */
155
+ readonly imageId: string;
156
+ }
140
157
  /** A closed assistant thread, as the panel paints it above the thread that continues it. */
141
158
  export interface AssistantEarlierThread {
142
159
  readonly threadId: string;
@@ -9,5 +9,5 @@ export { MemoryAuthorizedPhoenixApiClient } from './memory-authorized-client.js'
9
9
  export { MemoryAuthenticatedClientPhoenixApiClient, memoryStorage } from './memory-authenticated-client.js';
10
10
  export { createUnsignedJWT } from './unsigned-jwt.js';
11
11
  export type { components as ApiTypes } from './phoenix-api.js';
12
- export type { ConfigurationPayload, ConfigurationSchema, ChatConfiguration, ChatConversation, ChatEmbedding, ChatMessageParam, ChatToolCallResult, Entity, UnauthenticatedPhoenixApiClient, AuthenticatedClientPhoenixApiClient, AuthorizedPhoenixApiClient, AssistantChatConnection, AssistantEarlierThread, AssistantThreadCompaction, OAuthProvider, OAuthAccessTokenResult, GoogleOAuthConfig, ReleaseToProductionResult, SharePointSiteConfig, } from './contract.js';
12
+ export type { ConfigurationPayload, ConfigurationSchema, ChatConfiguration, ChatConversation, ChatEmbedding, ChatMessageParam, ChatToolCallResult, Entity, UnauthenticatedPhoenixApiClient, AuthenticatedClientPhoenixApiClient, AuthorizedPhoenixApiClient, AssistantChatConnection, AssistantEarlierThread, AssistantImageUpload, AssistantThreadCompaction, OAuthProvider, OAuthAccessTokenResult, GoogleOAuthConfig, ReleaseToProductionResult, SharePointSiteConfig, } from './contract.js';
13
13
  export { AssistantRequestError, InvalidAccessKeyError, InvalidDeploymentError, ChunkNotFoundError, OAuthStaleConnectionError, ConfigurationSchemaMismatchError, isConfigurationSchemaMismatchError, } from './contract.js';
@@ -1,5 +1,5 @@
1
1
  import type { ChatMessageContentChunk, ChatToolCalls } from '@salsita-ai/contracts/chat';
2
- import type { AssistantChatConnection, AssistantEarlierThread, AssistantThreadCompaction, AuthorizedPhoenixApiClient, ConfigurationPayload, ConfigurationSchema } from './contract.js';
2
+ import type { AssistantChatConnection, AssistantEarlierThread, AssistantImageUpload, AssistantThreadCompaction, AuthorizedPhoenixApiClient, ConfigurationPayload, ConfigurationSchema } from './contract.js';
3
3
  import { LocalStorageAuthenticatedClientPhoenixApiClient } from './local-authenticated-client.js';
4
4
  /**
5
5
  * Local storage implementation for local dev, it does not check for schema changes, so you may need to create new configuration if the schema changes and
@@ -17,6 +17,8 @@ export declare class LocalStorageAuthorizedPhoenixApiClient<FINALIZE_DATA extend
17
17
  getAssistantChatConnection: () => AssistantChatConnection;
18
18
  compactAssistantThread: () => Promise<AssistantThreadCompaction>;
19
19
  getAssistantEarlierThread: () => Promise<AssistantEarlierThread>;
20
+ uploadAssistantImage: () => Promise<AssistantImageUpload>;
21
+ getAssistantImage: () => Promise<Blob>;
20
22
  getChatConversation: (configurationId: string) => Promise<readonly {
21
23
  readonly augmentation?: import("./phoenix-api.js").components['schemas']['ChatMessageAugmentation'];
22
24
  readonly content?: string;
@@ -1 +1 @@
1
- import*as t from"uuid";import{ChunkType as e}from"@salsita-ai/contracts/chat";import{LocalStorageAuthenticatedClientPhoenixApiClient as o}from"./local-authenticated-client.js";let r=(t,o)=>{let r={role:"assistant",content:["I see","Yes","Definitely"][Math.round(2*Math.random())]};return localStorage.setItem(`${o}-chat`,JSON.stringify([...t,r])),Promise.resolve(new ReadableStream({start(t){t.enqueue({type:e.content,content:r.content??""}),t.close()}}))};export class LocalStorageAuthorizedPhoenixApiClient extends o{getConfigurationData=t=>{let e=JSON.parse(localStorage.getItem(t)??"[]");if(!e||0===e.length)throw Error("Configuration not found!");return e};getConfiguration=t=>{let e=this.getConfigurationData(t);return Promise.resolve(e[e.length-1])};updateConfiguration=(e,o)=>{let r=this.getConfigurationData(e),n=t.v4(),a={...o,configurationId:e,revisionId:n,modelVersion:this._modelVersion};return localStorage.setItem(e,JSON.stringify([...r,a])),Promise.resolve({revisionId:n})};finalize=()=>{throw Error("Not implemented!")};waitForFinalizationToComplete=()=>{throw Error("Not implemented!")};getConfigurationRevision=(t,e)=>{let o=this.getConfigurationData(t).find(t=>t.revisionId===e);if(!o)throw Error("Revision not found!");return Promise.resolve(o)};getAssistantChatConnection=()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};compactAssistantThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getAssistantEarlierThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getChatConversation=async t=>JSON.parse(localStorage.getItem(`${t}-chat`)??"[]");sendMessage=async(t,e)=>{let o=await this.getChatConversation(t);return localStorage.setItem(`${t}-chat`,JSON.stringify([...o,e])),r(o,t)};getSpeechToText=async()=>{throw Error("Not implemented!")};getTextToSpeech=async()=>{throw Error("Not implemented!")};sendToolCallResults=async t=>r(await this.getChatConversation(t),t);generateShareLink=async()=>({link:"https://yourcompany.com/configurator/link-will-not-work-it-is-dummy-link"});generateUploadLinks=async()=>{throw Error("Not implemented!")};getConfigurationPublicUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateResourceLink=async()=>{throw Error("Not implemented!")};cloneConfiguration=async(t,e)=>{let o=localStorage.getItem(t);if(!o)throw Error("Configuration not found");let r=JSON.parse(o).find(t=>t.revisionId===e);if(!r)throw Error("Revision not found");return this.createConfiguration(r)}}
1
+ import*as t from"uuid";import{ChunkType as e}from"@salsita-ai/contracts/chat";import{LocalStorageAuthenticatedClientPhoenixApiClient as r}from"./local-authenticated-client.js";let o=(t,r)=>{let o={role:"assistant",content:["I see","Yes","Definitely"][Math.round(2*Math.random())]};return localStorage.setItem(`${r}-chat`,JSON.stringify([...t,o])),Promise.resolve(new ReadableStream({start(t){t.enqueue({type:e.content,content:o.content??""}),t.close()}}))};export class LocalStorageAuthorizedPhoenixApiClient extends r{getConfigurationData=t=>{let e=JSON.parse(localStorage.getItem(t)??"[]");if(!e||0===e.length)throw Error("Configuration not found!");return e};getConfiguration=t=>{let e=this.getConfigurationData(t);return Promise.resolve(e[e.length-1])};updateConfiguration=(e,r)=>{let o=this.getConfigurationData(e),n=t.v4(),a={...r,configurationId:e,revisionId:n,modelVersion:this._modelVersion};return localStorage.setItem(e,JSON.stringify([...o,a])),Promise.resolve({revisionId:n})};finalize=()=>{throw Error("Not implemented!")};waitForFinalizationToComplete=()=>{throw Error("Not implemented!")};getConfigurationRevision=(t,e)=>{let r=this.getConfigurationData(t).find(t=>t.revisionId===e);if(!r)throw Error("Revision not found!");return Promise.resolve(r)};getAssistantChatConnection=()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};compactAssistantThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getAssistantEarlierThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};uploadAssistantImage=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getAssistantImage=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getChatConversation=async t=>JSON.parse(localStorage.getItem(`${t}-chat`)??"[]");sendMessage=async(t,e)=>{let r=await this.getChatConversation(t);return localStorage.setItem(`${t}-chat`,JSON.stringify([...r,e])),o(r,t)};getSpeechToText=async()=>{throw Error("Not implemented!")};getTextToSpeech=async()=>{throw Error("Not implemented!")};sendToolCallResults=async t=>o(await this.getChatConversation(t),t);generateShareLink=async()=>({link:"https://yourcompany.com/configurator/link-will-not-work-it-is-dummy-link"});generateUploadLinks=async()=>{throw Error("Not implemented!")};getConfigurationPublicUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateResourceLink=async()=>{throw Error("Not implemented!")};cloneConfiguration=async(t,e)=>{let r=localStorage.getItem(t);if(!r)throw Error("Configuration not found");let o=JSON.parse(r).find(t=>t.revisionId===e);if(!o)throw Error("Revision not found");return this.createConfiguration(o)}}
@@ -1,4 +1,4 @@
1
- import type { AssistantChatConnection, AssistantEarlierThread, AssistantThreadCompaction, AuthorizedPhoenixApiClient, ConfigurationPayload, ConfigurationSchema } from './contract.js';
1
+ import type { AssistantChatConnection, AssistantEarlierThread, AssistantImageUpload, AssistantThreadCompaction, AuthorizedPhoenixApiClient, ConfigurationPayload, ConfigurationSchema } from './contract.js';
2
2
  import { MemoryAuthenticatedClientPhoenixApiClient } from './memory-authenticated-client.js';
3
3
  /**
4
4
  * Memory implementation for local dev, it does not check for schema changes, so you may need to create new configuration if the schema changes and
@@ -17,6 +17,9 @@ export declare class MemoryAuthorizedPhoenixApiClient<FINALIZE_DATA extends Reco
17
17
  getAssistantChatConnection: () => AssistantChatConnection;
18
18
  compactAssistantThread: () => Promise<AssistantThreadCompaction>;
19
19
  getAssistantEarlierThread: () => Promise<AssistantEarlierThread>;
20
+ private readonly _assistantImages;
21
+ uploadAssistantImage: (configurationId: string, image: Blob) => Promise<AssistantImageUpload>;
22
+ getAssistantImage: (configurationId: string, imageId: string) => Promise<Blob>;
20
23
  sendMessage: () => Promise<never>;
21
24
  getSpeechToText: () => Promise<never>;
22
25
  getTextToSpeech: () => Promise<never>;
@@ -1 +1 @@
1
- import*as e from"uuid";import{MemoryAuthenticatedClientPhoenixApiClient as t,memoryStorage as r}from"./memory-authenticated-client.js";export class MemoryAuthorizedPhoenixApiClient extends t{getConfigurationData=e=>{let t=r.get(e);if(!t||!t||0===t.length)throw Error("Configuration not found!");return t};getConfiguration=e=>{let t=this.getConfigurationData(e);return Promise.resolve(t[t.length-1])};updateConfiguration=(t,o)=>{let n=this.getConfigurationData(t),i=e.v4(),a={...o,configurationId:t,revisionId:i,modelVersion:this._modelVersion};return r.set(t,[...n,a]),Promise.resolve({revisionId:i})};finalize=()=>{throw Error("Not implemented!")};waitForFinalizationToComplete=()=>{throw Error("Not implemented!")};getConfigurationRevision=(e,t)=>{let r=this.getConfigurationData(e).find(e=>e.revisionId===t);if(!r)throw Error("Revision not found!");return Promise.resolve(r)};getChatConversation=async()=>{throw Error("Not implemented!")};getAssistantChatConnection=()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};compactAssistantThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getAssistantEarlierThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};sendMessage=async()=>{throw Error("Not implemented!")};getSpeechToText=async()=>{throw Error("Not implemented!")};getTextToSpeech=async()=>{throw Error("Not implemented!")};sendToolCallResults=async()=>{throw Error("Not implemented!")};generateShareLink=async()=>({link:"https://yourcompany.com/configurator/link-will-not-work-it-is-dummy-link"});generateUploadLinks=async()=>{throw Error("Not implemented!")};getConfigurationPublicUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateResourceLink=async()=>{throw Error("Not implemented!")};cloneConfiguration=async(e,t)=>{let o=r.get(e);if(!o)throw Error("Configuration not found");let n=o.find(e=>e.revisionId===t);if(!n)throw Error("Revision not found");return this.createConfiguration(n)}}
1
+ import*as t from"uuid";import{MemoryAuthenticatedClientPhoenixApiClient as e,memoryStorage as r}from"./memory-authenticated-client.js";export class MemoryAuthorizedPhoenixApiClient extends e{getConfigurationData=t=>{let e=r.get(t);if(!e||!e||0===e.length)throw Error("Configuration not found!");return e};getConfiguration=t=>{let e=this.getConfigurationData(t);return Promise.resolve(e[e.length-1])};updateConfiguration=(e,o)=>{let n=this.getConfigurationData(e),i=t.v4(),a={...o,configurationId:e,revisionId:i,modelVersion:this._modelVersion};return r.set(e,[...n,a]),Promise.resolve({revisionId:i})};finalize=()=>{throw Error("Not implemented!")};waitForFinalizationToComplete=()=>{throw Error("Not implemented!")};getConfigurationRevision=(t,e)=>{let r=this.getConfigurationData(t).find(t=>t.revisionId===e);if(!r)throw Error("Revision not found!");return Promise.resolve(r)};getChatConversation=async()=>{throw Error("Not implemented!")};getAssistantChatConnection=()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};compactAssistantThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};getAssistantEarlierThread=async()=>{throw Error("The assistant chat needs the Phoenix server. Run the configurator against a server deployment.")};_assistantImages=new Map;uploadAssistantImage=async(e,r)=>{let o=t.v4();return this._assistantImages.set(`${e}/${o}`,r),{imageId:o}};getAssistantImage=async(t,e)=>{let r=this._assistantImages.get(`${t}/${e}`);if(!r)throw Error(`Assistant image ${e} was not uploaded for configuration ${t} through this client. Upload it before reading it.`);return r};sendMessage=async()=>{throw Error("Not implemented!")};getSpeechToText=async()=>{throw Error("Not implemented!")};getTextToSpeech=async()=>{throw Error("Not implemented!")};sendToolCallResults=async()=>{throw Error("Not implemented!")};generateShareLink=async()=>({link:"https://yourcompany.com/configurator/link-will-not-work-it-is-dummy-link"});generateUploadLinks=async()=>{throw Error("Not implemented!")};getConfigurationPublicUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateUploadLink=async()=>{throw Error("Not implemented!")};getConfigurationPrivateResourceLink=async()=>{throw Error("Not implemented!")};cloneConfiguration=async(t,e)=>{let o=r.get(t);if(!o)throw Error("Configuration not found");let n=o.find(t=>t.revisionId===e);if(!n)throw Error("Revision not found");return this.createConfiguration(n)}}
@@ -379,6 +379,40 @@ export interface paths {
379
379
  readonly patch?: never
380
380
  readonly trace?: never
381
381
  }
382
+ readonly '/configurations/{configurationId}/assistant/images': {
383
+ readonly parameters: {
384
+ readonly query?: never
385
+ readonly header?: never
386
+ readonly path?: never
387
+ readonly cookie?: never
388
+ }
389
+ readonly get?: never
390
+ readonly put?: never
391
+ /** @description Store a screenshot the browser took for the assistant in the caller's open thread on the configuration, while a run of the assistant answers there. The body is the raw JPEG or PNG image, with its type as the content type. The answer carries the id that tool results reference the image by, as `assistant-image:<imageId>`. Answers 409 when the caller has no open thread on the configuration or the thread is not in the middle of a reply (no run running and no client tool call waiting), and 415 for any other content type. */
392
+ readonly post: operations['uploadAssistantImage']
393
+ readonly delete?: never
394
+ readonly options?: never
395
+ readonly head?: never
396
+ readonly patch?: never
397
+ readonly trace?: never
398
+ }
399
+ readonly '/configurations/{configurationId}/assistant/images/{imageId}': {
400
+ readonly parameters: {
401
+ readonly query?: never
402
+ readonly header?: never
403
+ readonly path?: never
404
+ readonly cookie?: never
405
+ }
406
+ /** @description Load a screenshot of one of the caller's assistant threads on the configuration, the earlier threads of the chain included. The bytes never change, so the answer may be cached for good. Answers 404 for an image that is not in one of the caller's threads on this configuration. */
407
+ readonly get: operations['getAssistantImage']
408
+ readonly put?: never
409
+ readonly post?: never
410
+ readonly delete?: never
411
+ readonly options?: never
412
+ readonly head?: never
413
+ readonly patch?: never
414
+ readonly trace?: never
415
+ }
382
416
  readonly '/configurations/{configurationId}/chat': {
383
417
  readonly parameters: {
384
418
  readonly query?: never
@@ -1503,6 +1537,13 @@ export interface components {
1503
1537
  /** @enum {number} */
1504
1538
  readonly statusCode: 401
1505
1539
  }
1540
+ /** @description Unsupported Media Type: The request body is of a content type the route does not accept. */
1541
+ readonly UnsupportedMediaTypeError: {
1542
+ /** @example Unsupported Media Type! */
1543
+ readonly message: string
1544
+ /** @enum {number} */
1545
+ readonly statusCode: 415
1546
+ }
1506
1547
  /** @description A webhook resource to which the deployment can send requests. */
1507
1548
  readonly Webhook: {
1508
1549
  /**
@@ -3397,6 +3438,193 @@ export interface operations {
3397
3438
  }
3398
3439
  }
3399
3440
  }
3441
+ readonly uploadAssistantImage: {
3442
+ readonly parameters: {
3443
+ readonly query?: never
3444
+ readonly header?: never
3445
+ readonly path: {
3446
+ /** @description The configuration on which the caller's assistant thread is used. */
3447
+ readonly configurationId: components['schemas']['ConfigurationUuid']
3448
+ }
3449
+ readonly cookie?: never
3450
+ }
3451
+ /** @description The JPEG or PNG image. */
3452
+ readonly requestBody: {
3453
+ readonly content: {
3454
+ readonly 'image/jpeg': string
3455
+ readonly 'image/png': string
3456
+ }
3457
+ }
3458
+ readonly responses: {
3459
+ /** @description The image was stored in the thread. */
3460
+ readonly 200: {
3461
+ headers: {
3462
+ readonly [name: string]: unknown
3463
+ }
3464
+ content: {
3465
+ readonly 'application/json': {
3466
+ /**
3467
+ * Format: uuid
3468
+ * @description The id tool results reference the image by.
3469
+ */
3470
+ readonly imageId: string
3471
+ }
3472
+ }
3473
+ }
3474
+ /** @description Bad Request! */
3475
+ readonly 400: {
3476
+ headers: {
3477
+ readonly [name: string]: unknown
3478
+ }
3479
+ content: {
3480
+ readonly 'application/json': components['schemas']['BadRequestError']
3481
+ }
3482
+ }
3483
+ /** @description Unauthorized! */
3484
+ readonly 401: {
3485
+ headers: {
3486
+ readonly [name: string]: unknown
3487
+ }
3488
+ content: {
3489
+ readonly 'application/json': components['schemas']['UnauthorizedError']
3490
+ }
3491
+ }
3492
+ /** @description Forbidden! */
3493
+ readonly 403: {
3494
+ headers: {
3495
+ readonly [name: string]: unknown
3496
+ }
3497
+ content: {
3498
+ readonly 'application/json': components['schemas']['ForbiddenError']
3499
+ }
3500
+ }
3501
+ /** @description Not Found! */
3502
+ readonly 404: {
3503
+ headers: {
3504
+ readonly [name: string]: unknown
3505
+ }
3506
+ content: {
3507
+ readonly 'application/json': components['schemas']['NotFoundError']
3508
+ }
3509
+ }
3510
+ /** @description Conflict! */
3511
+ readonly 409: {
3512
+ headers: {
3513
+ readonly [name: string]: unknown
3514
+ }
3515
+ content: {
3516
+ readonly 'application/json': components['schemas']['ConflictError']
3517
+ }
3518
+ }
3519
+ /** @description Unsupported Media Type! */
3520
+ readonly 415: {
3521
+ headers: {
3522
+ readonly [name: string]: unknown
3523
+ }
3524
+ content: {
3525
+ readonly 'application/json': components['schemas']['UnsupportedMediaTypeError']
3526
+ }
3527
+ }
3528
+ /** @description Bad Gateway! */
3529
+ readonly 502: {
3530
+ headers: {
3531
+ readonly [name: string]: unknown
3532
+ }
3533
+ content: {
3534
+ readonly 'application/json': components['schemas']['BadGatewayError']
3535
+ }
3536
+ }
3537
+ /** @description An unexpected error occurred! */
3538
+ readonly default: {
3539
+ headers: {
3540
+ readonly [name: string]: unknown
3541
+ }
3542
+ content: {
3543
+ readonly 'application/json': components['schemas']['DefaultResponse']
3544
+ }
3545
+ }
3546
+ }
3547
+ }
3548
+ readonly getAssistantImage: {
3549
+ readonly parameters: {
3550
+ readonly query?: never
3551
+ readonly header?: never
3552
+ readonly path: {
3553
+ /** @description The configuration on which the caller's assistant thread is used. */
3554
+ readonly configurationId: components['schemas']['ConfigurationUuid']
3555
+ /** @description The image, as a tool result references it. */
3556
+ readonly imageId: string
3557
+ }
3558
+ readonly cookie?: never
3559
+ }
3560
+ readonly requestBody?: never
3561
+ readonly responses: {
3562
+ /** @description The image. */
3563
+ readonly 200: {
3564
+ headers: {
3565
+ readonly [name: string]: unknown
3566
+ }
3567
+ content: {
3568
+ readonly 'image/jpeg': unknown
3569
+ readonly 'image/png': unknown
3570
+ }
3571
+ }
3572
+ /** @description Bad Request! */
3573
+ readonly 400: {
3574
+ headers: {
3575
+ readonly [name: string]: unknown
3576
+ }
3577
+ content: {
3578
+ readonly 'application/json': components['schemas']['BadRequestError']
3579
+ }
3580
+ }
3581
+ /** @description Unauthorized! */
3582
+ readonly 401: {
3583
+ headers: {
3584
+ readonly [name: string]: unknown
3585
+ }
3586
+ content: {
3587
+ readonly 'application/json': components['schemas']['UnauthorizedError']
3588
+ }
3589
+ }
3590
+ /** @description Forbidden! */
3591
+ readonly 403: {
3592
+ headers: {
3593
+ readonly [name: string]: unknown
3594
+ }
3595
+ content: {
3596
+ readonly 'application/json': components['schemas']['ForbiddenError']
3597
+ }
3598
+ }
3599
+ /** @description Not Found! */
3600
+ readonly 404: {
3601
+ headers: {
3602
+ readonly [name: string]: unknown
3603
+ }
3604
+ content: {
3605
+ readonly 'application/json': components['schemas']['NotFoundError']
3606
+ }
3607
+ }
3608
+ /** @description Bad Gateway! */
3609
+ readonly 502: {
3610
+ headers: {
3611
+ readonly [name: string]: unknown
3612
+ }
3613
+ content: {
3614
+ readonly 'application/json': components['schemas']['BadGatewayError']
3615
+ }
3616
+ }
3617
+ /** @description An unexpected error occurred! */
3618
+ readonly default: {
3619
+ headers: {
3620
+ readonly [name: string]: unknown
3621
+ }
3622
+ content: {
3623
+ readonly 'application/json': components['schemas']['DefaultResponse']
3624
+ }
3625
+ }
3626
+ }
3627
+ }
3400
3628
  readonly getChatConversation: {
3401
3629
  readonly parameters: {
3402
3630
  readonly query?: never
@@ -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 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};
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 l,ImageRenderingSystem as c,LayoutSystem as S,SketchingSystem as d}from"./systems/index.js";export const createConfiguratorExtensionSelectors=u=>{let{getState:y,domainContextSelectors:C,specSelectors:p,domainServicesSelectors:w,accessSelectors:f,commonServicesSelectors:h}=u,A=e.withTypes()([p.selectConfiguratorSpec,C.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})),I={getConfigurationId:()=>f.getConfiguratorConfigurationId(),getAuthorizedClient:()=>h.getConfiguratorAuthorizedClient(),getAssistantSetup:()=>A(y()),domainContextSelectors:C,domainServicesSelectors:w,specSelectors:p},x={getSystems:()=>Q,getCanvasSystem:()=>z,getCameraControlSystem:()=>D,getAnimationSystem:()=>b,getDragAndDropSystem:()=>v,getImageRenderingSystem:()=>G,getLayoutSystem:()=>T,getSketchingSystem:()=>j,getExportSystem:()=>k,getCameraSystem:()=>F,getGraphQLSystem:()=>L,getAssistantSystem:()=>R},E={...I,configuratorExtensionSelectors:x},z=new i(I),D=new r({configuratorExtensionSelectors:x}),v=new g(E),b=new s(I),k=new m(E),L=new l({...I,renderScreenshot:async(e,t)=>{let[n]=await x.getImageRenderingSystem().generateImagesFromComponents([e],t,{},{awaitSettledState:!0});if(!n)throw Error("The canvas rendered no image.");return n.blob},uploadScreenshot:e=>I.getAuthorizedClient().uploadAssistantImage(I.getConfigurationId(),e)}),R=new o(E),T=new S(I),j=new d(E),F=new a(E),G=new c(E),Q={cameraControlSystem:D,layoutSystem:T,sketchingSystem:j,dragAndDropSystem:v,canvasSystem:z,cameraSystem:F,exportSystem:k,imageRenderingSystem:G,animationSystem:b,graphQLSystem:L,assistantSystem:R};return x};
@@ -136,6 +136,11 @@ export declare class AssistantSystem implements InitializableSystem {
136
136
  */
137
137
  private readonly _applyToolCallIds;
138
138
  private readonly _undoTipToolCallId;
139
+ /**
140
+ * Object URLs of the screenshots read so far, by image id. An uploaded image never changes, so
141
+ * each is read once and kept while the configuration stays loaded.
142
+ */
143
+ private _screenshotUrls;
139
144
  constructor(_context: AssistantSystemContext);
140
145
  /** The conversation. It never completes, and holds the empty chat whenever no chat is active. */
141
146
  readonly chat$: ObservableWithLastValue<AssistantChatSnapshot>;
@@ -231,6 +236,12 @@ export declare class AssistantSystem implements InitializableSystem {
231
236
  * refuses with a conflict is not a failure: the thread is read again and `false` is returned.
232
237
  */
233
238
  resume: () => Promise<boolean>;
239
+ /**
240
+ * An object URL of a screenshot of the conversation, by the id its tool result references it by.
241
+ * It is read from the server through the client of the current chat, once per image; a read that
242
+ * failed is forgotten, so it can be asked for again. Rejects without a chat or when the read fails.
243
+ */
244
+ loadScreenshotUrl: (imageId: string) => Promise<string>;
234
245
  /**
235
246
  * Loads the thread before the oldest painted one and paints it above. Does nothing when there is
236
247
  * none or one is being loaded. A load that fails is logged and can be asked for again.
@@ -300,6 +311,8 @@ export declare class AssistantSystem implements InitializableSystem {
300
311
  * it is: whether it finishes its reply or is cut off is the caller's decision.
301
312
  */
302
313
  private _retireChat;
314
+ /** Revokes the object URLs of the screenshots read for the configuration that was loaded, also those still being read. */
315
+ private _releaseScreenshotUrls;
303
316
  /** Ends the current chat without cutting its reply off. */
304
317
  private _endChat;
305
318
  }