@recursiv/sdk 0.7.50 → 0.7.52

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 (44) hide show
  1. package/README.md +69 -12
  2. package/dist/client.js +3 -1
  3. package/dist/index.d.ts +13 -6
  4. package/dist/index.js +5 -1
  5. package/dist/realtime.d.ts +79 -9
  6. package/dist/realtime.js +339 -76
  7. package/dist/resources/admin.d.ts +13 -0
  8. package/dist/resources/agents.d.ts +41 -4
  9. package/dist/resources/agents.js +45 -24
  10. package/dist/resources/app-subscriptions.d.ts +102 -4
  11. package/dist/resources/app-subscriptions.js +43 -3
  12. package/dist/resources/auth.d.ts +198 -2
  13. package/dist/resources/auth.js +397 -3
  14. package/dist/resources/boosts.d.ts +170 -0
  15. package/dist/resources/boosts.js +75 -0
  16. package/dist/resources/chat.d.ts +33 -3
  17. package/dist/resources/chat.js +38 -2
  18. package/dist/resources/communities.d.ts +23 -11
  19. package/dist/resources/communities.js +20 -6
  20. package/dist/resources/curator.d.ts +24 -6
  21. package/dist/resources/curator.js +2 -1
  22. package/dist/resources/deployments.d.ts +8 -3
  23. package/dist/resources/discovery.d.ts +15 -1
  24. package/dist/resources/discovery.js +29 -1
  25. package/dist/resources/invite-codes-admin.d.ts +13 -0
  26. package/dist/resources/invite-codes.d.ts +10 -0
  27. package/dist/resources/invite-codes.js +6 -6
  28. package/dist/resources/notifications.d.ts +74 -5
  29. package/dist/resources/notifications.js +38 -8
  30. package/dist/resources/posts.d.ts +28 -5
  31. package/dist/resources/posts.js +13 -4
  32. package/dist/resources/profiles.d.ts +45 -9
  33. package/dist/resources/profiles.js +54 -3
  34. package/dist/resources/protocols.d.ts +195 -14
  35. package/dist/resources/protocols.js +77 -12
  36. package/dist/resources/settings.d.ts +25 -3
  37. package/dist/resources/settings.js +28 -2
  38. package/dist/resources/video.d.ts +19 -0
  39. package/dist/resources/video.js +8 -0
  40. package/dist/resources/wallet.d.ts +41 -1
  41. package/dist/resources/wallet.js +8 -0
  42. package/dist/types.d.ts +217 -6
  43. package/llm.md +68 -20
  44. package/package.json +2 -2
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Boost: pay to promote your own post into the For You feed. CPM campaigns
3
+ * funded from a prepaid balance with an append-only ledger. Top-ups run on the
4
+ * app's own Stripe account and need a project-scoped key.
5
+ */
6
+ export class BoostsResource {
7
+ client;
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ /**
12
+ * Create a campaign for one of your own posts. Debits `budget_micros` from your balance
13
+ * (402 `insufficient_balance` when it cannot). Starts `pending` review.
14
+ * Not retried automatically, so a slow response cannot fund two campaigns.
15
+ */
16
+ create(input) {
17
+ return this.client.post('/boosts', input, { retry: false });
18
+ }
19
+ /** Your campaigns, newest first, with spend and impressions. */
20
+ mine(params) {
21
+ return this.client.get('/boosts/mine', params);
22
+ }
23
+ /**
24
+ * Your balance, recent ledger, the CPM floor, the top-up limits, and the boost credit your
25
+ * plan on this app adds each paid period (`meta.plan_credit`, a `grant` ledger row per period).
26
+ */
27
+ balance() {
28
+ return this.client.get('/boosts/balance');
29
+ }
30
+ /** Pause one of your active campaigns. */
31
+ pause(campaignId) {
32
+ return this.client.post(`/boosts/${encodeURIComponent(campaignId)}/pause`, {});
33
+ }
34
+ /** Resume one of your paused campaigns. */
35
+ resume(campaignId) {
36
+ return this.client.post(`/boosts/${encodeURIComponent(campaignId)}/resume`, {});
37
+ }
38
+ /** Cancel a campaign and refund its unspent budget to your balance (`meta.refunded_micros`). */
39
+ cancel(campaignId) {
40
+ return this.client.post(`/boosts/${encodeURIComponent(campaignId)}/cancel`, {});
41
+ }
42
+ /** Report an impression or click on a boosted feed entry. Idempotent per viewer per UTC day. */
43
+ recordEvent(campaignId, kind) {
44
+ return this.client.post(`/boosts/${encodeURIComponent(campaignId)}/events`, { kind });
45
+ }
46
+ /**
47
+ * Buy boost credit: opens a one-time Stripe Checkout for `amount_cents` (USD) on the app's
48
+ * own Stripe account. The balance is credited once the payment succeeds. Limits are in
49
+ * `balance().meta.topup`. Requires a project-scoped key with `billing:write`.
50
+ * `return_url` keeps its own query and fragment; `topup=success&session_id=<id>` or
51
+ * `topup=cancelled` is added to its query (to the fragment's query for a hash route).
52
+ */
53
+ topup(input) {
54
+ return this.client.post('/boosts/topup', input, { retry: false });
55
+ }
56
+ /**
57
+ * Credit a paid top-up when the member returns from Checkout. Safe to call repeatedly
58
+ * and alongside the webhook: a session credits the balance once.
59
+ */
60
+ confirmTopup(sessionId) {
61
+ return this.client.post('/boosts/topup/confirm', { session_id: sessionId });
62
+ }
63
+ /** Campaigns waiting for review, oldest first (admin). */
64
+ listPendingReview(params) {
65
+ return this.client.get('/boosts/review', params);
66
+ }
67
+ /** Approve or reject a campaign (admin). Rejecting refunds the unspent budget. */
68
+ review(campaignId, input) {
69
+ return this.client.post(`/boosts/review/${encodeURIComponent(campaignId)}`, input);
70
+ }
71
+ /** Grant balance to a user (admin). */
72
+ grant(input) {
73
+ return this.client.post('/boosts/grant', input, { retry: false });
74
+ }
75
+ }
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { PaginatedResponse, SingleResponse, SuccessResponse, Conversation, ConversationDetail, Message, SendMessageInput, SentMessage, DmInput, DmResult, PaginationParams } from '../types.js';
2
+ import type { PaginatedResponse, SingleResponse, SuccessResponse, Conversation, ConversationDetail, Message, SendMessageInput, SentMessage, DmInput, DmResult, PaginationParams, ConversationRequestResult, LeaveConversationResult } from '../types.js';
3
3
  export declare class ChatResource {
4
4
  private client;
5
5
  constructor(client: HttpClient);
@@ -11,7 +11,14 @@ export declare class ChatResource {
11
11
  communityConversation(communityId: string): Promise<SingleResponse<Conversation>>;
12
12
  /** Get a conversation by ID */
13
13
  conversation(id: string): Promise<SingleResponse<ConversationDetail>>;
14
- /** List messages in a conversation (newest first) */
14
+ /**
15
+ * List messages in a conversation (newest first).
16
+ *
17
+ * For scrolling back, pass the previous page's `meta.next_cursor` as
18
+ * `cursor` rather than growing `offset`: an offset counts from the newest
19
+ * message and shifts every time one arrives, a cursor does not. The cursor
20
+ * is bound to this conversation.
21
+ */
15
22
  messages(conversationId: string, params?: PaginationParams): Promise<PaginatedResponse<Message>>;
16
23
  /** Send a message to a conversation */
17
24
  send(input: SendMessageInput): Promise<SingleResponse<SentMessage>>;
@@ -35,8 +42,31 @@ export declare class ChatResource {
35
42
  name: string;
36
43
  member_ids: string[];
37
44
  }): Promise<SingleResponse<ConversationDetail>>;
38
- /** Delete a conversation (only the creator can delete) */
45
+ /**
46
+ * Delete a conversation for every member. Allowed when the conversation's
47
+ * `can_delete` is true: the caller started it, or every other member is an
48
+ * agent. Otherwise the API answers 403 `not_creator`; use `leave()` where
49
+ * `can_leave` is true.
50
+ */
39
51
  deleteConversation(id: string): Promise<SuccessResponse>;
52
+ /**
53
+ * Remove the caller from a conversation (a group, or a room whose other
54
+ * members are all agents). The last person to leave removes the room. A
55
+ * one-on-one with another person cannot be left (409 `dm_leave_unsupported`);
56
+ * check `can_leave` on the conversation first.
57
+ *
58
+ * This changes membership. It is not `realtime.leaveConversation()`, which
59
+ * only stops listening for live events.
60
+ */
61
+ leave(conversationId: string): Promise<SingleResponse<LeaveConversationResult>>;
62
+ /** Accept a message request (`request_status: 'pending'`). Idempotent; also reverses a decline. */
63
+ acceptRequest(conversationId: string): Promise<SingleResponse<ConversationRequestResult>>;
64
+ /**
65
+ * Decline a message request. The conversation leaves the caller's lists and
66
+ * stops notifying them; the sender is not told. Only a pending request can
67
+ * be declined (409 `not_a_request` otherwise). Idempotent.
68
+ */
69
+ declineRequest(conversationId: string): Promise<SingleResponse<ConversationRequestResult>>;
40
70
  /** React to a message (toggles: adds if not present, removes if already reacted) */
41
71
  reactToMessage(messageId: string, input: {
42
72
  type: string;
@@ -15,7 +15,14 @@ export class ChatResource {
15
15
  conversation(id) {
16
16
  return this.client.get(`/chat/conversations/${id}`);
17
17
  }
18
- /** List messages in a conversation (newest first) */
18
+ /**
19
+ * List messages in a conversation (newest first).
20
+ *
21
+ * For scrolling back, pass the previous page's `meta.next_cursor` as
22
+ * `cursor` rather than growing `offset`: an offset counts from the newest
23
+ * message and shifts every time one arrives, a cursor does not. The cursor
24
+ * is bound to this conversation.
25
+ */
19
26
  messages(conversationId, params) {
20
27
  return this.client.get(`/chat/conversations/${conversationId}/messages`, params);
21
28
  }
@@ -41,10 +48,39 @@ export class ChatResource {
41
48
  createGroup(input) {
42
49
  return this.client.post('/chat/conversations/group', input);
43
50
  }
44
- /** Delete a conversation (only the creator can delete) */
51
+ /**
52
+ * Delete a conversation for every member. Allowed when the conversation's
53
+ * `can_delete` is true: the caller started it, or every other member is an
54
+ * agent. Otherwise the API answers 403 `not_creator`; use `leave()` where
55
+ * `can_leave` is true.
56
+ */
45
57
  deleteConversation(id) {
46
58
  return this.client.delete(`/chat/conversations/${id}`);
47
59
  }
60
+ /**
61
+ * Remove the caller from a conversation (a group, or a room whose other
62
+ * members are all agents). The last person to leave removes the room. A
63
+ * one-on-one with another person cannot be left (409 `dm_leave_unsupported`);
64
+ * check `can_leave` on the conversation first.
65
+ *
66
+ * This changes membership. It is not `realtime.leaveConversation()`, which
67
+ * only stops listening for live events.
68
+ */
69
+ leave(conversationId) {
70
+ return this.client.post(`/chat/conversations/${conversationId}/leave`);
71
+ }
72
+ /** Accept a message request (`request_status: 'pending'`). Idempotent; also reverses a decline. */
73
+ acceptRequest(conversationId) {
74
+ return this.client.post(`/chat/conversations/${conversationId}/accept`);
75
+ }
76
+ /**
77
+ * Decline a message request. The conversation leaves the caller's lists and
78
+ * stops notifying them; the sender is not told. Only a pending request can
79
+ * be declined (409 `not_a_request` otherwise). Idempotent.
80
+ */
81
+ declineRequest(conversationId) {
82
+ return this.client.post(`/chat/conversations/${conversationId}/decline`);
83
+ }
48
84
  /** React to a message (toggles: adds if not present, removes if already reacted) */
49
85
  reactToMessage(messageId, input) {
50
86
  return this.client.post(`/chat/messages/${messageId}/react`, input);
@@ -1,19 +1,31 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { Community, CommunityBannedMember, CommunityDetail, CommunityMember, CreateCommunityInput, CreatedCommunity, JoinResult, LeaveResult, ListCommunitiesParams, PaginatedResponse, PaginationParams, SingleResponse } from '../types.js';
2
+ import type { Community, CommunityBannedMember, CommunityCard, CommunityDetail, CommunityMember, CommunityPeopleParams, CreateCommunityInput, CreatedCommunity, JoinResult, LeaveResult, ListCommunitiesParams, PaginatedResponse, PaginationParams, RelatedCommunity, SingleResponse } from '../types.js';
3
3
  export declare class CommunitiesResource {
4
4
  private client;
5
5
  constructor(client: HttpClient);
6
- /** List public communities, or only the authenticated caller's memberships. */
6
+ /**
7
+ * List public communities, or only the authenticated caller's memberships
8
+ * (`member: true`). `q` filters either list by name or description.
9
+ */
7
10
  list(params?: ListCommunitiesParams): Promise<PaginatedResponse<Community>>;
8
- /** Get a community by ID */
9
- get(id: string): Promise<SingleResponse<CommunityDetail>>;
10
- /** List members of a community */
11
- /** Groups related to this one, ranked by shared members; the caller's own groups are left out. */
11
+ /**
12
+ * Search communities by name or description, server-side. Name matches rank
13
+ * first. Pass `{ member: true }` to search only the caller's own groups.
14
+ */
15
+ search(query: string, params?: ListCommunitiesParams): Promise<PaginatedResponse<Community>>;
16
+ /**
17
+ * Get a community by ID or slug. A private group the caller is not in comes
18
+ * back as a `CommunityCard` (`limited: true`) instead of a 404; a hidden one
19
+ * still 404s.
20
+ */
21
+ get(id: string): Promise<SingleResponse<CommunityDetail | CommunityCard>>;
22
+ /** Public groups related to this one, ranked by shared members; each row carries the caller's `is_member`. */
12
23
  related(id: string, params?: {
13
24
  limit?: number;
14
- }): Promise<PaginatedResponse<Community>>;
15
- members(id: string, params?: PaginationParams): Promise<PaginatedResponse<CommunityMember>>;
16
- /** Create a new community */
25
+ }): Promise<PaginatedResponse<RelatedCommunity>>;
26
+ /** List members of a community. `q` searches by name or username. */
27
+ members(id: string, params?: CommunityPeopleParams): Promise<PaginatedResponse<CommunityMember>>;
28
+ /** Create a new community. Omit slug for a server-generated collision-resistant slug. */
17
29
  create(input: CreateCommunityInput): Promise<SingleResponse<CreatedCommunity>>;
18
30
  /** Join a community */
19
31
  join(id: string): Promise<SingleResponse<JoinResult>>;
@@ -28,8 +40,8 @@ export declare class CommunitiesResource {
28
40
  delete(id: string): Promise<SingleResponse<{
29
41
  deleted: boolean;
30
42
  }>>;
31
- /** People waiting to join a private group. Moderator or higher. */
32
- requests(id: string, params?: PaginationParams): Promise<PaginatedResponse<CommunityMember>>;
43
+ /** People waiting to join a private group. Moderator or higher. `q` searches by name or username. */
44
+ requests(id: string, params?: CommunityPeopleParams): Promise<PaginatedResponse<CommunityMember>>;
33
45
  /** Active bans an admin can review and lift. Admin or higher. */
34
46
  bans(id: string, params?: PaginationParams): Promise<PaginatedResponse<CommunityBannedMember>>;
35
47
  /** Let a pending member in. Admin or higher. */
@@ -3,23 +3,37 @@ export class CommunitiesResource {
3
3
  constructor(client) {
4
4
  this.client = client;
5
5
  }
6
- /** List public communities, or only the authenticated caller's memberships. */
6
+ /**
7
+ * List public communities, or only the authenticated caller's memberships
8
+ * (`member: true`). `q` filters either list by name or description.
9
+ */
7
10
  list(params) {
8
11
  return this.client.get('/communities', params);
9
12
  }
10
- /** Get a community by ID */
13
+ /**
14
+ * Search communities by name or description, server-side. Name matches rank
15
+ * first. Pass `{ member: true }` to search only the caller's own groups.
16
+ */
17
+ search(query, params) {
18
+ return this.client.get('/communities', { ...params, q: query });
19
+ }
20
+ /**
21
+ * Get a community by ID or slug. A private group the caller is not in comes
22
+ * back as a `CommunityCard` (`limited: true`) instead of a 404; a hidden one
23
+ * still 404s.
24
+ */
11
25
  get(id) {
12
26
  return this.client.get(`/communities/${id}`);
13
27
  }
14
- /** List members of a community */
15
- /** Groups related to this one, ranked by shared members; the caller's own groups are left out. */
28
+ /** Public groups related to this one, ranked by shared members; each row carries the caller's `is_member`. */
16
29
  related(id, params) {
17
30
  return this.client.get(`/communities/${encodeURIComponent(id)}/related`, params);
18
31
  }
32
+ /** List members of a community. `q` searches by name or username. */
19
33
  members(id, params) {
20
34
  return this.client.get(`/communities/${id}/members`, params);
21
35
  }
22
- /** Create a new community */
36
+ /** Create a new community. Omit slug for a server-generated collision-resistant slug. */
23
37
  create(input) {
24
38
  return this.client.post('/communities', input);
25
39
  }
@@ -40,7 +54,7 @@ export class CommunitiesResource {
40
54
  return this.client.delete(`/communities/${id}`);
41
55
  }
42
56
  // ── Running the group: the tools an owner, admin or moderator has ────────
43
- /** People waiting to join a private group. Moderator or higher. */
57
+ /** People waiting to join a private group. Moderator or higher. `q` searches by name or username. */
44
58
  requests(id, params) {
45
59
  return this.client.get(`/communities/${id}/requests`, params);
46
60
  }
@@ -1,5 +1,23 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { PaginatedResponse, Post, SingleResponse } from '../types.js';
2
+ import type { DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
3
+ /** Which surface is asking: Home's For You (default) or Discover's For you. */
4
+ export type ForYouSurface = 'home' | 'discover';
5
+ export type ForYouParams = {
6
+ limit?: number;
7
+ offset?: number;
8
+ /** The previous page's `meta.next_cursor`: pages a stored ranking, so posts read meanwhile never shift the next page. */
9
+ cursor?: string;
10
+ /** Adds `meta.discovery` (layouts) and the public safety floor; limit <= 25. */
11
+ discovery?: boolean;
12
+ /** `discover` ranks for Discover: fresher, beyond the member's follows, with their category order. */
13
+ surface?: ForYouSurface;
14
+ };
15
+ export interface ForYouMeta extends PaginationMeta {
16
+ discovery?: DiscoveryLayout;
17
+ next_cursor?: string;
18
+ /** Whether the ranker can personalize for this member yet (3 follows or 3 upvoted authors). */
19
+ personalized?: boolean;
20
+ }
3
21
  /** RSS / Atom source. Default — `type` may be omitted for back-compat. */
4
22
  export interface CuratorRssSource {
5
23
  type?: 'rss';
@@ -250,10 +268,10 @@ export declare class CuratorResource {
250
268
  * existing posts for the signed-in user (engagement + recency + social +
251
269
  * affinity, no model calls). Returns posts in the same shape as
252
270
  * `sdk.posts.list()`, so any feed UI renders it unchanged. Requires a
253
- * project-scoped key (the feed is per-app).
271
+ * project-scoped key (the feed is per-app). Servers that predate `cursor`,
272
+ * `discovery` and `surface` ignore them.
254
273
  */
255
- forYou(params?: {
256
- limit?: number;
257
- offset?: number;
258
- }): Promise<PaginatedResponse<Post>>;
274
+ forYou(params?: ForYouParams): Promise<PaginatedResponse<Post> & {
275
+ meta: ForYouMeta;
276
+ }>;
259
277
  }
@@ -47,7 +47,8 @@ export class CuratorResource {
47
47
  * existing posts for the signed-in user (engagement + recency + social +
48
48
  * affinity, no model calls). Returns posts in the same shape as
49
49
  * `sdk.posts.list()`, so any feed UI renders it unchanged. Requires a
50
- * project-scoped key (the feed is per-app).
50
+ * project-scoped key (the feed is per-app). Servers that predate `cursor`,
51
+ * `discovery` and `surface` ignore them.
51
52
  */
52
53
  forYou(params = {}) {
53
54
  return this.client.get('/curator/foryou', params);
@@ -47,15 +47,20 @@ export interface PrepareProtocolServersInput {
47
47
  * hostnames must be allow-listed. Default `staging`.
48
48
  */
49
49
  target?: 'staging' | 'production';
50
+ /**
51
+ * `activitypub` is the staging GoToSocial and is required for staging. A
52
+ * production stack is the Bluesky PDS and the Nostr relay only (ActivityPub is
53
+ * Fedify inside the API), and a production request that names it is refused.
54
+ */
50
55
  hosts: {
51
56
  pds: string;
52
- activitypub: string;
57
+ activitypub?: string;
53
58
  nostr: string;
54
59
  };
55
- /** Immutable upstream image references including @sha256 digest. */
60
+ /** Immutable upstream image references including @sha256 digest; `activitypub` as for `hosts`. */
56
61
  images: {
57
62
  pds: string;
58
- activitypub: string;
63
+ activitypub?: string;
59
64
  nostr: string;
60
65
  };
61
66
  /** A staging PLC, or https://plc.directory for a production PDS whose operator set HOSTED_PDS_PRODUCTION_PLC=true. */
@@ -1,4 +1,5 @@
1
1
  import type { DiscoveryLayout, Post } from '../types.js';
2
+ import type { CuratorResource } from './curator.js';
2
3
  import type { DiscoveryLearning } from './discovery-learning.js';
3
4
  import type { PostsResource } from './posts.js';
4
5
  import type { ProtocolsResource, SocialNetwork, SocialPost, SocialRadar, SocialRadarPosts } from './protocols.js';
@@ -201,7 +202,20 @@ export declare class DiscoveryFeedSession {
201
202
  export declare class DiscoveryResource {
202
203
  private posts;
203
204
  private protocols;
204
- constructor(posts: PostsResource, protocols: ProtocolsResource);
205
+ private curator?;
206
+ constructor(posts: PostsResource, protocols: ProtocolsResource, curator?: Pick<CuratorResource, "forYou"> | undefined);
207
+ /**
208
+ * A For you Discover feed: the member's For You ranking (asked for Discover's surface, with layouts and a
209
+ * snapshot cursor) in a session's Minds slot. With a category, the session also reads open-network posts
210
+ * from that category (the member's top one); without, it is Minds only. Never the radar. The page-0
211
+ * arrangement and when to use which are the app's choice.
212
+ */
213
+ createPersonalFeed(options: {
214
+ networks: SocialNetwork[];
215
+ learning?: DiscoveryLearning;
216
+ recommend?: DiscoveryRecommendationSources;
217
+ category?: string;
218
+ }): DiscoveryFeedSession;
205
219
  createFeed(options: {
206
220
  networks: SocialNetwork[];
207
221
  category?: string;
@@ -160,6 +160,9 @@ export class DiscoveryFeedSession {
160
160
  return this.pending;
161
161
  }
162
162
  async read(stream) {
163
+ // `discovery` asks only for the layout judgment (and, for Minds posts, the public-only floor). The
164
+ // server's safety floor does not depend on it, so a reader that drops it to skip layout work (a clip
165
+ // pool) is screened exactly like this session.
163
166
  const params = { limit: 25, ...(this.category ? { category: this.category } : {}), discovery: true };
164
167
  if (stream.protocol === 'minds') {
165
168
  // Discover leads with what is moving now: the REST hot order (engagement
@@ -462,9 +465,34 @@ export class DiscoveryFeedSession {
462
465
  export class DiscoveryResource {
463
466
  posts;
464
467
  protocols;
465
- constructor(posts, protocols) {
468
+ curator;
469
+ constructor(posts, protocols, curator) {
466
470
  this.posts = posts;
467
471
  this.protocols = protocols;
472
+ this.curator = curator;
473
+ }
474
+ /**
475
+ * A For you Discover feed: the member's For You ranking (asked for Discover's surface, with layouts and a
476
+ * snapshot cursor) in a session's Minds slot. With a category, the session also reads open-network posts
477
+ * from that category (the member's top one); without, it is Minds only. Never the radar. The page-0
478
+ * arrangement and when to use which are the app's choice.
479
+ */
480
+ createPersonalFeed(options) {
481
+ const curator = this.curator;
482
+ if (!curator)
483
+ throw new Error('createPersonalFeed needs the curator resource');
484
+ const list = {
485
+ // The session's `sort` and `category` are Discover's hot-order knobs; the ranker ignores them.
486
+ list: async (params) => {
487
+ const response = await curator.forYou({ limit: params?.limit, offset: params?.offset, cursor: params?.cursor, discovery: true, surface: 'discover' });
488
+ // A server that predates the cursor says has_more: false on a partial ranked page although its
489
+ // continuation serves the next offset. A cursor-aware server always says `personalized`, and its
490
+ // has_more is the end of the feed.
491
+ const legacy = typeof response.meta.personalized !== 'boolean';
492
+ return { ...response, meta: { ...response.meta, has_more: response.meta.has_more || (legacy && response.data.length > 0) } };
493
+ },
494
+ };
495
+ return new DiscoveryFeedSession(list, this.protocols, options.category ? options.networks : [], options.category, options.category ? 'all' : 'minds', options.learning, options.recommend);
468
496
  }
469
497
  createFeed(options) {
470
498
  // The radar rides along by default; an older server answers 404 and the session simply shows no story module.
@@ -18,12 +18,25 @@ export interface InviteCodeStats {
18
18
  used: number;
19
19
  expired: number;
20
20
  revoked: number;
21
+ /** @deprecated Use `total`. */
22
+ total_codes: number;
23
+ /** @deprecated Use `active`. */
24
+ active_codes: number;
25
+ /** @deprecated Use `used`. */
26
+ used_codes: number;
27
+ /** @deprecated Use `expired`. */
28
+ expired_codes: number;
29
+ /** @deprecated Use `revoked`. */
30
+ revoked_codes: number;
21
31
  };
22
32
  waitlist: Record<string, number>;
23
33
  current_wave: {
24
34
  id: string;
25
35
  wave: number;
26
36
  name: string;
37
+ max_codes: number;
38
+ invites_per_user: number;
39
+ reward_tokens: number;
27
40
  is_active: boolean;
28
41
  } | null;
29
42
  }
@@ -90,10 +90,20 @@ export interface BulkGenerateResult {
90
90
  }
91
91
  export interface InviteStats {
92
92
  codes: {
93
+ total: number;
94
+ active: number;
95
+ used: number;
96
+ expired: number;
97
+ revoked: number;
98
+ /** @deprecated Use `total`. Retained for legacy admin clients. */
93
99
  total_codes: number;
100
+ /** @deprecated Use `active`. Retained for legacy admin clients. */
94
101
  active_codes: number;
102
+ /** @deprecated Use `used`. Retained for legacy admin clients. */
95
103
  used_codes: number;
104
+ /** @deprecated Use `expired`. Retained for legacy admin clients. */
96
105
  expired_codes: number;
106
+ /** @deprecated Use `revoked`. Retained for legacy admin clients. */
97
107
  revoked_codes: number;
98
108
  };
99
109
  waitlist: Record<string, number>;
@@ -43,26 +43,26 @@ export class InviteCodesResource {
43
43
  // ── Admin Methods ──
44
44
  /** Admin: Initialize wave configs for the network */
45
45
  initializeWaves() {
46
- return this.client.post('/invite-codes/admin/initialize-waves');
46
+ return this.client.post('/invite-codes-admin/waves/initialize');
47
47
  }
48
48
  /** Admin: Get all wave configs */
49
49
  getWaves() {
50
- return this.client.get('/invite-codes/admin/waves');
50
+ return this.client.get('/invite-codes-admin/waves');
51
51
  }
52
52
  /** Admin: Activate a specific wave */
53
53
  activateWave(wave) {
54
- return this.client.post('/invite-codes/admin/activate-wave', { wave });
54
+ return this.client.post('/invite-codes-admin/waves/activate', { wave });
55
55
  }
56
56
  /** Admin: Bulk generate invite codes */
57
57
  bulkGenerate(input) {
58
- return this.client.post('/invite-codes/admin/bulk-generate', input);
58
+ return this.client.post('/invite-codes-admin/bulk-generate', input);
59
59
  }
60
60
  /** Admin: Get invite system stats */
61
61
  getStats() {
62
- return this.client.get('/invite-codes/admin/stats');
62
+ return this.client.get('/invite-codes-admin/stats');
63
63
  }
64
64
  /** Admin: Get waitlist entries */
65
65
  getWaitlist(params) {
66
- return this.client.get('/invite-codes/admin/waitlist', params);
66
+ return this.client.get('/invite-codes-admin/waitlist', params);
67
67
  }
68
68
  }
@@ -1,5 +1,35 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { RegisterTokenInput, SuccessResponse, PaginationParams } from '../types.js';
2
+ import type { RegisterTokenInput, SuccessResponse, SingleResponse, PaginationParams } from '../types.js';
3
+ /** Who did the thing a notification is about. */
4
+ export interface NotificationActor {
5
+ id: string;
6
+ name: string | null;
7
+ username: string | null;
8
+ image: string | null;
9
+ is_ai: boolean | null;
10
+ }
11
+ /**
12
+ * The post a notification references, as it reads NOW, with the viewer's own
13
+ * state so inline Upvote and Repost can start in the right position. `null`
14
+ * when the post is gone or no longer visible to the viewer.
15
+ */
16
+ export interface NotificationPostPreview {
17
+ id: string;
18
+ excerpt: string | null;
19
+ media_url: string | null;
20
+ media_type: string | null;
21
+ reactions_count?: number;
22
+ score?: number;
23
+ upvote_count?: number;
24
+ downvote_count?: number;
25
+ /** The viewer's own reaction on this post ('upvote', 'downvote', ...), if any. */
26
+ user_reaction?: string | null;
27
+ reply_count?: number;
28
+ unique_commenters?: number;
29
+ reposts_count?: number;
30
+ /** Id of the viewer's own repost of this post; delete it to undo the repost. */
31
+ reposted_by_viewer_id?: string | null;
32
+ }
3
33
  export interface Notification {
4
34
  id: string;
5
35
  target_type: string;
@@ -11,8 +41,22 @@ export interface Notification {
11
41
  status: 'unread' | 'read' | 'archived';
12
42
  created_at: string;
13
43
  read_at: string | null;
44
+ /** For `post_reaction` rows: which reaction ('upvote', 'downvote', ...). Null otherwise or on older rows. */
45
+ reaction_type?: string | null;
46
+ actor?: NotificationActor | null;
47
+ post_preview?: NotificationPostPreview | null;
14
48
  }
15
49
  export type NotificationCategory = 'all' | 'social' | 'system';
50
+ export type NotificationScopeParams = {
51
+ /** Which kind of notification to count. Defaults to `all`. Same meaning as in `list`. */
52
+ category?: NotificationCategory;
53
+ /** Scope to a specific organization, as `list` does. */
54
+ organization_id?: string;
55
+ };
56
+ export interface NotificationUnreadCount {
57
+ count: number;
58
+ category: NotificationCategory;
59
+ }
16
60
  export interface ListNotificationsParams extends PaginationParams {
17
61
  status?: 'unread' | 'read' | 'all';
18
62
  cursor?: string;
@@ -22,8 +66,10 @@ export interface ListNotificationsParams extends PaginationParams {
22
66
  * Which kind of notification to return. Defaults to `all`.
23
67
  *
24
68
  * `social` is what a person did to you (follows, replies, reactions,
25
- * reposts, mentions, chat, community invites/joins). `system` is everything
26
- * operational (agent, task, deploy, billing, security).
69
+ * reposts, mentions, community invites, joins and join requests, moderation
70
+ * decisions). `system` is everything operational (agent, task, deploy,
71
+ * billing, security). Direct messages are in neither: they live in the chat
72
+ * inbox. `all` (the default) lists every row that has not been archived.
27
73
  *
28
74
  * Ask for it here rather than filtering the page you get back: the server
29
75
  * applies the limit AFTER the category, so a client that filters afterwards
@@ -43,10 +89,33 @@ export declare class NotificationsResource {
43
89
  constructor(client: HttpClient);
44
90
  /** List notifications for the authenticated user */
45
91
  list(params?: ListNotificationsParams): Promise<NotificationListResponse>;
92
+ /**
93
+ * Count unread notifications in a category, straight from the database.
94
+ * Use this for badges: `list` returns at most 50 rows, so a badge built from
95
+ * a page can only ever say "50+".
96
+ *
97
+ * @example
98
+ * const { data } = await r.notifications.unreadCount({ category: 'social' });
99
+ * setBadge(data.count);
100
+ */
101
+ unreadCount(params?: NotificationScopeParams): Promise<SingleResponse<NotificationUnreadCount>>;
46
102
  /** Mark a single notification as read */
47
103
  markAsRead(notificationId: string): Promise<SuccessResponse>;
48
- /** Mark all notifications as read */
49
- markAllAsRead(category?: NotificationCategory): Promise<SuccessResponse>;
104
+ /**
105
+ * Dismiss a notification (swipe to remove). It stops appearing in `list`
106
+ * and stops counting as unread; the row is kept, not deleted. Dismissing an
107
+ * id that is already dismissed, or not yours, succeeds and changes nothing.
108
+ */
109
+ archive(notificationId: string): Promise<SuccessResponse>;
110
+ /**
111
+ * Mark every unread notification in a scope as read. Pass the same scope the
112
+ * list was read with, so a client only clears what it could show.
113
+ *
114
+ * @example
115
+ * await r.notifications.markAllAsRead('social');
116
+ * await r.notifications.markAllAsRead({ category: 'social', organization_id: orgId });
117
+ */
118
+ markAllAsRead(scope?: NotificationCategory | NotificationScopeParams): Promise<SuccessResponse>;
50
119
  /** Register a push notification token for the current user */
51
120
  registerToken(input: RegisterTokenInput): Promise<SuccessResponse>;
52
121
  /** Unregister a push notification token (e.g., on logout) */