OBJECTIVE-FIRST AI OPERATING SYSTEM
Restoring workspace
I°
Loading...
Open Aya OS — SDK
Aya exposes a set of stable surfaces for building on top of the OS: the MCP server, memory API, skill registry, model routing, streaming TTS, and eval harness. All surfaces are typed, server-rendered where possible, and documented below.
Aya treats storage as a mountable disk. Canonical state lives in versioned JSON files; databases and embeddings are rebuildable indexes. The provider-neutral DiskAdapter exposes read, write, list, delete, move, and stat with explicit revision conflicts.
import { createDiskAdapter, AyaDiskStore } from "@/lib/aya-disk"
const adapter = createDiskAdapter(
{ id: "default", provider: "supabase_storage", userId },
{ supabase }
)
const disk = new AyaDiskStore(adapter, userId)
await disk.write(ayaFile, expectedProviderVersion)Supabase Storage is the private reference adapter. External providers are planned, not yet shipped. Read the full disk specification or download the JSON Schema.
Aya implements MCP 2025-03-26 (JSON-RPC 2.0 over HTTP/SSE). Connect any MCP-compatible client — Claude Code, Cursor, Windsurf, or your own — to /api/mcp.
aya_memory_searchSemantic search over the context vaultaya_memory_storeWrite a new memory with optional tagsaya_graph_searchQuery the knowledge graph for related nodesaya_context_getRetrieve the current workspace contextaya_skills_listList all crystallized skills with confidenceaya_decision_recordRecord a decision into the decision graphaya://constitutionThe active AYA.md operator doctrineaya://workspaceCurrent workspace snapshot (apps, widgets, state)aya://skillsFull skill registry with confidence scores// .mcp.json at project root
{
"mcpServers": {
"aya": {
"type": "http",
"url": "http://localhost:3000/api/mcp"
}
}
}Aya connects to external MCP servers via lib/aya-mcp-host.ts. Set AYA_MCP_SERVERS to a JSON array. Tools are discovered in parallel and merged into every reasoning turn's streamText call.
// AYA_MCP_SERVERS environment variable
[
{ "id": "filesystem", "name": "Filesystem", "url": "http://localhost:3001" },
{ "id": "github", "name": "GitHub", "url": "http://localhost:3002" },
{ "id": "supabase", "name": "Supabase", "url": "http://localhost:3003" }
]
// aya-mcp-host.ts — used in chat route
import { getExternalMCPTools } from "@/lib/aya-mcp-host"
const externalTools = await getExternalMCPTools() // { [toolName]: AI SDK tool }
// Spread into streamText tools:
tools: { ...ayaBuiltinTools, ...externalTools }All model selection goes through lib/select-model.ts — one source of truth for the OS, eval harness, and every API route. Override any tier via environment variables.
| TaskClass | Default model | Label | Env override |
|---|---|---|---|
| conversation | anthropic/claude-sonnet-4.6 | conversation tier | AYA_MODEL_CONVERSATION |
| reasoning | anthropic/claude-opus-4.6 | extended thinking, 10k budget | AYA_MODEL_REASONING |
| vision | google/gemini-3-flash | multimodal | AYA_MODEL_VISION |
| speed | google/diffusion-gemma-26b | 4x faster diffusion, voice/short turns | AYA_MODEL_SPEED |
| code | anthropic/claude-opus-4.6 | SWE-Bench leader | AYA_MODEL_CODE |
| micro | openai/gpt-4o-mini | micro / classifier | AYA_MODEL_MICRO |
import { selectModel, fallbackChain } from "@/lib/select-model"
// Select model for a task class
const model = selectModel("speed") // "google/diffusion-gemma-26b"
// Full fallback chain for a task
const chain = fallbackChain("speed")
// ["google/diffusion-gemma-26b", "google/gemini-3-flash", "anthropic/claude-sonnet-4-6"]Aya's memory system is backed by Supabase with a discovery gate that prevents duplicate writes via GraphRAG edge scoring (arXiv:2606.01444). Memories are retrieved by semantic similarity at each turn.
import { storeMemory, searchMemory } from "@/lib/aya-memory"
// Store a memory with auto-dedup via discovery gate
await storeMemory({
userId: session.user.id,
content: "User prefers concise explanations",
tags: ["preference", "communication"],
})
// Semantic search
const results = await searchMemory({
userId: session.user.id,
query: "how does the user like responses formatted",
limit: 5,
})Aya speaks sentence-by-sentence as tokens arrive — text and audio advance in lock-step. chunkForSpeech() splits a growing text buffer into completed sentences at safe boundaries (.!?:,;).
import { chunkForSpeech } from "@/lib/aya-streaming-tts"
import { ayaVoice } from "@/lib/aya-voice"
let spokenUpTo = 0
// In your streaming reader loop:
const sentences = chunkForSpeech(displayText.slice(spokenUpTo))
const completeCount = done ? sentences.length : sentences.length - 1
for (let i = 0; i < completeCount; i++) {
if (sentences[i].trim()) {
ayaVoice.speak(sentences[i]).catch(() => {})
spokenUpTo += sentences[i].length
}
}
// After stream completes — speak the remaining tail:
const tail = displayText.slice(spokenUpTo).trim()
if (tail) ayaVoice.speak(tail)lib/aya-sfx.ts provides zero-dependency Web Audio API tones. No audio files, no network requests. Respects prefers-reduced-motion and a global enabled toggle.
import {
sfxAppOpen, sfxAppClose,
sfxMessageSend, sfxMessageReceive,
sfxVoiceStart, sfxVoiceStop,
sfxError, setSfxEnabled,
} from "@/lib/aya-sfx"
sfxAppOpen() // ascending chime on app mount
sfxMessageSend() // short click-pop on send
sfxError() // low thud on failure
setSfxEnabled(false) // mute all SFXThe CAISI-inspired eval harness measures five dimensions: capability, cost, latency, auditability, and workflow lift. Results are published to /api/aya/audit and surfaced in the eval dashboard.
// POST /api/aya/eval
// Run a task against baseline, Aya Pipeline, and Reasoner route
{
"taskId": "summarize-email",
"input": "Summarize this email thread...",
"expectedOutput": "...", // optional
"systemPromptOverride": "..." // used by autoresearch loop
}
// Response
{
"baseline": { "score": 0.71, "latencyMs": 312, "costUsd": 0.0012 },
"ayaPipeline": { "score": 0.89, "latencyMs": 445, "costUsd": 0.0018 },
"reasoner": { "score": 0.94, "latencyMs": 1240, "costUsd": 0.0071 }
}Aya uses Supabase Auth with Row Level Security on all 27 database tables. Every query that touches user data is scoped by the session user ID. No cross-tenant data access is architecturally possible.
// All user-scoped queries are parameterized by userId const memories = await db .select() .from(memoriesTable) .where(eq(memoriesTable.userId, session.user.id)) // RLS + query scoping // Supabase RLS policy (applied at DB level as well): // CREATE POLICY "users_own_memories" ON memories // USING (auth.uid() = user_id);