@recursiv/sdk 0.7.96 → 0.7.109
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 +8 -4
- package/dist/assistant/render.d.ts +3 -3
- package/dist/assistant/render.js +9 -8
- package/dist/assistant/subjectBundle.js +4 -4
- package/dist/assistant/types.d.ts +15 -8
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -0
- package/dist/privacy/facts.generated.d.ts +231 -0
- package/dist/privacy/facts.generated.js +264 -0
- package/dist/resources/link-preview.d.ts +5 -1
- package/dist/resources/link-preview.js +5 -1
- package/dist/resources/media.d.ts +22 -0
- package/dist/resources/media.js +4 -0
- package/dist/resources/protocols.d.ts +53 -0
- package/dist/resources/protocols.js +17 -0
- package/dist/resources/telemetry.d.ts +17 -0
- package/dist/resources/telemetry.js +10 -0
- package/dist/resources/wallet.d.ts +60 -2
- package/dist/resources/wallet.js +29 -1
- package/dist/types.d.ts +17 -0
- package/llm.md +21 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
69
69
|
| `r.envVars` | `set`, `list`, `delete` (values write-only; secrets masked on read) |
|
|
70
70
|
| `r.projects` | `create`, `deploy`, `executeCode`, `createSandbox`, `stopSandbox`, `deploymentLogs` |
|
|
71
71
|
| `r.storage` | `ensureBucket`, `getUploadUrl`, `getDownloadUrl`, `listItems`, `deleteObject` |
|
|
72
|
+
| `r.telemetry` | `productCounts` — app-bound first-party aggregate counts; reviewed event names and counts only, retained at most 30 days |
|
|
72
73
|
| `r.sandbox` | `execute` (anonymous, no API key needed) |
|
|
73
74
|
|
|
74
75
|
### AI Agents
|
|
@@ -114,7 +115,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
114
115
|
| `r.billing` | Usage tracking, subscriptions, checkout |
|
|
115
116
|
| `r.appSubscriptions` | Consumer app plans: `checkout`, `status`, `createPortalSession`, `previewTierChange`, `changeTier` |
|
|
116
117
|
| `r.boosts` | Promoted posts: `create`, `mine`, `balance`, `pause`, `resume`, `cancel`, `recordEvent`, `topup`, `confirmTopup`; admin `listPendingReview`, `review`, `grant` |
|
|
117
|
-
| `r.wallet` | Wallet, MINDS tokens (`getTokenBalance`, `getTokenLedger`), USD payouts (`getPayouts`, `startPayoutOnboarding`, `payoutLoginLink`) |
|
|
118
|
+
| `r.wallet` | Wallet, MINDS tokens (`getTokenBalance`, `getTokenLedger`), a wallet the member holds linked by signature (`createLinkNonce`, `linkWallet`, `listLinkedWallets`, `unlinkWallet`), USD payouts (`getPayouts`, `startPayoutOnboarding`, `payoutLoginLink`) |
|
|
118
119
|
| `r.notifications` | Notification stream (`list`, `unreadCount`, `markAsRead`, `archive`, `markAllAsRead`; `category: 'mentions'` for mentions and replies only, 0.7.72) and push tokens |
|
|
119
120
|
| `r.github` | GitHub integration |
|
|
120
121
|
| `r.integrations` | External service connections |
|
|
@@ -123,8 +124,9 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
123
124
|
| `r.trust` | The signed trust log (0.7.96; 404 until the operator turns on `TRUST_LOG_PUBLIC_READS`): keyless `keys()` (every key that ever signed it), `mine({ before?, limit? })` (`users:read`: the caller's own signed events with the server's check `verified`). `r.moderation.publicLog` then carries `trust_log` on each action (`status`, and where the signed decision and reversal sit: `chain`, `seq`, `key_id`, `occurred_at`, `verified`). No read carries a hash or a signature |
|
|
124
125
|
| `r.dispatcher` | Task queue, claims, signals, outcomes, webhooks |
|
|
125
126
|
| `r.goals` | Org goals with task rollups (group dispatcher tasks) |
|
|
126
|
-
| `r.
|
|
127
|
-
| `r.
|
|
127
|
+
| `r.media` | `sourceApprovals({ projectId, sources })` — cached public host/purpose judgments and exact owned hosts; no caller-triggered provider fetch or inference. Image/icon requests stay relayed; direct players use approved, unexpired decisions once enforcement is active |
|
|
128
|
+
| `r.linkPreview` | `get(url, source?, { projectId })` opts into expiring relayed image/icon capabilities; refresh before `media_expires_at`, and preserve source authorization. `get(url)` — a public link's Open Graph card (`data: null` when it has none). For a chat link pass `{ messageId }` or `{ conversationId }` on a signed-in key with `chat:read`: the link is fetched only when it is in the named message and a person wrote that message, or the caller wrote it in that conversation, else `refused: 'ai_link'`. A source with no id rejects (`ValidationError`). Not in encrypted chats: the server cannot read them, so leave `source` out there and apply the app's own rule |
|
|
129
|
+
| `r.protocols` | Private Minds safety: `sourceControls` (current enablement and saved choices; filter with `control`, each item may carry a `display` label), `setSourceControl`, `onSourceControlChange`, `reportSource`. Delegated accounts: `nativeSocialAccounts`, `startNativeSocialConnection`, `finishNativeSocialConnection`, `disconnectNativeSocialAccount`. Native follows: `nativeFollowStates` (every connected account's state for one source, one read), `nativeFollowState`, `prepareNativeFollow`, `submitNativeFollow`, `nativeFollowStatus` (native following or pending approval, distinct from a Minds feed subscription; preserve the complete signed Nostr list). Native reposts and undo: `nativeRepostState`, `prepareNativeRepost`, `submitNativeRepost`, `nativeRepostStatus` (public posts, explicit approval, connected account, no uncertain retries). Native likes and unlikes: `nativeLikeState`, `prepareNativeLike`, `submitNativeLike`, `nativeLikeStatus` (connected authority, app enablement and explicit approval; resume pending actions instead of duplicating them). Reviewed replies and optional linked posts: `prepareNativeReply`, `submitNativeReply`, `nativeReplyStatus`, `nativeReplyHistory`, `prepareNativeReplyRemoval`, `removeNativeReply` (require deployed API support and app enablement). Keyless shared post links: `sharedNetworks(projectId)`, `getSharedConversation(projectId, { protocol, post, readRef?, replies?: 'none' })` (`replies: 'none'`, 0.7.78: the post alone for a card, with `repliesStatus: 'not_requested'`; `getPublicConversation` and `getConnectedSocialConversation` take it too). Member public reading: `searchPublicPosts` (selected account required for Bluesky/ActivityPub; public Nostr search), `getConnectedSocialConversation` (preserve result readerAccountId/readRef), `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; a Fediverse community's newest threads, read-only (0.7.109): `getOpenGroupThreads({ group, limit?, cursor?, compass? })`, `group` being the `actor` of a groups read's `open_network` entry; radar stories: `getPublicRadarStory` (one story by id with its posts; one that left the radar within its minimum lifetime, or within the last half hour, still answers live with `liveUntil` and the story that continues it; one that has left for longer comes with its archived copy and the live story it continues as; the id may be a World Today edition story's, and the answer then names it in `editionStory` under the story's radar id; a story a World Today story seeded carries `volume`, the first page of the posts that story counts across Minds and the open networks, whose `cursor` goes on with `curator.storyPosts` or `curator.memberStoryPosts`), keyless `getSharedRadarStory(projectId, { cluster })` and `getSharedRadarPosts(projectId, { cluster })` (keyless reads carry images and videos only from outlets and members marked `visitorMedia`; signed-in reads keep all media, so a share card uses only those); the Viral read (fastest-moving posts across Bluesky, the Fediverse, Nostr and Minds, each against its own network's pace; 0.7.59): `getPublicViral({ network?, category?, media?: 'video', limit?: 5 | 10 | 20 | 40, cursor?, compass? })` under the member's key and keyless `getSharedViral(projectId, params)` (visitors: `compass` `all` or `hide` only; a visitor's `media: 'video'` read can carry items whose video the server withheld, so filter with the exported `viralItemHasVideo` before a video player; `item.post` narrows on `item.protocol`). `status: 'off'` resolves with no items until the app enables the read, and `getSharedViral` also resolves an older API's 401 `missing_api_key` as off; `getPublicViral` throws every 401 and 403, so a session failure is never read as off; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. `list` returns bounded adapter `maturity`, `limitations`, and exhaustive `supported`/`planned`/`unsupported` implementation capabilities (not provider health or enablement); settings, candidate refresh/status, search, and read-only quarantined candidate review via `listCandidates` / `getCandidate` |
|
|
128
130
|
|
|
129
131
|
Full reference: see [llm.md](./llm.md) (ships with this package) or the type definitions in `dist/`.
|
|
130
132
|
|
|
@@ -387,7 +389,9 @@ const { system, user, references } = renderBundleForModel(bundle, 'explain', { b
|
|
|
387
389
|
|
|
388
390
|
#### Post pictures as pictures
|
|
389
391
|
|
|
390
|
-
|
|
392
|
+
A post picture's `checked_at` is the time this read found it passed Minds' current checks (recursiv#5012): visibility, holds and adult labels. Where the server's media-safety floor is on (CONTENT_MEDIA_SAFETY_ENABLED), one of those labels is a vision model's stored verdict that the picture is not suggestive; no image classifier is run for `checked_at` itself. No hash bank (NCMEC, StopNCII) and no CSAM classifier is among the checks.
|
|
393
|
+
|
|
394
|
+
Pass each media item's `id`, the payload's `checked_at` and its `held: false` mark into `buildSubjectBundle`, then list the candidates, run your own checks on every one of them, and render with the ones that passed:
|
|
391
395
|
|
|
392
396
|
```typescript
|
|
393
397
|
import { type BundlePicture, bundlePictures, renderBundleForModel } from '@recursiv/sdk/assistant'
|
|
@@ -3,9 +3,9 @@ import type { AssistIntent, BundlePicture, BundlePictureOptions, RenderBundleOpt
|
|
|
3
3
|
export declare const MEMBER_LINE_PREFIX = "| ";
|
|
4
4
|
/**
|
|
5
5
|
* When a post picture may go to a model as a picture (sealed-full-scope-v1 2.1, 2.2): at most `max_pictures` in one
|
|
6
|
-
* message, pictures from the phone included (the caller passes the room left as `max`); only while
|
|
7
|
-
*
|
|
8
|
-
* still counts (clocks drift), and one further ahead is not a time Minds wrote, so it does not.
|
|
6
|
+
* message, pictures from the phone included (the caller passes the room left as `max`); only while the time it last
|
|
7
|
+
* passed Minds' current checks (`checked_at`) is younger than `fresh_ms`; a check time up to `clock_skew_ms` ahead of
|
|
8
|
+
* the caller's clock still counts (clocks drift), and one further ahead is not a time Minds wrote, so it does not.
|
|
9
9
|
*/
|
|
10
10
|
export declare const BUNDLE_PICTURE_LIMITS: Readonly<{
|
|
11
11
|
max_pictures: 3;
|
package/dist/assistant/render.js
CHANGED
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Pictures (sealed-full-scope-v1 2.1, 2.2): with `pictures` given, a picture on the asked-about post, its parent or
|
|
14
14
|
* its quote (never a reply's) also goes as a picture, an image part inside the content block after the content's text,
|
|
15
|
-
* when
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* cached bytes are the
|
|
15
|
+
* when the time it last passed Minds' current checks (`checked_at`; SubjectMediaInput says what those checks are and
|
|
16
|
+
* are not) is under 24 hours old by the caller's clock, the caller approved it (`allowed`, from bundlePictures, after
|
|
17
|
+
* the caller's own checks) and the caller's image cache holds its prepared JPEG; at most 3 per message, fewer when the
|
|
18
|
+
* message carries pictures from the phone, never for 'translate'. Every other picture goes as its alt text and stored
|
|
19
|
+
* caption only, as before. The renderer never reads a clock or fetches: the clock and the cached bytes are the
|
|
20
|
+
* caller's.
|
|
20
21
|
*/
|
|
21
22
|
import { count, isoTime, plainHandle, plainId, referencesOf, webAddress } from './subjectBundle.js';
|
|
22
23
|
import { ASSIST_INTENTS } from './types.js';
|
|
@@ -27,9 +28,9 @@ const LANGUAGE_PATTERN = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/;
|
|
|
27
28
|
export const MEMBER_LINE_PREFIX = '| ';
|
|
28
29
|
/**
|
|
29
30
|
* When a post picture may go to a model as a picture (sealed-full-scope-v1 2.1, 2.2): at most `max_pictures` in one
|
|
30
|
-
* message, pictures from the phone included (the caller passes the room left as `max`); only while
|
|
31
|
-
*
|
|
32
|
-
* still counts (clocks drift), and one further ahead is not a time Minds wrote, so it does not.
|
|
31
|
+
* message, pictures from the phone included (the caller passes the room left as `max`); only while the time it last
|
|
32
|
+
* passed Minds' current checks (`checked_at`) is younger than `fresh_ms`; a check time up to `clock_skew_ms` ahead of
|
|
33
|
+
* the caller's clock still counts (clocks drift), and one further ahead is not a time Minds wrote, so it does not.
|
|
33
34
|
*/
|
|
34
35
|
export const BUNDLE_PICTURE_LIMITS = Object.freeze({
|
|
35
36
|
max_pictures: 3,
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* - posts under a child-safety hold, and posts a moderator turned the assistant off for, are left out, whoever asks;
|
|
10
10
|
* - a post the server's per-viewer verdict (`assist_readable`) says is not readable is left out, whoever asks;
|
|
11
11
|
* - pictures go as alt text and the stored caption only: never bytes, never an address (held media not even that). A
|
|
12
|
-
* picture keeps its plain id, and
|
|
13
|
-
* exactly `false` (2.1: "checked within the last 24 hours and not held"
|
|
14
|
-
* cache may attach one on the asked-about post, parent or
|
|
15
|
-
* under 24 hours old (renderBundleForModel);
|
|
12
|
+
* picture keeps its plain id, and the time it last passed Minds' current checks (`checked_at`) when its input says
|
|
13
|
+
* `held` is exactly `false` (2.1: "checked within the last 24 hours and not held"; GET /posts sends both together,
|
|
14
|
+
* recursiv#5012), so a renderer given the caller's image cache may attach one on the asked-about post, parent or
|
|
15
|
+
* quote (never a reply's) as a picture while that check is under 24 hours old (renderBundleForModel);
|
|
16
16
|
* - every string loses what a reader cannot see (stripHiddenText) and is cut to its limit, and the bundle has a total
|
|
17
17
|
* that the asked-about post is cut to fit as well.
|
|
18
18
|
*
|
|
@@ -54,11 +54,15 @@ export interface SubjectMediaInput {
|
|
|
54
54
|
*/
|
|
55
55
|
id?: string | null;
|
|
56
56
|
/**
|
|
57
|
-
* When
|
|
58
|
-
* sealed-full-scope-v1 2.1
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* When the picture last passed Minds' current checks (the post payload's `media[].checked_at`, sent with `held:
|
|
58
|
+
* false`, sealed-full-scope-v1 2.1): an ISO time with its zone, written only by the server. The checks are
|
|
59
|
+
* visibility, holds and adult labels (recursiv#5012). Where the server's media-safety floor is on
|
|
60
|
+
* (CONTENT_MEDIA_SAFETY_ENABLED), one of those labels is a vision model's stored verdict that the picture is not
|
|
61
|
+
* suggestive; no image classifier is run for this field. No hash bank (NCMEC, StopNCII) and no CSAM classifier is
|
|
62
|
+
* among the checks, so run your own checks on the picture too. Kept only when `held` is exactly `false`. A picture
|
|
63
|
+
* can go to a model as a picture only while this is under 24 hours old by the renderer's clock; absent, not an ISO
|
|
64
|
+
* time with its zone, or without `held: false`, means alt text and the stored caption only, as for every other
|
|
65
|
+
* picture.
|
|
62
66
|
*/
|
|
63
67
|
checked_at?: string | null;
|
|
64
68
|
}
|
|
@@ -171,8 +175,8 @@ export interface BundleMedia {
|
|
|
171
175
|
/** Pictures only: the media's id, when the input had a plain one. Never written into model input. */
|
|
172
176
|
id?: string;
|
|
173
177
|
/**
|
|
174
|
-
* Pictures only:
|
|
175
|
-
* picture not held (`held: false`).
|
|
178
|
+
* Pictures only: when it last passed Minds' current checks (ISO time, UTC; `SubjectMediaInput.checked_at` says what
|
|
179
|
+
* those checks are and are not), when the input had a valid one and marked the picture not held (`held: false`).
|
|
176
180
|
*/
|
|
177
181
|
checked_at?: string;
|
|
178
182
|
}
|
|
@@ -300,7 +304,10 @@ export interface BundlePicture {
|
|
|
300
304
|
media_id: string;
|
|
301
305
|
/** Which picture of its item it is, from 1, as the content numbers them ("picture 2 alt text"). */
|
|
302
306
|
number: number;
|
|
303
|
-
/**
|
|
307
|
+
/**
|
|
308
|
+
* When it last passed Minds' current checks (ISO time, UTC; `SubjectMediaInput.checked_at` says what those checks
|
|
309
|
+
* are and are not), from the bundle, for your own freshness check. It is not a reason to skip your own checks.
|
|
310
|
+
*/
|
|
304
311
|
checked_at: string;
|
|
305
312
|
}
|
|
306
313
|
export interface BundlePictureOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { DiscoveryResource } from './resources/discovery.js';
|
|
|
3
3
|
export * from './resources/discovery.js';
|
|
4
4
|
export * from './feed-definition.js';
|
|
5
5
|
export * from './social-protocol-ids.js';
|
|
6
|
+
import { TelemetryResource } from './resources/telemetry.js';
|
|
7
|
+
export * from './resources/telemetry.js';
|
|
6
8
|
import { AuthResource } from './resources/auth.js';
|
|
7
9
|
import { PostsResource } from './resources/posts.js';
|
|
8
10
|
import { UsersResource } from './resources/users.js';
|
|
@@ -124,6 +126,7 @@ export declare class Recursiv {
|
|
|
124
126
|
readonly swarms: SwarmsResource;
|
|
125
127
|
readonly templates: TemplatesResource;
|
|
126
128
|
readonly media: MediaResource;
|
|
129
|
+
readonly telemetry: TelemetryResource;
|
|
127
130
|
readonly webhooks: WebhooksResource;
|
|
128
131
|
readonly jobs: JobsResource;
|
|
129
132
|
readonly curator: CuratorResource;
|
|
@@ -243,12 +246,12 @@ export { OrganizationSecurityResource } from './resources/organization-security.
|
|
|
243
246
|
export { UploadsResource } from './resources/uploads.js';
|
|
244
247
|
export type { UploadMediaInput, UploadUrlInput, UploadUrlResult } from './resources/uploads.js';
|
|
245
248
|
export { WalletResource } from './resources/wallet.js';
|
|
246
|
-
export type { LinkedTokenWallet, PayoutState, TokenBalance, TokenLedgerEntry } from './resources/wallet.js';
|
|
249
|
+
export type { LinkedTokenWallet, PayoutState, TokenBalance, TokenLedgerEntry, WalletLink, WalletLinkNonce } from './resources/wallet.js';
|
|
247
250
|
export { BoostsResource } from './resources/boosts.js';
|
|
248
251
|
export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostPlanCredit, BoostRejectionReason, BoostReviewStatus, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, } from './resources/boosts.js';
|
|
249
252
|
export { ProtocolsResource } from './resources/protocols.js';
|
|
250
253
|
export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, SOCIAL_FOLLOWING_SOURCE_STATUSES, viralItemHasVideo, } from './resources/protocols.js';
|
|
251
|
-
export type { SocialProfile, SocialBridge, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarSourceKind, 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, SocialFollowingSourceStatus, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeNetwork, NativeNetworkState, NativeNetworkPresence, NativeNetworkActivation, NativeNetworkOff, SetNativeNetworkInput, 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';
|
|
254
|
+
export type { SocialProfile, SocialBridge, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarSourceKind, 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, OpenGroupThreads, OpenGroupThreadsGroup, OpenGroupThreadsParams, SocialFollowingFeed, SocialFollowingSource, SocialFollowingSourceStatus, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeNetwork, NativeNetworkState, NativeNetworkPresence, NativeNetworkActivation, NativeNetworkOff, SetNativeNetworkInput, 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';
|
|
252
255
|
export { InboxResource } from './resources/inbox.js';
|
|
253
256
|
export { NetworkResource } from './resources/network.js';
|
|
254
257
|
export { SimulatorResource } from './resources/simulator.js';
|
|
@@ -272,7 +275,7 @@ export type { Swarm, SwarmMember, CreateSwarmInput, ListSwarmsParams, ScheduledT
|
|
|
272
275
|
export { TemplatesResource } from './resources/templates.js';
|
|
273
276
|
export type { Template, CreateTemplateInput, UpdateTemplateInput, ListTemplatesParams } from './resources/templates.js';
|
|
274
277
|
export { MediaResource } from './resources/media.js';
|
|
275
|
-
export type { GenerateImageInput, GeneratedImage, ImageProvider } from './resources/media.js';
|
|
278
|
+
export type { GenerateImageInput, GeneratedImage, ImageProvider, MediaSourceApprovalInput, MediaSourceApprovalResponse } from './resources/media.js';
|
|
276
279
|
export { RecursivError, AuthenticationError, AuthorizationError, LegacyHandoffError, NotFoundError, ValidationError, InsufficientCreditsError, RateLimitError, ConflictError, isActionRateLimitScope, EmailNotSentError, } from './errors.js';
|
|
277
280
|
export type { RateLimitDetails } from './errors.js';
|
|
278
281
|
export { parseSSE } from './lib/sse.js';
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { DiscoveryResource } from './resources/discovery.js';
|
|
|
3
3
|
export * from './resources/discovery.js';
|
|
4
4
|
export * from './feed-definition.js';
|
|
5
5
|
export * from './social-protocol-ids.js';
|
|
6
|
+
import { TelemetryResource } from './resources/telemetry.js';
|
|
7
|
+
export * from './resources/telemetry.js';
|
|
6
8
|
import { HttpClient } from './client.js';
|
|
7
9
|
import { AuthResource } from './resources/auth.js';
|
|
8
10
|
import { PostsResource } from './resources/posts.js';
|
|
@@ -125,6 +127,7 @@ export class Recursiv {
|
|
|
125
127
|
swarms;
|
|
126
128
|
templates;
|
|
127
129
|
media;
|
|
130
|
+
telemetry;
|
|
128
131
|
webhooks;
|
|
129
132
|
jobs;
|
|
130
133
|
curator;
|
|
@@ -204,6 +207,7 @@ export class Recursiv {
|
|
|
204
207
|
this.swarms = new SwarmsResource(client);
|
|
205
208
|
this.templates = new TemplatesResource(client);
|
|
206
209
|
this.media = new MediaResource(client);
|
|
210
|
+
this.telemetry = new TelemetryResource(client);
|
|
207
211
|
this.webhooks = new WebhooksResource(client);
|
|
208
212
|
this.jobs = new JobsResource(client);
|
|
209
213
|
this.curator = new CuratorResource(client);
|
|
@@ -76,4 +76,235 @@ export declare const PRIVACY_FACTS: readonly [{
|
|
|
76
76
|
readonly capability: "chat_tier";
|
|
77
77
|
readonly step: "S29";
|
|
78
78
|
readonly says: "No Sealed route sends a push notification or writes a notification: a Sealed answer goes back only on the request that asked for it.";
|
|
79
|
+
}, {
|
|
80
|
+
readonly id: "ai_prefs.agent_enabled";
|
|
81
|
+
readonly status: "planned";
|
|
82
|
+
readonly enforcement: "server_and_client";
|
|
83
|
+
readonly capability: "agent_enabled";
|
|
84
|
+
readonly step: "S9";
|
|
85
|
+
readonly says: "When a member turns their personal agent off, their turns to it make no provider call, it is not created again at sign-in, its chat leaves their conversation list and it sends them no push.";
|
|
86
|
+
}, {
|
|
87
|
+
readonly id: "ai_prefs.standard_allowed";
|
|
88
|
+
readonly status: "planned";
|
|
89
|
+
readonly enforcement: "server";
|
|
90
|
+
readonly capability: "standard_allowed";
|
|
91
|
+
readonly step: "S28";
|
|
92
|
+
readonly says: "When a member turns Standard AI off, no agent makes a call through OpenRouter or a model maker for a turn they send.";
|
|
93
|
+
}, {
|
|
94
|
+
readonly id: "ai_prefs.memory_facts";
|
|
95
|
+
readonly status: "planned";
|
|
96
|
+
readonly enforcement: "server";
|
|
97
|
+
readonly capability: "memory_facts";
|
|
98
|
+
readonly step: "S11";
|
|
99
|
+
readonly says: "When a member turns memory off, no agent in the app saves new facts about them, and saved facts are not sent with their turns.";
|
|
100
|
+
}, {
|
|
101
|
+
readonly id: "ai_prefs.memory_recall";
|
|
102
|
+
readonly status: "planned";
|
|
103
|
+
readonly enforcement: "server";
|
|
104
|
+
readonly capability: "memory_recall";
|
|
105
|
+
readonly step: "S11";
|
|
106
|
+
readonly says: "When a member turns off using their other chats, a turn carries only that chat's own messages and, while memory is on, saved facts.";
|
|
107
|
+
}, {
|
|
108
|
+
readonly id: "ai_prefs.memory_summaries";
|
|
109
|
+
readonly status: "planned";
|
|
110
|
+
readonly enforcement: "server";
|
|
111
|
+
readonly capability: "memory_summaries";
|
|
112
|
+
readonly step: "S11";
|
|
113
|
+
readonly says: "When a member turns summaries off, no summary call is made for their chats.";
|
|
114
|
+
}, {
|
|
115
|
+
readonly id: "ai_prefs.chat_memory";
|
|
116
|
+
readonly status: "planned";
|
|
117
|
+
readonly enforcement: "server";
|
|
118
|
+
readonly capability: "chat_memory";
|
|
119
|
+
readonly step: "S13";
|
|
120
|
+
readonly says: "When memory is off for one chat or one turn, nothing from it is saved to memory or used in other chats, and saved facts are not sent from it.";
|
|
121
|
+
}, {
|
|
122
|
+
readonly id: "ai_prefs.activity";
|
|
123
|
+
readonly status: "planned";
|
|
124
|
+
readonly enforcement: "server";
|
|
125
|
+
readonly capability: "activity";
|
|
126
|
+
readonly step: "S26";
|
|
127
|
+
readonly says: "Who a member follows, what they upvoted, their groups and their interests go with a turn only for the parts they ticked, and only once they decided.";
|
|
128
|
+
}, {
|
|
129
|
+
readonly id: "ai_prefs.feed_from_questions";
|
|
130
|
+
readonly status: "planned";
|
|
131
|
+
readonly enforcement: "server_and_client";
|
|
132
|
+
readonly capability: "feed_from_questions";
|
|
133
|
+
readonly step: "S26";
|
|
134
|
+
readonly says: "Asking an agent about a post is stored as a feed signal only when the member turned that on, and only the post id is stored, never the question.";
|
|
135
|
+
}, {
|
|
136
|
+
readonly id: "ai_prefs.push_preview";
|
|
137
|
+
readonly status: "enforced";
|
|
138
|
+
readonly enforcement: "server";
|
|
139
|
+
readonly capability: "push_preview";
|
|
140
|
+
readonly step: "S16";
|
|
141
|
+
readonly says: "A push for a message an AI agent sends in a chat carries the agent's name and none of the message's words, unless the member turned the preview on; a push whose sender cannot be read carries none of its words either.";
|
|
142
|
+
}, {
|
|
143
|
+
readonly id: "ai_prefs.standard_history_days";
|
|
144
|
+
readonly status: "planned";
|
|
145
|
+
readonly enforcement: "server";
|
|
146
|
+
readonly capability: "standard_history_days";
|
|
147
|
+
readonly step: "S24";
|
|
148
|
+
readonly says: "When a member picks 7 or 30 days, a daily job deletes their one-on-one agent chats older than that, with the chats' summaries and usage copies; billing rows without content stay.";
|
|
149
|
+
}, {
|
|
150
|
+
readonly id: "notifications.ai_message";
|
|
151
|
+
readonly status: "planned";
|
|
152
|
+
readonly enforcement: "server";
|
|
153
|
+
readonly capability: "push_ai_replies";
|
|
154
|
+
readonly step: "S16";
|
|
155
|
+
readonly says: "Pushes for agent replies have their own setting, separate from messages from people, and start from the member's Messages push setting.";
|
|
156
|
+
}, {
|
|
157
|
+
readonly id: "reports.ai_review_filter";
|
|
158
|
+
readonly status: "planned";
|
|
159
|
+
readonly enforcement: "server";
|
|
160
|
+
readonly capability: "report_ai_review";
|
|
161
|
+
readonly step: "S20";
|
|
162
|
+
readonly says: "An agent's key cannot list, resolve or dismiss a report whose reporter turned AI review off.";
|
|
163
|
+
}, {
|
|
164
|
+
readonly id: "posts.ai_ask_allowed";
|
|
165
|
+
readonly status: "planned";
|
|
166
|
+
readonly enforcement: "server_and_client";
|
|
167
|
+
readonly capability: "allow_ask_on_my_posts";
|
|
168
|
+
readonly step: "S22";
|
|
169
|
+
readonly says: "When an author turns asks off, agents do not read their posts from links, and each post carries ai_ask_allowed false so apps hide the ask option.";
|
|
170
|
+
}, {
|
|
171
|
+
readonly id: "posts.public_posts_ai";
|
|
172
|
+
readonly status: "planned";
|
|
173
|
+
readonly enforcement: "server";
|
|
174
|
+
readonly capability: "public_posts_ai";
|
|
175
|
+
readonly step: "S22";
|
|
176
|
+
readonly says: "When an author turns it off, their public posts are not embedded, topic or leaning labelled, or picked for editions, and existing vectors and those labels are removed; safety labels still run.";
|
|
177
|
+
}, {
|
|
178
|
+
readonly id: "openrouter.deny_all_paths";
|
|
179
|
+
readonly status: "planned";
|
|
180
|
+
readonly enforcement: "server";
|
|
181
|
+
readonly capability: null;
|
|
182
|
+
readonly step: "S4";
|
|
183
|
+
readonly says: "Every OpenRouter request, on the direct, fallback and summary paths, asks for providers whose stated policy is not to collect data (data_collection: deny). That is their policy; it is not checked.";
|
|
184
|
+
}, {
|
|
185
|
+
readonly id: "account.export_ai";
|
|
186
|
+
readonly status: "planned";
|
|
187
|
+
readonly enforcement: "server";
|
|
188
|
+
readonly capability: "memory_manage";
|
|
189
|
+
readonly step: "S18";
|
|
190
|
+
readonly says: "The account download includes the member's agent chats, the agents' replies, what agents saved about them and their agent's settings.";
|
|
191
|
+
}, {
|
|
192
|
+
readonly id: "account.delete_personal_agent";
|
|
193
|
+
readonly status: "planned";
|
|
194
|
+
readonly enforcement: "server";
|
|
195
|
+
readonly capability: null;
|
|
196
|
+
readonly step: "S18";
|
|
197
|
+
readonly says: "Deleting an account deletes the member's personal agent, its instructions, its saved preferences and its decision rows.";
|
|
198
|
+
}, {
|
|
199
|
+
readonly id: "sealed.guard_checks_every_turn";
|
|
200
|
+
readonly status: "planned";
|
|
201
|
+
readonly enforcement: "client";
|
|
202
|
+
readonly capability: "chat_tier";
|
|
203
|
+
readonly step: "S29";
|
|
204
|
+
readonly says: "Every Sealed message and every Sealed answer goes through the sealed text check (gpt-oss-safeguard-120b) before it is sent or shown; with no check, nothing is sent.";
|
|
205
|
+
}, {
|
|
206
|
+
readonly id: "sealed.picture_checked_on_phone";
|
|
207
|
+
readonly status: "planned";
|
|
208
|
+
readonly enforcement: "device";
|
|
209
|
+
readonly capability: "chat_tier";
|
|
210
|
+
readonly step: "S29";
|
|
211
|
+
readonly says: "Every phone picture added to a Sealed chat passes the on-phone nudity check first; one it flags stays on the phone unless the member is a known adult whose adult setting is on, in a build that applies the adult policy.";
|
|
212
|
+
}, {
|
|
213
|
+
readonly id: "sealed.picture_check_block_only";
|
|
214
|
+
readonly status: "planned";
|
|
215
|
+
readonly enforcement: "server_and_client";
|
|
216
|
+
readonly capability: "chat_tier";
|
|
217
|
+
readonly step: "S28";
|
|
218
|
+
readonly says: "The sealed picture check (gemma4-31b) sees the picture alone, before anything else of the turn is sent, and only blocks: no report, flag or verdict reaches Minds. The gateway sees only that a check request was made, and counts it in the day's checks.";
|
|
219
|
+
}, {
|
|
220
|
+
readonly id: "sealed.post_picture_server_checked";
|
|
221
|
+
readonly status: "planned";
|
|
222
|
+
readonly enforcement: "server_and_client";
|
|
223
|
+
readonly capability: "chat_tier";
|
|
224
|
+
readonly step: "S28";
|
|
225
|
+
readonly says: "A post picture goes into a Sealed chat only once the server's current checks have cleared it, and never while it or its post is held or under a child-safety report; no hash-bank match is claimed.";
|
|
226
|
+
}, {
|
|
227
|
+
readonly id: "sealed.search_via_tinfoil_to_exa";
|
|
228
|
+
readonly status: "planned";
|
|
229
|
+
readonly enforcement: "server_and_client";
|
|
230
|
+
readonly capability: "chat_tier";
|
|
231
|
+
readonly step: "S28";
|
|
232
|
+
readonly says: "Sealed search runs only through Tinfoil's search, which sends the search words and page addresses to Exa, and nothing of the member's account or IP address.";
|
|
233
|
+
}, {
|
|
234
|
+
readonly id: "sealed.search_filters_in_body";
|
|
235
|
+
readonly status: "planned";
|
|
236
|
+
readonly enforcement: "client";
|
|
237
|
+
readonly capability: "chat_tier";
|
|
238
|
+
readonly step: "S29";
|
|
239
|
+
readonly says: "The phone sets the PII and injection filters in the sealed body of every message that searches, and never a location; a search with no filter marker turns search off for the chat.";
|
|
240
|
+
}, {
|
|
241
|
+
readonly id: "sealed.memory_on_phone_only";
|
|
242
|
+
readonly status: "planned";
|
|
243
|
+
readonly enforcement: "device";
|
|
244
|
+
readonly capability: "sealed_history";
|
|
245
|
+
readonly step: "S29";
|
|
246
|
+
readonly says: "Sealed memory and About you live only in the phone's no-backup store, under the account, never on Minds.";
|
|
247
|
+
}, {
|
|
248
|
+
readonly id: "sealed.memory_not_with_search_temporary_or_standard";
|
|
249
|
+
readonly status: "planned";
|
|
250
|
+
readonly enforcement: "client";
|
|
251
|
+
readonly capability: "chat_tier";
|
|
252
|
+
readonly step: "S29";
|
|
253
|
+
readonly says: "Sealed memory and About you never go with a message that searches, a temporary chat or a Standard turn, and memory tags in answers to messages that carry someone else's words are ignored.";
|
|
254
|
+
}, {
|
|
255
|
+
readonly id: "sealed.signout_clears_phone";
|
|
256
|
+
readonly status: "planned";
|
|
257
|
+
readonly enforcement: "client";
|
|
258
|
+
readonly capability: "sealed_history";
|
|
259
|
+
readonly step: "S29";
|
|
260
|
+
readonly says: "Signing out clears the account's Sealed chats, pictures, memory and About you from the phone, after a warning.";
|
|
261
|
+
}, {
|
|
262
|
+
readonly id: "sealed.adult_setting_web_known_adult";
|
|
263
|
+
readonly status: "planned";
|
|
264
|
+
readonly enforcement: "server_and_client";
|
|
265
|
+
readonly capability: null;
|
|
266
|
+
readonly step: "S29";
|
|
267
|
+
readonly says: "The adult setting (D17) changes only on the web, only a member Minds knows is 18 or over can turn it on, and the phone only reads it.";
|
|
268
|
+
}, {
|
|
269
|
+
readonly id: "sealed.search_question_only";
|
|
270
|
+
readonly status: "planned";
|
|
271
|
+
readonly enforcement: "client";
|
|
272
|
+
readonly capability: "chat_tier";
|
|
273
|
+
readonly step: "S29";
|
|
274
|
+
readonly says: "A Sealed message that searches carries only the question: no earlier message, memory, About you or phone picture.";
|
|
275
|
+
}, {
|
|
276
|
+
readonly id: "sealed.search_off_with_others_content";
|
|
277
|
+
readonly status: "planned";
|
|
278
|
+
readonly enforcement: "client";
|
|
279
|
+
readonly capability: "chat_tier";
|
|
280
|
+
readonly step: "S29";
|
|
281
|
+
readonly says: "A Sealed message that carries someone else's words (a post, a members-only post, or messages from any chat) does not search.";
|
|
282
|
+
}, {
|
|
283
|
+
readonly id: "sealed.search_shown_as_sent";
|
|
284
|
+
readonly status: "planned";
|
|
285
|
+
readonly enforcement: "client";
|
|
286
|
+
readonly capability: "chat_tier";
|
|
287
|
+
readonly step: "S29";
|
|
288
|
+
readonly says: "The search words shown are the words Exa got (the filter's redacted query), and each page opened is shown with its full address.";
|
|
289
|
+
}, {
|
|
290
|
+
readonly id: "sealed.temporary_not_saved";
|
|
291
|
+
readonly status: "planned";
|
|
292
|
+
readonly enforcement: "client";
|
|
293
|
+
readonly capability: "sealed_history";
|
|
294
|
+
readonly step: "S29";
|
|
295
|
+
readonly says: "A temporary Sealed chat writes nothing to the phone: no history, picture or memory.";
|
|
296
|
+
}, {
|
|
297
|
+
readonly id: "sealed.services_from_path";
|
|
298
|
+
readonly status: "planned";
|
|
299
|
+
readonly enforcement: "client";
|
|
300
|
+
readonly capability: "chat_tier";
|
|
301
|
+
readonly step: "S29";
|
|
302
|
+
readonly says: "'Other services this chat used' is computed from the path the chat's turns took.";
|
|
303
|
+
}, {
|
|
304
|
+
readonly id: "sealed.export_to_phone_only";
|
|
305
|
+
readonly status: "planned";
|
|
306
|
+
readonly enforcement: "client";
|
|
307
|
+
readonly capability: "sealed_history";
|
|
308
|
+
readonly step: "S29";
|
|
309
|
+
readonly says: "Exporting a Sealed chat writes a file on the phone and sends nothing.";
|
|
79
310
|
}];
|
|
@@ -90,4 +90,268 @@ export const PRIVACY_FACTS = [
|
|
|
90
90
|
step: "S29",
|
|
91
91
|
says: "No Sealed route sends a push notification or writes a notification: a Sealed answer goes back only on the request that asked for it.",
|
|
92
92
|
},
|
|
93
|
+
{
|
|
94
|
+
id: "ai_prefs.agent_enabled",
|
|
95
|
+
status: "planned",
|
|
96
|
+
enforcement: "server_and_client",
|
|
97
|
+
capability: "agent_enabled",
|
|
98
|
+
step: "S9",
|
|
99
|
+
says: "When a member turns their personal agent off, their turns to it make no provider call, it is not created again at sign-in, its chat leaves their conversation list and it sends them no push.",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "ai_prefs.standard_allowed",
|
|
103
|
+
status: "planned",
|
|
104
|
+
enforcement: "server",
|
|
105
|
+
capability: "standard_allowed",
|
|
106
|
+
step: "S28",
|
|
107
|
+
says: "When a member turns Standard AI off, no agent makes a call through OpenRouter or a model maker for a turn they send.",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "ai_prefs.memory_facts",
|
|
111
|
+
status: "planned",
|
|
112
|
+
enforcement: "server",
|
|
113
|
+
capability: "memory_facts",
|
|
114
|
+
step: "S11",
|
|
115
|
+
says: "When a member turns memory off, no agent in the app saves new facts about them, and saved facts are not sent with their turns.",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "ai_prefs.memory_recall",
|
|
119
|
+
status: "planned",
|
|
120
|
+
enforcement: "server",
|
|
121
|
+
capability: "memory_recall",
|
|
122
|
+
step: "S11",
|
|
123
|
+
says: "When a member turns off using their other chats, a turn carries only that chat's own messages and, while memory is on, saved facts.",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "ai_prefs.memory_summaries",
|
|
127
|
+
status: "planned",
|
|
128
|
+
enforcement: "server",
|
|
129
|
+
capability: "memory_summaries",
|
|
130
|
+
step: "S11",
|
|
131
|
+
says: "When a member turns summaries off, no summary call is made for their chats.",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "ai_prefs.chat_memory",
|
|
135
|
+
status: "planned",
|
|
136
|
+
enforcement: "server",
|
|
137
|
+
capability: "chat_memory",
|
|
138
|
+
step: "S13",
|
|
139
|
+
says: "When memory is off for one chat or one turn, nothing from it is saved to memory or used in other chats, and saved facts are not sent from it.",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "ai_prefs.activity",
|
|
143
|
+
status: "planned",
|
|
144
|
+
enforcement: "server",
|
|
145
|
+
capability: "activity",
|
|
146
|
+
step: "S26",
|
|
147
|
+
says: "Who a member follows, what they upvoted, their groups and their interests go with a turn only for the parts they ticked, and only once they decided.",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "ai_prefs.feed_from_questions",
|
|
151
|
+
status: "planned",
|
|
152
|
+
enforcement: "server_and_client",
|
|
153
|
+
capability: "feed_from_questions",
|
|
154
|
+
step: "S26",
|
|
155
|
+
says: "Asking an agent about a post is stored as a feed signal only when the member turned that on, and only the post id is stored, never the question.",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "ai_prefs.push_preview",
|
|
159
|
+
status: "enforced",
|
|
160
|
+
enforcement: "server",
|
|
161
|
+
capability: "push_preview",
|
|
162
|
+
step: "S16",
|
|
163
|
+
says: "A push for a message an AI agent sends in a chat carries the agent's name and none of the message's words, unless the member turned the preview on; a push whose sender cannot be read carries none of its words either.",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "ai_prefs.standard_history_days",
|
|
167
|
+
status: "planned",
|
|
168
|
+
enforcement: "server",
|
|
169
|
+
capability: "standard_history_days",
|
|
170
|
+
step: "S24",
|
|
171
|
+
says: "When a member picks 7 or 30 days, a daily job deletes their one-on-one agent chats older than that, with the chats' summaries and usage copies; billing rows without content stay.",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "notifications.ai_message",
|
|
175
|
+
status: "planned",
|
|
176
|
+
enforcement: "server",
|
|
177
|
+
capability: "push_ai_replies",
|
|
178
|
+
step: "S16",
|
|
179
|
+
says: "Pushes for agent replies have their own setting, separate from messages from people, and start from the member's Messages push setting.",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "reports.ai_review_filter",
|
|
183
|
+
status: "planned",
|
|
184
|
+
enforcement: "server",
|
|
185
|
+
capability: "report_ai_review",
|
|
186
|
+
step: "S20",
|
|
187
|
+
says: "An agent's key cannot list, resolve or dismiss a report whose reporter turned AI review off.",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "posts.ai_ask_allowed",
|
|
191
|
+
status: "planned",
|
|
192
|
+
enforcement: "server_and_client",
|
|
193
|
+
capability: "allow_ask_on_my_posts",
|
|
194
|
+
step: "S22",
|
|
195
|
+
says: "When an author turns asks off, agents do not read their posts from links, and each post carries ai_ask_allowed false so apps hide the ask option.",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: "posts.public_posts_ai",
|
|
199
|
+
status: "planned",
|
|
200
|
+
enforcement: "server",
|
|
201
|
+
capability: "public_posts_ai",
|
|
202
|
+
step: "S22",
|
|
203
|
+
says: "When an author turns it off, their public posts are not embedded, topic or leaning labelled, or picked for editions, and existing vectors and those labels are removed; safety labels still run.",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "openrouter.deny_all_paths",
|
|
207
|
+
status: "planned",
|
|
208
|
+
enforcement: "server",
|
|
209
|
+
capability: null,
|
|
210
|
+
step: "S4",
|
|
211
|
+
says: "Every OpenRouter request, on the direct, fallback and summary paths, asks for providers whose stated policy is not to collect data (data_collection: deny). That is their policy; it is not checked.",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "account.export_ai",
|
|
215
|
+
status: "planned",
|
|
216
|
+
enforcement: "server",
|
|
217
|
+
capability: "memory_manage",
|
|
218
|
+
step: "S18",
|
|
219
|
+
says: "The account download includes the member's agent chats, the agents' replies, what agents saved about them and their agent's settings.",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "account.delete_personal_agent",
|
|
223
|
+
status: "planned",
|
|
224
|
+
enforcement: "server",
|
|
225
|
+
capability: null,
|
|
226
|
+
step: "S18",
|
|
227
|
+
says: "Deleting an account deletes the member's personal agent, its instructions, its saved preferences and its decision rows.",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: "sealed.guard_checks_every_turn",
|
|
231
|
+
status: "planned",
|
|
232
|
+
enforcement: "client",
|
|
233
|
+
capability: "chat_tier",
|
|
234
|
+
step: "S29",
|
|
235
|
+
says: "Every Sealed message and every Sealed answer goes through the sealed text check (gpt-oss-safeguard-120b) before it is sent or shown; with no check, nothing is sent.",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: "sealed.picture_checked_on_phone",
|
|
239
|
+
status: "planned",
|
|
240
|
+
enforcement: "device",
|
|
241
|
+
capability: "chat_tier",
|
|
242
|
+
step: "S29",
|
|
243
|
+
says: "Every phone picture added to a Sealed chat passes the on-phone nudity check first; one it flags stays on the phone unless the member is a known adult whose adult setting is on, in a build that applies the adult policy.",
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: "sealed.picture_check_block_only",
|
|
247
|
+
status: "planned",
|
|
248
|
+
enforcement: "server_and_client",
|
|
249
|
+
capability: "chat_tier",
|
|
250
|
+
step: "S28",
|
|
251
|
+
says: "The sealed picture check (gemma4-31b) sees the picture alone, before anything else of the turn is sent, and only blocks: no report, flag or verdict reaches Minds. The gateway sees only that a check request was made, and counts it in the day's checks.",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "sealed.post_picture_server_checked",
|
|
255
|
+
status: "planned",
|
|
256
|
+
enforcement: "server_and_client",
|
|
257
|
+
capability: "chat_tier",
|
|
258
|
+
step: "S28",
|
|
259
|
+
says: "A post picture goes into a Sealed chat only once the server's current checks have cleared it, and never while it or its post is held or under a child-safety report; no hash-bank match is claimed.",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "sealed.search_via_tinfoil_to_exa",
|
|
263
|
+
status: "planned",
|
|
264
|
+
enforcement: "server_and_client",
|
|
265
|
+
capability: "chat_tier",
|
|
266
|
+
step: "S28",
|
|
267
|
+
says: "Sealed search runs only through Tinfoil's search, which sends the search words and page addresses to Exa, and nothing of the member's account or IP address.",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "sealed.search_filters_in_body",
|
|
271
|
+
status: "planned",
|
|
272
|
+
enforcement: "client",
|
|
273
|
+
capability: "chat_tier",
|
|
274
|
+
step: "S29",
|
|
275
|
+
says: "The phone sets the PII and injection filters in the sealed body of every message that searches, and never a location; a search with no filter marker turns search off for the chat.",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: "sealed.memory_on_phone_only",
|
|
279
|
+
status: "planned",
|
|
280
|
+
enforcement: "device",
|
|
281
|
+
capability: "sealed_history",
|
|
282
|
+
step: "S29",
|
|
283
|
+
says: "Sealed memory and About you live only in the phone's no-backup store, under the account, never on Minds.",
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: "sealed.memory_not_with_search_temporary_or_standard",
|
|
287
|
+
status: "planned",
|
|
288
|
+
enforcement: "client",
|
|
289
|
+
capability: "chat_tier",
|
|
290
|
+
step: "S29",
|
|
291
|
+
says: "Sealed memory and About you never go with a message that searches, a temporary chat or a Standard turn, and memory tags in answers to messages that carry someone else's words are ignored.",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "sealed.signout_clears_phone",
|
|
295
|
+
status: "planned",
|
|
296
|
+
enforcement: "client",
|
|
297
|
+
capability: "sealed_history",
|
|
298
|
+
step: "S29",
|
|
299
|
+
says: "Signing out clears the account's Sealed chats, pictures, memory and About you from the phone, after a warning.",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: "sealed.adult_setting_web_known_adult",
|
|
303
|
+
status: "planned",
|
|
304
|
+
enforcement: "server_and_client",
|
|
305
|
+
capability: null,
|
|
306
|
+
step: "S29",
|
|
307
|
+
says: "The adult setting (D17) changes only on the web, only a member Minds knows is 18 or over can turn it on, and the phone only reads it.",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: "sealed.search_question_only",
|
|
311
|
+
status: "planned",
|
|
312
|
+
enforcement: "client",
|
|
313
|
+
capability: "chat_tier",
|
|
314
|
+
step: "S29",
|
|
315
|
+
says: "A Sealed message that searches carries only the question: no earlier message, memory, About you or phone picture.",
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "sealed.search_off_with_others_content",
|
|
319
|
+
status: "planned",
|
|
320
|
+
enforcement: "client",
|
|
321
|
+
capability: "chat_tier",
|
|
322
|
+
step: "S29",
|
|
323
|
+
says: "A Sealed message that carries someone else's words (a post, a members-only post, or messages from any chat) does not search.",
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: "sealed.search_shown_as_sent",
|
|
327
|
+
status: "planned",
|
|
328
|
+
enforcement: "client",
|
|
329
|
+
capability: "chat_tier",
|
|
330
|
+
step: "S29",
|
|
331
|
+
says: "The search words shown are the words Exa got (the filter's redacted query), and each page opened is shown with its full address.",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: "sealed.temporary_not_saved",
|
|
335
|
+
status: "planned",
|
|
336
|
+
enforcement: "client",
|
|
337
|
+
capability: "sealed_history",
|
|
338
|
+
step: "S29",
|
|
339
|
+
says: "A temporary Sealed chat writes nothing to the phone: no history, picture or memory.",
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: "sealed.services_from_path",
|
|
343
|
+
status: "planned",
|
|
344
|
+
enforcement: "client",
|
|
345
|
+
capability: "chat_tier",
|
|
346
|
+
step: "S29",
|
|
347
|
+
says: "'Other services this chat used' is computed from the path the chat's turns took.",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: "sealed.export_to_phone_only",
|
|
351
|
+
status: "planned",
|
|
352
|
+
enforcement: "client",
|
|
353
|
+
capability: "sealed_history",
|
|
354
|
+
step: "S29",
|
|
355
|
+
says: "Exporting a Sealed chat writes a file on the phone and sends nothing.",
|
|
356
|
+
},
|
|
93
357
|
];
|
|
@@ -9,6 +9,8 @@ export interface LinkPreview {
|
|
|
9
9
|
siteName: string | null;
|
|
10
10
|
}
|
|
11
11
|
export interface LinkPreviewResponse {
|
|
12
|
+
/** Relayed preview images expire; refetch their metadata before this time. */
|
|
13
|
+
media_expires_at?: string;
|
|
12
14
|
/**
|
|
13
15
|
* The preview, or null when the link cannot be previewed: the page refused a
|
|
14
16
|
* server (a private repository, a paywall or bot wall), is not HTML, or could
|
|
@@ -69,5 +71,7 @@ export declare class LinkPreviewResource {
|
|
|
69
71
|
* @example
|
|
70
72
|
* const { data, refused } = await r.linkPreview.get(url, { messageId: message.id });
|
|
71
73
|
*/
|
|
72
|
-
get(url: string, source?: LinkPreviewSource | null
|
|
74
|
+
get(url: string, source?: LinkPreviewSource | null, options?: {
|
|
75
|
+
projectId: string;
|
|
76
|
+
}): Promise<LinkPreviewResponse>;
|
|
73
77
|
}
|
|
@@ -28,8 +28,12 @@ export class LinkPreviewResource {
|
|
|
28
28
|
* @example
|
|
29
29
|
* const { data, refused } = await r.linkPreview.get(url, { messageId: message.id });
|
|
30
30
|
*/
|
|
31
|
-
async get(url, source) {
|
|
31
|
+
async get(url, source, options) {
|
|
32
32
|
const params = { url };
|
|
33
|
+
if (options) {
|
|
34
|
+
params.project_id = options.projectId;
|
|
35
|
+
params.relay_media = '1';
|
|
36
|
+
}
|
|
33
37
|
if (source != null) {
|
|
34
38
|
// A source with no id must not turn into a bare request: the server would
|
|
35
39
|
// then fetch the link with no check at all.
|
|
@@ -12,9 +12,31 @@ export interface GeneratedImage {
|
|
|
12
12
|
provider: ImageProvider;
|
|
13
13
|
prompt: string;
|
|
14
14
|
}
|
|
15
|
+
export interface MediaSourceApprovalInput {
|
|
16
|
+
projectId: string;
|
|
17
|
+
sources: Array<{
|
|
18
|
+
host: string;
|
|
19
|
+
purpose: 'video' | 'audio' | 'player' | 'icon';
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export interface MediaSourceApprovalResponse {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
enforce: boolean;
|
|
25
|
+
policy: string;
|
|
26
|
+
owned_hosts: string[];
|
|
27
|
+
sources: Array<{
|
|
28
|
+
host: string;
|
|
29
|
+
purpose: 'video' | 'audio' | 'player' | 'icon';
|
|
30
|
+
decision: 'approved' | 'relay' | 'pending';
|
|
31
|
+
expires_at: string | null;
|
|
32
|
+
proxy_url?: string | null;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
15
35
|
export declare class MediaResource {
|
|
16
36
|
private client;
|
|
17
37
|
constructor(client: HttpClient);
|
|
38
|
+
/** Read cached automatic provider reviews. Sends host/purpose only; never initiates a policy or media fetch. */
|
|
39
|
+
sourceApprovals(input: MediaSourceApprovalInput): Promise<SingleResponse<MediaSourceApprovalResponse>>;
|
|
18
40
|
/** Generate an AI image and return a URL for use in posts */
|
|
19
41
|
generateImage(input: GenerateImageInput): Promise<SingleResponse<GeneratedImage>>;
|
|
20
42
|
}
|
package/dist/resources/media.js
CHANGED
|
@@ -3,6 +3,10 @@ export class MediaResource {
|
|
|
3
3
|
constructor(client) {
|
|
4
4
|
this.client = client;
|
|
5
5
|
}
|
|
6
|
+
/** Read cached automatic provider reviews. Sends host/purpose only; never initiates a policy or media fetch. */
|
|
7
|
+
sourceApprovals(input) {
|
|
8
|
+
return this.client.get('/media/source-approvals', { project_id: input.projectId, sources: JSON.stringify(input.sources) });
|
|
9
|
+
}
|
|
6
10
|
/** Generate an AI image and return a URL for use in posts */
|
|
7
11
|
generateImage(input) {
|
|
8
12
|
return this.client.post('/media/generate', input);
|
|
@@ -1288,6 +1288,48 @@ export interface SocialAuthorFeed {
|
|
|
1288
1288
|
cursor: string | null;
|
|
1289
1289
|
coverage: 'original_posts';
|
|
1290
1290
|
}
|
|
1291
|
+
/** A Fediverse community (a Lemmy or PieFed community) as its threads page names it (0.7.109). */
|
|
1292
|
+
export interface OpenGroupThreadsGroup {
|
|
1293
|
+
/** Stable (`ap-…`) and never a Minds group id: never send it to a Minds groups route. */
|
|
1294
|
+
id: string;
|
|
1295
|
+
network: 'activitypub';
|
|
1296
|
+
/** The network as members read it ("ActivityPub"). */
|
|
1297
|
+
network_label: string;
|
|
1298
|
+
/** The community's Group actor id on its own server: the `group` its page is read by. */
|
|
1299
|
+
nativeId: string;
|
|
1300
|
+
/** `name@server`, as the network writes the community's address. */
|
|
1301
|
+
handle: string;
|
|
1302
|
+
name: string;
|
|
1303
|
+
/** Plain text. */
|
|
1304
|
+
description: string;
|
|
1305
|
+
/** The host of the server it lives on. */
|
|
1306
|
+
server: string;
|
|
1307
|
+
/** A community is read here, not joined (phase 1). */
|
|
1308
|
+
read_only: true;
|
|
1309
|
+
joinable: false;
|
|
1310
|
+
}
|
|
1311
|
+
/** A page of a Fediverse community's newest threads (`getOpenGroupThreads`, 0.7.109). */
|
|
1312
|
+
export interface OpenGroupThreads {
|
|
1313
|
+
group: OpenGroupThreadsGroup;
|
|
1314
|
+
/** Each thread's opening post, newest first, after every floor the server applies for this member. */
|
|
1315
|
+
items: SocialPost[];
|
|
1316
|
+
/** The next page's opaque cursor, or null at the end of what the community's server listed. */
|
|
1317
|
+
cursor: string | null;
|
|
1318
|
+
/** The community's newest threads as its own server's outbox lists them; older threads are not read. */
|
|
1319
|
+
coverage: 'community_outbox';
|
|
1320
|
+
/** Present when `compass` asked for a filter: what Hide politics left out. */
|
|
1321
|
+
compass_filter?: CompassFilterMeta;
|
|
1322
|
+
}
|
|
1323
|
+
export interface OpenGroupThreadsParams {
|
|
1324
|
+
/** The community's Group actor id (an https address): the `actor` of a groups read's `open_network` entry. */
|
|
1325
|
+
group: string;
|
|
1326
|
+
/** 1 to 25; the server's default is 20. */
|
|
1327
|
+
limit?: number;
|
|
1328
|
+
/** The `cursor` of the page before, as it was given. */
|
|
1329
|
+
cursor?: string;
|
|
1330
|
+
/** A Politics choice as `getPublicTrends` takes it: `hide` leaves out the threads in politics. */
|
|
1331
|
+
compass?: string;
|
|
1332
|
+
}
|
|
1291
1333
|
export interface SocialTrendProvenance {
|
|
1292
1334
|
discovery?: DiscoveryLayout;
|
|
1293
1335
|
provider: {
|
|
@@ -1944,6 +1986,17 @@ export declare class ProtocolsResource {
|
|
|
1944
1986
|
limit?: number;
|
|
1945
1987
|
cursor?: string;
|
|
1946
1988
|
}): Promise<SingleResponse<SocialAuthorFeed>>;
|
|
1989
|
+
/**
|
|
1990
|
+
* A Fediverse community's newest threads, read-only (0.7.109; GET /protocols/social/groups/threads): a Lemmy or PieFed
|
|
1991
|
+
* community that a groups read (`communities.list`, the group recommender) lists under `open_network`, read by its
|
|
1992
|
+
* `actor` as `group`. Every floor the server applies for this member is applied: the group words and tier-1 rules, the
|
|
1993
|
+
* app's moderation and the member's own blocks and mutes of the community, authors who opted out of indexing, the term
|
|
1994
|
+
* list and the safety verdict, and #nsfw threads unless the member turned Show NSFW on. Requires `users:read` and
|
|
1995
|
+
* `posts:read` on the member's app key. A 404 means the app does not offer groups from other networks
|
|
1996
|
+
* (OPEN_GROUPS_AP_ENABLED off) or does not list that community; a 400 an address it does not read; a 503 that the
|
|
1997
|
+
* community's server could not be read.
|
|
1998
|
+
*/
|
|
1999
|
+
getOpenGroupThreads(params: OpenGroupThreadsParams, options?: SocialReadOptions): Promise<SingleResponse<OpenGroupThreads>>;
|
|
1947
2000
|
listSubscriptions(): Promise<SingleResponse<SocialSubscription[]>>;
|
|
1948
2001
|
/** Recent originals from the current app user's followed accounts, with explicit source failures. */
|
|
1949
2002
|
getFollowingFeed(params?: {
|
|
@@ -231,6 +231,23 @@ export class ProtocolsResource {
|
|
|
231
231
|
protocol: params.protocol, actor: params.actor, limit: params.limit, cursor: params.cursor,
|
|
232
232
|
});
|
|
233
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* A Fediverse community's newest threads, read-only (0.7.109; GET /protocols/social/groups/threads): a Lemmy or PieFed
|
|
236
|
+
* community that a groups read (`communities.list`, the group recommender) lists under `open_network`, read by its
|
|
237
|
+
* `actor` as `group`. Every floor the server applies for this member is applied: the group words and tier-1 rules, the
|
|
238
|
+
* app's moderation and the member's own blocks and mutes of the community, authors who opted out of indexing, the term
|
|
239
|
+
* list and the safety verdict, and #nsfw threads unless the member turned Show NSFW on. Requires `users:read` and
|
|
240
|
+
* `posts:read` on the member's app key. A 404 means the app does not offer groups from other networks
|
|
241
|
+
* (OPEN_GROUPS_AP_ENABLED off) or does not list that community; a 400 an address it does not read; a 503 that the
|
|
242
|
+
* community's server could not be read.
|
|
243
|
+
*/
|
|
244
|
+
getOpenGroupThreads(params, options) {
|
|
245
|
+
// Only the four keys the server's strict query schema takes, each only when given.
|
|
246
|
+
return this.client.get('/protocols/social/groups/threads', { group: params.group,
|
|
247
|
+
...(params.limit !== undefined ? { limit: params.limit } : {}),
|
|
248
|
+
...(params.cursor !== undefined ? { cursor: params.cursor } : {}),
|
|
249
|
+
...(params.compass !== undefined ? { compass: params.compass } : {}) }, ...readOptions(options));
|
|
250
|
+
}
|
|
234
251
|
listSubscriptions() {
|
|
235
252
|
return this.client.get('/protocols/social/subscriptions');
|
|
236
253
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { SingleResponse } from '../types.js';
|
|
3
|
+
export type ProductEvent = 'auth.otp_verified' | 'community.join' | 'person.follow' | 'network.connect_started' | 'network.connect_finished' | 'social.follow_submitted' | 'social.like_submitted' | 'social.repost_submitted' | 'social.post_submitted' | 'social.reply_submitted' | 'signal' | 'screen.view';
|
|
4
|
+
export interface ProductCountsInput {
|
|
5
|
+
counts: Array<{
|
|
6
|
+
event: ProductEvent;
|
|
7
|
+
count: number;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export declare class TelemetryResource {
|
|
11
|
+
private readonly client;
|
|
12
|
+
constructor(client: HttpClient);
|
|
13
|
+
/** Count reviewed events within the member's app. No paths, member IDs or properties; requires an app-bound key. */
|
|
14
|
+
productCounts(input: ProductCountsInput): Promise<SingleResponse<{
|
|
15
|
+
accepted: boolean;
|
|
16
|
+
}>>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class TelemetryResource {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client) {
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
/** Count reviewed events within the member's app. No paths, member IDs or properties; requires an app-bound key. */
|
|
7
|
+
productCounts(input) {
|
|
8
|
+
return this.client.post('/telemetry/product-counts', input, { retry: false });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
|
-
import type { PaginatedResponse, SingleResponse } from '../types.js';
|
|
2
|
+
import type { DeleteResponse, PaginatedResponse, SingleResponse } from '../types.js';
|
|
3
3
|
interface WalletInfo {
|
|
4
4
|
configured: boolean;
|
|
5
5
|
address: string | null;
|
|
@@ -7,12 +7,41 @@ interface WalletInfo {
|
|
|
7
7
|
balance_wei: string | null;
|
|
8
8
|
user_index: number | null;
|
|
9
9
|
is_smart_account: boolean | null;
|
|
10
|
+
/** 'custodial_wallet_off_for_members' when a member asks: Minds-held wallets are for agents; members link their own (`createLinkNonce`, `linkWallet`). */
|
|
11
|
+
reason?: string;
|
|
10
12
|
}
|
|
11
13
|
interface WalletBalance {
|
|
12
14
|
configured: boolean;
|
|
13
15
|
address: string | null;
|
|
14
16
|
balance: string | null;
|
|
15
17
|
balance_wei: string | null;
|
|
18
|
+
/** 'custodial_wallet_off_for_members' when a member asks. */
|
|
19
|
+
reason?: string;
|
|
20
|
+
}
|
|
21
|
+
/** A nonce for a wallet link message (EIP-4361), from `createLinkNonce`. */
|
|
22
|
+
export interface WalletLinkNonce {
|
|
23
|
+
/** Put this in the message's `Nonce:` line. One waits per account for ten minutes; asking again replaces it. */
|
|
24
|
+
nonce: string;
|
|
25
|
+
issued_at: string;
|
|
26
|
+
expires_at: string;
|
|
27
|
+
/** The statement line the message must carry word for word; a message without it, or with another one, is refused (`siwe_message_invalid`). */
|
|
28
|
+
statement: string;
|
|
29
|
+
/** The hosts a link message may name as its domain. */
|
|
30
|
+
domains: string[];
|
|
31
|
+
/** The chain ids a link may name. */
|
|
32
|
+
chain_ids: number[];
|
|
33
|
+
/** The EIP-4361 text to sign, when address, chain id, domain and uri were sent; otherwise null. */
|
|
34
|
+
message: string | null;
|
|
35
|
+
}
|
|
36
|
+
/** A wallet on the account: one the member linked by signature, or one imported with their account. */
|
|
37
|
+
export interface WalletLink {
|
|
38
|
+
chain: string;
|
|
39
|
+
chain_id: number | null;
|
|
40
|
+
/** EIP-55 checksummed for a signed link. */
|
|
41
|
+
address: string;
|
|
42
|
+
/** When a signature proved the member holds it; null for an imported address never proved. */
|
|
43
|
+
verified_at: string | null;
|
|
44
|
+
imported: boolean;
|
|
16
45
|
}
|
|
17
46
|
interface WalletConfig {
|
|
18
47
|
configured: boolean;
|
|
@@ -94,7 +123,36 @@ export declare class WalletResource {
|
|
|
94
123
|
payoutLoginLink(): Promise<SingleResponse<{
|
|
95
124
|
url: string;
|
|
96
125
|
}>>;
|
|
97
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Start linking a wallet the member holds. Returns a one-time nonce (ten
|
|
128
|
+
* minutes). Pass the wallet's address, its chain id, the app's host and URL to
|
|
129
|
+
* also get the EIP-4361 message text to sign. Minds never holds the wallet's
|
|
130
|
+
* key and the wallet is never the login.
|
|
131
|
+
*/
|
|
132
|
+
createLinkNonce(params?: {
|
|
133
|
+
address: string;
|
|
134
|
+
chainId: number;
|
|
135
|
+
domain: string;
|
|
136
|
+
uri: string;
|
|
137
|
+
}): Promise<SingleResponse<WalletLinkNonce>>;
|
|
138
|
+
/**
|
|
139
|
+
* Link the wallet that signed `message` (a Sign-In with Ethereum message
|
|
140
|
+
* carrying a nonce from `createLinkNonce`). Plain keys and smart wallets
|
|
141
|
+
* (EIP-1271, and EIP-6492 before deployment) both link. A nonce works once.
|
|
142
|
+
* The link is screened like a send before it is written: 451 for a blocked or
|
|
143
|
+
* unknown region, 403 `wallet_address_sanctioned`, 503 when screening cannot answer.
|
|
144
|
+
*/
|
|
145
|
+
linkWallet(message: string, signature: string): Promise<SingleResponse<WalletLink & {
|
|
146
|
+
signer: 'key' | 'smart_wallet';
|
|
147
|
+
}>>;
|
|
148
|
+
/** The member's wallets: links they signed and addresses imported with their account. */
|
|
149
|
+
listLinkedWallets(): Promise<{
|
|
150
|
+
data: WalletLink[];
|
|
151
|
+
meta: Record<string, never>;
|
|
152
|
+
}>;
|
|
153
|
+
/** Unlink an address, on one chain (e.g. 'base') or on every chain when none is named. */
|
|
154
|
+
unlinkWallet(address: string, chain?: string): Promise<DeleteResponse>;
|
|
155
|
+
/** Send ETH from the current user's Minds-held wallet (agents; off for members, who link their own wallet) */
|
|
98
156
|
send(toAddress: string, amountEth: string): Promise<SingleResponse<unknown>>;
|
|
99
157
|
/** Check wallet system configuration (admin) */
|
|
100
158
|
getConfig(): Promise<SingleResponse<WalletConfig>>;
|
package/dist/resources/wallet.js
CHANGED
|
@@ -31,7 +31,35 @@ export class WalletResource {
|
|
|
31
31
|
payoutLoginLink() {
|
|
32
32
|
return this.client.post('/wallet/payouts/login-link', {});
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Start linking a wallet the member holds. Returns a one-time nonce (ten
|
|
36
|
+
* minutes). Pass the wallet's address, its chain id, the app's host and URL to
|
|
37
|
+
* also get the EIP-4361 message text to sign. Minds never holds the wallet's
|
|
38
|
+
* key and the wallet is never the login.
|
|
39
|
+
*/
|
|
40
|
+
createLinkNonce(params) {
|
|
41
|
+
return this.client.post('/wallet/links/nonce', params ? { address: params.address, chain_id: params.chainId, domain: params.domain, uri: params.uri } : {});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Link the wallet that signed `message` (a Sign-In with Ethereum message
|
|
45
|
+
* carrying a nonce from `createLinkNonce`). Plain keys and smart wallets
|
|
46
|
+
* (EIP-1271, and EIP-6492 before deployment) both link. A nonce works once.
|
|
47
|
+
* The link is screened like a send before it is written: 451 for a blocked or
|
|
48
|
+
* unknown region, 403 `wallet_address_sanctioned`, 503 when screening cannot answer.
|
|
49
|
+
*/
|
|
50
|
+
linkWallet(message, signature) {
|
|
51
|
+
return this.client.post('/wallet/links', { message, signature });
|
|
52
|
+
}
|
|
53
|
+
/** The member's wallets: links they signed and addresses imported with their account. */
|
|
54
|
+
listLinkedWallets() {
|
|
55
|
+
return this.client.get('/wallet/links');
|
|
56
|
+
}
|
|
57
|
+
/** Unlink an address, on one chain (e.g. 'base') or on every chain when none is named. */
|
|
58
|
+
unlinkWallet(address, chain) {
|
|
59
|
+
const query = chain ? `?chain=${encodeURIComponent(chain)}` : '';
|
|
60
|
+
return this.client.delete(`/wallet/links/${encodeURIComponent(address)}${query}`);
|
|
61
|
+
}
|
|
62
|
+
/** Send ETH from the current user's Minds-held wallet (agents; off for members, who link their own wallet) */
|
|
35
63
|
send(toAddress, amountEth) {
|
|
36
64
|
return this.client.post('/wallet/send', { to_address: toAddress, amount_eth: amountEth });
|
|
37
65
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -242,6 +242,20 @@ export interface MediaItem {
|
|
|
242
242
|
* of Watch and clip queues and show the poster instead. Absent when the file plays or is unjudged.
|
|
243
243
|
*/
|
|
244
244
|
unplayable?: boolean;
|
|
245
|
+
/**
|
|
246
|
+
* On an image only, in the member's own app with Sealed on: the time (ISO 8601) this read found the picture passed
|
|
247
|
+
* Minds' current checks: visibility, holds and adult labels. Where the server's media-safety floor is on
|
|
248
|
+
* (CONTENT_MEDIA_SAFETY_ENABLED), one of those labels is a vision model's stored verdict that the picture is not
|
|
249
|
+
* suggestive; no image classifier is run for this field. No hash bank (NCMEC, StopNCII) and no CSAM classifier is
|
|
250
|
+
* among the checks, so run your own. Absent for any other item, and whenever a check could not be read. Sealed sends
|
|
251
|
+
* a post picture only while this is under 24 hours old.
|
|
252
|
+
*/
|
|
253
|
+
checked_at?: string;
|
|
254
|
+
/**
|
|
255
|
+
* `false` exactly when `checked_at` is set, and absent otherwise: the payload's "not held" mark. Pass the item as it
|
|
256
|
+
* came to `buildSubjectBundle` (`@recursiv/sdk/assistant`), which keeps `checked_at` only beside `held: false`.
|
|
257
|
+
*/
|
|
258
|
+
held?: false;
|
|
245
259
|
}
|
|
246
260
|
/** The group a post is in, as every post payload carries it. */
|
|
247
261
|
export interface PostCommunity {
|
|
@@ -382,6 +396,7 @@ export interface PostRepliesPage {
|
|
|
382
396
|
next_cursor: string | null;
|
|
383
397
|
};
|
|
384
398
|
}
|
|
399
|
+
export type PostDestination = 'minds' | 'activitypub' | 'atproto' | 'nostr';
|
|
385
400
|
export interface CreatePostInput {
|
|
386
401
|
content: string;
|
|
387
402
|
content_format?: 'plain' | 'markdown';
|
|
@@ -393,6 +408,8 @@ export interface CreatePostInput {
|
|
|
393
408
|
/** Tag names to resolve or create atomically when the post is published. */
|
|
394
409
|
tag_names?: string[];
|
|
395
410
|
media_urls?: string[];
|
|
411
|
+
/** Exact publishing selection. Include minds; omitted only by legacy clients. */
|
|
412
|
+
destinations?: PostDestination[];
|
|
396
413
|
}
|
|
397
414
|
export interface CreatedPost {
|
|
398
415
|
id: string;
|
package/llm.md
CHANGED
|
@@ -28,6 +28,7 @@ const r = new Recursiv() // reads RECURSIV_API_KEY from env
|
|
|
28
28
|
| Receive external webhooks (Stripe, etc.) | `r.webhooks.register()` | Custom webhook server |
|
|
29
29
|
| Server-side cron / scheduled tasks | `r.jobs.create()` | External cron services |
|
|
30
30
|
| A member's engagement signals (views, dwell, hides) | `r.signals.batch()` | Raw fetch to `/signals/*`, or a resource's `client` |
|
|
31
|
+
| Coarse first-party product counts | `r.telemetry.productCounts()` | Member IDs, paths, URLs or arbitrary event properties |
|
|
31
32
|
|
|
32
33
|
**IMPORTANT:** Do NOT store structured app data (records, inventory, tasks, settings) as JSON inside `r.posts.content`. Posts are for social content. Use `r.databases` for application data.
|
|
33
34
|
|
|
@@ -825,6 +826,18 @@ Methods: `list`, `get`, `create`, `delete`
|
|
|
825
826
|
|
|
826
827
|
## Platform Resources
|
|
827
828
|
|
|
829
|
+
### r.telemetry — First-party aggregate product counts
|
|
830
|
+
|
|
831
|
+
`productCounts({ counts: [{ event, count }] })` sends reviewed event totals under a human member's project-bound key with `users:write`. It is unbillable, including for a free project with zero credits; authentication, scopes and rate limits still apply. No anonymous or agent keys. Only counts are stored in a network/project/UTC-day bucket, retained at most 30 days; member IDs, session IDs, paths, URLs and extra properties are refused. Use `r.signals` separately for personal feed ranking.
|
|
832
|
+
|
|
833
|
+
`event` is `auth.otp_verified`, `community.join`, `person.follow`, `network.connect_started`, `network.connect_finished`, `social.follow_submitted`, `social.like_submitted`, `social.repost_submitted`, `social.post_submitted`, `social.reply_submitted`, `signal` or `screen.view`. Each `count` is an integer from 1 to 1000; send 1-12 entries. Do not replay an accepted batch: counts increment, and the SDK deliberately does not retry this optional write. `{ data: { accepted: false } }` means storage was unavailable, not that the member's action failed. Catch request failures without blocking the app.
|
|
834
|
+
|
|
835
|
+
```typescript
|
|
836
|
+
// r holds the signed-in member's app-bound key; send no route or member properties.
|
|
837
|
+
const result = await r.telemetry.productCounts({ counts: [{ event: 'screen.view', count: 1 }] }).catch(() => null)
|
|
838
|
+
const counted = result?.data.accepted === true
|
|
839
|
+
```
|
|
840
|
+
|
|
828
841
|
- `r.settings` — User preferences, sessions, password changes, account deletion. `revokeOtherSessions({ keep_session_token? })` signs out every other device and keeps this key (and the proven session); `revokeAllSessions()` also revokes this key. Accounts with no password confirm `requestEmailChange({ new_email, email_code })` with a code from `requestConfirmationCode('email_change')`. With a project-bound key, `getPreferences()` includes app-scoped `content`; persist changes with `updateContent({ show_nsfw?, autoplay_video?, ai_enabled?, default_feed?, sealed_adult? })`; `content.default_feed_chosen` is true once the member set `default_feed` themselves (absent on older APIs). `privacy.age_is_adult` is true only when the member answered the age question 18 or older (false for 13-17 and for no answer; only their own preferences carry it). `content.sealed_adult` is the Sealed adult-content setting: it reads true only while it is on and the member is a known adult, and `updateContent({ sealed_adult: true })` from anyone else throws `AuthorizationError` with code `sealed_adult_needs_adult` and saves nothing in that request; `sealed_adult: false` is always accepted. Both are absent on older APIs (treat as false). Notifications (0.7.72): `getPreferences()` returns `notifications.channels`, each kind (`post_mention`, `post_reply`, `post_repost`, `post_reaction`, `follow`, `message`, and the operator kinds) with `{ inApp, push, email }`; `updateNotifications({ channels: { post_reaction: { push: false } } })` changes that kind only, while `in_app`/`push`/`email` stay master switches over every kind. `disableAccount({ password })` (0.7.71) deactivates the account (hidden, nothing deleted; signing back in restores it), asked once; an API that does not serve deactivation answers 404 `NotFoundError`, so keep the control hidden until yours does.
|
|
829
842
|
- `r.signals` (0.7.71) — The member's engagement signals for their own ranking (For You), under their app-bound key (`agents:write`): `batch(events, { keepalive?, retry? })` sends up to `SIGNAL_BATCH_MAX` (100) `{ event_type, post_id?, dwell_ms?, metadata? }` to POST /signals/batch; `record(event)` sends one to POST /signals/post (for an API before the batch route, which answers that one 404). `event_type`: `view`, `click`, `dwell`, `react_up`, `react_down`, `save`, `hide`, `share`, `mute_source`, `open_chat`, `ask_agent`, `more_like`, `unhide`. Best-effort: a signal the server cannot store answers 200 with `recorded` 0 (or false) and a `reason` (`no_project_scope`, `project_db_unavailable`, `viewing_off`, `rate_limited`, or for `record` `duplicate`). `keepalive: true` is for a flush as the page is hidden or unloads: the request is on the wire before the call returns and is asked once. Only from a public place, never a private message: a `metadata` key or placement (`surface`, `feed`, `source`, `context`, `route`...) naming a chat, DM, inbox, encrypted or Sealed conversation by any spelling (`chatList`, `dmThread`, `/messages/1`) answers 400 (`private_surface`) and nothing in that request is stored; this is read on the metadata as sent, before any key is dropped. An app names its own public places (`{ source: 'discover' }`). In the Minds app a `surface` must be one of `SignalSurface`'s names other than `other` (each a public place the server lists), a `feed`, `source`, `via` or `layoutSource` one the server lists (its feeds, Discover, Watch, stories, the pager and post cards), and any other key naming a place (`context`) is refused there too. With the member's "Use what I watch and read to personalize" off (`settings.updateContent({ personalize_from_viewing: false })`, on by default), only views and explicit choices are stored and `withheld` counts the dwell and opens left out. `clearViewingHistory()` (DELETE /signals/history) is "Clear my viewing history": the member's views, dwell, Watch time and opens in this app are deleted; their hides, "More like this", saves, shares and votes stay. Ingest contract (0.7.93): `metadata` is `SignalMetadata`; the server keeps only its keys (`surface`, `feed`, `block`, `position`, `slot`, `category`, `ref`, `watch_ms`, `duration_ms`, `completed`, `loops`, `sound_on`, `exit` (`swipe`/`leave`/`hide`/`tap`), `visible_count`, `autoplay`, `client_event_id`, `session_seq`, `via`, `source`, `presentation`) and drops the rest; a listed key of the wrong type or past its limit refuses that event (batch: `rejected` + the first ten `rejections` `{ index, reason, key }`, the rest stored; record: 400 `invalid_signal`). `batch`/`record` add a `client_event_id` to each event without one (the same id each time that object is sent), so a retry is stored once (`duplicates`). An open-network item is named by `metadata.ref` (`<protocol>/<native id>`, `cluster:<id>`, `today:<uuid>`). Watch time is held to `duration_ms` x (`loops` + 1) and to the wall clock, a card's dwell to 5 min; a watch under 1 s ending in `exit: 'swipe'` is stored as a skip. Past 600 events a minute per member the rest are `limited` (record: `reason: 'rate_limited'`).
|
|
830
843
|
|
|
@@ -851,7 +864,7 @@ const showCheckedLine = isFactLive('sealed.attest_before_send', capabilities, {
|
|
|
851
864
|
- `r.email` — Email campaigns, batch sending, and transactional email. **Pass `category: 'transactional'`** for receipts, invites, confirmations and alerts: `r.email.send({ to, subject, html, category: 'transactional' })`. Without it the send is treated as marketing and held for anyone who has not given marketing consent. A send where every recipient was held throws `EmailNotSentError` (its message carries the reason). A `status: 'partial'` result lists each recipient's `status` and `reason` in `messages`.
|
|
852
865
|
- `r.webhooks` — Register inbound webhook endpoints: `register()`, `list()`, `delete()`. Webhook events execute handler code in project sandbox.
|
|
853
866
|
- `r.jobs` — Scheduled cron jobs: `create({ name, cron, handler_code })`, `list()`, `update()`, `delete()`. Handlers execute in project sandbox on schedule.
|
|
854
|
-
- `r.wallet` — Wallet, MINDS tokens (`getTokenBalance`, `getTokenLedger`; wei stays a string), USD payouts (`getPayouts`, `startPayoutOnboarding`, `payoutLoginLink`)
|
|
867
|
+
- `r.wallet` — Wallet, MINDS tokens (`getTokenBalance`, `getTokenLedger`; wei stays a string), USD payouts (`getPayouts`, `startPayoutOnboarding`, `payoutLoginLink`). Link a wallet the member holds (0.7.104; the server answers 404 `wallet_link_disabled` until it turns linking on): `const { data: n } = await r.wallet.createLinkNonce({ address, chainId: 8453, domain: 'minds.com', uri: 'https://minds.com' })`, have the wallet sign `n.message` (a message you build yourself must carry `n.statement` word for word), then `await r.wallet.linkWallet(n.message, signature)` (plain keys and smart wallets, EIP-1271/6492; a nonce works once; 409 `wallet_linked_to_another_account`; screened like a send: 451 blocked or unknown region, 403 `wallet_address_sanctioned`, 503 `sanctions_screening_unavailable`); `listLinkedWallets()`, `unlinkWallet(address, chain?)`. The Minds-held wallet (`getMyWallet`, `getBalance`, `send`) is for agents: a member gets `configured: false` with `reason: 'custodial_wallet_off_for_members'` and `send` answers 403.
|
|
855
868
|
- `r.freeTier` — Free tier limits and status
|
|
856
869
|
|
|
857
870
|
```typescript
|
|
@@ -873,7 +886,7 @@ await r.appSubscriptions.changeTier({ tier: 'pro', proration_date: quote.prorati
|
|
|
873
886
|
- Native follows: `nativeFollowState({ accountId, protocol, nativeId })`, `prepareNativeFollow({ clientMutationId, accountId, protocol, nativeId, action: "follow" | "unfollow" })`, `submitNativeFollow(id, { approve: true, event? })`, `nativeFollowStatus(id)`. Show the acting identity and native following/requested/unfollowed state separately from the Minds feed subscription. Nostr must sign the complete review event without dropping other contacts; stale lists require a fresh review. Keep mutation IDs and reconcile ambiguous outcomes without replay. Requires current follow scopes and API `NATIVE_SOCIAL_FOLLOW_PROJECTS` plus source controls.
|
|
874
887
|
- Native reposts: `nativeRepostState({ accountId, protocol, nativeId })`, `prepareNativeRepost({ clientMutationId, accountId, protocol, nativeId, action: "repost" | "unrepost" })`, `submitNativeRepost(id, { approve: true, event? })`, `nativeRepostStatus(id)`. Requires `NATIVE_SOCIAL_REPOST_PROJECTS`, account capability and explicit approval; Nostr signs the exact review (kind 6 or own kind 5). Unknown attempts only reconcile. Bluesky connections need the new repost scope; ActivityPub uses write:statuses. Native likes: `nativeLikeState({ accountId, protocol, nativeId })`, `prepareNativeLike({ clientMutationId, accountId, protocol, nativeId, action: "like" | "unlike" })`, `submitNativeLike(id, { approve: true, event? })`, and `nativeLikeStatus(id)`. Display the selected account, source and action before approval. Only Nostr supplies `event`: sign the exact public `signing.event` in the review; never transmit a private key. Preserve mutation IDs on ambiguous responses and refresh unknown results instead of resending. Read capabilities before offering actions; old reply-only connections need reconnecting for likes. Nostr results describe the connected relay, not every public relay. The app-specific `NATIVE_SOCIAL_LIKE_PROJECTS` switch and source controls must be enabled on the API.
|
|
875
888
|
- Delegated social accounts: `r.protocols.nativeSocialAccounts()`, `startNativeSocialConnection({ protocol, identifier })`, `finishNativeSocialConnection({ protocol, state, code?, iss?, error? })`, `disconnectNativeSocialAccount(id)`. Example: `const { data } = await r.protocols.startNativeSocialConnection({ protocol: "bluesky", identifier: "alice.bsky.social" })`; open `data.authorizationUrl`, then complete with the same signed-in app user. Never log callback codes, send passwords, or auto-retry OAuth callbacks. Shared replies (off until schema/API deployment and live verification): `prepareNativeReply({ clientMutationId, text, parent: { protocol, nativeId, readRef? }, accountIds, media?, linkedPostAccountIds?, recoverFrom?: { batchId, deliveryIds } })`, `submitNativeReply(id, { approve: true, deliveryIds })`, `nativeReplyStatus(id)`, `nativeReplyHistory({ limit?, cursor? })`, `prepareNativeReplyRemoval(id, { deliveryIds })`, `removeNativeReply(id, { approve: true, removals })`. Review account, destination, `kind`, source, exact `wireText` and every reviewed `media` item with its alt text; connection is not publication approval. `media` takes the same `NativeMediaInput[]` as `prepareNativePost` and needs the account's `publish_media` capability. `linkedPostAccountIds` explicitly selects different-network standalone posts with a source link, never matching threaded replies or a same-network denial fallback. Preserve mutation IDs, show per-destination results and reconcile uncertain attempts without reposting. To review selected unsent or definitively rejected deliveries again, use recoverFrom with a new mutation ID, the same source/accounts and freshly reviewed text; preserve that ID on uncertain preparation. Only saved recoverable destinations qualify, current gates still apply, and replacedBy disables old sends atomically. Never restart unknown, accepted or published deliveries. The new preview still needs explicit approval. Saved delegated drafts/results: `const { data: history } = await r.protocols.nativeReplyHistory({ limit: 20 })`; use its opaque cursor for the next page and `nativeReplyStatus(item.id)` for details. This is a private read, not a write or provider refresh, and includes Nostr results marked type=nostr. Use nativeNostrReplyResult(item.id) to check that recorded event on its relay without signing or resending. Discard page state on owner/app changes. See README for examples and the callback, revocation and removal contracts.
|
|
876
|
-
- `r.protocols` — Private app safety: `sourceControls` returns current `enabled` plus paged saved choices; `setSourceControl`, `onSourceControlChange` and `reportSource` share app scope. Example: `const choices = new Map(); const stop = r.protocols.onSourceControlChange(change => choices.set(JSON.stringify([change.source, change.control]), change.enabled));` then `await r.protocols.setSourceControl({ source: { protocol: "bluesky", kind: "actor", nativeId: "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa" }, control: "mute", enabled: true, scope: "minds" });` and call `stop(); choices.clear()` on account change/unmount. Notifications follow an exact server confirmation, never an uncertain response. These preferences and reports apply in Minds, not on the remote network. Open network ids (0.7.87, readers first): `import { SOCIAL_PROTOCOL_IDS, isSocialProtocolId } from '@recursiv/sdk'` lists every network a post, account or `SocialSourceRef` may name: bluesky, activitypub, nostr and farcaster (a Farcaster account by its FID, "3"; a cast by `<fid>:0x<40 lowercase hex>`). Listing an id serves nothing: read only what `publicNetworks()` lists, and skip an id this SDK does not know. Writes stay narrower: a native reply, follow, like or repost takes only `NATIVE_WRITE_PROTOCOLS` (bluesky, activitypub, nostr; `isNativeWriteProtocol(post.protocol)` before offering one), and a feed top's `networks` always names those three while a Farcaster lane may be absent (`networks.farcaster ?? []`). Keyless post links: `const visitor = new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false }); const { data: thread } = await visitor.protocols.getSharedConversation(projectId, { protocol: 'nostr', post: publicEventId })`; `sharedNetworks(projectId)` lists public conversation capabilities. Only configured apps; app moderation and rate limits apply, no member state or write authority. Member public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. `getFollowingFeed` sources: a slow account is `late` (status `late`, or `ok` with `late: true` while its held first page is shown; older APIs say `unavailable` with `late: true`), never a failure: read the next page for its posts (`lateNetworks` names its network). Example after selecting a returned source post: `const { data: thread } = await r.protocols.getPublicConversation({ protocol: post.protocol, post: post.nativeId, readRef: post.readRef })`. Render `focus`, oldest-first `ancestors` and direct `replies`; `public_provider_window` never promises every reply, and `limited` marks known omissions. The author's own thread (0.7.81): `thread` holds the focus author's continuation below the post, oldest first (a "1/" post's 2/, 3/, 4/ ..., at most 20 parts, each held to the same moderation and member controls as a reply); its first part is not repeated in `replies`, so render `focus`, `thread`, then `replies` (`thread ?? []`: older APIs omit it, and a `replies: 'none'` read answers none). A card (a post shared into a chat, a link preview) asks for the post alone, 0.7.78: `getPublicConversation({ protocol, post, replies: 'none' })` (also `getSharedConversation` and `getConnectedSocialConversation`) answers `focus` with no `ancestors` or `replies` and `repliesStatus: 'not_requested'`; a Nostr note answers from one relay read instead of a whole thread's. An older API answers 400 `invalid_input`: read without it. Requires both `users:read` and `posts:read` on the current human app key. Protocol discovery/settings: `const { data: adapters } = await r.protocols.list()` returns bounded `{ id, name, icon, color, maturity, capabilities, limitations }` metadata; `supported` means implemented, not configured, healthy, authorized, or enabled. Methods: `list`, `status`, `listCandidates`, `getCandidate`, `search`, `getSettings`, `refresh`, `updateSettings`.
|
|
889
|
+
- `r.protocols` — Private app safety: `sourceControls` returns current `enabled` plus paged saved choices; `setSourceControl`, `onSourceControlChange` and `reportSource` share app scope. Example: `const choices = new Map(); const stop = r.protocols.onSourceControlChange(change => choices.set(JSON.stringify([change.source, change.control]), change.enabled));` then `await r.protocols.setSourceControl({ source: { protocol: "bluesky", kind: "actor", nativeId: "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa" }, control: "mute", enabled: true, scope: "minds" });` and call `stop(); choices.clear()` on account change/unmount. Notifications follow an exact server confirmation, never an uncertain response. These preferences and reports apply in Minds, not on the remote network. Open network ids (0.7.87, readers first): `import { SOCIAL_PROTOCOL_IDS, isSocialProtocolId } from '@recursiv/sdk'` lists every network a post, account or `SocialSourceRef` may name: bluesky, activitypub, nostr and farcaster (a Farcaster account by its FID, "3"; a cast by `<fid>:0x<40 lowercase hex>`). Listing an id serves nothing: read only what `publicNetworks()` lists, and skip an id this SDK does not know. Writes stay narrower: a native reply, follow, like or repost takes only `NATIVE_WRITE_PROTOCOLS` (bluesky, activitypub, nostr; `isNativeWriteProtocol(post.protocol)` before offering one), and a feed top's `networks` always names those three while a Farcaster lane may be absent (`networks.farcaster ?? []`). Keyless post links: `const visitor = new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false }); const { data: thread } = await visitor.protocols.getSharedConversation(projectId, { protocol: 'nostr', post: publicEventId })`; `sharedNetworks(projectId)` lists public conversation capabilities. Only configured apps; app moderation and rate limits apply, no member state or write authority. Member public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; a Fediverse community a groups read lists under `open_network` (0.7.109): `const { data: page } = await r.protocols.getOpenGroupThreads({ group: entry.actor, limit: 20 })` reads its newest threads read-only (`page.group`, `page.items`); `limit` is 1 to 25, `compass: 'hide'` leaves out the threads in politics, and the next page passes `cursor: page.cursor` while it is set (404 while the app does not offer groups from other networks); private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. `getFollowingFeed` sources: a slow account is `late` (status `late`, or `ok` with `late: true` while its held first page is shown; older APIs say `unavailable` with `late: true`), never a failure: read the next page for its posts (`lateNetworks` names its network). Example after selecting a returned source post: `const { data: thread } = await r.protocols.getPublicConversation({ protocol: post.protocol, post: post.nativeId, readRef: post.readRef })`. Render `focus`, oldest-first `ancestors` and direct `replies`; `public_provider_window` never promises every reply, and `limited` marks known omissions. The author's own thread (0.7.81): `thread` holds the focus author's continuation below the post, oldest first (a "1/" post's 2/, 3/, 4/ ..., at most 20 parts, each held to the same moderation and member controls as a reply); its first part is not repeated in `replies`, so render `focus`, `thread`, then `replies` (`thread ?? []`: older APIs omit it, and a `replies: 'none'` read answers none). A card (a post shared into a chat, a link preview) asks for the post alone, 0.7.78: `getPublicConversation({ protocol, post, replies: 'none' })` (also `getSharedConversation` and `getConnectedSocialConversation`) answers `focus` with no `ancestors` or `replies` and `repliesStatus: 'not_requested'`; a Nostr note answers from one relay read instead of a whole thread's. An older API answers 400 `invalid_input`: read without it. Requires both `users:read` and `posts:read` on the current human app key. Protocol discovery/settings: `const { data: adapters } = await r.protocols.list()` returns bounded `{ id, name, icon, color, maturity, capabilities, limitations }` metadata; `supported` means implemented, not configured, healthy, authorized, or enabled. Methods: `list`, `status`, `listCandidates`, `getCandidate`, `search`, `getSettings`, `refresh`, `updateSettings`.
|
|
877
890
|
- Native Nostr: `const { data: preview } = await r.protocols.prepareNativeNostr(identityId, { action: "publish", content: draft })`; review and sign the exact public event, then `submitNativeNostr(identityId, { intent: preview.intent, event, approve: true })`. Preparation/submission never auto-retry. After uncertainty, use `getNativeNostrPost(identityId, preview.postId ?? preview.eventId)` before a user-selected retry of the identical intent/event: deletion checks the original note's `postId`, publication checks `eventId`. Check `nativeNostrAccount()` enablement first; keys stay with the signer. For replies, after `nativeNostrAccount()` advertises `reply_text_note`, use `prepareNativeNostr(identityId, { action: "reply", content: draft, parentId: post.nativeId })`. Review returned parent/root/participants/relay and exact event, sign with the connected client signer, then `submitNativeNostr(identityId, { intent, event, approve: true })`. Requires users:read, posts:read and posts:write. Kind1 public notes only; reply enablement and shared UI/live proof remain pending. Do not inherit arbitrary notification tags or relay hints. Signing mutations never auto-retry; check `getNativeNostrPost(identityId, eventId)` after uncertainty before a user-selected identical-event retry.
|
|
878
891
|
- Native networks (0.7.89): every eligible member is on ActivityPub, AT Protocol and Nostr by rule. `const { data } = await r.protocols.networks()` lists `{ network, state, reason, mode, handle, engageDefault }` (read-only; `ready` = set up on their first post or interaction, show "Will be set up when you first post or interact"). `activateNetwork(network)` = "Set up now" (`pending` + `retryAt` over budget; 409 `network_off` after the member's off switch). `setNetwork(network, { enabled: false, removePast: true })` turns it off and removes what was shared; `{ enabled: true }` turns it back on (403 `network_locked`, 409 `not_rolled_out`/`age_band_required`). Needs users:read (+ users:write to change). A linked outside account is a Settings choice, never a step in a main flow.
|
|
879
892
|
- Hosted Bluesky account on the app's own PDS: `const { data } = await r.protocols.createHostedIdentity({ protocol: 'atproto', handle: 'alice', email })`. Only the label is sent; `data.handle` carries the domain and `data.password` is returned this once, never stored. Never auto-retried. Either the account is finished or it is removed again. 409 `handle_unavailable` = choose another label; 503 `handle_dns_unavailable`/`handle_unresolvable`/`handle_unverified` = operator setup, retry later unchanged.
|
|
@@ -1083,3 +1096,9 @@ Bluesky quote: accounts must advertise `quote`; call `prepareNativePost({clientM
|
|
|
1083
1096
|
Public post search (API/SDK 0.7.35): `const { data: page } = await r.protocols.searchPublicPosts({ protocol: 'nostr', query: 'open social', limit: 10 })`. Bluesky/ActivityPub require a selected `accountId` from `nativeSocialAccounts()` with `post_search`; older Bluesky grants must reconnect with consent. Open their results with `getConnectedSocialConversation({ protocol, accountId: page.readerAccountId, post: item.nativeId, readRef: item.readRef })` and pass the same accountId to `reportSource` for post reports. Nostr uses `getPublicConversation`. Pages report provider/observedAt and bounded coverage; no invented exhaustive or global search. Keep cursors paired with the exact owner, query, account, limit and connection revision. No writes, credentials or new ingestion are part of search.
|
|
1084
1097
|
|
|
1085
1098
|
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.
|
|
1099
|
+
|
|
1100
|
+
## Public media privacy
|
|
1101
|
+
|
|
1102
|
+
`await r.media.sourceApprovals({ projectId, sources: [{ host: 'www.youtube-nocookie.com', purpose: 'player' }] })` reads only cached judgments for a registered app. Respect `enabled`, `enforce`, exact `owned_hosts` and expiry; unknown/stale provider results remain pending. Static images/icons stay relayed. A judgment permits only its exact host and purpose, and does not establish downstream player hosts or tenant ownership.
|
|
1103
|
+
|
|
1104
|
+
`await r.linkPreview.get(url, { messageId }, { projectId })` opts a signed-in chat preview into expiring opaque image/icon URLs. Keep previews in a source/member-bound memory cache, refresh before `media_expires_at`, clear on sign-out, and never fall back to a bare private chat URL. Existing two-argument calls preserve their wire behavior.
|