OpenAI Agents SDK
Phone approvals for OpenAI Agents SDK agents. Your agent asks, your user taps Approve or Deny.
The OpenAI Agents SDK can stop a run before a tool executes. You mark the tool with
needsApproval, and the run comes back with interruptions instead of running it.
Somebody still has to decide. @pushary/openai-agents (TypeScript) and
pushary-openai-agents (Python) send each interruption to your user's phone, record
their answer on the run, and hand it back so you can continue.
Install
npm i @pushary/openai-agents @openai/agents zod
export PUSHARY_API_KEY=pk_xxx.sk_xxxGet the key at Partner onboarding.
Connect a user once
import { connect } from '@pushary/openai-agents'
const { universalLink } = await connect({ apiKey: process.env.PUSHARY_API_KEY! }, user.id)The user opens the link on their phone once. They install the free Pushary app and never sign up or pay.
Require approval before a tool runs
import { Agent, run, tool } from '@openai/agents'
import { z } from 'zod'
import { pusharyNeedsApproval, resolvePusharyInterruptions } from '@pushary/openai-agents'
const issueRefund = tool({
name: 'issue_refund',
description: 'Refund an order',
parameters: z.object({ orderId: z.string(), amount: z.number() }),
needsApproval: pusharyNeedsApproval(),
execute: async ({ orderId, amount }) => refundOrder(orderId, amount),
})
const agent = new Agent({
name: 'Refunds',
instructions: 'Refund orders when the customer asks.',
tools: [issueRefund],
})
let result = await run(agent, 'Refund order 1234 in full.')
while (result.interruptions?.length) {
const outcome = await resolvePusharyInterruptions(
{ externalId: user.id },
{ interruptions: result.interruptions, state: result.state },
)
if (!outcome.allApproved) break
result = await run(agent, result.state)
}Each interruption becomes one question on the phone, one at a time. Approve lets the
tool run when you continue the run. Deny, or no answer in time, rejects it, and the
model is told why so it does not keep retrying. refundOrder is your own function.
In Python, continue with outcome.state. It carries the answers. Replaying the
conversation instead would ask your user the same thing twice.
Let the model ask a question
import { Agent, run } from '@openai/agents'
import { pusharyTool } from '@pushary/openai-agents'
const agent = new Agent({
name: 'Support',
instructions: 'Ask the customer which order they need help with.',
tools: [pusharyTool({ apiKey: process.env.PUSHARY_API_KEY! }, { externalId: user.id })],
})
const result = await run(agent, 'Which order needs help?')The model decides when to call this tool, so it suits questions. Keep
needsApproval for anything that must not happen without a yes.
The user id is bound in your code, never taken from the model, so a tricked model cannot ask the wrong person.
Source and more examples: pushary-openai-agents on GitHub. OpenAI reference: human in the loop in TypeScript and in Python.