Enterprise Human-in-the-Loop System
Since: v7.39.0 | Availability: SDK
Treat every code sample below as the target API unless it appears in
"Currently available" immediately after this box. A heading marked
(Planned) has examples that use configuration keys or behaviour that do not
exist, and (Planned) is not a promise of a date. The sections under "Quick
Start (5 Minutes)" are covered by that heading's own label.
None of requireApproval, reviewCallback, statusCallback, mode,
confidenceThreshold, contentPatterns, escalationPolicy or the object form
of auditLog exists in src/lib/types/hitl.ts or is handled by
src/lib/hitl/hitlManager.ts. validateConfig does not reject unknown keys
either, so a config copied from this page is accepted silently and every one
of those keys is dropped — approval gates that never fire, with no error.
That includes the compliance sections. The HIPAA / SOC2 / GDPR examples show an
auditLog storage backend and an escalationPolicy that do not exist; do not
cite them as evidence of a control.
Tracking: #752.
Currently available
HITLConfig has exactly these ten keys (see src/lib/types/hitl.ts).
Anything else on this page is not implemented.
import {
NeuroLink,
type ConfirmationRequestEvent,
type ConfirmationResponseEvent,
} from "@juspay/neurolink";
const neurolink = new NeuroLink({
hitl: {
enabled: true,
dangerousActions: ["delete", "remove", "drop"], // Required. Case-insensitive substring match on the tool name
timeout: 30000, // Confirmation timeout in ms (default 30000)
confirmationMethod: "event", // The only supported value
autoApproveOnTimeout: false, // Default false: reject when nobody answers
autoApproveExclusions: [], // Exact tool names never auto-approved on timeout
allowArgumentModification: false, // Default false. Only advertised to your UI as allowModification; not enforced
auditLogging: true, // Log each request and decision
customRules: [], // { name, condition(toolName, args), requiresConfirmation, customMessage? }
confirmBuiltinTools: true, // Default true. See the note below the example
},
});
dangerousActions is required whenever enabled is true; omitting it throws
HITLConfigurationError from the NeuroLink constructor. auditLogging writes
each request and decision to the NeuroLink logger at info level. Entries include
the full tool arguments, so redact secrets and personal data at the logger sink.
allowArgumentModification is copied into the request event as
allowModification for your approval UI; NeuroLink does not enforce it, and a
hitl:confirmation-response that carries modifiedArguments is applied
either way, so forward only the arguments you accept. The
hitl:audit event is emitted on the internal HITLManager, and NeuroLink
forwards only hitl:confirmation-request and hitl:timeout to
getEventEmitter().
confirmBuiltinTools (default true) applies only while enabled is true.
With it on, the built-in writeFile and executeBashCommand tools need
confirmation on every call the model makes during generate() or stream(),
whether or not dangerousActions matches them. Setting it to false removes
only that built-in rule: those tools are then gated like any other tool, so a
dangerousActions keyword or a customRules match still requires approval.
A direct neurolink.executeTool("writeFile", ...) call is checked against
dangerousActions and customRules only, not against this rule, so list the
tool names in dangerousActions if those calls must be confirmed too. With
HITL disabled, no tool asks for confirmation.
Approval is event-based, through the SDK emitter — there is no
reviewCallback. The emitter is reached with getEventEmitter(); NeuroLink
does not extend EventEmitter, so neurolink.on(...) and neurolink.emit(...)
do not exist:
const emitter = neurolink.getEventEmitter();
emitter.on("hitl:confirmation-request", (event) => {
const receivedAt = Date.now();
// Listener arguments are typed unknown, so narrow the event
const { payload } = event as ConfirmationRequestEvent;
const { confirmationId, toolName, arguments: args } = payload;
// Your approval UI / Slack / email integration decides here.
emitter.emit("hitl:confirmation-response", {
type: "hitl:confirmation-response",
payload: {
confirmationId,
approved: true,
metadata: {
timestamp: new Date().toISOString(),
responseTime: Date.now() - receivedAt, // ms since the request arrived
},
},
} satisfies ConfirmationResponseEvent);
});
This is the shape exercised by test/continuous-test-suite-hitl.ts. For
production use today, refer to the Basic HITL Guide.
Executive Summary
This section describes the system the rest of this page is a design for. Today NeuroLink ships event-based tool approval and a log of each request and decision (see "Currently available"). It does not ship escalation, asynchronous review or an audit store, and it holds no HIPAA, SOC2 or GDPR certification or control.
Strategic Value Proposition
- Risk Mitigation: Prevent costly AI mistakes through mandatory human checkpoints
- Regulatory Compliance (target): Produce the review records regulated industries ask for. Today
auditLoggingonly writes a log line; compliance is the deployer's responsibility - Trust & Transparency: Build stakeholder confidence with auditable AI decisions
- Continuous Improvement: Capture human expertise to improve AI accuracy over time
When to Use HITL
Required for:
- Medical diagnosis and treatment recommendations
- Financial transactions above risk thresholds
- Legal document generation and review
- Code execution in production environments
- Personal data modification or deletion
- Irreversible operations (send email, post to social media)
Not recommended for:
- Read-only operations (information retrieval)
- Low-stakes content generation
- Development/testing environments
- High-volume, low-risk automation
Quick Start (5 Minutes) — target API, not implemented
The configuration in this section is not implemented. Use "Currently available" above for a setup that works.
Installation
HITL is built into NeuroLink SDK v7.39.0+. No additional packages required:
npm install @juspay/neurolink@latest
# or
pnpm add @juspay/neurolink@latest
Basic Configuration (Planned)
Minimal setup for tool-based approval workflow:
import { NeuroLink } from "@juspay/neurolink";
const neurolink = new NeuroLink({
hitl: {
enabled: true,
requireApproval: ["writeFile", "deleteFile", "executeCode"],
reviewCallback: async (action, context) => {
// Your approval logic - integrate with Slack, email, custom UI
console.log(`🔔 Approval needed: ${action.tool}`);
console.log(`📝 Arguments:`, action.args);
// Example: Simple prompt-based approval (replace with your system)
const approved = await promptUser(
`Allow AI to ${action.tool} with args ${JSON.stringify(action.args)}?`,
);
return {
approved,
reason: approved ? "User authorized" : "User denied",
reviewer: "admin@company.com",
};
},
},
});
First Approval Request
Complete end-to-end example with error handling:
try {
const result = await neurolink.generate({
input: {
text: "Delete the temporary files in the /tmp directory",
},
provider: "anthropic",
tools: [
{
name: "deleteFile",
description: "Delete a file from filesystem",
requiresConfirmation: true, // Triggers HITL
execute: async (args) => {
const fs = await import("fs/promises");
await fs.unlink(args.path);
return { success: true, deletedPath: args.path };
},
},
],
});
console.log(result.content);
} catch (error) {
if (error.code === "USER_CONFIRMATION_REQUIRED") {
// Handle approval workflow
const approvalResult = await handleApproval(error.details);
if (approvalResult.approved) {
// Retry with confirmation
const retryResult = await retryWithConfirmation(error.details);
console.log(retryResult);
}
}
}
Core Concepts
1. Approval Workflows
The target design supports both synchronous (blocking) and asynchronous (non-blocking) approval patterns. Today only the blocking, event-based flow under "Currently available" exists:
Synchronous Approval (Blocking) (Planned)
AI operation pauses until human approves or rejects:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
mode: "synchronous", // Default
timeout: 300000, // 5 minutes max wait
reviewCallback: async (action, context) => {
// Blocks here until approval received
return await showApprovalDialog(action);
},
},
});
Use cases:
- Real-time operations requiring immediate decision
- Interactive applications with user present
- High-risk actions requiring instant validation
Asynchronous Approval (Non-blocking) (Planned)
AI operation returns pending status, continues when approved:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
mode: "asynchronous",
reviewCallback: async (action, context) => {
// Queue for review, return immediately
const reviewId = await queueForReview(action);
return {
pending: true,
reviewId,
estimatedTime: 900000, // 15 minutes
};
},
statusCallback: async (reviewId) => {
// Check approval status
return await checkReviewStatus(reviewId);
},
},
});
Use cases:
- Batch processing workflows
- Operations requiring expert review (takes time)
- Multi-level approval chains
- Integration with ticketing systems (Jira, ServiceNow)
2. Review Triggers
Configure when human review is required:
Confidence Threshold Trigger (Planned)
Automatically request review when AI confidence is low:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
confidenceThreshold: 0.85, // Review if confidence < 85%
reviewCallback: async (action, context) => {
if (context.aiConfidence < 0.85) {
return await requestExpertReview(action, context);
}
return { approved: true, reason: "High confidence" };
},
},
});
Tool-Specific Rules (Planned)
Today, list the names in dangerousActions. It matches case-insensitive
substrings of the tool name, not exact names; use a customRules condition when
you need an exact match. requireApproval below does not exist.
Require approval for specific tools only:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
requireApproval: [
"deleteFile", // Always require approval
"sendEmail", // Prevent accidental sends
"executeCode", // Sandbox escape prevention
"updateDatabase", // Data integrity protection
],
},
});
Content Pattern Matching (Planned)
Trigger review based on content patterns:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
contentPatterns: [
/\b\d{3}-\d{2}-\d{4}\b/, // SSN pattern
/\b\d{16}\b/, // Credit card pattern
/password|secret|token/i, // Sensitive keywords
],
reviewCallback: async (action, context) => {
const containsSensitiveData = context.contentPatterns.some((pattern) =>
pattern.test(action.content),
);
if (containsSensitiveData) {
return await requestSecurityReview(action);
}
return { approved: true };
},
},
});
Time-Based Restrictions (Planned)
Today, a customRules entry whose condition checks the time can require
confirmation; the reviewCallback below does not exist.
Require approval outside business hours:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
reviewCallback: async (action, context) => {
const hour = new Date().getHours();
const isBusinessHours = hour >= 9 && hour < 17;
if (!isBusinessHours && action.tool === "executeCode") {
return {
approved: false,
reason: "Code execution restricted to business hours",
escalate: true,
};
}
return { approved: true };
},
},
});
3. Escalation Policies (Planned)
Handle timeout and multi-level approval:
const neurolink = new NeuroLink({
hitl: {
enabled: true,
timeout: 300000, // 5 minutes
escalationPolicy: {
onTimeout: "escalate", // Options: "approve", "reject", "escalate"
escalationLevels: [
{
level: 1,
reviewers: ["team-lead@company.com"],
timeout: 300000, // 5 minutes
},
{
level: 2,
reviewers: ["manager@company.com"],
timeout: 600000, // 10 minutes
},
{
level: 3,
reviewers: ["cto@company.com"],
timeout: 1800000, // 30 minutes
finalAuthority: true,
},
],
},
reviewCallback: async (action, context) => {
const level = context.escalationLevel || 1;
const reviewers =
context.escalationPolicy.escalationLevels[level - 1].reviewers;
return await requestApprovalFrom(reviewers, action);
},
},
});
SDK Integration
TypeScript Configuration (Planned)
Complete configuration interface:
type HITLConfiguration = {
// Core settings
enabled: boolean;
mode?: "synchronous" | "asynchronous"; // (Planned feature)
timeout?: number; // milliseconds
// Approval triggers
requireApproval?: string[]; // Tool names
confidenceThreshold?: number; // 0-1 (Planned feature)
contentPatterns?: RegExp[]; // (Planned feature)
// Callbacks
reviewCallback: (
action: HITLAction,
context: HITLContext,
) => Promise<HITLReviewResult>;
statusCallback?: (reviewId: string) => Promise<HITLReviewStatus>; // (Planned feature)
// Escalation (Planned feature)
escalationPolicy?: {
onTimeout: "approve" | "reject" | "escalate";
escalationLevels?: EscalationLevel[];
};
// Audit
auditLog?: {
enabled: boolean;
storage: "file" | "database" | "custom";
customLogger?: (entry: AuditEntry) => Promise<void>;
};
};
type HITLAction = {
tool: string;
args: Record<string, any>;
timestamp: Date;
sessionId: string;
};
type HITLContext = {
aiConfidence?: number;
provider: string;
model: string;
escalationLevel?: number;
};
type HITLReviewResult = {
approved: boolean;
reason?: string;
reviewer?: string;
modifications?: Record<string, any>;
escalate?: boolean;
};
Approval Callback Patterns
Slack Integration (Planned)
import { WebClient } from "@slack/web-api";
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
const neurolink = new NeuroLink({
hitl: {
enabled: true,
requireApproval: ["deleteFile", "sendEmail"],
reviewCallback: async (action, context) => {
// Send approval request to Slack
const message = await slack.chat.postMessage({
channel: "#ai-approvals",
text: `🤖 AI Approval Request`,
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `*Action:* \`${action.tool}\`\n*Args:* \`\`\`${JSON.stringify(action.args, null, 2)}\`\`\``,
},
},
{
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "Approve" },
style: "primary",
value: action.sessionId,
action_id: "approve",
},
{
type: "button",
text: { type: "plain_text", text: "Reject" },
style: "danger",
value: action.sessionId,
action_id: "reject",
},
],
},
],
});
// Wait for response (implement with Slack interactivity)
return await waitForSlackResponse(message.ts);
},
},
});
Email Integration (Planned)
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
const neurolink = new NeuroLink({
hitl: {
enabled: true,
mode: "asynchronous",
reviewCallback: async (action, context) => {
const reviewId = generateReviewId();
await transporter.sendMail({
from: "ai-system@company.com",
to: "approvers@company.com",
subject: `AI Approval Request: ${action.tool}`,
html: `
<h2>AI Action Requires Approval</h2>
<p><strong>Tool:</strong> ${action.tool}</p>
<p><strong>Arguments:</strong></p>
<pre>${JSON.stringify(action.args, null, 2)}</pre>
<p>
<a href="https://approvals.company.com/approve/${reviewId}">Approve</a> |
<a href="https://approvals.company.com/reject/${reviewId}">Reject</a>
</p>
`,
});
return {
pending: true,
reviewId,
estimatedTime: 1800000, // 30 minutes
};
},
statusCallback: async (reviewId) => {
return await checkApprovalStatus(reviewId);
},
},
});
Integration with External Systems
ServiceNow Integration (Planned)
import axios from "axios";
const serviceNowClient = axios.create({
baseURL: process.env.SERVICENOW_INSTANCE,
auth: {
username: process.env.SERVICENOW_USER,
password: process.env.SERVICENOW_PASS,
},
});
const neurolink = new NeuroLink({
hitl: {
enabled: true,
mode: "asynchronous",
reviewCallback: async (action, context) => {
// Create ServiceNow ticket
const ticket = await serviceNowClient.post("/api/now/table/incident", {
short_description: `AI Approval: ${action.tool}`,
description: JSON.stringify(action.args, null, 2),
urgency: 2,
category: "AI Operations",
assignment_group: "AI Review Team",
});
return {
pending: true,
reviewId: ticket.data.result.sys_id,
trackingUrl: `${process.env.SERVICENOW_INSTANCE}/nav_to.do?uri=incident.do?sys_id=${ticket.data.result.sys_id}`,
};
},
statusCallback: async (reviewId) => {
const ticket = await serviceNowClient.get(
`/api/now/table/incident/${reviewId}`,
);
return {
approved: ticket.data.result.state === "6", // Resolved
pending: ticket.data.result.state !== "6",
reason: ticket.data.result.close_notes,
};
},
},
});
CLI Integration (Planned)
The CLI has no HITL support today: loop --enable-hitl and the /hitl commands
below do not exist in src/cli. HITL is available through the SDK only.
HITL in Loop Mode (Planned)
Interactive CLI provides built-in HITL commands:
# Start loop with HITL enabled
npx @juspay/neurolink loop --enable-hitl
# Inside loop session
neurolink > /hitl status
📋 Pending HITL Approvals (2):
1. Tool: deleteFile
Args: { path: "/tmp/data.csv" }
Confidence: 0.76
Requested: 2 minutes ago
2. Tool: sendEmail
Args: { to: "customer@example.com", subject: "Order Update" }
Confidence: 0.92
Requested: 5 seconds ago
neurolink > /hitl approve 1
✅ Approved deleteFile operation
Execution completed successfully
neurolink > /hitl reject 2 --reason "Email template needs review"
❌ Rejected sendEmail operation
Reason logged: Email template needs review
CLI HITL Commands (Planned)
| Command | Description | Example |
|---|---|---|
/hitl status | View pending approvals | /hitl status |
/hitl approve <id> | Approve pending action | /hitl approve 1 |
/hitl reject <id> | Reject with optional reason | /hitl reject 2 --reason "Security concern" |
/hitl history | View approval history | /hitl history --last 10 |
/hitl config | View HITL configuration | /hitl config |
Enterprise Patterns
Pattern 1: Medical AI Validation (Planned)
Physician oversight for AI-generated diagnostic recommendations:
import { NeuroLink } from "@juspay/neurolink";
const medicalAI = new NeuroLink({
hitl: {
enabled: true,
mode: "synchronous",
confidenceThreshold: 0.95, // High bar for medical decisions
requireApproval: ["generateDiagnosis", "recommendTreatment"],
reviewCallback: async (action, context) => {
// Route to qualified physician based on specialty
const specialty = determineSpecialty(action.args);
const physician = await findAvailablePhysician(specialty);
// Present AI analysis to physician
const review = await presentToPhysician({
physician,
aiAnalysis: {
tool: action.tool,
recommendation: action.args,
confidence: context.aiConfidence,
supportingData: context.metadata,
},
patientContext: context.patientId,
});
// Log for HIPAA compliance
await auditLog.recordMedicalReview({
physician: physician.id,
decision: review.approved,
timestamp: new Date(),
patientId: context.patientId,
aiConfidence: context.aiConfidence,
humanConfidence: review.confidence,
});
return {
approved: review.approved,
reason: review.clinicalReasoning,
reviewer: physician.email,
modifications: review.modifications,
};
},
},
});
// Usage
const diagnosis = await medicalAI.generate({
input: {
text: "Analyze patient symptoms and recommend diagnosis",
},
context: {
patientId: "PT-12345",
symptoms: ["chest pain", "shortness of breath"],
vitals: { bp: "145/95", hr: 98 },
},
tools: [
{
name: "generateDiagnosis",
description: "Generate diagnostic recommendation",
requiresConfirmation: true,
execute: async (args) => {
return {
diagnosis: args.primaryDiagnosis,
differentials: args.differentialDiagnoses,
recommendedTests: args.tests,
};
},
},
],
});