Skip to content
Color theme

Features

Features

Everything you need to build, operate, and trust durable AI workflows — from human-in-the-loop to testing, durability, and observability.

Human-in-the-Loop

A workflow can stop and wait for a person. The waiting state is persisted, so a run can pause for minutes or days without holding a process open, then continue the moment someone answers.

  • Approvals, forms & confirmations
  • Answer from Studio or the terminal
  • Pause for minutes or days

Learn more about human-in-the-loop

@Transition({ from: 'classified', to: 'waiting_for_approval' })
askForApproval(state, ctx) {
this.assignState({ question: 'Escalate to on-call engineering?' });
}
@Transition({ from: 'waiting_for_approval', to: 'end' })
resolve(state, payload: { approved: boolean }) {
this.assignResult({ escalated: payload.approved });
}

Testing, Built In

Test workflows like any other code — unit tests for tools, end-to-end tests for whole runs. Recorded responses stand in for live LLM calls, simulated answers stand in for the human. Fast, deterministic, CI-ready.

  • Unit & End-to-End Tests
  • Record real runs, replay in CI
  • Scripted humans for HITL flows

Read the testing guide

it('escalates a high-severity ticket', async () => {
const run = await runWorkflow(TriageTicketWorkflow, {
args: { ticketId: 1042 },
fixture: 'triage-high-severity',
answers: {
waiting_for_approval: { approved: true },
},
});
expect(run.status).toBe('completed');
expect(run.result).toMatchObject({ escalated: true });
});

Never Lose a Run

Kill the process mid-run — the run survives. Every step is checkpointed to Postgres, so a restart reclaims the job and resumes from the last checkpoint. No lost work, no manual cleanup.

  • Survives crashes & deploys
  • Resumes from the last checkpoint
  • HITL pauses last indefinitely

How durability works

# terminal 1 — the server
▸ analyze_ticket (LLM call in flight)
✗ process killed (SIGKILL)
$ npm start
job stalled — reclaiming
▸ analyze_ticket (re-run from checkpoint)
✓ analyze_ticket (2.6s)
# terminal 2 — the CLI, same session
■ run completed in 7.1s

Inspect Every Run

Every run records what happened — each step with its duration, every tool call and document. Inspect runs live or afterwards, track token usage, and enforce quotas.

  • Every transition, tool call & document traced
  • Token usage & cost quotas
  • Audit events & tool interceptors

See the observability examples

fetch_tickettool120ms
analyze_ticketllm2.4s · 1,208 tok
waiting_for_approvalpark19ms
notifytool340ms
4 transitions · 2 tool calls · 1 document · 4.9s

Code Examples

Agents With Full Control

Tool calling, error recovery, and cancellation built in. Need custom exit logic, setup phases, or human interaction? Copy the agent and make it yours.

@Transition({ from: 'planning', to: 'implementing' })
async runAgent(state: MyState) {
await this.agent.run(
{
system: 'You are a code exploration agent.',
tools: ['glob', 'grep', 'read'],
userMessage: 'Find all API endpoints.',
},
{ callback: { transition: 'agentDone' } },
);
}

Learn more

Nested Agents and Workflows

The LLM decides when to launch sub-workflows. Each one runs in the background and reports back. Nest workflows as you need.

@Transition({ from: 'start', to: 'waiting' })
async delegate(state, ctx) {
await this.researchWorkflow.run(
{ topic: ctx.args.topic },
{
callback: { transition: 'onComplete' },
show: 'inline',
},
);
}

Learn more

Built-In Error Recovery

Retries 3 times with exponential backoff. State rolls back between attempts.

@Transition({
from: 'start',
to: 'fetched',
retry: { attempts: 3, backoff: 'exponential' },
timeout: 30_000,
onError: 'fetch_failed',
})
async fetchTicket(state, ctx) {
// state rolls back between attempts
}

Learn more

Configurable UI

Documents define your data. A YAML config controls how they render — choices, forms, buttons, markdown. No frontend code needed.

widgets:
- type: form
document: ClassificationDocument
fields:
priority:
type: choice
options: [urgent, high, medium, low]
summary:
type: markdown

Learn more

Registry

Browse packages on npm — modules, tools, and workflow examples for your Loopstack app.

npx giget gh:loopstack-ai/loopstack/registry/examples/hitl-examples src/hitl

Pull a package in as a dependency, or copy an example's source and own it.

Ready to Build?

Start a new app or drop Loopstack into your existing NestJS project.

npx @loopstack/cli create my-app