Skip to Content
Loopstack AI Logo

What if building for AI
was easy.

A complete TypeScript framework built on NestJS + React. Combine agentic loops and deterministic workflows in one state machine powered system, integrate anywhere, with built-in user interface, pause for human input, and resume from where you left off.

Free & open source under MIT license

Four Building Blocks

Workspaces, Workflows, Tools and Documents

default.workspace.ts
@Workspace()
export class DefaultWorkspace {

  @InjectWorkflow() triage: TriageWorkflow;
  @InjectWorkflow() agent: AgentWorkflow;

  @InjectTool() read: ReadTool;
  @InjectTool() write: WriteTool;
  @InjectTool() glob: GlobTool;
}

Workspaces

A workspace is the top-level container for your application. It declares which workflows users can run and which tools are available. Tools can be registered on the workspace or workflow level.

Learn more

Configurable UI

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

The workflow renders the UI, pauses execution, the user responds, and the workflow continues.

ask-user-options.ui.yaml
type: document
ui:
  widgets:
    - widget: choices
      options:
        transition: userAnswered
Rendered choices widget in Loopstack Studio

Agents With Full Control

Use a ready made agent or create your own flow. Same system, just the right level of abstraction.

Call an Agent

orchestrator.workflow.ts
@Transition({ from: 'planning', to: 'running' })
async runAgent() {
  await this.agent.run(
    {
      system: 'You are a code review agent.',
      tools: ['read', 'glob', 'grep'],
      userMessage: 'Review the auth module.',
    },
    { callback: { transition: 'agentDone' } },
  );
}

@Transition({ from: 'running', to: 'implementing', wait: true })
async agentDone(payload) {
  this.review = payload.data.response;
}

Build Your Own

review-agent.workflow.ts
@Transition({ from: 'ready', to: 'prompt_executed' })
async llmTurn() {
  const result = await this.claude.call({
    tools: ['read', 'glob'],
  });
  this.llmResult = result.data;
}

@Transition({ from: 'prompt_executed', to: 'awaiting_tools' })
@Guard('hasToolCalls')
async executeTools() {
  this.result = await this.delegate.call({
    message: this.llmResult,
  });
}

@Transition({ from: 'awaiting_tools', to: 'ready' })
@Guard('allToolsComplete')
async loop() {}

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

Nested Agents and -Workflows

Let agents launch sub-agents: Just wrap them in tools.

1. Define a workflow

test-runner.workflow.ts
@Workflow({ schema: z.object({ directory: z.string() }) })
export class TestRunnerWorkflow extends BaseWorkflow {
  // runs tests, returns results
}

2. Wrap it as a tool

run-tests.tool.ts
@Tool({ schema: z.object({ directory: z.string() }) })
export class RunTestsTask extends BaseTool {
  @InjectWorkflow() testRunner: TestRunnerWorkflow;

  async call(args, options) {
    const result = await this.testRunner.run(
      args, { callback: options?.callback },
    );
    return { pending: { workflowId: result.workflowId } };
  }
}

3. Let the agent use it

build-agent.workflow.ts
await this.claude.call({
  system: 'You are a build agent.',
  tools: ['read', 'write', 'runTests'],
});

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

Built-In Error Recovery

Auto-retry, timeout, and custom error states.

Auto-Retry

workflow.ts
@Transition({
  from: 'fetching',
  to: 'done',
  retry: 3,
})
async fetchData() {
  await this.http.call({ url });
}

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

Timeout

workflow.ts
@Transition({
  from: 'analyzing',
  to: 'done',
  timeout: 5000,
})
async analyze() {
  await this.analyzer.call({ data });
}

Kills the transition after 5s. Combine with retry to auto-retry on timeout.

Error States

workflow.ts
@Transition({
  from: 'deploying',
  to: 'deployed',
  retry: { place: 'deploy_failed' },
})
async deploy() {
  await this.deployer.call({});
}

Routes to a custom error state with recovery transitions.

Documents and state roll back automatically on failure. Every error is recorded as an audit trail. Manual retry is always available as a fallback.

Human-in-the-Loop

Pause for human input. Resume hours or days later. State is always preserved.

Ask a Question

workflow.ts
@Transition({ from: 'planning', to: 'asking' })
async askUser() {
  await this.askUser.run(
    {
      question: 'Which database?',
      options: ['Postgres', 'MySQL', 'SQLite'],
    },
    { callback: { transition: 'userAnswered' } },
  );
}

// pauses until user answers
@Transition({ from: 'asking', to: 'answered', wait: true })
async userAnswered(payload) {
  this.selectedDb = payload.data.answer;
}

Render the UI

ask-user.ui.yaml
type: document
ui:
  widgets:
    - widget: choices
      options:
        transition: userAnswered

Documents define the data, YAML configures how it renders. The workflow sleeps until the user responds — no polling, no timeouts, no lost state.

Run and Interact in the browser

Use the built-in ReactJS frontend for running, debugging and organizing automations.

Loopstack Studio workflow example 1

Community Registry

Ready made templates, tools and integrations. Install with a single command.

Terminal
$ loopstack add <package-name>

Get Started

One command to set up your local development environment.

$npx create-loopstack-app my-app
NestJS backend React frontend Docker Services

Key Benefits

Agentic Meets Deterministic

Drop an LLM call into any point in a deterministic workflow, or nest agents inside structured pipelines. One system, not two bolted together.

State Machine Workflows

Explicit states and transitions, not arbitrary graphs. Formal guarantees about valid paths make workflows easy to reason about, debug, and visualize.

Persistent State

Every step is checkpointed to the database. If a process fails, resume from the last successful state. Works the same for workflow steps and agentic loops.

Built-in Human Interaction

Approvals, forms, confirmations, and clarifications ship as framework primitives. Pause for human input for hours or days and resume cleanly.

Full-Stack on NestJS + React

Dependency injection, modules, guards, and a massive ecosystem out of the box. The React frontend ships end-user applications, not just backend pipelines.

Full Traceability

Every state transition, tool call, and LLM decision is recorded. Replay any execution step by step, audit what happened, and pinpoint exactly where things went wrong.

Ready-Made Registry

Install production-grade tools and workflow templates with a single command. AI providers, OAuth flows, sandboxed code execution, and more - ready to use or customize.

Nested Sub-Workflows

Break complex processes into composable sub-workflows that run independently and report back. Nest them arbitrarily deep - each with its own state, tools, and lifecycle.

Framework, Not a Platform

Extend it like any NestJS app. No vendor lock-in, no external execution engine. Free and open source under the MIT license.

Supported by:

IFB IS Logo