@agentcompose/engine
v0.1.2
Published
Headless orchestration engine for AgentCompose — turn a goal into a plan and execute it across configurable agents.
Readme
@agentcompose/engine
Headless orchestration for AgentCompose — turn a goal into a plan and execute it across configurable agents. Built on
@agentcompose/sdk.
Version: 0.1.0 · License: Apache-2.0 · Requires: Node ≥ 18.19
This is layer ③ of the AgentCompose stack — the engine that products (UI, agent builders, goal-based assistants) build on. It is headless: no UI, no database, no presentation. It exposes interfaces a product injects.
④ PRODUCT UI · memory store · human-in-the-loop UX (separate, on top)
③ ENGINE goal → plan → execute · this repo
② SDK define / run / compose one agent @agentcompose/sdk
① SPEC the AgentCompose contract agentcompose/specTwo parts: the hands and the brain
| Part | Role | Status |
|------|------|--------|
| Coordinator | the hands — execute calls across agents, wire output→input, forward progress, propagate cancel/errors | ✅ built |
| Engine | the brain + chassis — accept a goal, run a plan (authored or dynamically decided) through governance, durably and resumably; also an agent itself (asAgent) | ✅ chassis + dynamic planner + recursion |
The difference is goal-based vs imperative: you hand the Coordinator explicit calls; you hand the Engine a goal and it decides the calls. A master agent is itself an AgentCompose agent — composition is recursive.
Coordinator today
import { inProcess } from "@agentcompose/sdk";
import { Coordinator } from "@agentcompose/engine";
const team = new Coordinator([
{ name: "researcher", client: inProcess(researcher), config: { depth: "deep" } },
{ name: "summarizer", client: inProcess(summarizer) },
]);
const research = await team.call("researcher", goal); // run one member
const summary = await team.call("summarizer", research.parts); // wire output → input
const [a, b] = await team.callMany([...]); // fan out in parallel
await team.close();Coordinator is transport-agnostic — members may be inProcess or spawnStdio
(subprocess) clients; the code is identical.
Install
npm install @agentcompose/engineShips compiled JS + type declarations; runs on Node ≥ 18.19. Depends on
@agentcompose/sdk.
Try the demos
npm run demo:engine "AI agent interoperability" # authored workflow: goal → fixed DAG
npm run demo:dynamic "AI agent interoperability" # dynamic: goal → decided step-by-step (offline)
npm run demo:nested "AI agent interoperability" # recursion: an engine running as a step inside an engine
npm run demo:team "AI agent interoperability" # Coordinator: a master agent composing two membersThe engine demos run a researcher → summarizer flow — the first as a fixed DAG, the
second decided a step at a time by a (scripted, offline) brain over the decide port.
See DESIGN.md for the architecture and the reasons behind it.
Develop
npm install
npm test # coordinator: chaining, progress forwarding, failure, parallel
npm run typecheck
npm run build # emit dist/ (compiled JS + d.ts) as published to npmAuthoring vs distribution. This repo is authored in strip-mode TypeScript and run directly by Node in development;
npm run buildemits thedist/that is published to npm. Consumers get standard JavaScript + declarations.
What's built, and what's next
Built — the durable deterministic slice: Engine.run/resume, the planner
loop, variable-reference DAG execution, dependency ordering, runtime governance
(allow/block/rewrite/approve), per-step checkpointing, durable suspend/resume for
human approval, and cancellation. Reference in-memory CheckpointStore
and an authoredPlan planner.
Built — step resilience: transient-only retry with exponential backoff + jitter,
per-step timeouts, and ordered fallback agents (step.fallback), all behind
step-failed with step-retry/step-fallback events for observability. The
classifier retries only transient failures (rate-limit / 5xx / network / timeout);
deterministic errors fail fast. Per-step knobs are JSON-only, so durable
suspend/resume is unaffected; retries are in-process (resume re-runs a step from
attempt 1, since steps are idempotent).
Built — the dynamic planner (Tier A): a goal-driven dynamicPlanner over a tiny
decide port (engine core stays model-dependency-free), a no-network ScriptedDecider,
and one opt-in reference adapter (@agentcompose/engine/adapters/openai) — a raw fetch
to any OpenAI-compatible baseUrl, so a gateway handles provider portability. Pi's
pi-ai, the Vercel AI SDK, Instructor, or provider-native structured outputs are
drop-in alternatives at the same port.
Built — recursive composition (asAgent) — the engine's "publish" button: the SDK
lets you publish an agent you wrote (defineAgent); the engine lets you publish an agent
you composed (asAgent({ descriptor, engine })). Both emit the same shape, so a composed
team can be consumed — or shipped as a dependency — exactly like a leaf agent. (A
descriptor is a nameplate — id/version/capabilities — not "an AI"; an engine carries one
the same way a leaf does.) Governor approval bridges to the agent's input-required state.
Unused at a single level; it earns its place when a composition is shipped as a dependency.
Deferred (clearly): parallel execution of independent steps (the DAG already encodes the graph); real persistence
- per-
runIdlocking; exactly-once via idempotency keys; durable resume across theasAgentboundary; cross-run memory (its first consumer is the planner); typed capability I/O. SeeDESIGN.md.
