Skip to content

Runtime

The ADL runtime wires process-level services: message and workflow stores, observers, template engine, and workflow context scope.

Construct the runtime in src/adl.ts (recommended) and set adl on adl.config.ts. Registry code should import { adl } from "#adl" — see Project setup for the tsconfig alias. Tooling loads the runtime via loadAdlProject().getAdl() — not by importing src/adl.ts directly.

Drizzle/tRPC-style factory — primary app entrypoint:

import { createAdlRuntime, inMemoryMessageStore, inMemoryWorkflowStore } from "@agent-dev-lab/core";
import { openai } from "@ai-sdk/openai";
const adl = createAdlRuntime({
stores: {
message: inMemoryMessageStore(),
workflow: inMemoryWorkflowStore(),
},
observers: { workflows: [], agents: [] },
});
const researcher = adl.createAgent({
id: "researcher",
model: openai("gpt-4o"),
instructions: "You are a research assistant.",
});
const review = adl.createWorkflow({
id: "literature-review",
run: async (input: { topic: string }, ctx) => {
await ctx.step("research", async ({ ctx: child }) => {
await researcher.run({
memoryScope: child.memoryScope("notes"),
user: input.topic,
});
});
return { topic: input.topic };
},
});
const handle = review.run({ topic: "CRISPR delivery" });
handle.workflowRunId; // subscribe immediately (WorkflowStore / future SSE)
await handle.result;

adl.createAgent / adl.createWorkflow bind the runtime automatically — use these in project code.

Second argument on adl.createAgent / adl.createWorkflow:

import { inMemoryMessageStore, type AgentObserver, type MessageStore } from "@agent-dev-lab/core";
import { openai } from "@ai-sdk/openai";
// continues the createAdlRuntime example above
const episodeLogger: AgentObserver = {
onEvent: (event) => console.log(event.type, event),
};
const customStore: MessageStore = inMemoryMessageStore();
const researcher = adl.createAgent(
{ id: "researcher", model: openai("gpt-4o"), instructions: "You are a research assistant." },
{
observers: { agents: [episodeLogger] }, // appended to runtime defaults
stores: { message: customStore }, // replaces default for this agent
},
);
  • stores.message / stores.workflow: replace the runtime default for this agent/workflow/run.
  • observers.workflows / observers.agents: append to lists from createAdlRuntime (not replace).

Project code should use adl.createAgent, adl.createWorkflow, and adl.createTemplate. The package also exports createAgent(runtime, …), createWorkflow(runtime, …), and createTemplate(runtime, …) for unit tests and libraries that need an explicit runtime handle without a project adl module. Same behavior — prefer the bound methods in application code.

ALS is not used for runtime services (stores, observers) — those are passed explicitly.

ALS is used for workflow context propagation: when agent.run / agent.stream is called inside a workflow body or step, the active WorkflowContext is available so agents attach to the correct workflowRunId / stepId without manual wiring.

Callers can still pass workflow: { workflowRunId, stepId } explicitly — that takes priority over ALS.

WorkflowContext is a host object. step and emit close over parent services, workflowRunId, and step registry.

  • Child contexts are built from the parent host when ctx.step("name", async ({ ctx }) => …) runs.
  • Do not destructure ctx (const { step } = ctx breaks method binding).

Inside a workflow step (researcher defined in registry; query from workflow input):

await ctx.step("research", async ({ ctx: child }) => {
await researcher.run({
memoryScope: child.memoryScope("notes"),
user: query,
});
});

Tools created via adl.createToolFromAgent / adl.createToolFromWorkflow require ALS — they must be called from within a workflow run.

const handle = review.run(input);
handle.workflowRunId;
await handle.result;
  • Public API: run(input) for root runs (CLI, UI).
  • Author API: inside a workflow, otherWorkflow.run(input) nests via ALS.
  • No { project } on the execution path.

Nested runs can pass parentCtx explicitly:

// inside literature-review run; searchPapers imported from workflows/search-papers.ts
await ctx.step("search", async ({ ctx: child }) => {
const result = await searchPapers.run({ topic: input.topic }, { parentCtx: child }).result;
});

When stores are omitted, createAdlRuntime defaults to in-memory implementations for both message and workflow. SQLite-backed stores are planned in @agent-dev-lab/common but not shipped yet.