@gemstack/ai-autopilot
Orchestration for @gemstack/ai-sdk agents — the "director" layer that runs many agent runs under a control policy.
ai-sdk owns the single-agent loop and the handoff / subagent primitives. ai-autopilot owns orchestrating multiple runs: which agents run, in what order, how their results combine, and when to stop. If a feature is just calling an ai-sdk primitive, it belongs in ai-sdk — autopilot earns its keep only as the topology / control-policy layer.
The seed: Supervisor (plan → dispatch → synthesize)
The first slice is the supervisor/worker topology — the smallest thing clearly more than the primitives:
- Plan — a planner decomposes the task into subtasks.
- Dispatch — each subtask runs on a worker agent, with bounded concurrency, an optional token budget, and per-subtask error isolation.
- Synthesize — a synthesizer combines the results into the final answer.
import { Supervisor, agentPlanner, agentSynthesizer } from '@gemstack/ai-autopilot'
const supervisor = new Supervisor({
plan: agentPlanner(plannerAgent), // LLM decomposition
workers: { research: researchAgent, write: writerAgent }, // routed by subtask.worker
synthesize: agentSynthesizer(editorAgent), // LLM synthesis
concurrency: 3,
maxSubtasks: 8,
budget: { maxTotalTokens: 200_000 },
onEvent: (e) => console.log(e.type),
})
const run = await supervisor.run('Draft a launch brief for product X')
console.log(run.text) // synthesized answer
console.log(run.results) // per-subtask outcomes (ok / error / usage)
console.log(run.usage) // aggregate token usage across dispatched subtasks
console.log(run.stoppedEarly) // true if a guardrail trimmed or halted workPieces are pluggable
Each stage is a plain function, so you mix LLM and deterministic logic freely:
plan— aPlanner:(task) => Subtask[]. UseagentPlanner(agent)for LLM decomposition, or return a static list.workers— a singleAgent(all subtasks), aRecord<string, Agent>(routed bysubtask.worker), or aWorkerRouterfunction.synthesize— aSynthesizer:(task, results) => string. Defaults todefaultSynthesize(concatenate successes, no LLM call); passagentSynthesizer(agent)for an LLM pass.
Guardrails
concurrency(optional, default 4) — max workers in flight; positive integer.maxSubtasks(optional) — hard cap; a longer plan is trimmed andstoppedEarlyis set. Omit for no cap.budget.maxTotalTokens(optional) — stop dispatching once aggregate dispatch usage crosses the limit (in-flight workers finish; remaining subtasks are skipped). Omit for no limit.- Error isolation — a worker that throws becomes an
ok: falseresult; siblings continue. - Observer safety — an
onEventcallback that throws is logged and swallowed; it never aborts the run.
Supervisor validates its options at construction (plan, workers, positive concurrency / maxSubtasks), and run() rejects an empty task, so misconfiguration fails fast with a clear message.
Scope (what's deferred)
The seed dispatches autonomous workers via agent.prompt(). A worker that pauses for a client-tool or approval round-trip is reported as a failed subtask — durable pause/resume across a supervised run (building on ai-sdk's SubAgentRunStore + resume primitives) is a deferred adapter, as are other topologies (pipelines, debate) and queue-backed long-running execution. Those land on demand, behind optional seams, not in the core.
License
MIT