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

digital-workers

v2.1.3

Published

Common abstract interface over AI Agents and Humans

Readme

digital-workers

You're building AI-powered workflows. But who should actually do the work?

Sometimes it's an AI agent operating autonomously. Sometimes it requires human judgment. Often it's both working together. And as AI capabilities evolve, you need workflows that can adapt without rewriting everything.

digital-workers gives you a single interface that works whether the worker is human or AI - so you can design once and swap implementations as your needs change.

The Problem

// Without digital-workers: You're locked into implementation details
if (task.requiresHumanJudgment) {
  await sendSlackToAlice(task.description)
  const approval = await waitForSlackResponse()
  // Different code path for humans...
} else {
  await callAIAgent(task.description)
  // Completely different code path for AI...
}

Your workflow logic becomes tangled with who does the work instead of what needs to happen.

The Solution

// With digital-workers: Define what, not who
await worker$.approve('Deploy v2.0 to production', alice, { via: 'slack' })

The same code works whether alice is:

  • A human product manager who gets a Slack notification
  • An AI agent that evaluates deployment criteria autonomously
  • A supervised AI that escalates to humans for high-risk decisions

Quick Start

1. Install

pnpm add digital-workers

2. Define Your Workers

import type { Worker } from 'digital-workers'

const alice: Worker = {
  id: 'user_alice',
  name: 'Alice',
  type: 'human',
  status: 'available',
  contacts: {
    email: '[email protected]',
    slack: { workspace: 'acme', user: 'U123' },
  },
}

const codeReviewer: Worker = {
  id: 'agent_reviewer',
  name: 'Code Reviewer',
  type: 'agent',
  status: 'available',
  contacts: {
    api: { endpoint: 'https://api.internal/reviewer' },
  },
}

3. Build Your Workflow

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

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

  $.on.Expense.submitted(async (expense) => {
    // Notify the finance team
    await worker$.notify(finance, `New expense: ${expense.amount}`)

    // Request approval from manager
    const approval = await worker$.approve(
      `Expense: $${expense.amount} for ${expense.description}`,
      manager,
      { via: 'slack' }
    )

    if (approval.approved) {
      await worker$.notify(expense.submitter, 'Your expense was approved!')
    }
  })
})

What You Can Do

Notify - Send Messages

await worker$.notify(target, 'Deployment complete', {
  via: 'slack',
  priority: 'urgent',
})

Ask - Request Information

const result = await worker$.ask<{ priority: string }>(
  target,
  'What priority should this be?',
  { schema: { priority: 'low | normal | high' } }
)
console.log(result.answer.priority) // 'high'

Approve - Get Sign-off

const result = await worker$.approve(
  'Deploy v2.0 to production',
  manager,
  { via: 'slack', timeout: 3600000 }
)

if (result.approved) {
  console.log(`Approved by ${result.approvedBy?.name}`)
}

Decide - Make Choices

const result = await worker$.decide({
  options: ['React', 'Vue', 'Svelte'],
  context: 'Choosing a frontend framework',
  criteria: ['DX', 'Performance', 'Ecosystem'],
})

console.log(result.choice)     // 'React'
console.log(result.reasoning)  // 'React offers the best ecosystem...'
console.log(result.confidence) // 0.85

Teams

Group workers with shared contacts:

import { Team } from 'digital-workers'

const engineering = Team({
  id: 'team_eng',
  name: 'Engineering',
  members: [alice, bob, codeReviewer],
  contacts: {
    slack: '#engineering',
    email: '[email protected]',
  },
  lead: alice,
})

// Notify the whole team
await worker$.notify(engineering, 'Sprint planning in 10 minutes')

Standalone Usage

Use outside of workflows:

import { notify, ask, approve, decide } from 'digital-workers'

// Simple notification
await notify(alice, 'Task completed', { via: 'slack' })

// Ask variants
await ask(alice, 'What is the status?')
await ask.ai('What is our refund policy?', { policies: [...] })
await ask.yesNo(manager, 'Should we proceed?')

// Approval variants
await approve('Deploy to production', manager)
await approve.all('Major change', [cto, vpe, securityLead])  // All must approve
await approve.any('Urgent fix', oncallTeam)                   // Any one can approve

// Decision variants
await decide({ options: ['A', 'B', 'C'], criteria: ['cost', 'time'] })
await decide.yesNo('Should we proceed?', context)
await decide.prioritize(['Task 1', 'Task 2', 'Task 3'])

How It Fits Together

┌─────────────────────────────────────────────────────────┐
│                    ai-workflows                         │
│              (orchestrates work execution)              │
└────────────────────────┬────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────┐
│                  digital-workers                        │
│         (abstract interface for work & workers)         │
└────────────────────────┬────────────────────────────────┘
                         │
           ┌─────────────┴─────────────┐
           ▼                           ▼
┌─────────────────────┐   ┌─────────────────────────────┐
│  autonomous-agents  │   │     human-in-the-loop       │
│  (AI implementation)│   │   (human implementation)    │
└─────────────────────┘   └─────────────────────────────┘
  • digital-workers: The abstract Worker interface, action types, and communication patterns
  • autonomous-agents: Implements Worker for AI agents with autonomous decision-making
  • human-in-the-loop: Implements Worker for humans with approval workflows and notifications
  • ai-workflows: Orchestrates work execution with durable, event-driven workflows

Advanced Features

Capability Tiers

Route work based on agent capabilities:

import { matchTierToComplexity, canExecuteAtTier } from 'digital-workers'

const tier = matchTierToComplexity({ reasoning: 'high', toolUse: true })
// Returns: { tier: 'agentic', confidence: 0.9 }

Load Balancing

Distribute work across available workers:

import { createLeastBusyBalancer, createCapabilityRouter } from 'digital-workers'

const balancer = createLeastBusyBalancer(workers)
const router = createCapabilityRouter(agents)

Error Escalation

Automatic escalation when things go wrong:

import { createEscalationEngine, createEscalationPolicy } from 'digital-workers'

const policy = createEscalationPolicy({
  maxRetries: 3,
  escalationPath: ['junior-agent', 'senior-agent', 'human'],
})

const engine = createEscalationEngine(policy)

Agent Communication

Direct agent-to-agent messaging:

import { sendToAgent, requestFromAgent, initiateHandoff } from 'digital-workers'

await sendToAgent(targetAgent, { type: 'task', payload: data })
const response = await requestFromAgent(agent, query, { timeout: 5000 })
await initiateHandoff(fromAgent, toAgent, context)

Type Reference

Key exports:

  • Worker, Team, WorkerRef - Core interfaces
  • Contacts, ContactChannel - Communication configuration
  • NotifyResult, AskResult, ApprovalResult, DecideResult, DoResult - Action results
  • CapabilityTier, CapabilityProfile - Agent capability levels
  • LoadBalancer, EscalationEngine - Advanced orchestration

Related Packages

  • autonomous-agents - Implements Worker for AI agents with goals, metrics, and autonomous decision-making
  • human-in-the-loop - Implements Worker for humans with approval workflows, notifications, and escalation
  • ai-workflows - Uses digital-workers to orchestrate work execution with durable, event-driven workflows
  • services-as-software - External service integration (crosses company boundaries, unlike workers)