Orchestration
A state-graph runner for multi-agent workflows — nodes, conditional edges, handoff, and sub-graphs.
Some problems need more than one turn of one agent — a writer and a critic looping until approval, a supervisor routing to specialists, a plan → act → check cycle. StateGraph is a small runner for exactly that: nodes transform a shared state, edges route between them, and the run loops until it hits END or a step cap. LangGraph-style, minus the weight.
A graph
A node is just (state, ctx) => Partial<state>. Wire nodes with static or conditional edges:
import { StateGraph, END } from '@agentoria/runtime';
const graph = new StateGraph<{ topic: string; draft: string; ok: boolean }>()
.addNode('write', async (s) => ({ draft: await writer(s.topic, s.draft) }))
.addNode('review', async (s) => ({ ok: await critic(s.draft) }))
.addEdge('write', 'review')
.addConditionalEdge('review', (s) => (s.ok ? END : 'write')) // loop until approved
.setEntry('write')
.compile();
const { state, path } = await graph.run({ topic: 'agents', draft: '', ok: false });The run has a maxSteps cycle guard (throws GraphStepLimitError), an onStep hook for tracing, and returns the final state plus the path of nodes visited.
Channels
By default a node's update replaces state keys. Pass reducers to merge instead — e.g. accumulate a messages channel:
import { appendReducer } from '@agentoria/runtime';
new StateGraph<{ messages: Message[] }>({ messages: appendReducer })
.addNode('a', () => ({ messages: [msgA] }))
.addNode('b', () => ({ messages: [msgB] })); // state.messages = [msgA, msgB]Agents as nodes
agentNode turns any createAgent into a node — give each node a different agent (system prompt, tools) to build a team of specialists:
import { agentNode } from '@agentoria/runtime';
graph.addNode('researcher', agentNode(researchAgent, {
input: (s) => [{ role: 'user', content: s.question }],
output: (text, s) => ({ notes: [...s.notes, text] }),
}));Handoff & delegation
A conditional edge is handoff — a supervisor node routes to the next specialist:
.addConditionalEdge('supervisor', (s) => s.needsCode ? 'coder' : s.needsSearch ? 'researcher' : END)A sub-graph is delegation — embed a whole team as one node with subgraphNode:
import { subgraphNode } from '@agentoria/runtime';
parent.addNode('research_team', subgraphNode(researchGraph, {
input: (s) => ({ question: s.task }),
output: (r) => ({ findings: r.report }),
}));