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 🙏

© 2025 – Pkg Stats / Ryan Hefner

human-in-the-loop

v2.0.2

Published

Primitives for integrating human oversight and intervention in AI workflows

Downloads

196

Readme

human-in-the-loop

Primitives for integrating human oversight and intervention in AI workflows. Implements the digital-workers interface for humans operating within a company boundary.

Overview

This package provides a comprehensive toolkit for human-in-the-loop (HITL) workflows, enabling seamless integration of human judgment, approval, and intervention points in automated AI systems.

Key Features:

  • 🎯 Approval Workflows - Multi-step approval gates with escalation
  • Question & Answer - Ask humans for information or guidance
  • 📋 Task Assignment - Delegate tasks that require human judgment
  • 🔀 Decision Points - Request human decisions between options
  • 👀 Review Processes - Code, content, design, and data reviews
  • 📬 Notifications - Multi-channel notifications (Slack, email, SMS, web)
  • 👥 Role & Team Management - Define roles, teams, and capabilities
  • 📊 Goals & OKRs - Track objectives, KPIs, and key results
  • Timeouts & Escalation - Automatic escalation on timeout
  • 🔄 Review Queues - Organize and prioritize pending requests

Installation

pnpm add human-in-the-loop

Quick Start

import { Human, approve, ask, notify } from 'human-in-the-loop'

// Create a Human-in-the-loop manager
const human = Human({
  defaultTimeout: 3600000, // 1 hour
  autoEscalate: true,
})

// Request approval
const result = await approve({
  title: 'Deploy to production',
  description: 'Approve deployment of v2.0.0',
  subject: 'Production Deployment',
  input: { version: '2.0.0' },
  assignee: '[email protected]',
  priority: 'high',
})

if (result.approved) {
  await deploy()
  await notify({
    type: 'success',
    title: 'Deployment complete',
    message: 'v2.0.0 deployed to production',
    recipient: '[email protected]',
  })
}

API Reference

Core Functions

Human(options?)

Create a Human-in-the-loop manager instance.

const human = Human({
  defaultTimeout: 3600000, // Default timeout in ms
  defaultPriority: 'normal', // Default priority level
  autoEscalate: true, // Auto-escalate on timeout
  escalationPolicies: [...], // Escalation policies
  store: customStore, // Custom storage backend
})

approve(params)

Request approval from a human.

const result = await approve({
  title: 'Expense Approval',
  description: 'Approve employee expense claim',
  subject: 'Expense Claim #1234',
  input: { amount: 150, category: 'Travel' },
  assignee: '[email protected]',
  priority: 'normal',
  timeout: 86400000, // 24 hours
  escalatesTo: '[email protected]',
})

// result: { approved: boolean, comments?: string, conditions?: string[] }

ask(params)

Ask a question to a human.

const answer = await ask({
  title: 'Product naming',
  question: 'What should we name the new feature?',
  context: { feature: 'AI Assistant' },
  assignee: '[email protected]',
  suggestions: ['CodeMate', 'DevAssist', 'AIHelper'],
})

// answer: string

do(params)

Request a human to perform a task (implements digital-workers interface).

const result = await do({
  title: 'Review code',
  instructions: 'Review the PR and provide feedback',
  input: { prUrl: 'https://github.com/...' },
  assignee: '[email protected]',
  estimatedEffort: '30 minutes',
})

// result: TOutput (task-specific)

decide(params)

Request a human to make a decision.

const strategy = await decide({
  title: 'Deployment strategy',
  options: ['blue-green', 'canary', 'rolling'],
  context: { risk: 'high', users: 100000 },
  criteria: ['Minimize risk', 'Fast rollback'],
  assignee: '[email protected]',
})

// strategy: 'blue-green' | 'canary' | 'rolling'

generate(params)

Request content generation from a human (specialized form of do()).

const content = await generate({
  title: 'Write blog post',
  instructions: 'Write about our new AI features',
  input: { topic: 'AI Assistant', targetAudience: 'developers' },
  assignee: '[email protected]',
})

// content: string

is(params)

Request validation/type checking from a human.

const valid = await is({
  title: 'Validate data',
  question: 'Is this user data valid and complete?',
  input: userData,
  assignee: '[email protected]',
})

// valid: boolean

notify(params)

Send a notification to a human.

await notify({
  type: 'info', // 'info' | 'warning' | 'error' | 'success'
  title: 'Deployment complete',
  message: 'Version 2.0.0 deployed successfully',
  recipient: '[email protected]',
  channels: ['slack', 'email'], // Optional
  priority: 'normal',
})

Role & Team Management

Role(definition)

Define a human role.

const techLead = Role({
  id: 'tech-lead',
  name: 'Tech Lead',
  description: 'Technical leadership',
  capabilities: ['approve-prs', 'deploy-prod'],
  escalatesTo: 'engineering-manager',
})

Team(definition)

Define a team.

const engineering = Team({
  id: 'engineering',
  name: 'Engineering Team',
  members: ['alice', 'bob', 'charlie'],
  lead: 'alice',
})

registerHuman(human)

Register a human worker.

const alice = registerHuman({
  id: 'alice',
  name: 'Alice Smith',
  email: '[email protected]',
  roles: ['tech-lead'],
  teams: ['engineering'],
  channels: {
    slack: '@alice',
    email: '[email protected]',
  },
})

Goals & Performance Tracking

Goals(definition)

Define goals for a team or individual.

const q1Goals = Goals({
  id: 'q1-2024',
  objectives: ['Launch v2.0', 'Improve performance by 50%'],
  successCriteria: ['Release by March 31', 'Pass benchmarks'],
  targetDate: new Date('2024-03-31'),
})

kpis(kpi)

Track Key Performance Indicators.

kpis({
  id: 'response-time',
  name: 'API Response Time',
  value: 120,
  target: 100,
  unit: 'ms',
  trend: 'down',
})

okrs(okr)

Define Objectives and Key Results.

okrs({
  id: 'q1-2024-growth',
  objective: 'Accelerate user growth',
  keyResults: [
    {
      description: 'Increase active users by 50%',
      progress: 0.3,
      current: 13000,
      target: 15000,
    },
  ],
  period: 'Q1 2024',
  owner: '[email protected]',
})

Advanced Usage

Custom Store Implementation

Implement a custom storage backend:

import { HumanStore } from 'human-in-the-loop'

class DatabaseHumanStore implements HumanStore {
  async create(request) {
    // Save to database
  }

  async get(id) {
    // Fetch from database
  }

  async update(id, updates) {
    // Update in database
  }

  async complete(id, response) {
    // Complete request
  }

  async reject(id, reason) {
    // Reject request
  }

  async escalate(id, to) {
    // Escalate request
  }

  async cancel(id) {
    // Cancel request
  }

  async list(filters, limit) {
    // List requests with filters
  }
}

const human = Human({
  store: new DatabaseHumanStore(),
})

Escalation Policies

Define automatic escalation policies:

const human = Human({
  autoEscalate: true,
  escalationPolicies: [
    {
      id: 'critical-approval',
      name: 'Critical Approval Policy',
      conditions: {
        timeout: 1800000, // 30 minutes
        minPriority: 'critical',
      },
      escalationPath: [
        {
          assignee: 'tech-lead',
          afterMs: 1800000, // 30 minutes
          notifyVia: ['slack', 'sms'],
        },
        {
          assignee: 'engineering-manager',
          afterMs: 3600000, // 1 hour
          notifyVia: ['slack', 'email', 'sms'],
        },
      ],
    },
  ],
})

Approval Workflows

Create multi-step approval workflows:

const workflow = human.createWorkflow({
  id: 'prod-deployment-workflow',
  name: 'Production Deployment Workflow',
  steps: [
    {
      name: 'Code Review',
      role: 'engineer',
      approvers: ['bob'],
      requireAll: true,
    },
    {
      name: 'Security Review',
      role: 'security-engineer',
      approvers: ['security-team'],
      requireAll: false, // Any one approver
    },
    {
      name: 'Tech Lead Approval',
      role: 'tech-lead',
      approvers: ['alice'],
      requireAll: true,
    },
  ],
})

Review Queues

Organize and prioritize pending requests:

const queue = await human.getQueue({
  name: 'High Priority Queue',
  description: 'All high priority pending requests',
  filters: {
    status: ['pending', 'in_progress'],
    priority: ['high', 'critical'],
  },
  sortBy: 'priority',
  sortDirection: 'desc',
  limit: 10,
})

console.log(`Queue: ${queue.name}`)
console.log(`Items: ${queue.items.length}`)

for (const item of queue.items) {
  console.log(`- ${item.title} (${item.priority})`)
}

Request Management

Manually manage requests:

// Get a request
const request = await human.getRequest('req_123')

// Complete with response
await human.completeRequest('req_123', {
  approved: true,
  comments: 'Looks good!',
})

// Reject
await human.rejectRequest('req_123', 'Not ready yet')

// Escalate
await human.escalateRequest('req_123', '[email protected]')

// Cancel
await human.cancelRequest('req_123')

Integration with digital-workers

Human-in-the-loop implements the Worker interface from digital-workers, enabling humans to:

  • Receive notifications, questions, and approval requests via Worker Actions
  • Be targeted by workflow actions
  • Communicate through configured contact channels

Using Worker Actions in Workflows

import { Workflow } from 'ai-workflows'
import { registerWorkerActions, withWorkers } from 'digital-workers'
import type { Worker } from 'digital-workers'

// Define a human worker with contacts
const alice: Worker = {
  id: 'alice',
  name: 'Alice',
  type: 'human',
  status: 'available',
  contacts: {
    email: '[email protected]',
    slack: { workspace: 'company', user: 'U123' },
    phone: '+1-555-1234',
  },
}

const workflow = Workflow($ => {
  registerWorkerActions($)
  const worker$ = withWorkers($)

  $.on.Expense.submitted(async (expense) => {
    // Request approval from manager using Worker Actions
    const approval = await worker$.approve(
      `Expense: $${expense.amount} for ${expense.description}`,
      alice,
      { via: 'slack', timeout: 86400000 }
    )

    if (approval.approved) {
      await worker$.notify(
        expense.submitter,
        'Your expense has been approved!',
        { via: 'email' }
      )
    }
  })

  $.on.Deployment.requested(async (deploy) => {
    // Ask human for confirmation
    const confirmed = await worker$.ask(
      alice,
      `Proceed with deployment of ${deploy.version} to ${deploy.env}?`,
      { schema: { proceed: 'boolean', notes: 'string' } }
    )

    if (confirmed.answer.proceed) {
      $.send('Deployment.approved', deploy)
    }
  })
})

Human as Worker Target

import { registerHuman } from 'human-in-the-loop'
import { notify, ask, approve } from 'digital-workers'

// Register a human worker
const manager = registerHuman({
  id: 'manager',
  name: 'Manager',
  email: '[email protected]',
  roles: ['approver'],
  teams: ['finance'],
  channels: {
    slack: '@manager',
    email: '[email protected]',
  },
})

// Use digital-workers actions directly
await notify(manager, 'Weekly report ready', { via: 'email' })

const answer = await ask(manager, 'What is the Q2 budget?', {
  via: 'slack',
  schema: { amount: 'number', notes: 'string' },
})

const approval = await approve('Hire contractor', manager, {
  via: 'slack',
  context: { role: 'Senior Developer', rate: '$150/hr' },
})

Integration Examples

With AI Workflows

import { Human } from 'human-in-the-loop'
import { AI } from 'ai-functions'

const human = Human()
const ai = AI()

async function processWithOversight(data: unknown) {
  // AI generates initial result
  const result = await ai.analyze(data)

  // Human reviews before proceeding
  const review = await human.review({
    title: 'Review AI analysis',
    content: result,
    reviewType: 'data',
    criteria: ['Accuracy', 'Completeness', 'Bias'],
    assignee: '[email protected]',
  })

  if (review.approved) {
    return result
  } else {
    // Request human to fix
    return await human.do({
      title: 'Fix analysis',
      instructions: review.comments,
      input: { original: result, feedback: review.changes },
      assignee: '[email protected]',
    })
  }
}

With MCP Tools

import { createMCPServer } from '@mdxe/mcp'
import { Human } from 'human-in-the-loop'

const human = Human()

const mcpServer = createMCPServer({
  tools: {
    request_approval: {
      description: 'Request human approval',
      parameters: {
        type: 'object',
        properties: {
          title: { type: 'string' },
          description: { type: 'string' },
          assignee: { type: 'string' },
        },
        required: ['title', 'description'],
      },
      handler: async (params) => {
        const result = await human.approve({
          title: params.title,
          description: params.description,
          subject: params.title,
          input: params,
          assignee: params.assignee,
        })
        return result
      },
    },
  },
})

Architecture

The package follows a clean architecture with clear separation of concerns:

human-in-the-loop/
├── src/
│   ├── types.ts      # Type definitions
│   ├── store.ts      # Storage implementation
│   ├── human.ts      # Core HumanManager class
│   ├── helpers.ts    # Convenience functions
│   └── index.ts      # Public exports
├── examples/
│   └── basic-usage.ts  # Usage examples
└── README.md

Design Principles:

  • Store-agnostic: Works with any storage backend (in-memory, database, queue)
  • Channel-flexible: Supports multiple notification channels
  • Type-safe: Full TypeScript support with strict types
  • Extensible: Easy to add custom workflows and policies
  • Digital Workers Interface: Compatible with the digital-workers abstraction

Related Packages

  • ai-functions: Core AI primitives
  • digital-workers: Abstract interface over AI agents and humans
  • ai-workflows: Event-driven AI workflows
  • @mdxe/mcp: Model Context Protocol integration

License

MIT

Contributing

Contributions welcome! Please see the main repository for guidelines.