Quick Start
One agent, an in-memory store and one manually dispatched run. This is the same code as the example in the @chamberu/core README, which the test suite runs against a scripted model.
ts
import type { LanguageModel } from 'ai';
import { z } from 'zod';
import {
MemoryStore,
RunEngine,
defineAgent,
defineEffectTool,
manual,
} from '@chamberu/core';
export async function triageIssue(model: LanguageModel) {
const labels: string[] = [];
const triage = defineAgent({
name: 'triage',
model,
instructions: 'Label the issue in the trigger payload as bug, feature or question.',
triggers: [manual()],
budget: { maxSteps: 5 },
tools: () => ({
// the result is recorded per (run, tool, input): a resumed run reuses it
addLabel: defineEffectTool({
description: 'Adds a label to the issue.',
inputSchema: z.object({ label: z.enum(['bug', 'feature', 'question']) }),
execute: async ({ label }) => {
labels.push(label);
return { added: label };
},
}),
}),
});
const engine = new RunEngine({
store: new MemoryStore(),
agents: [triage],
resumeSecret: 'change-me',
});
await engine.start();
const { runId } = await engine.dispatch({
agent: 'triage',
trigger: 'manual',
source: 'manual',
payload: { title: 'App crashes on start' },
});
await engine.idle();
engine.stop();
return { run: await engine.getRun(runId), labels };
}Pass any AI SDK LanguageModel, for example anthropic(...) from @ai-sdk/anthropic. The function resolves with the settled run and the labels the tool added.
What happened
defineAgent()checked the name and filled in the default budget. Thetoolsfactory runs once per execution and returns an AI SDKToolSet.new RunEngine()validated the setup: a non-emptyresumeSecret, unique agent names, known notifiers.engine.start()recovered abandoned runs (there were none) and started polling.engine.dispatch()stored aqueuedrun. The trigger envelope (source, trigger id and payload) became the run's first user message.- The engine claimed the run, set it
runningand drove an AI SDKToolLoopAgent. After every step it appended the steps and saved a checkpoint to the store. - The model called
addLabel. Because it is an effect tool, its result was recorded under an effect key, so a resumed run would reuse it instead of labelling twice. - The model answered with text, and the run settled
completed. Itsoutputis the text of the last answer. engine.idle()resolved once nothing was running, andengine.stop()stopped the poll.