# How Do AI Agents Work in Instatic with Site and Content Tool Scopes?

> Understand how Instatic AI agents work with Site and Content tool scopes. Explore the server-side runtime and browser-executed tools for efficient context management.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: deep-dive
- Published: 2026-07-31

---

**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`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/siteAgentSnapshot.ts) exports `buildSiteAgentSnapshot()`, which captures the active page, selected node IDs, breakpoint states, and the entire site document:

```typescript
// 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:

1. **`POST /admin/api/ai/chat/:scope`** – Initializes the model chat, streams NDJSON events, and pauses when the model requests a tool call.
2. **`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`](https://github.com/CoreBunch/Instatic/blob/main/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:

```typescript
// 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`](https://github.com/CoreBunch/Instatic/blob/main/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`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/streamEvents.ts), it dispatches the tool through `executeAgentTool()` in [`src/admin/pages/site/agent/executor.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/agent/executor.ts):

```typescript
// 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`](https://github.com/CoreBunch/Instatic/blob/main/src/core/ai/toolSchemas.ts) using TypeBox. Both the server registry ([`server/ai/tools/site/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/tools/site/index.ts) and [`server/ai/tools/content/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/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`](https://github.com/CoreBunch/Instatic/blob/main/drivers/http/toolLoop.ts). Provider-specific adapters ([`server/ai/drivers/openai.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/openai.ts), [`anthropic.ts`](https://github.com/CoreBunch/Instatic/blob/main/anthropic.ts), [`openrouter.ts`](https://github.com/CoreBunch/Instatic/blob/main/openrouter.ts), [`ollama.ts`](https://github.com/CoreBunch/Instatic/blob/main/ollama.ts), [`openaiCompatible.ts`](https://github.com/CoreBunch/Instatic/blob/main/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`](https://github.com/CoreBunch/Instatic/blob/main/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:

```typescript
await agentSlice.sendAgentMessage([
  { type: 'text', content: 'Add a primary button labeled "Subscribe".' },
]);

```

The model generates a tool call to `site_insert_html`:

```json
{
  "parentId": "activePage.rootNodeId",
  "html": "<button class=\"btn-primary\">Subscribe</button>"
}

```

The browser executor validates the input against [`toolSchemas.ts`](https://github.com/CoreBunch/Instatic/blob/main/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`:

```json
{
  "document": { "type": "collectionEntry", "id": "42" },
  "part": undefined
}

```

The browser-side code retrieves the live entry from the content store:

```typescript
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/:scope` and `/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.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/ai/toolSchemas.ts) provide a single source of truth for input validation across server and browser.
- The **tool loop** in [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts) is 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.