How Do AI Agents Work in Instatic with Site and Content Tool Scopes?
Instatic's AI agents operate through a two-endpoint bridge architecture where a server-side runtime streams model responses to browser-executed tools, using scope-specific snapshots (SiteAgentSnapshot or ContentSnapshot) to provide context for 35 Site tools or 15 Content tools.
In the open-source CoreBunch/Instatic repository, the AI Agent is a model-powered assistant that lives in two distinct workspaces: the visual Site editor and the structured Content workspace. Both scopes share a single runtime located in server/ai/ that communicates with any supported model provider, yet they differ fundamentally in their tool catalogs, snapshot formats, and mutation strategies. Understanding how these agents work requires examining the bridge between server-side orchestration and browser-side execution.
Site vs. Content Scopes: Tool Catalogs and Snapshots
The primary distinction between scopes lies in what the AI edits and which tools it can invoke.
| Scope | Target | Tool Count | Snapshot Type |
|---|---|---|---|
| Site | Visual editor canvas (pages, templates, components) | 35 tools (6 server-side read + 29 browser-bridged) | SiteAgentSnapshot |
| Content | Content workspace (collections, entries, Tiptap editor) | 15 tools (7 server-side read + 8 browser-bridged) | ContentSnapshot |
When a user initiates a chat, the front-end builds a fresh snapshot of the current state. For the Site scope, src/admin/pages/site/agent/siteAgentSnapshot.ts exports buildSiteAgentSnapshot(), which captures the active page, selected node IDs, breakpoint states, and the entire site document:
// src/admin/pages/site/agent/pageContext.ts
import { buildSiteAgentSnapshot } from './siteAgentSnapshot';
const snapshot = buildSiteAgentSnapshot(
activePage, // the page currently open in the editor
get().site, // whole site document (breakpoints, tokens, etc.)
{
selectedNodeId: get().selectedNodeId,
activeBreakpointId: get().activeBreakpointId,
}
);
This snapshot is validated against SiteAgentSnapshotSchema before transmission. The Content workspace follows an analogous pattern but structures data around collections and entries rather than visual nodes.
The Two-Endpoint Bridge Architecture
Instatic routes all agent communication through two HTTP endpoints that separate model streaming from tool execution:
POST /admin/api/ai/chat/:scope– Initializes the model chat, streams NDJSON events, and pauses when the model requests a tool call.POST /admin/api/ai/tool-result– Receives the result of browser-executed tools and resumes the model stream.
This design keeps the browser store as the source of truth for live document state while allowing the server to orchestrate the conversation. The architecture is provider-agnostic, supporting Anthropic, OpenAI, OpenRouter, Ollama, and custom OpenAI-compatible endpoints through minimal driver adapters in server/ai/drivers/.
Server-Side Chat Handler and Tool Loop
The entry point server/ai/handlers/chat.ts exports chatHandler(), which performs capability checks (ai.chat and ai.tools.write), loads conversation history, resolves the appropriate driver, and builds the system prompt from the snapshot:
// server/ai/handlers/chat.ts
export async function chatHandler(req: Request) {
// 1️⃣ CSRF + capability checks (requires ai.chat)
// 2️⃣ Load conversation row + history
// 3️⃣ Resolve driver from stored credential
// 4️⃣ Build the system prompt from the snapshot
// 5️⃣ Create a bridge (bridgeReady event)
// 6️⃣ runChat(driver, request, persister, emit) // ← tool loop
}
Before starting the loop, the handler calls selectToolsForScope(scope, capabilities), which filters the full tool catalog to the requested scope and removes write-tools when the user lacks ai.tools.write permission.
The multi-turn tool loop resides in server/ai/drivers/http/toolLoop.ts. It streams the model's response, emits a toolRequest NDJSON event upon encountering a toolCall, and suspends execution until the tool-result endpoint receives the corresponding payload.
Browser-Side Tool Execution
When the front-end receives a toolRequest event via src/admin/pages/site/agent/streamEvents.ts, it dispatches the tool through executeAgentTool() in src/admin/pages/site/agent/executor.ts:
// src/admin/pages/site/agent/executor.ts
export async function executeAgentTool(name: string, input: any) {
// Validate input using the shared schemas (src/core/ai/toolSchemas.ts)
// Run the appropriate store action or read helper
// POST the result to /admin/api/ai/tool-result
}
All tool input schemas are defined once in src/core/ai/toolSchemas.ts using TypeBox. Both the server registry (server/ai/tools/site/index.ts and server/ai/tools/content/index.ts) and the browser executor import these schemas, guaranteeing type safety across the bridge.
Mid-Turn Snapshot Refresh
After each mutating tool (such as site_insert_html or content_set_document_fields), the executor optionally includes an updated snapshot in the snapshot field of the tool-result payload. The server updates toolContextBase.snapshot immediately, ensuring that subsequent catalog read tools (site_list_documents, content_list_documents, etc.) operate on freshly mutated state rather than stale data.
Tool Comparison: Site vs. Content Operations
The Tool Registry registers each tool with an execution mode ('browser' or 'server') and its corresponding schema.
Site-specific browser tools include:
- Structure:
site_insert_html,site_replace_node_html,site_get_node_html - Node operations:
site_update_node_props,site_move_node,site_delete_node - Styling:
site_apply_css,site_assign_class,site_remove_class - Assets:
site_write_code_asset,site_patch_code_asset - Pages:
site_add_page,site_set_page_template
Content-specific browser tools include:
- Documents:
content_read_document,content_open_document,content_set_document_fields - Structure:
content_insert_html,content_replace_node_html(for Tiptap body editing) - Collections:
content_list_documents(for entries rather than pages)
Server-side read tools (like listing available documents or tokens) execute within the chatHandler context and do not require browser round-trips.
Provider-Agnostic Runtime
All model providers share the identical multi-turn loop in drivers/http/toolLoop.ts. Provider-specific adapters (server/ai/drivers/openai.ts, anthropic.ts, openrouter.ts, ollama.ts, openaiCompatible.ts) are pure functions that map unified request shapes to vendor-specific HTTP endpoints. No provider SDKs are used; HTTP isolation is enforced by test gates in ai-driver-isolation.test.ts.
Practical Examples
Inserting a Styled Button in the Site Editor
When a user requests "Add a primary button labeled 'Subscribe'", the front-end sends the message and snapshot:
await agentSlice.sendAgentMessage([
{ type: 'text', content: 'Add a primary button labeled "Subscribe".' },
]);
The model generates a tool call to site_insert_html:
{
"parentId": "activePage.rootNodeId",
"html": "<button class=\"btn-primary\">Subscribe</button>"
}
The browser executor validates the input against toolSchemas.ts, dispatches the store action to import the HTML, extracts any <style> tags, registers the CSS class, and returns the created node IDs via POST /admin/api/ai/tool-result. The UI updates instantly because the mutation occurred in the browser's Zustand store.
Reading a Content Entry in the Content Workspace
For the query "What is the title of entry 42?", the model calls content_read_document:
{
"document": { "type": "collectionEntry", "id": "42" },
"part": undefined
}
The browser-side code retrieves the live entry from the content store:
const entry = getContentEntry('42');
const html = entry.body; // Tiptap JSON → HTML
const title = entry.title;
await postToolResult({
bridgeId,
requestId,
result: aiToolOk({ title, html })
});
The server receives this result, passes it back to the model, and the conversation continues with the retrieved data.
Summary
- Instatic's AI agents use a Site scope (35 tools, visual editing) and Content scope (15 tools, collection editing) with distinct snapshot formats.
- A two-endpoint bridge (
/admin/api/ai/chat/:scopeand/admin/api/ai/tool-result) separates server-side model orchestration from browser-side tool execution. - Snapshots (
SiteAgentSnapshot,ContentSnapshot) capture the full document state at the start of each turn and refresh mid-turn after mutations. - Tool schemas in
src/core/ai/toolSchemas.tsprovide a single source of truth for input validation across server and browser. - The tool loop in
server/ai/drivers/http/toolLoop.tsis provider-agnostic, supporting Anthropic, OpenAI, OpenRouter, Ollama, and custom endpoints through minimal HTTP adapters. - Browser execution ensures the live Zustand store remains the source of truth, enabling real-time visual updates without server-side DOM manipulation.
Frequently Asked Questions
What is the difference between Site and Content tool scopes in Instatic?
The Site scope operates on the visual editor canvas with 35 tools for manipulating pages, templates, HTML nodes, and CSS classes, using a SiteAgentSnapshot that captures the active page and selected elements. The Content scope manages collections and entries with 15 tools focused on document fields and Tiptap body editing, using a ContentSnapshot structured around collection data rather than visual nodes.
How does Instatic keep the AI agent's context current during tool execution?
After each browser-executed mutating tool, the executor optionally posts an updated snapshot in the snapshot field of the tool-result payload. The server immediately updates toolContextBase.snapshot, ensuring that subsequent server-side read tools (like listing documents) see the freshly mutated state within the same conversation turn.
Which AI providers are supported by Instatic's agent runtime?
The runtime supports Anthropic, OpenAI, OpenRouter, Ollama, and any custom OpenAI-compatible endpoint. Provider-specific adapters in server/ai/drivers/ are pure HTTP functions without SDK dependencies, allowing the unified tool loop to treat all providers interchangeably.
Why does Instatic execute AI tools in the browser rather than on the server?
Browser execution keeps the client-side store as the source of truth for the document state. When tools like site_insert_html or content_update_node_props run in the browser, they mutate the live Zustand store directly, causing immediate UI updates. This architecture avoids the complexity of server-side DOM reconciliation and ensures that visual changes appear instantly to the user.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →