npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pushary/ai-sdk

v0.3.1

Published

Phone approvals for Vercel AI SDK agents. Your agent asks, your user taps Approve or Deny.

Readme

@pushary/ai-sdk

Phone approvals for Vercel AI SDK agents. Your agent asks, your user taps Approve or Deny.

Integration guide · Connect your customer’s phone · Report a problem

CI npm license

What you need

Quick start

npm i @pushary/ai-sdk ai zod
import { generateText, stepCountIs } from 'ai'
import { createPusharyTools, enroll } from '@pushary/ai-sdk'

const { universalLink } = await enroll({ apiKey: process.env.PUSHARY_API_KEY! }, user.id)
// Once per user: show universalLink as a button or QR code.

const { text } = await generateText({
  model: 'openai/gpt-4o',
  tools: createPusharyTools({ apiKey: process.env.PUSHARY_API_KEY!, externalId: user.id }),
  stopWhen: stepCountIs(10),
  prompt: 'Ask the customer which order they need help with.',
})

The agent gets an askHuman tool. When it calls it, the person gets a push notification and approves, declines, picks an option, or types an answer from their phone. The tool waits until they reply, then hands the model a clear result. Cache the fact that a user enrolled, not the link itself: it is single-use.

Use createPusharyTools when your agent should ask a question. Use pusharyApproval (below) when a tool must not run without a yes. Yes or no questions can be answered from the lock screen. Choices and typed answers open the app. Legacy web links remain available.

The AI SDK's own approval docs: Tools and tool calling.

Try it before signing up

Open the no-signup browser demo. It demonstrates a human approval with an open phone page and temporary state; it does not demonstrate push delivery or durable production storage.

For a local example using the real AI SDK approval callback:

git clone https://github.com/Pushary/pushary-ai-sdk.git
cd pushary-ai-sdk
npm install
npm run build
node examples/refund.mjs

Use Node.js 22 and AI SDK 7 (installed by this repo). No account, card, API key, or model provider is needed for this simulation. It checks all three outcomes:

yes: executed (simulated refund)
no: blocked (simulated refund)
unanswered: blocked (simulated refund)

Read the example and try a real phone approval. The integration code is MIT-licensed; real phone delivery uses the hosted Pushary service and requires Partner access.

Get help or contribute an example.

Behavior that matters

  • No answer means no. A declined, expired, or unanswered confirm is reported to the model as "not approved, do not proceed." Approval only happens on an explicit yes.
  • Bounded wait. Each ask waits up to 55 seconds by default (timeoutMs). A later answer does not restart that completed request. For delayed answers, use the saved-message recipe below or your existing workflow runner.
  • No double-asks on retry. A retried step reuses the same decision instead of paging the human twice.

Gating a tool the model cannot skip

createPusharyTools gives the model a tool it chooses to call. That is right for "go ask someone about this", and wrong for "this must not happen without a yes", because a model that does not want to be interrupted can decline to call it.

For an enforced gate, use pusharyApproval() in the AI SDK's own toolApproval. The SDK evaluates it before the tool executes, so there is no path around it:

import { generateText } from 'ai'
import { pusharyApproval } from '@pushary/ai-sdk'

const { text } = await generateText({
  model: 'openai/gpt-4o',
  tools: { issueRefund, lookupOrder },
  // only issueRefund asks a human; lookupOrder runs untouched
  toolApproval: pusharyApproval({ externalId: user.id, tools: ['issueRefund'] }),
  prompt: 'Refund order 1234.',
})

Drop tools to gate every call. For the per-tool form:

import { pusharyToolApproval } from '@pushary/ai-sdk'

const { text } = await generateText({
  model: 'openai/gpt-4o',
  tools: { issueRefund, lookupOrder },
  toolApproval: {
    issueRefund: pusharyToolApproval({ toolName: 'issueRefund', externalId: user.id }),
  },
  prompt: 'Refund order 1234.',
})

A denial, an expiry, or nobody answering all come back denied and the tool does not run. The decision is keyed on the tool call, so a provider-level retry resolves to the same decision instead of asking twice.

For a multi-tenant product, resolve the end-user per call:

toolApproval: pusharyApproval({ externalId: (call) => ownerOf(call.input) })

Use ai@7 for the toolApproval gate examples above. This API is absent from [email protected] and [email protected]; the package's ai >= 5.0.0 peer range also serves the basic ask tool, which works from ai@5 on.

Runtime and approval boundaries

Set policy: false when a person must always decide. A trusted subject resolver can provide the action target, parameters, and presentation. Recipient identity comes from your authenticated application, never from model-supplied arguments. Tool retries keep their review only while the recipient and complete proposed action remain identical.

Run npm run build then node examples/refund.mjs for a real AI SDK 7 execution with simulated approval responses; --live enrolls your test phone while the refund remains simulated. This request-time gate is bounded by its timeout. For answers arriving later, persist the AI SDK's pending approval and messages in your application and resume them only after verifying the matching decision. This package does not add a durable runner.

Version 0.3.0 requires server SDK 2.1. The basic ask tool retains ai >=5 and Node.js 18 as its minimum metadata; follow the runtime requirements of your installed AI SDK version. The enforced toolApproval API requires AI SDK 7 and Node.js 22 or later. The delayed SQLite recipe needs Node.js 22.13 or later (tested on 24.3 with [email protected]); the broader peer range is not a claim that every version was tested. Do not assume nested subagent tools support the same approval API. Upgrade alongside server SDK 2.1 and finish old pending operations on their original version, because approval keys now bind the full action.

Delayed customer answers

The SQLite reference recipe saves AI SDK's native approval messages before asking the customer, reads the authoritative answer later, and resumes only the saved call. It includes atomic worker claims, exact customer/action binding, durable outputs and uncertain-execution recovery. No hosted scheduler is required.

Run npm run test:restart with Node.js 22.13 or later (tested on 24.3) to check fresh-process recovery, duplicate workers and crash handling using a simulated API. Examples are included in the npm package. The delayed recipe requires AI SDK 7; confirm/select/input in the existing ask tool remain request-time interactions.

API

createPusharyTools(config)

config: { apiKey, externalId, agentName?, timeoutMs?, baseUrl? }. Returns { askHuman }, a tool you pass to generateText / streamText. Merge it with your own tools:

tools: { ...createPusharyTools({ apiKey, externalId }), ...myOtherTools }

enroll(config, externalId)

config: { apiKey, baseUrl? }. Returns { token, deepLink, universalLink, expiresInSeconds }.

Under the hood

This package is a thin binding over the shared adapter kernel in @pushary/server (@pushary/server/adapters), which every Pushary framework adapter is built on. Use @pushary/server directly for any framework, @pushary/server/adapters to write your own adapter, or the Pushary MCP server to wire agents up with no code at all. See the adapters guide.

MIT

Example

A runnable example is in examples/.