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/eve

v0.3.1

Published

Eve tools and a Pushary channel: pause your agent until a real human approves on their phone (human-in-the-loop).

Readme

@pushary/eve

CI npm license

Full walkthrough: Human-in-the-loop for Eve. Reaching your own end-users on their phones is the Pushary Partner plan.

Human-in-the-loop for Eve. Give your agent a tool that pauses until a real human approves on their phone, answered from the lock screen.

Two calls is the whole integration:

  1. pusharyConnectPhone() returns a link the end-user taps once to connect their phone.
  2. pusharyAskHuman() asks that person and blocks until they answer, with a fail-closed result.

Requires the Pushary Partner plan.

Install

npm i @pushary/eve

Set PUSHARY_API_KEY (get it in your dashboard).

Use

Eve discovers tools by file. Drop in two one-line files:

// agent/tools/ask-human.ts
import { pusharyAskHuman } from '@pushary/eve'
export default pusharyAskHuman()
// agent/tools/connect-phone.ts
import { pusharyConnectPhone } from '@pushary/eve'
export default pusharyConnectPhone()

That is it. The agent now has ask-human (approve / choose / free-text, delivered to a phone, blocks until answered) and connect-phone (returns the one-tap connect link).

Gating a tool the model cannot skip

pusharyAskHuman() is a tool the model 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 Eve's own per-tool approval: field. Eve evaluates it before execute runs, so there is no path around it:

// agent/tools/issue-refund.ts
import { defineTool } from 'eve/tools'
import { z } from 'zod'
import { pusharyApproval } from '@pushary/eve'

export default defineTool({
  description: 'Refund an order',
  inputSchema: z.object({ amount: z.number(), customer: z.string() }),
  approval: pusharyApproval(),
  execute: async ({ amount }) => refund(amount),
})

Eve names a tool by its file path, so there is no name field; this one registers as issue_refund.

It sits alongside Eve's built-in always(), never() and once(), which decide statically. This one asks a person and waits for the answer.

Fail-closed: a denial, an expiry, or nobody answering all come back denied and the tool does not run. The decision is keyed on session, call and tool, so a replayed turn resolves to the same decision instead of asking twice.

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

approval: pusharyApproval({ externalId: (ctx) => ctx.toolInput?.customer })

pusharyApproval<TInput>() is generic over your tool's input, so ctx.toolInput is typed inside that callback.

The channel

The tools above are for asking on purpose. The channel covers everything Eve already pauses on: any tool gated with approval from eve/tools/approval, and the built-in ask_question. Eve renders those as buttons in Slack. This renders them on a phone.

// agent/channels/pushary.ts
import { pusharyChannel } from '@pushary/eve'
export default pusharyChannel()
PUSHARY_API_KEY=pk_...sk_...
PUSHARY_WEBHOOK_SECRET=whsec_...          # decisions.getWebhookSecret()
PUSHARY_CALLBACK_ORIGIN=https://your-agent.vercel.app

Eve emits input.requested and parks the turn durably. The channel opens a Pushary decision carrying a signed callback URL, and nothing is held open while it waits, so an approval can sit for hours at zero idle compute. When the human taps, POST /pushary/answer verifies the webhook signature and the per-request routing signature, then resumes the parked turn with the matching inputResponses entry.

Three more routes put the rest of the session on the phone:

| Route | Does | | --- | --- | | POST /pushary/message | Send a follow-up, starting a session if there is none | | POST /pushary/stop | cancel() the in-flight turn, leaving history intact | | POST /pushary/reset | reset() the session so the next message starts clean |

Options are matched back by label and then by id, so an approval resolves to Eve's approve or deny and a select resolves to the option that was tapped. Each decision carries an idempotency key derived from the session and request id, so a replayed step never asks the same person twice. A stale answer, one whose session already moved on, returns 410 rather than failing the webhook.

Who answers

By default each tool asks the session principal, so run user-scoped auth and each end-user is their own principal. To bind a fixed end-user (single-user agents, jobs), pass one:

export default pusharyAskHuman({ externalId: 'user_123' })

If there is no principal and no configured externalId, the tool throws a clear error instead of asking the wrong person.

Behavior that matters

  • Fail-closed. A declined, expired, or unanswered confirm is reported to the model as "not approved, do not proceed." Approval only happens on an explicit yes.
  • Serverless-safe. Each ask blocks up to 55 seconds by default (timeoutMs). The decision stays answerable for its full lifetime.

Config

pusharyAskHuman(config?) / pusharyConnectPhone(config?) accept { apiKey?, externalId?, agentName?, timeoutMs?, baseUrl? }. apiKey defaults to process.env.PUSHARY_API_KEY.

Under the hood

Thin binding over the shared adapter kernel in @pushary/server (@pushary/server/adapters), which every Pushary framework adapter is built on. The tools use enroll + decisions.ask; the channel uses decisions.create with a signed callback URL; pusharyApproval() uses the kernel's fail-closed gate. Requires [email protected] or newer. See the adapters guide.

MIT

Example

A runnable example is in examples/.