npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@loopstack/hitl

v0.4.4

Published

Human-in-the-loop workflows for Loopstack — ask questions, present options, and request confirmations from users during workflow execution

Readme


title: Human-in-the-Loop Module description: HITL workflows and tools for Loopstack — AskUserWorkflow (free-text, confirm, multiple-choice), ConfirmUserWorkflow (markdown review + confirm/deny), AskClarificationTool, AskForApprovalTool, document types for UI rendering

@loopstack/hitl

Human-in-the-loop module for the Loopstack automation framework.

Pause a running workflow or agent loop, ask the user a question or request confirmation, and resume once they answer. Ships with ready-to-use workflows, agent tools, and document types that render prompts in the Studio UI.

When to Use

  • Your workflow needs user input before it can continue — a name, a choice, a yes/no decision. Use AskUserWorkflow as a sub-workflow.
  • Your workflow needs explicit approval of generated content (e.g. a plan, a summary, a code diff). Use ConfirmUserWorkflow.
  • Your LLM agent needs to ask the user a question mid-loop without exiting. Use AskClarificationTool — it pauses the agent, collects the answer, and resumes.
  • Your LLM agent needs user approval before proceeding. Use AskForApprovalTool — it shows markdown content and waits for confirm/deny.

Installation

npm install @loopstack/hitl

Register the module:

import { Module } from '@nestjs/common';
import { HitlModule } from '@loopstack/hitl';

@Module({
  imports: [HitlModule],
  providers: [MyWorkflow],
})
export class MyModule {}

Quick Start

Sub-workflow: Ask a question

import { z } from 'zod';
import { BaseWorkflow, CallbackSchema, MessageDocument, Transition, Workflow } from '@loopstack/common';
import { AskUserWorkflow } from '@loopstack/hitl';

const AnswerCallback = CallbackSchema.extend({
  data: z.object({ answer: z.string() }),
});

@Workflow({ title: 'My Workflow' })
export class MyWorkflow extends BaseWorkflow {
  constructor(private readonly askUser: AskUserWorkflow) {
    super();
  }

  @Transition({ to: 'waiting' })
  async start(state: Record<string, unknown>): Promise<Record<string, unknown>> {
    await this.askUser.run(
      { question: 'What is your name?' },
      { callback: { transition: 'answerReceived' }, show: 'inline', label: 'Waiting for answer...' },
    );
    return state;
  }

  @Transition({ from: 'waiting', to: 'end', wait: true, schema: AnswerCallback })
  async answerReceived(state: Record<string, unknown>, payload: z.infer<typeof AnswerCallback>): Promise<unknown> {
    await this.documentStore.save(MessageDocument, {
      role: 'assistant',
      text: `Hello, ${payload.data.answer}!`,
    });
    return {};
  }
}

Agent tool: Ask for clarification

Register the tool in your module so an LLM agent can call it mid-loop:

import { Module } from '@nestjs/common';
import { AgentModule } from '@loopstack/agent';
import { HitlModule } from '@loopstack/hitl';
import { AskClarificationTool } from '@loopstack/hitl';

@Module({
  imports: [AgentModule, HitlModule],
  providers: [MyWorkflow, AskClarificationTool],
})
export class MyModule {}

Then include it in the agent's tool list:

await this.agent.run({
  system: 'You are a helpful assistant. Ask the user for clarification when needed.',
  tools: ['search', 'ask_clarification'],
  userMessage: 'Help me plan my project.',
});

How It Works

AskUserWorkflow

A sub-workflow with three modes, selected via the mode arg:

start → show_question → waiting_for_user → end

The show_question state uses guard-based routing to save the correct document type:

  • text (default) — saves AskUserDocument, renders a free-text input
  • options — saves AskUserOptionsDocument, renders a choice list
  • confirm — saves AskUserConfirmDocument, renders yes/no buttons

Returns: { answer: string }

Multiple-choice and confirmation modes

// Multiple choice
await this.askUser.run(
  {
    question: 'Which environment?',
    mode: 'options',
    options: ['staging', 'production'],
    allowCustomAnswer: false,
  },
  { callback: { transition: 'envSelected' } },
);

// Yes/No confirmation
await this.askUser.run(
  {
    question: 'Proceed with deletion?',
    mode: 'confirm',
  },
  { callback: { transition: 'confirmed' } },
);

ConfirmUserWorkflow

Shows markdown content and waits for a confirm or deny response:

start → waiting_for_confirmation → end

Two wait transitions (userConfirmed / userDenied) resolve to different results.

Returns: { confirmed: boolean, markdown: string }

import { ConfirmUserWorkflow } from '@loopstack/hitl';

const result = await this.confirmUser.run(
  { markdown: '## About to commit\n\n- 3 files changed' },
  { callback: { transition: 'decisionReceived' } },
);

Agent Tools

Both tools follow the same pattern: launch the corresponding sub-workflow, return a pending result, and complete when the user responds. The agent loop pauses automatically while waiting.

Args Reference

AskUserWorkflow

| Arg | Type | Required | Description | | ------------------- | ---------------------------------- | -------- | -------------------------------------- | | question | string | yes | The question to display | | mode | 'text' \| 'options' \| 'confirm' | no | Presentation mode (default: 'text') | | options | string[] | no | Choices when mode is 'options' | | allowCustomAnswer | boolean | no | Show free-text input alongside options |

Returns: { answer: string }

ConfirmUserWorkflow

| Arg | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------- | | markdown | string | yes | Markdown content to show for review |

Returns: { confirmed: boolean, markdown: string }

Tools Reference

ask_clarification

Ask the user a clarification question mid-agent-loop. Pauses the agent, waits for user input, resumes with the answer.

| Arg | Type | Required | Description | | ------------------- | ---------------------------------- | -------- | --------------------------------- | | question | string | yes | The clarification question | | mode | 'text' \| 'options' \| 'confirm' | no | Presentation mode | | options | string[] | no | Choices when mode is 'options' | | allowCustomAnswer | boolean | no | Allow free-text alongside options |

Returns: the user's answer as a string

ask_for_approval

Present markdown content to the user for approval. Pauses the agent until the user confirms or denies.

| Arg | Type | Required | Description | | --------- | -------- | -------- | ---------------------------------------- | | concept | string | yes | Markdown content to present for approval |

Returns: { concept: string } if approved, { denied: true } if denied

Public API

  • Module: HitlModule
  • Workflows: AskUserWorkflow, ConfirmUserWorkflow
  • Tools: AskClarificationTool, AskForApprovalTool
  • Documents: AskUserDocument, AskUserConfirmDocument, AskUserOptionsDocument, ConfirmUserDocument

Dependencies

  • @loopstack/commonBaseWorkflow, BaseTool, decorators
  • @loopstack/coreLoopCoreModule

Related

  • Human-in-the-Loop Patterns — wait transitions, document actions, conditional widgets
  • hitl-example-module — comprehensive HITL examples: custom document with widget, AskUserWorkflow / ConfirmUserWorkflow shortcuts, and agent-tool flows

About

Author: Jakob Klippel

License: MIT