Skip to content

Migrating from the HumanLayer SDK

Last verified 2026-10-11.

HumanLayer is a trademark of its respective owner. Vulnify is not affiliated with, endorsed by, or sponsored by HumanLayer.

Statements about HumanLayer come from HumanLayer’s public repository, README, and package pages, linked in Sources. If something here is out of date, tell us at Support and we will fix it.

HumanLayer described itself as “an API and SDK that enables AI Agents to contact humans for help, feedback, and approvals” [S4]. It shipped a Python package, humanlayer, and a TypeScript package, @humanlayer/sdk [S4].

Its README listed two core functions [S4]:

  • @hl.require_approval(), a decorator that “blocks specific function calls until a human has been consulted - upon denial, feedback will be passed to the LLM”.
  • hl.human_as_tool(), which “allows for contacting a human for answers, advice, or feedback”.

The README also listed contact across “Slack, Email, Discord, and more” and “Granular Routing” of approvals to teams or individuals. Its roadmap table listed “Slack Approvals | ⚙️ Beta”, “Email Approvals | ⚙️ Beta”, and “Discord Approvals | 🗓️ Planned” [S4].

  • On 2025-09-29, pull request #646, “Clean up humanlayer sdk stuff, this is codelayer now”, removed the SDKs from the humanlayer/humanlayer repository [S1].
  • The repository’s SDK document now says it “covers the HumanLayer SDK which is being superseded by CodeLayer” and that “The humanlayer sdks were removed in #646” [S2]. The repository README says “the code here is pretty much all deprecated” [S3].
  • The last releases are humanlayer 0.7.9 on PyPI and @humanlayer/sdk 0.7.9 on npm, both from 2025-06-03 [S4][S5]. Both packages are still listed. The npm package is not marked deprecated [S5].
  • HumanLayer’s site now describes “a multiplayer coding agent IDE and cloud” [S6].

We have not verified whether HumanLayer’s hosted API still serves the 0.7.x SDK. If your agents still run on it, check with HumanLayer before you change anything.

The two products work differently. As described in its README, require_approval asks a person on every call to a decorated function [S4]. Vulnify decides each call first: ALLOW runs it, REVIEW waits for a person, and BLOCK stops it. Which calls need a person is set by your policies and the risk engine. See Decisions.

HumanLayer SDK 0.7.x, per its README [S4] Vulnify Notes
HumanLayer() client Vulnify(api_key=...) (Python) or new Vulnify({ apiKey }) (Node.js) Vulnify reads the key you pass. Register the agent and resource names in the app.
@hl.require_approval() on a function vulnify.guard(fn, wait=..., **action) (Python) or vulnify.guard(action, fn, wait) (Node.js) You describe the action: agent, action, resource, destination, recordsAffected. With wait, the function runs only after approval.
Every decorated call goes to a person A policy with decision: REVIEW Calls that match the policy wait for a person. Others are allowed or blocked by policy or by the risk engine. See Policies.
Approval in Slack or by email Approval in the Vulnify app, or with Approve and Deny in Slack See Reviews and Slack. There is no email approval.
Routing to teams or individuals approverRoles on the policy: OWNER, ADMIN, MEMBER By role, not by named person.
Denial feedback passed to the LLM VulnifyBlockedError with the decision’s reasons. A denied review adds Review denied The agent receives the reasons, not a reviewer’s free-text reply.
hl.human_as_tool() No equivalent See What Vulnify does not do.
Approval from an MCP client decide_action, then get_decision with waitMs On the Vulnify MCP server. A person approves in Vulnify or Slack.

This is the example from the HumanLayer README, quoted as published [S4]:

from humanlayer import HumanLayer
hl = HumanLayer()
@hl.require_approval()
def send_email(to: str, subject: str, body: str):
"""Send an email to the customer"""
...
# made up function, use whatever
# tool-calling framework you prefer
run_llm_task(
prompt="""Send an email welcoming the customer to
the platform and encouraging them to invite a team member.""",
tools=[send_email],
llm="gpt-4o"
)

Source: README of humanlayer 0.7.9 on PyPI. Code © HumanLayer contributors, licensed under the Apache License 2.0.

The same tool, guarded by Vulnify. run_llm_task stays whatever framework you use. On REVIEW, the call waits up to 5 minutes for a person to approve, then sends only if approved. On BLOCK, or a denial, the tool returns the reason to the model.

Terminal window
pip install vulnify
import os
from vulnify import Vulnify, VulnifyBlockedError
vulnify = Vulnify(api_key=os.environ["VULNIFY_API_KEY"])
COMPANY_DOMAIN = "@yourcompany.com"
def send_email(to: str, subject: str, body: str):
"""Send an email to the customer"""
...
def send_email_tool(to: str, subject: str, body: str) -> str:
"""The tool you give the model. Vulnify decides before the email is sent."""
try:
vulnify.guard(
lambda: send_email(to, subject, body),
wait={"timeout": 300, "poll": 2},
agent="OnboardingBot",
action="SEND_EMAIL",
resource="Customer Contacts",
destination="INTERNAL" if to.lower().endswith(COMPANY_DOMAIN) else "EXTERNAL_EMAIL",
records_affected=1,
)
return f"Email sent to {to}."
except VulnifyBlockedError as blocked:
return f"Email not sent. {blocked}"

Then add a policy so outbound email waits for a person. The format is on Policy files:

apiVersion: vulnify.io/v1
kind: Policy
metadata:
name: review-external-customer-email
spec:
description: A person approves agent email to addresses outside the company
enabled: true
action: ANY
resource: ANY
condition:
action: SEND_EMAIL
destination: EXTERNAL
decision: REVIEW
mode: ENFORCE

To ask on every SEND_EMAIL call, drop destination from the condition. Every SEND_EMAIL then waits for a person.

On Node.js the same pattern is vulnify.guard(action, fn, { timeoutMs: 300_000 }). Adapters for LangChain, the OpenAI Agents SDK, the Vercel AI SDK, MCP servers, CrewAI, and LangGraph are on Framework adapters. A longer walkthrough is Stop an AI agent from exporting customer data.

If you rely on any of these, plan for it before you migrate.

  • No human-as-a-tool. An agent cannot ask a person a free-form question and get a text answer back. Vulnify only decides whether an action runs.
  • No email, Discord, or SMS approvals. Reviews are answered in the Vulnify app or in Slack.
  • No free-text feedback to the model. A denial returns the decision’s reasons and Review denied. There are no custom response or reject options.
  • No routing to named people. Approvers are chosen by role on the policy.
  • No local CLI approval. The vulnify CLI validates, applies, and tests policies. It does not approve reviews.
  • No webhook when a review is resolved. Your code polls GET /v1/events/{id}, or uses wait in the SDK. See Reviews.
  • Setup per action. Agents and resources are registered in the app, and each guarded call describes its action. A bare decorator with no arguments is not enough.
  • No ControlFlow or Griptape adapters. The adapters cover the frameworks listed on Framework adapters.

All accessed 2026-10-11.

HumanLayer is a trademark of its respective owner. Vulnify is not affiliated with, endorsed by, or sponsored by HumanLayer.