astral-site
v0.1.3
Published
Official site SDK for Astral, a protocol for browser–site cooperation that lets websites expose tools, UI regions, and metadata to AI browsers.
Maintainers
Readme
astral-site
Official SDK for implementing the Astral protocol on websites. Enable AI browsers to discover and safely invoke on-page actions.
What is Astral?
Astral is a protocol for browser-site cooperation that lets websites expose on-page capabilities to AI browsers. Instead of AI browsers blindly manipulating the DOM, sites explicitly offer safe, typed actions that AI can invoke.
What Astral Provides
An Astral-enabled website exposes:
- Tools - Named, typed actions AI browsers can invoke (e.g., "add task", "reorder items", "filter results")
- UI Regions - Marked DOM areas that tools can safely read or mutate
- Events - Real-time notifications when state changes
- Metadata - Site context, intents, and examples to help AI understand your site's purpose
What This SDK Does
This SDK makes it trivial to add Astral support to any website:
- Automatic manifest generation from your configuration
- JSON-RPC 2.0 tool invocation handling
- Region versioning with conflict detection
- Framework-agnostic - works with vanilla JS, React, Vue, Svelte, or any framework
Scope: Client-side tools only (on-page UI actions). No server RPC or backend endpoints.
Framework-Agnostic Design
This SDK is built entirely on standard Web APIs and has zero framework dependencies. It works seamlessly with:
- Vanilla JavaScript - Plain DOM manipulation
- React - Integrate with hooks and state management
- Vue - Use with Composition API or Options API
- Svelte - Works with Svelte's reactive stores
- Any other framework - Or no framework at all
The core implementation uses only standard browser APIs (Element, MessagePort, CustomEvent, window). Tool handlers are user-provided functions that can use any framework or library you prefer.
Installation
npm install astral-siteOr via CDN:
<script src="https://unpkg.com/astral-site/dist/index.umd.js"></script>Quick Start
Three steps to add Astral support to your site:
- Configure - Define tools, regions, and metadata
- Mount - Activate the actuator
- Signal - Announce readiness to AI browsers
The SDK automatically generates the Astral manifest from your configuration.
Minimal Example (Vanilla JS)
import { createActuator } from 'astral-site';
// 1. Configure: Define tools and regions
// The manifest is automatically generated from this config
const actuator = createActuator({
project: {
name: 'My Todo App',
version: '1.0.0',
},
tools: {
addTask: {
description: 'Add a new task to the list',
scope: 'ui:mutate',
input: {
type: 'object',
properties: {
title: { type: 'string' },
},
required: ['title'],
},
output: {
type: 'object',
properties: {
receipt: { type: 'string' },
},
},
handler: ({ title }) => {
// Your implementation
const taskList = document.getElementById('tasks');
const li = document.createElement('li');
li.textContent = title;
li.setAttribute('data-agent-item-id', `task-${Date.now()}`);
taskList.appendChild(li);
// Emit event
actuator.emit('task.added', { title });
return { receipt: `Added: ${title}` };
},
events: ['task.added'],
},
},
regions: {
'task-list': {
selector: '#tasks',
type: 'list',
mutable: true,
},
},
});
// 2. Mount: Activate the actuator
actuator.mount();
// 3. Signal: Announce readiness to AI browsers
actuator.signalReady();
// The manifest is now available at actuator.getManifest()
// It's automatically used in the HELLO/WELCOME handshakeHTML Markup
Mark your regions with data-agent-region and items with data-agent-item-id:
<ul id="tasks" data-agent-region="task-list">
<li data-agent-item-id="task-1">Buy milk</li>
<li data-agent-item-id="task-2">Email Alice</li>
</ul>Core Concepts
Tools
Tools are named actions that AI browsers can invoke. Each tool has:
- Description: Human-readable explanation
- Scope:
ui:read(read-only) orui:mutate(can modify DOM) - Input Schema: JSON Schema describing parameters
- Output Schema: JSON Schema describing the result
- Handler: Function that implements the behavior
- Events: List of events this tool emits
Example with region versioning:
tools: {
reorderTasks: {
description: 'Reorder tasks by ID',
scope: 'ui:mutate',
input: {
type: 'object',
properties: {
region: { type: 'string' },
order: { type: 'array', items: { type: 'string' } },
},
required: ['region', 'order'],
},
output: {
type: 'object',
properties: {
receipt: { type: 'string' },
post: {
type: 'object',
properties: {
region: { type: 'string' },
version: { type: 'integer' },
},
},
},
},
handler: ({ region, order }, meta) => {
// Check precondition (version)
const currentVersion = actuator.getRegionVersion(region);
if (meta.pre?.version && meta.pre.version !== currentVersion) {
throw { code: 'preconditionFailed', message: 'Version mismatch' };
}
// Perform reordering
const container = actuator.getRegionElement(region);
const itemsById = new Map(
Array.from(container.children).map(el => [
el.getAttribute('data-agent-item-id'),
el
])
);
order.forEach(id => {
const item = itemsById.get(id);
if (item) container.appendChild(item);
});
// Increment version and emit event
const newVersion = actuator.incrementRegionVersion(region);
actuator.emit('region.changed', { region, version: newVersion });
return {
receipt: `Reordered ${order.length} items`,
post: { region, version: newVersion },
};
},
events: ['region.changed'],
},
}Regions
Regions are DOM containers that tools can interact with:
regions: {
'sidebar': {
selector: '#sidebar', // CSS selector
type: 'stack', // 'grid', 'list', or 'stack'
mutable: false, // Can tools modify this region?
},
'main-content': {
element: document.getElementById('main'), // Or direct element reference
type: 'grid',
mutable: true,
},
}Regions are versioned - every mutation increments the version number, enabling:
- Conflict detection
- Optimistic locking
- Event-driven updates
Events
Events notify AI browsers about state changes:
// In your tool handler
actuator.emit('task.completed', { taskId: 'task-123' });Events declared in tools are automatically included in the manifest.
Advanced Features
Site Context
Help AI browsers understand your site's purpose:
project: {
name: 'TaskMaster Pro',
version: '2.0.0',
siteContext: {
summary: 'A powerful task management application',
category: 'productivity',
entities: ['task', 'project', 'user'],
keywords: ['todo', 'organize', 'collaborate'],
},
}Intents
Map high-level user goals to specific tools:
project: {
intents: [
{
name: 'capture_task',
description: 'Quickly add a new task',
uses_tools: ['addTask'],
},
{
name: 'organize_tasks',
description: 'Rearrange tasks by priority',
uses_tools: ['reorderTasks', 'setTaskPriority'],
},
],
}Examples
Provide few-shot learning examples for better AI understanding:
project: {
examples: [
{
prompt: 'Add a task: Buy groceries',
intent: 'capture_task',
tool: 'addTask',
input: { title: 'Buy groceries' },
},
{
prompt: 'Move task-5 to the top',
intent: 'organize_tasks',
tool: 'reorderTasks',
input: {
region: 'task-list',
order: ['task-5', 'task-1', 'task-2'],
},
},
],
}Error Handling
Throw structured errors in your tool handlers:
import { notFound, preconditionFailed, invalidParams } from 'astral-site';
handler: ({ taskId }) => {
const task = findTask(taskId);
if (!task) {
throw notFound('Task', taskId);
}
// Or throw plain objects
throw { code: 'permissionDenied', message: 'User not authorized' };
};Standard error codes:
invalidParams- Input validation failednotFound- Resource doesn't existpreconditionFailed- Version mismatchpermissionDenied- Action requires confirmationconflict- Concurrent modificationretryable- Temporary failureinternal- Unexpected error
Auto-mount and Auto-signal
Simplify initialization with automatic mounting and readiness signaling:
const actuator = createActuator({
// ... config ...
autoMount: true, // Automatically call mount()
autoSignalReady: true, // Automatically call signalReady()
});Custom Logger
Debug your integration:
const actuator = createActuator({
// ... config ...
logger: (message, data) => {
console.log('[MyApp]', message, data);
},
});Framework Integration Examples
The following examples demonstrate how to integrate the actuator with popular frameworks. These are optional patterns - the SDK itself has no framework dependencies.
React
import { useEffect } from 'react';
import { createActuator } from 'astral-site';
function TodoApp() {
const [tasks, setTasks] = useState([]);
useEffect(() => {
const actuator = createActuator({
tools: {
addTask: {
description: 'Add a task',
scope: 'ui:mutate',
input: {
/* ... */
},
output: {
/* ... */
},
handler: ({ title }) => {
// Update React state
setTasks((prev) => [...prev, { id: Date.now(), title }]);
actuator.emit('task.added', { title });
return { receipt: `Added: ${title}` };
},
},
},
regions: {
'task-list': {
selector: '#task-list',
type: 'list',
mutable: true,
},
},
autoMount: true,
});
// Signal ready after hydration
actuator.signalReady();
return () => actuator.destroy();
}, []);
return (
<ul id="task-list" data-agent-region="task-list">
{tasks.map((task) => (
<li key={task.id} data-agent-item-id={task.id}>
{task.title}
</li>
))}
</ul>
);
}Vue
<template>
<ul id="tasks" data-agent-region="task-list">
<li v-for="task in tasks" :key="task.id" :data-agent-item-id="task.id">
{{ task.title }}
</li>
</ul>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { createActuator } from 'astral-site';
const tasks = ref([]);
onMounted(() => {
const actuator = createActuator({
tools: {
addTask: {
description: 'Add a task',
scope: 'ui:mutate',
input: {
/* ... */
},
output: {
/* ... */
},
handler: ({ title }) => {
tasks.value.push({ id: Date.now(), title });
actuator.emit('task.added', { title });
return { receipt: `Added: ${title}` };
},
},
},
regions: {
'task-list': {
selector: '#tasks',
type: 'list',
mutable: true,
},
},
autoMount: true,
});
actuator.signalReady();
});
</script>Manifest Generation
Automatic Generation (Default)
The SDK automatically generates the Astral manifest when you call createActuator():
const actuator = createActuator({ /* config */ });
// Manifest is auto-generated and stored internally
const manifest = actuator.getManifest();
// Used automatically in HELLO/WELCOME handshake with AI browsersThe manifest includes:
- Tool definitions with JSON schemas
- Region declarations
- Event catalog
- Site metadata (context, intents, examples)
- Protocol version and timestamps
Static File Generation (Optional)
For serving at /.well-known/agent-manifest.json (protocol discovery):
import { buildManifestJSON } from 'astral-site';
import fs from 'fs';
const config = {
project: { name: 'My App', version: '1.0.0' },
tools: { /* ... */ },
regions: { /* ... */ },
};
// Generate static JSON file
const manifestJSON = buildManifestJSON(config);
fs.writeFileSync('.well-known/agent-manifest.json', manifestJSON);Note: Static files are optional. The runtime manifest from createActuator() is sufficient for full protocol support.
API Reference
createActuator(options)
Creates and returns an AgentActuator instance.
Options:
project- Project metadata (name, version, siteContext, intents, examples)tools- Tool definitions (keyed by tool name)regions- Region definitions (keyed by region ID)events- Additional events beyond those in toolspolicy- Custom policy overrideslogger- Custom logging functionautoMount- Auto-callmount()on creationautoSignalReady- Auto-callsignalReady()after mounting
AgentActuator Interface
Lifecycle Methods:
mount()- Attach listeners and expose onwindow.AgentActuatordestroy()- Cleanup listeners and remove from windowsignalReady()- Emit readiness signals (CustomEvent + postMessage)isMounted()- Check if actuator is mountedisConnected()- Check if connected to an AI browser
Manifest & Regions:
getManifest()- Get the auto-generated Astral manifestmarkRegion(element, id)- Mark a DOM element as a regiongetRegionVersion(id)- Get current version of a regiongetRegionElement(id)- Get DOM element for a regionincrementRegionVersion(id)- Increment region version (for tool handlers)
Events:
emit(event, payload)- Emit an event to connected AI browser
Browser Compatibility
- Modern browsers with ES2020 support
- MessageChannel API (all modern browsers)
- CustomEvent API (all modern browsers)
Protocol Compliance
This SDK implements the Astral Site Protocol with full compliance:
Core Protocol:
- ✅ Automatic manifest generation with protocol version and metadata
- ✅ JSON-RPC 2.0 tool invocation
- ✅ HELLO/WELCOME handshake via MessagePort
- ✅ Region versioning with monotonic counters (start at 1)
- ✅ Precondition checking (
meta.pre.version) - ✅ Post-condition reporting (
result.post.version) - ✅ Event emission via MessagePort
- ✅ Readiness signaling (CustomEvent
agent-protocol-ready+ postMessageASTRAL_ANNOUNCE)
Data & Metadata:
- ✅ JSON Schema validation (minimal subset: type, properties, required, items)
- ✅ Site context metadata (
site-context,intents,examples) - ✅ Error classification (transient/permanent) with standard codes
- ✅ Idempotency via
meta.idempotencycache
Implementation:
- ✅ Framework-agnostic (zero dependencies, standard Web APIs only)
- ✅ TypeScript with strict mode
- ✅ Multi-format builds (CJS, ESM, UMD)
TypeScript Support
Full TypeScript definitions included:
import type {
AgentActuator,
AstralManifest,
ToolHandler,
CreateActuatorOptions,
} from 'astral-site';Examples
A complete working example is included in example.html - a todo list built with vanilla JavaScript demonstrating all core features.
Contributing
Contributions welcome! Please read CONTRIBUTING.md first.
License
MIT © Astral Team
