Blog

Luis Majano

September 04, 2026

Spread the word


Share your thoughts

Every AI agent that touches something real eventually needs a human in the loop. Someone has to approve the delete, confirm the wire transfer, or just say "go ahead" before a tool call runs. The question BoxLang AI 3.4.0 answers is: approve it how? A terminal prompt? A webhook? A Slack button?

The answer is the new Gateway SPI, IGateway, a single interface that normalizes every one of those into the same shape.

What a gateway actually is

A gateway is a bidirectional adapter. In one direction, it turns a platform event, a keystroke, an HTTP POST, a button click, into normalized agent input. In the other, it turns agent events, including a suspended approval request, back into something native to that platform.

Every gateway implements IGateway. Every capability method has a safe default, so a gateway only needs to override what it actually supports:

// Capabilities a gateway can declare
inboundMessages, outboundMessages, streaming, threads,
attachments, messageEditing, interactiveActions,
humanApproval, argumentEditing, authentication

A gateway that only handles approvals doesn't need to implement streaming. One that only streams doesn't need to know what argument editing is. Nobody pays for capabilities they don't use.

Resolving a gateway

The aiGateway() BIF resolves a gateway by name, core or externally registered:

cli  = aiGateway( "cli" )
http = aiGateway( "http", { secret: "shared-hmac-secret" } )

Three gateways ship in the box:

GatewayWhat it does
CliGatewayBlocking stdin/stdout approval prompt, now with approve_always/approve_session, not just approve/reject/quit
HttpGatewayHMAC-SHA256 signed requests, timestamp tolerance + nonce dedup, TTL-bounded pending interactions, atomic decision claims
MockGatewayIn-memory gateway for tests and examples

That last point on HttpGateway matters more than it sounds. A duplicate decision POST for an interaction that's already resolved gets rejected outright, not silently overwritten. Two people can't race to approve or reject the same request and have the second write clobber the first.

We also have tons of new gateways that are coming on our new agent framework to be released soon: BxAgents from WhatsApp, Signal, Slack, Discourse, Email, Telegram, etc.

Attaching a gateway to an agent

Gateways plug straight into HumanInTheLoopMiddleware:

aiAgent(
    middleware  : new HumanInTheLoopMiddleware( gateway: aiGateway( "http" ) ),
    checkpointer: aiMemory( "cache" )
)

Swap aiGateway( "http" ) for aiGateway( "cli" ) in a local dev script and nothing else in your agent code changes. The approval flow, the middleware, the checkpointer, none of it knows or cares which gateway is behind it.

Building your own

External platforms, Slack, Discord, Teams, whatever your team runs on, ship as their own modules and register a gateway instance under their own name:

aiGatewayRegistry().register( new MyPlatformGateway(), "my-platform" )
myGateway = aiGateway( "my-platform" )

aiGateway() can also auto-register the instance it constructs, so you don't have to call the registry yourself:

myGateway = aiGateway( name: "http", register: true, module: "my-module" )

To implement IGateway yourself, you get lifecycle hooks for free by extending BaseGateway, which tracks isRunning() and fires onGatewayConnect/onGatewayDisconnect automatically. Override onStart()/onStop(), never start()/stop() directly.

Why this matters

Before 3.4.0, "how does a human approve this" was a decision baked into your middleware code, tied to whatever transport you happened to build first. Now it's a resolvable object. Write your HITL logic once, then decide at deploy time whether approvals come in over a terminal, a signed webhook, or a chat platform your team hasn't built yet.

Next in the series: what happens on the other side of that gateway, the fully rebuilt Human-in-the-Loop subsystem, and the durable grants that mean nobody gets asked the same question twice.

Docs: Middleware, Human-in-the-Loop · Gateways

Add Your Comment

Recent Entries

BoxLang Under the Lens: Observability Meets AI - Free Webinar

BoxLang Under the Lens: Observability Meets AI - Free Webinar

Your application is slow. Something is failing. Where do you start looking?

On October 28, 2026, at 11:00 AM CDT, join Luis Majano, Ortus Solutions CEO and BoxLang creator, for a live, hands-on look at how observability and AI come together in BoxLang.

BoxLang Under the Lens: Observability Meets AI introduces BxLens, Lensy, and the additional monitoring capabilities of BoxLang+....

Victor Campos
Victor Campos
October 19, 2026
QR Codes Come to BoxLang Images

QR Codes Come to BoxLang Images

The bx-image module can now generate and read QR codes. Two new functions, a string member function, and two new bx:image actions. No extra dependencies for you to manage.

Luis Majano
Luis Majano
October 14, 2026