@pingroom/sdk 0.1.0 → 0.3.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
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 and wait for the answer, 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, 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.
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.
@@ -20,12 +20,32 @@ import { PingRoom } from '@pingroom/sdk';
20
20
  const pr = new PingRoom({ token: process.env.PINGROOM_TOKEN });
21
21
 
22
22
  // Broadcast to a room you own
23
- await pr.broadcast('ab12cd', { message: 'Deploy shipped ✅', data: { version: '1.4.0' } });
23
+ await pr.broadcast('ab12cd', {
24
+ message: 'Deploy shipped ✅',
25
+ data: { version: '1.4.0' },
26
+ requires_ack: true,
27
+ ack_timeout_seconds: 300,
28
+ });
24
29
 
25
30
  // Ping another agent by handle
26
31
  await pr.agents.ping('agt_reviewer', { message: 'PR #42 ready', correlation_id: 'pr-42' });
27
32
  ```
28
33
 
34
+ ### Link pings
35
+
36
+ A ping becomes a tappable link button when its `data` carries the reserved
37
+ keys `url` (absolute http(s), ≤ 2048 chars) and optional `button_label`
38
+ (≤ 26 chars). The `linkPing()` helper builds and validates that fragment:
39
+
40
+ ```ts
41
+ import { PingRoom, linkPing } from '@pingroom/sdk';
42
+
43
+ await pr.broadcast('ab12cd', {
44
+ message: 'Build 512 ready',
45
+ data: { commit: 'abc123', ...linkPing({ url: 'https://ci.example.com/b/512', buttonLabel: 'Open build' }) },
46
+ });
47
+ ```
48
+
29
49
  > 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
50
 
31
51
  ## Authentication
@@ -57,7 +77,7 @@ Generic MCP clients (Cursor, Claude Desktop, Claude Code) should use the standar
57
77
  ```ts
58
78
  await pr.rooms.list();
59
79
  await pr.rooms.get('ab12cd');
60
- await pr.rooms.create({ name: 'Deploys', icon: 'rocket', color: '#e53d30' });
80
+ await pr.rooms.create({ name: 'Deploys', icon: 'rocket', color: '#e33122' });
61
81
  await pr.rooms.join({ invite_code: 'ab12cd' });
62
82
 
63
83
  await pr.actions.update('ab12cd', 1, { label: 'Approve', icon: 'check' });
@@ -83,6 +103,26 @@ Or a single long-poll:
83
103
  const { notifications, cursor } = await pr.notifications.wait({ after: lastCursor, timeout: 20 });
84
104
  ```
85
105
 
106
+ Read one ping and wait for its acknowledgement through the same namespace:
107
+
108
+ ```ts
109
+ const ping = await pr.notifications.getNotification(notificationId);
110
+ console.log(ping.action_state?.status); // open | acked | expired
111
+
112
+ const result = await pr.notifications.waitForAcknowledgement(notificationId, { timeout: 30 });
113
+ switch (result.action_state.status) {
114
+ case 'acked':
115
+ console.log(`Acknowledged by ${result.action_state.acked_by?.name ?? 'someone'}`);
116
+ break;
117
+ case 'expired':
118
+ console.log('Nobody acknowledged before the deadline');
119
+ break;
120
+ case 'open':
121
+ // This bounded hold timed out; call waitForAcknowledgement again to continue.
122
+ break;
123
+ }
124
+ ```
125
+
86
126
  ## Human-in-the-loop approvals
87
127
 
88
128
  Ask the human you act for to decide, and block until they tap an answer on their phone.
@@ -100,6 +140,103 @@ if (decided.decision === 'Ship it') {
100
140
  }
101
141
  ```
102
142
 
143
+ ## Human-in-the-loop questions
144
+
145
+ A **question** is the general form of an approval: 2–4 options (or a short typed answer), delivered as a push with tappable buttons, resolving to exactly one answer — first tap wins. Reach for `pr.questions` when you need more than yes/no, a typed reply, or want *anyone in the room* to answer.
146
+
147
+ ```ts
148
+ // Ask — omit `options` for a binary Approve/Deny; ttl defaults to 1h (30s–24h).
149
+ const q = await pr.questions.ask('ab12cd', {
150
+ prompt: 'Which environment should I deploy?',
151
+ options: [
152
+ { value: 'prod', label: 'Production', style: 'primary' },
153
+ { value: 'staging', label: 'Staging' },
154
+ { value: 'cancel', label: 'Cancel', style: 'danger' },
155
+ ],
156
+ responder_scope: 'room', // or 'direct' (defaults to your bound user)
157
+ ttl: 600,
158
+ correlation_id: 'deploy-1.4.0',
159
+ });
160
+
161
+ // Block until a human taps (or it expires / is cancelled)
162
+ const answered = await pr.questions.waitForAnswer(q.id);
163
+
164
+ switch (answered.state) {
165
+ case 'answered':
166
+ console.log(`${answered.answer.responder?.display_name} chose ${answered.answer.value}`);
167
+ // → answered.answer.value is 'prod' | 'staging' | 'cancel'
168
+ break;
169
+ case 'expired': /* nobody answered in time */ break;
170
+ case 'cancelled': /* the asker withdrew it */ break;
171
+ }
172
+ ```
173
+
174
+ The three outcomes are distinct: an **answered** question whose `answer.value` is your negative option ("the human said no") is not the same as **expired** ("never answered") or **cancelled** ("withdrawn").
175
+
176
+ ```ts
177
+ await pr.questions.get(q.id); // current state (polling / audit)
178
+ await pr.questions.list({ state: 'pending' }); // your open questions
179
+ await pr.questions.cancel(q.id); // withdraw a pending one
180
+ ```
181
+
182
+ > `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.
183
+
184
+ ## Handoffs (Agent → one human)
185
+
186
+ 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:
187
+
188
+ - **`ack`** — "acknowledge this." Resolves the moment the human taps acknowledge. States: `open | acked | expired`.
189
+ - **`question`** — "pick one." 2–4 tappable options. States: `pending | answered | expired | cancelled`.
190
+
191
+ Requires the `pingroom:handoffs:create` scope.
192
+
193
+ On first connect, activate the human's private Agent Inbox. This is idempotent:
194
+ it creates at most one inbox and one onboarding test Question.
195
+
196
+ ```ts
197
+ const onboarding = await pr.inbox.ensure();
198
+ console.log(onboarding.room.invite_code, onboarding.question.state);
199
+ ```
200
+
201
+ ```ts
202
+ // Ask for an acknowledgement. Pass idempotencyKey so a retried create is a no-op.
203
+ const task = await pr.handoffs.requestAck({
204
+ prompt: 'Deploy 1.4.0 to prod is queued — ack to proceed.',
205
+ idempotencyKey: 'deploy-1.4.0', // reuse verbatim on network retries
206
+ });
207
+
208
+ // Block until the human acks (or it expires). Terminal states never throw.
209
+ const result = await pr.handoffs.waitForResult(task.id);
210
+ if (result.state === 'acked') {
211
+ console.log(`Acked by ${result.acked_by?.display_name} at ${result.acked_at}`);
212
+ }
213
+ ```
214
+
215
+ ```ts
216
+ // Ask a multi-option question. Bare strings normalize to { value, label: value }.
217
+ const q = await pr.handoffs.ask({
218
+ prompt: 'Ship or hold 1.4.0?',
219
+ options: ['deploy', 'hold'],
220
+ idempotencyKey: 'ship-1.4.0',
221
+ });
222
+
223
+ const answered = await pr.handoffs.waitForResult(q.id);
224
+ if (answered.state === 'answered') {
225
+ // A negative choice ('hold') is a SUCCESSFUL answered result — not an error.
226
+ console.log(`Human chose ${answered.answer?.value}`);
227
+ }
228
+ ```
229
+
230
+ `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`.
231
+
232
+ ```ts
233
+ await pr.handoffs.get(id); // current state (polling / audit)
234
+ await pr.handoffs.list({ state: 'open' }); // your open handoffs
235
+ await pr.handoffs.list({ state: 'all' }); // recent history (up to 200 per kind)
236
+ ```
237
+
238
+ 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`.
239
+
103
240
  ## Incoming webhooks (no token needed)
104
241
 
105
242
  A room's incoming-webhook URL carries its own secret — ideal for CI/deploy hooks:
@@ -110,21 +247,138 @@ import { sendIncomingWebhook } from '@pingroom/sdk';
110
247
  await sendIncomingWebhook(process.env.PINGROOM_WEBHOOK_URL, {
111
248
  message: 'Build #42 passed',
112
249
  data: { commit: 'abc123' },
250
+ requires_ack: true,
251
+ ack_timeout_seconds: 300,
113
252
  }, { idempotencyKey: 'build-42' });
114
253
  ```
115
254
 
255
+ ### Live Activities from a webhook
256
+
257
+ See [Live Activities](#live-activities) below for the full picture. From a webhook, put `live_status` at the **top level** of the payload:
258
+
259
+ ```ts
260
+ await sendIncomingWebhook(process.env.PINGROOM_WEBHOOK_URL, {
261
+ message: 'Deploying v1.4.0',
262
+ correlation_id: 'deploy-42',
263
+ live_status: { state: 'running', template: 'progress', progress: 0.4 },
264
+ });
265
+ ```
266
+
267
+ > **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.)
268
+
269
+ ## Live Activities
270
+
271
+ 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.
272
+
273
+ Protocol: <https://pingroom.io/liveactivities.md> (v1.2).
274
+
275
+ ### Two ways to send one
276
+
277
+ ```ts
278
+ import { PingRoom, liveStatus, sendIncomingWebhook } from '@pingroom/sdk';
279
+
280
+ // A. Agent credential — needs the `pingroom:live:write` scope.
281
+ const pr = new PingRoom({ token: process.env.PINGROOM_TOKEN });
282
+ await pr.live.push('AB12CD', liveStatus.progress('deploy-42', {
283
+ state: 'running',
284
+ message: 'Building image',
285
+ progress: 0.4,
286
+ }));
287
+
288
+ // B. A room's incoming webhook (Pro) — no token. The builder returns
289
+ // { correlation_id, live_status }, which is exactly the webhook body.
290
+ await sendIncomingWebhook(
291
+ process.env.PINGROOM_WEBHOOK_URL,
292
+ liveStatus.progress('deploy-42', {
293
+ state: 'running',
294
+ message: 'Building image',
295
+ progress: 0.4,
296
+ }),
297
+ );
298
+ ```
299
+
300
+ 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.
301
+
302
+ ### Templates
303
+
304
+ `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.
305
+
306
+ | Builder | Renders |
307
+ |---|---|
308
+ | `liveStatus.status` | Title, message, optional progress |
309
+ | `liveStatus.steps` | Segmented tracker; `steps` (2–8 labels) required on the first ping and immutable after |
310
+ | `liveStatus.progress` | Determinate bar with optional `eta_at` |
311
+ | `liveStatus.metrics` | Up to 3 `{label, value}` counters |
312
+ | `liveStatus.countdown` | Large live timer to `deadline_at` |
313
+ | `liveStatus.question` | `prompt` + up to 4 `{value, label}` options on the lock screen |
314
+ | `liveStatus.matchup` | `left` / `center` / `right` — a score or A-vs-B |
315
+
316
+ ```ts
317
+ await pr.live.push('AB12CD', liveStatus.steps('release-7', {
318
+ state: 'running',
319
+ steps: ['Build', 'Test', 'Deploy', 'Verify'],
320
+ current_step: 2,
321
+ message: 'Deploying to prod',
322
+ }));
323
+
324
+ await pr.live.push('AB12CD', liveStatus.matchup('game-3', {
325
+ state: 'running',
326
+ left: { label: 'ARS', value: '2' },
327
+ right: { label: 'CHE', value: '1' },
328
+ center: "68'",
329
+ }));
330
+ ```
331
+
332
+ ### Ending and reading back
333
+
334
+ ```ts
335
+ // Always end a stream. `done`/`failed` is never rate-limited or quota-blocked,
336
+ // so a card can't be metered into hanging open on a lock screen.
337
+ await pr.live.push('AB12CD', liveStatus.done('deploy-42', 'Shipped v1.4.0'));
338
+ // ...or liveStatus.failed('deploy-42', 'Rollback triggered')
339
+
340
+ // Reconcile after a restart instead of opening a duplicate.
341
+ // Returns null (not a throw) when there is no such stream in the last 24 hours.
342
+ const snap = await pr.live.get('AB12CD', 'deploy-42');
343
+ if (snap) console.log(snap.template, snap.current_step, snap.updated_at);
344
+ ```
345
+
346
+ `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.
347
+
348
+ ### Ownership and limits
349
+
350
+ 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.
351
+
352
+ ### `liveActivity.*` is not this
353
+
354
+ 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.
355
+
116
356
  ## Verifying outgoing webhooks
117
357
 
118
- 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.**
358
+ When PingRoom POSTs an event to *your* server, verify it before trusting it. New deliveries carry two lower-case hex HMAC-SHA256 headers:
359
+
360
+ - `X-PingRoom-Signature-V2` signs `` `v2\n${timestamp}\n${deliveryId}\n${rawBody}` `` and binds `X-PingRoom-Delivery` as well as the timestamp and body.
361
+ - `X-PingRoom-Signature` keeps the legacy `` `${timestamp}.${rawBody}` `` contract so existing receivers continue to work.
362
+
363
+ `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.
119
364
 
120
365
  ```ts
121
- import { verifyWebhookSignature, WEBHOOK_SIGNATURE_HEADER } from '@pingroom/sdk';
366
+ import {
367
+ verifyWebhookSignature,
368
+ WEBHOOK_SIGNATURE_HEADER,
369
+ WEBHOOK_SIGNATURE_V2_HEADER,
370
+ WEBHOOK_TIMESTAMP_HEADER,
371
+ WEBHOOK_DELIVERY_HEADER,
372
+ } from '@pingroom/sdk';
122
373
 
123
374
  // Express example — capture the raw body (e.g. express.raw())
124
375
  app.post('/pingroom', async (req, res) => {
125
376
  const ok = await verifyWebhookSignature({
126
377
  payload: req.body, // a Buffer/string of the RAW body
127
378
  signature: req.get(WEBHOOK_SIGNATURE_HEADER),
379
+ signatureV2: req.get(WEBHOOK_SIGNATURE_V2_HEADER),
380
+ timestamp: req.get(WEBHOOK_TIMESTAMP_HEADER),
381
+ deliveryId: req.get(WEBHOOK_DELIVERY_HEADER),
128
382
  secret: process.env.PINGROOM_SIGNING_SECRET,
129
383
  });
130
384
  if (!ok) return res.status(401).end();
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, Paginated, PingInput, PingResult, PingRoomOptions, QuickAction, RegisterParams, Room, RotateHandleResult, TriggerInput, UpdateQuickActionInput, WaitApprovalInput, WaitInput, WaitResult } from './types.js';
4
+ import type { AcknowledgementWaitResult, AgentInboxEnsureResult, 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, Room, RequestAckInput, RotateHandleResult, TriggerInput, UpdateQuickActionInput, WaitApprovalInput, WaitHandoffInput, WaitForAcknowledgementInput, WaitInput, WaitQuestionInput, WaitResult } from './types.js';
4
5
  /** Agent credential lifecycle (auth.md flow). */
5
6
  declare class AuthApi {
6
7
  private readonly http;
@@ -40,6 +41,13 @@ declare class NotificationsApi {
40
41
  constructor(http: HttpClient);
41
42
  /** Paginated list (polling fallback). Prefer wait()/listen() for real-time. */
42
43
  list(input?: ListNotificationsInput): Promise<Paginated<AgentNotification>>;
44
+ /** Fetch one visible notification, including its current acknowledgement state. */
45
+ getNotification(notificationId: string): Promise<NotificationDetail>;
46
+ /**
47
+ * One bounded long-poll for an ack-required ping. The returned state may
48
+ * still be `open` when the hold timeout elapses; call again to keep waiting.
49
+ */
50
+ waitForAcknowledgement(notificationId: string, input?: WaitForAcknowledgementInput): Promise<AcknowledgementWaitResult>;
43
51
  /** One long-poll: resolves as soon as new pings land, or with an empty list at timeout. */
44
52
  wait(input?: WaitInput): Promise<WaitResult>;
45
53
  /**
@@ -53,7 +61,11 @@ declare class NotificationsApi {
53
61
  declare class AgentsApi {
54
62
  private readonly http;
55
63
  constructor(http: HttpClient);
56
- /** Direct agent-to-agent ping, addressed by the target's handle. */
64
+ /**
65
+ * @deprecated Retired server-side — always fails with 410
66
+ * `cross_account_ping_retired`. An agent can only reach the account that
67
+ * connected it; to reach anyone else, post into a room you both belong to.
68
+ */
57
69
  ping(handle: string, ping: PingInput): Promise<DirectPingResult>;
58
70
  }
59
71
  declare class ApprovalsApi {
@@ -69,6 +81,77 @@ declare class ApprovalsApi {
69
81
  signal?: AbortSignal;
70
82
  }): Promise<Approval>;
71
83
  }
84
+ /**
85
+ * The general human-in-the-loop primitive: ask a human a question with 2–4
86
+ * options (or a short typed answer) and block until they tap an answer. An
87
+ * {@link ApprovalsApi | approval} is the two-option case; reach for questions
88
+ * when you need more choices, a typed reply, or room-scope answering.
89
+ */
90
+ declare class QuestionsApi {
91
+ private readonly http;
92
+ constructor(http: HttpClient);
93
+ /** Ask a Question in a room; returns the pending Question (id + computed expiry). */
94
+ ask(inviteCode: string, input: QuestionInput): Promise<Question>;
95
+ /** Fetch a Question's current state. */
96
+ get(questionId: string): Promise<Question>;
97
+ /** One long-poll for the answer; returns the (possibly still-pending) Question. */
98
+ wait(questionId: string, input?: WaitQuestionInput): Promise<Question>;
99
+ /**
100
+ * Block until the human answers (or the question expires/cancels), looping
101
+ * wait() under the hood. Returns the resolved Question — inspect `state` and
102
+ * `answer.value` to branch.
103
+ */
104
+ waitForAnswer(questionId: string, input?: {
105
+ signal?: AbortSignal;
106
+ }): Promise<Question>;
107
+ /** List the asker's Questions, optionally filtered by state. */
108
+ list(input?: ListQuestionsInput): Promise<Question[]>;
109
+ /** Withdraw a still-pending Question (409 if it already resolved). */
110
+ cancel(questionId: string): Promise<Question>;
111
+ }
112
+ /**
113
+ * The Handoff façade: the Agent → one-human loop. Always DIRECT (exactly one
114
+ * human) and PRIVATE (no other room member can read it). Backed by the ack and
115
+ * question primitives, so its {@link Handoff.state | state} space is their union
116
+ * — kept DISTINCT per {@link Handoff.kind | kind}. A negative answer to a
117
+ * question handoff (e.g. `hold`) is a SUCCESSFUL `answered` result, not an error.
118
+ *
119
+ * const task = await pr.handoffs.requestAck({ prompt, idempotencyKey });
120
+ * const result = await pr.handoffs.waitForResult(task.id);
121
+ *
122
+ * const q = await pr.handoffs.ask({ prompt, options: ['deploy', 'hold'], idempotencyKey });
123
+ */
124
+ declare class HandoffsApi {
125
+ private readonly http;
126
+ constructor(http: HttpClient);
127
+ /** Ask a human to acknowledge (an `ack` handoff). Resolves on the first acknowledgement. */
128
+ requestAck(input: RequestAckInput): Promise<Handoff>;
129
+ /**
130
+ * Ask a human a multi-option question (a `question` handoff). Resolves on the
131
+ * first tapped answer. Bare-string options normalize to `{ value, label }`.
132
+ */
133
+ ask(input: AskInput): Promise<Handoff>;
134
+ /** Fetch a handoff's current state. */
135
+ get(id: string): Promise<Handoff>;
136
+ /**
137
+ * Block until the handoff resolves (or expires/cancels), looping the bounded
138
+ * long-poll under the hood. `expired`/`cancelled` and a negative answer (e.g.
139
+ * `hold`) all surface as normal terminal results — nothing is thrown for them.
140
+ */
141
+ waitForResult(id: string, input?: WaitHandoffInput): Promise<Handoff>;
142
+ /** List the agent's open handoffs or bounded recent history. */
143
+ list(input?: ListHandoffsInput): Promise<Handoff[]>;
144
+ /** One bounded long-poll; returns the (possibly still-open) handoff. */
145
+ private wait;
146
+ private create;
147
+ }
148
+ /** One-call activation of the bound human's private Agent Inbox. */
149
+ declare class InboxApi {
150
+ private readonly http;
151
+ constructor(http: HttpClient);
152
+ /** Idempotently create/resolve the inbox and deliver its one-time test Question. */
153
+ ensure(): Promise<AgentInboxEnsureResult>;
154
+ }
72
155
  declare class ProfileApi {
73
156
  private readonly http;
74
157
  constructor(http: HttpClient);
@@ -77,6 +160,28 @@ declare class ProfileApi {
77
160
  /** Rotate the agent's handle — the kill-switch for a leaked handle. */
78
161
  rotateHandle(): Promise<RotateHandleResult>;
79
162
  }
163
+ /**
164
+ * Live-status streams — a self-updating card on the room members' lock screen.
165
+ *
166
+ * Requires the `pingroom:live:write` scope. Free accounts get a small daily
167
+ * budget of NEW streams; updates and the terminal ping are never charged, so a
168
+ * card can never be quota-blocked into hanging open.
169
+ */
170
+ declare class LiveApi {
171
+ private readonly http;
172
+ constructor(http: HttpClient);
173
+ /**
174
+ * Send one stream ping — start, update, or end, chosen by `live_status.state`
175
+ * and whether the correlation already exists.
176
+ */
177
+ push(inviteCode: string, ping: LiveStatusPing): Promise<LiveStatusResult>;
178
+ /**
179
+ * Read back a stream this credential started, so a restarted producer can
180
+ * reconcile instead of opening a duplicate. Returns null when there is no
181
+ * such stream in the last 24 hours.
182
+ */
183
+ get(inviteCode: string, correlationId: string): Promise<LiveStatusSnapshot | null>;
184
+ }
80
185
  /** Public, unauthenticated agent directory. */
81
186
  declare class DirectoryApi {
82
187
  private readonly http;
@@ -91,6 +196,10 @@ export declare class PingRoom {
91
196
  readonly notifications: NotificationsApi;
92
197
  readonly agents: AgentsApi;
93
198
  readonly approvals: ApprovalsApi;
199
+ readonly questions: QuestionsApi;
200
+ readonly handoffs: HandoffsApi;
201
+ readonly inbox: InboxApi;
202
+ readonly live: LiveApi;
94
203
  readonly profile: ProfileApi;
95
204
  readonly directory: DirectoryApi;
96
205
  readonly mcp: McpClient;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EACV,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,sBAAsB,EACtB,WAAW,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,eAAe,EACf,WAAW,EACX,cAAc,EACd,IAAI,EACJ,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,iBAAiB,EACjB,SAAS,EACT,UAAU,EACX,MAAM,YAAY,CAAC;AAsCpB,iDAAiD;AACjD,cAAM,OAAO;IACC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,mGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAKrD,iFAAiF;IACjF,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAKtF,sFAAsF;IACtF,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAM/D,yDAAyD;IACzD,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IAI9B,qCAAqC;IACrC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAGxB;AAED,cAAM,QAAQ;IACA,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAIvB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAI1C;AAED,cAAM,UAAU;IACF,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAIhD,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IASrG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;CAMjG;AAED,cAAM,gBAAgB;IACR,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,+EAA+E;IAC/E,IAAI,CAAC,KAAK,GAAE,sBAA2B,GAAG,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAI/E,2FAA2F;IAC3F,IAAI,CAAC,KAAK,GAAE,SAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAQhD;;;;;OAKG;IACI,MAAM,CAAC,KAAK,GAAE,WAAgB,GAAG,cAAc,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,CAAC;CAoCtF;AAED,cAAM,SAAS;IACD,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,oEAAoE;IACpE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAKjE;AAED,cAAM,YAAY;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,yEAAyE;IACzE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQpE,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI1C,qFAAqF;IACrF,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,iBAAsB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ1E,6FAA6F;IACvF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAWnG;AAED,cAAM,UAAU;IACF,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,wDAAwD;IACxD,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK7C,uEAAuE;IACvE,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC;CAG5C;AAED,+CAA+C;AAC/C,cAAM,YAAY;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,CAAC,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAOxE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAG/C;AAID,qBAAa,QAAQ;IACnB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IAExB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;gBAEtB,OAAO,GAAE,eAAoB;IAsBzC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIpC,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB,0DAA0D;IAC1D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;CAMpE"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EACV,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,OAAO,EAGP,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,SAAS,EACT,SAAS,EACT,UAAU,EACV,eAAe,EACf,QAAQ,EACR,aAAa,EACb,WAAW,EACX,cAAc,EACd,IAAI,EACJ,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,2BAA2B,EAC3B,SAAS,EACT,iBAAiB,EACjB,UAAU,EACX,MAAM,YAAY,CAAC;AAsCpB,iDAAiD;AACjD,cAAM,OAAO;IACC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,mGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAKrD,iFAAiF;IACjF,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAKtF,sFAAsF;IACtF,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;IAM/D,yDAAyD;IACzD,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IAI9B,qCAAqC;IACrC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAGxB;AAED,cAAM,QAAQ;IACA,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAIvB,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAI1C;AAED,cAAM,UAAU;IACF,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAIhD,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IASrG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;CAMjG;AAED,cAAM,gBAAgB;IACR,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,+EAA+E;IAC/E,IAAI,CAAC,KAAK,GAAE,sBAA2B,GAAG,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAI/E,mFAAmF;IACnF,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAKpE;;;OAGG;IACH,sBAAsB,CACpB,cAAc,EAAE,MAAM,EACtB,KAAK,GAAE,2BAAgC,GACtC,OAAO,CAAC,yBAAyB,CAAC;IASrC,2FAA2F;IAC3F,IAAI,CAAC,KAAK,GAAE,SAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAQhD;;;;;OAKG;IACI,MAAM,CAAC,KAAK,GAAE,WAAgB,GAAG,cAAc,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,CAAC;CAoCtF;AAED,cAAM,SAAS;IACD,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAKjE;AAED,cAAM,YAAY;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,yEAAyE;IACzE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQpE,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI1C,qFAAqF;IACrF,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,iBAAsB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ1E,6FAA6F;IACvF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAWnG;AAED;;;;;GAKG;AACH,cAAM,YAAY;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,qFAAqF;IACrF,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQhE,wCAAwC;IACxC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI1C,mFAAmF;IACnF,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,iBAAsB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ1E;;;;OAIG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYhG,gEAAgE;IAC1D,IAAI,CAAC,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAO/D,sEAAsE;IACtE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAG9C;AAaD;;;;;;;;;;;GAWG;AACH,cAAM,WAAW;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,4FAA4F;IAC5F,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAMpD;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtC,uCAAuC;IACvC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKjC;;;;OAIG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,gBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC;IAa/E,gEAAgE;IAC1D,IAAI,CAAC,KAAK,GAAE,iBAAsB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAO7D,wEAAwE;IACxE,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,MAAM;CAqBf;AAED,oEAAoE;AACpE,cAAM,QAAQ;IACA,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,oFAAoF;IACpF,MAAM,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAG1C;AAED,cAAM,UAAU;IACF,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,wDAAwD;IACxD,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK7C,uEAAuE;IACvE,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC;CAG5C;AAED;;;;;;GAMG;AACH,cAAM,OAAO;IACC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;OAGG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAOzE;;;;OAIG;IACG,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;CAazF;AAED,+CAA+C;AAC/C,cAAM,YAAY;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,CAAC,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAOxE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAG/C;AAID,qBAAa,QAAQ;IACnB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IAExB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;gBAEtB,OAAO,GAAE,eAAoB;IA0BzC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIpC,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB,0DAA0D;IAC1D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;CAMpE"}