
Implementing Human-in-the-Loop AI Agents using Eve.dev and TypeScript
Ensure safety and accuracy in your AI workflows. Learn how to build human-in-the-loop authorization gates using Eve.dev's durable execution framework.
Summary
TL;DR: Never let an AI execute dangerous actions unchecked. This tutorial covers how to use Eve.dev to build "Human-in-the-Loop" (HITL) workflows, allowing agents to pause and request human approval before proceeding.
As AI agents move from answering questions to taking actions (like modifying databases, sending emails to clients, or deploying code), safety becomes the primary concern. Fully autonomous execution is a liability.
You need a Human-in-the-Loop (HITL) architecture.
In this guide, we will use Eve.dev to build a durable workflow where an agent prepares a financial transaction, suspends its own execution, asks a human administrator for approval, and then securely resumes.
Why Eve.dev for HITL?
Implementing HITL from scratch is incredibly difficult. If an LLM decides to execute an action, how do you pause the script, store the entire execution state (including chat history and memory), wait hours for a human to click a button on a web dashboard, and then resume the exact same script?
Eve.dev solves this inherently because every step of an Eve workflow is durable and checkpointed to a database.
Step 1: Designing the Agent
Let's build a "Financial Analyst Agent" that can review invoices and issue payments.
In our Eve workspace (/eve/agents/finance), we create our core instructions.md:
# Financial Analyst Agent
Your job is to review incoming invoices and process payments.
## Rules
1. You may extract data from invoices autonomously.
2. **CRITICAL:** You MUST NEVER execute a payment without getting human approval first using the `request_approval` tool.
Step 2: Building the Suspension Tool
The magic happens in our tools. We will create a tool that intentionally suspends the agent.
Create /eve/agents/finance/tools/request_approval.ts:
import { tool, suspend } from '@eve/core';
import { z } from 'zod';
export default tool({
name: 'request_approval',
description: 'Pauses execution to ask a human manager for approval before taking a critical action.',
schema: z.object({
amount: z.number(),
payee: z.string(),
reason: z.string()
}),
execute: async (payload) => {
// The suspend() function throws a special interrupt that Eve.dev catches.
// It serializes the agent's state to the database and halts execution.
return suspend({
type: 'human_approval_required',
data: payload,
message: `Waiting for human approval to pay $${payload.amount} to ${payload.payee}.`
});
}
});
Step 3: Building the Execution Tool
We also need the actual tool that performs the payment, which should only run after approval.
Create /eve/agents/finance/tools/execute_payment.ts:
import { tool } from '@eve/core';
import { z } from 'zod';
export default tool({
name: 'execute_payment',
description: 'Executes the payment transaction.',
schema: z.object({
amount: z.number(),
payee: z.string(),
}),
execute: async ({ amount, payee }) => {
// In production, this would call Stripe or a banking API
console.log(`[BANK] Processing payment of $${amount} to ${payee}`);
return { success: true, transactionId: "tx_12345" };
}
});
Step 4: The Application Backend (Next.js or Express)
Now, how does our UI interact with this agent? We need two endpoints: one to start the process, and one for the human to submit their approval.
import { Eve } from '@eve/core';
const eve = new Eve({ workspace: './eve', storage: 'postgres://...' });
// 1. Endpoint to process an invoice
async function processInvoice(invoiceId: string, invoiceText: string) {
const agent = await eve.getAgent('finance');
// We use the invoiceId as our durable workflow ID
const result = await agent.run({
id: invoiceId,
input: `Please review and pay this invoice: ${invoiceText}`
});
if (result.status === 'suspended') {
// The agent hit our request_approval tool.
// We notify the frontend to show an "Approve/Reject" UI.
console.log("ALERT: Human approval required:", result.suspensionMessage);
// e.g., trigger email or Slack notification to manager
}
}
// 2. Endpoint for the manager to click "Approve"
async function handleHumanDecision(invoiceId: string, isApproved: boolean) {
const agent = await eve.getAgent('finance');
// We resume the exact same workflow by passing the ID
// and we inject the human's decision back into the agent's context!
const result = await agent.resume({
id: invoiceId,
injectedMessage: isApproved
? "The human manager has APPROVED this transaction. Proceed with execute_payment."
: "The human manager has REJECTED this transaction. Do not pay. Explain why."
});
console.log("Agent finished:", result.finalOutput);
}
How the Execution Flows
- The user asks the agent to pay an invoice.
- The agent reads it, extracts the amount, and realizes it must follow Rule #2.
- The agent calls
request_approval(amount: 5000, payee: "Acme Corp"). - Eve.dev catches this, saves the entire memory array to PostgreSQL, and stops the process. The server can safely shut down.
- Two days later, a manager clicks "Approve" in a web dashboard.
- The
handleHumanDecisionfunction wakes up the agent, passing in the approval message. - The LLM reads the new message, realizes it has permission, and calls
execute_payment().
Conclusion
By leveraging Eve.dev's durable execution model, building Human-in-the-Loop workflows becomes trivial. You no longer have to build custom state machines or hack together Redis caches to pause AI execution. You can securely gate any dangerous action, ensuring your AI applications are safe for enterprise deployment.
Frequently Asked Questions (FAQ)
What is a Human-in-the-Loop (HITL) system? HITL is an architecture where an automated process (like an AI agent) must pause and request authorization or input from a human operator before proceeding with a high-risk action, such as executing a financial transaction or deleting data.
How does Eve.dev handle pausing an agent?
Eve.dev uses a suspend() function within tool definitions. When called, the framework halts the agent's execution loop, serializes the current state (memory, chat history, active variables), and saves it to a durable database until a resume() signal is received.
Can Eve.dev workflows survive a server crash? Yes. Because Eve.dev is built on durable execution principles, every state transition is checkpointed. If the server crashes while the agent is waiting for human approval, the state is safely preserved in the database and can be resumed on any other server instance.
Related reading
Our monthly client reports followed a 40-line prompt nobody dared touch. I moved the whole playbook into an uploaded skill using the new uploadSkill API in Vercel AI SDK v7.
Build an AIOps on-call agent with Eve.dev that triages alerts, runs diagnostics, and gates risky remediation behind human approval—surviving restarts.
Use Eve.dev's multi-agent orchestration to build an accounts-payable pipeline: a supervisor delegates invoice extraction, 3-way matching, and durable approvals.
Let's build something great.
Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.