@pingroom/sdk 0.8.0 → 0.9.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.
Files changed (179) hide show
  1. package/README.md +153 -41
  2. package/dist/client.d.ts +14 -348
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +15 -1300
  5. package/dist/client.js.map +1 -1
  6. package/dist/errors.d.ts +32 -3
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +47 -1
  9. package/dist/errors.js.map +1 -1
  10. package/dist/http.d.ts.map +1 -1
  11. package/dist/http.js +7 -1
  12. package/dist/http.js.map +1 -1
  13. package/dist/index.d.ts +3 -5
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -2
  16. package/dist/index.js.map +1 -1
  17. package/dist/internal/guards.d.ts +7 -0
  18. package/dist/internal/guards.d.ts.map +1 -1
  19. package/dist/internal/guards.js +1 -1
  20. package/dist/internal/guards.js.map +1 -1
  21. package/dist/internal/request.d.ts +41 -0
  22. package/dist/internal/request.d.ts.map +1 -0
  23. package/dist/internal/request.js +78 -0
  24. package/dist/internal/request.js.map +1 -0
  25. package/dist/internal/retry.d.ts +9 -2
  26. package/dist/internal/retry.d.ts.map +1 -1
  27. package/dist/internal/retry.js +24 -6
  28. package/dist/internal/retry.js.map +1 -1
  29. package/dist/internal/validate.d.ts +13 -0
  30. package/dist/internal/validate.d.ts.map +1 -0
  31. package/dist/internal/validate.js +36 -0
  32. package/dist/internal/validate.js.map +1 -0
  33. package/dist/liveStatus.d.ts +106 -11
  34. package/dist/liveStatus.d.ts.map +1 -1
  35. package/dist/liveStatus.js +20 -3
  36. package/dist/liveStatus.js.map +1 -1
  37. package/dist/mcp.d.ts.map +1 -1
  38. package/dist/mcp.js +30 -8
  39. package/dist/mcp.js.map +1 -1
  40. package/dist/namespaces/actions.d.ts +50 -0
  41. package/dist/namespaces/actions.d.ts.map +1 -0
  42. package/dist/namespaces/actions.js +219 -0
  43. package/dist/namespaces/actions.js.map +1 -0
  44. package/dist/namespaces/approvals.d.ts +23 -0
  45. package/dist/namespaces/approvals.d.ts.map +1 -0
  46. package/dist/namespaces/approvals.js +78 -0
  47. package/dist/namespaces/approvals.js.map +1 -0
  48. package/dist/namespaces/attachments.d.ts +37 -0
  49. package/dist/namespaces/attachments.d.ts.map +1 -0
  50. package/dist/namespaces/attachments.js +91 -0
  51. package/dist/namespaces/attachments.js.map +1 -0
  52. package/dist/namespaces/auth.d.ts +58 -0
  53. package/dist/namespaces/auth.d.ts.map +1 -0
  54. package/dist/namespaces/auth.js +242 -0
  55. package/dist/namespaces/auth.js.map +1 -0
  56. package/dist/namespaces/directory.d.ts +10 -0
  57. package/dist/namespaces/directory.d.ts.map +1 -0
  58. package/dist/namespaces/directory.js +19 -0
  59. package/dist/namespaces/directory.js.map +1 -0
  60. package/dist/namespaces/handoffs.d.ts +47 -0
  61. package/dist/namespaces/handoffs.d.ts.map +1 -0
  62. package/dist/namespaces/handoffs.js +126 -0
  63. package/dist/namespaces/handoffs.js.map +1 -0
  64. package/dist/namespaces/inbox.d.ts +17 -0
  65. package/dist/namespaces/inbox.d.ts.map +1 -0
  66. package/dist/namespaces/inbox.js +192 -0
  67. package/dist/namespaces/inbox.js.map +1 -0
  68. package/dist/namespaces/live.d.ts +57 -0
  69. package/dist/namespaces/live.d.ts.map +1 -0
  70. package/dist/namespaces/live.js +116 -0
  71. package/dist/namespaces/live.js.map +1 -0
  72. package/dist/namespaces/notifications.d.ts +25 -0
  73. package/dist/namespaces/notifications.d.ts.map +1 -0
  74. package/dist/namespaces/notifications.js +128 -0
  75. package/dist/namespaces/notifications.js.map +1 -0
  76. package/dist/namespaces/profile.d.ts +19 -0
  77. package/dist/namespaces/profile.d.ts.map +1 -0
  78. package/dist/namespaces/profile.js +22 -0
  79. package/dist/namespaces/profile.js.map +1 -0
  80. package/dist/namespaces/questions.d.ts +41 -0
  81. package/dist/namespaces/questions.d.ts.map +1 -0
  82. package/dist/namespaces/questions.js +107 -0
  83. package/dist/namespaces/questions.js.map +1 -0
  84. package/dist/namespaces/rooms.d.ts +13 -0
  85. package/dist/namespaces/rooms.d.ts.map +1 -0
  86. package/dist/namespaces/rooms.js +41 -0
  87. package/dist/namespaces/rooms.js.map +1 -0
  88. package/dist/namespaces/webhooks.d.ts +11 -0
  89. package/dist/namespaces/webhooks.d.ts.map +1 -0
  90. package/dist/namespaces/webhooks.js +32 -0
  91. package/dist/namespaces/webhooks.js.map +1 -0
  92. package/dist/types/approvals.d.ts +62 -0
  93. package/dist/types/approvals.d.ts.map +1 -0
  94. package/dist/types/approvals.js +2 -0
  95. package/dist/types/approvals.js.map +1 -0
  96. package/dist/types/auth.d.ts +185 -0
  97. package/dist/types/auth.d.ts.map +1 -0
  98. package/dist/types/auth.js +2 -0
  99. package/dist/types/auth.js.map +1 -0
  100. package/dist/types/common.d.ts +43 -0
  101. package/dist/types/common.d.ts.map +1 -0
  102. package/dist/types/common.js +2 -0
  103. package/dist/types/common.js.map +1 -0
  104. package/dist/types/handoffs.d.ts +121 -0
  105. package/dist/types/handoffs.d.ts.map +1 -0
  106. package/dist/types/handoffs.js +2 -0
  107. package/dist/types/handoffs.js.map +1 -0
  108. package/dist/types/inbox.d.ts +51 -0
  109. package/dist/types/inbox.d.ts.map +1 -0
  110. package/dist/types/inbox.js +2 -0
  111. package/dist/types/inbox.js.map +1 -0
  112. package/dist/types/notifications.d.ts +376 -0
  113. package/dist/types/notifications.d.ts.map +1 -0
  114. package/dist/types/notifications.js +2 -0
  115. package/dist/types/notifications.js.map +1 -0
  116. package/dist/types/profile.d.ts +30 -0
  117. package/dist/types/profile.d.ts.map +1 -0
  118. package/dist/types/profile.js +3 -0
  119. package/dist/types/profile.js.map +1 -0
  120. package/dist/types/questions.d.ts +210 -0
  121. package/dist/types/questions.d.ts.map +1 -0
  122. package/dist/types/questions.js +2 -0
  123. package/dist/types/questions.js.map +1 -0
  124. package/dist/types/rooms.d.ts +163 -0
  125. package/dist/types/rooms.d.ts.map +1 -0
  126. package/dist/types/rooms.js +3 -0
  127. package/dist/types/rooms.js.map +1 -0
  128. package/dist/types.d.ts +9 -1199
  129. package/dist/types.d.ts.map +1 -1
  130. package/dist/version.d.ts +1 -1
  131. package/dist/version.js +1 -1
  132. package/dist/webhooks.d.ts +14 -4
  133. package/dist/webhooks.d.ts.map +1 -1
  134. package/dist/webhooks.js +30 -5
  135. package/dist/webhooks.js.map +1 -1
  136. package/package.json +4 -2
  137. package/src/client.ts +115 -0
  138. package/src/errors.ts +291 -0
  139. package/src/http.ts +225 -0
  140. package/src/index.ts +97 -0
  141. package/src/internal/async.ts +54 -0
  142. package/src/internal/guards.ts +129 -0
  143. package/src/internal/request.ts +97 -0
  144. package/src/internal/retry.ts +157 -0
  145. package/src/internal/url.ts +78 -0
  146. package/src/internal/validate.ts +42 -0
  147. package/src/linkPing.ts +56 -0
  148. package/src/liveStatus.ts +337 -0
  149. package/src/locationPing.ts +101 -0
  150. package/src/mcp.ts +161 -0
  151. package/src/namespaces/actions.ts +234 -0
  152. package/src/namespaces/approvals.ts +104 -0
  153. package/src/namespaces/attachments.ts +106 -0
  154. package/src/namespaces/auth.ts +284 -0
  155. package/src/namespaces/directory.ts +21 -0
  156. package/src/namespaces/handoffs.ts +160 -0
  157. package/src/namespaces/inbox.ts +253 -0
  158. package/src/namespaces/live.ts +134 -0
  159. package/src/namespaces/notifications.ts +152 -0
  160. package/src/namespaces/profile.ts +24 -0
  161. package/src/namespaces/questions.ts +133 -0
  162. package/src/namespaces/rooms.ts +53 -0
  163. package/src/namespaces/webhooks.ts +38 -0
  164. package/src/types/approvals.ts +67 -0
  165. package/src/types/auth.ts +202 -0
  166. package/src/types/common.ts +74 -0
  167. package/src/types/handoffs.ts +148 -0
  168. package/src/types/inbox.ts +64 -0
  169. package/src/types/notifications.ts +393 -0
  170. package/src/types/profile.ts +37 -0
  171. package/src/types/questions.ts +226 -0
  172. package/src/types/rooms.ts +169 -0
  173. package/src/types.ts +17 -0
  174. package/src/version.ts +2 -0
  175. package/src/webhooks.ts +373 -0
  176. package/dist/liveActivity.d.ts +0 -101
  177. package/dist/liveActivity.d.ts.map +0 -1
  178. package/dist/liveActivity.js +0 -83
  179. package/dist/liveActivity.js.map +0 -1
@@ -0,0 +1,148 @@
1
+ import type { JsonObject } from './common.js';
2
+ import type { QuestionResponder } from './questions.js';
3
+
4
+ // --- handoffs -------------------------------------------------------------
5
+ //
6
+ // A handoff is the Agent → one-human loop: DIRECT (exactly one human) and
7
+ // PRIVATE (no other room member can read it). It's a façade over the ack and
8
+ // question primitives, so its state space is the union of the two — kept
9
+ // DISTINCT per kind:
10
+ //
11
+ // ack → open | acked | expired
12
+ // question → pending | answered | expired | cancelled
13
+ //
14
+ // A negative answer to a question handoff (e.g. `hold`/`deny`) is a SUCCESSFUL
15
+ // `answered` state, not an error.
16
+
17
+ /** Which primitive backs a handoff: an acknowledgement or a multi-option question. */
18
+ export type HandoffKind = 'ack' | 'question';
19
+
20
+ /** The lifecycle states of an `ack` handoff. Terminal: `acked`, `expired`. */
21
+ export type HandoffAckState = 'open' | 'acked' | 'expired';
22
+
23
+ /** The lifecycle states of a `question` handoff. Terminal: `answered`, `expired`, `cancelled`. */
24
+ export type HandoffQuestionState = 'answered' | 'expired' | 'cancelled' | 'pending';
25
+
26
+ /** The full handoff state space (union of ack + question states). */
27
+ export type HandoffState = HandoffAckState | HandoffQuestionState;
28
+
29
+ /**
30
+ * Whether the ping was enqueued, remains pending dispatch, or exhausted its
31
+ * retry budget (`failed`). Only populated on create; null on reads.
32
+ */
33
+ export type HandoffDeliveryState = 'enqueued' | 'pending' | 'failed';
34
+
35
+ /** One option offered on a `question` handoff. A bare string is shorthand for `{ value, label: value }`. */
36
+ export interface HandoffOptionInput {
37
+ /** Stable machine token returned as the answer. */
38
+ value: string;
39
+ /** Human text on the button (defaults to `value`). */
40
+ label?: string;
41
+ /** Presentation hint: `primary` = affirmative, `danger` = cancel/negative. */
42
+ style?: 'primary' | 'danger' | 'default';
43
+ }
44
+
45
+ export type HandoffOptionSpec = string | HandoffOptionInput;
46
+
47
+ /** A resolved option on the handoff wire shape. */
48
+ export interface HandoffOption {
49
+ value: string;
50
+ label: string;
51
+ [key: string]: unknown;
52
+ }
53
+
54
+ /** The human who acknowledged an `ack` handoff (present once `state === 'acked'`). */
55
+ export interface HandoffAcker {
56
+ /** Null when the actor is redacted (reader-privacy). */
57
+ id: string | null;
58
+ display_name: string | null;
59
+ }
60
+
61
+ /** The resolution of a `question` handoff (present once `state === 'answered'`). */
62
+ export interface HandoffAnswer {
63
+ /** The chosen option's `value`, or null for a text-only answer. */
64
+ value: string | null;
65
+ /** The chosen option's `label`. */
66
+ label: string | null;
67
+ /** The typed answer, when the question invited one. */
68
+ text: string | null;
69
+ /** The authenticated human who answered. */
70
+ responder: QuestionResponder | null;
71
+ answered_at: string | null;
72
+ }
73
+
74
+ /**
75
+ * The public handoff wire shape — identical across create / get / wait / list.
76
+ * `options`/`answer` appear on question handoffs; `acked_by`/`acked_at` on ack
77
+ * handoffs.
78
+ */
79
+ export interface Handoff {
80
+ id: string;
81
+ kind: HandoffKind;
82
+ prompt: string;
83
+ state: HandoffState;
84
+ /** Delivery status, populated only on create; null on reads. */
85
+ delivery_state: HandoffDeliveryState | null;
86
+ correlation_id: string | null;
87
+ reply_to: string | null;
88
+ data: JsonObject | null;
89
+ expires_at: string | null;
90
+ created_at: string | null;
91
+ /** ack handoff: the human who acknowledged (once `state === 'acked'`). */
92
+ acked_by?: HandoffAcker | null;
93
+ /** ack handoff: when it was acknowledged. */
94
+ acked_at?: string | null;
95
+ /** question handoff: the offered options. */
96
+ options?: HandoffOption[];
97
+ /** question handoff: the resolution (once `state === 'answered'`). */
98
+ answer?: HandoffAnswer | null;
99
+ /**
100
+ * Question handoffs used for Agent Inbox onboarding expose whether the
101
+ * server recorded the exact delivery + answer + agent-observation event.
102
+ */
103
+ activation_completed?: boolean;
104
+ [key: string]: unknown;
105
+ }
106
+
107
+ /** Ask a human to acknowledge (the ack handoff). Resolves on the first acknowledgement. */
108
+ export interface RequestAckInput {
109
+ /** What the human reads (required, ≤ 500 chars). */
110
+ prompt: string;
111
+ /**
112
+ * Who to reach: `me`, the bound human (the default). The bound human's own
113
+ * user id is accepted as an alias; any other id is refused with 403
114
+ * `target_not_permitted`.
115
+ */
116
+ target?: string;
117
+ /** Seconds until expiry. Clamped server-side to 120–86400; default 900. */
118
+ expiresIn?: number;
119
+ /** Interruption level — `active` (default) or `passive`. */
120
+ urgency?: 'active' | 'passive';
121
+ correlationId?: string;
122
+ replyTo?: string;
123
+ data?: JsonObject;
124
+ /**
125
+ * Idempotency key — same key + same body replays the original resource; a
126
+ * different body for the same key returns a 409 `idempotency_conflict`.
127
+ * Reuse it verbatim on network retries. 1–255 printable ASCII characters
128
+ * (spaces allowed); an empty string sends no key.
129
+ */
130
+ idempotencyKey?: string;
131
+ }
132
+
133
+ /** Ask a human a multi-option question (the question handoff). Resolves on the first tapped answer. */
134
+ export interface AskInput extends RequestAckInput {
135
+ /** 2–4 options. A bare string is shorthand for `{ value, label: value }`. */
136
+ options: HandoffOptionSpec[];
137
+ }
138
+
139
+ export interface WaitHandoffInput {
140
+ /** Seconds the server holds the connection (server caps at 25s; default 20). */
141
+ timeout?: number;
142
+ signal?: AbortSignal;
143
+ }
144
+
145
+ export interface ListHandoffsInput {
146
+ /** `open` for unresolved work, or `all` for recent history (up to 200 per kind). */
147
+ state?: 'open' | 'all';
148
+ }
@@ -0,0 +1,64 @@
1
+ import type {
2
+ HandoffQuestionState,
3
+ HandoffOption,
4
+ HandoffAnswer,
5
+ Handoff,
6
+ WaitHandoffInput,
7
+ } from './handoffs.js';
8
+
9
+ // --- Agent Inbox ---------------------------------------------------------
10
+
11
+ /** Idempotent first-connect result from POST /api/agent/inbox/ensure. */
12
+ export interface AgentInboxEnsureResult {
13
+ onboarded: true;
14
+ replayed: boolean;
15
+ room: {
16
+ id: string;
17
+ name: string;
18
+ invite_code: string;
19
+ is_agent_inbox: boolean;
20
+ };
21
+ question: {
22
+ id: string;
23
+ kind: 'question';
24
+ prompt: string;
25
+ options: HandoffOption[];
26
+ state: HandoffQuestionState;
27
+ expires_at: string | null;
28
+ created_at: string | null;
29
+ };
30
+ }
31
+
32
+ /** Cancellation for the idempotent Agent Inbox ensure request. */
33
+ export interface AgentInboxEnsureInput {
34
+ signal?: AbortSignal;
35
+ }
36
+
37
+ /**
38
+ * Controls the activation handoff wait. `timeout` is the duration of each
39
+ * bounded server hold; a still-pending response is polled again until the
40
+ * server confirms activation, the Question expires/cancels, the overall
41
+ * deadline elapses, or `signal` is aborted.
42
+ */
43
+ export interface AgentInboxActivateInput extends WaitHandoffInput {
44
+ /**
45
+ * Wall-clock limit for ensure + every pending long-poll combined. Defaults
46
+ * to 120000 (two minutes). Distinct from `timeout`, which controls one hold.
47
+ */
48
+ overallTimeoutMs?: number;
49
+ }
50
+
51
+ /** The terminal question returned by a completed Agent Inbox activation. */
52
+ export type AgentInboxActivationQuestion =
53
+ Handoff & {
54
+ kind: 'question';
55
+ state: 'answered';
56
+ answer: HandoffAnswer;
57
+ activation_completed: true;
58
+ };
59
+
60
+ /** `ensure` metadata plus the terminal, fully resolved onboarding handoff. */
61
+ export type AgentInboxActivationResult = Omit<AgentInboxEnsureResult, 'question'> & {
62
+ activation_completed: true;
63
+ question: AgentInboxActivationQuestion;
64
+ };
@@ -0,0 +1,393 @@
1
+ import type { JsonObject } from './common.js';
2
+ import type { Room } from './rooms.js';
3
+ import type { Question } from './questions.js';
4
+
5
+ // --- pings / notifications ------------------------------------------------
6
+
7
+ /** A visible Ping — used for broadcasts and the retired direct-ping compatibility method. */
8
+ export interface PingInput {
9
+ /** Visible body (≤ 120 characters in private rooms; ≤ 160 in public rooms). */
10
+ message: string;
11
+ /** Optional visible title (≤ 40 characters). */
12
+ title?: string;
13
+ action_number?: number;
14
+ action_icon?: string;
15
+ /**
16
+ * Structured data echoed on every read surface (<= 25 keys / 8KB).
17
+ * Reserved keys `url` + `button_label` make the ping a tappable link —
18
+ * see the `linkPing()` helper. Reserved `location` carries finite numeric
19
+ * latitude/longitude plus an optional label/address — see `locationPing()`.
20
+ */
21
+ data?: JsonObject;
22
+ correlation_id?: string;
23
+ reply_to?: string;
24
+ /**
25
+ * Ordered ids from `attachments.upload()` (max 4). Ids are single-use: the
26
+ * send claims them, and an already-claimed or expired id fails the whole
27
+ * ping. File bytes never ride this JSON body.
28
+ */
29
+ attachment_ids?: string[];
30
+ /**
31
+ * Open the acknowledgement lifecycle for this ping. The ack_mode rule
32
+ * determines when it resolves, and recipients see a lock-screen card with an Acknowledge button.
33
+ *
34
+ * Does NOT raise the delivery priority — that is {@link PingInput.is_urgent},
35
+ * and the two are independent. They were one flag, which meant asking for a
36
+ * ping that cuts through Focus also demanded that somebody acknowledge it.
37
+ */
38
+ requires_ack?: boolean;
39
+ /** Confirmation rule; defaults to any. all waits for every original eligible recipient. */
40
+ ack_mode?: AckMode;
41
+ /**
42
+ * Deliver time-sensitive so the ping breaks through Focus / Do Not Disturb
43
+ * and reaches members who muted the room or you (a block still wins).
44
+ * Delivery priority only: no acknowledgement, no Live Activity, nothing asked
45
+ * of the recipient. Set alongside `requires_ack` for an acknowledgement that
46
+ * also cuts through Focus.
47
+ */
48
+ is_urgent?: boolean;
49
+ /**
50
+ * Optional acknowledgement deadline in seconds (60–86400). Omit/null for no
51
+ * deadline. Validated on every send, so an out-of-range value is a 422 even
52
+ * without `requires_ack`; it takes effect only when the ping requires
53
+ * acknowledgement.
54
+ */
55
+ ack_timeout_seconds?: number | null;
56
+ /**
57
+ * Sent as the `Idempotency-Key` header (1–255 printable ASCII characters,
58
+ * spaces allowed), never in the body.
59
+ * Reuse the same key when retrying after a timeout or network error: the
60
+ * server replays the first result instead of sending the ping again.
61
+ */
62
+ idempotencyKey?: string;
63
+ }
64
+
65
+ /**
66
+ * Safe attachment metadata. Storage keys, checksums, owner ids, and permanent
67
+ * URLs are deliberately not part of this contract — read the bytes through
68
+ * `attachments.content()` with the credential that can see the ping.
69
+ */
70
+ export interface Attachment {
71
+ id: string;
72
+ filename: string;
73
+ mime_type: string;
74
+ size_bytes: number;
75
+ created_at: string;
76
+ }
77
+
78
+ /** The file types the attachment endpoint accepts. */
79
+ /** One row of a `.zip` attachment's listing. */
80
+ export interface ZipManifestEntry {
81
+ name: string;
82
+ /** Null when the archive declared no size, or declared a ZIP64 sentinel. */
83
+ size_bytes: number | null;
84
+ is_directory: boolean;
85
+ }
86
+
87
+ /**
88
+ * A `.zip` attachment's contents as recorded at upload. Bounded server-side, so
89
+ * `entries` may be shorter than `total_entries`.
90
+ */
91
+ export interface ZipManifest {
92
+ entries: ZipManifestEntry[];
93
+ total_entries: number;
94
+ truncated: boolean;
95
+ /** Sum of the sizes the archive DECLARES. Nothing verifies or expands them. */
96
+ total_uncompressed_bytes: number;
97
+ }
98
+
99
+ export type AttachmentExtension =
100
+ | 'md'
101
+ | 'pdf'
102
+ | 'html'
103
+ | 'txt'
104
+ | 'jpg'
105
+ | 'jpeg'
106
+ | 'png'
107
+ | 'zip';
108
+
109
+ export interface UploadAttachmentInput {
110
+ /** File bytes (1 byte–5 MiB). A Blob/File, a Uint8Array, or a UTF-8 string. */
111
+ content: Blob | Uint8Array | string;
112
+ /** Display filename. Its extension must be one of AttachmentExtension. */
113
+ filename: string;
114
+ /** Overrides the type sent in the multipart part. Usually inferred is fine. */
115
+ contentType?: string;
116
+ signal?: AbortSignal;
117
+ }
118
+
119
+ export interface TriggerInput {
120
+ /**
121
+ * Only the sources a device legitimately originates; `webhook`/`system` are
122
+ * stamped server-side and are rejected here. `manual` (the default) presses
123
+ * the slot.
124
+ *
125
+ * `location` is the device-geofence source, not a press modifier. It is for
126
+ * the ROOM OWNER only and sends the owner's Location Ping (an "Arrived at
127
+ * {place}" or "Left {place}" Ping to every member, with no action number)
128
+ * INSTEAD of the quick action in the slot. A caller who is not the room owner
129
+ * gets `403 location_trigger_owner_only`; a room with no enabled location
130
+ * trigger (or none that listens for the reported crossing) gets
131
+ * `409 location_trigger_inactive`; the operator's Location Pings switch being
132
+ * off gets `403 feature_disabled`. `is_urgent` and `requires_ack` still apply
133
+ * to it. To press a Quick Ping leave this unset.
134
+ */
135
+ trigger_source?: 'manual' | 'location';
136
+ /**
137
+ * Deliver this one press time-sensitive so it breaks through Focus / Do Not
138
+ * Disturb and reaches members who muted the room or you (a block still
139
+ * wins). Send-time only — the action's saved configuration is untouched,
140
+ * and quick actions have no stored urgency policy to override. Never implies
141
+ * `requires_ack`, which is the action's own saved acknowledgement policy.
142
+ */
143
+ is_urgent?: boolean;
144
+ /**
145
+ * One-shot acknowledgement modifier for this press only. It is OR-ed with the
146
+ * action's stored policy, so it can only ADD an ack requirement — passing
147
+ * `false` never removes one from an action already configured to require it.
148
+ * The action's saved configuration is untouched either way.
149
+ */
150
+ requires_ack?: boolean;
151
+ /** Applies to this press only, when its effective acknowledgement policy is enabled. */
152
+ ack_mode?: AckMode;
153
+ /**
154
+ * The detail this press carries when the action has an `input_type`. Only
155
+ * `location` and `url` are accepted on a trigger — no `button_label`; build
156
+ * the fragments with `locationPing()` / `linkPing()` or pass them directly.
157
+ */
158
+ data?: TriggerData;
159
+ /**
160
+ * Ids from `attachments.upload()`, at most 4. Required when `input_type` is
161
+ * `file`, `photo` (jpg/png only) or `pdf` (pdf only); a mismatched type is
162
+ * `422 quick_action_input_type`.
163
+ */
164
+ attachment_ids?: string[];
165
+ /**
166
+ * The action's `id` as read from `actions.list()`. When that slot has since
167
+ * moved pages the press is refused with `409 quick_action_layout_changed`
168
+ * rather than firing the wrong Ping.
169
+ */
170
+ quick_action_id?: string;
171
+ /**
172
+ * Sent as the `Idempotency-Key` header (1–255 printable ASCII characters,
173
+ * spaces allowed), never in the body.
174
+ * Reuse the same key when retrying after a timeout or network error: the
175
+ * server replays the first result instead of firing the press again.
176
+ */
177
+ idempotencyKey?: string;
178
+ }
179
+
180
+ /** The closed `data` object a quick-action press may carry. */
181
+ export interface TriggerData {
182
+ location?: TriggerLocation;
183
+ /** Absolute http(s) URL, at most 2048 characters. */
184
+ url?: string;
185
+ }
186
+
187
+ /** Same shape as `LocationPingLocation`; declared here to keep types.ts import-free. */
188
+ export interface TriggerLocation {
189
+ latitude: number;
190
+ longitude: number;
191
+ label?: string;
192
+ address?: string;
193
+ }
194
+
195
+ export type AckMode = 'any' | 'all';
196
+
197
+ /** The server-authoritative acknowledgement lifecycle attached to a ping. */
198
+ export interface ActionState {
199
+ /** Omitted by older servers and by the default any mode. */
200
+ mode?: AckMode;
201
+ confirmed_count?: number;
202
+ required_count?: number;
203
+ /** REST only; public rooms redact identities and MCP omits them. */
204
+ confirmed_user_ids?: string[];
205
+ status: 'open' | 'acked' | 'expired' | null;
206
+ requires_ack: boolean;
207
+ acked_by: {
208
+ id: string | null;
209
+ name: string | null;
210
+ profile_image: string | null;
211
+ } | null;
212
+ acked_at: string | null;
213
+ expires_at: string | null;
214
+ /** Set when the confirmation window was closed early. */
215
+ ended_at?: string | null;
216
+ /** Viewer-specific human mutation eligibility; agent read paths return false. */
217
+ can_ack?: boolean;
218
+ /** Recipients who dismissed the card without confirming. Never counts as a confirmation. */
219
+ dismissed_count?: number;
220
+ /** REST only; public rooms redact identities and MCP omits them. */
221
+ dismissed_user_ids?: string[];
222
+ /** Viewer-scoped: when the reading viewer dismissed it. */
223
+ dismissed_at?: string | null;
224
+ }
225
+
226
+ /**
227
+ * Who cannot show Confirm for this ping. A recipient with no capable device
228
+ * gets a plain alert, so their confirmation may never come. `checked: false`
229
+ * means nothing was checked (public rooms, rooms over 100 recipients, a failed
230
+ * lookup), not that everyone is reachable. Absent unless `requires_ack`.
231
+ */
232
+ export type ConfirmationReach =
233
+ | { checked: true; unavailable_user_ids: string[]; unavailable_count: number }
234
+ | { checked: false };
235
+
236
+ export interface PingResult {
237
+ id: string;
238
+ message: string;
239
+ action_number: number | null;
240
+ action_icon: string | null;
241
+ trigger_source: string | null;
242
+ data?: JsonObject | null;
243
+ correlation_id?: string | null;
244
+ reply_to?: string | null;
245
+ attachments?: Attachment[];
246
+ action_state?: ActionState | null;
247
+ created_at: string;
248
+ recipient_count?: number;
249
+ muted_count?: number;
250
+ confirmation_reach?: ConfirmationReach;
251
+ [key: string]: unknown;
252
+ }
253
+
254
+ /**
255
+ * The named fields of a listened/listed notification, without the index
256
+ * signature. `Omit` collapses a type carrying a string index signature down to
257
+ * that signature alone, so `NotificationDetail` derives from this rather than
258
+ * from `AgentNotification` — otherwise every named field would widen to
259
+ * `unknown`.
260
+ */
261
+ interface AgentNotificationFields {
262
+ id: string;
263
+ message: string;
264
+ action_number: number | null;
265
+ action_icon: string | null;
266
+ trigger_source: string | null;
267
+ /**
268
+ * True when this ping is an agent handoff (a request to take over a
269
+ * conversation) rather than an ordinary ping.
270
+ */
271
+ is_handoff?: boolean;
272
+ data?: JsonObject | null;
273
+ correlation_id?: string | null;
274
+ reply_to?: string | null;
275
+ attachments?: Attachment[];
276
+ action_state?: ActionState | null;
277
+ /** Confirmation rule the ping was sent with; absent on older rows. */
278
+ ack_mode?: AckMode;
279
+ /**
280
+ * Delivered without sound/haptics (the sender's silent mode at send time).
281
+ * `null` on rows written before 2026-09-16; not surfaced through MCP.
282
+ */
283
+ is_silent?: boolean | null;
284
+ /**
285
+ * The embedded Question when this ping carries one, else null. Same wire
286
+ * shape as the dedicated Question read paths.
287
+ */
288
+ question?: Question | null;
289
+ created_at: string;
290
+ room?: { code: string; name: string; icon: string | null; color: string | null };
291
+ sender?: { id: string; name: string | null };
292
+ /**
293
+ * Present when a connected agent (MCP / CLI credential) originated the
294
+ * ping: the agent registration's display identity. Null/absent on human,
295
+ * webhook, and trigger rows.
296
+ */
297
+ origin_agent?: {
298
+ id?: string;
299
+ handle: string | null;
300
+ display_name: string | null;
301
+ avatar_url?: string | null;
302
+ } | null;
303
+ }
304
+
305
+ export interface AgentNotification extends AgentNotificationFields {
306
+ [key: string]: unknown;
307
+ }
308
+
309
+ /** Full notification record returned by the single-notification read path. */
310
+ export type NotificationDetail = Omit<AgentNotificationFields, 'room'> & {
311
+ room_id?: string;
312
+ sender_id?: string;
313
+ type?: string;
314
+ requires_ack?: boolean;
315
+ /** Whether this ping was delivered time-sensitive. Independent of `requires_ack`. */
316
+ is_urgent?: boolean;
317
+ action_state: ActionState | null;
318
+ room?: Room;
319
+ [key: string]: unknown;
320
+ };
321
+
322
+ /** History keeps the full room and adds the listener-compatible `room.code`. */
323
+ export interface NotificationHistoryEntry extends AgentNotification {
324
+ room?: Room & { code: string };
325
+ }
326
+
327
+ export interface ListNotificationsInput {
328
+ /** `received` or `sent`; the server refuses any other value. */
329
+ type?: 'received' | 'sent';
330
+ room_id?: string;
331
+ date?: string;
332
+ /** Page size (1–25). Omit to retain the server's legacy default. */
333
+ limit?: number;
334
+ /** Page number, starting at 1. */
335
+ page?: number;
336
+ /** @deprecated Use limit. Sent as limit when limit is absent. */
337
+ notifications_per_page?: number;
338
+ }
339
+
340
+ export interface WaitInput {
341
+ /** Cursor (a notification id). Omit to start from the current head. */
342
+ after?: string;
343
+ /** Seconds to hold the connection (0–30, default 20). */
344
+ timeout?: number;
345
+ /** Max notifications to return (1–100, default 50). */
346
+ limit?: number;
347
+ signal?: AbortSignal;
348
+ }
349
+
350
+ export interface WaitResult {
351
+ notifications: AgentNotification[];
352
+ cursor: string;
353
+ }
354
+
355
+ export interface WaitForAcknowledgementInput {
356
+ /** Seconds to hold the connection (0–30, default 20). */
357
+ timeout?: number;
358
+ signal?: AbortSignal;
359
+ }
360
+
361
+ export interface AcknowledgementWaitResult {
362
+ id: string;
363
+ action_state: ActionState;
364
+ }
365
+
366
+ export interface ListenInput {
367
+ /** Cursor (a notification id). Omit to start from the current head. */
368
+ after?: string;
369
+ /**
370
+ * Seconds each poll holds the connection (0–30, default 20). Polls start at
371
+ * least 2.1 s apart after an empty answer, so `0` (or a proxy that answers
372
+ * early) cannot use up the wait route's 30-per-minute limit, which is shared
373
+ * with the human's other listeners.
374
+ */
375
+ timeout?: number;
376
+ /** Max notifications per poll (1–100, default 50). */
377
+ limit?: number;
378
+ signal?: AbortSignal;
379
+ /**
380
+ * Called with each transient error (network failure, request timeout, 429,
381
+ * 5xx) before the loop backs off and retries; transient errors are retried
382
+ * whether or not this is set. Any other error (401, 403, 422, …) ends the
383
+ * iterator by throwing, so a revoked credential never retries forever.
384
+ */
385
+ onError?: (err: unknown) => void;
386
+ /**
387
+ * First backoff after a transient error, in ms (default 1000, minimum 250:
388
+ * a smaller value is raised to 250, so a persistent failure never retries in
389
+ * a tight loop against the shared wait limit). It doubles on each consecutive
390
+ * failure up to 30 s; a server `retryAfter` replaces it.
391
+ */
392
+ backoffMs?: number;
393
+ }
@@ -0,0 +1,37 @@
1
+ // --- profile --------------------------------------------------------------
2
+
3
+ export interface RotateHandleResult {
4
+ handle: string;
5
+ [key: string]: unknown;
6
+ }
7
+
8
+ // --- directory ------------------------------------------------------------
9
+
10
+ export type PingPolicy = 'open' | 'members' | 'closed' | (string & {});
11
+ export type ActivityBucket = 'active_now' | 'today' | 'this_week' | 'quiet' | (string & {});
12
+
13
+ export interface DirectoryEntry {
14
+ handle: string;
15
+ display_name: string;
16
+ tagline: string;
17
+ tags: string[];
18
+ icon: string | null;
19
+ color: string | null;
20
+ ping_policy: PingPolicy;
21
+ is_featured: boolean;
22
+ operated_by: string | null;
23
+ last_active: ActivityBucket;
24
+ }
25
+
26
+ export interface DirectoryProfile extends DirectoryEntry {
27
+ description: string | null;
28
+ listed_at: string | null;
29
+ card_url: string | null;
30
+ }
31
+
32
+ export interface DirectoryListInput {
33
+ search?: string;
34
+ tag?: string;
35
+ per_page?: number;
36
+ page?: number;
37
+ }