awaithumans — HITL infrastructure for AI agents
HITL infrastructure for AI agents — open source. A single primitive (awaitHuman()) your agent calls when the model can't or shouldn't proceed alone. A human gets notified (Slack, email, or dashboard), reviews the request, submits a typed response, and your agent resumes — like awaiting any other promise.
import { awaitHuman } from "awaithumans";
import { z } from "zod";
const RefundRequest = z.object({
orderId: z.string(),
amountUsd: z.number(),
});
const Decision = z.object({
approved: z.boolean(),
note: z.string().optional(),
});
const decision = await awaitHuman({
task: "Approve refund request",
payloadSchema: RefundRequest,
payload: { orderId: "A-4721", amountUsd: 180 },
responseSchema: Decision,
timeoutMs: 900_000,
});
if (decision.approved) {
await processRefund(...);
}


Install
npm install awaithumans
pnpm add awaithumans
bun add awaithumans
Works in Node 20+, Bun, Deno, and edge runtimes (Cloudflare Workers,
Vercel Edge). No node:* imports.
Run the server
The awaithumans server (which handles task storage, Slack/email
channels, and hosts the review dashboard) is written in Python. As a
TypeScript developer you don't have to touch a Python environment —
the npm CLI wraps it:
npx awaithumans dev
Under the hood this uses uv to fetch + run the
Python server on demand. Install uv once:
curl -LsSf https://astral.sh/uv/install.sh | sh
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
First run prints a setup URL. Open it, create the operator account,
you're in. The dashboard is at http://localhost:3001.
Prefer Docker? docker run -p 3001:3001 ghcr.io/awaithumans/awaithumans:latest.
Tasks can be delivered to Slack channels with a "Claim this task" button — first clicker atomically wins, response form opens as a modal, agent unblocks when they submit. Add notify: ["slack:#ops"] to the awaitHuman() call:

Durable workflows
When your agent runs inside Temporal or LangGraph, you don't want the
wait sitting on an orchestrator thread for 15 minutes:
import { awaitHuman } from "awaithumans/temporal";
import { awaitHuman } from "awaithumans/langgraph";
Same awaitHuman shape, same typed response. The adapter just
changes how the wait is orchestrated.
Testing
An in-memory mock client so your agent tests don't need a running
server:
import { createTestClient } from "awaithumans/testing";
const client = createTestClient();
client.onAwait((task) => ({ approved: true, note: "looks good" }));
Documentation
License
Apache License 2.0.
The TypeScript SDK, every adapter subpath export, and the Python
server + dashboard it talks to are all under the same license —
permissive, OSI-approved, with an explicit patent grant.