@recursiv/sdk 0.7.82 → 0.7.89

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 (59) hide show
  1. package/README.md +128 -1
  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 +30 -0
  9. package/dist/assistant/index.js +30 -0
  10. package/dist/assistant/render.d.ts +31 -0
  11. package/dist/assistant/render.js +434 -0
  12. package/dist/assistant/subjectBundle.d.ts +30 -0
  13. package/dist/assistant/subjectBundle.js +535 -0
  14. package/dist/assistant/turnCaps.d.ts +3 -0
  15. package/dist/assistant/turnCaps.js +141 -0
  16. package/dist/assistant/types.d.ts +569 -0
  17. package/dist/assistant/types.js +26 -0
  18. package/dist/assistant-surfaces.d.ts +42 -0
  19. package/dist/assistant-surfaces.js +113 -0
  20. package/dist/index.d.ts +15 -6
  21. package/dist/index.js +8 -1
  22. package/dist/privacy/facts.generated.d.ts +79 -0
  23. package/dist/privacy/facts.generated.js +93 -0
  24. package/dist/privacy/index.d.ts +48 -0
  25. package/dist/privacy/index.js +42 -0
  26. package/dist/realtime.d.ts +10 -3
  27. package/dist/realtime.js +11 -2
  28. package/dist/resources/agents.d.ts +6 -1
  29. package/dist/resources/agents.js +12 -2
  30. package/dist/resources/discovery-merge.d.ts +17 -1
  31. package/dist/resources/discovery-merge.js +84 -4
  32. package/dist/resources/discovery-progressive.d.ts +4 -3
  33. package/dist/resources/discovery-progressive.js +11 -8
  34. package/dist/resources/discovery.d.ts +9 -1
  35. package/dist/resources/discovery.js +62 -15
  36. package/dist/resources/e2ee.d.ts +56 -0
  37. package/dist/resources/e2ee.js +21 -0
  38. package/dist/resources/feeds.d.ts +15 -6
  39. package/dist/resources/feeds.js +2 -1
  40. package/dist/resources/integrations.d.ts +3 -0
  41. package/dist/resources/link-preview.d.ts +42 -1
  42. package/dist/resources/link-preview.js +36 -2
  43. package/dist/resources/network.d.ts +6 -0
  44. package/dist/resources/privacy.d.ts +13 -0
  45. package/dist/resources/privacy.js +14 -0
  46. package/dist/resources/project-brain.d.ts +8 -0
  47. package/dist/resources/protocols.d.ts +99 -11
  48. package/dist/resources/protocols.js +45 -0
  49. package/dist/resources/sealed.d.ts +127 -7
  50. package/dist/resources/sealed.js +33 -6
  51. package/dist/resources/settings.d.ts +3 -1
  52. package/dist/resources/settings.js +3 -1
  53. package/dist/resources/users.d.ts +11 -2
  54. package/dist/resources/users.js +1 -1
  55. package/dist/social-protocol-ids.d.ts +28 -0
  56. package/dist/social-protocol-ids.js +30 -0
  57. package/dist/types.d.ts +71 -0
  58. package/llm.md +45 -4
  59. package/package.json +11 -1
@@ -0,0 +1,569 @@
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, its alt text, `held`, and for pictures its plain id and
35
+ * `checked_at` are read: an address or the bytes, if the caller's 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: the builder leaves out its alt text, and the post's stored caption, which may
43
+ * describe it. Any value but `false`, `null` or absent counts as set. `true` is server-side, like the post's hold:
44
+ * never send it to another member's client. A server leaves held media out of that payload, and leaves out the
45
+ * post's stored caption (`ai_caption`) whenever any of its media is held: a client builder cannot see media it was
46
+ * never sent, so it keeps whatever caption the payload carries. `false` is also the payload's "not held" mark
47
+ * (sealed-full-scope-v1 2.1): a picture goes to a model as a picture only when `held` is exactly `false`, so pass
48
+ * it through from the payload. Absent or null still goes as alt text, but never as a picture.
49
+ */
50
+ held?: boolean | null;
51
+ /**
52
+ * The media's id in the post payload (`media[].id`), so a caller that renders with pictures can find the picture in
53
+ * its own image cache. Kept for pictures only, and only when it is a plain id: never an address.
54
+ */
55
+ id?: string | null;
56
+ /**
57
+ * When Minds last matched this picture against the child-safety hash lists (the post payload's `media[].checked_at`,
58
+ * sealed-full-scope-v1 2.1, gate P1): an ISO time with its zone, written only by the server. Kept only when `held`
59
+ * is exactly `false`. A picture can go to a model as a picture only while this is under 24 hours old by the
60
+ * renderer's clock; absent, not an ISO time with its zone, or without `held: false`, means alt text and the stored
61
+ * caption only, as for every other picture.
62
+ */
63
+ checked_at?: string | null;
64
+ }
65
+ export interface SubjectLinkCardInput {
66
+ url?: string | null;
67
+ title?: string | null;
68
+ description?: string | null;
69
+ }
70
+ export interface SubjectCountsInput {
71
+ votes?: number | null;
72
+ upvotes?: number | null;
73
+ downvotes?: number | null;
74
+ replies?: number | null;
75
+ reposts?: number | null;
76
+ }
77
+ /** A post, reply, parent or quoted post as the caller holds it. */
78
+ export interface SubjectPostInput {
79
+ id: string;
80
+ author: SubjectAuthorInput | null;
81
+ title?: string | null;
82
+ content?: string | null;
83
+ /** An ISO time with its zone ("2026-10-01T14:05:00Z"). A time without one is left out. */
84
+ created_at?: string | null;
85
+ media?: readonly SubjectMediaInput[] | null;
86
+ /** The description the platform stored for the post's picture. */
87
+ ai_caption?: string | null;
88
+ /** The card the caller already read for the post's link. Nothing is fetched. */
89
+ link_card?: SubjectLinkCardInput | null;
90
+ counts?: SubjectCountsInput | null;
91
+ /** The author's choice (S22). Absent means the default, 'all'; a value this SDK does not know means 'off'. */
92
+ ai_ask_allowed?: AiAskAllowed | boolean | null;
93
+ /**
94
+ * Server-side input: under a child-safety hold (CS-P0-5). Never send it to another member's client, the author's
95
+ * included (it would warn them); a client passes `assist_readable`. Any value but `false`, `null` or absent counts
96
+ * as set, so a hold record, a time, `1` or `'t'` all leave the post out.
97
+ */
98
+ child_safety_hold?: boolean | null;
99
+ /** Server-side input: a moderator turned the assistant off for this post. Read like `child_safety_hold`. */
100
+ assistant_suppressed?: boolean | null;
101
+ /**
102
+ * The server's verdict for this viewer (from `readPostForViewer`), for a builder on a client: it folds in the
103
+ * author's choice, a hold, suppression, a known-minor author and a block by the author, and does not say which.
104
+ * 'sealed_only' comes only from the author's choice. Absent means no verdict (the other fields decide); a value this
105
+ * SDK does not know means 'none'. It holds whoever asks, the author included.
106
+ */
107
+ assist_readable?: AssistReadable | null;
108
+ }
109
+ /** What the server says this viewer's assistant may read of a post: all of it, in Sealed only, or nothing. */
110
+ export type AssistReadable = 'all' | 'sealed_only' | 'none';
111
+ /** A list of account ids: an array or a set. A single id as a bare string is refused, never read letter by letter. */
112
+ export type AccountIds = readonly string[] | ReadonlySet<string>;
113
+ /**
114
+ * Who is asking. A list that is present but cannot be read (a string, a plain object, a Promise, a Map, an entry
115
+ * that is not an id) throws a TypeError rather than counting as empty. A number in a list is read as its decimal string.
116
+ */
117
+ export interface SubjectAsker {
118
+ id: string;
119
+ /** Accounts the asker blocked. */
120
+ blocked_ids?: AccountIds | null;
121
+ /** Accounts the asker muted. */
122
+ muted_ids?: AccountIds | null;
123
+ /**
124
+ * Server-side input: accounts that blocked the asker. Never send this list to the asker's client; a client leaves
125
+ * it out (the server already hides those accounts' posts from the asker, and `assist_readable` folds it in).
126
+ */
127
+ blocked_by_ids?: AccountIds | null;
128
+ }
129
+ export interface SubjectBundleLimits {
130
+ /** Replies kept, in the order given (top first). */
131
+ max_replies: number;
132
+ /** Pictures, clips and sounds kept per item. */
133
+ max_media: number;
134
+ /** Characters of the asked-about post's words. */
135
+ post_chars: number;
136
+ /** Characters of the parent's or quoted post's words. */
137
+ context_chars: number;
138
+ /** Characters of one reply's words. */
139
+ reply_chars: number;
140
+ title_chars: number;
141
+ name_chars: number;
142
+ alt_text_chars: number;
143
+ caption_chars: number;
144
+ link_title_chars: number;
145
+ link_description_chars: number;
146
+ /**
147
+ * Characters across the bundle; `chars` never passes it. The asked-about post is always kept, its strings cut to fit
148
+ * (words first, then title, display name, alt text, stored caption and link card); a parent, quoted post or reply
149
+ * that would take the total past it is left out whole.
150
+ */
151
+ total_chars: number;
152
+ }
153
+ export interface BuildSubjectBundleInput {
154
+ mode: AssistantMode;
155
+ asker: SubjectAsker;
156
+ /** The post the member asked about. */
157
+ post: SubjectPostInput;
158
+ /** The post it replies to. */
159
+ parent?: SubjectPostInput | null;
160
+ /** The post it quotes or reposts. */
161
+ quoted?: SubjectPostInput | null;
162
+ /** Replies the asker can see, top first. */
163
+ replies?: readonly SubjectPostInput[] | null;
164
+ /** Lower limits. A value above the default is held at the default. */
165
+ limits?: Partial<SubjectBundleLimits> | null;
166
+ }
167
+ export type BundleItemRole = 'subject' | 'parent' | 'quoted' | 'reply';
168
+ export interface BundleMedia {
169
+ type: 'image' | 'video' | 'audio' | 'other';
170
+ alt_text: string | null;
171
+ /** Pictures only: the media's id, when the input had a plain one. Never written into model input. */
172
+ id?: string;
173
+ /**
174
+ * Pictures only: Minds' last child-safety check of it (ISO time, UTC), when the input had a valid one and marked the
175
+ * picture not held (`held: false`).
176
+ */
177
+ checked_at?: string;
178
+ }
179
+ export interface BundleCounts {
180
+ votes?: number;
181
+ upvotes?: number;
182
+ downvotes?: number;
183
+ replies?: number;
184
+ reposts?: number;
185
+ }
186
+ export interface BundleLinkCard {
187
+ url: string | null;
188
+ title: string | null;
189
+ description: string | null;
190
+ }
191
+ export interface BundleItem {
192
+ role: BundleItemRole;
193
+ id: string;
194
+ author: {
195
+ id: string;
196
+ /** Without the @. Null when the handle was missing or not a plain handle. */
197
+ username: string | null;
198
+ name: string | null;
199
+ };
200
+ /** ISO time (UTC), or null. */
201
+ created_at: string | null;
202
+ title: string | null;
203
+ text: string;
204
+ media: BundleMedia[];
205
+ ai_caption: string | null;
206
+ link_card: BundleLinkCard | null;
207
+ counts: BundleCounts | null;
208
+ /** Some of this item's text was cut to fit a limit. */
209
+ truncated: boolean;
210
+ }
211
+ /**
212
+ * Every id, handle (without the @) and web address the content holds: all an answer may cite or link. No picture or
213
+ * media address is ever listed (by file type, or a known media host or path such as cdn.minds.com or /fs/v1/), and
214
+ * picture addresses in members' text are replaced by "[link to a picture]". An answer may write these addresses only
215
+ * as plain links: the output filter must never render an image the model wrote, whatever its address.
216
+ */
217
+ export interface BundleReferences {
218
+ ids: string[];
219
+ handles: string[];
220
+ urls: string[];
221
+ }
222
+ export interface SubjectBundle {
223
+ version: 1;
224
+ mode: AssistantMode;
225
+ subject: BundleItem;
226
+ parent: BundleItem | null;
227
+ quoted: BundleItem | null;
228
+ replies: BundleItem[];
229
+ references: BundleReferences;
230
+ /** Characters (code points) of member-written text across the bundle: never more than `limits.total_chars`. */
231
+ chars: number;
232
+ }
233
+ /**
234
+ * Why something was left out, as the asker may be told it. 'unavailable' covers a block or mute in either direction,
235
+ * a known-minor author, a child-safety hold (on the post or one of its pictures), moderator suppression and a
236
+ * server's 'none' verdict alike, so neither `refused` nor a count says which. The asker knows whom they blocked or
237
+ * muted, so a reason of its own for blocks would name who blocked them; one for minors or holds would tell anyone
238
+ * whose account is a child's, or warn a poster that their post is held. The author's own choice is theirs to show.
239
+ */
240
+ export type ExclusionReason = 'unavailable' | 'author_opted_out' | 'author_sealed_only' | 'unknown_author' | 'invalid' | 'duplicate' | 'empty' | 'reply_limit' | 'media_limit' | 'size_limit';
241
+ /** The detail behind 'unavailable'. Server log only: never sent to the asker or to any member's client. */
242
+ export type UnavailableReason = 'child_safety_hold' | 'moderator_suppressed' | 'blocked_or_muted' | 'known_minor_author' | 'media_held' | 'not_readable';
243
+ export type DetailedExclusionReason = Exclude<ExclusionReason, 'unavailable'> | UnavailableReason;
244
+ export type ExclusionRole = BundleItemRole | 'media' | 'caption';
245
+ /** How many of one kind were left out, and why. Counts only: never an id or a handle. */
246
+ export interface SubjectExclusion {
247
+ role: ExclusionRole;
248
+ reason: ExclusionReason;
249
+ count: number;
250
+ }
251
+ export interface DetailedExclusion {
252
+ role: ExclusionRole;
253
+ reason: DetailedExclusionReason;
254
+ count: number;
255
+ }
256
+ export interface SubjectBundleResult {
257
+ /** Null when the asked-about post itself may not be read: make no model call. */
258
+ bundle: SubjectBundle | null;
259
+ /**
260
+ * Why the asked-about post may not be read, as the asker may be told it. Show 'unavailable' the same way as an
261
+ * error ("this post can't be read right now"): to an author asking about their own post it can only mean a hold or
262
+ * suppression.
263
+ */
264
+ refused: ExclusionReason | null;
265
+ /**
266
+ * 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
267
+ * replies a count per reason can still point at one reply.
268
+ */
269
+ excluded: SubjectExclusion[];
270
+ /**
271
+ * The same with the detail behind 'unavailable', for the server's own log. Never send it to the asker or to any
272
+ * member's client, and never into a stored snapshot a client reads.
273
+ */
274
+ audit: {
275
+ refused: DetailedExclusionReason | null;
276
+ excluded: DetailedExclusion[];
277
+ };
278
+ }
279
+ export interface RenderBundleOptions {
280
+ /**
281
+ * A fresh random string for this call alone (16 to 128 letters, digits, '-' or '_'), from the caller's random
282
+ * source: 32 hex digits from `crypto.getRandomValues` will do. It marks where the untrusted content starts and ends.
283
+ */
284
+ boundary: string;
285
+ /** For 'translate': the language to translate into, as a BCP 47 tag ('es', 'pt-BR'). */
286
+ language?: string | null;
287
+ /**
288
+ * Attach post pictures as pictures (sealed-full-scope-v1 2.1 and 2.2). Without it, or when no picture qualifies,
289
+ * every picture goes as its alt text and the stored caption only, and the output is exactly as before.
290
+ */
291
+ pictures?: RenderPictureOptions | null;
292
+ }
293
+ /** A post picture in a bundle that may go to the model as a picture: see `bundlePictures`. */
294
+ export interface BundlePicture {
295
+ /** The id of the item the picture is on. */
296
+ item_id: string;
297
+ /** Where that item sits: the asked-about post, its parent or its quote. A reply's pictures never go as pictures. */
298
+ role: Exclude<BundleItemRole, 'reply'>;
299
+ /** The picture's id in the post payload: find its bytes in your image cache by this. */
300
+ media_id: string;
301
+ /** Which picture of its item it is, from 1, as the content numbers them ("picture 2 alt text"). */
302
+ number: number;
303
+ /** Minds' last child-safety check of it (ISO time, UTC), from the bundle, for your own freshness check. */
304
+ checked_at: string;
305
+ }
306
+ export interface BundlePictureOptions {
307
+ /** Your clock, in milliseconds since 1970 UTC, or a Date. The renderer reads no clock of its own. */
308
+ now: number | Date;
309
+ /**
310
+ * How many post pictures this message still has room for: 3 (the per-message limit, sealed-full-scope-v1 2.2) minus
311
+ * the pictures from the phone it carries (`Math.max(0, 3 - phonePictures)`). A whole number; above 3 counts as 3;
312
+ * absent means 3; below 0 throws. Pass the same value to bundlePictures and renderBundleForModel.
313
+ */
314
+ max?: number | null;
315
+ }
316
+ export interface RenderPictureOptions extends BundlePictureOptions {
317
+ /**
318
+ * The pictures you approved: the list bundlePictures gave you, less any your own checks left out. The renderer
319
+ * attaches only a picture on this list that still qualifies at `now`, so a picture left out here, or one that went
320
+ * stale since, goes as alt text only, and no other picture takes its place. Read `now` again for this call
321
+ * (`Date.now()` after your checks): a later clock can only take pictures away.
322
+ */
323
+ allowed: readonly BundlePicture[];
324
+ /**
325
+ * The picture's bytes from your own image cache: the base64 of a JPEG already prepared on this device (shrunk,
326
+ * re-encoded, metadata stripped; sealed-full-scope-v1 2.2), or null when it has not loaded. Never fetch here: a
327
+ * picture that has not loaded is not sent. Anything but base64 JPEG bytes, or a throw, means that picture goes as alt
328
+ * text only. Asked only for pictures on `allowed`. Prepare first, then check: return exactly the prepared bytes your
329
+ * checks passed for this picture, never a second read of the cache (an entry replaced in between would send a picture
330
+ * nobody checked, and the raw cached file still carries its metadata). The renderer can tell only that the bytes look
331
+ * like a JPEG, not that they were prepared.
332
+ */
333
+ cached: (picture: BundlePicture) => string | null | undefined;
334
+ }
335
+ /** One part of the user message, in the chat-completions content-part shape. */
336
+ export type RenderedUserPart = {
337
+ type: 'text';
338
+ text: string;
339
+ } | {
340
+ type: 'image_url';
341
+ image_url: {
342
+ url: string;
343
+ };
344
+ };
345
+ /**
346
+ * A rendered bundle. Render only a bundle this process built from its own reads: the renderer re-checks every field
347
+ * it writes outside a member line and throws on one that does not fit, but a bundle a client sent has skipped every
348
+ * exclusion rule, so a server never renders one.
349
+ */
350
+ export interface RenderedBundle {
351
+ /** Fixed instructions written by the renderer: trusted, for the system role. */
352
+ system: string;
353
+ /**
354
+ * The delimited content, then the task again: for the user role. Words only: when `pictures` is not empty it still
355
+ * says which pictures are attached but cannot carry them, so send `user_parts` instead.
356
+ */
357
+ user: string;
358
+ /**
359
+ * `system` and `user` as one message, for a path that sends one. Only when `pictures` is empty: it names the
360
+ * attached pictures but cannot carry them.
361
+ */
362
+ text: string;
363
+ /**
364
+ * What the rendered content holds: an answer may cite or link these and nothing else, and only as plain links (see
365
+ * BundleReferences): never render an image the model wrote. Never a picture's address or bytes, attached or not.
366
+ */
367
+ references: BundleReferences;
368
+ /** The pictures attached as image parts, in order: empty unless `pictures` was given and one qualified. */
369
+ pictures: BundlePicture[];
370
+ /**
371
+ * The user message as content parts, for a request that takes pictures: the content's opening line and its text,
372
+ * then each attached picture as an image part (a `data:image/jpeg;base64,` address, never a web address), then the
373
+ * content's closing line and the task again, so the pictures sit inside the delimited block. Its text parts, joined
374
+ * with a line break, are `user`. With no picture attached it is one text part holding `user`.
375
+ */
376
+ user_parts: RenderedUserPart[];
377
+ }
378
+ /** Which fixed card a message calls for. */
379
+ export type FixedCardKind = 'election' | 'crisis';
380
+ /** The languages the matcher reads and the cards are written in. */
381
+ export type FixedCardLanguage = 'en' | 'es';
382
+ export declare const FIXED_CARD_LANGUAGES: readonly FixedCardLanguage[];
383
+ export interface FixedCardOptions {
384
+ /**
385
+ * The member's country, as an ISO 3166-1 alpha-2 code ('US', 'mx'), when the caller knows it: from the profile or
386
+ * the request's edge country header. Unknown or absent gives the generic card, which names no country's line but
387
+ * the US one, labelled as such.
388
+ */
389
+ country?: string | null;
390
+ /**
391
+ * The member's app language, as a BCP 47 tag ('es-MX'). The card is written in the language the message matched
392
+ * in; this decides only when it matched in both, or for `fixedCardFor`, which reads no message.
393
+ */
394
+ locale?: string | null;
395
+ }
396
+ /** A number to call or text, as the card shows it. */
397
+ export interface FixedCardPhone {
398
+ /** Who answers, by the service's own name. */
399
+ name: string;
400
+ /** As the service writes it. */
401
+ number: string;
402
+ /** What a phone dials, for a `tel:` link: digits, with a leading `*` kept. */
403
+ dial: string;
404
+ /** The number to text, or null when the line takes calls only. */
405
+ text: string | null;
406
+ /** Emergency services rather than a helpline. */
407
+ emergency: boolean;
408
+ /** Where the number was checked. */
409
+ sources: string[];
410
+ }
411
+ /** An official site the card links to. */
412
+ export interface FixedCardLink {
413
+ label: string;
414
+ url: string;
415
+ /** Where the address was checked. */
416
+ sources: string[];
417
+ }
418
+ /**
419
+ * A fixed card: short, plain, the same every time for the same country and language, and written by people, not a
420
+ * model. Every number and address in `lines` is also in `phones` or `links`, with its sources.
421
+ */
422
+ export interface FixedCard {
423
+ kind: FixedCardKind;
424
+ /**
425
+ * Stable: 'crisis.US', 'crisis.generic', 'election.MX', 'election.generic'. A crisis card may add '.danger' (danger
426
+ * from someone else or a medical emergency: it leads with the emergency number, the police where that is a separate
427
+ * number) or '.other' (worry about someone else: its line speaks to the helper).
428
+ */
429
+ id: string;
430
+ /** The country the card is for, or null for the generic card. */
431
+ country: string | null;
432
+ language: FixedCardLanguage;
433
+ title: string;
434
+ /** What the card says, in order, one short sentence or two per line. */
435
+ lines: string[];
436
+ phones: FixedCardPhone[];
437
+ links: FixedCardLink[];
438
+ /** The day the numbers and addresses were last checked against their sources (YYYY-MM-DD). */
439
+ as_of: string;
440
+ }
441
+ /** What a message calls for. It names no phrase that matched: the words a member wrote stay with the caller. */
442
+ export interface FixedCardMatch {
443
+ kind: FixedCardKind;
444
+ card: FixedCard;
445
+ }
446
+ /**
447
+ * What the turn does after the card: 'card_only' shows the card instead of a model answer (no model call);
448
+ * 'card_then_answer' shows the card first, then the turn may go on to a model answer.
449
+ */
450
+ export type FixedCardAnswer = 'card_only' | 'card_then_answer';
451
+ /**
452
+ * How a message is sent: Sealed alone, or 'both' (D3: the same prompt to Standard and to Sealed, side by side). 'both'
453
+ * with encrypted-chat messages throws: an encrypted-chat ask is Sealed only (G21).
454
+ */
455
+ export type SealedTurnMode = 'sealed' | 'both';
456
+ /**
457
+ * Someone else's content in a Sealed message, by where it came from:
458
+ * - 'public_post': a post its author made public, as the post in a George ask, an attached post, a quote or a chat's
459
+ * subject (G13d);
460
+ * - 'members_only_post': a post in a group that is not public, in any of those places;
461
+ * - 'chat_messages': messages picked from a chat;
462
+ * - 'encrypted_chat': messages from an encrypted chat (G21). Sealed only: with mode 'both' it throws.
463
+ */
464
+ export type SealedContentKind = 'public_post' | 'members_only_post' | 'chat_messages' | 'encrypted_chat';
465
+ export declare const SEALED_CONTENT_KINDS: readonly SealedContentKind[];
466
+ /**
467
+ * One Sealed message, as the app is about to send it. Choices that send something (search, memory, the chip, the tick,
468
+ * About you, Tinfoil's page-open limit) count only when `true`. The six facts the caps cannot see for themselves
469
+ * (`others_content`, `phone_pictures`, `temporary`, `searched_earlier`, `phone_pictures_earlier`, `earlier_messages`)
470
+ * are required: absent or null throws, and the four flags count as set for any value but `false`. A `mode`,
471
+ * `others_content` or `phone_pictures` this function cannot read throws a TypeError, and so does mode 'both' with
472
+ * 'encrypted_chat' content (G21: Sealed only).
473
+ */
474
+ export interface SealedTurnInput {
475
+ mode: SealedTurnMode;
476
+ /** "Search the web" is on for this chat (the composer's switch). */
477
+ search?: boolean | null;
478
+ /**
479
+ * Someone else's content in this message or anywhere earlier in this chat; a chat's subject counts for every message
480
+ * in it. Required: pass `[]` for a message of the member's own words only.
481
+ */
482
+ others_content: readonly SealedContentKind[];
483
+ /**
484
+ * The chat has earlier messages or answers. Required: pass `false` for the first message. It decides the
485
+ * 'search.question_only' line: a message that searches never carries the chat so far, and the member is told so.
486
+ */
487
+ earlier_messages: boolean;
488
+ /**
489
+ * A message earlier in this chat searched the web, so George's earlier answers may echo a web page. Required: pass
490
+ * `false` when none did. It stops memory suggestions (4.2, gate M3); it does not stop search or memory.
491
+ */
492
+ searched_earlier: boolean;
493
+ /**
494
+ * A message earlier in this chat carried a picture from the phone to Sealed, so George's earlier answers about it,
495
+ * which stay in the history (2.2), may echo words written in it. Required: pass `false` when none did. It stops
496
+ * memory suggestions (4.2); it does not stop search or memory.
497
+ */
498
+ phone_pictures_earlier: boolean;
499
+ /** Sealed memory is on (Settings > George > What George remembers, "On this phone"). Off by default. */
500
+ memory?: boolean | null;
501
+ /**
502
+ * The header's Memory chip is on for this chat. Counts only when `others_content` is empty: memory is off in any
503
+ * ask or chat carrying someone else's content unless the member ticks `memory_tick` (George plan 4.7).
504
+ */
505
+ memory_in_chat?: boolean | null;
506
+ /**
507
+ * The member ticked "Use what George remembers" for this ask or chat (it starts off). The only thing that sends
508
+ * memory, or About you, with someone else's content; ignored when `others_content` is empty.
509
+ */
510
+ memory_tick?: boolean | null;
511
+ /** "Use these" is on for the About you fields ("How George talks to you"). */
512
+ about_you?: boolean | null;
513
+ /**
514
+ * How many pictures from the phone are attached: a whole number from 0 to 3 (a message carries at most 3 pictures in
515
+ * all, sealed-full-scope-v1 2.2, so the composer caps them). Required: pass `0` for none. Absent or null throws, so a
516
+ * caller that forgot it never searches beside a picture or drops one without its line; more than 3 throws.
517
+ */
518
+ phone_pictures: number;
519
+ /** A temporary Sealed chat. Required: pass `false` for a saved chat. */
520
+ temporary: boolean;
521
+ /**
522
+ * Tinfoil limits page opens for Minds' key to addresses from search results or the member's message
523
+ * (sealed-full-scope-v1 3.6, ask 3). Defaults to false. Set it only from a server switch, once Tinfoil confirms.
524
+ */
525
+ page_opens_limited?: boolean | null;
526
+ }
527
+ /**
528
+ * Why something the member turned on is off for this message, as the copy id the app shows (sealed-full-scope-v1
529
+ * 5.1 and 5.2):
530
+ * - 'search.off_mix': it carries memory, About you or a picture from the phone, so it doesn't search;
531
+ * - 'search.off_subject': it carries someone else's public post, and Tinfoil doesn't limit page opens yet (ask 3);
532
+ * - 'search.off_members': it carries a members-only post or picked chat messages, which never search;
533
+ * - 'search.e2ee': it carries encrypted-chat messages, which never search;
534
+ * - 'search.question_only': it searches, so it carries only the question, not the chat so far;
535
+ * - 'both.no_search': a Both message doesn't search;
536
+ * - 'both.no_photo': a Both message doesn't carry a picture from the phone.
537
+ */
538
+ export type SealedTurnReason = 'search.off_mix' | 'search.off_subject' | 'search.off_members' | 'search.e2ee' | 'search.question_only' | 'both.no_search' | 'both.no_photo';
539
+ export declare const SEALED_TURN_REASONS: readonly SealedTurnReason[];
540
+ /**
541
+ * What one Sealed message may carry. Someone else's content in the message itself (the post asked about) always goes.
542
+ * In a Both message it goes to the Standard half too, an ordinary Minds turn (2.6), whose bundle is built with mode
543
+ * 'standard' (that leaves out posts whose authors chose Sealed only). So these caps do not keep a public post, a
544
+ * members-only post or picked chat messages from Standard. Only encrypted-chat content is Sealed only: a Both message
545
+ * with it throws instead of returning caps.
546
+ */
547
+ export interface SealedTurnCaps {
548
+ /** Put `web_search_options`, with both of Tinfoil's filters, in the sealed body, and mark the request as one that may search. */
549
+ search: boolean;
550
+ /** Send the chat's earlier messages and George's earlier answers. Never with a message that searches. */
551
+ history: boolean;
552
+ /** Send the saved facts, in the sealed body (4.3). */
553
+ memory: boolean;
554
+ /** Send the About you fields, in the same system message as the facts. */
555
+ about_you: boolean;
556
+ /**
557
+ * The attached pictures from the phone may go. The picture checks (2.4) still decide each one. Never in Both, and
558
+ * never with encrypted-chat messages (G21 is one message of words; that surface has no Photo item, so no line).
559
+ */
560
+ phone_pictures: boolean;
561
+ /**
562
+ * A suggestion tagged in the answer may be offered for saving (4.2, gate M3). Never from a message that may hold
563
+ * someone else's words: a post, chat messages, this message's search results, a picture from the phone (it may be a
564
+ * screenshot), or an earlier answer built from a search or about a phone picture.
565
+ */
566
+ suggest_memory: boolean;
567
+ /** Copy ids for what the member turned on that this message leaves off, each once, in a fixed order. */
568
+ reasons: SealedTurnReason[];
569
+ }
@@ -0,0 +1,26 @@
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'];
12
+ export const SEALED_CONTENT_KINDS = [
13
+ 'public_post',
14
+ 'members_only_post',
15
+ 'chat_messages',
16
+ 'encrypted_chat',
17
+ ];
18
+ export const SEALED_TURN_REASONS = [
19
+ 'search.off_mix',
20
+ 'search.off_subject',
21
+ 'search.off_members',
22
+ 'search.e2ee',
23
+ 'search.question_only',
24
+ 'both.no_search',
25
+ 'both.no_photo',
26
+ ];
@@ -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;