Examples

DeepAgents

A Deep Agent with a research subagent that writes a report and needs a human's approval to publish it.

A report writer built with create_deep_agent. It delegates fact-finding to a researcher subagent, writes the report with the built-in file tools, and pauses on the one step with consequences: publish_report. Deep Agents use the same human-in-the-loop middleware as create_agent, so the Vigilator bridge works unchanged. Source: Vigilator/examples/deepagents.

What you'll see

  • Inbox - an interrupt for publish_report with its arguments and the transcript, including the report the agent wrote, with Approve and Reject.
  • Live View - a report-writer session where the subagent's work is folded behind the task tool chip and the report appears as the write_file call.

Run it

Install

git clone https://github.com/Vigilator/examples
cd examples/deepagents
uv sync

Deep Agents need Python 3.11 or newer.

Configure

cp .env.example .env

Fill in VIGILATOR_API_KEY and ANTHROPIC_API_KEY.

Run with polling

uv run main.py

The agent researches, writes /reports/q3-support.md, and asks to publish it. Approve and the run completes; reject with a reason and the agent keeps the draft and explains why it stopped.

Or resume by webhook

uv run fastapi dev webhook.py
svix listen http://localhost:8000/webhooks/vigilator

Register the relay's URL for interrupt.answered, put the signing secret in .env, and POST /runs.

How it works

interrupt_on gates the tool; the subagent and the built-in file tools run unreviewed:

agent.py
RESEARCHER = {
    "name": "researcher",
    "description": "Finds facts and figures in the team's notes.",
    "system_prompt": "Search the notes and reply with the facts you found as bullets.",
    "tools": [search_notes],
}

agent = create_deep_agent(
    model=MODEL,
    tools=[publish_report],
    system_prompt=SYSTEM_PROMPT,
    subagents=[RESEARCHER],
    interrupt_on={"publish_report": {"allowed_decisions": ["approve", "reject"]}},
    checkpointer=checkpointer,
)

The interrupt payload is the same action_requests / review_configs shape as LangChain, so the bridge opens the Vigilator interrupt and resumes with Command(resume={"decisions": [...]}) without any Deep Agent specifics.

Streaming needs one allowance: Deep Agent updates carry more than messages. The middleware nodes emit None and the tools node also emits files, so the bridge forwards only the messages it finds:

vigilator_bridge.py
for chunk in agent.stream(input_, config, stream_mode="updates"):
    for node, update in chunk.items():
        if node == "__interrupt__":
            pending = update[0]
            continue
        if isinstance(update, dict) and update.get("messages"):
            live.push(update["messages"])

Adapting it

  • Gate built-in tools the same way, for example "write_file": {"allowed_decisions": ["approve", "edit", "reject"]}, and add the tool's schema to ARGS_SCHEMAS if you allow edits.
  • Subagents may have gated tools of their own; their interrupts surface through the parent run on the same thread.
  • Stream with subgraphs=True to see the subagent's own turns in Live View rather than just the task result.

On this page