HumanLayer / CodeLayer + Ravi

HumanLayer / CodeLayer + Ravi

HumanLayer has a two-chapter story. The original SDK introduced a principled framework for human-in-the-loop oversight of AI agents — a @require_approval decorator that intercepts high-stakes function calls and blocks execution until a human signs off. The same team has since pivoted to CodeLayer, an open-source IDE for orchestrating parallel Claude Code sessions across multiple git worktrees with a keyboard-first UI.

Both chapters share the same underlying problem: when an agent needs to reach a human — for approval, for sign-off, for feedback — it needs a trusted, verifiable channel to do it. Anonymous Slack messages and generic webhook callbacks aren’t that channel. Ravi is.

The Problem

HumanLayer’s original classification still holds: read-public is low stakes, read-private is medium stakes, and “communicate on my behalf” or “write private data” is high stakes. For high-stakes actions, you want human approval. But the approval channel itself has a trust problem.

The approval request has to come from somewhere. If your agent sends a Slack DM, the human sees a bot username with no clear provenance — was this from the production agent or the test agent? Which session? If there are five parallel Claude Code workers running, which one is asking?

The response has to route back correctly. A human approves via Slack or email and types “yes.” That response has to find its way back to the right agent instance, not be broadcast to all of them, not get lost in a queue, not require a webhook to be running to receive it.

The interaction has to be auditable. High-stakes approvals are compliance events. The record of what was asked, who approved it, and when needs to live somewhere durable. An ephemeral Slack thread doesn’t qualify.

For CodeLayer specifically, parallel agent sessions sharpen every one of these problems. Five workers running simultaneously, each potentially making approval requests, each needing isolated response channels — shared bot credentials are the wrong foundation.

What Ravi Adds

1. Per-Agent Identity for Approval Flows

Ravi provisions each agent with its own real email address and phone number. When a HumanLayer @require_approval intercepts a high-stakes function call, the approval request goes out from the agent’s Ravi address — not a shared bot inbox, not an anonymous system user.

The human receiving the request sees agent-worker-3@yourproject.ravi.app. They know exactly which agent is asking. They reply with “approve” or “deny” from any email client. The reply routes to an inbox the agent owns and monitors exclusively.

Compare that to a Slack DM from a bot named @ai-assistant. Trust, accountability, and routing are all weaker.

2. Email-Based Approval Loop Without Webhook Infrastructure

The typical pattern for HumanLayer approval channels requires running a receiver — a Slack app, a webhook endpoint, or a custom integration. Ravi’s email inbox is asynchronous by design and requires nothing to be running on a server to receive a response.

The agent sends an approval request via ravi_email_compose. It polls ravi_inbox_email for a reply. When the human responds, the agent reads the reply with ravi_read_email, parses the approval decision, and unblocks.

This works across timezones. It works when the human isn’t at their desk. It works when the human is on mobile. The interaction happens in a medium humans already trust — their email inbox.

3. Isolated Inboxes for Parallel CodeLayer Sessions

CodeLayer’s MULTICLAUDE feature runs multiple Claude Code instances simultaneously in parallel worktrees. Each worker potentially needs its own approval channel. With Ravi, each session gets its own provisioned identity — isolated email, isolated inbox, isolated response routing.

Worker 1 asks for approval to push to production. Worker 2 asks for approval to delete a test database. Both approvals go to the right human with clear attribution. Both responses route to the right worker. No cross-contamination.

4. Durable Audit Trail in Standard Email

Because approvals happen over email, the audit trail is inherent. Every approval request and human response lives in a standard email thread — exportable, searchable, and readable without any special tooling. When a compliance review asks “who approved the database deletion at 3pm Tuesday,” the answer is a forwarded email thread, not a database query into a SaaS approval platform.

Concrete Integration: Email-Based Approval Plugin

Here’s a working pattern for a Ravi-backed HumanLayer approval channel:

import ravi
from humanlayer import HumanLayer, ContactChannel

class RaviEmailChannel(ContactChannel):
    """HumanLayer contact channel backed by Ravi email."""

    def __init__(self, approver_email: str):
        self.approver_email = approver_email

    def request_approval(self, function_name: str, args: dict, risk: str) -> bool:
        # Send approval request from the agent's Ravi identity
        subject = f"Approval required: {function_name} [{risk} risk]"
        body = f"""
Your AI agent is requesting approval to execute a function.

Function: {function_name}
Arguments: {args}
Risk level: {risk}

Reply with APPROVE or DENY.
        """.strip()

        ravi.email_compose(
            to=self.approver_email,
            subject=subject,
            body=body
        )

        # Poll for response (with timeout)
        import time
        deadline = time.time() + 3600  # 1-hour timeout

        while time.time() < deadline:
            threads = ravi.inbox_email(unread=True)
            for thread in threads:
                if subject in thread["subject"]:
                    messages = ravi.read_email(thread["thread_id"])
                    for msg in messages:
                        if msg["from_email"] == self.approver_email:
                            reply = msg["text_content"].strip().upper()
                            return "APPROVE" in reply
            time.sleep(30)  # Poll every 30 seconds

        raise TimeoutError(f"Approval for {function_name} timed out")


# Usage with HumanLayer
hl = HumanLayer(contact_channel=RaviEmailChannel("human@yourcompany.com"))

@hl.require_approval()
def delete_production_data(table: str, condition: str):
    # This won't execute until the human approves via email
    db.execute(f"DELETE FROM {table} WHERE {condition}")

Setup Guide

Prerequisites

  • humanlayer or CodeLayer installed
  • Ravi CLI configured with an active identity (ravi identity list)

Step 1: Provision an identity for each agent session

# For a single agent
ravi identity create "approval-agent"

# For parallel CodeLayer workers
ravi identity create "codelayer-worker-1"
ravi identity create "codelayer-worker-2"
ravi identity create "codelayer-worker-3"

Each identity gets its own email address and phone number — fully isolated from the others.

Step 2: Store the approver’s contact info

# Store where approvals should go
ravi secrets set APPROVER_EMAIL human@yourcompany.com

Step 3: Configure the HumanLayer channel

In your agent setup, wire up the Ravi email channel as shown in the integration example above. For CodeLayer, run each worker with its own Ravi identity configured — this ensures parallel workers send from distinct addresses and poll distinct inboxes.

Step 4: Test the approval loop

Run a low-stakes decorated function, confirm the email arrives at the approver’s inbox from the agent’s Ravi address, reply APPROVE, and verify the agent unblocks correctly before deploying to production workloads.

Why Ravi Fits HumanLayer Specifically

HumanLayer’s core thesis is that human oversight of high-stakes agent actions requires a trusted channel. The original SDK was built on that insight — but the channel abstraction was left open. You could wire up Slack, email, SMS, or a custom webhook. The quality of the oversight depends entirely on the quality of the channel.

Ravi makes the channel trustworthy:

  • Known sender identity. The human knows which agent is asking, not just “an AI agent.”
  • No shared credentials. Each parallel worker has its own inbox. Approvals can’t route to the wrong worker.
  • Asynchronous by default. No server has to be running to receive the human’s response.
  • Persistent record. Every approval lives in email — the most durable and auditable medium available.
  • Works across all contact methods. Ravi provisions both email and phone, so the same pattern works for SMS-based approvals when email isn’t the right channel.

For CodeLayer’s parallel execution model, this matters even more. MULTICLAUDE is only useful if you can manage multiple agents safely. Safe management means knowing which agent is doing what, routing human oversight to the right worker, and keeping a clear record. Ravi is the infrastructure that makes that possible.

Resources