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-examples

v0.2.2

Published

Workflow examples for Human-in-the-Loop patterns in Loopstack — custom Document with widget, AskUserWorkflow / ConfirmUserWorkflow sub-workflow shortcuts, LLM agent loops with ask_clarification / ask_for_approval, and a meeting-notes review flow.

Readme


title: HITL Examples description: Workflow examples for Human-in-the-Loop patterns in Loopstack — custom Document with a form widget, AskUserWorkflow / ConfirmUserWorkflow sub-workflow shortcuts, LLM agent loops with ask_clarification / ask_for_approval, and an end-to-end meeting-notes review flow.

@loopstack/hitl-examples

Human-in-the-Loop workflow examples for the Loopstack automation framework.

Side-by-side examples of every way to pause a Loopstack workflow for the user. The point of this package is to answer one question: when do I design a custom document with a widget, vs. delegate to a HITL sub-workflow, vs. let an LLM agent ask the user?

Decision matrix

| You are building... | Use | Examples | | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | A predefined workflow with known user-input steps — form fields, structured data, a record the user edits | Custom document with a widget — your workflow owns the document and the wait: true transition | Inline Form, Prompt Input Chat, Meeting Notes | | An LLM agent loop where the LLM dynamically decides to ask the user something or request approval | Agent tools ask_clarification / ask_for_approval from @loopstack/hitl | Agent Ask Clarification, Agent Ask For Approval | | A predefined workflow that just needs a quick generic ask — one free-text field, yes/no, pick-one — and you don't want to design a form | Sub-workflow shortcut — AskUserWorkflow / ConfirmUserWorkflow from @loopstack/hitl | Ask User Text, Ask User Options, Ask User Confirm, Confirm Content |

Install as Source (Recommended)

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

Then register the module in your app:

import { Module } from '@nestjs/common';
import { LoopstackModule } from '@loopstack/loopstack-module';
import { HitlExamplesModule } from './hitl-examples/hitl-examples.module';

@Module({
  imports: [LoopstackModule.forRoot(), HitlExamplesModule],
})
export class AppModule {}

Install as a Dependency

npm install @loopstack/hitl-examples
import { HitlExamplesModule } from '@loopstack/hitl-examples';

Required app-module configuration

HitlExamplesModule brings its own HitlModule, AgentModule, and ClaudeModule. The Meeting Notes and Agent examples additionally call LlmGenerateObjectTool / LlmGenerateTextTool from @loopstack/llm-provider-module. That module is @Global and must be configured once in your root module to set the default model:

import { Module } from '@nestjs/common';
import { HitlExamplesModule } from '@loopstack/hitl-examples';
import { LlmProviderModule } from '@loopstack/llm-provider-module';
import { LoopstackModule } from '@loopstack/loopstack-module';

@Module({
  imports: [LoopstackModule.forRoot(), LlmProviderModule.forRoot({ model: 'claude-sonnet-4-6' }), HitlExamplesModule],
})
export class AppModule {}

ClaudeModule (re-exported from HitlExamplesModule) registers the Claude provider with the LLM registry; LlmProviderModule.forRoot(...) sets the default model the tools dispatch to. The non-agent examples (Inline Form, Prompt Input Chat, Ask User *, Confirm Content) work without it.

Environment

The agent and meeting-notes examples require Claude credentials:

ANTHROPIC_API_KEY=sk-ant-...

The non-agent examples (Inline Form, Prompt Input Chat, Ask User *, Confirm Content) work without any LLM provider.

Examples

| Example | Studio title | Description | | --------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------- | | Inline Form | HITL - Inline Form Example | Custom Document with a form widget, wait: true payload typed by the document schema | | Prompt Input Chat | HITL - Prompt Input Chat Example | Workflow-level prompt-input widget; loops on user messages without an LLM | | Ask User Text | HITL - Ask User Text Example | AskUserWorkflow for a generic free-text question | | Ask User Options | HITL - Ask User Options Example | AskUserWorkflow with mode: 'options' and allowCustomAnswer | | Ask User Confirm | HITL - Ask User Confirm Example | AskUserWorkflow with mode: 'confirm' for yes/no decisions | | Confirm Content | HITL - Confirm Content Example | ConfirmUserWorkflow to show a pre-rendered markdown blob for explicit approval | | Agent Ask Clarification | HITL - Agent Ask Clarification Example | LLM agent with ask_clarification tool — pauses to request missing info | | Agent Ask For Approval | HITL - Agent Ask For Approval Example | LLM agent with ask_for_approval tool — drafts content and pauses for confirm/deny | | Meeting Notes | HITL - Meeting Notes Example | End-to-end document-review flow: user notes → LLM-extracted structure → user approval |


Inline Form

The workflow owns a FeedbackFormDocument whose schema doubles as the wait: true transition payload. The form's Submit button triggers the transition with typed data — no wrapping or unpacking.

Files

  • inline-form-example.workflow.ts — workflow class
  • feedback-form-document.ts — @Document with Zod schema
  • feedback-form-document.yaml — form widget definition

Prompt Input Chat

Workflow-level widget: prompt-input with enabledWhen: [waiting_for_user]. Loops on user messages and echoes them back — no LLM and no sub-workflow involved. Demonstrates the chat-style entry pattern that LLM agent loops are built on.

Files

  • prompt-input-chat-example.workflow.ts — workflow class
  • prompt-input-chat-example.workflow.yaml — workflow-level prompt-input widget

Ask User Text

Delegate a generic free-text question to AskUserWorkflow. The parent workflow only owns the callback transition that receives the answer. Use the inline-form pattern instead when you need a structured form.

Files

  • ask-user-text-example.workflow.ts — workflow class

Ask User Options

Delegate a "pick one from a list" question to AskUserWorkflow with mode: 'options'. allowCustomAnswer: true adds a free-text field next to the choices.

Files

  • ask-user-options-example.workflow.ts — workflow class

Ask User Confirm

Delegate a yes/no decision to AskUserWorkflow with mode: 'confirm'. The answer comes back as the literal string 'yes' or 'no'.

Files

  • ask-user-confirm-example.workflow.ts — workflow class

Confirm Content

Hand a pre-rendered markdown blob to ConfirmUserWorkflow for a user-facing review. The callback receives { confirmed: boolean, markdown: string }. Useful for showing a release plan, summary, or diff for explicit approval.

Files

  • confirm-content-example.workflow.ts — workflow class

Agent Ask Clarification

Launches AgentWorkflow with tools: ['ask_clarification']. When the LLM lacks information, it issues an ask_clarification tool call — the loop pauses and prompts the user, then the answer flows back into the agent context.

Files

  • agent-ask-clarification-example.workflow.ts — workflow class

Agent Ask For Approval

Launches AgentWorkflow with tools: ['ask_for_approval']. The LLM drafts content (release notes, in this example), then calls ask_for_approval with the markdown — the loop pauses until the user explicitly confirms or denies.

Files

  • agent-ask-for-approval-example.workflow.ts — workflow class

Meeting Notes

A complete HITL document-review flow:

  1. Save a MeetingNotesDocument with the user's raw notes (form widget for edits)
  2. User reviews/edits and submits via wait: true transition
  3. LlmGenerateObjectTool extracts structured data via Zod schema
  4. Save OptimizedNotesDocument for the user to review/approve
  5. Final approval via second wait: true transition

Combines custom documents, LLM-driven structured output, and two HITL review steps.

Files

  • meeting-notes-example.workflow.ts — workflow class
  • documents/meeting-notes-document.{ts,yaml} — input form document
  • documents/optimized-notes-document.{ts,yaml} — structured output document
  • templates/extract-notes.md — LLM prompt template

Testing

Every workflow ships with an inner-loop spec in its __tests__/ folder — run them with npm run test (in-process, no backend, no API key). The specs double as the reference for how to test each HITL flavor, extending the decision matrix above:

| HITL flavor | How its tests work | Spec | | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | Custom document + own wait: true transition | Script the submission with answers, keyed by the wait-transition method name | inline-form | | Sub-workflow shortcut (AskUserWorkflow / ConfirmUserWorkflow) | Script the child's wait transition (userAnswered, userConfirmed / userDenied) — answers reaches inline sub-workflows at any depth | ask-user-*, confirm-content | | Chat loop (same wait transition parks repeatedly) | queue('first', 'second') submits one message per park, then the run parks again; scripted LLM replies via replay | prompt-input-chat | | LLM step inside a predefined workflow | replay scripts the LLM response; answers drives the surrounding HITL steps | meeting-notes | | Agent HITL (ask_clarification / ask_for_approval) | replayTools: [LlmGenerateTextTool] scripts only the LLM turns; the agent loop, delegation, and the real HITL child run live; answers resumes the nested child | agent-ask-clarification, agent-ask-for-approval |

The record/refresh loop. The replay specs here use hand-written LLM turns for readability; for your own workflows, record fixtures from a real run instead: run the test once with fixture: '<path>' and live providers (records on the first run, replays afterwards — delete the file to re-record), or derive one from a backend run started with loopstack run --trace via loopstack runs <run-id> --record <file> --tools llm_generate_text. When a replayed test fails with a drift error after a prompt change, that is the signal: run the live check-ups, and re-record when green.

Live check-ups (*.live.spec.ts) run the same flows against the real model with structural assertions — npm run test:live, needs ANTHROPIC_API_KEY, on demand after prompt or model changes, never in the PR gate.

Outer loop. Before relying on a flow, run the real thing once from the terminal and watch it: loopstack run <workflow_name> follows the run live and prompts you at every HITL step; loopstack runs shows the inbox of runs waiting for input.

See the testing guide for the full story.

About

Author: Jakob Klippel

License: MIT