docs
/
AI skills

Build Studio's AI and the page-building AI tools

Every AI action in Build Studio (the AI bar, the element toolbar's Rephrase, Improve, Summarize, Expand, Replace Image, Improve Layout, Add Animation and Add Interaction, and "Describe it, let AI build it"), the engine tool behind each (update_data, page_write, save_page_asset, fetch_asset, generate_image), what each one really writes, which ones are billed, where Appmint's own AI instructions are wrong, and how an agent without the canvas builds pages the same way.

Build Studio has two kinds of AI: a chat (the Build Studio AI bar, the same conversation as the AI Agent panel) that edits the open page through the engine's tools, and one-shot buttons on the element toolbar that ask the AI for suggestions. This page says what each one calls, what it writes, what it costs, and what doesn't work. It ends with how an agent that has no canvas (an AI employee, an outside agent on the API) builds pages with the same tools.

For what a page record looks like, see Build a website page; for screens, A page with several screens.

No AI action was run for this page. Each one spends the business's money and calls an outside model. Everything about the AI is read from the code, with the file and lines named, and marked code. What the site does with the resulting page was tested (the screens, the API writes); those results are in A page with several screens.

Every AI action in Build Studio

Action, and whereWhat it doesEngine callWhat it writesBilled
Build Studio AI bar (bottom right of the canvas; reopened from the toolbar's AI button). Takes a prompt, up to 3 reference images, ↑ for history.A chat turn. The prompt goes with a snapshot of the canvas: the selected element, a list of top-level sections, the body HTML (left out when an element is selected), body class and style, activeScreenId, the screen names, and the result of the AI's last edit (build-studio-ai-bar.tsx 46–145).POST /ai/agent/stream; on /build-studio the engine loads the build-studio and appmint-pages skills (base-agent.ts 3082–3130)through update_data: the canvas only, unsaved (below). Through page_write: the record directly.yes, by tokens
Element toolbar → Rephrase, Improve, Summarize, Expand (select an element; the first button repeats the last one used)Sends the element's text with a prompt for 3 versions, shows them one at a time; Apply puts the chosen one in (AIContextMenu.tsx 87–216, 281–341; ai-actions/text-tool.ts).POST /ai/agent/stream as an ordinary agent request; the engine ignores the quickAction flags it sends (no reader in appengine)the canvas, unsaved. On an element with tags inside (<p>Hi <b>there</b></p>), every piece of text becomes the whole suggestion, so it shows several times (AIContextMenu.tsx 299–316). Use it on a single heading or paragraph.yes, by tokens
Element toolbar → Replace Image (only on an image, or an element holding one)Asks the AI for a search keyword from the HTML around the image, then searches stock photos (Pexels) and shows 5; Apply sets src and alt. The tooltip says "AI generated image"; it is not generated (ai-actions/change-image.ts 145–263).POST /ai/agent/stream for the keyword, then POST /repository/media/photosthe image's src/alt in the canvas, unsaved, pointing at the stock photo's URLthe keyword step, by tokens; the photo search is not AI
Element toolbar → Improve LayoutYou describe the change; it generates new HTML and CSS for the element; Apply Update copies the new root element's attributes and, when the inside changed, puts the new inside in as text, so the markup shows on the page as literal <div …> (AIContextMenu.tsx 913–970; selective-virtual-dom.ts 710–714)POST /ai/agent/streambroken; undo with Ctrl-Zyes, by tokens
Element toolbar → Add AnimationGenerates anime.js code from a template. Apply Animation does nothing: it logs and closes (AIContextMenu.tsx 720–745). Animations that work are made in the animation panel.POST /ai/agent/streamnothingyes, by tokens
Element toolbar → Add InteractionGenerates plain JavaScript; Apply Interaction keeps only attributes starting x-, @ or : from it, which plain JavaScript doesn't have and the site doesn't run (AIContextMenu.tsx 807–845, 823; ai-actions/interaction-action.ts 48)POST /ai/agent/streamnothing usefulyes, by tokens
Element toolbar → SavedSuggestions and images you saved for laternonethe browser's local storage (ai-assist/ai-store.ts 127, 585)no
Describe it, let AI build it (Create your website)Makes an empty page from the brief's site, opens it in Build Studio, and sends the person's brief to the AI Agent once the page is open (setup-wizard/create-website-step.tsx 306–356, send at 352)as the AI baras the AI bar: the canvas, unsavedyes, by tokens
"Make me an image" in the chatThe agent's generate_image tool. Text-to-image goes to the business's AI provider and returns data[0].url (base-agent.ts 106–131). The current OpenAI and Gemini image models answer in base64 with no URL (integrations/open-ai/openai.api.ts 56–90), so the AI gets "Image generated successfully" and no address to put on the page.the provider's image APInothing on the pageyes, per image (upstream/upstream.service.ts 111–113)

Not wired up: the "Choose a section to add" dialog's generator (section-selector-dialog.tsx 45–58 is an empty stub and nothing opens the dialog) and the "Generate Content with AI" dialog (content-generation-store.ts imports generateSiteSectionAction, which ai-actions/index.ts doesn't export). To add a section with AI, ask in the AI bar.

The engine tools behind them

ToolDefined inAvailable whenWhat it writes
update_data with path: "/build-studio"ai/agents/base-agent.ts 2641–2668 (definition 3570)every agent. It only works while the page is open in Build Studio: the engine sends it to the browser and waits up to 15 s for the browser to confirm; without it the result says confirmed: false, "NOT applied on screen"the canvas of the open page, through the Studio's updateAIData (websitemint build-studio-store.tsx 560–830). Never the record.
navigate /build-studiobase-agent.ts 3419–3460every agentopens a page in Studio: { appType: "web", id: "<sk>", pageName }, or { appType: "web" } for a new one
page_writeai/skills/appmint-pages.skill.ts 28–147the appmint-pages skill: loaded on /build-studio, /pages, /site and their sub-routes, or in the build-studio / studio apps (24–25); anywhere else after load_skill appmint-pagesthe record: html into the active screen (or data.html on a page without screens); title, description; the top-level style (javascript, css appended unless replaceStyle; scriptLinks, styleLinks merged)
save_page_assetai/skills/build-studio.skill.ts 751–855 (definition 773)the build-studio skill: on /build-studio (43)a CSS/JS (or any text) file at path in the org's file storage; returns { url, path, contentType, size }, the URL signed and with ?ts= to break caches
fetch_assetbuild-studio.skill.ts 751–855 (definition 755)samenothing; reads an http(s) URL of up to 200 KB, text types only
generate_imagebase-agent.ts 106–131, 1725–1800 (definition 3210)every agentnothing on the page (see above)
repo_find, repo_find_one, repo_create, repo_update, repo_deleteai/skills/repository.skill.ts 30–112the repository skillthe record; repo_update shallow-merges into data only and can't reach style
call_appengineai/skills/api-passthrough.skill.ts 218the API passthrough skillany endpoint, e.g. PUT /repository/create with style
load_skillai/skills/skill-manager.skill.ts 57the skill managerloads appmint-pages (or build-studio) off its routes

update_data on /build-studio, field by field

What the Studio does with each field the AI sends (build-studio-store.tsx 560–830, code):

The AI sendsThe canvasAt Save
html as a full document, with no nodeId or selectorthe active screen is replaced whole: its head goes into the canvas head, its body into the canvasstored as the active screen
html as a fragment, no targetreplaces the canvas body; the screen's head staysstored in the active screen's body
nodeId (or selector) + html, replaceMode outer (default) or innerswaps that element only, then checks the result: wrong nesting, a lost data-node-id or a missing target come back in the next turn's lastUpdate (targetFound, validated, validationError, resolvedNodeId, warnings)stored
bodyStyle, bodyClassset on the canvas <body> (the editor's own classes kept)written into the active screen's <body> tag (selective-virtual-dom.ts 1446–1459)
screens, activeScreenId, or any other keycopied into the Studio's in-memory page only; the canvas's own screen list is not reloadedlost. Save rebuilds screens, activeScreenId and html from the canvas's list (build-studio-store.tsx 410–433)

So the AI can change the screen that is open, but can't add a screen, change another screen, switch the live screen or add animations through update_data, although the skill tells it to (build-studio.skill.ts 498–506, 611–619). Do those in Studio by hand, or through the record (below) with the page closed in Studio.

Every AI change goes onto the undo stack first (Ctrl-Z undoes it), marks the page unsaved, and stays unsaved until the person presses Save. Save also makes the screen open on the canvas the live one (A page with several screens).

page_write and the open canvas don't know about each other. In Build Studio the AI has both tools. page_write saves to the record at once; the open canvas doesn't reload it, and the person's next Save sends the canvas's own copy of the page. If the AI wrote with page_write, close and reopen the page in Studio before anyone saves.

page_write in detail

appmint-pages.skill.ts 57–147, code:

  • Screens page: html replaces the screen named by activeScreenId (or the first). Other screens and data.html are left alone, so data.html is out of date afterwards; the site doesn't read it. The result's notes say "wrote into screen N of M".
  • No screens: html goes into data.html.
  • Old data.sections / data.content page: refused unless convertLegacyContent: true; then the old content is copied to data.legacyContentBackup and cleared.
  • Style: javascript and css are appended to what is there, joined by a blank line; scriptLinks and styleLinks are merged without repeats. replaceStyle: true replaces all four. Running the same call twice adds the code twice.
  • It saves the whole record and returns { success, id, notes, styleAppended, version }. A save is not proof; check the site.

What it costs

  • Every AI call above is billed to the business when it runs on Appmint's shared AI: by the tokens it used, per model, with Appmint's markup; images per image (Service rates). Before each call the engine checks the business's agreement and balance (upstream/upstream.service.ts 820–835); after it, it meters the use (361; usage/service-pricing.service.ts 1494–1555).
  • A business on its own AI key pays its provider directly; Appmint bills nothing for those calls.
  • There is no flat fee per AI request (usage/usage.constants.ts 13–17), so a long chat turn that reads the page and edits several times costs more than one line. Every toolbar button is a full agent request, not a cheaper path.
  • Free: the stock-photo search, Saved suggestions, undo, Save, and anything done by hand on the canvas.

Stop: before running any AI action for the person (a chat turn that will rebuild a page, a text tool, an image), say that it is billed to the business, and wait for yes. Don't generate several versions or retry without asking.

Where Appmint's own AI instructions are wrong

The build-studio and appmint-pages skills are the instructions the in-app AI gets. These parts don't match the code; follow the right-hand column.

The instructions sayWhat is trueProof
wm-drawer: "Attrs: position="left|right|top|bottom", trigger-text, overlay. Content as direct children." wm-dialog: "trigger-text, overlay. Content as children." (build-studio.skill.ts 341–344)The side is direction (default right); there is no trigger-text; both need a slot="trigger" child (it may be hidden) and a slot="content" child, or they never build.custom-elements/drawer.ts 110–121, dialog.ts 165–175; tested in Appmint components on a page
wm-navigation — "top nav bar" (350)Not in the bundle the site loads; it stays an inert tag. Use plain links, wm-mega-menu or a wm-drawer.custom-elements/bundle.ts 16–23
wm-storefront-product-control — "product listing/filter widget" (352–353)One product's options, quantity and buttons. On the site it reads the product from a data-product attribute and only fires events; your code adds to the cart.storefront-product-control.ts 389–408; components page
"wm-drawer and wm-dialog default to overlay="false"" (appmint-pages.skill.ts 375–376)The backdrop shows unless overlay="false" is written.drawer.ts 113, dialog.ts 165
page_write html: "Writing this clears screens/sections/content." (appmint-pages.skill.ts 41)It never clears screens: it writes the active screen. Old sections/content are cleared only with convertLegacyContent: true, after a backup.appmint-pages.skill.ts 67–112
"Short / page-specific scripts → <script> block in <head>" (build-studio.skill.ts 184), and "NO separate styleLinks/scriptLinks/page.style.* fields" (30–31)Page code belongs in Page settings → Style (style.javascript, or a file in scriptLinks). A <script> in the page runs on a full load only, not after the site moves the visitor without a reload, and never inside root's screens. style is also the only CSS the sign-in, account and checkout pages load.Build a website page step 4; JS engine; Built-in pages
"An inline <script> inside data.html NEVER runs" (appmint-pages.skill.ts 210)It ran on a full load (tested, in a screen's head and body); it doesn't after an in-app move. Still don't use it.A page with several screens
Effects notify:…, close-dialog, open-dialog:#id, and refresh (400–402, 450)notify shows nothing on a site page; open-dialog / close-dialog don't open or close; a bare refresh re-runs nothing. Use set-state + data-show, ui.dialog, refresh:#id.Build a website page
data-show="customer.signedIn" and a sign-in link to /signin (471–472)customer.signedIn is true for every visitor; use customer.current.data.email. Sign-in is at /login.Build a website page; site page routing
Change another screen, add a screen or add animations by sending screens through update_data (498–506, 611–619)Dropped at Save (above).build-studio-store.tsx 410–433, 795–797
The AI bar's own header comment names a bs_set_page_html tool (build-studio-ai-bar.tsx 10–15)There is no such tool; edits go through update_data.appengine has no bs_set_page_html

Ask first

  • Which page, on which site? Open it in Build Studio for the chat to edit it.
  • What should change, in their words? One element, one section, or the whole page. Scope decides whether the AI swaps one element or rewrites the page.
  • Is it all right to spend AI credit? Every action is billed (see above), unless the business uses its own AI key.
  • Which screen? The chat edits the screen open on the canvas, and Save makes that screen live.
  • Images: their own photo (upload it to Files first), a stock photo (Replace Image), or a generated one (billed, and it doesn't reach the page today).

Steps

1. Build Studio: open the page on the screen to change

Open /app/build-studio/<page name> (or navigate to /build-studio with { appType: "web", id: "<sk>", pageName: "<name>" }). Click the chip of the screen to change in the screen bar. Select an element on the canvas if the change is to one element.

Hands on: the page's id and the screen open on the canvas. Check: the canvas shows the page, on the right screen; the Build Studio AI bar is at the bottom right (if not, the toolbar's AI button brings it back).

2. Build Studio AI: make the change

Stop: say the change will be billed as AI use, and wait for yes.

Type the request in the AI bar (or, as the in-app AI, call update_data with path: "/build-studio"):

RequestWhat the AI should send
One element selected, any change to it{ "nodeId": "<selectedElement.nodeId>", "html": "<that element, changed>", "replaceMode": "outer" }
A named section ("the pricing section")the same, with the nodeId from sections[]
Only the words inside an elementreplaceMode: "inner" and the new inside
The whole page's background{ "bodyStyle": "…" } with nothing selected
A new page from scratchhtml as a full document, CSS in its <head>, no <script>

For text in one element, the toolbar's Rephrase / Improve / Summarize / Expand are the cheap way: choose, read the three versions, Apply one. For a photo, Replace Image → Apply. Don't use Improve Layout, Add Animation or Add Interaction (see the table at the top).

Hands on: the canvas, changed and unsaved. Check: the canvas shows the change and nothing else changed. When the in-app AI made it, the next turn's context.currentPage.lastUpdate shows success: true; on a partial swap, targetFound and validated are true.

3. Build Studio: Save

The person presses Save. Make sure the screen visitors should see is the one open.

Stop: on a site that serves drafts, Save puts the change live. Say what will change at which address, and wait for yes. On a site with publishedPagesOnly, publishing is its own step (Publish, unpublish and restore a page).

Hands on: the saved page. Check: repo_find_one on the page: the active screen's html has the change, activeScreenId is the screen visitors should see, data.html equals it.

4. Check it on the site

Open https://<site host>/<slug> in a browser, not a grep of the source (the source carries every screen's HTML in a script payload). The change shows; an image from Replace Image loads; text applied with a text tool appears once.

Building pages without the canvas

An AI employee, the AI Agent on another screen, or an agent outside Appmint has no canvas, so update_data does nothing for it. It writes the record, the way Build Studio stores it:

StepIn-app agentOutside agent (REST, header orgid, a signed-in user's token)
Toolsload_skill appmint-pages unless on /build-studio, /pages or /sitenone
Find the pagerepo_find on page, then repo_find_onePOST /repository/find/page, then GET /repository/get/page/<id> (a find result is partial)
A new pagerepo_create on page with screens, activeScreenId, html (shape in Build a website page)PUT /repository/create with { "datatype": "page", "isNew": true, "data": { … }, "style": { … } } (tested)
Change the live screen's bodypage_write { id, html }, then repo_update patch: { html } to keep the mirrorread the record, change data.screens[<active>].html and data.html, then POST /repository/update/<id> with the whole record (tested)
Change another screen, add or reorder screensrepo_update patch: { screens: [ …all of them… ], activeScreenId, html }POST /repository/update-partial/page/<id> with { "data.screens": [ … ], "version": <current> } (tested). Never { "data": { … } }.
Page CSS / JavaScriptpage_write css / javascript (appended), replaceStyle: true to replacethe top-level style in the whole-record update (tested)
A CSS or JS filesave_page_asset, then its URL in styleLinks / scriptLinksPOST /repository/file/upload (location, file), then the URL in style
Checkopen the site address in a browsersame

Every rule on what renders is in Build a website page and A page with several screens.

Stop: a page saved on a site that serves drafts is live at once. Show the URL and what will be on it, and wait for yes.

When it's done

Tell the person: what changed and on which screen; which AI actions ran and that they were billed; that the change is saved (or still waiting for their Save); and the address to see it. If they used an action that doesn't work (Improve Layout, Add Animation, Add Interaction, an image generated in chat), say so and what to do instead.

What it can't do yet

  • The chat can only change the screen open on the canvas. Screens, the live screen and animations sent through update_data are dropped at Save.
  • Nothing the AI does in Studio is saved until a person presses Save, and Save makes the open screen live.
  • Improve Layout breaks the element (its new inside shows as text), Add Animation and Add Interaction apply nothing, but all three are billed.
  • Text tools repeat the suggestion in every piece of text of an element that has tags inside.
  • Replace Image is a stock-photo search, not AI image generation.
  • An image generated in the chat doesn't reach the page with today's image models (no URL comes back), but is billed.
  • No AI section generator outside the chat; the section dialogs are not wired up.
  • page_write while the page is open in Studio can be overwritten by the next Save from the canvas.
  • Appmint's AI instructions are wrong in places (table above); the in-app AI may still follow them.

Don'ts

  • Don't run an AI action without telling the person it is billed.
  • Don't tell the person a change is done before they have pressed Save and you have seen it on the site.
  • Don't send screens or activeScreenId through update_data; change screens in Studio or through the record.
  • Don't use Improve Layout, Add Animation or Add Interaction.
  • Don't apply a text tool to a whole section; select one heading or paragraph.
  • Don't use page_write on a page someone has open in Build Studio.
  • Don't follow the in-app instructions for wm-drawer, wm-dialog, wm-navigation, <script> in the head, notify, or customer.signedIn.
  • Don't put an operator token in a page; page code calls the engine through the site's /api.