System Overview
Avocado Studio is a pnpm monorepo with three apps and twelve packages: The orchestrator has three parallel front doors: the editor web app for humans, the MCP server for AI assistants in any MCP host, and the Jira integration for ticket-driven workflows. All three go through the same operation pipeline — Zod validation, undo history, version log, demo-mode gating — so anything you can do in the web editor, you can do from an MCP client or a Jira ticket, and the other way round. See MCP Server and Jira Integration for setup. That the vocabulary is the same at every door is the point, and it is also the safety boundary: all three doors speak operations, and an operation cannot express a change to your code.Data Flow: From Chat to Preview
When a user sends a message in the editor, here’s what happens: Step 6 is where a malformed plan dies. An operation naming a prop the block does not declare, or a value the schema rejects, never reaches your content — it comes back as a skipped op with a reason, not as a broken page.Packages
The monorepo includes these packages — the ones under@avocadostudio-ai are published to npm, the @ai-site-editor ones are monorepo-only:
Communication Protocols
Editor ↔ Orchestrator: HTTP + SSE
The editor communicates with the orchestrator via REST API and Server-Sent Events:POST /chat/start— Start a streamed run, returns astreamIdGET /chat/stream?streamId=…— Subscribe to the stream via SSEPOST /chat— Non-streaming variant (immediate response)GET /draft/pages— Fetch current draft page statePOST /ops— Apply hand-authored operations (bypassing the planner)POST /history/undo,POST /history/redo— Undo/redo operationsPOST /publish— Publish draft to production
Editor ↔ Site: postMessage
The editor embeds the site in an iframe. They communicate via thesite-editor/v1 postMessage protocol:
- Editor → Site: Request block highlight, navigate to page, refresh preview
- Site → Editor: Report selected block, confirm preview updated, send block manifest
Orchestrator ↔ Site: HTTP
The site fetches draft content from the orchestrator when in draft mode:GET /draft/pages?session=…&slug=…— One draft page by slug. Both parameters are required; an unknown slug is a 404.GET /draft/slugs?session=…— Every slug the session has, which is the route that lists pages
Session State
The orchestrator maintains per-session state for each editing session:- Draft pages — Current page content with all pending edits
- Operation history — Full undo/redo stack
- Edit plans — Generated plans awaiting approval
- Session config — Selected AI provider, model tier, locale
.data/orchestrator.db, via better-sqlite3 + WAL) with synchronous transactional writes on every mutation, so crash recovery is automatic.
SQLite is the working copy, not the source of truth. Your CMS / JSON file / custom store is the origin; SQLite holds drafts, undo stacks, and chat history scoped per session. On the first chat for a fresh session, the orchestrator calls the configured CmsAdapter.getPages() to seed SQLite. On publish, onPublish(pages) writes back. That separation is what lets the same chat UX work against any upstream store without per-integration handshakes.
The orchestrator runs as a single instance with a local SQLite file. Multi-replica horizontal scaling — which would require moving state to a network-accessible store (Postgres, Turso/libSQL, Redis, etc.) — is on the roadmap but not implemented today.
Publishing Pipeline
Publishing promotes draft content to production: ThePublishTarget interface is pluggable. Three built-in targets ship in the box:
site-contract— POSTs pages + assets to the remote site’s/api/editor/publishendpoint. Selected whensiteOriginis supplied. The receiving route is the site’s, not the orchestrator’s, and it fails closed twice: 401 when it runs underNODE_ENV=productionwith no publish secret, 409 when the payload would remove every page. Both carry areasonthe editor shows.git— Serializes draft pages to JSON, commits, and pushes to a Git branch. A Vercel deploy hook wired to that branch auto-builds.deploy-hook— Calls a rawVERCEL_DEPLOY_HOOK_URLand polls the Vercel API for deployment status.
registerPublishTarget() to integrate with any workflow — S3, GitLab Pages, Netlify, a CMS API, a custom CI/CD pipeline. See How it Works — Publishing for the full interface.