@recursiv/sdk 0.7.81 → 0.7.83

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 (38) hide show
  1. package/README.md +45 -2
  2. package/dist/assistant/fixedCardData.d.ts +91 -0
  3. package/dist/assistant/fixedCardData.js +372 -0
  4. package/dist/assistant/fixedCards.d.ts +35 -0
  5. package/dist/assistant/fixedCards.js +904 -0
  6. package/dist/assistant/hiddenText.d.ts +24 -0
  7. package/dist/assistant/hiddenText.js +263 -0
  8. package/dist/assistant/index.d.ts +22 -0
  9. package/dist/assistant/index.js +22 -0
  10. package/dist/assistant/render.d.ts +8 -0
  11. package/dist/assistant/render.js +253 -0
  12. package/dist/assistant/subjectBundle.d.ts +30 -0
  13. package/dist/assistant/subjectBundle.js +518 -0
  14. package/dist/assistant/types.d.ts +353 -0
  15. package/dist/assistant/types.js +11 -0
  16. package/dist/assistant-surfaces.d.ts +42 -0
  17. package/dist/assistant-surfaces.js +113 -0
  18. package/dist/index.d.ts +8 -4
  19. package/dist/index.js +2 -1
  20. package/dist/realtime.d.ts +10 -3
  21. package/dist/realtime.js +11 -2
  22. package/dist/resources/agents.d.ts +6 -1
  23. package/dist/resources/agents.js +12 -2
  24. package/dist/resources/chat.d.ts +73 -1
  25. package/dist/resources/chat.js +83 -1
  26. package/dist/resources/e2ee.d.ts +56 -0
  27. package/dist/resources/e2ee.js +21 -0
  28. package/dist/resources/link-preview.d.ts +42 -1
  29. package/dist/resources/link-preview.js +36 -2
  30. package/dist/resources/network.d.ts +12 -0
  31. package/dist/resources/project-brain.d.ts +8 -0
  32. package/dist/resources/reports.d.ts +24 -0
  33. package/dist/resources/reports.js +12 -0
  34. package/dist/resources/users.d.ts +11 -2
  35. package/dist/resources/users.js +1 -1
  36. package/dist/types.d.ts +34 -0
  37. package/llm.md +35 -3
  38. package/package.json +6 -1
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Types for `@recursiv/sdk/assistant`: the subject bundle (what an assistant may read about a post) and its rendering
3
+ * as model input. Plain data only, so the same values pass between the server, a phone and a stored snapshot.
4
+ *
5
+ * Some inputs are facts other members must never learn: whether an author is a known minor, whether a post is under a
6
+ * child-safety hold or suppressed, and who blocked the asker. They are marked "server-side input". The server reads
7
+ * them and never sends them to another member's client; a client that builds a bundle passes each post's
8
+ * `assist_readable` instead, the server's verdict for that viewer, which does not say why.
9
+ */
10
+ /**
11
+ * What an author chose for asks about their posts (S22): asks in every mode, Sealed asks only, or none. A server from
12
+ * before the granular choice sends a boolean (`true` for 'all', `false` for 'off') or nothing.
13
+ */
14
+ export type AiAskAllowed = 'all' | 'sealed_only' | 'off';
15
+ /** Where an ask runs: 'standard' through the platform's server, 'sealed' from the phone to a checked model server. */
16
+ export type AssistantMode = 'standard' | 'sealed';
17
+ /** What the member asked for. Every intent is text only for now: pictures go as their alt text and stored caption. */
18
+ export type AssistIntent = 'explain' | 'summarise' | 'fact_context' | 'translate' | 'describe';
19
+ export declare const ASSIST_INTENTS: readonly AssistIntent[];
20
+ export interface SubjectAuthorInput {
21
+ id: string;
22
+ /** The handle, with or without its leading @. A federated handle may carry its server (`alice@example.social`). */
23
+ username?: string | null;
24
+ /** The display name. Written by the author, so it is rendered as their text, never as a label. */
25
+ name?: string | null;
26
+ /**
27
+ * Server-side input: the platform knows this account belongs to someone under 18. Never send it to another member's
28
+ * client (it would make children findable); a client leaves it out and passes the post's `assist_readable`. Any
29
+ * value but `false`, `null` or absent counts as set.
30
+ */
31
+ known_minor?: boolean | null;
32
+ }
33
+ /**
34
+ * One picture, clip or sound on a post. Only its type and alt text are read: an address or the bytes, if the caller's
35
+ * object carries them, are never copied into the bundle.
36
+ */
37
+ export interface SubjectMediaInput {
38
+ type?: string | null;
39
+ /** What the author wrote for it. */
40
+ alt_text?: string | null;
41
+ /**
42
+ * Under a child-safety hold or reported: its alt text and the post's stored caption are left out. Server-side input,
43
+ * like the post's hold. Any value but `false`, `null` or absent counts as set.
44
+ */
45
+ held?: boolean | null;
46
+ }
47
+ export interface SubjectLinkCardInput {
48
+ url?: string | null;
49
+ title?: string | null;
50
+ description?: string | null;
51
+ }
52
+ export interface SubjectCountsInput {
53
+ votes?: number | null;
54
+ upvotes?: number | null;
55
+ downvotes?: number | null;
56
+ replies?: number | null;
57
+ reposts?: number | null;
58
+ }
59
+ /** A post, reply, parent or quoted post as the caller holds it. */
60
+ export interface SubjectPostInput {
61
+ id: string;
62
+ author: SubjectAuthorInput | null;
63
+ title?: string | null;
64
+ content?: string | null;
65
+ /** An ISO time with its zone ("2026-10-01T14:05:00Z"). A time without one is left out. */
66
+ created_at?: string | null;
67
+ media?: readonly SubjectMediaInput[] | null;
68
+ /** The description the platform stored for the post's picture. */
69
+ ai_caption?: string | null;
70
+ /** The card the caller already read for the post's link. Nothing is fetched. */
71
+ link_card?: SubjectLinkCardInput | null;
72
+ counts?: SubjectCountsInput | null;
73
+ /** The author's choice (S22). Absent means the default, 'all'; a value this SDK does not know means 'off'. */
74
+ ai_ask_allowed?: AiAskAllowed | boolean | null;
75
+ /**
76
+ * Server-side input: under a child-safety hold (CS-P0-5). Never send it to another member's client, the author's
77
+ * included (it would warn them); a client passes `assist_readable`. Any value but `false`, `null` or absent counts
78
+ * as set, so a hold record, a time, `1` or `'t'` all leave the post out.
79
+ */
80
+ child_safety_hold?: boolean | null;
81
+ /** Server-side input: a moderator turned the assistant off for this post. Read like `child_safety_hold`. */
82
+ assistant_suppressed?: boolean | null;
83
+ /**
84
+ * The server's verdict for this viewer (from `readPostForViewer`), for a builder on a client: it folds in the
85
+ * author's choice, a hold, suppression, a known-minor author and a block by the author, and does not say which.
86
+ * 'sealed_only' comes only from the author's choice. Absent means no verdict (the other fields decide); a value this
87
+ * SDK does not know means 'none'. It holds whoever asks, the author included.
88
+ */
89
+ assist_readable?: AssistReadable | null;
90
+ }
91
+ /** What the server says this viewer's assistant may read of a post: all of it, in Sealed only, or nothing. */
92
+ export type AssistReadable = 'all' | 'sealed_only' | 'none';
93
+ /** A list of account ids: an array or a set. A single id as a bare string is refused, never read letter by letter. */
94
+ export type AccountIds = readonly string[] | ReadonlySet<string>;
95
+ /**
96
+ * Who is asking. A list that is present but cannot be read (a string, a plain object, a Promise, a Map, an entry
97
+ * that is not an id) throws a TypeError rather than counting as empty. A number in a list is read as its decimal string.
98
+ */
99
+ export interface SubjectAsker {
100
+ id: string;
101
+ /** Accounts the asker blocked. */
102
+ blocked_ids?: AccountIds | null;
103
+ /** Accounts the asker muted. */
104
+ muted_ids?: AccountIds | null;
105
+ /**
106
+ * Server-side input: accounts that blocked the asker. Never send this list to the asker's client; a client leaves
107
+ * it out (the server already hides those accounts' posts from the asker, and `assist_readable` folds it in).
108
+ */
109
+ blocked_by_ids?: AccountIds | null;
110
+ }
111
+ export interface SubjectBundleLimits {
112
+ /** Replies kept, in the order given (top first). */
113
+ max_replies: number;
114
+ /** Pictures, clips and sounds kept per item. */
115
+ max_media: number;
116
+ /** Characters of the asked-about post's words. */
117
+ post_chars: number;
118
+ /** Characters of the parent's or quoted post's words. */
119
+ context_chars: number;
120
+ /** Characters of one reply's words. */
121
+ reply_chars: number;
122
+ title_chars: number;
123
+ name_chars: number;
124
+ alt_text_chars: number;
125
+ caption_chars: number;
126
+ link_title_chars: number;
127
+ link_description_chars: number;
128
+ /**
129
+ * Characters across the bundle; `chars` never passes it. The asked-about post is always kept, its strings cut to fit
130
+ * (words first, then title, display name, alt text, stored caption and link card); a parent, quoted post or reply
131
+ * that would take the total past it is left out whole.
132
+ */
133
+ total_chars: number;
134
+ }
135
+ export interface BuildSubjectBundleInput {
136
+ mode: AssistantMode;
137
+ asker: SubjectAsker;
138
+ /** The post the member asked about. */
139
+ post: SubjectPostInput;
140
+ /** The post it replies to. */
141
+ parent?: SubjectPostInput | null;
142
+ /** The post it quotes or reposts. */
143
+ quoted?: SubjectPostInput | null;
144
+ /** Replies the asker can see, top first. */
145
+ replies?: readonly SubjectPostInput[] | null;
146
+ /** Lower limits. A value above the default is held at the default. */
147
+ limits?: Partial<SubjectBundleLimits> | null;
148
+ }
149
+ export type BundleItemRole = 'subject' | 'parent' | 'quoted' | 'reply';
150
+ export interface BundleMedia {
151
+ type: 'image' | 'video' | 'audio' | 'other';
152
+ alt_text: string | null;
153
+ }
154
+ export interface BundleCounts {
155
+ votes?: number;
156
+ upvotes?: number;
157
+ downvotes?: number;
158
+ replies?: number;
159
+ reposts?: number;
160
+ }
161
+ export interface BundleLinkCard {
162
+ url: string | null;
163
+ title: string | null;
164
+ description: string | null;
165
+ }
166
+ export interface BundleItem {
167
+ role: BundleItemRole;
168
+ id: string;
169
+ author: {
170
+ id: string;
171
+ /** Without the @. Null when the handle was missing or not a plain handle. */
172
+ username: string | null;
173
+ name: string | null;
174
+ };
175
+ /** ISO time (UTC), or null. */
176
+ created_at: string | null;
177
+ title: string | null;
178
+ text: string;
179
+ media: BundleMedia[];
180
+ ai_caption: string | null;
181
+ link_card: BundleLinkCard | null;
182
+ counts: BundleCounts | null;
183
+ /** Some of this item's text was cut to fit a limit. */
184
+ truncated: boolean;
185
+ }
186
+ /**
187
+ * Every id, handle (without the @) and web address the content holds: all an answer may cite or link. No picture or
188
+ * media address is ever listed (by file type, or a known media host or path such as cdn.minds.com or /fs/v1/), and
189
+ * picture addresses in members' text are replaced by "[link to a picture]". An answer may write these addresses only
190
+ * as plain links: the output filter must never render an image the model wrote, whatever its address.
191
+ */
192
+ export interface BundleReferences {
193
+ ids: string[];
194
+ handles: string[];
195
+ urls: string[];
196
+ }
197
+ export interface SubjectBundle {
198
+ version: 1;
199
+ mode: AssistantMode;
200
+ subject: BundleItem;
201
+ parent: BundleItem | null;
202
+ quoted: BundleItem | null;
203
+ replies: BundleItem[];
204
+ references: BundleReferences;
205
+ /** Characters (code points) of member-written text across the bundle: never more than `limits.total_chars`. */
206
+ chars: number;
207
+ }
208
+ /**
209
+ * Why something was left out, as the asker may be told it. 'unavailable' covers a block or mute in either direction,
210
+ * a known-minor author, a child-safety hold (on the post or one of its pictures), moderator suppression and a
211
+ * server's 'none' verdict alike, so neither `refused` nor a count says which. The asker knows whom they blocked or
212
+ * muted, so a reason of its own for blocks would name who blocked them; one for minors or holds would tell anyone
213
+ * whose account is a child's, or warn a poster that their post is held. The author's own choice is theirs to show.
214
+ */
215
+ export type ExclusionReason = 'unavailable' | 'author_opted_out' | 'author_sealed_only' | 'unknown_author' | 'invalid' | 'duplicate' | 'empty' | 'reply_limit' | 'media_limit' | 'size_limit';
216
+ /** The detail behind 'unavailable'. Server log only: never sent to the asker or to any member's client. */
217
+ export type UnavailableReason = 'child_safety_hold' | 'moderator_suppressed' | 'blocked_or_muted' | 'known_minor_author' | 'media_held' | 'not_readable';
218
+ export type DetailedExclusionReason = Exclude<ExclusionReason, 'unavailable'> | UnavailableReason;
219
+ export type ExclusionRole = BundleItemRole | 'media' | 'caption';
220
+ /** How many of one kind were left out, and why. Counts only: never an id or a handle. */
221
+ export interface SubjectExclusion {
222
+ role: ExclusionRole;
223
+ reason: ExclusionReason;
224
+ count: number;
225
+ }
226
+ export interface DetailedExclusion {
227
+ role: ExclusionRole;
228
+ reason: DetailedExclusionReason;
229
+ count: number;
230
+ }
231
+ export interface SubjectBundleResult {
232
+ /** Null when the asked-about post itself may not be read: make no model call. */
233
+ bundle: SubjectBundle | null;
234
+ /**
235
+ * Why the asked-about post may not be read, as the asker may be told it. Show 'unavailable' the same way as an
236
+ * error ("this post can't be read right now"): to an author asking about their own post it can only mean a hold or
237
+ * suppression.
238
+ */
239
+ refused: ExclusionReason | null;
240
+ /**
241
+ * What was left out, as the asker may be told it. Show the asker a total at most: on a thread with one or two
242
+ * replies a count per reason can still point at one reply.
243
+ */
244
+ excluded: SubjectExclusion[];
245
+ /**
246
+ * The same with the detail behind 'unavailable', for the server's own log. Never send it to the asker or to any
247
+ * member's client, and never into a stored snapshot a client reads.
248
+ */
249
+ audit: {
250
+ refused: DetailedExclusionReason | null;
251
+ excluded: DetailedExclusion[];
252
+ };
253
+ }
254
+ export interface RenderBundleOptions {
255
+ /**
256
+ * A fresh random string for this call alone (16 to 128 letters, digits, '-' or '_'), from the caller's random
257
+ * source: 32 hex digits from `crypto.getRandomValues` will do. It marks where the untrusted content starts and ends.
258
+ */
259
+ boundary: string;
260
+ /** For 'translate': the language to translate into, as a BCP 47 tag ('es', 'pt-BR'). */
261
+ language?: string | null;
262
+ }
263
+ /**
264
+ * A rendered bundle. Render only a bundle this process built from its own reads: the renderer re-checks every field
265
+ * it writes outside a member line and throws on one that does not fit, but a bundle a client sent has skipped every
266
+ * exclusion rule, so a server never renders one.
267
+ */
268
+ export interface RenderedBundle {
269
+ /** Fixed instructions written by the renderer: trusted, for the system role. */
270
+ system: string;
271
+ /** The delimited content, then the task again: for the user role. */
272
+ user: string;
273
+ /** `system` and `user` as one message, for a path that sends one. */
274
+ text: string;
275
+ /**
276
+ * What the rendered content holds: an answer may cite or link these and nothing else, and only as plain links (see
277
+ * BundleReferences): never render an image the model wrote.
278
+ */
279
+ references: BundleReferences;
280
+ }
281
+ /** Which fixed card a message calls for. */
282
+ export type FixedCardKind = 'election' | 'crisis';
283
+ /** The languages the matcher reads and the cards are written in. */
284
+ export type FixedCardLanguage = 'en' | 'es';
285
+ export declare const FIXED_CARD_LANGUAGES: readonly FixedCardLanguage[];
286
+ export interface FixedCardOptions {
287
+ /**
288
+ * The member's country, as an ISO 3166-1 alpha-2 code ('US', 'mx'), when the caller knows it: from the profile or
289
+ * the request's edge country header. Unknown or absent gives the generic card, which names no country's line but
290
+ * the US one, labelled as such.
291
+ */
292
+ country?: string | null;
293
+ /**
294
+ * The member's app language, as a BCP 47 tag ('es-MX'). The card is written in the language the message matched
295
+ * in; this decides only when it matched in both, or for `fixedCardFor`, which reads no message.
296
+ */
297
+ locale?: string | null;
298
+ }
299
+ /** A number to call or text, as the card shows it. */
300
+ export interface FixedCardPhone {
301
+ /** Who answers, by the service's own name. */
302
+ name: string;
303
+ /** As the service writes it. */
304
+ number: string;
305
+ /** What a phone dials, for a `tel:` link: digits, with a leading `*` kept. */
306
+ dial: string;
307
+ /** The number to text, or null when the line takes calls only. */
308
+ text: string | null;
309
+ /** Emergency services rather than a helpline. */
310
+ emergency: boolean;
311
+ /** Where the number was checked. */
312
+ sources: string[];
313
+ }
314
+ /** An official site the card links to. */
315
+ export interface FixedCardLink {
316
+ label: string;
317
+ url: string;
318
+ /** Where the address was checked. */
319
+ sources: string[];
320
+ }
321
+ /**
322
+ * A fixed card: short, plain, the same every time for the same country and language, and written by people, not a
323
+ * model. Every number and address in `lines` is also in `phones` or `links`, with its sources.
324
+ */
325
+ export interface FixedCard {
326
+ kind: FixedCardKind;
327
+ /**
328
+ * Stable: 'crisis.US', 'crisis.generic', 'election.MX', 'election.generic'. A crisis card may add '.danger' (danger
329
+ * from someone else or a medical emergency: it leads with the emergency number, the police where that is a separate
330
+ * number) or '.other' (worry about someone else: its line speaks to the helper).
331
+ */
332
+ id: string;
333
+ /** The country the card is for, or null for the generic card. */
334
+ country: string | null;
335
+ language: FixedCardLanguage;
336
+ title: string;
337
+ /** What the card says, in order, one short sentence or two per line. */
338
+ lines: string[];
339
+ phones: FixedCardPhone[];
340
+ links: FixedCardLink[];
341
+ /** The day the numbers and addresses were last checked against their sources (YYYY-MM-DD). */
342
+ as_of: string;
343
+ }
344
+ /** What a message calls for. It names no phrase that matched: the words a member wrote stay with the caller. */
345
+ export interface FixedCardMatch {
346
+ kind: FixedCardKind;
347
+ card: FixedCard;
348
+ }
349
+ /**
350
+ * What the turn does after the card: 'card_only' shows the card instead of a model answer (no model call);
351
+ * 'card_then_answer' shows the card first, then the turn may go on to a model answer.
352
+ */
353
+ export type FixedCardAnswer = 'card_only' | 'card_then_answer';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Types for `@recursiv/sdk/assistant`: the subject bundle (what an assistant may read about a post) and its rendering
3
+ * as model input. Plain data only, so the same values pass between the server, a phone and a stored snapshot.
4
+ *
5
+ * Some inputs are facts other members must never learn: whether an author is a known minor, whether a post is under a
6
+ * child-safety hold or suppressed, and who blocked the asker. They are marked "server-side input". The server reads
7
+ * them and never sends them to another member's client; a client that builds a bundle passes each post's
8
+ * `assist_readable` instead, the server's verdict for that viewer, which does not say why.
9
+ */
10
+ export const ASSIST_INTENTS = ['explain', 'summarise', 'fact_context', 'translate', 'describe'];
11
+ export const FIXED_CARD_LANGUAGES = ['en', 'es'];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Server-driven kill switches for the assistant's surfaces (George plan step G7a).
3
+ *
4
+ * A flag compiled into a phone app cannot be switched off once that build ships, so every place an app shows the
5
+ * assistant asks the server first. The switches are the app's project setting `assistant.surfaces`, and they arrive
6
+ * inside responses the app already fetches: `users.me()` (the boot read) and `network.getConfig()` both carry
7
+ * `data.assistant.surfaces`. An app's owner or admin changes them with
8
+ * `projectBrain.updateSettings(projectId, { assistant: { surfaces: { post_button: true } } })`.
9
+ *
10
+ * Fail closed: show a surface only when {@link assistantSurfaceOn} says so, which is only for an explicit `true`. A
11
+ * response from an older server (no `assistant`), a read that failed, a malformed value and an id this SDK does not
12
+ * know are all off.
13
+ *
14
+ * The server keeps the same list (packages/server/src/features/assistant/surfaces.ts); a contract test holds them equal.
15
+ */
16
+ /**
17
+ * Every assistant surface an app may show, one switch each. All default off. Step numbers are the George plan's §8.
18
+ * New ids are appended; an id is never renamed.
19
+ */
20
+ export declare const ASSISTANT_SURFACE_IDS: readonly ["post_button", "answer_sheet", "search_row", "top_bar", "inbox_row", "mentions", "chats", "e2ee_asks", "groups_catch_up", "translate", "compose", "profile_facts", "why_seeing", "daily_brief", "notes", "george_tab", "sealed_in_tab", "thread_summary", "selection", "topic_catch_up", "describe_image", "alt_text", "feed_steering", "share_answer"];
21
+ export type AssistantSurfaceId = (typeof ASSISTANT_SURFACE_IDS)[number];
22
+ /** Every surface id with its switch, as the API sends it under `assistant.surfaces`. */
23
+ export type AssistantSurfaces = Record<AssistantSurfaceId, boolean>;
24
+ /** The `assistant` block of `users.me()` and `network.getConfig()` (absent on servers from before G7a). */
25
+ export interface AssistantSettings {
26
+ surfaces: AssistantSurfaces;
27
+ }
28
+ /** A write to the switches: any subset of surface ids, each an explicit boolean. */
29
+ export interface AssistantSettingsUpdate {
30
+ surfaces: Partial<Record<AssistantSurfaceId, boolean>>;
31
+ }
32
+ /** True for a surface id this SDK knows. */
33
+ export declare function isAssistantSurfaceId(value: unknown): value is AssistantSurfaceId;
34
+ /**
35
+ * Whether the app may show surface `id`: true only when `settings` holds an explicit `true` for it. `settings` is a
36
+ * response's `data` (`(await sdk.users.me()).data`, `(await sdk.network.getConfig()).data`) or its `assistant` block.
37
+ * Anything else is off: no settings (a read that failed, an older server), a malformed value, a non-boolean, an id
38
+ * this SDK does not know.
39
+ */
40
+ export declare function assistantSurfaceOn(settings: unknown, id: AssistantSurfaceId | string): boolean;
41
+ /** Every known surface with its switch, read from `settings` by the same rule as {@link assistantSurfaceOn}. */
42
+ export declare function assistantSurfaces(settings: unknown): AssistantSurfaces;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Server-driven kill switches for the assistant's surfaces (George plan step G7a).
3
+ *
4
+ * A flag compiled into a phone app cannot be switched off once that build ships, so every place an app shows the
5
+ * assistant asks the server first. The switches are the app's project setting `assistant.surfaces`, and they arrive
6
+ * inside responses the app already fetches: `users.me()` (the boot read) and `network.getConfig()` both carry
7
+ * `data.assistant.surfaces`. An app's owner or admin changes them with
8
+ * `projectBrain.updateSettings(projectId, { assistant: { surfaces: { post_button: true } } })`.
9
+ *
10
+ * Fail closed: show a surface only when {@link assistantSurfaceOn} says so, which is only for an explicit `true`. A
11
+ * response from an older server (no `assistant`), a read that failed, a malformed value and an id this SDK does not
12
+ * know are all off.
13
+ *
14
+ * The server keeps the same list (packages/server/src/features/assistant/surfaces.ts); a contract test holds them equal.
15
+ */
16
+ /**
17
+ * Every assistant surface an app may show, one switch each. All default off. Step numbers are the George plan's §8.
18
+ * New ids are appended; an id is never renamed.
19
+ */
20
+ export const ASSISTANT_SURFACE_IDS = [
21
+ // The G glyph on a post (feed, post page, network or bridged post, clip, article, story), with its long-press item
22
+ // and screen-reader action. G3.
23
+ 'post_button',
24
+ // The answer sheet: the answer streamed in place (bottom sheet on phones, right rail on desktop), its chips and
25
+ // follow-ups. G11 (G3's interim sheet rides it too).
26
+ 'answer_sheet',
27
+ // "Ask about these results" on a question-shaped search, and the Cmd-K ask. G4; answers G23a.
28
+ 'search_row',
29
+ // The glyph in the top bar that opens the assistant tab. G4.
30
+ 'top_bar',
31
+ // The assistant's one row in the Chat inbox. G4.
32
+ 'inbox_row',
33
+ // Mentioning the assistant in a reply (the `ai:george` chip), raw `@askgeorge` routing, and the push that an answer
34
+ // is ready. G22.
35
+ 'mentions',
36
+ // Asking about picked messages in an ordinary (not encrypted) DM or group chat, and mentioning the assistant in a
37
+ // small closed group chat. G20.
38
+ 'chats',
39
+ // Asking the Sealed assistant about one message in an encrypted chat, on the phone only. G21.
40
+ 'e2ee_asks',
41
+ // "Catch me up" in a group's (community's) header. G24.
42
+ 'groups_catch_up',
43
+ // "Translate" under a post in another language. G23a (on-device G25).
44
+ 'translate',
45
+ // The compose chip: check tone, shorten, fix spelling, translate, draft a reply. G23a.
46
+ 'compose',
47
+ // "About this account": the facts card on a profile, with no model call. G23a.
48
+ 'profile_facts',
49
+ // "Why am I seeing this" on a feed post, and asking the assistant to explain it. G28.
50
+ 'why_seeing',
51
+ // The opt-in daily brief and routines. G30.
52
+ 'daily_brief',
53
+ // Context notes the assistant drafts and people rate. G31.
54
+ 'notes',
55
+ // The assistant tab itself: thread list, new chat, the chat screen. G13.
56
+ 'george_tab',
57
+ // Sealed chats inside the assistant tab, on the phone. G15b.
58
+ 'sealed_in_tab',
59
+ // "Summarise replies" on a thread with 10 or more replies. G11.
60
+ 'thread_summary',
61
+ // Ask and Quote on selected text (web selection popover G11; phone apps G32).
62
+ 'selection',
63
+ // "What's going on" on a topic bar or Today story. G23a.
64
+ 'topic_catch_up',
65
+ // "Describe" on a picture in the media viewer. G23b, only after the upload checks are live.
66
+ 'describe_image',
67
+ // "Suggest alt text" for your own picture in compose. G23b, only after the upload checks are live.
68
+ 'alt_text',
69
+ // "Tell the assistant what you want more or less of" in feed settings. G29.
70
+ 'feed_steering',
71
+ // Sharing an answer as your own post. G27, not before 2026-11-04.
72
+ 'share_answer',
73
+ ];
74
+ const SURFACE_IDS = new Set(ASSISTANT_SURFACE_IDS);
75
+ /** True for a surface id this SDK knows. */
76
+ export function isAssistantSurfaceId(value) {
77
+ return typeof value === 'string' && SURFACE_IDS.has(value);
78
+ }
79
+ function isPlainObject(value) {
80
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
81
+ }
82
+ /**
83
+ * The surfaces map inside `settings`, which may be a response's `data` (carrying `assistant.surfaces`) or its
84
+ * `assistant` block (carrying `surfaces`). Null when there is none to read.
85
+ */
86
+ function surfacesIn(settings) {
87
+ if (!isPlainObject(settings))
88
+ return null;
89
+ if (Object.hasOwn(settings, 'assistant')) {
90
+ const assistant = settings.assistant;
91
+ return isPlainObject(assistant) && isPlainObject(assistant.surfaces) ? assistant.surfaces : null;
92
+ }
93
+ return isPlainObject(settings.surfaces) ? settings.surfaces : null;
94
+ }
95
+ /**
96
+ * Whether the app may show surface `id`: true only when `settings` holds an explicit `true` for it. `settings` is a
97
+ * response's `data` (`(await sdk.users.me()).data`, `(await sdk.network.getConfig()).data`) or its `assistant` block.
98
+ * Anything else is off: no settings (a read that failed, an older server), a malformed value, a non-boolean, an id
99
+ * this SDK does not know.
100
+ */
101
+ export function assistantSurfaceOn(settings, id) {
102
+ if (!isAssistantSurfaceId(id))
103
+ return false;
104
+ const surfaces = surfacesIn(settings);
105
+ return !!surfaces && Object.hasOwn(surfaces, id) && surfaces[id] === true;
106
+ }
107
+ /** Every known surface with its switch, read from `settings` by the same rule as {@link assistantSurfaceOn}. */
108
+ export function assistantSurfaces(settings) {
109
+ const out = {};
110
+ for (const id of ASSISTANT_SURFACE_IDS)
111
+ out[id] = assistantSurfaceOn(settings, id);
112
+ return out;
113
+ }
package/dist/index.d.ts CHANGED
@@ -165,9 +165,10 @@ export { POSTS_BATCH_MAX, PostsResource } from './resources/posts.js';
165
165
  export type { CategoryOrder, CategoryOrderMeta, CategoryOrderReason, PostBatch, PostBatchItem } from './resources/posts.js';
166
166
  export { UsersResource } from './resources/users.js';
167
167
  export { CommunitiesResource } from './resources/communities.js';
168
- export { ChatResource } from './resources/chat.js';
168
+ export { ChatResource, CHAT_ATTACHMENT_REF_PREFIX, chatAttachmentKeysIn, chatAttachmentRefsIn } from './resources/chat.js';
169
+ export type { ChatAttachmentRef, ChatAttachmentUploadInput, ChatAttachmentUploadUrl, ChatAttachmentUploadUrlInput, SignedChatAttachment, } from './resources/chat.js';
169
170
  export { E2eeResource } from './resources/e2ee.js';
170
- export type { E2eeAccountReset, E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelope, E2eeEvent, E2eeHasDependentsDetails, E2eeKeyPackageStatus, E2eeLink, E2eeLinkCancelledBy, E2eeLinkProgress, E2eeLinkRealtimeEvent, E2eeLinkState, E2eeObjectDownload, E2eeObjectUpload, E2eeRealtimeEvent, E2eeResetCodeSent, E2eeResetErrorCode, E2eeRevokeResponse, E2eeStartRequest, E2eeStatus, E2eeStatusReason, } from './resources/e2ee.js';
171
+ export type { E2eeAccountReset, E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelope, E2eeEvent, E2eeHasDependentsDetails, E2eeKeyPackageStatus, E2eeLink, E2eeLinkCancelledBy, E2eeLinkProgress, E2eeLinkRealtimeEvent, E2eeLinkState, E2eeObjectDownload, E2eeObjectUpload, E2eePeerChange, E2eePeerErrorCode, E2eePeerNotJoinedDetails, E2eePeerRealtimeEvent, E2eeRealtimeEvent, E2eeResetCodeSent, E2eeResetErrorCode, E2eeRevokeResponse, E2eeStartRequest, E2eeStatus, E2eeStatusReason, } from './resources/e2ee.js';
171
172
  export { ProjectsResource } from './resources/projects.js';
172
173
  export { AgentsResource, SUPPORTED_MODELS, AGENT_DIRECTORY_SORTS, AGENT_DIRECTORY_MAX_LIMIT } from './resources/agents.js';
173
174
  export { EvidenceResource } from './resources/evidence.js';
@@ -207,7 +208,7 @@ export type { AppSubscriptionBoostCredit, AppSubscriptionCheckoutResult, AppSubs
207
208
  export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayParams, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, CuratorTodayDiscussion, CuratorStoryDiscussion, CuratorStoryLaneOutcome, CuratorStoryMindsPost, CuratorStoryPostItem, CuratorStoryPostPagesOptions, CuratorStoryPostsPage, CuratorStoryPostsParams, CuratorStoryReader, CuratorStorySocialPost, CuratorStoryVolumeLanes, CuratorStoryVolumeMeta, StoryPostNetwork, StoryPostPool, StoryPostSort, ForYouMeta, ForYouParams, ForYouSurface, ForYouWorldBlock, ForYouWorldBlockType, ForYouWorldCategoryShortcut, ForYouWorldItem, ForYouWorldMeta, ForYouWorldParams, ForYouWorldPart, ForYouWorldResponse, ForYouWorldStory, ForYouWorldStoryTarget, ForYouWorldViral, } from './resources/curator.js';
208
209
  export { STORY_POOL_MAX_POSTS, STORY_POST_NETWORKS, STORY_POSTS_PAGE_MAX } from './resources/curator.js';
209
210
  export { ReportsResource } from './resources/reports.js';
210
- export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
211
+ export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, ReportedMessageAttachment, ReportedMessageAttachments, } from './resources/reports.js';
211
212
  export { JudgmentsResource, isJudgmentSharedWordsError } from './resources/judgments.js';
212
213
  export type { JudgmentSubjectKind, JudgmentSource, JudgmentOverrideMethod, PostLabelsStatus, PostLabelsQuery, PostLabelsSubject, PostLabelsCategory, PostLabelsPolitics, PostJudgmentRecord, PostJudgment, PostSafetyAttribution, PostLabelsCategoryView, PostLabelsSummaryView, PostLabelsFullView, PostLabels, DisputeJudgmentInput, JudgmentDispute, ReviewJudgmentInput, JudgmentReviewUpheld, JudgmentReviewAwaitingReviewer, JudgmentReviewOverridden, JudgmentReview, JudgmentSharedWordsDetails, JudgmentSharedWordsError, } from './resources/judgments.js';
213
214
  export { ModerationResource } from './resources/moderation.js';
@@ -225,7 +226,7 @@ export type { SignalBatchResult, SignalEventInput, SignalEventType, SignalNotRec
225
226
  export { SEALED_ALLOWANCE_REMAINING_HEADER, SEALED_REFUSAL_HEADER, SealedResource, SealedTransportError, } from './resources/sealed.js';
226
227
  export type { SealedAllowance, SealedAttestationInput, SealedConfig, SealedModel, SealedSendInput, SealedTarget, SealedTargetKind, SealedTransportErrorCode, } from './resources/sealed.js';
227
228
  export type { SiblingAccount, SiblingAccountsResponse, SwitchAccountResult, } from './resources/accounts.js';
228
- export type { LinkPreview, LinkPreviewResponse, } from './resources/link-preview.js';
229
+ export type { LinkPreview, LinkPreviewResponse, LinkPreviewSource, } from './resources/link-preview.js';
229
230
  export type { AgentCapabilityConsentRequirement, AgentCapabilityDataSensitivity, AgentCapabilityDomain, AgentCapabilityExportBoundary, AgentCapabilityRetentionClass, AgentCapabilitySubdomain, AgentCapabilitySubject, AgentCapabilityTrustTier, AgentToolAudience, AgentToolScope, BrowserUseRunTaskInput, ProjectBrainSettings, ProjectToolBundleClassification, ProjectToolBundleKey, ProjectToolPermission, ProjectToolPermissionOverrides, UpdateProjectSettingsInput, } from './resources/project-brain.js';
230
231
  export { OrganizationSettingsResource } from './resources/organization-settings.js';
231
232
  export { ProjectSettingsResource } from './resources/project-settings.js';
@@ -269,3 +270,6 @@ export { parseSSE } from './lib/sse.js';
269
270
  export * from './lib/webhooks.js';
270
271
  export type * from './types.js';
271
272
  export type { PrepareHostedServiceInput, HostedServiceScope, HostedServiceOperation, HostedServiceReservation, HostedServiceObservation } from './resources/deployments.js';
273
+ export { ASSISTANT_SURFACE_IDS, assistantSurfaceOn, assistantSurfaces, isAssistantSurfaceId, } from './assistant-surfaces.js';
274
+ export type { AssistantSettings, AssistantSettingsUpdate, AssistantSurfaceId, AssistantSurfaces, } from './assistant-surfaces.js';
275
+ export type { CurrentUser } from './resources/users.js';
package/dist/index.js CHANGED
@@ -236,7 +236,7 @@ export { AuthResource } from './resources/auth.js';
236
236
  export { POSTS_BATCH_MAX, PostsResource } from './resources/posts.js';
237
237
  export { UsersResource } from './resources/users.js';
238
238
  export { CommunitiesResource } from './resources/communities.js';
239
- export { ChatResource } from './resources/chat.js';
239
+ export { ChatResource, CHAT_ATTACHMENT_REF_PREFIX, chatAttachmentKeysIn, chatAttachmentRefsIn } from './resources/chat.js';
240
240
  export { E2eeResource } from './resources/e2ee.js';
241
241
  export { ProjectsResource } from './resources/projects.js';
242
242
  export { AgentsResource, SUPPORTED_MODELS, AGENT_DIRECTORY_SORTS, AGENT_DIRECTORY_MAX_LIMIT } from './resources/agents.js';
@@ -302,3 +302,4 @@ export { MediaResource } from './resources/media.js';
302
302
  export { RecursivError, AuthenticationError, AuthorizationError, LegacyHandoffError, NotFoundError, ValidationError, InsufficientCreditsError, RateLimitError, ConflictError, isActionRateLimitScope, EmailNotSentError, } from './errors.js';
303
303
  export { parseSSE } from './lib/sse.js';
304
304
  export * from './lib/webhooks.js';
305
+ export { ASSISTANT_SURFACE_IDS, assistantSurfaceOn, assistantSurfaces, isAssistantSurfaceId, } from './assistant-surfaces.js';
@@ -1,6 +1,6 @@
1
1
  import type { Socket } from 'socket.io-client';
2
2
  import { HttpClient } from './client.js';
3
- import type { E2eeDevicesRealtimeEvent, E2eeLinkRealtimeEvent, E2eeRealtimeEvent } from './resources/e2ee.js';
3
+ import type { E2eeDevicesRealtimeEvent, E2eeLinkRealtimeEvent, E2eePeerRealtimeEvent, E2eeRealtimeEvent } from './resources/e2ee.js';
4
4
  import type { ChatConversationEvent, ChatMembersEvent } from './types.js';
5
5
  export interface ChatMessageEvent {
6
6
  id: string;
@@ -150,8 +150,8 @@ export declare class RealtimeClient {
150
150
  *
151
151
  * Server events include `notification`, `feed_update`,
152
152
  * `conversation_created`, `chat_message`, `chat_typing`, `agent_thinking`,
153
- * `chat:members`, `chat:conversation`, `e2ee:event`, `e2ee:link` and
154
- * `e2ee:devices`.
153
+ * `chat:members`, `chat:conversation`, `e2ee:event`, `e2ee:link`,
154
+ * `e2ee:devices` and `e2ee:peer`.
155
155
  */
156
156
  on<T = unknown>(event: string, listener: (payload: T) => void): () => void;
157
157
  /** Join a conversation room now, and again after every reconnect, until you leave it. */
@@ -199,4 +199,11 @@ export declare class RealtimeClient {
199
199
  * `{ deviceId, reason: 'revoked' }`.
200
200
  */
201
201
  onE2eeDevices(callback: (event: E2eeDevicesRealtimeEvent) => void): () => void;
202
+ /**
203
+ * Someone this account shares an encrypted conversation with reset encrypted
204
+ * chat: `{ userId, reason: 'root_changed', conversationId }`, once per shared
205
+ * conversation. Look at their devices again (`r.e2ee.devices({ user_id })`);
206
+ * catch up with `r.e2ee.peerChanges()` after time in the background.
207
+ */
208
+ onE2eePeer(callback: (event: E2eePeerRealtimeEvent) => void): () => void;
202
209
  }
package/dist/realtime.js CHANGED
@@ -369,8 +369,8 @@ export class RealtimeClient {
369
369
  *
370
370
  * Server events include `notification`, `feed_update`,
371
371
  * `conversation_created`, `chat_message`, `chat_typing`, `agent_thinking`,
372
- * `chat:members`, `chat:conversation`, `e2ee:event`, `e2ee:link` and
373
- * `e2ee:devices`.
372
+ * `chat:members`, `chat:conversation`, `e2ee:event`, `e2ee:link`,
373
+ * `e2ee:devices` and `e2ee:peer`.
374
374
  */
375
375
  on(event, listener) {
376
376
  // Every call is its own subscription, even for a function that is already
@@ -473,4 +473,13 @@ export class RealtimeClient {
473
473
  onE2eeDevices(callback) {
474
474
  return this.on('e2ee:devices', callback);
475
475
  }
476
+ /**
477
+ * Someone this account shares an encrypted conversation with reset encrypted
478
+ * chat: `{ userId, reason: 'root_changed', conversationId }`, once per shared
479
+ * conversation. Look at their devices again (`r.e2ee.devices({ user_id })`);
480
+ * catch up with `r.e2ee.peerChanges()` after time in the background.
481
+ */
482
+ onE2eePeer(callback) {
483
+ return this.on('e2ee:peer', callback);
484
+ }
476
485
  }
@@ -234,7 +234,10 @@ export declare class AgentsResource {
234
234
  }>;
235
235
  /** Delete an agent */
236
236
  delete(id: string): Promise<DeleteResponse>;
237
- /** Send a message to an agent (async — agent responds asynchronously) */
237
+ /**
238
+ * Send a message to an agent (async — agent responds asynchronously).
239
+ * `turn: { safe: true }` makes it a safe turn in the caller's Asks thread (see `AgentChatTurn`).
240
+ */
238
241
  chat(id: string, input: AgentChatInput): Promise<SingleResponse<AgentChatResult>>;
239
242
  /** List an agent's conversations */
240
243
  conversations(id: string, params?: PaginationParams): Promise<PaginatedResponse<AgentConversation>>;
@@ -398,6 +401,8 @@ export declare class AgentsResource {
398
401
  *
399
402
  * Requires `ReadableStream` support (Node.js, modern browsers).
400
403
  * For React Native, use `chatStreamText()` instead.
404
+ * `turn: { safe: true }` streams a safe turn in the caller's Asks thread (see `AgentChatTurn`); a refusal throws a
405
+ * `RecursivError` whose code is a `SafeTurnRefusalCode`, before anything streams.
401
406
  */
402
407
  chatStream(id: string, input: AgentChatInput): AsyncGenerator<StreamChunk>;
403
408
  /**