@recursiv/sdk 0.7.70 → 0.7.71

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
@@ -74,7 +74,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
74
74
  ### AI Agents
75
75
  | Resource | Key methods |
76
76
  |----------|------------|
77
- | `r.agents` | `create`, `update`, `chat`, `chatStream`, `retryChatStream`, `grantProjectAccess`, `conversations` |
77
+ | `r.agents` | `create`, `update`, `chat`, `chatStream`, `retryChatStream`, `grantProjectAccess`, `conversations`, `discover` (the app's agent directory: server search and sort, keyless-readable) |
78
78
  | `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `requestResetCode` and `resetAccount` (reset encrypted chat with an emailed code where `status().accountReset`), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent`, `events`, `acknowledge`, `conversations`, `requestStart` and `cancelStartRequest` (ask the other person to encrypt a chat when they have no device yet: a message with `metadata.kind` `e2ee_start_request`; the live ones are `ConversationDetail.encryption_requests`); device linking by scan: `createLink`, `link`, `scannedLink`, `approveLink`, `completeLink`, `linkProgress`, `cancelLink` (flag-gated E2EE v2 transport; its doorbell is `r.realtime.onE2eeEvent`, which carries a small message's stored ciphertext to the owner's own sockets; link and device changes arrive on `r.realtime.onE2eeLink` and `onE2eeDevices`) |
79
79
  | `r.brain` | `sendMessage` |
80
80
  | `r.commands` | `execute`, `gatePrompt` |
@@ -99,14 +99,16 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
99
99
  | `r.communities` | `create`, `join`, `search`, `members` |
100
100
  | `r.chat` | `send`, `dm`, `createGroup` (`encrypted: true` for an end-to-end encrypted group, 0.7.59), `messages` (cursor paging), `acceptRequest`, `declineRequest`, `leave`, `deleteConversation`; a private group's admin: `addMembers`, `removeMember`, `updateGroup` (0.7.59) |
101
101
  | `r.tags` | `create`, `list` |
102
+ | `r.signals` | `batch` (up to 100 engagement signals, such as views, dwell and hides, for the member's own ranking; `keepalive` for a page-leave flush), `record` (one signal) (0.7.71) |
102
103
  | `r.curator` | `today` (World Today; a world story can carry `story_key` and `discussion`, its count across networks), `storyPosts` / `memberStoryPosts` (one page of a story's posts, keyless or under the member's key), `storyPostPages` (walk every page), `forYou`, `run`, `refreshIfStale`, `seedDeck` |
103
104
  | `r.uploads` | `uploadMedia` — upload post/chat bytes end to end |
105
+ | `r.judgments` | Jev's labels of a post (0.7.71): `get({ kind, id })` (the author and staff get every judgment and the safety verdict; other members the category and the compass band in words; `status: 'unavailable'` is returned, not thrown), keyless `getPublic(projectId, { kind, id })` (the category only), `dispute(judgmentId, { question_key, proposed, note? })` (the author's dispute is an appeal; a repeat returns `existing: true`), staff `review(judgmentId, { outcome, proposed?, note?, apply_to_shared? })` (sent once; 202 `awaiting_reviewer` until a compass label's second reviewer, or an agreeing Jev re-judge; `isJudgmentSharedWordsError` for the 409) |
104
106
  | `r.video` | `entitlement` (may the caller publish video, and how much is left), `createUpload`, `renewUpload` (the creator's fresh TUS credentials for the same video, with `createUpload`'s `resumeGrant`, so a resume past the hour continues it; 0.7.63), `getStatus`, `getPlaybackToken` |
105
107
 
106
108
  ### Platform
107
109
  | Resource | Purpose |
108
110
  |----------|---------|
109
- | `r.settings` | User preferences (including app-scoped content choices), sessions (`revokeOtherSessions` keeps this device), password management, email change (`requestConfirmationCode` for accounts with no password) |
111
+ | `r.settings` | User preferences (including app-scoped content choices), sessions (`revokeOtherSessions` keeps this device), password management, email change (`requestConfirmationCode` for accounts with no password), deactivation (`disableAccount({ password })`, 0.7.71; 404 on an API that does not serve it) |
110
112
  | `r.billing` | Usage tracking, subscriptions, checkout |
111
113
  | `r.appSubscriptions` | Consumer app plans: `checkout`, `status`, `createPortalSession`, `previewTierChange`, `changeTier` |
112
114
  | `r.boosts` | Promoted posts: `create`, `mine`, `balance`, `pause`, `resume`, `cancel`, `recordEvent`, `topup`, `confirmTopup`; admin `listPendingReview`, `review`, `grant` |
@@ -138,6 +140,18 @@ Render candidate `content` as plain text and respect
138
140
  API failures, including routes absent from an older
139
141
  deployment, throw the normal SDK errors; do not present them as an empty queue.
140
142
 
143
+ ### Ingestion health (0.7.71)
144
+
145
+ `r.protocols.ingestionHealth()` reads `GET /protocols/ingestion/health`: counts-only
146
+ health for the key's app, so an operator can see whether each ingestion lane reaches
147
+ done or dies part way without SQL. It needs the same key and authority as the
148
+ candidate reads, but not the `content_intelligence_admin` binding. It sends no query
149
+ (the API rejects one). Per lane: `jobs` counts, the open job (`current`: its
150
+ `position`, `pages_committed`, `consecutive_failures`, `lease_expired`,
151
+ `starved_since`), `last_done_at`, 24-hour `activity` by reason, and `dead_letters`
152
+ by reason and position; plus open or counting `circuits`. A section that does not
153
+ answer reads as null and is named in `unavailable`; errors throw.
154
+
141
155
  ## Configuration
142
156
 
143
157
  ```typescript
@@ -640,7 +654,7 @@ Standalone public publication uses `protocols.prepareNativePost({ clientMutation
640
654
 
641
655
  To delete a post everywhere, link its network copies when you cross-post: pass `crossPostOf: post.id` (the member's own post in this app, from `sdk.posts`) to `prepareNativePost` and to `prepareNativeNostr(id, { action: 'publish', content, crossPostOf })`. Later, `nativePostHistory({ crossPostOf: post.id })` lists exactly that post's copies, each with `crossPostOf` set: delegated items are removed with `prepareNativePostRemoval`/`removeNativePost`, and `type: 'nostr'` items (original notes, which this SDK's history lists; the REST route returns them only with `include=nostr` or `crossPostOf`, so older apps keep the delegated-only shape) with the Nostr signing flow (`prepareNativeNostr(identityId, { action: 'delete', postId: eventId })`). Another member's post, or one from another app, answers `not_found`; copies prepared without `crossPostOf` stay unlinked.
642
656
 
643
- For Bluesky accounts advertising `quote`, prepare the same publication with `quote: { protocol: 'bluesky', nativeId: sourceAtUri }`. The server resolves the exact source CID using each connected account, checks quote permission and Minds safety, and rechecks before writing. Review each delivery’s `quote` alongside the account and comment before submitting. Changed or unavailable sources cannot publish; uncertain deliveries reconcile the original record key. ActivityPub/Nostr quotes are not provided by this method. Public post reads include at most one filtered `quote` with `state: 'available'` and `post`, or `state: 'unavailable'` without source content. Available quotes may include `embeddedReferences`: exact standalone source-reference lines in the unchanged outer `text`. Omit them only when the quoted original is actually visible; retain them for unavailable or locally hidden quotes. Nostr references are decoded and matched by the server, without following relay hints.
657
+ For Bluesky accounts advertising `quote`, prepare the same publication with `quote: { protocol: 'bluesky', nativeId: sourceAtUri }`. The server resolves the exact source CID using each connected account, checks quote permission and Minds safety, and rechecks before writing. Review each delivery’s `quote` alongside the account and comment before submitting. Changed or unavailable sources cannot publish; uncertain deliveries reconcile the original record key. Mastodon accounts advertising `quote` (0.7.71) take `quote: { protocol: 'activitypub', nativeId: statusUri }`: the member's server must create quote posts (Mastodon API version 7, sent as `quoted_status_id`) and allow this account to quote the status, or prepare answers `quote_not_supported` / `reply_not_allowed`, never a plain link. A Nostr quote is `prepareNativeNostr(identityId, { action: 'publish', content, quote: { protocol: 'nostr', nativeId: eventId } })` (accounts advertising `quote`): the event carries NIP-18 `q` and `p` tags and a `nostr:nevent` line after the text, for the member's signer to sign as usual. Public post reads include at most one filtered `quote` with `state: 'available'` and `post`, or `state: 'unavailable'` without source content. Available quotes may include `embeddedReferences`: exact standalone source-reference lines in the unchanged outer `text`. Omit them only when the quoted original is actually visible; retain them for unavailable or locally hidden quotes. Nostr references are decoded and matched by the server, without following relay hints.
644
658
 
645
659
  Topic browsing can request `getPublicTrends({ protocol: "bluesky", includePreviews: true })`. Optional `SocialTrend.preview` is a real image-bearing post from that topic, never a generated illustration. The server samples at most eight posts for each of ten topics, with three concurrent reads and a shared 2.5-second deadline; missing images leave usable text topics. App moderation and user source controls filter previews on every request. Bluesky CDN image attachments, quote-with-media images, and unlabeled link-preview thumbnails are supported; omitted content retains the source link.
646
660
 
@@ -654,8 +668,12 @@ authority and interactive controls. `retryFailed()` explicitly retries unavailab
654
668
  sources; create a new session to refresh exhausted source windows. A topics network
655
669
  (Bluesky) is read one topic page at a time; in a category the catalog's previews are not
656
670
  judged to be in it, so a read walks into the topics at once, and walks past a topic the
657
- provider no longer lists (`trend_unavailable`) or one with nothing in the category, up
658
- to five topic pages a read, so a page reaches a topic that carries posts. A network read
671
+ provider no longer lists (`trend_unavailable`) or one with nothing new, up
672
+ to five topic pages a read, so a page reaches a topic that carries posts. In a category a
673
+ topic page that shows nothing new ends its topic (the provider's cursor is not followed),
674
+ after three such pages in a row (`DISCOVERY_EMPTY_TOPIC_PAGES`) the network's other topics
675
+ are passed over without a call, and the network's source-index stream (`idx:tf:` cursors)
676
+ is started once and ends on its third page in a row with nothing new. A network read
659
677
  that fails for a passing reason (past the 15 s page deadline, a dropped connection, a 429
660
678
  or a 5xx) leaves the network out of that page and the next, named in `retryingSources`
661
679
  until a later page asks it again (from the page after, when its turn comes), up to two
@@ -671,6 +689,51 @@ and says whether the member is personalized yet (`meta.personalized`). With a
671
689
  `category` (the member's first), the session also reads open-network posts from it;
672
690
  without, it is Minds only. It never carries the radar.
673
691
 
692
+ ### Progressive Discover feed
693
+
694
+ `sdk.discovery.createProgressiveFeed(options, settings?)` reads the same feed without
695
+ waiting for its slowest network (a session's page waits for every source, up to 15 s). It
696
+ opens one `createFeed` session per source (Minds with the story radar and suggestions, and
697
+ one per network with `public_trends`), and `next(onPart)` hands each source's page on as a
698
+ `DiscoveryPart` (`{ source, round, modules, composed?, mixed? }`) the moment it lands; the
699
+ promise resolves with the round's summary (`hasMore`, `failedSources`, `retryingSources`,
700
+ `fallover`) and rejects only when every source it read failed. A round waits
701
+ `DISCOVERY_STRAGGLER_MS` (3 s) past its first answer; a source still reading then lands
702
+ later, and `onStatus(listener)` says so. `setNetworks(catalog)` takes in a network the feed
703
+ did not have (a feed opened before the catalog answered) and stops reading one the catalog
704
+ dropped.
705
+
706
+ The first load is held until Minds and one other network have answered, or
707
+ `FIRST_SCREEN_MS` (2.5 s, a member's; pass `firstScreenMs: VISITOR_FIRST_SCREEN_MS`, 0.8 s,
708
+ for a visitor) after the first answer, and arrives as one part with `composed: true`, built
709
+ by `composeFirstScreen`:
710
+
711
+ - it leads with the top's first post when that is a video that can play; else with a post
712
+ that opens on a picture or video (`moduleOpensOnPicture`) from the best
713
+ `FIRST_SCREEN_LEAD_WINDOW` (8) by engagement. A story, a suggestion rail, a video strip,
714
+ a conversation or thread (`isColumnModule`), a sensitive Minds post (`isSensitivePost`)
715
+ or an open-network post that links a porn site (`linksAdultSite`) never leads;
716
+ - the posts then follow by engagement, every network's together (`DiscoveryEngagementMerge`:
717
+ normalised per network, decayed by age, video and pictures first, no network more than
718
+ 8 of any 20 rows or 3 in a row, a Minds member's post at least every sixth row); posts by
719
+ `name@host` accounts in Minds' lane count as ActivityPub's (`mindsPostNetwork`);
720
+ - the row under the lead is the best post from another network while one of the same standing
721
+ is left (`FIRST_SCREEN_NETWORK_ROWS`), so a screen that shows two rows is never one network's;
722
+ - a story goes in as row 3 (`FIRST_SCREEN_STORY_SLOT`) and a rail as row 5
723
+ (`FIRST_SCREEN_RAIL_SLOT`); galleries are pooled into one; nothing is dropped.
724
+
725
+ Later parts over every network carry `mixed: true`: merge them below what the reader has
726
+ seen with `mergeDiscoveryArrival(rows, arriving, reachedIds)`. `top`, `seen`, `seenClusters`
727
+ and `bucket` reach every session; the top is read once per open (one session token), and
728
+ not asked again for five minutes after it answers 404. Settings: `perSource: false` keeps
729
+ a feed that picks its own networks whole (For you: pass
730
+ `{ createFeed: o => sdk.discovery.createPersonalFeed(o) }` to the standalone
731
+ `createProgressiveFeed(factory, options, { perSource: false })`), `firstScreen: false` hands
732
+ every part on as it lands, and `onLanded(source, failed)` reports each landing. A client
733
+ that moves clips out of a composed part recomposes the rest with
734
+ `composeFirstScreen([{ modules }], { keepLead: true })`. `withoutSeenItems` and
735
+ `readFalloverSteps` are the rules the feed applies across sources and to `fallover`.
736
+
674
737
  ### For You: the world by the member's interests
675
738
 
676
739
  `sdk.curator.forYouWorld({ part?, session?, snapshot?, compass? })` reads the For You overview
@@ -716,11 +779,14 @@ its posts as soon as the radar answers, whatever the top does. When it answers,
716
779
  carries `top: { ref, position, block }`, a lane entry also `lane`), and its lanes stand in
717
780
  for the networks' first reads. Minds items the first hot page does not carry are read by id
718
781
  once, within the same 1.5 s (`top.post`, or `GET /posts/:id` without retries). `seen` (Minds post ids and
719
- `<protocol>/<nativeId>` refs the member already saw) leaves those posts out of the hot pages,
720
- and no hot page repeats a top item; a hot page left under five posts walks on, up to three
782
+ `<protocol>/<nativeId>` refs the member already saw) leaves those posts out of the hot pages
783
+ and out of the networks' trend pages, topic previews and topic pages (the top's own lanes
784
+ stay), and no hot page repeats a top item; a hot page left under five posts walks on, up to three
721
785
  more pages, and the feed's `fallover` still reports when the ranking ends. Without impressions
722
- in the top's order, the member's seen top items follow the unseen ones. With no server lead,
723
- `seenClusters` (`Map<id, { n, clicked }>`) skips a story shown three times without an open,
786
+ in the top's order, the member's seen top items follow the unseen ones. An item's
787
+ `top.position` is its place in the whole top, so sessions that each place their own
788
+ network's part never share a place. `seenClusters` (`Map<id, { n, clicked }>`) skips a story
789
+ shown three times without an open, the server's lead included,
724
790
  and a visitor's `bucket` rotates page 0's story among the breaking ones
725
791
  (`pickRadarCluster` is the same rule on its own). With none of these options a session is
726
792
  unchanged.
@@ -743,6 +809,8 @@ a legislature, or a party or politician. Hide politics leaves those out whatever
743
809
  `feeds.top()` carry `political` too (0.7.67), and those reads and `curator.forYouWorld()` leave political stories out
744
810
  under Hide politics.
745
811
 
812
+ Every story names one outlet count (0.7.71): radar stories, World Today stories, category strip stories, feed-top and search stories carry `outlets: { total, mainstream, independent, other }`, the distinct outlets of the articles the story lists (buckets sum to `total`). Show `outlets.total`; `sources.outlet` counts articles, not outlets.
813
+
746
814
  A World Today story's discussion across networks (0.7.69; servers with World Story Volume on): `sdk.curator.today()`
747
815
  gives each world story the server indexed a `story_key` and, when the reader's pool holds a post, a `discussion`:
748
816
  `total`, `people`, `by_network` (`minds`, `bluesky`, `activitypub`, `nostr`), `capped` (the pool reached 1,000: show
package/dist/client.d.ts CHANGED
@@ -33,9 +33,14 @@ export declare class HttpClient {
33
33
  get<T>(path: string, params?: Record<string, unknown>, options?: {
34
34
  retry?: boolean;
35
35
  }): Promise<T>;
36
+ /**
37
+ * `keepalive: true` lets the request outlive the page (a page-leave flush): it is on the wire before this returns,
38
+ * and it is asked once, since no retry can run after the page is gone.
39
+ */
36
40
  post<T>(path: string, body?: unknown, options?: {
37
41
  retry?: boolean;
38
42
  idempotencyKey?: string;
43
+ keepalive?: boolean;
39
44
  }): Promise<T>;
40
45
  put<T>(path: string, body?: unknown, options?: {
41
46
  retry?: boolean;
package/dist/client.js CHANGED
@@ -182,6 +182,10 @@ export class HttpClient {
182
182
  const response = await (options?.retry === false ? this.fetchWithTimeout(url, request) : this.fetchWithRetry(url, request));
183
183
  return this.handleResponse(response);
184
184
  }
185
+ /**
186
+ * `keepalive: true` lets the request outlive the page (a page-leave flush): it is on the wire before this returns,
187
+ * and it is asked once, since no retry can run after the page is gone.
188
+ */
185
189
  async post(path, body, options) {
186
190
  const url = this.buildUrl(path);
187
191
  const request = {
@@ -192,8 +196,10 @@ export class HttpClient {
192
196
  ...(options?.idempotencyKey ? { 'Idempotency-Key': options.idempotencyKey } : {}),
193
197
  },
194
198
  body: body ? JSON.stringify(body) : undefined,
199
+ ...(options?.keepalive ? { keepalive: true } : {}),
195
200
  };
196
- const response = await (options?.retry === false ? this.fetchWithTimeout(url, request) : this.fetchWithRetry(url, request));
201
+ const once = options?.retry === false || options?.keepalive === true;
202
+ const response = await (once ? this.fetchWithTimeout(url, request) : this.fetchWithRetry(url, request));
197
203
  return this.handleResponse(response);
198
204
  }
199
205
  async put(path, body, options) {
package/dist/index.d.ts CHANGED
@@ -59,11 +59,13 @@ import { JobsResource } from './resources/jobs.js';
59
59
  import { CuratorResource } from './resources/curator.js';
60
60
  import { ReportsResource } from './resources/reports.js';
61
61
  import { ModerationResource } from './resources/moderation.js';
62
+ import { JudgmentsResource } from './resources/judgments.js';
62
63
  import { LinkPreviewResource } from './resources/link-preview.js';
63
64
  import { SearchResource } from './resources/search.js';
64
65
  import { BookmarksResource } from './resources/bookmarks.js';
65
66
  import { FeedsResource } from './resources/feeds.js';
66
67
  import { AccountsResource } from './resources/accounts.js';
68
+ import { SignalsResource } from './resources/signals.js';
67
69
  import { RealtimeClient } from './realtime.js';
68
70
  import type { RecursivConfig } from './types.js';
69
71
  export declare class Recursiv {
@@ -129,12 +131,16 @@ export declare class Recursiv {
129
131
  readonly boosts: BoostsResource;
130
132
  readonly reports: ReportsResource;
131
133
  readonly moderation: ModerationResource;
134
+ /** Jev's labels of a post: read them, dispute or appeal one, and (staff) review a dispute. */
135
+ readonly judgments: JudgmentsResource;
132
136
  readonly linkPreview: LinkPreviewResource;
133
137
  /** Blended search: posts, open network, people, groups, topics, stories, related and trending in one answer. */
134
138
  readonly search: SearchResource;
135
139
  /** Synced bookmarks: the member's saved posts, open-network posts and articles in this app, across devices. */
136
140
  readonly bookmarks: BookmarksResource;
137
141
  readonly accounts: AccountsResource;
142
+ /** The member's engagement signals (views, dwell, hides, "More like this") for their own ranking. */
143
+ readonly signals: SignalsResource;
138
144
  readonly realtime: RealtimeClient;
139
145
  constructor(config?: Partial<RecursivConfig>);
140
146
  /**
@@ -160,10 +166,10 @@ export { ChatResource } from './resources/chat.js';
160
166
  export { E2eeResource } from './resources/e2ee.js';
161
167
  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';
162
168
  export { ProjectsResource } from './resources/projects.js';
163
- export { AgentsResource, SUPPORTED_MODELS } from './resources/agents.js';
169
+ export { AgentsResource, SUPPORTED_MODELS, AGENT_DIRECTORY_SORTS, AGENT_DIRECTORY_MAX_LIMIT } from './resources/agents.js';
164
170
  export { EvidenceResource } from './resources/evidence.js';
165
171
  export type { EvidenceTarget, EvalType, CreateEvalInput, SimulateInput, SimulateResult, EvalRunRow, EvalResultRow, EvalRunDetail, EvidenceSummary, EvidenceEvent, EvidenceRecordDetail } from './resources/evidence.js';
166
- export type { AgentMessage, AgentMessageInput, AgentInboxParams, AgentAskInput, AgentAskResult, SupportedModel, SalesLead, SalesLeadListParams, SalesLeadStatus } from './resources/agents.js';
172
+ export type { AgentMessage, AgentMessageInput, AgentInboxParams, AgentAskInput, AgentAskResult, SupportedModel, SalesLead, SalesLeadListParams, SalesLeadStatus, AgentDirectorySort, AgentDirectoryParams, AgentDirectoryEntry, AgentDirectoryMeta, AgentDirectoryPage } from './resources/agents.js';
167
173
  export { TagsResource } from './resources/tags.js';
168
174
  export { SandboxResource } from './resources/sandbox.js';
169
175
  export { ProfilesResource } from './resources/profiles.js';
@@ -199,6 +205,8 @@ export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, C
199
205
  export { STORY_POOL_MAX_POSTS, STORY_POST_NETWORKS, STORY_POSTS_PAGE_MAX } from './resources/curator.js';
200
206
  export { ReportsResource } from './resources/reports.js';
201
207
  export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
208
+ export { JudgmentsResource, isJudgmentSharedWordsError } from './resources/judgments.js';
209
+ 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';
202
210
  export { ModerationResource } from './resources/moderation.js';
203
211
  export type { ModerationTargetType, ModerationActionType, ModerationAppealStatus, ModerationPrinciple, PublicModerationAction, PublicModerationLog, OwnModerationAppeal, OwnModerationAction, OwnModerationLog, ApplyModerationActionInput, AgentModerationMode, AgentModerationGrant, } from './resources/moderation.js';
204
212
  export { LinkPreviewResource } from './resources/link-preview.js';
@@ -209,6 +217,8 @@ export { FEED_TOP_NETWORKS, FeedsResource } from './resources/feeds.js';
209
217
  export type { FeedStoriesParams, FeedStoriesResponse, FeedStory, FeedTopExplain, FeedTopItem, FeedTopMeta, FeedTopNetwork, FeedTopNetworkCard, FeedTopNetworkEntry, FeedTopParams, FeedTopResponse, FeedTopTarget, PublicFeedTopMeta, PublicFeedTopParams, PublicFeedTopResponse, } from './resources/feeds.js';
210
218
  export type { BookmarkCollection, BookmarkCollectionInput, BookmarkCollectionKind, BookmarkCollectionPatch, BookmarkCollectionsList, BookmarkCollectionTarget, BookmarkCollectionVisibility, BookmarkedPost, BookmarkInput, BookmarkItem, BookmarkKey, BookmarkKeyRow, BookmarkKeysPage, BookmarkKind, BookmarkMergeItem, BookmarkMergeResult, BookmarkProtocol, BookmarkRequestOptions, BookmarksPage, FiledBookmark, RemovedBookmarkCollection, SavedBookmark, UnfiledBookmark, UnsavedBookmark, } from './resources/bookmarks.js';
211
219
  export { AccountsResource } from './resources/accounts.js';
220
+ export { SIGNAL_BATCH_MAX, SignalsResource } from './resources/signals.js';
221
+ export type { SignalBatchResult, SignalEventInput, SignalEventType, SignalNotRecordedReason, SignalRecordResult, SignalSendOptions, } from './resources/signals.js';
212
222
  export type { SiblingAccount, SiblingAccountsResponse, SwitchAccountResult, } from './resources/accounts.js';
213
223
  export type { LinkPreview, LinkPreviewResponse, } from './resources/link-preview.js';
214
224
  export type { AgentCapabilityConsentRequirement, AgentCapabilityDataSensitivity, AgentCapabilityDomain, AgentCapabilityExportBoundary, AgentCapabilityRetentionClass, AgentCapabilitySubdomain, AgentCapabilitySubject, AgentCapabilityTrustTier, AgentToolAudience, AgentToolScope, BrowserUseRunTaskInput, ProjectBrainSettings, ProjectToolBundleClassification, ProjectToolBundleKey, ProjectToolPermission, ProjectToolPermissionOverrides, UpdateProjectSettingsInput, } from './resources/project-brain.js';
@@ -223,7 +233,7 @@ export { BoostsResource } from './resources/boosts.js';
223
233
  export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostPlanCredit, BoostRejectionReason, BoostReviewStatus, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, } from './resources/boosts.js';
224
234
  export { ProtocolsResource } from './resources/protocols.js';
225
235
  export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, viralItemHasVideo, } from './resources/protocols.js';
226
- export type { SocialProfile, SocialBridge, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarPosts, SocialRadarStory, SocialRadarStoryArchive, ViralBaseline, ViralItem, ViralItemFields, ViralItemSpread, ViralItemVelocity, ViralLabel, ViralMindsCard, ViralMindsItem, ViralNetwork, ViralOpenProtocol, ViralProtocol, ViralProvider, ViralReadMeta, ViralReadParams, ViralReadResponse, ViralReadStatus, ViralRef, ViralSocialItem, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
236
+ export type { SocialProfile, SocialBridge, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarPosts, SocialRadarStory, SocialRadarStoryArchive, ViralBaseline, ViralItem, ViralItemFields, ViralItemSpread, ViralItemVelocity, ViralLabel, ViralMindsCard, ViralMindsItem, ViralNetwork, ViralOpenProtocol, ViralProtocol, ViralProvider, ViralReadMeta, ViralReadParams, ViralReadResponse, ViralReadStatus, ViralRef, ViralSocialItem, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostQuote, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionActivityReason, ProtocolIngestionCircuitHealth, ProtocolIngestionDeadLetterGroup, ProtocolIngestionHealthPosition, ProtocolIngestionHealthReport, ProtocolIngestionHealthRuntime, ProtocolIngestionHealthSection, ProtocolIngestionLaneActivity, ProtocolIngestionLaneCurrentJob, ProtocolIngestionLaneDeadLetters, ProtocolIngestionLaneHealth, ProtocolIngestionLaneJobs, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
227
237
  export { InboxResource } from './resources/inbox.js';
228
238
  export { NetworkResource } from './resources/network.js';
229
239
  export { SimulatorResource } from './resources/simulator.js';
package/dist/index.js CHANGED
@@ -60,11 +60,13 @@ import { JobsResource } from './resources/jobs.js';
60
60
  import { CuratorResource } from './resources/curator.js';
61
61
  import { ReportsResource } from './resources/reports.js';
62
62
  import { ModerationResource } from './resources/moderation.js';
63
+ import { JudgmentsResource } from './resources/judgments.js';
63
64
  import { LinkPreviewResource } from './resources/link-preview.js';
64
65
  import { SearchResource } from './resources/search.js';
65
66
  import { BookmarksResource } from './resources/bookmarks.js';
66
67
  import { FeedsResource } from './resources/feeds.js';
67
68
  import { AccountsResource } from './resources/accounts.js';
69
+ import { SignalsResource } from './resources/signals.js';
68
70
  import { RealtimeClient } from './realtime.js';
69
71
  export class Recursiv {
70
72
  /** Shared transport, kept so the client name can be set after construction. */
@@ -130,12 +132,16 @@ export class Recursiv {
130
132
  boosts;
131
133
  reports;
132
134
  moderation;
135
+ /** Jev's labels of a post: read them, dispute or appeal one, and (staff) review a dispute. */
136
+ judgments;
133
137
  linkPreview;
134
138
  /** Blended search: posts, open network, people, groups, topics, stories, related and trending in one answer. */
135
139
  search;
136
140
  /** Synced bookmarks: the member's saved posts, open-network posts and articles in this app, across devices. */
137
141
  bookmarks;
138
142
  accounts;
143
+ /** The member's engagement signals (views, dwell, hides, "More like this") for their own ranking. */
144
+ signals;
139
145
  realtime;
140
146
  constructor(config) {
141
147
  const client = new HttpClient(config);
@@ -198,10 +204,12 @@ export class Recursiv {
198
204
  this.boosts = new BoostsResource(client);
199
205
  this.reports = new ReportsResource(client);
200
206
  this.moderation = new ModerationResource(client);
207
+ this.judgments = new JudgmentsResource(client);
201
208
  this.linkPreview = new LinkPreviewResource(client);
202
209
  this.search = new SearchResource(client);
203
210
  this.bookmarks = new BookmarksResource(client);
204
211
  this.accounts = new AccountsResource(client);
212
+ this.signals = new SignalsResource(client);
205
213
  this.realtime = new RealtimeClient(client);
206
214
  this.#http = client;
207
215
  }
@@ -227,7 +235,7 @@ export { CommunitiesResource } from './resources/communities.js';
227
235
  export { ChatResource } from './resources/chat.js';
228
236
  export { E2eeResource } from './resources/e2ee.js';
229
237
  export { ProjectsResource } from './resources/projects.js';
230
- export { AgentsResource, SUPPORTED_MODELS } from './resources/agents.js';
238
+ export { AgentsResource, SUPPORTED_MODELS, AGENT_DIRECTORY_SORTS, AGENT_DIRECTORY_MAX_LIMIT } from './resources/agents.js';
231
239
  export { EvidenceResource } from './resources/evidence.js';
232
240
  export { TagsResource } from './resources/tags.js';
233
241
  export { SandboxResource } from './resources/sandbox.js';
@@ -255,12 +263,14 @@ export { VideoResource } from './resources/video.js';
255
263
  export { AppSubscriptionsResource } from './resources/app-subscriptions.js';
256
264
  export { STORY_POOL_MAX_POSTS, STORY_POST_NETWORKS, STORY_POSTS_PAGE_MAX } from './resources/curator.js';
257
265
  export { ReportsResource } from './resources/reports.js';
266
+ export { JudgmentsResource, isJudgmentSharedWordsError } from './resources/judgments.js';
258
267
  export { ModerationResource } from './resources/moderation.js';
259
268
  export { LinkPreviewResource } from './resources/link-preview.js';
260
269
  export { SEARCH_SECTIONS, SearchResource } from './resources/search.js';
261
270
  export { BOOKMARK_MERGE_MAX_ITEMS, BookmarkCollectionsResource, BookmarksResource } from './resources/bookmarks.js';
262
271
  export { FEED_TOP_NETWORKS, FeedsResource } from './resources/feeds.js';
263
272
  export { AccountsResource } from './resources/accounts.js';
273
+ export { SIGNAL_BATCH_MAX, SignalsResource } from './resources/signals.js';
264
274
  export { OrganizationSettingsResource } from './resources/organization-settings.js';
265
275
  export { ProjectSettingsResource } from './resources/project-settings.js';
266
276
  export { OrganizationSecurityResource } from './resources/organization-security.js';
@@ -91,6 +91,44 @@ export interface AgentAskResult {
91
91
  export interface AgentLeaderboardParams extends PaginationParams {
92
92
  organization_id?: string;
93
93
  }
94
+ /** The orders the agent directory serves (server: features/agents/agentDirectory.ts). */
95
+ export declare const AGENT_DIRECTORY_SORTS: readonly ["popular", "recent", "name"];
96
+ export type AgentDirectorySort = (typeof AGENT_DIRECTORY_SORTS)[number];
97
+ /** The most agents one directory page holds. */
98
+ export declare const AGENT_DIRECTORY_MAX_LIMIT = 50;
99
+ export interface AgentDirectoryParams {
100
+ /** Words to find in an agent's name, handle (with or without its @) or description, case aside. */
101
+ q?: string;
102
+ /** popular (default): official and curated agents, then most followers. recent: newest first. name: A to Z. */
103
+ sort?: AgentDirectorySort;
104
+ /** The previous page's `meta.next_cursor`, for the same sort. */
105
+ cursor?: string;
106
+ /** 1 to 50, default 20. */
107
+ limit?: number;
108
+ }
109
+ /** One agent the app publishes, as the directory lists it. */
110
+ export interface AgentDirectoryEntry {
111
+ id: string;
112
+ name: string;
113
+ username: string | null;
114
+ image: string | null;
115
+ bio: string | null;
116
+ follower_count: number;
117
+ /** An account the operator vouches for, as people rows carry it. */
118
+ verified: boolean;
119
+ created_at: string;
120
+ }
121
+ export interface AgentDirectoryMeta {
122
+ limit: number;
123
+ sort: AgentDirectorySort;
124
+ /** Where the next page starts, or null on the last page. */
125
+ next_cursor: string | null;
126
+ has_more: boolean;
127
+ }
128
+ export interface AgentDirectoryPage {
129
+ data: AgentDirectoryEntry[];
130
+ meta: AgentDirectoryMeta;
131
+ }
94
132
  export type SalesLeadStatus = 'new' | 'contacted' | 'engaged' | 'meeting' | 'won' | 'lost' | 'declined' | 'stale' | 'unsubscribed';
95
133
  export interface SalesLeadListParams {
96
134
  /** Server-side filter preset. Mirrors the agent's own sales_lead_list presets. */
@@ -223,6 +261,16 @@ export declare class AgentsResource {
223
261
  global?: boolean;
224
262
  include_global?: boolean;
225
263
  }): Promise<PaginatedResponse<Agent>>;
264
+ /**
265
+ * Search and sort the agents the app publishes: its Discover Agents page. The server searches (`q` in the name,
266
+ * handle or description) and orders (`popular`, `recent`, `name`); page with `meta.next_cursor`. Never lists a
267
+ * personal agent, a project's Lead or workers, a simulator bot, another company's unpublished agent, an agent its
268
+ * organization keeps private, one that is not public, or one wearing an official account's name (so a page can
269
+ * hold fewer than `limit`; keep paging while `next_cursor` is set). Works keyless for a signed-out visitor: build
270
+ * the client with `new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false })` on the app's API host (the
271
+ * host names the network; public profiles only, 60 reads per IP a minute). With an app key it is that app's list.
272
+ */
273
+ discover(params?: AgentDirectoryParams): Promise<AgentDirectoryPage>;
226
274
  /**
227
275
  * Find-or-create the calling user's personal agent. Platform
228
276
  * primitive for apps that want to give every user an auto-provisioned
@@ -1,5 +1,10 @@
1
1
  import { RecursivError } from '../errors.js';
2
2
  import { parseSSE } from '../lib/sse.js';
3
+ // ── Agent directory (GET /agents/discover) ──
4
+ /** The orders the agent directory serves (server: features/agents/agentDirectory.ts). */
5
+ export const AGENT_DIRECTORY_SORTS = ['popular', 'recent', 'name'];
6
+ /** The most agents one directory page holds. */
7
+ export const AGENT_DIRECTORY_MAX_LIMIT = 50;
3
8
  // ── Supported models ──
4
9
  /**
5
10
  * Model identifiers the Recursiv platform currently accepts for agent
@@ -109,6 +114,23 @@ export class AgentsResource {
109
114
  listDiscoverable(params) {
110
115
  return this.client.get('/agents/discoverable', params);
111
116
  }
117
+ /**
118
+ * Search and sort the agents the app publishes: its Discover Agents page. The server searches (`q` in the name,
119
+ * handle or description) and orders (`popular`, `recent`, `name`); page with `meta.next_cursor`. Never lists a
120
+ * personal agent, a project's Lead or workers, a simulator bot, another company's unpublished agent, an agent its
121
+ * organization keeps private, one that is not public, or one wearing an official account's name (so a page can
122
+ * hold fewer than `limit`; keep paging while `next_cursor` is set). Works keyless for a signed-out visitor: build
123
+ * the client with `new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false })` on the app's API host (the
124
+ * host names the network; public profiles only, 60 reads per IP a minute). With an app key it is that app's list.
125
+ */
126
+ discover(params = {}) {
127
+ return this.client.get('/agents/discover', {
128
+ q: params.q,
129
+ sort: params.sort,
130
+ cursor: params.cursor,
131
+ limit: params.limit,
132
+ });
133
+ }
112
134
  /**
113
135
  * Find-or-create the calling user's personal agent. Platform
114
136
  * primitive for apps that want to give every user an auto-provisioned
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
2
+ import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse, StoryOutlets } from '../types.js';
3
3
  import type { FeedTopNetworkCard, FeedTopTarget } from './feeds.js';
4
4
  import type { SocialPost } from './protocols.js';
5
5
  /** Which surface is asking: Home's For You (default) or Discover's For you. */
@@ -355,9 +355,15 @@ export interface CuratorTodayStory {
355
355
  political?: boolean;
356
356
  /** Outlet links for a world story; null for a Minds story. */
357
357
  sources: CuratorTodaySource[] | null;
358
- /** Distinct outlets behind a world story; null for a Minds story. */
358
+ /**
359
+ * How many outlets reported a world story, by lane (StoryOutlets, 0.7.71): the radar story's own when the story seeded
360
+ * one, so the card names the number its story page, the radar and the News section name. Null for a Minds story;
361
+ * absent from an older server.
362
+ */
363
+ outlets?: StoryOutlets | null;
364
+ /** Distinct outlets behind a world story (`outlets.total` from 0.7.71's servers); null for a Minds story. */
359
365
  outlet_count: number | null;
360
- /** Distinct outlets per lane behind a world story; null for a Minds story. */
366
+ /** Distinct outlets per lane behind a world story (`outlets`' two lanes from 0.7.71's servers); null for a Minds story. */
361
367
  lane_mix: CuratorTodayLaneMix | null;
362
368
  /**
363
369
  * Which lanes carry a world story: 'mainstream', 'independent' (nobody in
@@ -0,0 +1,8 @@
1
+ import type { FalloverStep } from '../types.js';
2
+ /**
3
+ * A feed page's fall-through (the Minds read's `meta.fallover`, or a session page's `fallover`), step by step. A step of a kind this SDK knows (`feed` included, 0.7.60) that carries
4
+ * the fields its kind declares is kept as the server sent it; a step of a kind a later server added, or a malformed
5
+ * one, is left out and never guessed at, so every step a page reports is a FalloverStep. Undefined when the page has
6
+ * no list (a server before the contract, or a feed that is not a category's).
7
+ */
8
+ export declare function readFalloverSteps(value: unknown): FalloverStep[] | undefined;
@@ -0,0 +1,26 @@
1
+ const FALLOVER_VIA = new Set(['personal', 'popular', 'static']);
2
+ const filled = (value) => typeof value === 'string' && value.length > 0;
3
+ /**
4
+ * A feed page's fall-through (the Minds read's `meta.fallover`, or a session page's `fallover`), step by step. A step of a kind this SDK knows (`feed` included, 0.7.60) that carries
5
+ * the fields its kind declares is kept as the server sent it; a step of a kind a later server added, or a malformed
6
+ * one, is left out and never guessed at, so every step a page reports is a FalloverStep. Undefined when the page has
7
+ * no list (a server before the contract, or a feed that is not a category's).
8
+ */
9
+ export function readFalloverSteps(value) {
10
+ if (!Array.isArray(value))
11
+ return undefined;
12
+ return value.filter((raw) => {
13
+ if (!raw || typeof raw !== 'object')
14
+ return false;
15
+ const step = raw;
16
+ switch (step.kind) {
17
+ case 'category_older': return filled(step.category) && filled(step.cursor);
18
+ case 'category_network': return filled(step.category);
19
+ case 'category': return filled(step.category) && typeof step.via === 'string' && FALLOVER_VIA.has(step.via);
20
+ case 'for_you':
21
+ case 'all': return true;
22
+ case 'feed': return filled(step.ref);
23
+ default: return false;
24
+ }
25
+ });
26
+ }
@@ -0,0 +1,30 @@
1
+ import type { DiscoveryModule } from './discovery.js';
2
+ /** The row a story takes when there are posts enough above it: under the lead and one other post. */
3
+ export declare const FIRST_SCREEN_STORY_SLOT = 2;
4
+ /** The row a suggestion rail takes. */
5
+ export declare const FIRST_SCREEN_RAIL_SLOT = 4;
6
+ /** How far down the screen's engagement order a post may be and still lead it for its picture: about one screen. */
7
+ export declare const FIRST_SCREEN_LEAD_WINDOW = 8;
8
+ /** The first row a column of several posts (a conversation, a thread) may take. */
9
+ export declare const FIRST_SCREEN_FEATURE_SLOT = 3;
10
+ /** The first rows that each come from a network no row above them does, while the screen has one: the lead and the row under it. */
11
+ export declare const FIRST_SCREEN_NETWORK_ROWS = 2;
12
+ /**
13
+ * A module that opens on a picture or a video: one post that shows one, or a gallery whose first card does. A column
14
+ * of posts whose picture is further down opens on text, and a video strip is not a post's picture.
15
+ */
16
+ export declare function moduleOpensOnPicture(module: DiscoveryModule): boolean;
17
+ /** The top's first post (the smallest place among the modules), when it is a video that can play: it leads the screen. */
18
+ export declare function discoveryTopVideoLead(modules: readonly DiscoveryModule[]): DiscoveryModule | undefined;
19
+ /**
20
+ * The first screen's modules, from the parts that landed (in landing order), in the order described above. Pure for a
21
+ * given `now` (the time the posts' ages are counted to): the same parts give the same screen.
22
+ * `keepLead`: the parts' first module is the lead already (a composed screen a client recomposes after moving its
23
+ * clips into a strip of its own).
24
+ */
25
+ export declare function composeFirstScreen(parts: ReadonlyArray<{
26
+ modules: readonly DiscoveryModule[];
27
+ }>, { keepLead, now }?: {
28
+ keepLead?: boolean;
29
+ now?: number;
30
+ }): DiscoveryModule[];