@pushary/server 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +46 -37
  2. package/package.json +6 -4
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # @pushary/server
2
2
 
3
- Server-side SDK for [Pushary](https://pushary.com): send push notifications, and add human-in-the-loop approvals to your AI agent (pause on a decision until a specific human answers).
3
+ The decision layer for AI agents. Your agent asks, your user decides on their phone.
4
+
5
+ This is the server SDK for [Pushary](https://pushary.com). It also sends push notifications.
6
+
7
+ ## What you need
8
+
9
+ - A Pushary Partner plan, from $99 a month, to ask your own users. [Start the trial](https://pushary.com/sign-up?from=agent&plan=partner).
10
+ - An API key from [Partner onboarding](https://pushary.com/onboarding/partner), set as `PUSHARY_API_KEY`.
11
+ - Your users install the free Pushary app ([iPhone](https://apps.apple.com/us/app/pushary/id6785677563), [Android](https://play.google.com/store/apps/details?id=com.pushary.app)). They never sign up or pay.
4
12
 
5
13
  ## Installation
6
14
 
@@ -19,17 +27,25 @@ bun add @pushary/server
19
27
  ```typescript
20
28
  import { createPusharyServer } from '@pushary/server'
21
29
 
22
- const pushary = createPusharyServer({
23
- apiKey: process.env.PUSHARY_API_KEY,
24
- })
30
+ const pushary = createPusharyServer({ apiKey: process.env.PUSHARY_API_KEY })
25
31
 
26
- await pushary.notifications.send({
27
- title: 'Hello!',
28
- body: 'Your order has shipped',
29
- subscriberIds: ['sub_123'],
32
+ const { universalLink } = await pushary.enroll('user-123')
33
+ // Once per user: show universalLink as a button or QR code.
34
+
35
+ const { approved } = await pushary.decisions.ask({
36
+ externalId: 'user-123',
37
+ question: 'Issue a $50 refund?',
38
+ type: 'confirm', // confirm | select | input
30
39
  })
40
+ if (approved) await issueRefund()
31
41
  ```
32
42
 
43
+ That is the whole integration: connect a user's phone once, then ask them whenever
44
+ your agent needs a yes. `ask()` creates a fresh decision per call and waits until the
45
+ person answers or the deadline passes (55 seconds by default). `approved` is true only
46
+ when the person said yes, so a declined, expired or unanswered decision blocks the
47
+ action. For longer waits, see [Decisions (lower-level)](#decisions-lower-level).
48
+
33
49
  ## API Key
34
50
 
35
51
  The server SDK requires your full API key (`pk_xxx.sk_xxx`) which includes the secret portion.
@@ -38,6 +54,22 @@ The server SDK requires your full API key (`pk_xxx.sk_xxx`) which includes the s
38
54
 
39
55
  Get your API key by following [Get your API key](https://pushary.com/docs/agents/api-key).
40
56
 
57
+ ## Push notifications quick start
58
+
59
+ ```typescript
60
+ import { createPusharyServer } from '@pushary/server'
61
+
62
+ const pushary = createPusharyServer({
63
+ apiKey: process.env.PUSHARY_API_KEY,
64
+ })
65
+
66
+ await pushary.notifications.send({
67
+ title: 'Hello!',
68
+ body: 'Your order has shipped',
69
+ subscriberIds: ['sub_123'],
70
+ })
71
+ ```
72
+
41
73
  ## Resources
42
74
 
43
75
  ### Subscribers
@@ -121,30 +153,6 @@ await pushary.notifications.send({
121
153
  })
122
154
  ```
123
155
 
124
- ### Human-in-the-loop for agents (the two-call contract)
125
-
126
- The whole integration is two calls. Connect an end-user's phone once, then ask them
127
- whenever your agent needs a human. Requires the [Partner plan](https://pushary.com/agent-notifications-integration).
128
-
129
- ```typescript
130
- // 1. Connect an end-user's phone (keyless, no account for them). Show the link.
131
- const { universalLink } = await pushary.enroll('user-123')
132
- // Render universalLink as a button or QR. One tap turns on approvals.
133
-
134
- // 2. Ask that person and block until they answer. Fail-closed `approved` flag.
135
- const { approved, value, status } = await pushary.decisions.ask({
136
- externalId: 'user-123',
137
- question: 'Issue a $50 refund?',
138
- type: 'confirm', // confirm | select | input
139
- })
140
- if (approved) await issueRefund()
141
- ```
142
-
143
- `ask()` creates a fresh decision per call and polls
144
- durably until the human answers or the deadline passes (default 55s for a positive local wait budget).
145
- `approved` is true only when the person actually said yes, so a declined, expired, or
146
- unanswered decision safely blocks the action.
147
-
148
156
  ### Decisions (lower-level)
149
157
 
150
158
  For long waits or your own resume logic, use `create` + a webhook or `get`, and
@@ -248,7 +256,8 @@ For an explicit human gate, set `policy: false`. The gate keeps the full review
248
256
  **Upgrade handling:** version 2.1 changes human gate keys. Finish already-started operations on the SDK version that created them, or migrate their persisted bindings explicitly. Do not replay previously executed operations through the new key scheme. A fingerprint identifies the proposed action; the application's execution record and framework persistence still own recovery after an uncertain tool execution.
249
257
 
250
258
  Every official Pushary adapter (`@pushary/eve`, `@pushary/ai-sdk`,
251
- `@pushary/langgraph`, `@pushary/mastra`, `@pushary/openai-agents`) is a thin binding
259
+ `@pushary/langgraph`, `@pushary/mastra`, `@pushary/openai-agents`,
260
+ `@pushary/claude-agent-sdk`) is a thin binding
252
261
  over one shared kernel, and that kernel is public. If you run an in-house harness, or
253
262
  a framework we have not shipped for, this is the same surface they are built on.
254
263
 
@@ -316,15 +325,15 @@ names asks a person. `run` is called only after the action is authorized, and an
316
325
  error it throws is not caught: reporting it as `ok: false` would be
317
326
  indistinguishable from a refusal.
318
327
 
319
- `protect()` is **at most once**. The decision is idempotent — a replay of the same
320
- `runId` + `callId` + action lands on the same approval instead of asking twice — and
328
+ `protect()` is **at most once**. The decision is idempotent: a replay of the same
329
+ `runId` + `callId` + action lands on the same approval instead of asking twice: and
321
330
  the approval is then spent against a durable permit before `run` is called. A retry, a
322
331
  concurrent worker and a resumed run all reach the same permit and exactly one of them
323
332
  proceeds; the rest come back `ok: false` with a reason worded to stop the model rather
324
333
  than invite another attempt.
325
334
 
326
- The permit is bound to the exact subject that was authorized — the action, its target,
327
- the actor, the environment, the end-user and every fact you sent — so changing the
335
+ The permit is bound to the exact subject that was authorized: the action, its target,
336
+ the actor, the environment, the end-user and every fact you sent: so changing the
328
337
  amount between the approval and the execution leaves you with no permit for the action
329
338
  you now want to run. `protect()` then records `succeeded` or `failed` against it; a
330
339
  process that dies mid-action leaves the permit unresolved, which is visible and
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pushary/server",
3
- "version": "2.2.0",
4
- "description": "Pushary Server SDK: send push notifications and add human-in-the-loop approvals to your AI agent (pause on a decision until a specific human answers).",
3
+ "version": "2.2.1",
4
+ "description": "The decision layer for AI agents. Your agent asks, your user decides on their phone. Also sends push notifications.",
5
5
  "author": "Pushary <business@pushary.com>",
6
6
  "homepage": "https://pushary.com",
7
7
  "bugs": {
@@ -53,7 +53,6 @@
53
53
  "keywords": [
54
54
  "pushary",
55
55
  "push-notifications",
56
- "web-push",
57
56
  "notifications",
58
57
  "server-sdk",
59
58
  "api-client",
@@ -61,7 +60,10 @@
61
60
  "human-in-the-loop",
62
61
  "hitl",
63
62
  "agent-approvals",
64
- "llm"
63
+ "llm",
64
+ "approvals",
65
+ "approval",
66
+ "tool-approval"
65
67
  ],
66
68
  "repository": {
67
69
  "type": "git",