particle-academy / charlotte
Headless website-builder agent kit for Laravel: CMS page tools over fancy-cms drafts, a structured brief with code-derived fact checks, autonomy modes, and an ordered event log any chat UI can long-poll. Runs on prism-harness with any Prism provider.
Requires
- php: ^8.4
- illuminate/bus: ^13.0
- illuminate/container: ^13.0
- illuminate/contracts: ^13.0
- illuminate/database: ^13.0
- illuminate/http: ^13.0
- illuminate/routing: ^13.0
- illuminate/support: ^13.0
- particle-academy/fancy-cms: >=0.8.1 <2.0
- particle-academy/prism: >=0.135 <1.0
- particle-academy/prism-harness: >=0.17 <1.0
Requires (Dev)
- laravel/framework: ^13.30
- pestphp/pest: ^4.0|^5.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A headless website-builder agent kit for Laravel. Charlotte lets your app host an agent that builds and edits fancy-cms pages, live, beside a person. It runs on prism-harness, so any Prism provider works.
It is headless on purpose: Charlotte writes an ordered event log, and any chat
UI you build reads it. A plain fetch loop is enough
(docs/fetch-chat.html).
Pre-1.0. Breaking changes land in minor releases; read the CHANGELOG before upgrading.
What you get
- Page tools over a draft. The agent edits a fancy-cms page through nine
closed tools:
charlotte_brief,cms_describe,cms_icons,cms_assets,cms_get,cms_apply,cms_changes_since,cms_checkandcms_finish. Writes are pinned to a revision, so a person editing at the same time is never overwritten. - Drafts only. Charlotte edits a draft. Nothing it does publishes.
Publishing is one explicit host call,
$session->publish($revision, $by). - Autonomy is your setting.
fullapplies each edit to the draft at once.approve_on_savecollects edits in a change set a person approves or rejects. - A structured brief with code-derived fact checks. Required facts must
appear on the page (
missing_required_fact), and prices, percentages, counts, phone numbers, emails, years and quotes that no fact supports are flagged (unsupported_claim). No model is involved. - Page checks. Heading outline, alt text, contrast where it can be computed, link safety, placeholder copy and meta, lifted from the Fancy lab that measured them.
- A versioned, overridable guide. fancy-cms's authoring guide plus Charlotte's own (drafts, autonomy, content truth, the webmaster lessons). Layer your house style on top; every session records the guide hash it ran.
- An event log any client can long-poll. Envelope v1, gap-free per-session cursors, short requests that work behind Cloudflare. See docs/events.md.
- A thin, optional controller kit. Routes behind your authorization hook and your tenant resolver. With no hook, every route is refused.
- Optional usage telemetry. If you turn on Prism's telemetry, Charlotte reports token usage, and cost when you give prices. It is never required and never refuses anything.
Quick look
use Charlotte\Brief\Brief; use Charlotte\Facades\Charlotte; // Once, in a service provider: Charlotte::resolveTenantUsing(fn ($request) => (string) $request->user()->current_team_id); Charlotte::authorizeUsing(fn ($user, string $ability, array $subject): bool => $user->can('charlotte.'.$ability)); Charlotte::routes(); // In your controller: $session = Charlotte::for($request->user()) ->tenant((string) $team->getKey()) ->page('home') ->brief(Brief::fromArray($validated['brief'])) ->autonomy('approve_on_save') ->open(); $cursor = $session->send('Build the landing page from the brief.', (string) $request->user()->getKey()); // A queue worker runs the turn; clients poll GET /charlotte/sessions/{id}/events?after=<cursor>&wait=20
Full setup: docs/laravel-quickstart.md.
Requirements
- PHP 8.4, Laravel 13 (illuminate components), a queue worker.
particle-academy/prism-harnessandparticle-academy/fancy-cms, installed with Charlotte.
Security
Agent-authored content is untrusted.
- No
richtextnodes, from the agent or from a person: the browser renderer injects richtext HTML as is. - Images only by handle from your
AssetCatalogue. Authoredsrc,srcset, dimensions and CSSurl()/image-set()/@import, escapes and comments are refused after every write. - Agent links only to your own paths, anchors,
mailto:,tel:or origins you allowlist. - Page, tenant and actor are host-owned, never tool arguments.
- The brief, the page and a person's edits are evidence, never instructions, and the builder has no network, URL, browser or file tool.
- Events and API errors carry codes, never provider messages, so keys and URLs inside a provider error cannot leak to a client.
- Provider keys stay in your server config.
Testing
composer install vendor/bin/pest php tests/lint.php
The suite is hermetic: in-memory SQLite, the sync queue, and a scripted Prism provider that really executes the tools. No network and no key.
License
MIT.