Use this file to discover all available pages before exploring further.
What this builds. A conversational agent whose Exa search tool is gated with REQUIRES_APPROVAL, so the turn pauses until the client replies.
You’ll end up with. An [approval] log line, an auto-approved response via session.respond(...), and a resumed turn that streams to completion.Conversational agents pause their turn when a tool is gated with
ToolApprovalConfig.REQUIRES_APPROVAL. The paused turn surfaces a
SessionEvent on listen() with
event.type == SessionEventType.APPROVAL_REQUEST. Item 3’s helpers let
you reply on the same session without hand-rolling proto/JSON payloads.What this demonstrates:
session.respond(event, approved=True) — dispatches on event type
The paused turn resumes and more MESSAGE_DELTA events flow
Requirements for a real e2e run:
A conversational agent whose tool(s) are already gated with
`ToolApprovalConfig.REQUIRES_APPROVAL` (typically configured onthe agent in the dashboard, or set via `tool.approval_config`post-construction once that runtime wiring lands).
The Go WS handler’s send_response path landed (v2 item 7). Until
that ships, this example will successfully emit the JSON frame butthe server will reject it as an unknown message type and the turnwill time out.
The v1 tool constructors do not yet plumb approval_config through
at runtime (the .pyi stub accepts it but the value is dropped). This
example therefore creates the tool without it and expects the agent’s
approval gating to come from the server-side configuration.
import asynciofrom vectorshift import ToolApprovalConfigfrom vectorshift.agent import Agent, AgentType, LLMInfo, MemoryConfigfrom vectorshift.agent.tool import ToolInput, ToolInputTypefrom vectorshift.agent.tools import ExaAiToolfrom vectorshift.events import SessionEventTypeasync def main() -> None: search = ExaAiTool( tool_name="exa_ai_search", query=ToolInput(type=ToolInputType.DYNAMIC, description="Search query"), num_results=ToolInput( type=ToolInputType.STATIC, description="Number of results to return", value=5 ), approval_config=ToolApprovalConfig.REQUIRES_APPROVAL, ) agent = Agent.new( name="Approval demo", type=AgentType.CONVERSATIONAL, llm_info=LLMInfo(provider="openai", model_id="gpt-4o"), tools=[search], instructions="You help research topics; always ask to search the web.", memory_config=MemoryConfig(enable_session_memory=True), ) print(f"Created agent: {agent.name} (id={agent.id})") try: async with await agent.create_session() as session: print(f"Session: {session.session_id}") await session.send( "Find the latest news on quantum computing. Use the EXA AI search tool to search the web." ) async for event in session.listen(): if event.type == SessionEventType.APPROVAL_REQUEST: tool = event.data.get("tool_name") or event.data.get("tool_id") print(f"\n[approval] tool={tool!r} — auto-approving") await session.respond(event, approved=True) # Or, with extra field values the agent requested: # await session.respond_approval( # event, approved=True, confirm={"num_results": 3} # ) continue if event.type == SessionEventType.TOOL_RESULT: print(f"[Tool Result] {event.data.get('result', '')}") continue if event.type == SessionEventType.TOOL_CALL: print( f"[Tool Call] {event.tool_name} - {event.data.get('status', '')}" ) print(f"[Tool Call Data] {event.data}") continue if event.delta: print(event.delta, end="", flush=True) if event.is_complete: print("\n(turn complete)") break finally: agent.delete() print("Deleted agent.")if __name__ == "__main__": asyncio.run(main())