@warlock.js/ai
Standalone — usable in any Node project, no
@warlock.js/corerequired.
@warlock.js/ai is the AI orchestration layer of Warlock. It gives you a small ladder of primitives — agents, workflows, supervisors — that share one result envelope, one error hierarchy, one event model, and one persistence story. You pair it with a provider adapter (@warlock.js/ai-openai, @warlock.js/ai-anthropic, etc.) and you have a typed, testable, resumable AI runtime.
The package is provider-agnostic on purpose. The model, the embedder, the pricing — those live in the adapter. The orchestration — tool loops, retries, snapshot resume, structured output, middleware — lives here.
The 4-primitive ladder
Section titled “The 4-primitive ladder”ai.agent() → single LLM turn, tool loop, structured outputai.workflow() → deterministic multi-step pipeline, resumableai.supervisor() → multi-intent router with specialists, iterativeai.orchestrator() → stateful session-aware orchestration (durable sessions, resume)Each primitive is an escape hatch to the next rung of complexity. Start at the bottom; graduate up only when you need to. Every primitive returns the same envelope ({ data, error, usage, report }) and exposes .asTool(), so they compose freely.
Beyond the ladder
Section titled “Beyond the ladder”The four rungs cover the core orchestration shapes, but the package has grown a wider verb set around them — every one returning the same envelope and composing through the same tool surface.
ai.team()— a manager + role-named members (builder, reviewer, fixer) with a built-in quality gate. Thin sugar over a supervisor for the review-then-fix and test-then-fix shapes.ai.planner()— an LLM generates an ordered plan over your capabilities, then runs it step by step. The escape hatch for when you can’t name the steps up front.ai.memory()— working (in-run scratch) plus semantic recall across turns, backed by anyCacheDriver.ai.rag()— document retrieval: chunk, embed, store, retrieve, rerank, cite. Drop it into an agent’stoolswith.asTool().ai.skills()— a runtime skills library: an always-injected metadata catalog plus an on-demandloadSkilltool.ai.prompts— one process-widename@versionprompt registry; a namedsystemPrompt(...)auto-registers, then resolves / merges / validates / diffs by name (ai.promptis now a thin facade over it).ai.dataset()andai.vcr()— evaluation datasets foragent.eval(...)and record/replay of model traffic for deterministic tests.ai.guardrail()— content-intelligence guardrails (moderation, PII, injection, topic) as composed input / output / tool middleware.ai.human— human-in-the-loop tool approval with durable interrupt / resume.
A separate observability package, @warlock.js/ai-panoptic, exports run traces to OpenTelemetry, Langfuse, the console, a file, or a queryable store.
Why a separate package
Section titled “Why a separate package”- Standalone. Drop it into a CLI, a worker, a script, an existing Express app. No Warlock framework required.
- Typed end to end.
ai.agent({ output: schema })flows the schema’s inferred type through toresult.data. Same forai.workflow<TInput, TOutput, TState>. execute()never throws. Failures funnel intoresult.erroras a typedAIErrorsubclass. You branch oninstanceofor on the stableerror.codestring.- One result shape.
{ data, error, usage, report }— agents, workflows, supervisors. Cost rolls up the tree. - Persistence is delegated. Snapshot resume + semantic cache both accept any
CacheDriverfrom@warlock.js/cache. The AI package owns no storage. - Logging is delegated. Every primitive emits through the
logsingleton from@warlock.js/logger. Configure channels once at boot; the framework picks it up.
What’s in this section
Section titled “What’s in this section”- Installation — install the core package plus a provider adapter.
- Pick a provider — OpenAI vs Anthropic vs Bedrock vs Google vs Ollama.
- Your first agent — five-minute walkthrough from zero to a streaming agent.
- Troubleshooting — the first errors you’ll hit and the one-line fix for each.
Where to go after
Section titled “Where to go after”There are two reading orders, depending on what you need. For the mental model — why each primitive exists and how they nest — read Architecture Concepts first; those pages are the why. For the how-to — the calls you make day to day — each domain section below is the how, ordered so you can read it top to bottom. The two intentionally overlap: a concept page explains a primitive, its sibling how-to page runs it.
- Architecture Concepts — the mental model behind agents, workflows, supervisors, orchestrators, memory, planner, middleware.
- Agents — run one agent well: streaming, structured output, reasoning control, sub-agents, serving over SSE.
- Tools & Capabilities — define your own tools, use the built-in belt, connect MCP servers, jail a workspace, load runtime skills.
- Orchestration — the ladder above a single agent: workflows, supervisors, teams, orchestrators.
- Prompts — write, version, validate, and compile system prompts.
- RAG & Knowledge — retrieval pipelines, loaders and stores, hybrid ranking, embeddings.
- Modalities — images, speech, transcription, realtime and video.
- Reliability & Safety — typed errors, durable resume, persistence, budgets, guardrails, human-in-the-loop, SSRF and redaction.
- Testing & Evaluation — datasets and evals as a CI gate, record/replay cassettes.
- Observability — the observer seam and
@warlock.js/ai-panoptictraces. - Best Practices — opinionated pillar pages: what a senior dev does, and why.
- Recipes — copy-paste solutions for common patterns.
- Reference — every public export grouped by primitive.
A 30-second example
Section titled “A 30-second example”import { ai } from "@warlock.js/ai";import { OpenAISDK } from "@warlock.js/ai-openai";
const openai = new OpenAISDK({ apiKey: process.env.OPENAI_API_KEY! });
const myAgent = ai.agent({ model: openai.model({ name: "gpt-4o-mini" }), systemPrompt: "You are a concise senior TypeScript engineer.",});
const { text, usage, error } = await myAgent.execute("Why use generics?");
if (error) { console.warn(error.code, error.category);} else { console.log(text, usage.total);}No try/catch. No exception leaks. Typed errors with stable codes. That’s the contract you get from every primitive in this package.