Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
ag2ai avatar

Ag2 Hitl

  • 36 installs
  • 8 repo stars
  • Updated July 27, 2026
  • ag2ai/ag2-skills

ag2-hitl is a Claude Code skill that pauses an AG2 beta Agent to collect human input or gate a tool call behind human approval.

About

ag2-hitl is a Claude Code skill that adds human-in-the-loop control to an AG2 beta Agent. It pauses the agent mid-run to collect typed human input via context.input() with a hitl_hook, and gates specific tool calls behind approval_required() middleware. A developer uses it when the agent should ask for confirmation, request missing information, or require human approval before running sensitive, irreversible or expensive tool calls such as sending emails, deleting records or payments.

  • Pauses an AG2 beta Agent mid-run to collect human input via context.input() and a hitl_hook
  • Gates sensitive tool calls behind approval_required() middleware with approve/deny/always
  • Covers both open-question input and per-tool approval, including custom prompts and timeouts

Ag2 Hitl by the numbers

  • 36 all-time installs (skills.sh)
  • Ranked #8,638 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Aug 1, 2026 (Skillselion catalog sync)
At a glance

ag2-hitl capabilities & compatibility

Free skill; running the agent needs an LLM provider API key such as OpenAI.

Capabilities
human in the loop · tool approval · mid run input · agent guardrails
Works with
openai
Use cases
orchestration
Pricing
Bring your own API key
From the docs

What ag2-hitl says it does

Pause an AG2 beta `Agent` mid-run to collect human input via `context.input()`, or gate a tool call with `approval_required()` middleware.
SKILL.md
npx skills add https://github.com/ag2ai/ag2-skills --skill ag2-hitl

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs36
repo stars8
Last updatedJuly 27, 2026
Repositoryag2ai/ag2-skills

What it does

Add human confirmation, input requests and tool-call approval gates to an AG2 beta Agent.

Who is it for?

Developers who need an AG2 agent to ask for confirmation, request missing info, or require approval before risky tool calls.

Skip if: Fully autonomous agents that never need human confirmation or input.

When should I use this skill?

The agent should ask for confirmation, request missing info, or require approval for a sensitive or irreversible tool call.

What you get

The agent pauses for typed human input or explicit approval before risky tool calls proceed.

By the numbers

  • 2 HITL mechanisms (context.input and approval_required)
  • default approval timeout is 30 seconds

Files

SKILL.mdMarkdownGitHub ↗

Human-in-the-loop

When to use

  • The agent should ask for confirmation before doing something risky.
  • The agent needs information from the user mid-conversation (a password, an API key, missing context).
  • A specific tool call should require human approval before it runs (irreversible / expensive / sensitive).
  • Quality assurance — show a draft, get human edits/approval before finalising.

Two distinct mechanisms — pick by intent:

NeedUse
Tool asks an open question and waits for a typed answercontext.input() from inside the tool + hitl_hook on the agent
Approve / deny a specific tool call before its body runsapproval_required() tool middleware

Pattern 1 — context.input() for open questions

A tool requests input via Context.input(message, timeout=...). The agent must have a hitl_hook that knows how to collect that input.

from autogen.beta import Agent, Context, tool
from autogen.beta.events import HumanInputRequest, HumanMessage

@tool
async def execute_query(context: Context) -> str:
    answer = await context.input(
        "Are you sure you want to run this query? (yes/no)",
        timeout=60.0,
    )
    if answer.strip().lower() != "yes":
        return "Query cancelled."
    return "Query executed successfully."

def hitl_hook(event: HumanInputRequest) -> HumanMessage:
    print(f"Agent asks: {event.content}")
    return HumanMessage(content=input("Your answer: "))

agent = Agent("dba", tools=[execute_query], hitl_hook=hitl_hook)

The hook receives a HumanInputRequest (the prompt is in event.content) and returns either a HumanMessage or a plain str (the framework wraps a str via HumanMessage.ensure_message). Both def and async def hooks are supported.

You can also register the hook after construction:

agent = Agent("dba", tools=[execute_query])

@agent.hitl_hook
async def async_hitl_hook(event: HumanInputRequest) -> HumanMessage:
    answer = await collect_from_ui(event.content)
    return HumanMessage(content=answer)

The decorator overrides any hook set in the constructor — but if one was already set (e.g. via the constructor), applying @agent.hitl_hook emits a RuntimeWarning ("You already set HITL hook, provided value overrides it"). Set the hook in exactly one place to avoid the warning.

The hook participates in dependency injection — Context, Inject, Variable, Depends work the same as in tools.

If context.input() is called and no hook is registered, the framework raises HumanInputNotProvidedError.

Pattern 2 — approval_required() for specific tool calls

Gate a single tool with the built-in approval middleware. The user is prompted before the tool body runs and can approve or deny.

from autogen.beta import Agent, tool
from autogen.beta.config import OpenAIConfig
from autogen.beta.middleware import approval_required

@tool(middleware=[approval_required()])
def delete_account(user_id: str) -> str:
    """Deletes a user account by ID permanently."""
    return f"Account {user_id} deleted."

agent = Agent(
    "support",
    config=OpenAIConfig(model="gpt-4o-mini"),
    tools=[delete_account],
    hitl_hook=lambda event: input(event.content),
)

When the agent calls delete_account, the user sees (with the default allow_always=True):

Agent wants to call the tool:
`delete_account`, {"user_id": "abc-123"}
Please approve or deny this request.
Y/N/Always?

The answer is lowercased before matching. y, yes, or 1 approve this one call; always approves this call and all subsequent calls of the same tool in the same context (it sets a per-context bypass flag). Anything else denies it; the agent receives denied_message (default "User denied the tool call request") and can adjust. The default timeout is 30 seconds. Set allow_always=False to drop the "Always" option (the prompt then shows just Y/N?).

approval_required() calls context.input() under the hood, so it also requires a `hitl_hook` — without one you'll get a runtime error.

Custom prompt

@tool(middleware=[approval_required(
    message="⚠️ Run `{tool_name}` with {tool_arguments}? (y/n)",
    denied_message="Operation blocked by user.",
)])
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to the given address."""
    ...

{tool_name} and {tool_arguments} are interpolated.

Pairing both patterns

For a tool that both gathers input mid-run and requires approval:

@tool(middleware=[approval_required()])
async def schedule_report(name: str, context: Context) -> str:
    """Schedule a report — asks the user for the cadence, then runs after approval."""
    cadence = await context.input("How often? (daily / weekly / monthly)")
    return f"Scheduled '{name}' on {cadence} cadence."

The approval middleware runs first (outermost). Once approved, the tool body executes and context.input() triggers a second human interaction.

Going deeper

  • Source docs: website/docs/beta/context/human_in_the_loop.mdx (context.input, hitl_hook), website/docs/beta/tools/approval_required.mdx (approval_required middleware).
  • Tool middleware in general — website/docs/beta/tools/tool_middleware.mdx. See also ag2-middleware for agent-wide HITL interception via BaseMiddleware.on_human_input().
  • HITL hooks support dependency injection identically to tools — see ../ag2-add-custom-tool/references/dependency_injection.md.

Common pitfalls

  • `approval_required()` without a `hitl_hook` — the middleware calls context.input(), so the agent needs a hook. You'll see HumanInputNotProvidedError otherwise.
  • Forgetting to handle the denial pathcontext.input() returns whatever the hook returns. If you only branch on "yes", any other answer (including silence/default) lets the operation continue. Always validate.
  • Sync `input()` in an async UIinput() blocks the event loop. Use an async hook (async def) and an async input collector (web socket, message queue) for any non-CLI app.
  • No timeoutcontext.input(prompt) can wait forever. Pass timeout=60.0 (seconds) for any production path.
  • Decorator hook overrides constructor hook — if you set both, the decorator wins and emits a RuntimeWarning. Pick one place.
  • Expecting `HumanMessage` to flow into the conversation history automatically — it does for the requesting tool's return value, but mid-run inputs collected via ctx.input() are not separate user turns. They live in the tool's scope.

Related skills

FAQ

How do the two HITL mechanisms differ?

context.input() with a hitl_hook asks an open question and waits for a typed answer, while approval_required() middleware approves or denies a specific tool call before it runs.

Does approval_required() need a hitl_hook?

Yes. approval_required() calls context.input() under the hood, so it also requires a hitl_hook or you get a runtime error.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.