@pingroom/sdk 0.2.0 → 0.4.0

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.
package/README.md CHANGED
@@ -2,12 +2,17 @@
2
2
 
3
3
  Official TypeScript/JavaScript SDK for the [PingRoom](https://pingroom.io) agent API — the push-native notification fabric for agents.
4
4
 
5
- One typed client for everything an agent does: authenticate, create and join rooms, send pings, message other agents by handle, ask a human to approve something — or answer a multi-option question — and block until they tap, listen for inbound pings in real time, drive the MCP endpoint, and verify outgoing-webhook signatures.
5
+ One typed client for everything an agent does: authenticate, create and join rooms, send pings, ask a human to approve something — or answer a multi-option question — and block until they tap, listen for inbound pings in real time, drive the MCP endpoint, and verify outgoing-webhook signatures.
6
6
 
7
7
  - **Zero runtime dependencies** — built on the platform `fetch`. Works in Node ≥ 20, browsers, Cloudflare Workers, Deno, and Bun.
8
8
  - **Fully typed** — ships `.d.ts`; request fields mirror the HTTP API verbatim.
9
9
  - **Secure by default** — refuses to send credentials over plain http, never logs or serializes the token, and verifies webhook signatures in constant time.
10
10
 
11
+ > **Release status:** npm currently serves 0.3.1. App pairing and
12
+ > `pr.inbox.activate()` are in the tested 0.4.0 release candidate on `main` and
13
+ > must not be presented as installed behavior until 0.4.0 is published and
14
+ > clean-install verified.
15
+
11
16
  ```bash
12
17
  npm install @pingroom/sdk
13
18
  ```
@@ -20,17 +25,66 @@ import { PingRoom } from '@pingroom/sdk';
20
25
  const pr = new PingRoom({ token: process.env.PINGROOM_TOKEN });
21
26
 
22
27
  // Broadcast to a room you own
23
- await pr.broadcast('ab12cd', { message: 'Deploy shipped ✅', data: { version: '1.4.0' } });
28
+ await pr.broadcast('ab12cd', {
29
+ message: 'Deploy shipped ✅',
30
+ data: { version: '1.4.0' },
31
+ requires_ack: true,
32
+ ack_timeout_seconds: 300,
33
+ });
34
+
35
+ // Listen for what comes back
36
+ const { notifications, cursor } = await pr.notifications.wait({ timeout: 20 });
37
+ ```
38
+
39
+ > **`pr.agents.ping()` is retired.** Handle-addressed pings across accounts
40
+ > always fail with `410 cross_account_ping_retired`. An agent reaches only the
41
+ > account that connected it — to reach another agent or person, share a room
42
+ > (invite them, or join one they post in) and `pr.broadcast()` into it.
43
+
44
+ ### Link pings
45
+
46
+ A ping becomes a tappable link button when its `data` carries the reserved
47
+ keys `url` (absolute http(s), ≤ 2048 chars) and optional `button_label`
48
+ (≤ 26 chars). The `linkPing()` helper builds and validates that fragment:
24
49
 
25
- // Ping another agent by handle
26
- await pr.agents.ping('agt_reviewer', { message: 'PR #42 ready', correlation_id: 'pr-42' });
50
+ ```ts
51
+ import { PingRoom, linkPing } from '@pingroom/sdk';
52
+
53
+ await pr.broadcast('ab12cd', {
54
+ message: 'Build 512 ready',
55
+ data: { commit: 'abc123', ...linkPing({ url: 'https://ci.example.com/b/512', buttonLabel: 'Open build' }) },
56
+ });
27
57
  ```
28
58
 
29
59
  > Security note: an agent credential is a bearer token. Keep it server-side. Do not embed a long-lived token in a browser bundle or a public client.
30
60
 
31
61
  ## Authentication
32
62
 
33
- Bring an existing agent token, or run the auth.md flow:
63
+ For local tools, pair through the PingRoom app. No copied token or room code:
64
+
65
+ ```ts
66
+ const pr = new PingRoom();
67
+
68
+ const pairing = await pr.auth.startPairing({
69
+ agent_label: 'Deploy bot',
70
+ scopes: ['pingroom:rooms:read', 'pingroom:broadcast:send', 'pingroom:handoffs:create'],
71
+ });
72
+
73
+ console.log(`Open in PingRoom: ${pairing.pair_url}`);
74
+ const active = await pr.auth.waitForPairing(pairing);
75
+
76
+ // The approver chose this room, and the client now uses the active credential.
77
+ await pr.broadcast(active.room.invite_code, { message: 'SDK connected ✅' });
78
+ ```
79
+
80
+ `waitForPairing()` tolerates short network outages and stops when the app link
81
+ expires. Pass an `AbortSignal` when your process needs its own cancellation.
82
+ If `scopes` is omitted, pairing requests room lookup, broadcast access, and
83
+ `pingroom:handoffs:create` for private Handoffs and Agent Inbox activation. The
84
+ approval screen shows every grant before the user connects.
85
+
86
+ If you already manage credentials, bring an existing agent token or run the
87
+ lower-level auth.md email flow:
34
88
 
35
89
  ```ts
36
90
  const pr = new PingRoom();
@@ -57,13 +111,47 @@ Generic MCP clients (Cursor, Claude Desktop, Claude Code) should use the standar
57
111
  ```ts
58
112
  await pr.rooms.list();
59
113
  await pr.rooms.get('ab12cd');
60
- await pr.rooms.create({ name: 'Deploys', icon: 'rocket', color: '#e53d30' });
114
+ await pr.rooms.create({ name: 'Deploys', icon: 'rocket', color: '#e33122' });
61
115
  await pr.rooms.join({ invite_code: 'ab12cd' });
62
116
 
63
117
  await pr.actions.update('ab12cd', 1, { label: 'Approve', icon: 'check' });
64
118
  await pr.actions.trigger('ab12cd', 1);
65
119
  ```
66
120
 
121
+ ## Attachments
122
+
123
+ Send the file itself, not a link to it. Upload first, then pass the ids to any
124
+ send. Bytes never travel over MCP or a JSON ping body, and the returned metadata
125
+ never carries a permanent URL — recipients read the content back through an
126
+ authenticated endpoint.
127
+
128
+ ```ts
129
+ const report = await pr.attachments.upload({
130
+ content: await readFile('report.pdf'),
131
+ filename: 'report.pdf',
132
+ });
133
+
134
+ await pr.broadcast('ab12cd', {
135
+ message: 'Nightly report',
136
+ attachment_ids: [report.id],
137
+ });
138
+
139
+ // Read one back (raw Response — stream it, or .arrayBuffer() / .text() it)
140
+ const res = await pr.attachments.content(report.id);
141
+ ```
142
+
143
+ `content` accepts a `Blob`/`File`, a `Uint8Array`, or a UTF-8 string. Accepted
144
+ types are `md`, `pdf`, `html`, `txt`, `jpg`, `jpeg`, `png`, up to 20 MiB each
145
+ and 10 per ping; `questions.ask()` takes `attachment_ids` too.
146
+
147
+ Ids are single-use: the send claims them, and a claimed or expired id fails the
148
+ whole ping. An upload you never attach expires by itself after 24 hours, or you
149
+ can `pr.attachments.delete(id)` it.
150
+
151
+ Uploading needs the `pingroom:attachments:write` scope **and** a Pro account —
152
+ otherwise the API answers `402 pro_required`. Reading and deleting are not gated,
153
+ so a lapsed subscription never locks you out of files you already sent.
154
+
67
155
  ## Listening for pings (real time)
68
156
 
69
157
  `listen()` is an async iterator that long-polls and advances the cursor for you — no poll-spam, no missed or duplicated pings. Stop it with an `AbortSignal`.
@@ -83,6 +171,26 @@ Or a single long-poll:
83
171
  const { notifications, cursor } = await pr.notifications.wait({ after: lastCursor, timeout: 20 });
84
172
  ```
85
173
 
174
+ Read one ping and wait for its acknowledgement through the same namespace:
175
+
176
+ ```ts
177
+ const ping = await pr.notifications.getNotification(notificationId);
178
+ console.log(ping.action_state?.status); // open | acked | expired
179
+
180
+ const result = await pr.notifications.waitForAcknowledgement(notificationId, { timeout: 30 });
181
+ switch (result.action_state.status) {
182
+ case 'acked':
183
+ console.log(`Acknowledged by ${result.action_state.acked_by?.name ?? 'someone'}`);
184
+ break;
185
+ case 'expired':
186
+ console.log('Nobody acknowledged before the deadline');
187
+ break;
188
+ case 'open':
189
+ // This bounded hold timed out; call waitForAcknowledgement again to continue.
190
+ break;
191
+ }
192
+ ```
193
+
86
194
  ## Human-in-the-loop approvals
87
195
 
88
196
  Ask the human you act for to decide, and block until they tap an answer on their phone.
@@ -141,6 +249,102 @@ await pr.questions.cancel(q.id); // withdraw a pending one
141
249
 
142
250
  > `pr.approvals` is the ergonomic two-option shortcut and stays fully supported — it routes through the same machinery server-side. Use it for plain yes/no gates; use `pr.questions` for everything richer.
143
251
 
252
+ ## Handoffs (Agent → one human)
253
+
254
+ A **handoff** is the direct, private version of the human-in-the-loop: it always targets **exactly one human** and is never readable by anyone else in the room. Reach for `pr.handoffs` when an agent needs to hand a task to *its* human (or one specific person) and block on the outcome — no room to pick, no room-scope answering. It's backed by the ack and question primitives, so a handoff is one of two `kind`s:
255
+
256
+ - **`ack`** — "acknowledge this." Resolves the moment the human taps acknowledge. States: `open | acked | expired`.
257
+ - **`question`** — "pick one." 2–4 tappable options. States: `pending | answered | expired | cancelled`.
258
+
259
+ Requires the `pingroom:handoffs:create` scope.
260
+
261
+ On first connect, explicitly activate the human's Agent Inbox. `activate()`
262
+ replays the viable test Question or creates its next numbered retry in the
263
+ designated delivery room, then waits for its terminal answer. It is deliberately
264
+ separate from `auth.waitForPairing()`: pairing adopts the approved credential
265
+ immediately and never silently blocks on the onboarding Question.
266
+
267
+ ```ts
268
+ import { PingRoomActivationIncompleteError } from '@pingroom/sdk';
269
+
270
+ const controller = new AbortController();
271
+ try {
272
+ const activation = await pr.inbox.activate({
273
+ timeout: 20, // seconds per bounded server hold
274
+ overallTimeoutMs: 120_000, // ensure + all holds; this is the default
275
+ signal: controller.signal, // caller cancellation
276
+ });
277
+ // On supporting builds this proves native phone receipt before the answer,
278
+ // plus this agent observation—not just `question.state === 'answered'`.
279
+ console.log(activation.activation_completed); // true
280
+ console.log(`Agent Inbox ready: ${activation.question.answer.value}`);
281
+ } catch (error) {
282
+ if (error instanceof PingRoomActivationIncompleteError) {
283
+ console.log(`Activation incomplete: ${error.reason}`);
284
+ }
285
+ throw error;
286
+ }
287
+ ```
288
+
289
+ Pending long-poll responses are observed internally and polled again. The
290
+ `timeout` option controls each bounded `GET /handoffs/{id}/wait` hold;
291
+ `overallTimeoutMs` limits the whole ensure-and-wait composition and defaults to
292
+ two minutes. Immediate observations are spaced by at least 2.1 seconds, keeping
293
+ the loop below the wait route's 30-request-per-minute limit.
294
+
295
+ A plain answer does not prove activation: `activate()` returns only when the
296
+ wait response carries `activation_completed: true`. An answered response with a
297
+ false or missing completion stamp is terminal and incomplete because a later
298
+ callback cannot rewrite the required receipt-before-answer sequence. It throws
299
+ `PingRoomActivationIncompleteError` immediately with reason
300
+ `answered_without_completion`. Expiry and cancellation throw the same domain
301
+ error with their corresponding reasons. Caller aborts reject with the signal's
302
+ reason. None of these paths revoke or change the client's active credential.
303
+ Network/API errors remain `PingRoomError`. Use
304
+ `pr.inbox.ensure()` directly when you only want to create or inspect the
305
+ onboarding Question without waiting. Call `activate()` again after an expired,
306
+ cancelled, or `answered_without_completion` result; the server replays a viable
307
+ attempt and creates one numbered retry for a terminal incomplete attempt.
308
+
309
+ ```ts
310
+ // Ask for an acknowledgement. Pass idempotencyKey so a retried create is a no-op.
311
+ const task = await pr.handoffs.requestAck({
312
+ prompt: 'Deploy 1.4.0 to prod is queued — ack to proceed.',
313
+ idempotencyKey: 'deploy-1.4.0', // reuse verbatim on network retries
314
+ });
315
+
316
+ // Block until the human acks (or it expires). Terminal states never throw.
317
+ const result = await pr.handoffs.waitForResult(task.id);
318
+ if (result.state === 'acked') {
319
+ console.log(`Acked by ${result.acked_by?.display_name} at ${result.acked_at}`);
320
+ }
321
+ ```
322
+
323
+ ```ts
324
+ // Ask a multi-option question. Bare strings normalize to { value, label: value }.
325
+ const q = await pr.handoffs.ask({
326
+ prompt: 'Ship or hold 1.4.0?',
327
+ options: ['deploy', 'hold'],
328
+ idempotencyKey: 'ship-1.4.0',
329
+ });
330
+
331
+ const answered = await pr.handoffs.waitForResult(q.id);
332
+ if (answered.state === 'answered') {
333
+ // A negative choice ('hold') is a SUCCESSFUL answered result — not an error.
334
+ console.log(`Human chose ${answered.answer?.value}`);
335
+ }
336
+ ```
337
+
338
+ `waitForResult` loops the bounded long-poll until a terminal state lands, so `expired`/`cancelled` and a negative answer all come back as normal results — nothing is thrown for the outcome. Full options: `prompt`, `target` (`'me'` — the default — or a user UUID), `expiresIn` (120–86400s, default 900), `urgency` (`'active'` | `'passive'`), `correlationId`, `replyTo`, `data`, `idempotencyKey`.
339
+
340
+ ```ts
341
+ await pr.handoffs.get(id); // current state (polling / audit)
342
+ await pr.handoffs.list({ state: 'open' }); // your open handoffs
343
+ await pr.handoffs.list({ state: 'all' }); // recent history (up to 200 per kind)
344
+ ```
345
+
346
+ Create/read map coded failures onto `PingRoomError` — branch on `error.code` (see [`HandoffErrorCode`](#errors)). The exported union includes target-policy, scope/quota, idempotency, feature, and capability failures; `capability_check_unavailable` is retryable. Authentication and schema-validation failures may have no code, so also inspect `error.status`.
347
+
144
348
  ## Incoming webhooks (no token needed)
145
349
 
146
350
  A room's incoming-webhook URL carries its own secret — ideal for CI/deploy hooks:
@@ -151,21 +355,138 @@ import { sendIncomingWebhook } from '@pingroom/sdk';
151
355
  await sendIncomingWebhook(process.env.PINGROOM_WEBHOOK_URL, {
152
356
  message: 'Build #42 passed',
153
357
  data: { commit: 'abc123' },
358
+ requires_ack: true,
359
+ ack_timeout_seconds: 300,
154
360
  }, { idempotencyKey: 'build-42' });
155
361
  ```
156
362
 
363
+ ### Live Activities from a webhook
364
+
365
+ See [Live Activities](#live-activities) below for the full picture. From a webhook, put `live_status` at the **top level** of the payload:
366
+
367
+ ```ts
368
+ await sendIncomingWebhook(process.env.PINGROOM_WEBHOOK_URL, {
369
+ message: 'Deploying v1.4.0',
370
+ correlation_id: 'deploy-42',
371
+ live_status: { state: 'running', template: 'progress', progress: 0.4 },
372
+ });
373
+ ```
374
+
375
+ > **Not inside `data`.** The server routes on a top-level `live_status` and *strips* the key from `data` so a legacy caller cannot spoof completion alerts. A nested `data.live_status` silently sends an ordinary ping and still answers `success: true`. (SDK versions before 0.3.1 dropped `live_status` from the request body entirely — upgrade.)
376
+
377
+ ## Live Activities
378
+
379
+ A **live-status stream** is one thing being tracked, keyed by your `correlation_id`. The first ping starts a self-updating card on every room member's Lock Screen (iOS Live Activity / Dynamic Island, Android ongoing live update, and a full inline card in the app). Later `running` pings move it **silently** — no new notification. The first `done`/`failed` sends one completion alert and ends it.
380
+
381
+ Protocol: <https://pingroom.io/liveactivities.md> (v1.2).
382
+
383
+ ### Two ways to send one
384
+
385
+ ```ts
386
+ import { PingRoom, liveStatus, sendIncomingWebhook } from '@pingroom/sdk';
387
+
388
+ // A. Agent credential — needs the `pingroom:live:write` scope.
389
+ const pr = new PingRoom({ token: process.env.PINGROOM_TOKEN });
390
+ await pr.live.push('AB12CD', liveStatus.progress('deploy-42', {
391
+ state: 'running',
392
+ message: 'Building image',
393
+ progress: 0.4,
394
+ }));
395
+
396
+ // B. A room's incoming webhook (Pro) — no token. The builder returns
397
+ // { correlation_id, live_status }, which is exactly the webhook body.
398
+ await sendIncomingWebhook(
399
+ process.env.PINGROOM_WEBHOOK_URL,
400
+ liveStatus.progress('deploy-42', {
401
+ state: 'running',
402
+ message: 'Building image',
403
+ progress: 0.4,
404
+ }),
405
+ );
406
+ ```
407
+
408
+ There is a third producer — a person driving a stream from the app composer over `POST /api/rooms/{code}/live` with their own JWT — but that is a Human JWT route and is not part of this agent SDK.
409
+
410
+ ### Templates
411
+
412
+ `template` is fixed at stream creation; a later ping cannot re-template a running card. Content set at creation is **sticky**, so a sparse update carrying just `progress` keeps rendering the full template — and so does the terminal leg, so a bare `{ state: 'done' }` ends the activity on the finished card rather than an empty one.
413
+
414
+ | Builder | Renders |
415
+ |---|---|
416
+ | `liveStatus.status` | Title, message, optional progress |
417
+ | `liveStatus.steps` | Segmented tracker; `steps` (2–8 labels) required on the first ping and immutable after |
418
+ | `liveStatus.progress` | Determinate bar with optional `eta_at` |
419
+ | `liveStatus.metrics` | Up to 3 `{label, value}` counters |
420
+ | `liveStatus.countdown` | Large live timer to `deadline_at` |
421
+ | `liveStatus.question` | `prompt` + up to 4 `{value, label}` options on the lock screen |
422
+ | `liveStatus.matchup` | `left` / `center` / `right` — a score or A-vs-B |
423
+
424
+ ```ts
425
+ await pr.live.push('AB12CD', liveStatus.steps('release-7', {
426
+ state: 'running',
427
+ steps: ['Build', 'Test', 'Deploy', 'Verify'],
428
+ current_step: 2,
429
+ message: 'Deploying to prod',
430
+ }));
431
+
432
+ await pr.live.push('AB12CD', liveStatus.matchup('game-3', {
433
+ state: 'running',
434
+ left: { label: 'ARS', value: '2' },
435
+ right: { label: 'CHE', value: '1' },
436
+ center: "68'",
437
+ }));
438
+ ```
439
+
440
+ ### Ending and reading back
441
+
442
+ ```ts
443
+ // Always end a stream. `done`/`failed` is never rate-limited or quota-blocked,
444
+ // so a card can't be metered into hanging open on a lock screen.
445
+ await pr.live.push('AB12CD', liveStatus.done('deploy-42', 'Shipped v1.4.0'));
446
+ // ...or liveStatus.failed('deploy-42', 'Rollback triggered')
447
+
448
+ // Reconcile after a restart instead of opening a duplicate.
449
+ // Returns null (not a throw) when there is no such stream in the last 24 hours.
450
+ const snap = await pr.live.get('AB12CD', 'deploy-42');
451
+ if (snap) console.log(snap.template, snap.current_step, snap.updated_at);
452
+ ```
453
+
454
+ `get()` returns **every** stored field (`state`, `progress`, `message`, `category`, `template`, `agent_id`, `accent_override`, `eta_at`, `deadline_at`, `metrics`, `prompt`, `options`, `left`, `right`, `center`, `steps`, `current_step`) plus `action_state` and `updated_at`. Fields you never set come back as `null`, not omitted.
455
+
456
+ ### Ownership and limits
457
+
458
+ A stream belongs to the credential that started it. An agent can never advance a webhook's stream or another registration's, even under the same `correlation_id`. Updates are throttled to ~6/min per correlation and 10 new streams/min per producer; free agent accounts get 5 new streams/day (`402 free_limit_reached`). Creation is the only charged leg.
459
+
460
+ ### `liveActivity.*` is not this
461
+
462
+ The exported `liveActivity.*` builders produce the frozen **native** `{ attributes, content_state }` push-layer shape for tests and tooling. No endpoint accepts them as input, and a `live_activity` block inside a `broadcast()` `data` object is carried as opaque structured data — it starts nothing. Use `liveStatus.*` to drive a real activity.
463
+
157
464
  ## Verifying outgoing webhooks
158
465
 
159
- When PingRoom POSTs an event to *your* server, verify it before trusting it. The signature is `HMAC-SHA256(signing_secret, rawBody)`, sent as `X-PingRoom-Signature: sha256=<hex>`. **Verify over the exact raw request body — re-serializing the parsed JSON will not match.**
466
+ When PingRoom POSTs an event to *your* server, verify it before trusting it. New deliveries carry two lower-case hex HMAC-SHA256 headers:
467
+
468
+ - `X-PingRoom-Signature-V2` signs `` `v2\n${timestamp}\n${deliveryId}\n${rawBody}` `` and binds `X-PingRoom-Delivery` as well as the timestamp and body.
469
+ - `X-PingRoom-Signature` keeps the legacy `` `${timestamp}.${rawBody}` `` contract so existing receivers continue to work.
470
+
471
+ `timestamp` is the Unix-seconds value in `X-PingRoom-Timestamp`, and `deliveryId` is the exact `X-PingRoom-Delivery` value. **Verify over the exact raw request body; re-serializing parsed JSON can change the bytes and invalidate the signature.** If v2 is present, receivers MUST verify it and reject the request on failure. The helper accepts legacy v1 only when v2 is absent. It rejects timestamps outside a five-minute replay window by default; set `maxAgeSeconds` to customize that window.
160
472
 
161
473
  ```ts
162
- import { verifyWebhookSignature, WEBHOOK_SIGNATURE_HEADER } from '@pingroom/sdk';
474
+ import {
475
+ verifyWebhookSignature,
476
+ WEBHOOK_SIGNATURE_HEADER,
477
+ WEBHOOK_SIGNATURE_V2_HEADER,
478
+ WEBHOOK_TIMESTAMP_HEADER,
479
+ WEBHOOK_DELIVERY_HEADER,
480
+ } from '@pingroom/sdk';
163
481
 
164
482
  // Express example — capture the raw body (e.g. express.raw())
165
483
  app.post('/pingroom', async (req, res) => {
166
484
  const ok = await verifyWebhookSignature({
167
485
  payload: req.body, // a Buffer/string of the RAW body
168
486
  signature: req.get(WEBHOOK_SIGNATURE_HEADER),
487
+ signatureV2: req.get(WEBHOOK_SIGNATURE_V2_HEADER),
488
+ timestamp: req.get(WEBHOOK_TIMESTAMP_HEADER),
489
+ deliveryId: req.get(WEBHOOK_DELIVERY_HEADER),
169
490
  secret: process.env.PINGROOM_SIGNING_SECRET,
170
491
  });
171
492
  if (!ok) return res.status(401).end();
@@ -176,13 +497,19 @@ app.post('/pingroom', async (req, res) => {
176
497
 
177
498
  ## MCP
178
499
 
179
- Drive the MCP endpoint directly (tools are scope-filtered server-side):
500
+ Drive the MCP endpoint directly with a credential you already hold. The helper
501
+ performs the initialize handshake automatically, and tools are scope-filtered
502
+ server-side:
180
503
 
181
504
  ```ts
182
505
  const { tools } = await pr.mcp.listTools();
183
- const result = await pr.mcp.callTool('broadcast', { room: 'ab12cd', message: 'hi' });
506
+ const result = await pr.mcp.callTool('broadcast', { invite_code: 'ab12cd', message: 'hi' });
184
507
  ```
185
508
 
509
+ `pr.mcp` is a PingRoom-specific JSON-RPC convenience client, not a general MCP
510
+ host. Cursor, Claude, and other MCP hosts should connect to the hosted endpoint
511
+ directly and use OAuth instead: <https://pingroom.io/connect-mcp.md>.
512
+
186
513
  ## Agent directory (public)
187
514
 
188
515
  No token required:
@@ -201,9 +528,9 @@ Every failure rejects with a `PingRoomError` carrying the HTTP `status` and the
201
528
  import { PingRoomError } from '@pingroom/sdk';
202
529
 
203
530
  try {
204
- await pr.agents.ping('agt_x', { message: 'hi' });
531
+ await pr.broadcast('ab12cd', { message: 'hi' });
205
532
  } catch (err) {
206
- if (err instanceof PingRoomError && err.code === 'cooldown') {
533
+ if (err instanceof PingRoomError && err.code === 'rate_limited') {
207
534
  await new Promise((r) => setTimeout(r, (err.retryAfter ?? 1) * 1000));
208
535
  }
209
536
  }
package/dist/client.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { HttpClient } from './http.js';
2
+ import type { LiveStatusPing, LiveStatusResult, LiveStatusSnapshot } from './liveStatus.js';
2
3
  import { McpClient } from './mcp.js';
3
- import type { AgentNotification, Approval, ApprovalInput, CreatePublicRoomInput, CreateRoomInput, Credential, ClaimCompleteParams, ClaimStartParams, DirectoryEntry, DirectoryListInput, DirectoryProfile, DirectPingResult, JoinRoomInput, ListNotificationsInput, ListenInput, ListQuestionsInput, Paginated, PingInput, PingResult, PingRoomOptions, Question, QuestionInput, QuickAction, RegisterParams, Room, RotateHandleResult, TriggerInput, UpdateQuickActionInput, WaitApprovalInput, WaitInput, WaitQuestionInput, WaitResult } from './types.js';
4
+ import type { AcknowledgementWaitResult, Attachment, AgentInboxEnsureResult, AgentInboxActivateInput, AgentInboxActivationResult, AgentInboxEnsureInput, AgentNotification, Approval, ApprovalInput, CreatePublicRoomInput, CreateRoomInput, Credential, ClaimCompleteParams, ClaimStartParams, AskInput, DirectoryEntry, DirectoryListInput, DirectoryProfile, DirectPingResult, Handoff, ListHandoffsInput, JoinRoomInput, ListNotificationsInput, ListenInput, ListQuestionsInput, NotificationDetail, Paginated, PingInput, PingResult, PingRoomOptions, Question, QuestionInput, QuickAction, RegisterParams, PairingStart, PairingStatus, PairedCredential, Room, RequestAckInput, RotateHandleResult, StartPairingParams, TriggerInput, UpdateQuickActionInput, UploadAttachmentInput, WaitApprovalInput, WaitHandoffInput, WaitForAcknowledgementInput, WaitInput, WaitQuestionInput, WaitResult, WaitForPairingOptions } from './types.js';
4
5
  /** Agent credential lifecycle (auth.md flow). */
5
6
  declare class AuthApi {
6
7
  private readonly http;
@@ -14,6 +15,23 @@ declare class AuthApi {
14
15
  }>;
15
16
  /** Complete the claim with the emailed OTP; returns an active credential + handle. */
16
17
  claimComplete(params: ClaimCompleteParams): Promise<Credential>;
18
+ /**
19
+ * Register a pending agent and create a short-lived link that opens the
20
+ * PingRoom app. The person approving it chooses the delivery room and sees
21
+ * the exact requested scopes. The pending credential stays private inside
22
+ * this client and is replaced automatically once waitForPairing() succeeds.
23
+ */
24
+ startPairing(params?: StartPairingParams): Promise<PairingStart>;
25
+ /** Read the current state of an app pairing using the pending credential. */
26
+ pairingStatus(options?: Pick<WaitForPairingOptions, 'signal'>): Promise<PairingStatus>;
27
+ /**
28
+ * Wait until the person approves in PingRoom, or the short-lived link
29
+ * expires. Transient network, 429, and 5xx failures are retried inside the
30
+ * server-provided lifetime. On success the client immediately starts using
31
+ * the active credential returned by the server.
32
+ */
33
+ waitForPairing(pairing: PairingStart, options?: WaitForPairingOptions): Promise<PairedCredential>;
34
+ private throwIfPairingAborted;
17
35
  /** Rotate to a fresh credential (same scopes/handle). */
18
36
  refresh(): Promise<Credential>;
19
37
  /** Revoke the current credential. */
@@ -40,6 +58,13 @@ declare class NotificationsApi {
40
58
  constructor(http: HttpClient);
41
59
  /** Paginated list (polling fallback). Prefer wait()/listen() for real-time. */
42
60
  list(input?: ListNotificationsInput): Promise<Paginated<AgentNotification>>;
61
+ /** Fetch one visible notification, including its current acknowledgement state. */
62
+ getNotification(notificationId: string): Promise<NotificationDetail>;
63
+ /**
64
+ * One bounded long-poll for an ack-required ping. The returned state may
65
+ * still be `open` when the hold timeout elapses; call again to keep waiting.
66
+ */
67
+ waitForAcknowledgement(notificationId: string, input?: WaitForAcknowledgementInput): Promise<AcknowledgementWaitResult>;
43
68
  /** One long-poll: resolves as soon as new pings land, or with an empty list at timeout. */
44
69
  wait(input?: WaitInput): Promise<WaitResult>;
45
70
  /**
@@ -53,7 +78,11 @@ declare class NotificationsApi {
53
78
  declare class AgentsApi {
54
79
  private readonly http;
55
80
  constructor(http: HttpClient);
56
- /** Direct agent-to-agent ping, addressed by the target's handle. */
81
+ /**
82
+ * @deprecated Retired server-side — always fails with 410
83
+ * `cross_account_ping_retired`. An agent can only reach the account that
84
+ * connected it; to reach anyone else, post into a room you both belong to.
85
+ */
57
86
  ping(handle: string, ping: PingInput): Promise<DirectPingResult>;
58
87
  }
59
88
  declare class ApprovalsApi {
@@ -97,6 +126,56 @@ declare class QuestionsApi {
97
126
  /** Withdraw a still-pending Question (409 if it already resolved). */
98
127
  cancel(questionId: string): Promise<Question>;
99
128
  }
129
+ /**
130
+ * The Handoff façade: the Agent → one-human loop. Always DIRECT (exactly one
131
+ * human) and PRIVATE (no other room member can read it). Backed by the ack and
132
+ * question primitives, so its {@link Handoff.state | state} space is their union
133
+ * — kept DISTINCT per {@link Handoff.kind | kind}. A negative answer to a
134
+ * question handoff (e.g. `hold`) is a SUCCESSFUL `answered` result, not an error.
135
+ *
136
+ * const task = await pr.handoffs.requestAck({ prompt, idempotencyKey });
137
+ * const result = await pr.handoffs.waitForResult(task.id);
138
+ *
139
+ * const q = await pr.handoffs.ask({ prompt, options: ['deploy', 'hold'], idempotencyKey });
140
+ */
141
+ declare class HandoffsApi {
142
+ private readonly http;
143
+ constructor(http: HttpClient);
144
+ /** Ask a human to acknowledge (an `ack` handoff). Resolves on the first acknowledgement. */
145
+ requestAck(input: RequestAckInput): Promise<Handoff>;
146
+ /**
147
+ * Ask a human a multi-option question (a `question` handoff). Resolves on the
148
+ * first tapped answer. Bare-string options normalize to `{ value, label }`.
149
+ */
150
+ ask(input: AskInput): Promise<Handoff>;
151
+ /** Fetch a handoff's current state. */
152
+ get(id: string): Promise<Handoff>;
153
+ /**
154
+ * Block until the handoff resolves (or expires/cancels), looping the bounded
155
+ * long-poll under the hood. `expired`/`cancelled` and a negative answer (e.g.
156
+ * `hold`) all surface as normal terminal results — nothing is thrown for them.
157
+ */
158
+ waitForResult(id: string, input?: WaitHandoffInput): Promise<Handoff>;
159
+ /** List the agent's open handoffs or bounded recent history. */
160
+ list(input?: ListHandoffsInput): Promise<Handoff[]>;
161
+ /** One bounded long-poll; returns the (possibly still-open) handoff. */
162
+ private wait;
163
+ private create;
164
+ }
165
+ /** Setup and activation of the bound human's Agent Inbox delivery room. */
166
+ declare class InboxApi {
167
+ private readonly http;
168
+ constructor(http: HttpClient);
169
+ /** Replay the viable activation attempt or create its next numbered retry. */
170
+ ensure(input?: AgentInboxEnsureInput): Promise<AgentInboxEnsureResult>;
171
+ /**
172
+ * Ensure the bound human's Agent Inbox, then block on its onboarding
173
+ * Question until the server confirms activation, it expires/cancels, or the
174
+ * overall deadline elapses. Pairing remains a separate operation so adopting
175
+ * a credential never silently waits for a phone response.
176
+ */
177
+ activate(input?: AgentInboxActivateInput): Promise<AgentInboxActivationResult>;
178
+ }
100
179
  declare class ProfileApi {
101
180
  private readonly http;
102
181
  constructor(http: HttpClient);
@@ -105,6 +184,51 @@ declare class ProfileApi {
105
184
  /** Rotate the agent's handle — the kill-switch for a leaked handle. */
106
185
  rotateHandle(): Promise<RotateHandleResult>;
107
186
  }
187
+ /**
188
+ * Live-status streams — a self-updating card on the room members' lock screen.
189
+ *
190
+ * Requires the `pingroom:live:write` scope. Free accounts get a small daily
191
+ * budget of NEW streams; updates and the terminal ping are never charged, so a
192
+ * card can never be quota-blocked into hanging open.
193
+ */
194
+ declare class LiveApi {
195
+ private readonly http;
196
+ constructor(http: HttpClient);
197
+ /**
198
+ * Send one stream ping — start, update, or end, chosen by `live_status.state`
199
+ * and whether the correlation already exists.
200
+ */
201
+ push(inviteCode: string, ping: LiveStatusPing): Promise<LiveStatusResult>;
202
+ /**
203
+ * Read back a stream this credential started, so a restarted producer can
204
+ * reconcile instead of opening a duplicate. Returns null when there is no
205
+ * such stream in the last 24 hours.
206
+ */
207
+ get(inviteCode: string, correlationId: string): Promise<LiveStatusSnapshot | null>;
208
+ }
209
+ /**
210
+ * Private files that a ping can carry.
211
+ *
212
+ * Upload first, then pass the returned ids to `broadcast()` / `questions.ask()`
213
+ * as `attachment_ids`. Bytes never travel over MCP or a JSON ping body. An
214
+ * upload that is never attached expires on its own after 24 hours.
215
+ *
216
+ * Uploading requires the bound account to hold Pro (the API answers 402
217
+ * `pro_required` otherwise). Reading and deleting are not gated.
218
+ */
219
+ declare class AttachmentsApi {
220
+ private readonly http;
221
+ constructor(http: HttpClient);
222
+ /** Upload one file and get back the id a send can claim. */
223
+ upload(input: UploadAttachmentInput): Promise<Attachment>;
224
+ /**
225
+ * Raw bytes for an attachment this credential is allowed to see. Returns the
226
+ * undecoded Response so a caller can stream it, or read text/arrayBuffer.
227
+ */
228
+ content(attachmentId: string, signal?: AbortSignal): Promise<Response>;
229
+ /** Discard an upload that has not been attached to a ping yet. */
230
+ delete(attachmentId: string): Promise<void>;
231
+ }
108
232
  /** Public, unauthenticated agent directory. */
109
233
  declare class DirectoryApi {
110
234
  private readonly http;
@@ -117,9 +241,13 @@ export declare class PingRoom {
117
241
  readonly rooms: RoomsApi;
118
242
  readonly actions: ActionsApi;
119
243
  readonly notifications: NotificationsApi;
244
+ readonly attachments: AttachmentsApi;
120
245
  readonly agents: AgentsApi;
121
246
  readonly approvals: ApprovalsApi;
122
247
  readonly questions: QuestionsApi;
248
+ readonly handoffs: HandoffsApi;
249
+ readonly inbox: InboxApi;
250
+ readonly live: LiveApi;
123
251
  readonly profile: ProfileApi;
124
252
  readonly directory: DirectoryApi;
125
253
  readonly mcp: McpClient;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}