@recursiv/sdk 0.7.58 → 0.7.62
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 +32 -9
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +12 -0
- package/dist/feed-definition.d.ts +129 -0
- package/dist/feed-definition.js +46 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +3 -2
- package/dist/realtime.d.ts +11 -1
- package/dist/realtime.js +14 -1
- package/dist/resources/agents.d.ts +70 -0
- package/dist/resources/agents.js +10 -0
- package/dist/resources/chat.d.ts +40 -6
- package/dist/resources/chat.js +41 -2
- package/dist/resources/curator.d.ts +5 -1
- package/dist/resources/curator.js +6 -2
- package/dist/resources/discovery.d.ts +119 -1
- package/dist/resources/discovery.js +140 -17
- package/dist/resources/e2ee.d.ts +87 -2
- package/dist/resources/e2ee.js +31 -2
- package/dist/resources/email.d.ts +27 -3
- package/dist/resources/email.js +18 -3
- package/dist/resources/env-vars.d.ts +25 -0
- package/dist/resources/env-vars.js +8 -0
- package/dist/resources/moderation.d.ts +22 -0
- package/dist/resources/moderation.js +16 -0
- package/dist/resources/posts.d.ts +1 -1
- package/dist/resources/posts.js +6 -2
- package/dist/resources/protocols.d.ts +230 -4
- package/dist/resources/protocols.js +66 -5
- package/dist/types.d.ts +137 -0
- package/llm.md +27 -9
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
|
-
import type { DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse } from '../types.js';
|
|
2
|
+
import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse } from '../types.js';
|
|
3
3
|
/** Canonical protocol operation names returned by the adapter registry. */
|
|
4
4
|
export declare const PROTOCOL_CAPABILITY_OPERATIONS: readonly ["discover", "stream", "backfill", "search", "resolveIdentity", "fetchProfile", "fetchThread", "publish", "reply", "react", "repost", "follow", "delete", "health", "checkpoint"];
|
|
5
5
|
export declare const PROTOCOL_CAPABILITY_STATUSES: readonly ["supported", "planned", "unsupported"];
|
|
@@ -906,7 +906,8 @@ export interface SocialNetwork {
|
|
|
906
906
|
url: string;
|
|
907
907
|
};
|
|
908
908
|
coverage: string;
|
|
909
|
-
|
|
909
|
+
/** `compass_filter`: the Politics filter is served to this viewer (an app offers it only then); listed on every network. */
|
|
910
|
+
capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed' | 'conversation_read' | 'public_trends' | 'trend_feed' | 'popular_accounts' | 'post_search' | 'compass_filter'>;
|
|
910
911
|
profileSearchPagination: boolean;
|
|
911
912
|
postSearchAccountRequired?: boolean;
|
|
912
913
|
/** Categories available from the current app configuration. */
|
|
@@ -930,8 +931,22 @@ export interface SocialProfile {
|
|
|
930
931
|
postsCount: number | null;
|
|
931
932
|
/** Nostr only: the NIP-05 name its domain has confirmed for this key ("lyn@primal.net"); `handle` stays the key. */
|
|
932
933
|
nip05?: string;
|
|
934
|
+
/** A bridge (Bridgy Fed, Mostr) made this account from one on another network (SocialBridge). Present only on a bridged account. */
|
|
935
|
+
bridge?: SocialBridge;
|
|
933
936
|
subscription: SocialSubscription | null;
|
|
934
937
|
}
|
|
938
|
+
/**
|
|
939
|
+
* A post or account a bridge copied from another network: Bridgy Fed (`*.brid.gy`), Mostr (`mostr.pub`), or a Nostr
|
|
940
|
+
* event's NIP-48 `proxy` tag naming no bridge the server knows (`proxy`). `originId` names the original, normalised (a
|
|
941
|
+
* Bluesky at-uri or DID, an ActivityPub object URL or `user@host`, a Nostr event id or key, a web URL), when the bridge
|
|
942
|
+
* says. Where the server reads both, feeds, search and the radar already fold a copy into its original; an app that
|
|
943
|
+
* merges several networks' pages itself can fold the same way: a copy whose original is shown is the same post.
|
|
944
|
+
*/
|
|
945
|
+
export interface SocialBridge {
|
|
946
|
+
via: 'bridgy_fed' | 'mostr' | 'proxy';
|
|
947
|
+
protocol: 'bluesky' | 'activitypub' | 'nostr' | 'web';
|
|
948
|
+
originId?: string;
|
|
949
|
+
}
|
|
935
950
|
export interface SocialSubscription {
|
|
936
951
|
id: string;
|
|
937
952
|
protocol: string;
|
|
@@ -1028,6 +1043,8 @@ export interface SocialPost {
|
|
|
1028
1043
|
* to signed-out visitors.
|
|
1029
1044
|
*/
|
|
1030
1045
|
authorOptOut?: true;
|
|
1046
|
+
/** 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. */
|
|
1047
|
+
bridge?: SocialBridge;
|
|
1031
1048
|
}
|
|
1032
1049
|
export interface SocialPostSearch {
|
|
1033
1050
|
items: SocialPost[];
|
|
@@ -1103,7 +1120,12 @@ export type SocialTrends = SocialTrendProvenance & ({
|
|
|
1103
1120
|
safetyFilter?: {
|
|
1104
1121
|
withheld: number;
|
|
1105
1122
|
};
|
|
1106
|
-
})
|
|
1123
|
+
}) & {
|
|
1124
|
+
/** Present when `getPublicTrends` named a filtering `compass`: what Hide politics did to this page (counts only). */
|
|
1125
|
+
meta?: {
|
|
1126
|
+
compass_filter?: CompassFilterMeta;
|
|
1127
|
+
};
|
|
1128
|
+
};
|
|
1107
1129
|
export interface SocialTrendFeed extends SocialTrendProvenance {
|
|
1108
1130
|
trend: SocialTrend;
|
|
1109
1131
|
items: SocialPost[];
|
|
@@ -1229,10 +1251,20 @@ export interface SocialRadar {
|
|
|
1229
1251
|
selected: string;
|
|
1230
1252
|
status: 'complete' | 'partial';
|
|
1231
1253
|
};
|
|
1254
|
+
/** Present when the read named a filtering `compass`: what Hide politics did to the stories listed (counts only). */
|
|
1255
|
+
meta?: {
|
|
1256
|
+
compass_filter?: CompassFilterMeta;
|
|
1257
|
+
};
|
|
1232
1258
|
}
|
|
1233
1259
|
export interface SocialRadarParams {
|
|
1234
1260
|
category?: string;
|
|
1235
1261
|
limit?: number;
|
|
1262
|
+
/**
|
|
1263
|
+
* A Politics choice. Hide politics (`hide`) leaves out the stories whose theme is politics, and the ones not themed yet
|
|
1264
|
+
* or themed `mixed` (a story has no compass placement, so it fails closed); the stories after them fill the list.
|
|
1265
|
+
* A side or Mix is answered unfiltered, marked `unavailable`; the keyless `getSharedRadar` takes only `all` or `hide`.
|
|
1266
|
+
*/
|
|
1267
|
+
compass?: string;
|
|
1236
1268
|
}
|
|
1237
1269
|
/** One radar story's social member posts, in the cluster's member order. Empty once the story leaves the snapshot. */
|
|
1238
1270
|
export interface SocialRadarPosts {
|
|
@@ -1290,6 +1322,182 @@ export interface SocialRadarStory {
|
|
|
1290
1322
|
*/
|
|
1291
1323
|
editionStory?: string;
|
|
1292
1324
|
}
|
|
1325
|
+
/**
|
|
1326
|
+
* The Viral read (`getPublicViral`, `getSharedViral`): what is moving fastest on Bluesky, the Fediverse, Nostr and
|
|
1327
|
+
* Minds right now, each measured against its own network's pace, from a snapshot the server's rank tick rebuilds about
|
|
1328
|
+
* every five minutes. A read never reaches a provider or the model. These types mirror the server's
|
|
1329
|
+
* ViralSnapshotContract; a contract test compares them key by key.
|
|
1330
|
+
*/
|
|
1331
|
+
export type ViralNetwork = 'all' | 'minds' | 'bluesky' | 'activitypub' | 'nostr';
|
|
1332
|
+
export type ViralProtocol = 'bluesky' | 'activitypub' | 'nostr' | 'minds';
|
|
1333
|
+
/** The networks whose posts the read serves as a `SocialPost`. */
|
|
1334
|
+
export type ViralOpenProtocol = 'bluesky' | 'activitypub' | 'nostr';
|
|
1335
|
+
/**
|
|
1336
|
+
* `viral`: a measured rate well above its network's baseline (z of 1 or more) with two or more kinds of engagement;
|
|
1337
|
+
* `rising`: a measured rate speeding up (accel of 0.7 or more) on a post under six hours old; `hot`: everything else,
|
|
1338
|
+
* including every post without a measured rate.
|
|
1339
|
+
*/
|
|
1340
|
+
export type ViralLabel = 'viral' | 'rising' | 'hot';
|
|
1341
|
+
/**
|
|
1342
|
+
* `ok`: a build served. `building`: no build yet. `screening`: served, but fewer than 30% of the top 200 posts have
|
|
1343
|
+
* a safety verdict yet. `off`: the read is not enabled for this app (or, on `getSharedViral` only, the API predates it):
|
|
1344
|
+
* no items, and nothing to retry until it is turned on.
|
|
1345
|
+
*/
|
|
1346
|
+
export type ViralReadStatus = 'ok' | 'building' | 'screening' | 'off';
|
|
1347
|
+
/** Every parameter is optional. The server rejects any other parameter with a 400, so only these travel. */
|
|
1348
|
+
export interface ViralReadParams {
|
|
1349
|
+
/** Default `all`: every network, with a per-network quota so no one network fills the page. */
|
|
1350
|
+
network?: ViralNetwork;
|
|
1351
|
+
/** A Discover category id: only posts judged into it. */
|
|
1352
|
+
category?: string;
|
|
1353
|
+
/**
|
|
1354
|
+
* `video`: only posts whose own media includes a video. A member's read serves every such video. A visitor's read
|
|
1355
|
+
* (`getSharedViral`) still serves a post's media only where the server cleared it, so some items can arrive with
|
|
1356
|
+
* the video withheld (the card's `media` empty): keep `items.filter(viralItemHasVideo)` for a video-only player.
|
|
1357
|
+
*/
|
|
1358
|
+
media?: 'video';
|
|
1359
|
+
/** Page size, default 20. */
|
|
1360
|
+
limit?: 5 | 10 | 20 | 40;
|
|
1361
|
+
/** The previous page's `cursor`. A cursor names its build; one whose build expired (30 minutes) ends the list. */
|
|
1362
|
+
cursor?: string;
|
|
1363
|
+
/** A Politics choice. A visitor (`getSharedViral`) may send only `all` or `hide`; anything else is a 400. */
|
|
1364
|
+
compass?: string;
|
|
1365
|
+
}
|
|
1366
|
+
/** How fast a post is gathering reactions, reposts and replies (per hour), against its network's baseline. */
|
|
1367
|
+
export interface ViralItemVelocity {
|
|
1368
|
+
perHour1h: number;
|
|
1369
|
+
perHour6h: number;
|
|
1370
|
+
/** The 1-hour rate against its network's trailing baseline, from -2 to 4 (1 or more is well above). */
|
|
1371
|
+
z: number;
|
|
1372
|
+
/** ln((1 + perHour1h) / (1 + perHour6h)), from -1 to 2: above 0 is speeding up. */
|
|
1373
|
+
accel: number;
|
|
1374
|
+
/** Readings behind the rate; null for a Minds post. */
|
|
1375
|
+
samples: number | null;
|
|
1376
|
+
spanHours: number;
|
|
1377
|
+
/** The rate comes from two or more readings over time (a Minds post's always does). An unmeasured post is never viral or rising. */
|
|
1378
|
+
measured: boolean;
|
|
1379
|
+
/** Who counted: Bluesky's AppView, one Fediverse server, a third party's report (Primal, for Nostr), or Minds itself. */
|
|
1380
|
+
countsOrigin: 'appview' | 'instance_local' | 'provider_reported' | 'minds';
|
|
1381
|
+
/** Minutes since the counts were last read, when known. */
|
|
1382
|
+
countsAgeMin?: number;
|
|
1383
|
+
/** Present when a third party reported the counts (`Primal`): show them as reported by it, never as verified. */
|
|
1384
|
+
countsReportedBy?: string;
|
|
1385
|
+
}
|
|
1386
|
+
/** Another post in the same spread group: the same link posted on another network. */
|
|
1387
|
+
export interface ViralRef {
|
|
1388
|
+
protocol: ViralProtocol;
|
|
1389
|
+
nativeId: string;
|
|
1390
|
+
}
|
|
1391
|
+
export interface ViralItemSpread {
|
|
1392
|
+
/** The networks sharing the post's link (its own alone when no other network carries it). */
|
|
1393
|
+
protocols: ViralProtocol[];
|
|
1394
|
+
/** Fediverse: how many servers' trends lists carried the post. */
|
|
1395
|
+
instances?: number;
|
|
1396
|
+
clusterId: null;
|
|
1397
|
+
alsoOn: ViralRef[];
|
|
1398
|
+
}
|
|
1399
|
+
/** A Minds post's card as the read serves it; open it at /post/:id. A visitor's carries media only where the server cleared it. */
|
|
1400
|
+
export interface ViralMindsCard {
|
|
1401
|
+
id: string;
|
|
1402
|
+
title: string | null;
|
|
1403
|
+
text: string;
|
|
1404
|
+
createdAt: string;
|
|
1405
|
+
author: {
|
|
1406
|
+
name: string | null;
|
|
1407
|
+
username: string | null;
|
|
1408
|
+
} | null;
|
|
1409
|
+
media: Array<{
|
|
1410
|
+
type: string;
|
|
1411
|
+
url: string;
|
|
1412
|
+
width: number | null;
|
|
1413
|
+
height: number | null;
|
|
1414
|
+
thumbnailUrl: string | null;
|
|
1415
|
+
hlsUrl: string | null;
|
|
1416
|
+
}>;
|
|
1417
|
+
likeCount: number;
|
|
1418
|
+
repostCount: number;
|
|
1419
|
+
replyCount: number;
|
|
1420
|
+
}
|
|
1421
|
+
/** What every ranked post carries, whichever network it comes from. */
|
|
1422
|
+
export interface ViralItemFields {
|
|
1423
|
+
/** Stable across builds: use it to drop a post already shown. */
|
|
1424
|
+
key: string;
|
|
1425
|
+
label: ViralLabel;
|
|
1426
|
+
score: number;
|
|
1427
|
+
/** 1-based position in this read's ordering, across pages. */
|
|
1428
|
+
rank: number;
|
|
1429
|
+
category: string | null;
|
|
1430
|
+
velocity: ViralItemVelocity;
|
|
1431
|
+
spread: ViralItemSpread;
|
|
1432
|
+
}
|
|
1433
|
+
/** A ranked Minds post; open it at /post/:id. */
|
|
1434
|
+
export interface ViralMindsItem extends ViralItemFields {
|
|
1435
|
+
protocol: 'minds';
|
|
1436
|
+
post: ViralMindsCard;
|
|
1437
|
+
}
|
|
1438
|
+
/** A ranked Bluesky, Fediverse or Nostr post. */
|
|
1439
|
+
export interface ViralSocialItem extends ViralItemFields {
|
|
1440
|
+
protocol: ViralOpenProtocol;
|
|
1441
|
+
post: SocialPost;
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* One ranked post: check `protocol` and TypeScript narrows `post` (a `ViralMindsCard` for `minds`, a `SocialPost`
|
|
1445
|
+
* otherwise). No item carries a compass value, an author id, a safety flag or its media kind. A visitor's card keeps
|
|
1446
|
+
* its own images and video only where the server cleared them; a withheld one has empty `media` (a `SocialPost` also
|
|
1447
|
+
* has `hasAdditionalContent: true`). Even under `media: 'video'` a visitor's item can arrive without its video, so
|
|
1448
|
+
* test each one with `viralItemHasVideo` before a video player.
|
|
1449
|
+
*/
|
|
1450
|
+
export type ViralItem = ViralMindsItem | ViralSocialItem;
|
|
1451
|
+
/**
|
|
1452
|
+
* Whether the item's own card carries a playable video. Use it on a `media: 'video'` read before a Watch-style
|
|
1453
|
+
* player: a visitor's read can serve an item whose video the server withheld (its card's `media` is then empty).
|
|
1454
|
+
* A video only in a quoted post does not count, as the server never counts a quote's media.
|
|
1455
|
+
*/
|
|
1456
|
+
export declare function viralItemHasVideo(item: ViralItem): boolean;
|
|
1457
|
+
/** A network's trailing pace that velocity is measured against. */
|
|
1458
|
+
export interface ViralBaseline {
|
|
1459
|
+
median: number;
|
|
1460
|
+
p90: number;
|
|
1461
|
+
n: number;
|
|
1462
|
+
source: 'trailing' | 'tick' | 'prior';
|
|
1463
|
+
}
|
|
1464
|
+
export interface ViralProvider {
|
|
1465
|
+
name: string;
|
|
1466
|
+
ok: boolean;
|
|
1467
|
+
lastOkAt: string | null;
|
|
1468
|
+
observedAt: string;
|
|
1469
|
+
}
|
|
1470
|
+
export interface ViralReadMeta {
|
|
1471
|
+
network: ViralNetwork;
|
|
1472
|
+
category_filter: {
|
|
1473
|
+
selected: string;
|
|
1474
|
+
} | null;
|
|
1475
|
+
media_filter: 'video' | null;
|
|
1476
|
+
/** Null unless the read named a filtering `compass`: what Hide politics left out (sides and Mix are not applied here). */
|
|
1477
|
+
compass_filter: CompassFilterMeta | null;
|
|
1478
|
+
safety_filter: {
|
|
1479
|
+
withheld: number;
|
|
1480
|
+
pending: number;
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
export interface ViralReadResponse {
|
|
1484
|
+
items: ViralItem[];
|
|
1485
|
+
/** Pass as `cursor` for the next page; null at the end of this build's list. */
|
|
1486
|
+
cursor: string | null;
|
|
1487
|
+
builtAt: string | null;
|
|
1488
|
+
status: ViralReadStatus;
|
|
1489
|
+
window: {
|
|
1490
|
+
primary: '1h';
|
|
1491
|
+
context: '6h';
|
|
1492
|
+
};
|
|
1493
|
+
baselines: Record<string, ViralBaseline>;
|
|
1494
|
+
provenance: {
|
|
1495
|
+
providers: ViralProvider[];
|
|
1496
|
+
};
|
|
1497
|
+
meta: ViralReadMeta;
|
|
1498
|
+
/** True when no further page exists in this build (always on `off` and `building`). */
|
|
1499
|
+
exhausted: boolean;
|
|
1500
|
+
}
|
|
1293
1501
|
/** Read options for callers that hold their own deadline, such as a discovery page. */
|
|
1294
1502
|
export interface SocialReadOptions {
|
|
1295
1503
|
retry?: boolean;
|
|
@@ -1409,6 +1617,14 @@ export declare class ProtocolsResource {
|
|
|
1409
1617
|
getSharedRadarStory(projectId: string, params: {
|
|
1410
1618
|
cluster: string;
|
|
1411
1619
|
}): Promise<SingleResponse<SocialRadarStory>>;
|
|
1620
|
+
/**
|
|
1621
|
+
* The Viral read without a key, for a signed-out visitor: posts whose authors allow wide reach only, and media only
|
|
1622
|
+
* where the server cleared it, so a `media: 'video'` read can carry items whose video was withheld (filter with
|
|
1623
|
+
* `viralItemHasVideo`). `status: 'off'` (no items, never thrown) until the server enables the read for the app;
|
|
1624
|
+
* an API that predates the read answers this path 401 `missing_api_key`, which resolves as off too. Every other
|
|
1625
|
+
* failure throws. Cacheable for a minute by a shared cache.
|
|
1626
|
+
*/
|
|
1627
|
+
getSharedViral(projectId: string, params?: ViralReadParams, options?: SocialReadOptions): Promise<SingleResponse<ViralReadResponse>>;
|
|
1412
1628
|
publicNetworks(): Promise<SingleResponse<SocialNetwork[]>>;
|
|
1413
1629
|
/** Query the live public network. It does not import users or posts. */
|
|
1414
1630
|
searchPublicPosts(params: SearchPublicPostsParams): Promise<SingleResponse<SocialPostSearch>>;
|
|
@@ -1442,7 +1658,10 @@ export declare class ProtocolsResource {
|
|
|
1442
1658
|
}): Promise<SingleResponse<SocialConversation>>;
|
|
1443
1659
|
/**
|
|
1444
1660
|
* Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
|
|
1445
|
-
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
1661
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout. `compass` (a Politics
|
|
1662
|
+
* choice, spelled as `posts.list` takes it): Hide politics (`hide`) leaves out the topics and posts whose cached
|
|
1663
|
+
* browse category is politics, where the filter is served to the member; a side or Mix is answered unfiltered. The
|
|
1664
|
+
* page then says what it did in `meta.compass_filter`.
|
|
1446
1665
|
*/
|
|
1447
1666
|
getPublicTrends(params: {
|
|
1448
1667
|
protocol: string;
|
|
@@ -1451,6 +1670,7 @@ export declare class ProtocolsResource {
|
|
|
1451
1670
|
includePreviews?: boolean;
|
|
1452
1671
|
category?: string;
|
|
1453
1672
|
discovery?: boolean;
|
|
1673
|
+
compass?: string;
|
|
1454
1674
|
}, options?: SocialReadOptions): Promise<SingleResponse<SocialTrends>>;
|
|
1455
1675
|
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. Screened like `getPublicTrends`. */
|
|
1456
1676
|
getPublicTrendFeed(params: {
|
|
@@ -1477,6 +1697,12 @@ export declare class ProtocolsResource {
|
|
|
1477
1697
|
getPublicRadarStory(params: {
|
|
1478
1698
|
cluster: string;
|
|
1479
1699
|
}, options?: SocialReadOptions): Promise<SingleResponse<SocialRadarStory>>;
|
|
1700
|
+
/**
|
|
1701
|
+
* The Viral read under the signed-in member's app key (scopes `users:read` and `posts:read`): every network's posts
|
|
1702
|
+
* the build keeps, less the member's own blocks, mutes and hidden posts. Never cached. `status: 'off'` resolves with
|
|
1703
|
+
* no items; a 401 or 403 always throws (a session or key failure is never reported as off).
|
|
1704
|
+
*/
|
|
1705
|
+
getPublicViral(params?: ViralReadParams, options?: SocialReadOptions): Promise<SingleResponse<ViralReadResponse>>;
|
|
1480
1706
|
/** Provider-ranked public accounts with explicit provenance; served from the app's index when a fresh snapshot exists. */
|
|
1481
1707
|
getPublicPopularProfiles(params: {
|
|
1482
1708
|
protocol: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ValidationError } from '../errors.js';
|
|
1
|
+
import { AuthenticationError, ValidationError } from '../errors.js';
|
|
2
2
|
/** Canonical protocol operation names returned by the adapter registry. */
|
|
3
3
|
export const PROTOCOL_CAPABILITY_OPERATIONS = Object.freeze([
|
|
4
4
|
'discover',
|
|
@@ -34,11 +34,43 @@ function hostedIdentityPath(id) {
|
|
|
34
34
|
}
|
|
35
35
|
return `/protocols/identities/${encodeURIComponent(id)}`;
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Whether the item's own card carries a playable video. Use it on a `media: 'video'` read before a Watch-style
|
|
39
|
+
* player: a visitor's read can serve an item whose video the server withheld (its card's `media` is then empty).
|
|
40
|
+
* A video only in a quoted post does not count, as the server never counts a quote's media.
|
|
41
|
+
*/
|
|
42
|
+
export function viralItemHasVideo(item) {
|
|
43
|
+
return item.post.media.some(media => media.type === 'video');
|
|
44
|
+
}
|
|
45
|
+
/** Only the radar's own fields travel; caller scope or destination extras never reach the query string. */
|
|
38
46
|
/** Forwarded only when given, so a default read makes exactly the call it always made. */
|
|
39
47
|
const readOptions = (options) => options ? [options] : [];
|
|
40
48
|
function radarQuery(params) {
|
|
41
|
-
return { ...(params.category !== undefined ? { category: params.category } : {}), ...(params.limit !== undefined ? { limit: params.limit } : {})
|
|
49
|
+
return { ...(params.category !== undefined ? { category: params.category } : {}), ...(params.limit !== undefined ? { limit: params.limit } : {}),
|
|
50
|
+
...(params.compass !== undefined ? { compass: params.compass } : {}) };
|
|
51
|
+
}
|
|
52
|
+
/** Only the six Viral read fields travel, each only when given: the read answers any other parameter with a 400. */
|
|
53
|
+
function viralQuery(params) {
|
|
54
|
+
const query = {};
|
|
55
|
+
for (const key of ['network', 'category', 'media', 'limit', 'cursor', 'compass'])
|
|
56
|
+
if (params[key] !== undefined)
|
|
57
|
+
query[key] = params[key];
|
|
58
|
+
return query;
|
|
59
|
+
}
|
|
60
|
+
/** An off read is empty whatever else its body carried. Every other status is passed through as served. */
|
|
61
|
+
function viralRead(response) {
|
|
62
|
+
if (response.data?.status !== 'off')
|
|
63
|
+
return response;
|
|
64
|
+
return { ...response, data: { ...response.data, items: [], cursor: null, exhausted: true } };
|
|
65
|
+
}
|
|
66
|
+
/** The server's own off answer (ViralReadService), built here for an API that predates the read. */
|
|
67
|
+
function viralOff(params) {
|
|
68
|
+
return { data: {
|
|
69
|
+
items: [], cursor: null, builtAt: null, status: 'off', window: { primary: '1h', context: '6h' }, baselines: {}, provenance: { providers: [] },
|
|
70
|
+
meta: { network: params.network ?? 'all', category_filter: params.category !== undefined ? { selected: params.category } : null,
|
|
71
|
+
media_filter: params.media ?? null, compass_filter: null, safety_filter: { withheld: 0, pending: 0 } },
|
|
72
|
+
exhausted: true,
|
|
73
|
+
} };
|
|
42
74
|
}
|
|
43
75
|
/** Forward only the media contract fields; nothing else from the app's own media rows. */
|
|
44
76
|
const nativeMediaInput = (media) => media.map(item => ({ url: item.url,
|
|
@@ -117,6 +149,23 @@ export class ProtocolsResource {
|
|
|
117
149
|
getSharedRadarStory(projectId, params) {
|
|
118
150
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/radar/story`, { cluster: params.cluster });
|
|
119
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* The Viral read without a key, for a signed-out visitor: posts whose authors allow wide reach only, and media only
|
|
154
|
+
* where the server cleared it, so a `media: 'video'` read can carry items whose video was withheld (filter with
|
|
155
|
+
* `viralItemHasVideo`). `status: 'off'` (no items, never thrown) until the server enables the read for the app;
|
|
156
|
+
* an API that predates the read answers this path 401 `missing_api_key`, which resolves as off too. Every other
|
|
157
|
+
* failure throws. Cacheable for a minute by a shared cache.
|
|
158
|
+
*/
|
|
159
|
+
async getSharedViral(projectId, params = {}, options) {
|
|
160
|
+
try {
|
|
161
|
+
return viralRead(await this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/viral`, viralQuery(params), ...readOptions(options)));
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error instanceof AuthenticationError && error.code === 'missing_api_key')
|
|
165
|
+
return viralOff(params);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
120
169
|
publicNetworks() {
|
|
121
170
|
return this.client.get('/protocols/social/networks');
|
|
122
171
|
}
|
|
@@ -156,13 +205,17 @@ export class ProtocolsResource {
|
|
|
156
205
|
}
|
|
157
206
|
/**
|
|
158
207
|
* Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
|
|
159
|
-
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
208
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout. `compass` (a Politics
|
|
209
|
+
* choice, spelled as `posts.list` takes it): Hide politics (`hide`) leaves out the topics and posts whose cached
|
|
210
|
+
* browse category is politics, where the filter is served to the member; a side or Mix is answered unfiltered. The
|
|
211
|
+
* page then says what it did in `meta.compass_filter`.
|
|
160
212
|
*/
|
|
161
213
|
getPublicTrends(params, options) {
|
|
162
214
|
return this.client.get('/protocols/social/trends', { protocol: params.protocol, limit: params.limit, cursor: params.cursor,
|
|
163
215
|
...(params.includePreviews !== undefined ? { includePreviews: params.includePreviews } : {}),
|
|
164
216
|
...(params.category !== undefined ? { category: params.category } : {}),
|
|
165
|
-
...(params.discovery !== undefined ? { discovery: params.discovery } : {})
|
|
217
|
+
...(params.discovery !== undefined ? { discovery: params.discovery } : {}),
|
|
218
|
+
...(params.compass !== undefined ? { compass: params.compass } : {}) }, ...readOptions(options));
|
|
166
219
|
}
|
|
167
220
|
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. Screened like `getPublicTrends`. */
|
|
168
221
|
getPublicTrendFeed(params, options) {
|
|
@@ -188,6 +241,14 @@ export class ProtocolsResource {
|
|
|
188
241
|
getPublicRadarStory(params, options) {
|
|
189
242
|
return this.client.get('/protocols/social/radar/story', { cluster: params.cluster }, ...readOptions(options));
|
|
190
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* The Viral read under the signed-in member's app key (scopes `users:read` and `posts:read`): every network's posts
|
|
246
|
+
* the build keeps, less the member's own blocks, mutes and hidden posts. Never cached. `status: 'off'` resolves with
|
|
247
|
+
* no items; a 401 or 403 always throws (a session or key failure is never reported as off).
|
|
248
|
+
*/
|
|
249
|
+
async getPublicViral(params = {}, options) {
|
|
250
|
+
return viralRead(await this.client.get('/protocols/social/viral', viralQuery(params), ...readOptions(options)));
|
|
251
|
+
}
|
|
191
252
|
/** Provider-ranked public accounts with explicit provenance; served from the app's index when a fresh snapshot exists. */
|
|
192
253
|
getPublicPopularProfiles(params) {
|
|
193
254
|
return this.client.get('/protocols/social/popular-profiles', { protocol: params.protocol, limit: params.limit, cursor: params.cursor });
|
package/dist/types.d.ts
CHANGED
|
@@ -37,6 +37,10 @@ export type FeedNext = {
|
|
|
37
37
|
* cursor; the same continuation `next` carries), the same category on the open networks, the member's
|
|
38
38
|
* next categories (`via`: the order they came from, the one `posts.categories()` serves), For you
|
|
39
39
|
* (signed in), All.
|
|
40
|
+
*
|
|
41
|
+
* `feed` (0.7.60) names the next feed by its ref (e.g. `builtin:category:science`, or a member's `feed:<id>`). No
|
|
42
|
+
* server sends it yet: it comes with feeds. Skip a step whose kind your app cannot open yet; a feed session
|
|
43
|
+
* (`discovery.createFeed`) already leaves out any step this SDK does not know, or a malformed one.
|
|
40
44
|
*/
|
|
41
45
|
export type FalloverStep = {
|
|
42
46
|
kind: 'category_older';
|
|
@@ -53,9 +57,45 @@ export type FalloverStep = {
|
|
|
53
57
|
kind: 'for_you';
|
|
54
58
|
} | {
|
|
55
59
|
kind: 'all';
|
|
60
|
+
} | {
|
|
61
|
+
kind: 'feed';
|
|
62
|
+
ref: string;
|
|
56
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* What a Politics choice (`compass`) did to a page, on the feeds that take one (`posts.list` on discovery, category
|
|
66
|
+
* and Minds' own lists, `curator.forYou`). Counts only: never a post's placement, never the cells asked for.
|
|
67
|
+
* `unavailable` means nothing was applied (the filter is not served to this caller yet, or the judgments are not
|
|
68
|
+
* there). Under Choose sides every post counts once as kept or left out; `non_political`, `pending` and `unplaced`
|
|
69
|
+
* (with posts judged maybe) are the not-placed rest. `mix` is Mix sides' band counts over the page served.
|
|
70
|
+
*/
|
|
71
|
+
export interface CompassFilterMeta {
|
|
72
|
+
status: 'applied' | 'unavailable';
|
|
73
|
+
mode: 'all' | 'mix' | 'hide' | 'cells';
|
|
74
|
+
kept: number;
|
|
75
|
+
left_out: number;
|
|
76
|
+
unplaced: number;
|
|
77
|
+
pending: number;
|
|
78
|
+
non_political: number;
|
|
79
|
+
uncertain_shown: number;
|
|
80
|
+
truncated: boolean;
|
|
81
|
+
rubric?: string;
|
|
82
|
+
mix?: {
|
|
83
|
+
econ: {
|
|
84
|
+
l: number;
|
|
85
|
+
c: number;
|
|
86
|
+
r: number;
|
|
87
|
+
};
|
|
88
|
+
power: {
|
|
89
|
+
l: number;
|
|
90
|
+
m: number;
|
|
91
|
+
o: number;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
}
|
|
57
95
|
export interface PaginationMeta {
|
|
58
96
|
discovery?: DiscoveryLayout;
|
|
97
|
+
/** Present when the request named `compass`: what the Politics choice did to this page. */
|
|
98
|
+
compass_filter?: CompassFilterMeta;
|
|
59
99
|
scanned?: number;
|
|
60
100
|
category_filter?: {
|
|
61
101
|
selected: string;
|
|
@@ -348,6 +388,21 @@ export interface ListPostsParams extends PaginationParams {
|
|
|
348
388
|
discovery?: boolean;
|
|
349
389
|
/** Public-only broad category. Requires an enabled app experiment; limit <=25. */
|
|
350
390
|
category?: string;
|
|
391
|
+
/**
|
|
392
|
+
* A Politics choice on a discovery, category or Minds list read: `all`, `mix`, `hide`, or cells in the canonical
|
|
393
|
+
* order `lo,co,ro,lm,cm,rm,ll,cl,rl`, optionally followed by `np`. Any other spelling is 400 `invalid_compass`; on a
|
|
394
|
+
* profile, group, Following, liked or saved read it is 400 `compass_not_supported`. Mix sides and Choose sides
|
|
395
|
+
* spend the member's hourly budget (429 `compass_rate_limited`). The page says what it did in
|
|
396
|
+
* `meta.compass_filter`. Only Minds posts are filtered.
|
|
397
|
+
*/
|
|
398
|
+
compass?: string;
|
|
399
|
+
/**
|
|
400
|
+
* `false` asks once: the first 5xx or 429 is thrown, with no retry and no Retry-After sleep. For a read that holds its
|
|
401
|
+
* own deadline, such as a Discover feed session's Minds read. Never sent to the server. It rides in the params, not
|
|
402
|
+
* in a second argument, so a reader wrapped by a function that forwards only its params (as the Minds app wraps the
|
|
403
|
+
* `posts.list` it hands a feed session, to add a Politics choice) still asks once.
|
|
404
|
+
*/
|
|
405
|
+
retry?: boolean;
|
|
351
406
|
community_id?: string;
|
|
352
407
|
author_id?: string;
|
|
353
408
|
organization_id?: string;
|
|
@@ -355,6 +410,8 @@ export interface ListPostsParams extends PaginationParams {
|
|
|
355
410
|
tag_ids?: string;
|
|
356
411
|
/** Pass 'me' to return only posts where audience_user_id matches the caller (personal agent feed). */
|
|
357
412
|
audience_user_id?: 'me' | string;
|
|
413
|
+
/** Return only posts by accounts the authenticated caller follows, newest first (the Following feed). */
|
|
414
|
+
following?: boolean;
|
|
358
415
|
/** Return only posts the authenticated caller has upvoted. */
|
|
359
416
|
liked?: boolean;
|
|
360
417
|
/**
|
|
@@ -584,6 +641,19 @@ export interface ConversationMember {
|
|
|
584
641
|
username: string;
|
|
585
642
|
image: string | null;
|
|
586
643
|
is_ai: boolean;
|
|
644
|
+
/**
|
|
645
|
+
* `admin` for a private group's admin: the one person who adds, removes and
|
|
646
|
+
* renames, and whose device sets up an encrypted group's keys. The creator,
|
|
647
|
+
* then, once they leave, the longest-standing person left. `member` for
|
|
648
|
+
* everyone else. Omitted by APIs before SDK 0.7.59's group routes.
|
|
649
|
+
*/
|
|
650
|
+
role?: ChatMemberRole;
|
|
651
|
+
/**
|
|
652
|
+
* In a private group, every member's request: `pending` until an invitee
|
|
653
|
+
* accepts. Declining stays private, so to anyone else a member who declined
|
|
654
|
+
* still reads `pending`. Omitted outside private groups and by older APIs.
|
|
655
|
+
*/
|
|
656
|
+
request_status?: ChatRequestStatus;
|
|
587
657
|
}
|
|
588
658
|
export interface ConversationMemberDetail extends ConversationMember {
|
|
589
659
|
joined_at: string;
|
|
@@ -633,6 +703,12 @@ export interface LeaveConversationResult {
|
|
|
633
703
|
left: true;
|
|
634
704
|
/** True when the caller was the last person in it, so the room was removed. */
|
|
635
705
|
conversation_deleted: boolean;
|
|
706
|
+
/**
|
|
707
|
+
* When the admin of an encrypted group leaves: the invitees who had not
|
|
708
|
+
* accepted, whose invites were cancelled with them (the next admin
|
|
709
|
+
* re-invites). Omitted when there were none.
|
|
710
|
+
*/
|
|
711
|
+
invites_cancelled?: string[];
|
|
636
712
|
}
|
|
637
713
|
export interface ConversationDetail {
|
|
638
714
|
/** Missing on older APIs: treat as unknown, never as plaintext permission. */
|
|
@@ -652,6 +728,67 @@ export interface ConversationDetail {
|
|
|
652
728
|
can_leave?: boolean;
|
|
653
729
|
created_at: string;
|
|
654
730
|
}
|
|
731
|
+
export type ChatMemberRole = 'admin' | 'member';
|
|
732
|
+
/**
|
|
733
|
+
* Why an encrypted group refused someone: `details.reason` of a 409
|
|
734
|
+
* `conversation_not_eligible` (`RecursivError`) from
|
|
735
|
+
* `chat.createGroup({ encrypted: true })` or `chat.addMembers`, beside
|
|
736
|
+
* `details.max_people`. The group must be a private group (not a DM, not a
|
|
737
|
+
* community room) of people only (no agents), all inside the encryption
|
|
738
|
+
* pilot, and at most `max_people` (8 at launch) counting pending invitees.
|
|
739
|
+
*/
|
|
740
|
+
export type EncryptedGroupRefusalReason = 'not_a_private_group' | 'community' | 'too_many_people' | 'agent' | 'outside_pilot';
|
|
741
|
+
export interface CreateGroupInput {
|
|
742
|
+
name: string;
|
|
743
|
+
/** Up to 100 people, invited in this order. */
|
|
744
|
+
member_ids: string[];
|
|
745
|
+
/**
|
|
746
|
+
* End-to-end encrypted. The group and its encryption record are created
|
|
747
|
+
* together or not at all: a refusal never leaves a plaintext group behind.
|
|
748
|
+
*/
|
|
749
|
+
encrypted?: boolean;
|
|
750
|
+
/** With `encrypted`: the creator's active encrypted-chat device, when they have one. */
|
|
751
|
+
device_id?: string;
|
|
752
|
+
}
|
|
753
|
+
export interface AddGroupMembersResult {
|
|
754
|
+
id: string;
|
|
755
|
+
/** Who was added, in the order asked; `pending` for someone who does not follow you (a message request). */
|
|
756
|
+
added: Array<{
|
|
757
|
+
id: string;
|
|
758
|
+
request_status: ChatRequestStatus;
|
|
759
|
+
}>;
|
|
760
|
+
/** Asked for but already in the group: nothing changed for them. */
|
|
761
|
+
already_members: string[];
|
|
762
|
+
}
|
|
763
|
+
export interface RemoveGroupMemberResult {
|
|
764
|
+
id: string;
|
|
765
|
+
user_id: string;
|
|
766
|
+
removed: true;
|
|
767
|
+
}
|
|
768
|
+
export interface UpdateGroupResult {
|
|
769
|
+
id: string;
|
|
770
|
+
name: string;
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Realtime `chat:members`: a private group's people changed. Ids only: read
|
|
774
|
+
* the conversation again (and, in an encrypted group, check its keys).
|
|
775
|
+
* `actorId` is the admin for `added` and `removed`, and the leaver for `left`
|
|
776
|
+
* and `invites_cancelled` (the invites an encrypted group's admin sent, which
|
|
777
|
+
* went with them).
|
|
778
|
+
*/
|
|
779
|
+
export interface ChatMembersEvent {
|
|
780
|
+
conversationId: string;
|
|
781
|
+
change: 'added' | 'removed' | 'left' | 'invites_cancelled';
|
|
782
|
+
userIds: string[];
|
|
783
|
+
actorId: string;
|
|
784
|
+
}
|
|
785
|
+
/** Realtime `chat:conversation`: a private group was renamed by its admin. */
|
|
786
|
+
export interface ChatConversationEvent {
|
|
787
|
+
conversationId: string;
|
|
788
|
+
change: 'renamed';
|
|
789
|
+
name: string;
|
|
790
|
+
actorId: string;
|
|
791
|
+
}
|
|
655
792
|
export interface MessageReaction {
|
|
656
793
|
type: string;
|
|
657
794
|
user_id: string;
|