@recursiv/sdk 0.7.65 → 0.7.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -670,6 +670,25 @@ and says whether the member is personalized yet (`meta.personalized`). With a
670
670
  `category` (the member's first), the session also reads open-network posts from it;
671
671
  without, it is Minds only. It never carries the radar.
672
672
 
673
+ ### For You: the world by the member's interests
674
+
675
+ `sdk.curator.forYouWorld({ part?, session?, snapshot?, compass? })` reads the For You overview
676
+ for the signed-in member, with their own key of the app. `stories` are world stories (Today,
677
+ the radar and the viral read) matched to the member's interests with breadth: at least four
678
+ themes and two breaking or general stories in the first eight, at most two per theme, and a
679
+ story shown three times without an open rests for a week. `items` are the member's For You
680
+ picks interleaved with the most engaging posts of their top interests (half or more on their
681
+ interests, no interest over 40%, videos weighted up), and `blocks` lay the page out like All:
682
+ `stories`, `lead_video`, `clips`, `posts`, `people`, `groups`, `topics`, `viral` and
683
+ `categories`, never the same type twice in a row, each naming the refs its views should send.
684
+ Every story and item carries `reasons` (`interest:<category>`, `followed:@<handle>`, `breaking`,
685
+ `general`, `for_you`, `engaging`, `breadth`). `part: 'stories'` and `part: 'items'` read the
686
+ two halves on their own, so an app can draw each as it lands; a part=items answer keeps the
687
+ stories rail as a `deferred` block. `meta.interests` are category ids for chips (never put them
688
+ in a URL), `meta.partial` names a source that missed its deadline, and `meta.foryou_cursor`
689
+ continues the same ranking with `curator.forYou({ cursor })`. 404 while the app's operator has
690
+ it off; a 503 `for_you_world_unavailable` carries Retry-After.
691
+
673
692
  ### The top of a feed
674
693
 
675
694
  `sdk.feeds.top({ ref, limit?, session?, compass? })` answers the top of a feed for the
package/dist/index.d.ts CHANGED
@@ -195,7 +195,7 @@ export { VideoResource } from './resources/video.js';
195
195
  export type { VideoUploadCreds, CreateVideoUploadResult, VideoStatusInfo, VideoPlaybackToken, VideoEntitlement, } from './resources/video.js';
196
196
  export { AppSubscriptionsResource } from './resources/app-subscriptions.js';
197
197
  export type { AppSubscriptionBoostCredit, AppSubscriptionCheckoutResult, AppSubscriptionPortalResult, AppSubscriptionStatusResult, AppSubscriptionTierChangePreview, AppSubscriptionTierChangeResult, } from './resources/app-subscriptions.js';
198
- export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, ForYouMeta, ForYouParams, ForYouSurface, } from './resources/curator.js';
198
+ export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, ForYouMeta, ForYouParams, ForYouSurface, ForYouWorldBlock, ForYouWorldBlockType, ForYouWorldCategoryShortcut, ForYouWorldItem, ForYouWorldMeta, ForYouWorldParams, ForYouWorldPart, ForYouWorldResponse, ForYouWorldStory, ForYouWorldStoryTarget, ForYouWorldViral, } from './resources/curator.js';
199
199
  export { ReportsResource } from './resources/reports.js';
200
200
  export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
201
201
  export { ModerationResource } from './resources/moderation.js';
@@ -1,5 +1,6 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
2
+ import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
3
+ import type { FeedTopNetworkCard, FeedTopTarget } from './feeds.js';
3
4
  /** Which surface is asking: Home's For You (default) or Discover's For you. */
4
5
  export type ForYouSurface = 'home' | 'discover';
5
6
  export type ForYouParams = {
@@ -22,6 +23,158 @@ export interface ForYouMeta extends PaginationMeta {
22
23
  /** Whether the ranker can personalize for this member yet (3 follows or 3 upvoted authors). */
23
24
  personalized?: boolean;
24
25
  }
26
+ /** Which half to read: the stories rail, the items, or both (the default). The app can read the two at once and draw each as it lands. */
27
+ export type ForYouWorldPart = 'stories' | 'items' | 'all';
28
+ export interface ForYouWorldParams {
29
+ part?: ForYouWorldPart;
30
+ /** Opaque per app session ([A-Za-z0-9_-], up to 64): the same token keeps one session's order stable across reads. */
31
+ session?: string;
32
+ /** A For You snapshot nonce the app holds (`meta.snapshot` of a previous answer): reused while the server holds it. */
33
+ snapshot?: string;
34
+ /** A Politics choice, as `posts.list` takes it. */
35
+ compass?: string;
36
+ }
37
+ /** Where a story opens in the app: a radar story by its cluster id, or a World Today story the radar has none for by its Today id. */
38
+ export type ForYouWorldStoryTarget = {
39
+ kind: 'story';
40
+ id: string;
41
+ } | {
42
+ kind: 'today_story';
43
+ id: string;
44
+ };
45
+ /** One world story: a radar story (with Today's headline when Today seeded it), a Today story, or a viral post. */
46
+ export interface ForYouWorldStory {
47
+ /** What an impression of it names: 'cluster:<id>', 'today:<id>' or the post's ref. Send it with `block: 'top'` on a visible view. */
48
+ ref: string;
49
+ id: string;
50
+ source: 'today' | 'radar' | 'viral';
51
+ target: FeedTopTarget | ForYouWorldStoryTarget;
52
+ label: string;
53
+ kind: 'breaking' | 'viral' | 'story';
54
+ breaking: boolean;
55
+ /** A front-page or general-news story: one of those shown whatever the member's interests. */
56
+ general: boolean;
57
+ theme: string | null;
58
+ categories: string[];
59
+ /** How widely it is told: posts per source kind, and the outlets that ran it (Today), when known. */
60
+ counts: {
61
+ sources: Record<string, number>;
62
+ outlets: number | null;
63
+ };
64
+ media: {
65
+ images: number;
66
+ videos: number;
67
+ };
68
+ freshness: {
69
+ newestAt: string | null;
70
+ };
71
+ position: number;
72
+ /** Why it is here: `interest:<category>`, `breaking`, `general`, `viral`, `breadth` or `trending`. */
73
+ reasons: string[];
74
+ explain: {
75
+ score: number;
76
+ interest: number;
77
+ rank: number;
78
+ impression: 'fresh' | 'demoted';
79
+ };
80
+ }
81
+ /** One item: a Minds post (`ref` is its id) or an open-network post (`ref` is '<protocol>/<nativeId>'). */
82
+ export interface ForYouWorldItem {
83
+ ref: string;
84
+ kind: 'post' | 'network_post';
85
+ authorId: string;
86
+ target: FeedTopTarget;
87
+ /** Reading order through the blocks. */
88
+ position: number;
89
+ /** 'top' for the first 10: send `block: 'top'` and the position with a visible view of one. */
90
+ block: 'top' | 'below';
91
+ media: 'video' | 'image' | 'none';
92
+ topic: string | null;
93
+ /** Why it is here: `interest:<category>`, `followed:@<handle>`, `for_you`, `engaging` or `breadth`. */
94
+ reasons: string[];
95
+ explain: {
96
+ score: number;
97
+ source: 'foryou' | 'category' | 'all';
98
+ label: 'safe' | 'open';
99
+ impression: 'fresh' | 'demoted';
100
+ engagement: {
101
+ source: string;
102
+ rate: number;
103
+ };
104
+ };
105
+ }
106
+ /** A viral post in the viral block. `card` is an open-network post's card (no URL on its network); null for a Minds post, read by id. */
107
+ export interface ForYouWorldViral {
108
+ ref: string;
109
+ kind: 'post' | 'network_post';
110
+ protocol: string;
111
+ target: FeedTopTarget;
112
+ label: string;
113
+ category: string | null;
114
+ media: 'video' | 'image' | null;
115
+ rank: number;
116
+ velocity: {
117
+ perHour1h: number;
118
+ z: number;
119
+ };
120
+ card: FeedTopNetworkCard | null;
121
+ }
122
+ export interface ForYouWorldCategoryShortcut {
123
+ id: string;
124
+ name: string;
125
+ reason: 'interest' | 'breadth';
126
+ target: {
127
+ kind: 'category';
128
+ id: string;
129
+ };
130
+ }
131
+ /** The page's components, laid out like All: no type follows itself. */
132
+ export type ForYouWorldBlockType = 'stories' | 'lead_video' | 'clips' | 'posts' | 'people' | 'groups' | 'topics' | 'categories' | 'viral';
133
+ export interface ForYouWorldBlock {
134
+ id: string;
135
+ type: ForYouWorldBlockType;
136
+ /** What it shows, in order: item refs (lead_video, clips, posts), story refs, entity keys, category ids or viral refs. */
137
+ refs: string[];
138
+ /** The stories rail of a part=items answer: fill it from your own part=stories read. */
139
+ deferred?: true;
140
+ /** people, groups and topics: the recommender's suggestions (three showings without a tap rest one). */
141
+ entities?: Array<Record<string, unknown> & {
142
+ kind: string;
143
+ key: string;
144
+ }>;
145
+ categories?: ForYouWorldCategoryShortcut[];
146
+ viral?: ForYouWorldViral[];
147
+ }
148
+ export interface ForYouWorldMeta {
149
+ part: ForYouWorldPart;
150
+ flag: {
151
+ world: 'on';
152
+ impressions: 'on' | 'off' | 'unavailable';
153
+ };
154
+ /** Fewer than 3 follows and no signals yet: a breadth-only overview. */
155
+ cold_start: boolean;
156
+ personalized: boolean | null;
157
+ /** The member's top interests as browse category ids, for chips. Never put them in a URL. */
158
+ interests: string[];
159
+ /**
160
+ * Sources left out because they missed their deadline or failed. `consent` is the Bluesky consent read: when it is
161
+ * named, Bluesky posts from the category pools and the viral read were left out of this answer.
162
+ */
163
+ partial: string[];
164
+ safety: 'safe' | 'open' | null;
165
+ /** The For You snapshot the items came from; `foryou_cursor` continues it on `curator.forYou({ cursor })`. */
166
+ snapshot: string | null;
167
+ foryou_cursor: string | null;
168
+ short: boolean;
169
+ compass_filter?: CompassFilterMeta;
170
+ }
171
+ /** GET /curator/foryou/world: `stories` unless part=items, `items` and `blocks` unless part=stories. */
172
+ export interface ForYouWorldResponse {
173
+ stories?: ForYouWorldStory[];
174
+ items?: ForYouWorldItem[];
175
+ blocks?: ForYouWorldBlock[];
176
+ meta: ForYouWorldMeta;
177
+ }
25
178
  /** RSS / Atom source. Default — `type` may be omitted for back-compat. */
26
179
  export interface CuratorRssSource {
27
180
  type?: 'rss';
@@ -278,4 +431,13 @@ export declare class CuratorResource {
278
431
  forYou(params?: ForYouParams): Promise<PaginatedResponse<Post> & {
279
432
  meta: ForYouMeta;
280
433
  }>;
434
+ /**
435
+ * For You as an overview of the world by the member's interests, read with their own key of the app: world stories
436
+ * (interest-matched, with breadth), then a page of typed blocks laid out like All (a lead video, clip strips, posts,
437
+ * people and groups to follow, trending topics, viral posts, category shortcuts). `part` reads one half on its own.
438
+ * 404 until the app's operator turns it on; a 503 `for_you_world_unavailable` carries Retry-After. Always private.
439
+ */
440
+ forYouWorld(params?: ForYouWorldParams, options?: {
441
+ retry?: boolean;
442
+ }): Promise<ForYouWorldResponse>;
281
443
  }
@@ -57,4 +57,13 @@ export class CuratorResource {
57
57
  const { retry, ...query } = params;
58
58
  return retry === false ? this.client.get('/curator/foryou', query, { retry: false }) : this.client.get('/curator/foryou', query);
59
59
  }
60
+ /**
61
+ * For You as an overview of the world by the member's interests, read with their own key of the app: world stories
62
+ * (interest-matched, with breadth), then a page of typed blocks laid out like All (a lead video, clip strips, posts,
63
+ * people and groups to follow, trending topics, viral posts, category shortcuts). `part` reads one half on its own.
64
+ * 404 until the app's operator turns it on; a 503 `for_you_world_unavailable` carries Retry-After. Always private.
65
+ */
66
+ forYouWorld(params = {}, options) {
67
+ return this.client.get('/curator/foryou/world', { part: params.part, session: params.session, snapshot: params.snapshot, compass: params.compass }, options);
68
+ }
60
69
  }
@@ -3,7 +3,10 @@ import type { PaginatedResponse, SingleResponse, SuccessResponse, Profile, Profi
3
3
  export declare class ProfilesResource {
4
4
  private client;
5
5
  constructor(client: HttpClient);
6
- /** List users in the network */
6
+ /**
7
+ * List users in the network, newest first. Automated accounts are left out (a personal agent stays, `is_ai`), and
8
+ * so is an account wearing an official account's name; a page can come back shorter than `limit`.
9
+ */
7
10
  list(params?: {
8
11
  limit?: number;
9
12
  offset?: number;
@@ -3,7 +3,10 @@ export class ProfilesResource {
3
3
  constructor(client) {
4
4
  this.client = client;
5
5
  }
6
- /** List users in the network */
6
+ /**
7
+ * List users in the network, newest first. Automated accounts are left out (a personal agent stays, `is_ai`), and
8
+ * so is an account wearing an official account's name; a page can come back shorter than `limit`.
9
+ */
7
10
  list(params) {
8
11
  return this.client.get('/profiles', params);
9
12
  }
@@ -1043,11 +1043,10 @@ export interface SocialPost {
1043
1043
  };
1044
1044
  /**
1045
1045
  * Present, and only then, when the ActivityPub author opted out of indexing (`noindex`,
1046
- * `indexable: false`, or #nobot, #noindex or #noai in their bio). The keyless trends, trend-feed and
1047
- * radar reads leave such posts out, and no post served from the source index is one. The keyless
1048
- * author-feed and conversation reads (a profile or a post someone opened) and member reads of a
1049
- * provider window still return them with this mark, so an app should never re-share or promote one
1050
- * to signed-out visitors.
1046
+ * `indexable: false`, or #nobot, #noindex or #noai in their bio). The trends, trend-feed and radar
1047
+ * reads leave such posts out, keyless and for a member alike, and no post served from the source index
1048
+ * is one. The author-feed and conversation reads (a profile or a post someone opened), keyless or a
1049
+ * member's, still return them with this mark, so an app should never re-share or promote one.
1051
1050
  */
1052
1051
  authorOptOut?: true;
1053
1052
  /** A bridge (Bridgy Fed, Mostr, a NIP-48 proxy tag) made this post from one on another network (SocialBridge). Present only on a copy read live. */
package/llm.md CHANGED
@@ -639,6 +639,18 @@ await r.bookmarks.unsave(key) // or uns
639
639
 
640
640
  Newest saved first. Pages can be short while they skip posts the member can no longer read: page on `meta.next_cursor`, not length. Needs the member's own app key (else 403 `project_binding_required`). 503 `bookmarks_unavailable` means the server has no synced bookmarks yet: keep saves on the device (pass `{ retry: false }` to the probe so the 503 is not retried).
641
641
 
642
+ ### r.curator.forYouWorld — For You as the world by the member's interests
643
+
644
+ ```typescript
645
+ const head = r.curator.forYouWorld({ part: 'stories', session }) // member's own app key; read the two halves at once
646
+ const page = await r.curator.forYouWorld({ part: 'items', session, snapshot }) // items + blocks; snapshot = a previous meta.snapshot
647
+ // stories: { ref, id, source: today|radar|viral, target, label, kind, breaking, general, theme, counts, reasons, ... }
648
+ // items: { ref, kind, target, position, block: 'top' | 'below', media, topic, reasons, explain }
649
+ // blocks: [{ id, type: stories|lead_video|clips|posts|people|groups|topics|viral|categories, refs, deferred?, entities?, categories?, viral? }]
650
+ ```
651
+
652
+ 404 while the app's operator has it off; always private (`Cache-Control: private, no-store`). A story's or item's `target` is in the app (a radar story by cluster id, a Today story as `today_story`, a post id, `/social/post/<protocol>/<id>`), never a URL out. Send `block: 'top'` with a visible view of a story in the rail or one of the first 10 items: three such views without an open rest it for a week. `meta.partial` names a source that missed its deadline; `meta.interests` are category ids for chips, never for a URL; `meta.foryou_cursor` continues on `r.curator.forYou({ cursor })`.
653
+
642
654
  ### r.feeds — The top of a feed (member and visitor)
643
655
 
644
656
  ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recursiv/sdk",
3
- "version": "0.7.65",
3
+ "version": "0.7.66",
4
4
  "description": "TypeScript SDK for the Recursiv API",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",