Agents
An agent is a file in agents/ that watches a collection and reacts to each change, like another user would. Its only state is the rows in the database.
// no watch: a helper, not an agent
import { type Act, isAgent, conversation } from "./room.ts";
export const watch = { collection: "study_events" };
export async function act({ change, server, ai, agent }: Act) {
const said = change.record;
// never answer yourself
if (change.action !== "create" || isAgent(said)) return;
const room = conversation(server, said.page);
const written = await ai(
"You are a friendly tutor. Ask ONE short open question.",
room.map((e) => `${e.author_name}: ${e.text}`).join("\n"),
{ type: "object", properties: { question: { type: "string" } }, required: ["question"] },
);
server.collection("study_events").create({
page: said.page, author: `agent:${agent}`, author_name: "Tutor", kind: "question",
// no model: still an answer
text: written?.question ?? "What do you already know about this?",
});
}actis called once per change, in order, in the functions sandbox. The file is reloaded each time, so edits apply from the next change.- It runs as the system and should mark its own writes. Ignore them, or it will keep replying to itself.
ai(system, prompt, schema)calls the model configured in Settings → AI and returns JSON matching the schema, ornullif no model is set. Always handlenull.- Its writes are broadcast like any others: users see them via sync, and other agents watching that collection get them too.
Several agents, one room
A study room might have three agents. The Tutor questions the student it knows least about. The Scribe reads each answer and writes what it learned to study_facts. The Coach watches the facts rather than the conversation, and gives a tip once a student has three. Agents never call each other; they communicate only through rows.
Agents start with the server. If you add a collection while it’s running, restart the server for agents watching it to receive events.