You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/features/agent.md
+22-8Lines changed: 22 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,8 +10,8 @@ The agent runs on a provider-agnostic AI runtime (`server/ai/`) that can drive a
10
10
11
11
-**Structure via HTML.**`insertHtml` and `replaceNodeHtml` accept semantic HTML strings; the browser executor calls `importHtml` (the same pipeline as the paste-HTML UI) to convert them into first-class, editable `PageNode`s.
12
12
-**Styling via CSS.** The agent emits CSS the same way a human pastes it: a `<style>` block and/or `class=` attributes inside the `insertHtml`/`replaceNodeHtml` payload, or the standalone `applyCss` tool. The importer (`cssToStyleRules`) classifies every selector — a bare `.foo {}` rule becomes a reusable Selectors-panel class bound to `class="foo"`; any other selector (`.hero a`, `a:hover`, `nav > li`) becomes an ambient rule; `style=` attributes land on the node's inline styles. There is no structured `classes` parameter — the agent never hand-builds classes node-by-node at insert time. `applyCss` is the single tool for authoring/editing CSS on its own; it **upserts**, so re-applying a selector edits the existing rule (the way descendant/pseudo rules get restyled).
13
-
-**29 tools total.**7 server-side read tools (resolved server-side from the posted snapshot / DB) + 22 browser-bridged write tools.
14
-
-**Two-endpoint bridge.**`POST /admin/api/ai/chat/site` opens an NDJSON stream. When the model calls a write tool, the server emits `toolRequest`; the browser executor applies it to the editor store and POSTs the `AiToolOutput` result to `POST /admin/api/ai/tool-result`.
13
+
-**35 tools total.**6 server-side catalog read tools (resolved server-side from the posted snapshot / DB) + 29 browser-bridged tools.
14
+
-**Two-endpoint bridge.**`POST /admin/api/ai/chat/site` opens an NDJSON stream. When the model calls a browser-bridged tool, the server emits `toolRequest`; the browser executor reads or mutates the editor store and POSTs the `AiToolOutput` result to `POST /admin/api/ai/tool-result`.
15
15
-**Provider-agnostic.** The runtime selects a driver (Anthropic, OpenAI, OpenRouter, Ollama) from the conversation's configured credential.
16
16
-**Tool input schemas are a single source of truth** in `@core/ai` (`src/core/ai/toolSchemas.ts`). The server tool registry (`server/ai/tools/site/writeTools.ts`) and the browser executor (`executor.ts` + `tokenRunners.ts`) import the exact same schema objects — a constraint added once is enforced on both sides at build time. Gated by `ai-tool-schema-ssot.test.ts` and `ai-tools-typebox-only.test.ts`.
17
17
-**Capabilities.**`ai.chat` required to stream; `ai.tools.write` required for write tools. Gated by `ai-handlers-capability-gated.test.ts`.
@@ -165,7 +165,7 @@ Server: chat.ts
165
165
│ → browser reads or opens the target page/template/visual component
The two-endpoint design keeps the **browser as editor-store authority** (write tools mutate the live Zustand store in the browser) while the **server runs the model** (driver + tool routing live server-side).
195
+
The two-endpoint design keeps the **browser as editor-store authority** (browser-bridged tools read or mutate the live Zustand store in the browser) while the **server runs the model** (driver + tool routing live server-side).
196
196
197
197
---
198
198
@@ -301,9 +301,9 @@ Resolved server-side from the posted `SiteAgentSnapshot` or the data repositorie
301
301
|`list_loop_sources`| Loop source ids, source fields, order/filter options, and data-table field catalogs with valid `{currentEntry.field}` tokens. For post/custom table loops, use source id `data.rows`, the returned table `id` as `<instatic-loop data-table-id>`, and the returned tokens inside the loop body |
302
302
|`list_tokens`| Design tokens: colors (with shades/tints), typography/spacing scale steps, font tokens — each with CSS variable + utility classes; optional `family` filter (`colors`\|`typography`\|`spacing`\|`fonts`) |
303
303
304
-
### Browser tools — 24, browser-bridged
304
+
### Browser tools — 29, browser-bridged
305
305
306
-
All 24 tools carry `execution: 'browser'` in their `AiTool` definition. The server emits `toolRequest`; the browser executor validates input with TypeBox, runs the store action or read helper, and POSTs the canonical `AiToolOutput` result back.
306
+
All 29 tools carry `execution: 'browser'` in their `AiTool` definition. The server emits `toolRequest`; the browser executor validates input with TypeBox, runs the store action or read helper, and POSTs the canonical `AiToolOutput` result back.
307
307
308
308
**Documents**
309
309
@@ -366,6 +366,20 @@ The agent calls `list_loop_sources` first to get the valid source id, data table
366
366
|`assignClass`|`{ nodeId, classId }`| none | Attach an existing class to a node; `classId` accepts id or name|
367
367
|`removeClass`|`{ nodeId, classId }`| none | Detach a class from a node (the class itself remains) |
368
368
369
+
**Code assets**
370
+
371
+
Scripts and user stylesheets live in `site.files[]`; runtime targeting and loading options live in `site.runtime.scripts` / `site.runtime.styles`. These tools expose that existing Code Editor storage to the agent, so behavior such as theme toggles, tabs, menus, filters, and DOM-ready interactions is authored as a real runtime script instead of attempted through HTML import.
|`list_code_assets`|`{ type?: 'script' \| 'style' }`|`{ assets }`| List runtime code assets with file ids, paths, full-content hashes, sizes, timestamps, and runtime config |
376
+
|`read_code_asset`|`{ fileId? \| path?, part?, maxChars? }`|`{ fileId, path, type, content, hash, runtime, pageInfo }`| Read an exact script/stylesheet content slice. The `hash` is for the full file; page through with `pageInfo.nextPart`|
377
+
|`write_code_asset`|`{ path, type, content, runtime? }`| asset summary + `{ action }`| Create or replace a runtime script/stylesheet and normalize its runtime config. Existing paths are updated, new paths are created |
378
+
|`patch_code_asset`|`{ fileId? \| path?, expectedHash, replacements }`| asset summary + `{ replacements }`| Apply exact text replacements only when `expectedHash` matches the latest content. Ambiguous matches require a wider `oldText` or explicit `replaceAll:true`|
379
+
|`inspect_code_runtime`|`{ document?: { type, id } }`|`{ pageId, document, scripts, styles }`| Report which runtime scripts/stylesheets apply to the current page/template or supplied page/template document ref |
380
+
381
+
`insertHtml` / `replaceNodeHtml` intentionally strip `<script>` elements and inline event handlers (`onclick`, `onload`, etc.). When a request needs behavior, the agent should use `write_code_asset({ type: "script", ... })` and then `inspect_code_runtime`, not raw `<script>` tags or event attributes in HTML.
382
+
369
383
**Pages**
370
384
371
385
| Tool | Input | Success `data`| What it does |
@@ -610,7 +624,7 @@ When `POST /admin/api/ai/credentials` creates a new credential, `seedEmptyDefaul
610
624
-`src/core/ai/documentRefs.ts` — document refs/descriptors for pages, templates, and visual components
611
625
-`src/core/ai/readSurface.ts` — runtime-agnostic `renderAgentDocument` annotated HTML + compact CSS renderer
612
626
-`src/core/ai/index.ts` — barrel re-exporting the above
Copy file name to clipboardExpand all lines: docs/features/html-import.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -142,6 +142,8 @@ Callers splice the fragment into the page tree via `insertImportedNodes(parentId
142
142
|`style="…"` attributes | Declarations harvested onto `node.inlineStyles`; the attribute is removed |
143
143
| HTML comments and processing instructions | Stripped silently — no count |
144
144
145
+
The AI agent should not use stripped constructs for behavior. If an edit needs JavaScript, it writes a real runtime script with `write_code_asset({ type: "script", ... })` and verifies targeting with `inspect_code_runtime` instead of embedding `<script>` or `onclick` in an HTML import.
146
+
145
147
After insert, `ImportHtmlModal` builds a toast body from the added-selector count plus the non-zero stripped counts, e.g. `"3 CSS selectors, stripped 2 <script>"`. If nothing notable happened, the toast shows only the node count.
146
148
147
149
**Inline `style="…"` → `node.inlineStyles`.** Before `stripUnsafe` removes a `style` attribute, `harvestInlineStyles` (`inlineStyle.ts`) reads the element's parsed CSSOM declaration and copies **every** declaration into a camelCase bag, dropping only property names rejected by `isEmittableProperty` (the publisher's security denylist — the same gate `cssToStyleRules` uses). A `url(…)` background is canonicalised to `url('payload')` form so the Super Import asset rewriter and the editor's `BackgroundImageControl` recognise it. The bag is attached to the produced node as `node.inlineStyles` — the editor's first-class per-node `style=""` layer — which the publisher emits verbatim and the user edits via the Properties panel's inline-style mode. In Super Import any `url(…)` is uploaded to the media library and rewritten to its media URL.
Copy file name to clipboardExpand all lines: server/ai/tools/site/systemPrompt.ts
+6Lines changed: 6 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -35,6 +35,12 @@ Structure as HTML, styling as CSS:
35
35
- applyCss is the ONE tool for authoring or editing CSS on its own — after insertion, or for any selector a class= can't express. Pass real CSS text: a bare \`.foo { … }\` selector creates/edits a reusable class; ANY other selector (\`.hero a\`, \`a:hover\`, \`nav > li\`, \`.card::before\`, \`h1\`) creates/edits an ambient rule that attaches by matching. Re-applying a selector MERGES onto it, so applyCss both creates AND edits — that is how you restyle an existing descendant/pseudo rule (e.g. \`applyCss(".hero a:hover { color: var(--primary) }")\`). There is no class-by-id patch tool; just write the CSS, referencing tokens via var(--…).
36
36
- Per-breakpoint variation: use @media queries — in the <style> block of an insert, or inside applyCss — with min/max-width queries that line up with the breakpoint widths in the dynamic suffix. Don't invent "mobile"/"tablet"/"desktop".
37
37
38
+
Behavior and runtime code:
39
+
- insertHtml/replaceNodeHtml deliberately strip <script> and inline event handlers (onclick/onload/etc). NEVER try to add behavior with <script>, onclick, or custom inline JS in HTML.
40
+
- To add behavior such as theme toggles, tabs, menus, filters, or DOM-ready interactions, use write_code_asset({ type:"script", path:"src/scripts/...", content, runtime }). The script file is stored in the site file layer and loaded through site.runtime.
41
+
- Before changing existing scripts or user stylesheets, call list_code_assets/read_code_asset. Patch exact spans with patch_code_asset using the latest hash; if the text occurs multiple times, use a larger oldText span or replaceAll:true intentionally.
42
+
- Use inspect_code_runtime after writing code to confirm scripts/styles apply to the current page/template, are enabled, and have the intended priority/placement/timing.
43
+
38
44
Responsive:
39
45
- Design for every breakpoint in the suffix from the start. All variation is CSS via @media (in an insert's <style> block or applyCss), matched against the suffix breakpoint widths.
'List user-authored runtime code assets stored in the site file layer. Optional `type` filters to scripts or styles. Returns file ids, paths, content hashes, size metadata, and current runtime config. Use before read_code_asset / patch_code_asset when modifying existing scripts or stylesheets.',
229
+
inputSchema: ListCodeAssetsInputSchema,
230
+
}
231
+
232
+
constreadCodeAssetTool: AiTool={
233
+
name: 'read_code_asset',
234
+
scope: 'site',
235
+
execution: 'browser',
236
+
requiredCapabilities: ['site.read'],
237
+
description:
238
+
'Read one script or stylesheet by fileId or path. Returns the exact content slice, full-file SHA-256 hash, runtime config, and pageInfo for pagination. If pageInfo.nextPart is not null, call read_code_asset again with the same asset and part.',
239
+
inputSchema: ReadCodeAssetInputSchema,
240
+
}
241
+
242
+
constwriteCodeAssetTool: AiTool={
243
+
name: 'write_code_asset',
244
+
scope: 'site',
245
+
execution: 'browser',
246
+
requiredCapabilities: SITE_STRUCTURE_CAPS,
247
+
description:
248
+
'Create or replace a runtime script/style file in site.files and attach normalized site.runtime config. Use `type:"script"` for behavior such as theme toggles, menus, tabs, analytics hooks, and DOM-ready interactions; use `type:"style"` for global user stylesheets that should load as files. `path` is a safe site-relative path such as src/scripts/theme-toggle.js or src/styles/theme.css. `runtime` is optional and merges with existing/default config.',
249
+
inputSchema: WriteCodeAssetInputSchema,
250
+
}
251
+
252
+
constpatchCodeAssetTool: AiTool={
253
+
name: 'patch_code_asset',
254
+
scope: 'site',
255
+
execution: 'browser',
256
+
requiredCapabilities: SITE_STRUCTURE_CAPS,
257
+
description:
258
+
'Patch an existing script or stylesheet by exact text replacement. Requires the latest `expectedHash` from read_code_asset/list_code_assets to prevent stale edits. Each replacement must match exactly; if oldText occurs multiple times, either make oldText more specific or set replaceAll:true.',
259
+
inputSchema: PatchCodeAssetInputSchema,
260
+
}
261
+
262
+
constinspectCodeRuntimeTool: AiTool={
263
+
name: 'inspect_code_runtime',
264
+
scope: 'site',
265
+
execution: 'browser',
266
+
requiredCapabilities: ['site.read'],
267
+
description:
268
+
'Inspect which runtime scripts and user stylesheets apply to the current page/template, or to a supplied page/template document ref. Returns each asset path, enabled state, scope applicability, priority, and script placement/timing. Use after write_code_asset to confirm a script/style is targeted correctly.',
0 commit comments