Skip to content
Looms
Documentation
On this page

Runs & threads

A run is what you start, list, and open in a debugger. Inside it, threads form a tree: a root agent or workflow, plus any children it spawned. Agents, DAG workflows, and custom kinds share the same model; nesting is normal.

Thread hierarchyA parent block branches into child blocks on one shared platform, representing a single run. A parent agent spawns child workflows and agents. Different thread kinds share one run and its canonical event stream.
Many threads. One run.A parent agent spawns child workflows and agents. Different thread kinds share one run and its canonical event stream.

Run#

The durability boundary and event-stream container for one logical operation. All child threads, signals, and events belong to one canonical log identified by runId. Crash recovery means replaying that log.

Thread#

The unit of computation inside a run. A thread owns a state machine, may have a parent, and has a thread kind: built-in agent / workflow, or a custom kind from a module.

Definitions live on modules (defineAgent, defineWorkflow, or your own). You start work with looms.start(definition, input); the runtime creates the root thread and begins appending events.

Custom thread kinds#

A custom kind is a module that registers a thread definition and implements m.thread. Stamp the startable definition with createKind (the same shape defineAgent and defineWorkflow already produce). The definition is what you pass to start; the thread is the state machine that handles events for that kind:

ts
import { z } from 'zod'import { createKind, defineEventCatalog, defineModule } from '@swirls/looms/core'const AuctionInput = z.object({  item: z.string(),  reservePrice: z.number(),})const auctionCatalog = defineEventCatalog('auction', {  bid: z.object({ bidder: z.string(), amount: z.number() }),  close: z.object({ reason: z.string().optional() }),})const auction = createKind('auction')const vintageWatch = auction.define({  name: 'vintage-watch',  input: AuctionInput,})const auctionModule = defineModule(  { namespace: 'auction', protocolVersion: '1.0.0', events: auctionCatalog },  (m) => ({    definitions: [vintageWatch],    threads: {      auction: m.thread({        kind: 'auction',        input: AuctionInput,        initialState: (ctx) => ({          item: ctx.input.item,          reservePrice: ctx.input.reservePrice,          highestBid: 0,          status: 'open' as const,        }),        step(state, event) {          switch (event.type) {            case 'auction.bid':              return event.payload.amount > state.highestBid                ? { ...state, highestBid: event.payload.amount }                : state            case 'auction.close':              return { ...state, status: 'closed' as const }            default:              return state          }        },      }),    },  }),)

Start the definition on a host that includes the module. The input type follows the definition's schema:

ts
import { createLooms } from '@swirls/looms/runtime'const looms = createLooms({ modules: [auctionModule] })const { runId } = await looms.start(vintageWatch, {  item: '1968 Chronograph',  reservePrice: 250,})

Wait handling, signaling, and a bids projection live in examples/custom-thread.ts (bun run --filter @looms/examples custom-thread).

Status#

A run (and each thread) is in one of:

  • running — actively reducing or dispatching effects
  • waiting — parked on a timer, approval, child completion, or matching event
  • Terminal: completed, failed, or cancelled

Three orthogonal structures#

A run coordinates heterogeneous work by keeping structure, history, and causation separate:

Thread tree#

Parent/child invocation hierarchy (agent → checkout workflow → approval gate).

Run stream#

One append-only chronological log across all threads in the run.

Causation graph#

Provenance via causationId and effectId linking events to the effects that produced them.

Hierarchy lives in metadata on the same log, so arbitrary nesting stays on one coherent timeline. Formal treatment: Math. Next: Events & effects.