# humanInTheLoopMiddleware

> **Function** in `langchain`

📖 [View in docs](https://reference.langchain.com/javascript/langchain/browser/humanInTheLoopMiddleware)

Creates a Human-in-the-Loop (HITL) middleware for tool approval and oversight.

This middleware intercepts tool calls made by an AI agent and provides human oversight
capabilities before execution. It enables selective approval workflows where certain tools
require human intervention while others can execute automatically.

A invocation result that has been interrupted by the middleware will have a `__interrupt__`
property that contains the interrupt request.

```ts
import { type HITLRequest, type HITLResponse } from "langchain";
import { type Interrupt } from "langchain";

const result = await agent.invoke(request);
const interruptRequest = result.__interrupt__?.[0] as Interrupt<HITLRequest>;

// Examine the action requests and review configs
const actionRequests = interruptRequest.value.actionRequests;
const reviewConfigs = interruptRequest.value.reviewConfigs;

// Create decisions for each action
const resume: HITLResponse = {
  decisions: actionRequests.map((action, i) => {
    if (action.name === "calculator") {
      return { type: "approve" };
    } else if (action.name === "write_file") {
      return {
        type: "edit",
        editedAction: { name: "write_file", args: { filename: "safe.txt", content: "Safe content" } }
      };
    }
    return { type: "reject", message: "Action not allowed" };
  })
};

// Resume with decisions
await agent.invoke(new Command({ resume }), config);
```

## Features

- **Selective Tool Approval**: Configure which tools require human approval
- **Multiple Decision Types**: Approve, edit, or reject tool calls
- **Asynchronous Workflow**: Uses LangGraph's interrupt mechanism for non-blocking approval
- **Custom Approval Messages**: Provide context-specific descriptions for approval requests

## Decision Types

When a tool requires approval, the human operator can respond with:
- `approve`: Execute the tool with original arguments
- `edit`: Modify the tool name and/or arguments before execution
- `reject`: Provide a manual response instead of executing the tool

## Signature

```javascript
humanInTheLoopMiddleware(options: __type): AgentMiddleware<undefined, ZodObject<__type, "strip", ZodTypeAny, __type, __type>, __type, readonly ClientTool | ServerTool[], readonly []>
```

## Description

- Tool calls are processed in the order they appear in the AI message
- Auto-approved tools execute immediately without interruption
- Multiple tools requiring approval are bundled into a single interrupt request
- The middleware operates in the `afterModel` phase, intercepting before tool execution
- Requires a checkpointer to maintain state across interruptions

## Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `options` | `__type` | Yes | Configuration options for the middleware |

## Returns

`AgentMiddleware<undefined, ZodObject<__type, "strip", ZodTypeAny, __type, __type>, __type, readonly ClientTool | ServerTool[], readonly []>`

A middleware instance that can be passed to `createAgent`

## Examples

### Basic usage with selective tool approval

```typescript
import { humanInTheLoopMiddleware } from "langchain";
import { createAgent } from "langchain";

const hitlMiddleware = humanInTheLoopMiddleware({
  interruptOn: {
    // Interrupt write_file tool and allow edits or approvals
    "write_file": {
      allowedDecisions: ["approve", "edit"],
      description: "⚠️ File write operation requires approval"
    },
    // Auto-approve read_file tool
    "read_file": false
  }
});

const agent = createAgent({
  model: "openai:gpt-4",
  tools: [writeFileTool, readFileTool],
  middleware: [hitlMiddleware]
});
```

### Handling approval requests

```typescript
import { type HITLRequest, type HITLResponse, type Interrupt } from "langchain";
import { Command } from "@langchain/langgraph";

// Initial agent invocation
const result = await agent.invoke({
  messages: [new HumanMessage("Write 'Hello' to output.txt")]
}, config);

// Check if agent is paused for approval
if (result.__interrupt__) {
  const interruptRequest = result.__interrupt__?.[0] as Interrupt<HITLRequest>;

  // Show tool call details to user
  console.log("Actions:", interruptRequest.value.actionRequests);
  console.log("Review configs:", interruptRequest.value.reviewConfigs);

  // Resume with approval
  const resume: HITLResponse = {
    decisions: [{ type: "approve" }]
  };
  await agent.invoke(
    new Command({ resume }),
    config
  );
}
```

### Different decision types

```typescript
import { type HITLResponse } from "langchain";

// Approve the tool call as-is
const resume: HITLResponse = {
  decisions: [{ type: "approve" }]
};

// Edit the tool arguments
const resume: HITLResponse = {
  decisions: [{
    type: "edit",
    editedAction: { name: "write_file", args: { filename: "safe.txt", content: "Modified" } }
  }]
};

// Reject with feedback
const resume: HITLResponse = {
  decisions: [{
    type: "reject",
    message: "File operation not allowed in demo mode"
  }]
};
```

### Production use case with database operations

```typescript
const hitlMiddleware = humanInTheLoopMiddleware({
  interruptOn: {
    "execute_sql": {
      allowedDecisions: ["approve", "edit", "reject"],
      description: "🚨 SQL query requires DBA approval\nPlease review for safety and performance"
    },
    "read_schema": false,  // Reading metadata is safe
    "delete_records": {
      allowedDecisions: ["approve", "reject"],
      description: "⛔ DESTRUCTIVE OPERATION - Requires manager approval"
    }
  },
  descriptionPrefix: "Database operation pending approval"
});
```

### Using dynamic callable descriptions

```typescript
import { type DescriptionFactory, type ToolCall } from "langchain";
import type { AgentBuiltInState, Runtime } from "langchain/agents";

// Define a dynamic description factory
const formatToolDescription: DescriptionFactory = (
  toolCall: ToolCall,
  state: AgentBuiltInState,
  runtime: Runtime<unknown>
) => {
  return `Tool: ${toolCall.name}\nArguments:\n${JSON.stringify(toolCall.args, null, 2)}`;
};

const hitlMiddleware = humanInTheLoopMiddleware({
  interruptOn: {
    "write_file": {
      allowedDecisions: ["approve", "edit"],
      // Use dynamic description that can access tool call, state, and runtime
      description: formatToolDescription
    },
    // Or use an inline function
    "send_email": {
      allowedDecisions: ["approve", "reject"],
      description: (toolCall, state, runtime) => {
        const { to, subject } = toolCall.args;
        return `Email to ${to}\nSubject: ${subject}\n\nRequires approval before sending`;
      }
    }
  }
});
```

---

[View source on GitHub](https://github.com/langchain-ai/langchainjs/blob/6d212ef91affd19b71dac22fd3761287a96108bf/libs/langchain/src/agents/middleware/hitl.ts#L520)